trim build dependencies from output

This commit is contained in:
Andrew Stanton-Nurse 2016-05-02 19:51:12 -07:00
parent afa471cde3
commit 08c4aae6a9
11 changed files with 209 additions and 51 deletions

View file

@ -0,0 +1,12 @@
using System;
namespace ConsoleApplication
{
public class Program
{
public static void Main(string[] args)
{
Console.WriteLine("Hello World!");
}
}
}

View file

@ -0,0 +1,23 @@
{
"version": "1.0.0-*",
"compilationOptions": {
"emitEntryPoint": true
},
"dependencies": {
"Microsoft.NETCore.App": {
"version": "1.0.0-rc2-*",
"type": "platform"
},
"xunit.core": "2.1.0",
"xunit": {
"version": "2.1.0",
"type": "build"
}
},
"frameworks": {
"netcoreapp1.0": {
"imports": [ "portable-net451+win8" ]
}
}
}

View file

@ -0,0 +1,18 @@
{
"version": "1.0.0-*",
"dependencies": {
"Microsoft.NETCore.App": {
"version": "1.0.0-rc2-*",
"type": "platform"
},
},
"frameworks": {
"netcoreapp1.0": {
"imports": [ "portable-net451+win8" ]
}
},
"runtimes": {
"win7-x64": {},
"win7-x86": {}
}
}

View file

@ -109,8 +109,8 @@ namespace Microsoft.DotNet.Cli.Build
ExecSilent(_crossGenPath, crossgenArgs, env);
File.Delete(file);
File.Move(tempPathName, file);
File.Copy(tempPathName, file, overwrite: true);
File.Delete(tempPathName);
}
}
}

View file

@ -37,11 +37,14 @@ namespace Microsoft.DotNet.Cli.Compiler.Common
private readonly CommonCompilerOptions _compilerOptions;
public Executable(ProjectContext context, OutputPaths outputPaths, LibraryExporter exporter, string configuration)
: this(context, outputPaths, outputPaths.RuntimeOutputPath, outputPaths.IntermediateOutputDirectoryPath, exporter, configuration) { }
public Executable(ProjectContext context, OutputPaths outputPaths, string runtimeOutputPath, string intermediateOutputDirectoryPath, LibraryExporter exporter, string configuration)
{
_context = context;
_outputPaths = outputPaths;
_runtimeOutputPath = outputPaths.RuntimeOutputPath;
_intermediateOutputPath = outputPaths.IntermediateOutputDirectoryPath;
_runtimeOutputPath = runtimeOutputPath;
_intermediateOutputPath = intermediateOutputDirectoryPath;
_exporter = exporter;
_configuration = configuration;
_compilerOptions = _context.ProjectFile.GetCompilerOptions(_context.TargetFramework, configuration);
@ -135,12 +138,9 @@ namespace Microsoft.DotNet.Cli.Compiler.Common
private void WriteDepsFileAndCopyProjectDependencies(LibraryExporter exporter)
{
WriteDeps(exporter);
if (_context.ProjectFile.HasRuntimeOutput(_configuration))
{
WriteRuntimeConfig(exporter);
WriteDevRuntimeConfig(exporter);
}
// When called this way we don't need to filter exports, so we pass the same list to both.
var exports = exporter.GetAllExports().ToList();
WriteConfigurationFiles(exports, exports, includeDevConfig: true);
var projectExports = exporter.GetDependencies(LibraryType.Project);
CopyAssemblies(projectExports);
@ -150,7 +150,20 @@ namespace Microsoft.DotNet.Cli.Compiler.Common
CopyAssets(packageExports);
}
private void WriteRuntimeConfig(LibraryExporter exporter)
public void WriteConfigurationFiles(IEnumerable<LibraryExport> allExports, IEnumerable<LibraryExport> depsExports, bool includeDevConfig)
{
WriteDeps(depsExports);
if (_context.ProjectFile.HasRuntimeOutput(_configuration))
{
WriteRuntimeConfig(allExports);
if (includeDevConfig)
{
WriteDevRuntimeConfig();
}
}
}
private void WriteRuntimeConfig(IEnumerable<LibraryExport> allExports)
{
if (!_context.TargetFramework.IsDesktop())
{
@ -161,7 +174,7 @@ namespace Microsoft.DotNet.Cli.Compiler.Common
var runtimeOptions = new JObject();
json.Add("runtimeOptions", runtimeOptions);
WriteFramework(runtimeOptions, exporter);
WriteFramework(runtimeOptions, allExports);
WriteRuntimeOptions(runtimeOptions);
var runtimeConfigJsonFile =
@ -175,15 +188,14 @@ namespace Microsoft.DotNet.Cli.Compiler.Common
}
}
private void WriteFramework(JObject runtimeOptions, LibraryExporter exporter)
private void WriteFramework(JObject runtimeOptions, IEnumerable<LibraryExport> allExports)
{
var redistPackage = _context.PlatformLibrary;
if (redistPackage != null)
{
var packageName = redistPackage.Identity.Name;
var redistExport = exporter.GetAllExports()
.FirstOrDefault(e => e.Library.Identity.Name.Equals(packageName));
var redistExport = allExports.FirstOrDefault(e => e.Library.Identity.Name.Equals(packageName));
if (redistExport == null)
{
throw new InvalidOperationException($"Platform package '{packageName}' was not present in the graph.");
@ -212,7 +224,7 @@ namespace Microsoft.DotNet.Cli.Compiler.Common
}
}
private void WriteDevRuntimeConfig(LibraryExporter exporter)
private void WriteDevRuntimeConfig()
{
if (_context.TargetFramework.IsDesktop())
{
@ -241,13 +253,12 @@ namespace Microsoft.DotNet.Cli.Compiler.Common
runtimeOptions.Add("additionalProbingPaths", additionalProbingPaths);
}
public void WriteDeps(LibraryExporter exporter)
public void WriteDeps(IEnumerable<LibraryExport> exports)
{
Directory.CreateDirectory(_runtimeOutputPath);
var includeCompile = _compilerOptions.PreserveCompilationContext == true;
var exports = exporter.GetAllExports().ToArray();
var dependencyContext = new DependencyContextBuilder().Build(
compilerOptions: includeCompile ? _compilerOptions : null,
compilationExports: includeCompile ? exports : null,

View file

@ -64,7 +64,7 @@ namespace Microsoft.DotNet.ProjectModel
.WithProjectResolver(ProjectResolver)
.WithLockFileResolver(LockFileResolver)
.WithProjectReaderSettings(ProjectReaderSettings);
if(IsDesignTime)
if (IsDesignTime)
{
builder.AsDesignTime();
}
@ -168,14 +168,14 @@ namespace Microsoft.DotNet.ProjectModel
var deduper = new HashSet<string>();
foreach (var target in LockFile.Targets)
{
var id = $"{target.TargetFramework}/{target.RuntimeIdentifier}";
var context = Clone()
.WithTargetFramework(target.TargetFramework)
.WithRuntimeIdentifiers(new[] { target.RuntimeIdentifier }).Build();
var id = $"{context.TargetFramework}/{context.RuntimeIdentifier}";
if (deduper.Add(id))
{
var builder = Clone()
.WithTargetFramework(target.TargetFramework)
.WithRuntimeIdentifiers(new[] { target.RuntimeIdentifier });
yield return builder.Build();
yield return context;
}
}
}

View file

@ -1,7 +1,5 @@
using System;
using System.Collections.Generic;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.DotNet.ProjectModel.Compilation;
using Microsoft.DotNet.ProjectModel.Graph;
@ -9,13 +7,7 @@ namespace Microsoft.DotNet.ProjectModel
{
public static class ProjectModelPlatformExtensions
{
public static IEnumerable<LibraryExport> ExcludePlatformExports(this ProjectContext context, IEnumerable<LibraryExport> allExports)
{
var exclusionList = context.GetPlatformExclusionList(allExports);
return allExports.Where(e => !exclusionList.Contains(e.Library.Identity.Name));
}
public static HashSet<string> GetPlatformExclusionList(this ProjectContext context, IEnumerable<LibraryExport> allExports)
public static HashSet<string> GetPlatformExclusionList(this ProjectContext context, IDictionary<string, LibraryExport> exports)
{
var exclusionList = new HashSet<string>();
var redistPackage = context.PlatformLibrary;
@ -23,9 +15,6 @@ namespace Microsoft.DotNet.ProjectModel
{
return exclusionList;
}
var exports = allExports
.Where(e => e.Library.Identity.Type.Equals(LibraryType.Package))
.ToDictionary(e => e.Library.Identity.Name);
var redistExport = exports[redistPackage.Identity.Name];
@ -34,7 +23,7 @@ namespace Microsoft.DotNet.ProjectModel
return exclusionList;
}
private static void CollectDependencies(Dictionary<string, LibraryExport> exports, IEnumerable<LibraryRange> dependencies, HashSet<string> exclusionList)
private static void CollectDependencies(IDictionary<string, LibraryExport> exports, IEnumerable<LibraryRange> dependencies, HashSet<string> exclusionList)
{
foreach (var dependency in dependencies)
{
@ -46,5 +35,34 @@ namespace Microsoft.DotNet.ProjectModel
}
}
}
public static HashSet<string> GetTypeBuildExclusionList(this ProjectContext context, IDictionary<string, LibraryExport> exports)
{
var acceptedExports = new HashSet<string>();
// Accept the root project, obviously :)
acceptedExports.Add(context.RootProject.Identity.Name);
// Walk all dependencies, tagging exports. But don't walk through Build dependencies.
CollectNonBuildDependencies(exports, context.RootProject.Dependencies, acceptedExports);
// Whatever is left in exports was brought in ONLY by a build dependency
var exclusionList = new HashSet<string>(exports.Keys);
exclusionList.ExceptWith(acceptedExports);
return exclusionList;
}
private static void CollectNonBuildDependencies(IDictionary<string, LibraryExport> exports, IEnumerable<LibraryRange> dependencies, HashSet<string> acceptedExports)
{
foreach (var dependency in dependencies)
{
var export = exports[dependency.Name];
if (!dependency.Type.Equals(LibraryDependencyType.Build))
{
acceptedExports.Add(export.Library.Identity.Name);
CollectNonBuildDependencies(exports, export.Library.Dependencies, acceptedExports);
}
}
}
}
}

View file

@ -134,7 +134,14 @@ namespace Microsoft.DotNet.Tools.Publish
var buildOutputPaths = context.GetOutputPaths(configuration, buildBasePath);
var exports = exporter.GetAllExports();
foreach (var export in context.ExcludePlatformExports(exports))
var exportsLookup = exports.ToDictionary(e => e.Library.Identity.Name);
var platformExclusionList = context.GetPlatformExclusionList(exportsLookup);
var buildExclusionList = context.GetTypeBuildExclusionList(exportsLookup);
var allExclusionList = new HashSet<string>(platformExclusionList);
allExclusionList.UnionWith(buildExclusionList);
foreach (var export in FilterExports(exports, allExclusionList))
{
Reporter.Verbose.WriteLine($"publish: Publishing {export.Library.Identity.ToString().Green().Bold()} ...");
@ -164,12 +171,9 @@ namespace Microsoft.DotNet.Tools.Publish
if (context.ProjectFile.HasRuntimeOutput(configuration) && !context.TargetFramework.IsDesktop())
{
PublishFiles(
new[] {
buildOutputPaths.RuntimeFiles.DepsJson,
buildOutputPaths.RuntimeFiles.RuntimeConfigJson
},
outputPath);
// Make executable in the new location
var executable = new Executable(context, buildOutputPaths, outputPath, buildOutputPaths.IntermediateOutputDirectoryPath, exporter, configuration);
executable.WriteConfigurationFiles(exports, FilterExports(exports, buildExclusionList), includeDevConfig: false);
}
var contentFiles = new ContentFiles(context);
@ -202,6 +206,11 @@ namespace Microsoft.DotNet.Tools.Publish
return true;
}
private static IEnumerable<LibraryExport> FilterExports(IEnumerable<LibraryExport> exports, HashSet<string> exclusionList)
{
return exports.Where(e => !exclusionList.Contains(e.Library.Identity.Name));
}
/// <summary>
/// Filters which export's RuntimeAssets should get copied to the output path.
/// </summary>

View file

@ -1,14 +1,17 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using FluentAssertions;
using Microsoft.DotNet.ProjectModel.Graph;
using Microsoft.DotNet.Tools.Test.Utilities;
using NuGet.Frameworks;
using Xunit;
namespace Microsoft.DotNet.ProjectModel.Tests
{
public class ProjectContextBuilderTests
public class ProjectContextBuilderTests : TestBase
{
private static readonly HashSet<string> KnownProperties = new HashSet<string>(StringComparer.Ordinal) {
"Project",
@ -48,7 +51,7 @@ namespace Microsoft.DotNet.ProjectModel.Tests
var clonedBuilder = initialBuilder.Clone();
// Compare all the properties. This is a shallow clone, so they should all be exactly ReferenceEqual
foreach(var prop in typeof(ProjectContextBuilder).GetTypeInfo().DeclaredProperties)
foreach (var prop in typeof(ProjectContextBuilder).GetTypeInfo().DeclaredProperties)
{
KnownProperties.Remove(prop.Name).Should().BeTrue(because: $"{prop.Name} should be listed in the known properties to ensure it is properly tested.");
@ -65,5 +68,18 @@ namespace Microsoft.DotNet.ProjectModel.Tests
KnownProperties.Should().BeEmpty(because: "all properties should have been checked by the CloneTest");
}
[Fact]
public void BuildAllTargetsProperlyDeduplicatesTargets()
{
// Load all project contexts for the test project
var contexts = new ProjectContextBuilder()
.WithProjectDirectory(Path.Combine(TestAssetsManager.AssetsRoot, "TestProjectContextBuildAllDedupe"))
.BuildAllTargets()
.ToList();
// This is a portable app, so even though RIDs are specified, BuildAllTargets should only produce one output.
Assert.Equal(1, contexts.Count);
}
}
}

View file

@ -19,7 +19,7 @@ namespace Microsoft.Extensions.Testing.Abstractions.UnitTests
public void It_creates_a_pdb_reader_right_away()
{
var pdbReaderFactoryMock = new Mock<IPdbReaderFactory>();
var sourceInformationProvider = new SourceInformationProvider(_pdbPath, null, pdbReaderFactoryMock.Object);
var sourceInformationProvider = new SourceInformationProvider(_pdbPath, pdbReaderFactoryMock.Object);
pdbReaderFactoryMock.Verify(p => p.Create(_pdbPath), Times.Once);
}
@ -37,7 +37,7 @@ namespace Microsoft.Extensions.Testing.Abstractions.UnitTests
var pdbReaderFactoryMock = new Mock<IPdbReaderFactory>();
pdbReaderFactoryMock.Setup(p => p.Create(_pdbPath)).Returns(pdbReaderMock.Object);
var sourceInformationProvider = new SourceInformationProvider(_pdbPath, null, pdbReaderFactoryMock.Object);
var sourceInformationProvider = new SourceInformationProvider(_pdbPath, pdbReaderFactoryMock.Object);
var actualSourceInformation = sourceInformationProvider.GetSourceInformation(methodInfo);
@ -53,7 +53,7 @@ namespace Microsoft.Extensions.Testing.Abstractions.UnitTests
pdbReaderFactoryMock.Setup(p => p.Create(_pdbPath)).Returns(pdbReaderMock.Object);
using (var sourceInformationProvider =
new SourceInformationProvider(_pdbPath, null, pdbReaderFactoryMock.Object))
new SourceInformationProvider(_pdbPath, pdbReaderFactoryMock.Object))
{
}

View file

@ -0,0 +1,51 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using FluentAssertions;
using Microsoft.DotNet.Tools.Test.Utilities;
using Microsoft.Extensions.DependencyModel;
using Xunit;
namespace Microsoft.DotNet.Tools.Publish.Tests
{
public class PublishAppWithBuildDependency : TestBase
{
[Fact]
public void PublishExcludesBuildDependencies()
{
var testInstance = TestAssetsManager.CreateTestInstance("AppWithDirectDependencyAndTypeBuild")
.WithLockFiles();
new RestoreCommand() { WorkingDirectory = testInstance.TestRoot }.Execute().Should().Pass();
var publishCommand = new PublishCommand(testInstance.TestRoot);
var publishResult = publishCommand.Execute();
publishResult.Should().Pass();
var publishDir = publishCommand.GetOutputDirectory(portable: true);
publishDir.Should().HaveFiles(new[]
{
// This one is directly referenced
"xunit.core.dll"
});
// But these are brought in only by the type:build dependency, and should not be published
publishDir.Should().NotHaveFiles(new [] {
"xunit.assert.dll"
});
// Check the deps file
var reader = new DependencyContextJsonReader();
DependencyContext context;
using (var file = File.OpenRead(Path.Combine(publishDir.FullName, "AppWithDirectDependencyAndTypeBuild.deps.json")))
{
context = reader.Read(file);
}
context.RuntimeLibraries.Should().NotContain(l => string.Equals(l.Name, "xunit.assert"));
context.CompileLibraries.Should().NotContain(l => string.Equals(l.Name, "xunit.assert"));
}
}
}