Merge pull request #2220 from dotnet/brthor/cross-publish

Enable Cross Platform Publishing and add tests
This commit is contained in:
Bryan Thornbury 2016-04-12 15:34:28 -07:00
commit b0588fc4be
12 changed files with 220 additions and 36 deletions

View file

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

View file

@ -0,0 +1,35 @@
{
"compilationOptions": {
"emitEntryPoint": true
},
"frameworks": {
"netcoreapp1.0": {
"imports": [
"dnxcore50",
"portable-net45+win8"
],
"dependencies": {
"Microsoft.NETCore.App": "1.0.0-rc2-*"
}
}
},
"runtimes": {
"win7-x64": {},
"win7-x86": {},
"osx.10.10-x64": {},
"osx.10.11-x64": {},
"ubuntu.14.04-x64": {},
"centos.7-x64": {},
"rhel.7.2-x64": {},
"debian.8.2-x64": {}
},
"runtimeOptions": {
"somethingString": "anything",
"somethingBoolean": true,
"someArray": ["one", "two"],
"someObject": {
"someProperty": "someValue"
}
}
}

View file

@ -12,9 +12,9 @@
"../src/*.cs"
],
"frameworks": {
"netstandardapp1.5": {
"netcoreapp1.0": {
"dependencies": {
"Microsoft.NETCore.App": "1.0.0-rc2-24008"
"Microsoft.NETCore.App": "1.0.0-rc2-3002306"
},
"imports": [
"dnxcore50",

View file

@ -422,6 +422,9 @@ namespace Microsoft.DotNet.Cli.Build
File.Copy(
Path.Combine(Dirs.Corehost, HostPolicyBaseName),
Path.Combine(SharedFrameworkNameAndVersionRoot, HostPolicyBaseName), true);
File.Copy(
Path.Combine(Dirs.Corehost, DotnetHostFxrBaseName),
Path.Combine(SharedFrameworkNameAndVersionRoot, DotnetHostFxrBaseName), true);
if (File.Exists(Path.Combine(SharedFrameworkNameAndVersionRoot, "mscorlib.ni.dll")))
{

View file

@ -58,7 +58,7 @@ namespace Microsoft.DotNet.Cli.Build
[Target(nameof(RestoreTestAssetPackages), nameof(BuildTestAssetPackages))]
public static BuildTargetResult SetupTestPackages(BuildTargetContext c) => c.Success();
[Target(nameof(RestoreTestAssetProjects), nameof(RestoreDesktopTestAssetProjects), nameof(BuildTestAssetProjects))]
[Target(nameof(RestoreTestAssetProjects), nameof(RestoreDesktopTestAssetProjects), nameof(RestoreCrossPublishTestAssetProjects), nameof(BuildTestAssetProjects))]
public static BuildTargetResult SetupTestProjects(BuildTargetContext c) => c.Success();
[Target]
@ -137,6 +137,18 @@ namespace Microsoft.DotNet.Cli.Build
return c.Success();
}
[Target]
public static BuildTargetResult RestoreCrossPublishTestAssetProjects(BuildTargetContext c)
{
var dotnet = DotNetCli.Stage2;
dotnet.Restore("--verbosity", "verbose")
.WorkingDirectory(Path.Combine(c.BuildContext.BuildDirectory, "TestAssets", "CrossPublishTestProjects"))
.Execute().EnsureSuccessful();
return c.Success();
}
[Target(nameof(CleanTestPackages), nameof(CleanProductPackages))]
public static BuildTargetResult BuildTestAssetPackages(BuildTargetContext c)

View file

@ -34,6 +34,7 @@ namespace Microsoft.DotNet.Cli.Utils
public static readonly string ResponseFileSuffix = ".rsp";
public static readonly string PublishedHostExecutableName = "dotnet";
public static readonly string HostExecutableName = "corehost" + ExeSuffix;
public static readonly string[] HostBinaryNames = new string[] {
HostExecutableName,

View file

@ -1,5 +1,6 @@
using System.Collections.Generic;
using System.IO;
using System;
using System.Linq;
using Microsoft.DotNet.ProjectModel;
using Microsoft.DotNet.ProjectModel.Compilation;

View file

@ -139,11 +139,10 @@ namespace Microsoft.DotNet.Tools.Publish
var exports = exporter.GetAllExports();
foreach (var export in exports.Where(e => !collectExclusionList.Contains(e.Library.Identity.Name)))
{
Reporter.Verbose.WriteLine($"Publishing {export.Library.Identity.ToString().Green().Bold()} ...");
Reporter.Verbose.WriteLine($"publish: Publishing {export.Library.Identity.ToString().Green().Bold()} ...");
PublishAssetGroups(export.RuntimeAssemblyGroups, outputPath, nativeSubdirectories: false, includeRuntimeGroups: isPortable);
PublishAssetGroups(export.NativeLibraryGroups, outputPath, nativeSubdirectories, includeRuntimeGroups: isPortable);
export.RuntimeAssets.StructuredCopyTo(outputPath, outputPaths.IntermediateOutputDirectoryPath);
}
if (options.PreserveCompilationContext.GetValueOrDefault())
@ -153,19 +152,9 @@ namespace Microsoft.DotNet.Tools.Publish
PublishRefs(export, outputPath, !collectExclusionList.Contains(export.Library.Identity.Name));
}
}
if (context.ProjectFile.HasRuntimeOutput(configuration) && !context.TargetFramework.IsDesktop())
{
// Get the output paths used by the call to `dotnet build` above (since we didn't pass `--output`, they will be different from
// our current output paths)
var buildOutputPaths = context.GetOutputPaths(configuration, buildBasePath);
PublishFiles(
new[] {
buildOutputPaths.RuntimeFiles.DepsJson,
buildOutputPaths.RuntimeFiles.RuntimeConfigJson
},
outputPath);
}
var buildOutputPaths = context.GetOutputPaths(configuration, buildBasePath, null);
PublishBuildOutputFiles(buildOutputPaths, context, outputPath, Configuration);
var contentFiles = new ContentFiles(context);
contentFiles.StructuredCopyTo(outputPath);
@ -173,17 +162,72 @@ namespace Microsoft.DotNet.Tools.Publish
// Publish a host if this is an application
if (options.EmitEntryPoint.GetValueOrDefault() && !string.IsNullOrEmpty(context.RuntimeIdentifier))
{
Reporter.Verbose.WriteLine($"Copying native host to output to create fully standalone output.");
PublishHost(context, outputPath, options);
Reporter.Verbose.WriteLine($"publish: Renaming native host in output to create fully standalone output.");
RenamePublishedHost(context, outputPath, options);
}
RunScripts(context, ScriptNames.PostPublish, contextVariables);
Reporter.Output.WriteLine($"Published to {outputPath}".Green().Bold());
Reporter.Output.WriteLine($"publish: Published to {outputPath}".Green().Bold());
return true;
}
private void PublishBuildOutputFiles(OutputPaths buildOutputPaths, ProjectContext context, string outputPath, string configuration)
{
List<string> filesToPublish = new List<string>();
string[] buildOutputFiles = null;
if (context.ProjectFile.HasRuntimeOutput(configuration))
{
Reporter.Verbose.WriteLine($"publish: using runtime build output files");
buildOutputFiles = new string[]
{
buildOutputPaths.RuntimeFiles.DepsJson,
buildOutputPaths.RuntimeFiles.RuntimeConfigJson,
buildOutputPaths.RuntimeFiles.Config,
buildOutputPaths.RuntimeFiles.Assembly,
buildOutputPaths.RuntimeFiles.PdbPath,
Path.ChangeExtension(buildOutputPaths.RuntimeFiles.Assembly, "xml")
};
filesToPublish.AddRange(buildOutputPaths.RuntimeFiles.Resources());
}
else
{
Reporter.Verbose.WriteLine($"publish: using compilation build output files");
buildOutputFiles = new string[]
{
buildOutputPaths.CompilationFiles.Assembly,
buildOutputPaths.CompilationFiles.PdbPath,
Path.ChangeExtension(buildOutputPaths.CompilationFiles.Assembly, "xml")
};
filesToPublish.AddRange(buildOutputPaths.CompilationFiles.Resources());
}
foreach (var buildOutputFile in buildOutputFiles)
{
if (File.Exists(buildOutputFile))
{
filesToPublish.Add(buildOutputFile);
}
else
{
Reporter.Verbose.WriteLine($"publish: build output file not found {buildOutputFile} ");
}
}
Reporter.Verbose.WriteLine($"publish: Copying build output files:\n {string.Join("\n", filesToPublish)}");
PublishFiles(
filesToPublish,
outputPath);
}
private bool InvokeBuildOnProject(ProjectContext context, string buildBasePath, string configuration)
{
var args = new List<string>()
@ -272,33 +316,60 @@ namespace Microsoft.DotNet.Tools.Publish
}
}
private static int PublishHost(ProjectContext context, string outputPath, CommonCompilerOptions compilationOptions)
private static int RenamePublishedHost(ProjectContext context, string outputPath, CommonCompilerOptions compilationOptions)
{
if (context.TargetFramework.IsDesktop())
{
return 0;
}
foreach (var binaryName in Constants.HostBinaryNames)
var publishedHostFile = ResolvePublishedHostFile(outputPath);
if (publishedHostFile == null)
{
var hostBinaryPath = Path.Combine(AppContext.BaseDirectory, binaryName);
if (!File.Exists(hostBinaryPath))
{
Reporter.Error.WriteLine($"Cannot find {binaryName} in the dotnet directory.".Red());
return 1;
}
Reporter.Output.WriteLine($"publish: warning: host executable not available in dependencies, using host for current platform");
// TODO should this be an error?
var outputBinaryName = binaryName.Equals(Constants.HostExecutableName)
? compilationOptions.OutputName + Constants.ExeSuffix
: binaryName;
var outputBinaryPath = Path.Combine(outputPath, outputBinaryName);
CoreHost.CopyTo(outputPath, compilationOptions.OutputName + Constants.ExeSuffix);
return 0;
}
File.Copy(hostBinaryPath, outputBinaryPath, overwrite: true);
var publishedHostExtension = Path.GetExtension(publishedHostFile);
var renamedHostName = compilationOptions.OutputName + publishedHostExtension;
var renamedHostFile = Path.Combine(outputPath, renamedHostName);
try
{
Reporter.Verbose.WriteLine($"publish: renaming published host {publishedHostFile} to {renamedHostFile}");
File.Copy(publishedHostFile, renamedHostFile, true);
File.Delete(publishedHostFile);
}
catch (Exception e)
{
Reporter.Error.WriteLine($"publish: Failed to rename {publishedHostFile} to {renamedHostFile}: {e.Message}");
return 1;
}
return 0;
}
private static string ResolvePublishedHostFile(string outputPath)
{
var tryExtensions = new string[] { "", ".exe" };
foreach (var extension in tryExtensions)
{
var hostFile = Path.Combine(outputPath, Constants.PublishedHostExecutableName + extension);
if (File.Exists(hostFile))
{
Reporter.Verbose.WriteLine($"resolved published host: {hostFile}");
return hostFile;
}
}
Reporter.Verbose.WriteLine($"failed to resolve published host in: {outputPath}");
return null;
}
private static void PublishFiles(IEnumerable<string> files, string outputPath)
{
foreach (var file in files)
@ -326,6 +397,7 @@ namespace Microsoft.DotNet.Tools.Publish
Directory.CreateDirectory(destinationDirectory);
}
Reporter.Verbose.WriteLine($"Publishing file {Path.GetFileName(file.RelativePath)} to {destinationDirectory}");
File.Copy(file.ResolvedPath, Path.Combine(destinationDirectory, file.FileName), overwrite: true);
}
}

View file

@ -218,6 +218,7 @@ namespace Microsoft.DotNet.Cli.Utils.Tests
projectToolsCommandResolver.GenerateDepsJsonFile(lockFile, depsJsonFile);
File.ReadAllText(depsJsonFile).Should().Be("temp");
File.Delete(depsJsonFile);
}
private ProjectToolsCommandResolver SetupProjectToolsCommandResolver(

View file

@ -55,13 +55,20 @@ namespace Microsoft.DotNet.Tools.Test.Utilities
{
if (s_testsAssetsMgr == null)
{
string assetsRoot = Path.Combine(RepoRoot, "TestAssets", "TestProjects");
s_testsAssetsMgr = new TestAssetsManager(assetsRoot);
s_testsAssetsMgr = GetTestGroupTestAssetsManager("TestProjects");
}
return s_testsAssetsMgr;
}
}
protected static TestAssetsManager GetTestGroupTestAssetsManager(string testGroup)
{
string assetsRoot = Path.Combine(RepoRoot, "TestAssets", testGroup);
var testAssetsMgr = new TestAssetsManager(assetsRoot);
return testAssetsMgr;
}
protected TestBase()
{

View file

@ -92,6 +92,46 @@ namespace Microsoft.DotNet.Tools.Publish.Tests
publishCommand.Execute().Should().Fail();
}
[Theory]
[InlineData("centos.7-x64", "", new string[] { "libhostfxr.so", "libcoreclr.so", "libhostpolicy.so" })]
[InlineData("rhel.7.2-x64", "", new string[] { "libhostfxr.so", "libcoreclr.so", "libhostpolicy.so" })]
[InlineData("ubuntu.14.04-x64", "", new string[] { "libhostfxr.so", "libcoreclr.so", "libhostpolicy.so" })]
[InlineData("win7-x64", ".exe", new string[] { "hostfxr.dll", "coreclr.dll", "hostpolicy.dll" })]
[InlineData("osx.10.11-x64", "", new string[] { "libhostfxr.dylib", "libcoreclr.dylib", "libhostpolicy.dylib" })]
public void CrossPublishingSucceedsAndHasExpectedArtifacts(string rid, string hostExtension, string[] expectedArtifacts)
{
var testNugetCache = "packages_cross_publish_test";
TestInstance instance = GetTestGroupTestAssetsManager("CrossPublishTestProjects")
.CreateTestInstance("StandaloneAppCrossPublish");
var testProject = Path.Combine(instance.TestRoot, "project.json");
var restoreCommand = new RestoreCommand();
restoreCommand.WorkingDirectory = Path.GetDirectoryName(testProject);
restoreCommand.Environment["NUGET_PACKAGES"] = testNugetCache;
restoreCommand.Execute().Should().Pass();
var buildCommand = new BuildCommand(testProject, runtime: rid);
buildCommand.WorkingDirectory = Path.GetDirectoryName(testProject);
buildCommand.Environment["NUGET_PACKAGES"] = testNugetCache;
buildCommand.Execute().Should().Pass();
var publishCommand = new PublishCommand(testProject, runtime: rid, noBuild: true);
publishCommand.Environment["NUGET_PACKAGES"] = testNugetCache;
publishCommand.WorkingDirectory = Path.GetDirectoryName(testProject);
publishCommand.Execute().Should().Pass();
var publishedDir = publishCommand.GetOutputDirectory();
publishedDir.Should().HaveFile("StandaloneAppCrossPublish"+ hostExtension);
foreach (var artifact in expectedArtifacts)
{
publishedDir.Should().HaveFile(artifact);
}
}
[Fact]
public void LibraryPublishTest()
{