From 7470658080d3ff62cac61060cb0d65349d35100d Mon Sep 17 00:00:00 2001 From: Sven Boemer Date: Fri, 12 Apr 2019 13:19:19 -0700 Subject: [PATCH 1/6] Add ILLink targets and tests --- .../Microsoft.NET.Build.Tasks.csproj | 1 + .../targets/Microsoft.NET.ILLink.targets | 110 +++++++ .../targets/Microsoft.NET.Publish.targets | 12 + .../targets/Microsoft.NET.Sdk.targets | 1 + .../GivenThatWeWantToRunILLink.cs | 303 ++++++++++++++++++ 5 files changed, 427 insertions(+) create mode 100644 src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.ILLink.targets create mode 100644 src/Tests/Microsoft.NET.Publish.Tests/GivenThatWeWantToRunILLink.cs diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/Microsoft.NET.Build.Tasks.csproj b/src/Tasks/Microsoft.NET.Build.Tasks/Microsoft.NET.Build.Tasks.csproj index 9410041e9314..7d3f218b87c2 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/Microsoft.NET.Build.Tasks.csproj +++ b/src/Tasks/Microsoft.NET.Build.Tasks/Microsoft.NET.Build.Tasks.csproj @@ -104,6 +104,7 @@ + ..\%(Stage0SdkFile.SdkName)\%(Stage0SdkFile.RecursiveDir)%(Stage0SdkFile.Filename)%(Stage0SdkFile.Extension) diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.ILLink.targets b/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.ILLink.targets new file mode 100644 index 000000000000..cb692865217f --- /dev/null +++ b/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.ILLink.targets @@ -0,0 +1,110 @@ + + + + + + + $(ILTransformDependsOn);ILLink + $(IntermediateOutputPath)linked + + <_LinkSemaphore>$(IntermediateOutputPath)Link.semaphore + + + + + + + + + + + + <_LinkedResolvedFileToPublish Include="@(_LinkedResolvedFileToPublishCandidates)" Condition="Exists('%(Identity)')" /> + + + + + + + <_RemovedManagedAssemblies Include="@(_ManagedAssembliesToLink)" Condition="!Exists('$(IntermediateLinkDir)/%(Filename)%(Extension)')" /> + <_PublishConflictPackageFiles Include="@(_RemovedManagedAssemblies)" /> + + + + + + + + + + + + + + + + + + + + + + + + + + <_ManagedAssembliesToLink Remove="@(_ManagedResolvedFileToPublish->WithMetadataValue('AssetType', 'resources'))" /> + <_LinkedResolvedFileToPublishCandidates Include="@(_ManagedAssembliesToLink->'$(IntermediateLinkDir)/%(Filename)%(Extension)')" /> + + + + + + diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.Publish.targets b/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.Publish.targets index 51cf7dfb4d07..a169bf76ce8e 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.Publish.targets +++ b/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.Publish.targets @@ -107,6 +107,7 @@ Copyright (c) .NET Foundation. All rights reserved. --> @@ -196,6 +197,17 @@ Copyright (c) .NET Foundation. All rights reserved. + + + + <_RemovedManagedAssemblies Include="@(_ManagedAssembliesToLink)" Condition="!Exists('$(IntermediateLinkDir)/%(Filename)%(Extension)')" /> - <_PublishConflictPackageFiles Include="@(_RemovedManagedAssemblies)" /> + + + + + + + @@ -72,8 +77,8 @@ Copyright (c) .NET Foundation. All rights reserved. Outputs="$(_LinkSemaphore)"> - - + <_UseBuildDependencyFile Condition="'@(_ExcludeFromPublishPackageReference)' == '' and '@(RuntimeStorePackages)' == '' and - '$(PreserveStoreLayout)' != 'true'">true + '$(PreserveStoreLayout)' != 'true' and + '$(LinkDuringPublish)' != 'true'">true diff --git a/src/Tests/Microsoft.NET.Publish.Tests/GivenThatWeWantToRunILLink.cs b/src/Tests/Microsoft.NET.Publish.Tests/GivenThatWeWantToRunILLink.cs index 5718c9e0b2b8..afdbebe1f57e 100644 --- a/src/Tests/Microsoft.NET.Publish.Tests/GivenThatWeWantToRunILLink.cs +++ b/src/Tests/Microsoft.NET.Publish.Tests/GivenThatWeWantToRunILLink.cs @@ -25,7 +25,7 @@ public GivenThatWeWantToRunILLink(ITestOutputHelper log) : base(log) public void ILLink_only_runs_when_switch_is_enabled(string targetFramework) { var projectName = "HelloWorld"; - var referenceProjectName = "ClassLib"; + var referenceProjectName = "ClassLibForILLink"; var rid = EnvironmentInfo.GetCompatibleRid(targetFramework); var testProject = CreateTestProjectForILLinkTesting(targetFramework, projectName, referenceProjectName); @@ -43,10 +43,16 @@ public void ILLink_only_runs_when_switch_is_enabled(string targetFramework) var publishedDll = Path.Combine(publishDirectory, $"{projectName}.dll"); var unusedDll = Path.Combine(publishDirectory, $"{referenceProjectName}.dll"); + var unusedFrameworkDll = Path.Combine(publishDirectory, $"{unusedFrameworkAssembly}.dll"); // Linker inputs are kept, including unused assemblies File.Exists(publishedDll).Should().BeTrue(); File.Exists(unusedDll).Should().BeTrue(); + File.Exists(unusedFrameworkDll).Should().BeTrue(); + + var depsFile = Path.Combine(publishDirectory, $"{projectName}.deps.json"); + DoesDepsFileHaveAssembly(depsFile, referenceProjectName).Should().BeTrue(); + DoesDepsFileHaveAssembly(depsFile, unusedFrameworkAssembly).Should().BeTrue(); } [Theory] @@ -54,7 +60,7 @@ public void ILLink_only_runs_when_switch_is_enabled(string targetFramework) public void ILLink_runs_and_creates_linked_app(string targetFramework) { var projectName = "HelloWorld"; - var referenceProjectName = "ClassLib"; + var referenceProjectName = "ClassLibForILLink"; var rid = EnvironmentInfo.GetCompatibleRid(targetFramework); var testProject = CreateTestProjectForILLinkTesting(targetFramework, projectName, referenceProjectName); @@ -73,14 +79,18 @@ public void ILLink_runs_and_creates_linked_app(string targetFramework) var linkedDll = Path.Combine(linkedDirectory, $"{projectName}.dll"); var publishedDll = Path.Combine(publishDirectory, $"{projectName}.dll"); var unusedDll = Path.Combine(publishDirectory, $"{referenceProjectName}.dll"); + var unusedFrameworkDll = Path.Combine(publishDirectory, $"{unusedFrameworkAssembly}.dll"); // Intermediate assembly is kept by linker and published, but not unused assemblies File.Exists(linkedDll).Should().BeTrue(); File.Exists(publishedDll).Should().BeTrue(); File.Exists(unusedDll).Should().BeFalse(); + File.Exists(unusedFrameworkDll).Should().BeFalse(); var depsFile = Path.Combine(publishDirectory, $"{projectName}.deps.json"); + DoesDepsFileHaveAssembly(depsFile, projectName).Should().BeTrue(); DoesDepsFileHaveAssembly(depsFile, referenceProjectName).Should().BeFalse(); + DoesDepsFileHaveAssembly(depsFile, unusedFrameworkAssembly).Should().BeFalse(); } [Theory] @@ -88,7 +98,7 @@ public void ILLink_runs_and_creates_linked_app(string targetFramework) public void ILLink_accepts_root_descriptor(string targetFramework) { var projectName = "HelloWorld"; - var referenceProjectName = "ClassLib"; + var referenceProjectName = "ClassLibForILLink"; var rid = EnvironmentInfo.GetCompatibleRid(targetFramework); var testProject = CreateTestProjectForILLinkTesting(targetFramework, projectName, referenceProjectName); @@ -115,7 +125,7 @@ public void ILLink_accepts_root_descriptor(string targetFramework) public void ILLink_runs_incrementally(string targetFramework) { var projectName = "HelloWorld"; - var referenceProjectName = "ClassLib"; + var referenceProjectName = "ClassLibForILLink"; var rid = EnvironmentInfo.GetCompatibleRid(targetFramework); var testProject = CreateTestProjectForILLinkTesting(targetFramework, projectName, referenceProjectName); @@ -146,7 +156,7 @@ public void ILLink_runs_incrementally(string targetFramework) public void ILLink_does_not_include_leftover_artifacts_on_second_run(string targetFramework) { var projectName = "HelloWorld"; - var referenceProjectName = "ClassLib"; + var referenceProjectName = "ClassLibForILLink"; var rid = EnvironmentInfo.GetCompatibleRid(targetFramework); var testProject = CreateTestProjectForILLinkTesting(targetFramework, projectName, referenceProjectName); @@ -190,7 +200,7 @@ public void ILLink_does_not_include_leftover_artifacts_on_second_run(string targ public void ILLink_runs_on_portable_app(string targetFramework) { var projectName = "HelloWorld"; - var referenceProjectName = "ClassLib"; + var referenceProjectName = "ClassLibForILLink"; var testProject = CreateTestProjectForILLinkTesting(targetFramework, projectName, referenceProjectName); var testAsset = _testAssetsManager.CreateTestProject(testProject) @@ -214,6 +224,7 @@ public void ILLink_runs_on_portable_app(string targetFramework) File.Exists(unusedDll).Should().BeFalse(); var depsFile = Path.Combine(publishDirectory, $"{projectName}.deps.json"); + DoesDepsFileHaveAssembly(depsFile, projectName).Should().BeTrue(); DoesDepsFileHaveAssembly(depsFile, referenceProjectName).Should().BeFalse(); } @@ -242,9 +253,21 @@ private static bool DoesDepsFileHaveAssembly(string depsFilePath, string assembl dependencyContext = new DependencyContextJsonReader().Read(fs); } - var runtimeLibrary = dependencyContext.RuntimeLibraries.Single(l => l.Name == assemblyName); - var runtimeFiles = runtimeLibrary.RuntimeAssemblyGroups.SelectMany(rag => rag.RuntimeFiles).ToList(); - return runtimeFiles.Any(); + return dependencyContext.RuntimeLibraries.Any(l => + l.RuntimeAssemblyGroups.Any(rag => + rag.AssetPaths.Any(f => + Path.GetFileName(f) == $"{assemblyName}.dll"))); + } + + static string unusedFrameworkAssembly = "System.IO"; + + private TestPackageReference GetPackageReference(TestProject project) + { + var asset = _testAssetsManager.CreateTestProject(project, project.Name).Restore(Log, project.Name); + var pack = new PackCommand(Log, Path.Combine(asset.TestRoot, project.Name)); + pack.Execute().Should().Pass(); + + return new TestPackageReference(project.Name, "1.0.0", pack.GetNuGetPackage(project.Name)); } private TestProject CreateTestProjectForILLinkTesting(string targetFramework, string mainProjectName, string referenceProjectName) @@ -269,13 +292,18 @@ public void UnusedMethodToRoot() } "; + var packageReference = GetPackageReference(referenceProject); + var testProject = new TestProject() { Name = mainProjectName, TargetFrameworks = targetFramework, IsSdkProject = true, - ReferencedProjects = { referenceProject } + PackageReferences = { packageReference } }; + + testProject.AdditionalProperties.Add("RestoreAdditionalProjectSources", Path.GetDirectoryName(packageReference.NupkgPath)); + testProject.SourceFiles[$"{mainProjectName}.cs"] = @" using System; public class Program From 075b1374f312ee1c79bbcbeab946606bca81a2e8 Mon Sep 17 00:00:00 2001 From: Sven Boemer Date: Wed, 17 Apr 2019 14:51:45 -0700 Subject: [PATCH 3/6] Support old deps generation logic --- .../targets/Microsoft.NET.ILLink.targets | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.ILLink.targets b/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.ILLink.targets index 451548d21dfb..fce82e1a120c 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.ILLink.targets +++ b/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.ILLink.targets @@ -57,8 +57,14 @@ Copyright (c) .NET Foundation. All rights reserved. + + + <_PublishConflictPackageFiles Include="@(_RemovedManagedAssemblies)" /> + From 1c98c83f414d13ca13467c0d6255f148ee4d8098 Mon Sep 17 00:00:00 2001 From: Sven Boemer Date: Wed, 17 Apr 2019 15:31:41 -0700 Subject: [PATCH 4/6] Update property names and respond to PR feedback The current public-facing linker properties are now: - PublishTrimmed - TrimmerRootDescriptors The IL transform extension point has been named ILTransformForPublish, to make it clear that it only runs on publish. This also enables incremental build for project file changes, and lets the linker run on projects targeting .NETStandard. --- .../targets/Microsoft.NET.ILLink.targets | 20 +++++++++---------- .../targets/Microsoft.NET.Publish.targets | 14 ++++++------- .../GivenThatWeWantToRunILLink.cs | 18 ++++++++--------- 3 files changed, 26 insertions(+), 26 deletions(-) diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.ILLink.targets b/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.ILLink.targets index fce82e1a120c..4a9d19f8dd35 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.ILLink.targets +++ b/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.ILLink.targets @@ -14,28 +14,28 @@ Copyright (c) .NET Foundation. All rights reserved. - $(ILTransformDependsOn);ILLink + $(ILTransformForPublishDependsOn);_ILLink $(IntermediateOutputPath)linked <_LinkSemaphore>$(IntermediateOutputPath)Link.semaphore - + - + - diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.Publish.targets b/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.Publish.targets index a51f686351a3..77420768497e 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.Publish.targets +++ b/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.Publish.targets @@ -107,7 +107,7 @@ Copyright (c) .NET Foundation. All rights reserved. --> @@ -199,14 +199,14 @@ Copyright (c) .NET Foundation. All rights reserved. - + <_LinkSemaphore>$(IntermediateOutputPath)Link.semaphore + + new @@ -34,7 +38,8 @@ Copyright (c) .NET Foundation. All rights reserved. ============================================================ --> - <_RemovedManagedAssemblies Include="@(_ManagedAssembliesToLink)" Condition="!Exists('$(IntermediateLinkDir)/%(Filename)%(Extension)')" /> + <_RemovedManagedAssemblies Include="@(_ManagedAssembliesToLink)" Condition="!Exists('$(IntermediateLinkDir)%(Filename)%(Extension)')" /> @@ -57,14 +62,8 @@ Copyright (c) .NET Foundation. All rights reserved. - - - <_PublishConflictPackageFiles Include="@(_RemovedManagedAssemblies)" /> - @@ -88,7 +87,7 @@ Copyright (c) .NET Foundation. All rights reserved. RootAssemblyNames="@(IntermediateAssembly->'%(Filename)')" RootDescriptorFiles="@(TrimmerRootDescriptors)" OutputDirectory="$(IntermediateLinkDir)" - ExtraArgs="$(ExtraLinkerArgs) --skip-unresolved true" /> + ExtraArgs="$(_ExtraTrimmerArgs) --skip-unresolved true" /> @@ -100,8 +99,9 @@ Copyright (c) .NET Foundation. All rights reserved. Compute the set of inputs to the linker. Currently this uses a heuristic to get the relevant input from ResolvedFileToPublish, - but in the future this should be replaced with the exact set of - runtime assemblies that will be in the deps file. + but with https://github.com/dotnet/sdk/issues/3109, this should be + replaced with the exact set of runtime assemblies that will be in + the deps file. ============================================================ --> @@ -112,7 +112,7 @@ Copyright (c) .NET Foundation. All rights reserved. <_ManagedAssembliesToLink Remove="@(_ManagedResolvedFileToPublish->WithMetadataValue('AssetType', 'resources'))" /> - <_LinkedResolvedFileToPublishCandidates Include="@(_ManagedAssembliesToLink->'$(IntermediateLinkDir)/%(Filename)%(Extension)')" /> + <_LinkedResolvedFileToPublishCandidates Include="@(_ManagedAssembliesToLink->'$(IntermediateLinkDir)%(Filename)%(Extension)')" /> diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.Publish.targets b/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.Publish.targets index 77420768497e..e921a0f21fd8 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.Publish.targets +++ b/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.Publish.targets @@ -107,7 +107,7 @@ Copyright (c) .NET Foundation. All rights reserved. --> @@ -197,17 +197,6 @@ Copyright (c) .NET Foundation. All rights reserved. - - -