Update dotnet-build to produce portable layout

dotnet-build will produce a deps file for portable builds, and will now
create "runnable" outputs for RID-less targets

the outputs won't actually be runnable today because we need corehost
changes and to generate a deps.json file for corehost to use.
This commit is contained in:
Andrew Stanton-Nurse 2016-03-01 17:42:44 -08:00
parent 444e4f9fd7
commit 7cc90d9ad1
22 changed files with 334 additions and 99 deletions

View file

@ -0,0 +1,3 @@
public static class Thingy
{
}

View file

@ -0,0 +1,16 @@
{
"dependencies": {
},
"frameworks": {
"net45": {},
"netstandardapp1.5": {
"imports": [
"dnxcore50",
"portable-net45+win8"
],
"dependencies": {
"NETStandard.Library": "1.0.0-rc2-23901"
}
}
}
}

View file

@ -0,0 +1,3 @@
public static class Thingy
{
}

View file

@ -0,0 +1,20 @@
{
"dependencies": { },
"frameworks": {
"netstandardapp1.5": {
"imports": [
"dnxcore50",
"portable-net45+win8"
],
"dependencies": {
"NETStandard.Library": "1.0.0-rc2-23901"
}
}
},
"runtimes": {
"win7-x64": {},
"osx.10.10-x64": {},
"ubuntu.14.04-x64": {},
"centos.7-x64": {}
}
}

View file

@ -6,11 +6,16 @@
$oldPath = $env:PATH $oldPath = $env:PATH
try { try {
# Put the stage2 output on the front of the path # Put the stage2 output on the front of the path
$stage2 = "$PSScriptRoot\..\artifacts\win7-x64\stage2\bin" if(!(Get-Command dotnet -ErrorAction SilentlyContinue)) {
throw "You need to have a version of 'dotnet' on your path so we can determine the RID"
}
$rid = dotnet --version | where { $_ -match "^ Runtime Id:\s*(.*)$" } | foreach { $matches[1] }
$stage2 = "$PSScriptRoot\..\artifacts\$rid\stage2\bin"
if (Test-Path $stage2) { if (Test-Path $stage2) {
$env:PATH="$stage2;$env:PATH" $env:PATH="$stage2;$env:PATH"
} else { } else {
Write-Host "You don't have a dev build in the 'artifacts\win7-x64\stage2' folder!" Write-Host "You don't have a dev build in the 'artifacts\$rid\stage2' folder!"
} }
dotnet @args dotnet @args

View file

@ -45,6 +45,13 @@ namespace Microsoft.DotNet.Cli.Build
"Microsoft.Extensions.Testing.Abstractions" "Microsoft.Extensions.Testing.Abstractions"
}; };
// Updates the stage 2 with recent changes.
[Target(nameof(PrepareTargets.Init), nameof(CompileStage2))]
public static BuildTargetResult UpdateBuild(BuildTargetContext c)
{
return c.Success();
}
[Target(nameof(PrepareTargets.Init), nameof(CompileCoreHost), nameof(CompileStage1), nameof(CompileStage2))] [Target(nameof(PrepareTargets.Init), nameof(CompileCoreHost), nameof(CompileStage1), nameof(CompileStage2))]
public static BuildTargetResult Compile(BuildTargetContext c) public static BuildTargetResult Compile(BuildTargetContext c)
{ {

View file

@ -19,8 +19,9 @@
"netstandard1.3": { "netstandard1.3": {
"imports": "dnxcore50", "imports": "dnxcore50",
"dependencies": { "dependencies": {
"NETStandard.Library": "1.0.0-rc2-23901" "NETStandard.Library": "1.0.0-rc2-23901",
"System.Diagnostics.Process": "4.1.0-rc2-23901"
} }
} }
} }
} }

View file

@ -39,11 +39,6 @@ namespace Microsoft.Dotnet.Cli.Compiler.Common
public void MakeCompilationOutputRunnable() public void MakeCompilationOutputRunnable()
{ {
if (string.IsNullOrEmpty(_context.RuntimeIdentifier))
{
throw new InvalidOperationException($"Can not make output runnable for framework {_context.TargetFramework}, because it doesn't have runtime target");
}
CopyContentFiles(); CopyContentFiles();
ExportRuntimeAssets(); ExportRuntimeAssets();
} }
@ -72,8 +67,11 @@ namespace Microsoft.Dotnet.Cli.Compiler.Common
{ {
WriteDepsFileAndCopyProjectDependencies(_exporter); WriteDepsFileAndCopyProjectDependencies(_exporter);
// TODO: Pick a host based on the RID if (!string.IsNullOrEmpty(_context.RuntimeIdentifier))
CoreHost.CopyTo(_runtimeOutputPath, _context.ProjectFile.Name + Constants.ExeSuffix); {
// TODO: Pick a host based on the RID
CoreHost.CopyTo(_runtimeOutputPath, _context.ProjectFile.Name + Constants.ExeSuffix);
}
} }
private void CopyContentFiles() private void CopyContentFiles()

View file

@ -48,12 +48,18 @@ namespace Microsoft.DotNet.ProjectModel
{ {
if (!string.IsNullOrEmpty(runtimeIdentifier)) if (!string.IsNullOrEmpty(runtimeIdentifier))
{ {
runtimeOutputPath= PathUtility.EnsureTrailingSlash(Path.Combine(compilationOutputPath, runtimeIdentifier)); runtimeOutputPath = PathUtility.EnsureTrailingSlash(Path.Combine(compilationOutputPath, runtimeIdentifier));
}
else
{
// "Runtime" assets (i.e. the deps file) will be dropped to the compilation output path, because
// we are building a RID-less target.
runtimeOutputPath = compilationOutputPath;
} }
} }
else else
{ {
runtimeOutputPath= PathUtility.EnsureTrailingSlash(Path.GetFullPath(outputPath)); runtimeOutputPath = PathUtility.EnsureTrailingSlash(Path.GetFullPath(outputPath));
} }
var intermediateOutputPath = PathUtility.EnsureTrailingSlash(Path.Combine( var intermediateOutputPath = PathUtility.EnsureTrailingSlash(Path.Combine(

View file

@ -35,7 +35,7 @@ namespace Microsoft.DotNet.ProjectModel
return Path.GetDirectoryName(ProjectFilePath); return Path.GetDirectoryName(ProjectFilePath);
} }
} }
public AnalyzerOptions AnalyzerOptions { get; set; } public AnalyzerOptions AnalyzerOptions { get; set; }
public string Name { get; set; } public string Name { get; set; }
@ -124,12 +124,12 @@ namespace Microsoft.DotNet.ProjectModel
public bool HasRuntimeOutput(string configuration) public bool HasRuntimeOutput(string configuration)
{ {
var compilationOptions = GetCompilerOptions(targetFramework: null, configurationName: configuration); var compilationOptions = GetCompilerOptions(targetFramework: null, configurationName: configuration);
// TODO: Make this opt in via another mechanism // TODO: Make this opt in via another mechanism
return compilationOptions.EmitEntryPoint.GetValueOrDefault() || IsTestProject; return compilationOptions.EmitEntryPoint.GetValueOrDefault() || IsTestProject;
} }
private CommonCompilerOptions GetCompilerOptions() private CommonCompilerOptions GetCompilerOptions()
{ {
return _defaultCompilerOptions; return _defaultCompilerOptions;

View file

@ -83,7 +83,7 @@ namespace Microsoft.DotNet.ProjectModel
.WithRuntimeIdentifiers(runtimeIdentifiers) .WithRuntimeIdentifiers(runtimeIdentifiers)
.Build(); .Build();
} }
public static ProjectContextBuilder CreateBuilder(string projectPath, NuGetFramework framework) public static ProjectContextBuilder CreateBuilder(string projectPath, NuGetFramework framework)
{ {
if (projectPath.EndsWith(Project.FileName)) if (projectPath.EndsWith(Project.FileName))
@ -120,15 +120,12 @@ namespace Microsoft.DotNet.ProjectModel
/// <summary> /// <summary>
/// Creates a project context for each target located in the project at <paramref name="projectPath"/> /// Creates a project context for each target located in the project at <paramref name="projectPath"/>
/// </summary> /// </summary>
public static IEnumerable<ProjectContext> CreateContextForEachTarget(string projectPath) public static IEnumerable<ProjectContext> CreateContextForEachTarget(string projectPath, ProjectReaderSettings settings = null)
{ {
if (!projectPath.EndsWith(Project.FileName))
{
projectPath = Path.Combine(projectPath, Project.FileName);
}
var project = ProjectReader.GetProject(projectPath); var project = ProjectReader.GetProject(projectPath);
return new ProjectContextBuilder() return new ProjectContextBuilder()
.WithReaderSettings(settings)
.WithProject(project) .WithProject(project)
.BuildAllTargets(); .BuildAllTargets();
} }
@ -143,15 +140,20 @@ namespace Microsoft.DotNet.ProjectModel
public ProjectContext CreateRuntimeContext(IEnumerable<string> runtimeIdentifiers) public ProjectContext CreateRuntimeContext(IEnumerable<string> runtimeIdentifiers)
{ {
var context = Create(ProjectFile.ProjectFilePath, TargetFramework, runtimeIdentifiers); // Check if there are any runtime targets (i.e. are we portable)
if (context.RuntimeIdentifier == null) var standalone = LockFile.Targets
.Where(t => t.TargetFramework.Equals(TargetFramework))
.Any(t => !string.IsNullOrEmpty(t.RuntimeIdentifier));
var context = Create(ProjectFile.ProjectFilePath, TargetFramework, standalone ? runtimeIdentifiers : Enumerable.Empty<string>());
if (standalone && context.RuntimeIdentifier == null)
{ {
// We are standalone, but don't support this runtime
var rids = string.Join(", ", runtimeIdentifiers); var rids = string.Join(", ", runtimeIdentifiers);
throw new InvalidOperationException($"Can not find runtime target for framework '{TargetFramework}' and RID's '{rids}'. " + throw new InvalidOperationException($"Can not find runtime target for framework '{TargetFramework}' compatible with one of the target runtimes: '{rids}'. " +
"Possible causes:" + Environment.NewLine + "Possible causes:" + Environment.NewLine +
"1. Project is not restored or restore failed - run `dotnet restore`" + Environment.NewLine + "1. The project has not been restored or restore failed - run `dotnet restore`" + Environment.NewLine +
"2. Project is not targeting `runable` framework (`netstandardapp*` or `net*`)" $"2. The project does not list one of '{rids}' in the 'runtimes' section.");
);
} }
return context; return context;
} }

View file

@ -61,18 +61,20 @@ namespace Microsoft.DotNet.ProjectModel
return true; return true;
} }
public static Project GetProject(string projectFile, ProjectReaderSettings settings = null) public static Project GetProject(string projectPath, ProjectReaderSettings settings = null) => GetProject(projectPath, new List<DiagnosticMessage>(), settings);
{
return GetProject(projectFile, new List<DiagnosticMessage>(), settings);
}
public static Project GetProject(string projectFile, ICollection<DiagnosticMessage> diagnostics, ProjectReaderSettings settings = null) public static Project GetProject(string projectPath, ICollection<DiagnosticMessage> diagnostics, ProjectReaderSettings settings = null)
{ {
var name = Path.GetFileName(Path.GetDirectoryName(projectFile)); if (!projectPath.EndsWith(Project.FileName))
using (var stream = new FileStream(projectFile, FileMode.Open, FileAccess.Read, FileShare.Read))
{ {
return new ProjectReader().ReadProject(stream, name, projectFile, diagnostics, settings); projectPath = Path.Combine(projectPath, Project.FileName);
}
var name = Path.GetFileName(Path.GetDirectoryName(projectPath));
using (var stream = new FileStream(projectPath, FileMode.Open, FileAccess.Read, FileShare.Read))
{
return new ProjectReader().ReadProject(stream, name, projectPath, diagnostics, settings);
} }
} }
@ -546,7 +548,8 @@ namespace Microsoft.DotNet.ProjectModel
analyzerOptions.LanguageId = languageId; analyzerOptions.LanguageId = languageId;
break; break;
default:; default:
;
throw FileFormatException.Create( throw FileFormatException.Create(
$"Unrecognized analyzerOption key: {key}", $"Unrecognized analyzerOption key: {key}",
project.ProjectFilePath); project.ProjectFilePath);

View file

@ -48,7 +48,7 @@ namespace Microsoft.DotNet.Tools.Build
{ {
CreateOutputDirectories(); CreateOutputDirectories();
return CompileDendencies(incremental) && CompileRootProject(incremental); return CompileDependencies(incremental) && CompileRootProject(incremental);
} }
private bool CompileRootProject(bool incremental) private bool CompileRootProject(bool incremental)
@ -66,7 +66,7 @@ namespace Microsoft.DotNet.Tools.Build
return success; return success;
} }
private bool CompileDendencies(bool incremental) private bool CompileDependencies(bool incremental)
{ {
if (_args.ShouldSkipDependencies) if (_args.ShouldSkipDependencies)
{ {
@ -395,15 +395,7 @@ namespace Microsoft.DotNet.Tools.Build
if (succeeded) if (succeeded)
{ {
if (_rootProject.ProjectFile.HasRuntimeOutput(_args.ConfigValue)) MakeRunnable();
{
MakeRunnable();
}
else if (!string.IsNullOrEmpty(_args.OutputValue))
{
var outputPaths = _rootProject.GetOutputPaths(_args.ConfigValue, _args.BuildBasePathValue, _args.OutputValue);
CopyCompilationOutput(outputPaths);
}
} }
return succeeded; return succeeded;
@ -428,9 +420,22 @@ namespace Microsoft.DotNet.Tools.Build
private void MakeRunnable() private void MakeRunnable()
{ {
var runtimeContext = _rootProject.CreateRuntimeContext(_args.GetRuntimes()); var runtimeContext = _rootProject.CreateRuntimeContext(_args.GetRuntimes());
if(_args.PortableMode)
{
// HACK: Force the use of the portable target
runtimeContext = _rootProject;
}
var outputPaths = runtimeContext.GetOutputPaths(_args.ConfigValue, _args.BuildBasePathValue, _args.OutputValue); var outputPaths = runtimeContext.GetOutputPaths(_args.ConfigValue, _args.BuildBasePathValue, _args.OutputValue);
var libraryExporter = runtimeContext.CreateExporter(_args.ConfigValue, _args.BuildBasePathValue); var libraryExporter = runtimeContext.CreateExporter(_args.ConfigValue, _args.BuildBasePathValue);
CopyCompilationOutput(outputPaths);
// If we're building for a specific RID, we need to copy the RID-less compilation output into
// the RID-specific output dir
if (!string.IsNullOrEmpty(runtimeContext.RuntimeIdentifier))
{
CopyCompilationOutput(outputPaths);
}
var executable = new Executable(runtimeContext, outputPaths, libraryExporter); var executable = new Executable(runtimeContext, outputPaths, libraryExporter);
executable.MakeCompilationOutputRunnable(); executable.MakeCompilationOutputRunnable();

View file

@ -3,6 +3,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using Microsoft.Dnx.Runtime.Common.CommandLine; using Microsoft.Dnx.Runtime.Common.CommandLine;
@ -28,6 +29,7 @@ namespace Microsoft.DotNet.Tools.Compiler
private CommandOption _runtimeOption; private CommandOption _runtimeOption;
private CommandOption _versionSuffixOption; private CommandOption _versionSuffixOption;
private CommandOption _configurationOption; private CommandOption _configurationOption;
private CommandOption _portableOption;
private CommandArgument _projectArgument; private CommandArgument _projectArgument;
private CommandOption _nativeOption; private CommandOption _nativeOption;
private CommandOption _archOption; private CommandOption _archOption;
@ -41,8 +43,8 @@ namespace Microsoft.DotNet.Tools.Compiler
// resolved values for the options and arguments // resolved values for the options and arguments
public string ProjectPathValue { get; set; } public string ProjectPathValue { get; set; }
public string BuildBasePathValue { get; set; } public string BuildBasePathValue { get; set; }
public string OutputValue { get; set; }
public string RuntimeValue { get; set; } public string RuntimeValue { get; set; }
public string OutputValue { get; set; }
public string VersionSuffixValue { get; set; } public string VersionSuffixValue { get; set; }
public string ConfigValue { get; set; } public string ConfigValue { get; set; }
public bool IsNativeValue { get; set; } public bool IsNativeValue { get; set; }
@ -53,6 +55,7 @@ namespace Microsoft.DotNet.Tools.Compiler
public bool IsCppModeValue { get; set; } public bool IsCppModeValue { get; set; }
public string AppDepSdkPathValue { get; set; } public string AppDepSdkPathValue { get; set; }
public string CppCompilerFlagsValue { get; set; } public string CppCompilerFlagsValue { get; set; }
public bool PortableMode { get; set; }
// workaround: CommandLineApplication is internal therefore I cannot make _app protected so baseclasses can add their own params // workaround: CommandLineApplication is internal therefore I cannot make _app protected so baseclasses can add their own params
private readonly Dictionary<string, CommandOption> baseClassOptions; private readonly Dictionary<string, CommandOption> baseClassOptions;
@ -77,12 +80,15 @@ namespace Microsoft.DotNet.Tools.Compiler
_outputOption = _app.Option("-o|--output <OUTPUT_DIR>", "Directory in which to place outputs", CommandOptionType.SingleValue); _outputOption = _app.Option("-o|--output <OUTPUT_DIR>", "Directory in which to place outputs", CommandOptionType.SingleValue);
_buildBasePath = _app.Option("-b|--build-base-path <OUTPUT_DIR>", "Directory in which to place temporary outputs", CommandOptionType.SingleValue); _buildBasePath = _app.Option("-b|--build-base-path <OUTPUT_DIR>", "Directory in which to place temporary outputs", CommandOptionType.SingleValue);
_frameworkOption = _app.Option("-f|--framework <FRAMEWORK>", "Compile a specific framework", CommandOptionType.MultipleValue); _frameworkOption = _app.Option("-f|--framework <FRAMEWORK>", "Compile a specific framework", CommandOptionType.SingleValue);
_runtimeOption = _app.Option("-r|--runtime <RUNTIME_IDENTIFIER>", "Produce runtime-specific assets for the specified runtime", CommandOptionType.SingleValue);
_configurationOption = _app.Option("-c|--configuration <CONFIGURATION>", "Configuration under which to build", CommandOptionType.SingleValue); _configurationOption = _app.Option("-c|--configuration <CONFIGURATION>", "Configuration under which to build", CommandOptionType.SingleValue);
_runtimeOption = _app.Option("-r|--runtime <RUNTIME_IDENTIFIER>", "Target runtime to publish for", CommandOptionType.SingleValue);
_versionSuffixOption = _app.Option("--version-suffix <VERSION_SUFFIX>", "Defines what `*` should be replaced with in version field in project.json", CommandOptionType.SingleValue); _versionSuffixOption = _app.Option("--version-suffix <VERSION_SUFFIX>", "Defines what `*` should be replaced with in version field in project.json", CommandOptionType.SingleValue);
_projectArgument = _app.Argument("<PROJECT>", "The project to compile, defaults to the current directory. Can be a path to a project.json or a project directory"); _projectArgument = _app.Argument("<PROJECT>", "The project to compile, defaults to the current directory. Can be a path to a project.json or a project directory");
// HACK: Allow us to treat a project as though it was portable by ignoring the runtime-specific targets. This is temporary until RID inference is removed from NuGet
_portableOption = _app.Option("--portable", "TEMPORARY: Enforces portable build/publish mode", CommandOptionType.NoValue);
// Native Args // Native Args
_nativeOption = _app.Option("-n|--native", "Compiles source to native machine code.", CommandOptionType.NoValue); _nativeOption = _app.Option("-n|--native", "Compiles source to native machine code.", CommandOptionType.NoValue);
_archOption = _app.Option("-a|--arch <ARCH>", "The architecture for which to compile. x64 only currently supported.", CommandOptionType.SingleValue); _archOption = _app.Option("-a|--arch <ARCH>", "The architecture for which to compile. x64 only currently supported.", CommandOptionType.SingleValue);
@ -98,6 +104,12 @@ namespace Microsoft.DotNet.Tools.Compiler
{ {
_app.OnExecute(() => _app.OnExecute(() =>
{ {
if (_outputOption.HasValue() && !_frameworkOption.HasValue())
{
Reporter.Error.WriteLine("When the '--output' option is provided, the '--framework' option must also be provided.");
return 1;
}
// Locate the project and get the name and full path // Locate the project and get the name and full path
ProjectPathValue = _projectArgument.Value; ProjectPathValue = _projectArgument.Value;
if (string.IsNullOrEmpty(ProjectPathValue)) if (string.IsNullOrEmpty(ProjectPathValue))
@ -110,6 +122,7 @@ namespace Microsoft.DotNet.Tools.Compiler
ConfigValue = _configurationOption.Value() ?? Constants.DefaultConfiguration; ConfigValue = _configurationOption.Value() ?? Constants.DefaultConfiguration;
RuntimeValue = _runtimeOption.Value(); RuntimeValue = _runtimeOption.Value();
VersionSuffixValue = _versionSuffixOption.Value(); VersionSuffixValue = _versionSuffixOption.Value();
PortableMode = _portableOption.HasValue();
IsNativeValue = _nativeOption.HasValue(); IsNativeValue = _nativeOption.HasValue();
ArchValue = _archOption.Value(); ArchValue = _archOption.Value();
@ -120,8 +133,6 @@ namespace Microsoft.DotNet.Tools.Compiler
IsCppModeValue = _cppModeOption.HasValue(); IsCppModeValue = _cppModeOption.HasValue();
CppCompilerFlagsValue = _cppCompilerFlagsOption.Value(); CppCompilerFlagsValue = _cppCompilerFlagsOption.Value();
IEnumerable<ProjectContext> contexts;
// Set defaults based on the environment // Set defaults based on the environment
var settings = ProjectReaderSettings.ReadFromEnvironment(); var settings = ProjectReaderSettings.ReadFromEnvironment();
@ -130,29 +141,35 @@ namespace Microsoft.DotNet.Tools.Compiler
settings.VersionSuffix = VersionSuffixValue; settings.VersionSuffix = VersionSuffixValue;
} }
if (_frameworkOption.HasValue()) // Load the project file and construct all the targets
var targets = ProjectContext.CreateContextForEachFramework(ProjectPathValue, settings).ToList();
if (targets.Count == 0)
{ {
contexts = _frameworkOption.Values // Project is missing 'frameworks' section
.Select(f => Reporter.Error.WriteLine("Project does not have any frameworks listed in the 'frameworks' section.");
{ return 1;
return ProjectContext.CreateBuilder(ProjectPathValue, NuGetFramework.Parse(f))
.WithReaderSettings(settings)
.Build();
});
}
else
{
if (!string.IsNullOrEmpty(OutputValue))
{
throw new InvalidOperationException($"'{_frameworkOption.LongName}' is required when '{_outputOption.LongName}' is specified");
}
else
{
contexts = ProjectContext.CreateContextForEachFramework(ProjectPathValue, settings);
}
} }
var success = execute(contexts.ToList(), this); // Filter the targets down based on the inputs
if (_frameworkOption.HasValue())
{
var fx = NuGetFramework.Parse(_frameworkOption.Value());
targets = targets.Where(t => fx.Equals(t.TargetFramework)).ToList();
if (targets.Count == 0)
{
// We filtered everything out
Reporter.Error.WriteLine($"Project does not support framework: {fx.DotNetFrameworkName}.");
return 1;
}
Debug.Assert(targets.Count == 1);
}
Debug.Assert(targets.All(t => string.IsNullOrEmpty(t.RuntimeIdentifier)));
var success = execute(targets, this);
return success ? 0 : 1; return success ? 0 : 1;
}); });

View file

@ -9,7 +9,6 @@ using Microsoft.DotNet.Cli.Compiler.Common;
using Microsoft.DotNet.Cli.Utils; using Microsoft.DotNet.Cli.Utils;
using Microsoft.DotNet.ProjectModel; using Microsoft.DotNet.ProjectModel;
using Microsoft.DotNet.ProjectModel.Compilation; using Microsoft.DotNet.ProjectModel.Compilation;
using Microsoft.DotNet.ProjectModel.Utilities;
using Microsoft.Extensions.DependencyModel; using Microsoft.Extensions.DependencyModel;
namespace Microsoft.DotNet.Tools.Compiler namespace Microsoft.DotNet.Tools.Compiler
@ -161,6 +160,10 @@ namespace Microsoft.DotNet.Tools.Compiler
contextVariables.Add( contextVariables.Add(
"compile:RuntimeOutputDir", "compile:RuntimeOutputDir",
runtimeOutputPath.RuntimeOutputPath.TrimEnd('\\', '/')); runtimeOutputPath.RuntimeOutputPath.TrimEnd('\\', '/'));
contextVariables.Add(
"compile:RuntimeIdentifier",
runtimeContext.RuntimeIdentifier);
} }
_scriptRunner.RunScripts(context, ScriptNames.PreCompile, contextVariables); _scriptRunner.RunScripts(context, ScriptNames.PreCompile, contextVariables);

View file

@ -27,5 +27,5 @@
"testRunner": "xunit", "testRunner": "xunit",
"scripts": { "precompile": "dotnet build ../ArgumentsReflector/project.json --framework netstandardapp1.5 --output %compile:RuntimeOutputDir%" } "scripts": { "precompile": "dotnet build ../ArgumentsReflector/project.json --framework netstandardapp1.5 --runtime %compile:RuntimeIdentifier% --output %compile:RuntimeOutputDir%" }
} }

View file

@ -60,7 +60,14 @@ namespace Microsoft.DotNet.Tools.Test.Utilities
return new AndConstraint<CommandResultAssertions>(this); return new AndConstraint<CommandResultAssertions>(this);
} }
public AndConstraint<CommandResultAssertions> StdOutMatchPattern(string pattern, RegexOptions options = RegexOptions.None) public AndConstraint<CommandResultAssertions> HaveStdOutContaining(string pattern)
{
Execute.Assertion.ForCondition(_commandResult.StdOut.Contains(pattern))
.FailWith(AppendDiagnosticsTo($"The command output did not contain expected result: {pattern}{Environment.NewLine}"));
return new AndConstraint<CommandResultAssertions>(this);
}
public AndConstraint<CommandResultAssertions> HaveStdOutMatching(string pattern, RegexOptions options = RegexOptions.None)
{ {
Execute.Assertion.ForCondition(Regex.Match(_commandResult.StdOut, pattern, options).Success) Execute.Assertion.ForCondition(Regex.Match(_commandResult.StdOut, pattern, options).Success)
.FailWith(AppendDiagnosticsTo($"Matching the command output failed. Pattern: {pattern}{Environment.NewLine}")); .FailWith(AppendDiagnosticsTo($"Matching the command output failed. Pattern: {pattern}{Environment.NewLine}"));
@ -74,6 +81,20 @@ namespace Microsoft.DotNet.Tools.Test.Utilities
return new AndConstraint<CommandResultAssertions>(this); return new AndConstraint<CommandResultAssertions>(this);
} }
public AndConstraint<CommandResultAssertions> HaveStdErrContaining(string pattern)
{
Execute.Assertion.ForCondition(_commandResult.StdErr.Contains(pattern))
.FailWith(AppendDiagnosticsTo($"The command error output did not contain expected result: {pattern}{Environment.NewLine}"));
return new AndConstraint<CommandResultAssertions>(this);
}
public AndConstraint<CommandResultAssertions> HaveStdErrMatching(string pattern, RegexOptions options = RegexOptions.None)
{
Execute.Assertion.ForCondition(Regex.Match(_commandResult.StdErr, pattern, options).Success)
.FailWith(AppendDiagnosticsTo($"Matching the command error output failed. Pattern: {pattern}{Environment.NewLine}"));
return new AndConstraint<CommandResultAssertions>(this);
}
public AndConstraint<CommandResultAssertions> NotHaveStdOut() public AndConstraint<CommandResultAssertions> NotHaveStdOut()
{ {
Execute.Assertion.ForCondition(string.IsNullOrEmpty(_commandResult.StdOut)) Execute.Assertion.ForCondition(string.IsNullOrEmpty(_commandResult.StdOut))

View file

@ -11,23 +11,25 @@ namespace Microsoft.DotNet.Tools.Test.Utilities
public sealed class BuildCommand : TestCommand public sealed class BuildCommand : TestCommand
{ {
private Project _project; private Project _project;
private string _projectPath; private readonly string _projectPath;
private string _outputDirectory; private readonly string _outputDirectory;
private string _buidBasePathDirectory; private readonly string _buidBasePathDirectory;
private string _configuration; private readonly string _configuration;
private string _framework; private readonly string _framework;
private string _versionSuffix; private readonly string _versionSuffix;
private bool _noHost; private readonly bool _noHost;
private bool _native; private readonly bool _native;
private string _architecture; private readonly string _architecture;
private string _ilcArgs; private readonly string _ilcArgs;
private string _ilcPath; private readonly string _ilcPath;
private string _appDepSDKPath; private readonly string _appDepSDKPath;
private bool _nativeCppMode; private readonly bool _nativeCppMode;
private string _cppCompilerFlags; private readonly string _cppCompilerFlags;
private bool _buildProfile; private readonly bool _buildProfile;
private bool _noIncremental; private readonly bool _noIncremental;
private bool _noDependencies; private readonly bool _noDependencies;
private readonly string _runtime;
private readonly bool _forcePortable;
private string OutputOption private string OutputOption
{ {
@ -39,6 +41,16 @@ namespace Microsoft.DotNet.Tools.Test.Utilities
} }
} }
private string ForcePortableOption
{
get
{
return _forcePortable ?
"--portable" :
string.Empty;
}
}
private string BuildBasePathOption private string BuildBasePathOption
{ {
get get
@ -67,7 +79,7 @@ namespace Microsoft.DotNet.Tools.Test.Utilities
$"--framework {_framework}"; $"--framework {_framework}";
} }
} }
private string VersionSuffixOption private string VersionSuffixOption
{ {
get get
@ -98,6 +110,16 @@ namespace Microsoft.DotNet.Tools.Test.Utilities
} }
} }
private string RuntimeOption
{
get
{
return _runtime == string.Empty ?
"" :
$"--runtime {_runtime}";
}
}
private string ArchitectureOption private string ArchitectureOption
{ {
get get
@ -194,6 +216,7 @@ namespace Microsoft.DotNet.Tools.Test.Utilities
string buidBasePath="", string buidBasePath="",
string configuration="", string configuration="",
string framework="", string framework="",
string runtime="",
string versionSuffix="", string versionSuffix="",
bool noHost=false, bool noHost=false,
bool native=false, bool native=false,
@ -205,7 +228,8 @@ namespace Microsoft.DotNet.Tools.Test.Utilities
string cppCompilerFlags="", string cppCompilerFlags="",
bool buildProfile=true, bool buildProfile=true,
bool noIncremental=false, bool noIncremental=false,
bool noDependencies=false bool noDependencies=false,
bool forcePortable=false
) )
: base("dotnet") : base("dotnet")
{ {
@ -217,6 +241,7 @@ namespace Microsoft.DotNet.Tools.Test.Utilities
_configuration = configuration; _configuration = configuration;
_versionSuffix = versionSuffix; _versionSuffix = versionSuffix;
_framework = framework; _framework = framework;
_runtime = runtime;
_noHost = noHost; _noHost = noHost;
_native = native; _native = native;
_architecture = architecture; _architecture = architecture;
@ -228,6 +253,7 @@ namespace Microsoft.DotNet.Tools.Test.Utilities
_buildProfile = buildProfile; _buildProfile = buildProfile;
_noIncremental = noIncremental; _noIncremental = noIncremental;
_noDependencies = noDependencies; _noDependencies = noDependencies;
_forcePortable = forcePortable;
} }
public override CommandResult Execute(string args = "") public override CommandResult Execute(string args = "")
@ -251,7 +277,7 @@ namespace Microsoft.DotNet.Tools.Test.Utilities
private string BuildArgs() private string BuildArgs()
{ {
return $"{BuildProfile} {NoDependencies} {NoIncremental} \"{_projectPath}\" {OutputOption} {BuildBasePathOption} {ConfigurationOption} {FrameworkOption} {VersionSuffixOption} {NoHostOption} {NativeOption} {ArchitectureOption} {IlcArgsOption} {IlcPathOption} {AppDepSDKPathOption} {NativeCppModeOption} {CppCompilerFlagsOption}"; return $"{BuildProfile} {ForcePortableOption} {NoDependencies} {NoIncremental} \"{_projectPath}\" {OutputOption} {BuildBasePathOption} {ConfigurationOption} {FrameworkOption} {RuntimeOption} {VersionSuffixOption} {NoHostOption} {NativeOption} {ArchitectureOption} {IlcArgsOption} {IlcPathOption} {AppDepSDKPathOption} {NativeCppModeOption} {CppCompilerFlagsOption}";
} }
} }
} }

View file

@ -0,0 +1,55 @@
using System.IO;
using Microsoft.DotNet.Tools.Test.Utilities;
using Xunit;
namespace Microsoft.DotNet.Tools.Builder.Tests
{
public class BuildInvalidArgumentsTests : TestBase
{
[Fact]
public void ErrorOccursWhenBuildingPortableProjectToSpecificOutputPathWithoutSpecifyingFramework()
{
var testInstance = TestAssetsManager.CreateTestInstance("BuildTestPortableProject")
.WithLockFiles();
var result = new BuildCommand(
projectPath: testInstance.TestRoot,
output: Path.Combine(testInstance.TestRoot, "out"))
.ExecuteWithCapturedOutput();
result.Should().Fail();
result.Should().HaveStdErrContaining("When the '--output' option is provided, the '--framework' option must also be provided.");
}
[Fact]
public void ErrorOccursWhenBuildingPortableProjectAndSpecifyingFrameworkThatProjectDoesNotSupport()
{
var testInstance = TestAssetsManager.CreateTestInstance("BuildTestPortableProject")
.WithLockFiles();
var result = new BuildCommand(
projectPath: testInstance.TestRoot,
output: Path.Combine(testInstance.TestRoot, "out"),
framework: "sl40")
.ExecuteWithCapturedOutput();
result.Should().Fail();
result.Should().HaveStdErrContaining("Project does not support framework: Silverlight,Version=v4.0.");
}
[Fact]
public void ErrorOccursWhenBuildingStandaloneProjectToSpecificOutputPathWithoutSpecifyingFramework()
{
var testInstance = TestAssetsManager.CreateTestInstance("BuildTestStandaloneProject")
.WithLockFiles();
var result = new BuildCommand(
projectPath: testInstance.TestRoot,
output: Path.Combine(testInstance.TestRoot, "out"))
.ExecuteWithCapturedOutput();
result.Should().Fail();
result.Should().HaveStdErrContaining("When the '--output' option is provided, the '--framework' option must also be provided.");
}
}
}

View file

@ -0,0 +1,44 @@
using System.IO;
using Microsoft.DotNet.Tools.Test.Utilities;
using Xunit;
namespace Microsoft.DotNet.Tools.Builder.Tests
{
public class BuildPortableTests : TestBase
{
[Fact]
public void BuildingAPortableProjectProducesDepsFile()
{
var testInstance = TestAssetsManager.CreateTestInstance("BuildTestPortableProject")
.WithLockFiles();
var result = new BuildCommand(
projectPath: testInstance.TestRoot,
forcePortable: true)
.ExecuteWithCapturedOutput();
result.Should().Pass();
var outputBase = new DirectoryInfo(Path.Combine(testInstance.TestRoot, "bin", "Debug"));
var netstandardappOutput = outputBase.Sub("netstandardapp1.5");
var fxSubdirs = new[] {
netstandardappOutput,
outputBase.Sub("net45")
};
foreach(var fxSubdir in fxSubdirs)
{
fxSubdir.Should()
.Exist().And
.HaveFiles(new[]
{
"BuildTestPortableProject.dll",
"BuildTestPortableProject.pdb"
});
}
netstandardappOutput.Should().HaveFile("BuildTestPortableProject.deps");
}
}
}

View file

@ -75,7 +75,7 @@ namespace Microsoft.DotNet.Tools.Builder.Tests
// second build with no dependencies and no incremental; only the root rebuilds // second build with no dependencies and no incremental; only the root rebuilds
var result2 = BuildProject(noDependencies: true, noIncremental: true); var result2 = BuildProject(noDependencies: true, noIncremental: true);
result2.Should().StdOutMatchPattern("Compiling.*L0.*"); result2.Should().HaveStdOutMatching("Compiling.*L0.*");
AssertResultDoesNotContainStrings(result2, dependencies); AssertResultDoesNotContainStrings(result2, dependencies);

View file

@ -188,7 +188,7 @@ namespace Microsoft.DotNet.Tools.Publish.Tests
var publishCommand = new PublishCommand(testProject); var publishCommand = new PublishCommand(testProject);
var result = publishCommand.ExecuteWithCapturedOutput(); var result = publishCommand.ExecuteWithCapturedOutput();
result.Should().StdOutMatchPattern("\nprepublish_output( \\?[^%]+\\?){5}.+\npostpublish_output( \\?[^%]+\\?){5}", RegexOptions.Singleline); result.Should().HaveStdOutMatching("\nprepublish_output( \\?[^%]+\\?){5}.+\npostpublish_output( \\?[^%]+\\?){5}", RegexOptions.Singleline);
result.Should().Pass(); result.Should().Pass();
} }
} }