Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[dotnet] Add support for 'dotnet pack'. Fixes #12631. #12900

Merged
merged 2 commits into from
Oct 4, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 63 additions & 1 deletion msbuild/Xamarin.Shared/Xamarin.Shared.targets
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ Copyright (C) 2018 Microsoft. All rights reserved.
</Target>

<PropertyGroup>
<BindingResourcePath>$(ProjectDir)$(OutputPath)$(AssemblyName).resources</BindingResourcePath>
<BindingResourcePath>$(OutputPath)$(AssemblyName).resources</BindingResourcePath>
</PropertyGroup>

<Target Name="_CreateBindingResourcePackage" Condition="'$(DesignTimeBuild)' != 'true'"
Expand Down Expand Up @@ -194,6 +194,68 @@ Copyright (C) 2018 Microsoft. All rights reserved.
</Touch>
</Target>

<!--
Binding projects may include NativeReference items, which makes the 'pack' target want to include a Native.$(AssemblyName).manifest into the NuGet,
which obviously fails because we don't create such a file. So let's remove that file.

Ref: https://github.com/dotnet/msbuild/blob/3a1e456fe227f3e2b190b434578844c31e8bcb4a/src/Tasks/Microsoft.Common.CurrentVersion.targets#L6111-L6118
Ref: https://github.com/dotnet/msbuild/issues/4584
-->
<PropertyGroup>
<GenerateNuspecDependsOn>
$(GenerateNuspecDependsOn);
_RemoveNativeManifestFromPack;
</GenerateNuspecDependsOn>
</PropertyGroup>
<Target Name="_RemoveNativeManifestFromPack">
<ItemGroup>
<_BuildOutputInPackage Remove="@(_BuildOutputInPackage)" Condition="'%(Filename)%(Extension)' == '$(_DeploymentTargetApplicationManifestFileName)'" />
</ItemGroup>
</Target>

<!--
Add any binding resource packages to the nupkg

Ref: https://github.com/dotnet/sdk/issues/14042#issuecomment-716868311
Ref: https://github.com/NuGet/Home/issues/10063#issuecomment-713083004
Ref: https://github.com/xamarin/xamarin-android/blob/681887ebdbd192ce7ce1cd02221d4939599ba762/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.AndroidLibraries.targets#L86-L99
-->
<PropertyGroup>
<TargetsForTfmSpecificContentInPackage>$(TargetsForTfmSpecificContentInPackage);_IncludeBindingResourcesInNuGetPackage</TargetsForTfmSpecificContentInPackage>
</PropertyGroup>
<Target Name="_IncludeBindingResourcesInNuGetPackage"
Condition="'$(IsMacEnabled)' == 'true' And '$(IncludeBuildOutput)' != 'false'"
>
<PropertyGroup>
<_HasOldStyleBindingItems Condition="@(ObjcBindingNativeLibrary->Count()) > 0">true</_HasOldStyleBindingItems>
</PropertyGroup>
<Error Condition="'$(_HasOldStyleBindingItems)' == 'true'" Text="Creating a NuGet package is not supported for projects that have ObjcBindingNativeLibrary items. Migrate to use NativeReference items instead." />

<!-- Figure out where to place the binding resources (see references above for more info) -->
<GetNuGetShortFolderName
TargetFrameworkMoniker="$(TargetFrameworkMoniker)"
TargetPlatformMoniker="$(TargetPlatformMoniker)">
<Output TaskParameter="NuGetShortFolderName" PropertyName="_NuGetShortFolderName" />
</GetNuGetShortFolderName>

<!-- The binding project may either produce a 'MyBindingProject.resources' directory, or a compressed 'MyBindingProject.resources.zip' version -->
<PropertyGroup>
<_AppleBindingResourceBasePath>$(TargetDir)$(TargetName).resources</_AppleBindingResourceBasePath>
</PropertyGroup>

<ItemGroup>
<!-- Add all the files that might exist in any binding resource packages -->
<_AppleBindingResource Include="$(_AppleBindingResourceBasePath)\**\*" PackagePath="lib\$(_NuGetShortFolderName)\$(TargetName).resources" />
<!-- Add any compressed files that might exist as well -->
<_AppleBindingResource Include="$(_AppleBindingResourceBasePath).zip" PackagePath="lib\$(_NuGetShortFolderName)" Condition="Exists('$(_AppleBindingResourceBasePath).zip')" />
</ItemGroup>

<!-- Add what we found to the NuGet -->
<ItemGroup>
<TfmSpecificPackageFile Include="@(_AppleBindingResource)" />
</ItemGroup>
</Target>

<!-- Cleaning via FileWrites leaves empty framework directories on disk, so nuke via RemoveDir -->
<PropertyGroup>
<CleanDependsOn>
Expand Down
13 changes: 13 additions & 0 deletions tests/common/DotNet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,18 @@ public static string Executable {
}
}

public static ExecutionResult AssertPack (string project, Dictionary<string, string> properties = null)
{
return Execute ("pack", project, properties, true);
}

public static ExecutionResult AssertPackFailure (string project, Dictionary<string, string> properties = null)
{
var rv = Execute ("pack", project, properties, false);
Assert.AreNotEqual (0, rv.ExitCode, "Unexpected success");
return rv;
}

public static ExecutionResult AssertPublish (string project, Dictionary<string, string> properties = null)
{
return Execute ("publish", project, properties, true);
Expand Down Expand Up @@ -75,6 +87,7 @@ public static ExecutionResult Execute (string verb, string project, Dictionary<s
switch (verb) {
case "clean":
case "build":
case "pack":
case "publish":
var args = new List<string> ();
args.Add (verb);
Expand Down
6 changes: 0 additions & 6 deletions tests/dotnet/MyClassLibrary/MyClassLibrary.csproj

This file was deleted.

22 changes: 22 additions & 0 deletions tests/dotnet/UnitTests/ApplePlatformExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using System;

namespace Xamarin.Utils {
public static class ApplePlatformExtensionsWithVersions {
public static string ToFrameworkWithDefaultVersion (this ApplePlatform @this)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a little confusing to me...
From quick googling, it looks like the '@this' allows you to use '@this' as a variable name, but why do we want to do this here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an extension method, which allows you to write methods that kind of work like instance methods. In instance methods you use this to refer to the instance, and while you can't use this in an extension method, you can use @this(the @ sign makes it so that this isn't a keyword, just a normal variable name), and that makes it look more like an instance method. In other words: it's just a convention to make extension methods look more like instance methods.

{
var netVersion = "net6.0";
switch (@this) {
case ApplePlatform.iOS:
return netVersion + "-ios" + SdkVersions.iOS;
case ApplePlatform.MacOSX:
return netVersion + "-macos" + SdkVersions.OSX;
case ApplePlatform.TVOS:
return netVersion + "-tvos" + SdkVersions.TVOS;
case ApplePlatform.MacCatalyst:
return netVersion + "-maccatalyst" + SdkVersions.MacCatalyst;
default:
return "Unknown";
}
}
}
}
3 changes: 3 additions & 0 deletions tests/dotnet/UnitTests/DotNetUnitTests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@
<Compile Include="..\..\..\external\Xamarin.MacDev\Xamarin.MacDev\PListObject.cs">
<Link>external\PListObject.cs</Link>
</Compile>
<Compile Include="..\..\..\tools\common\SdkVersions.cs">
<Link>external\SdkVersions.cs</Link>
</Compile>
</ItemGroup>
<ItemGroup>
<Folder Include="external\" />
Expand Down
175 changes: 175 additions & 0 deletions tests/dotnet/UnitTests/PackTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
#nullable enable

using System;
using System.IO;
using System.IO.Compression;
using System.Linq;

using NUnit.Framework;

using Xamarin.Utils;
using Xamarin.MacDev;

namespace Xamarin.Tests {
public class PackTest : TestBaseClass {


[Test]
[TestCase (ApplePlatform.iOS)]
[TestCase (ApplePlatform.MacCatalyst)]
[TestCase (ApplePlatform.TVOS)]
[TestCase (ApplePlatform.MacOSX)]
public void BindingProject (ApplePlatform platform)
{
var project = "bindings-test";
Configuration.IgnoreIfIgnoredPlatform (platform);

var project_path = Path.Combine (Configuration.RootPath, "tests", project, "dotnet", platform.AsString (), $"{project}.csproj");
Clean (project_path);
Configuration.CopyDotNetSupportingFiles (Path.GetDirectoryName (project_path));

var tmpdir = Cache.CreateTemporaryDirectory ();
var outputPath = Path.Combine (tmpdir, "OutputPath");
var intermediateOutputPath = Path.Combine (tmpdir, "IntermediateOutputPath");
var properties = GetDefaultProperties ();
properties ["OutputPath"] = outputPath + Path.DirectorySeparatorChar;
properties ["IntermediateOutputPath"] = intermediateOutputPath + Path.DirectorySeparatorChar;

var rv =DotNet.AssertPackFailure (project_path, properties);
var errors = BinLog.GetBuildLogErrors (rv.BinLogPath).ToArray ();
Assert.AreEqual (1, errors.Length, "Error count");
Assert.AreEqual ($"Creating a NuGet package is not supported for projects that have ObjcBindingNativeLibrary items. Migrate to use NativeReference items instead.", errors [0].Message, "Error message");
}

[Test]
[TestCase (ApplePlatform.iOS, true)]
[TestCase (ApplePlatform.iOS, false)]
[TestCase (ApplePlatform.MacCatalyst, true)]
[TestCase (ApplePlatform.MacCatalyst, false)]
[TestCase (ApplePlatform.TVOS, true)]
[TestCase (ApplePlatform.TVOS, false)]
[TestCase (ApplePlatform.MacOSX, true)]
[TestCase (ApplePlatform.MacOSX, false)]
public void BindingFrameworksProject (ApplePlatform platform, bool noBindingEmbedding)
{
var project = "bindings-framework-test";
Configuration.IgnoreIfIgnoredPlatform (platform);

var project_path = Path.Combine (Configuration.RootPath, "tests", project, "dotnet", platform.AsString (), $"{project}.csproj");
Clean (project_path);
Configuration.CopyDotNetSupportingFiles (Path.GetDirectoryName (project_path));

var tmpdir = Cache.CreateTemporaryDirectory ();
var outputPath = Path.Combine (tmpdir, "OutputPath");
var intermediateOutputPath = Path.Combine (tmpdir, "IntermediateOutputPath");
var properties = GetDefaultProperties ();
properties ["OutputPath"] = outputPath + Path.DirectorySeparatorChar;
properties ["IntermediateOutputPath"] = intermediateOutputPath + Path.DirectorySeparatorChar;
properties ["NoBindingEmbedding"] = noBindingEmbedding ? "true" : "false";

DotNet.AssertPack (project_path, properties);

var nupkg = Path.Combine (outputPath, project + ".1.0.0.nupkg");
Assert.That (nupkg, Does.Exist, "nupkg existence");

var archive = ZipFile.OpenRead (nupkg);
var files = archive.Entries.Select (v => v.FullName).ToHashSet ();
var hasSymlinks = noBindingEmbedding && (platform == ApplePlatform.MacCatalyst || platform == ApplePlatform.MacOSX);
if (noBindingEmbedding) {
Assert.That (archive.Entries.Count, Is.EqualTo (hasSymlinks ? 6 : 10), $"nupkg file count - {nupkg}");
} else {
Assert.That (archive.Entries.Count, Is.EqualTo (5), $"nupkg file count - {nupkg}");
}
Assert.That (files, Does.Contain (project + ".nuspec"), "nuspec");
Assert.That (files, Does.Contain ("_rels/.rels"), ".rels");
Assert.That (files, Does.Contain ("[Content_Types].xml"), "[Content_Types].xml");
Assert.That (files, Does.Contain ($"lib/{platform.ToFrameworkWithDefaultVersion ()}/{project}.dll"), $"{project}.dll");
Assert.That (files, Has.Some.Matches<string> (v => v.StartsWith ("package/services/metadata/core-properties/", StringComparison.Ordinal) && v.EndsWith (".psmdcp", StringComparison.Ordinal)), "psmdcp");
if (noBindingEmbedding) {
if (hasSymlinks) {
Assert.That (files, Does.Contain ($"lib/{platform.ToFrameworkWithDefaultVersion ()}/{project}.resources.zip"), $"{project}.resources.zip");
} else {
Assert.That (files, Does.Contain ($"lib/{platform.ToFrameworkWithDefaultVersion ()}/{project}.resources/XStaticArTest.framework/XStaticArTest"), $"XStaticArTest.framework/XStaticArTest");
Assert.That (files, Does.Contain ($"lib/{platform.ToFrameworkWithDefaultVersion ()}/{project}.resources/XStaticObjectTest.framework/XStaticObjectTest"), $"XStaticObjectTest.framework/XStaticObjectTest");
Assert.That (files, Does.Contain ($"lib/{platform.ToFrameworkWithDefaultVersion ()}/{project}.resources/XTest.framework/XTest"), $"XTest.framework/XTest");
Assert.That (files, Does.Contain ($"lib/{platform.ToFrameworkWithDefaultVersion ()}/{project}.resources/XTest.framework/Info.plist"), $"XTest.framework/Info.plist");
Assert.That (files, Does.Contain ($"lib/{platform.ToFrameworkWithDefaultVersion ()}/{project}.resources/manifest"), $"manifest");
}
}
}

[Test]
[TestCase (ApplePlatform.iOS, true)]
[TestCase (ApplePlatform.iOS, false)]
[TestCase (ApplePlatform.MacCatalyst, true)]
[TestCase (ApplePlatform.MacCatalyst, false)]
[TestCase (ApplePlatform.TVOS, true)]
[TestCase (ApplePlatform.TVOS, false)]
[TestCase (ApplePlatform.MacOSX, true)]
[TestCase (ApplePlatform.MacOSX, false)]
public void BindingXcFrameworksProject (ApplePlatform platform, bool noBindingEmbedding)
{
var project = "bindings-xcframework-test";
Configuration.IgnoreIfIgnoredPlatform (platform);

var project_path = Path.Combine (Configuration.RootPath, "tests", project, "dotnet", platform.AsString (), $"{project}.csproj");
Clean (project_path);
Configuration.CopyDotNetSupportingFiles (Path.GetDirectoryName (project_path));

var tmpdir = Cache.CreateTemporaryDirectory ();
var outputPath = Path.Combine (tmpdir, "OutputPath");
var intermediateOutputPath = Path.Combine (tmpdir, "IntermediateOutputPath");
var properties = GetDefaultProperties ();
properties ["OutputPath"] = outputPath + Path.DirectorySeparatorChar;
properties ["IntermediateOutputPath"] = intermediateOutputPath + Path.DirectorySeparatorChar;
properties ["NoBindingEmbedding"] = noBindingEmbedding ? "true" : "false";
properties ["AssemblyName"] = project;

DotNet.AssertPack (project_path, properties);

var nupkg = Path.Combine (outputPath, project + ".1.0.0.nupkg");
Assert.That (nupkg, Does.Exist, "nupkg existence");

var archive = ZipFile.OpenRead (nupkg);
var files = archive.Entries.Select (v => v.FullName).ToHashSet ();
Assert.That (archive.Entries.Count, Is.EqualTo (noBindingEmbedding ? 6 : 5), $"nupkg file count - {nupkg}");
Assert.That (files, Does.Contain (project + ".nuspec"), "nuspec");
Assert.That (files, Does.Contain ("_rels/.rels"), ".rels");
Assert.That (files, Does.Contain ("[Content_Types].xml"), "[Content_Types].xml");
Assert.That (files, Does.Contain ($"lib/{platform.ToFrameworkWithDefaultVersion ()}/{project}.dll"), $"{project}.dll");
Assert.That (files, Has.Some.Matches<string> (v => v.StartsWith ("package/services/metadata/core-properties/", StringComparison.Ordinal) && v.EndsWith (".psmdcp", StringComparison.Ordinal)), "psmdcp");
if (noBindingEmbedding) {
Assert.That (files, Does.Contain ($"lib/{platform.ToFrameworkWithDefaultVersion ()}/{project}.resources.zip"), $"{project}.resources.zip");
}
}

[Test]
[TestCase (ApplePlatform.iOS)]
[TestCase (ApplePlatform.MacCatalyst)]
[TestCase (ApplePlatform.TVOS)]
[TestCase (ApplePlatform.MacOSX)]
public void LibraryProject (ApplePlatform platform)
{
var project = "MyClassLibrary";
Configuration.IgnoreIfIgnoredPlatform (platform);

var project_path = GetProjectPath (project, runtimeIdentifiers: string.Empty, platform: platform, out var appPath);
Clean (project_path);
var properties = GetDefaultProperties ();

DotNet.AssertPack (project_path, properties);

var nupkg = Path.Combine (Path.GetDirectoryName (project_path)!, "bin", "Debug", project + ".1.0.0.nupkg");
Assert.That (nupkg, Does.Exist, "nupkg existence");

var archive = ZipFile.OpenRead (nupkg);
var files = archive.Entries.Select (v => v.FullName).ToHashSet ();
Assert.That (archive.Entries.Count, Is.EqualTo (5), "nupkg file count");
Assert.That (files, Does.Contain (project + ".nuspec"), "nuspec");
Assert.That (files, Does.Contain ("_rels/.rels"), ".rels");
Assert.That (files, Does.Contain ("[Content_Types].xml"), "[Content_Types].xml");
Assert.That (files, Does.Contain ($"lib/{platform.ToFrameworkWithDefaultVersion ()}/{project}.dll"), $"{project}.dll");
Assert.That (files, Has.Some.Matches<string> (v => v.StartsWith ("package/services/metadata/core-properties/", StringComparison.Ordinal) && v.EndsWith (".psmdcp", StringComparison.Ordinal)), "psmdcp");
}
}
}