Merge pull request #625 from cdmihai/buildCommandWithSafetyChecks
Introduce --build-profile flag and precondition checking in the Build command
This commit is contained in:
commit
d1a257bff7
22 changed files with 990 additions and 344 deletions
|
@ -1,7 +1,7 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 14
|
||||
VisualStudioVersion = 14.0.23107.0
|
||||
VisualStudioVersion = 14.0.24720.0
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.DotNet.Cli", "src\Microsoft.DotNet.Cli\Microsoft.DotNet.Cli.xproj", "{60CF7E6C-D6C8-439D-B7B7-D8A27E29BE2C}"
|
||||
EndProject
|
||||
|
@ -64,6 +64,8 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.DotNet.ProjectMod
|
|||
EndProject
|
||||
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.DotNet.ProjectModel.Server.Tests", "test\Microsoft.DotNet.ProjectModel.Server.Tests\Microsoft.DotNet.ProjectModel.Server.Tests.xproj", "{11C77123-E4DA-499F-8900-80C88C2C69F2}"
|
||||
EndProject
|
||||
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.DotNet.Tools.Builder", "src\Microsoft.DotNet.Tools.Builder\Microsoft.DotNet.Tools.Builder.xproj", "{0A309227-A9D8-4DDF-88DD-326B57B04379}"
|
||||
EndProject
|
||||
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.Extensions.DependencyModel", "src\Microsoft.Extensions.DependencyModel\Microsoft.Extensions.DependencyModel.xproj", "{688870C8-9843-4F9E-8576-D39290AD0F25}"
|
||||
EndProject
|
||||
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.DotNet.Tools.Compiler.Fsc", "src\Microsoft.DotNet.Tools.Compiler.Fsc\Microsoft.DotNet.Tools.Compiler.Fsc.xproj", "{74F25188-BF63-4BF3-879B-B6CDB11ED608}"
|
||||
|
@ -448,6 +450,22 @@ Global
|
|||
{08A68C6A-86F6-4ED2-89A7-B166D33E9F85}.RelWithDebInfo|Any CPU.Build.0 = Release|Any CPU
|
||||
{08A68C6A-86F6-4ED2-89A7-B166D33E9F85}.RelWithDebInfo|x64.ActiveCfg = Release|Any CPU
|
||||
{08A68C6A-86F6-4ED2-89A7-B166D33E9F85}.RelWithDebInfo|x64.Build.0 = Release|Any CPU
|
||||
{0A309227-A9D8-4DDF-88DD-326B57B04379}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{0A309227-A9D8-4DDF-88DD-326B57B04379}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{0A309227-A9D8-4DDF-88DD-326B57B04379}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{0A309227-A9D8-4DDF-88DD-326B57B04379}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{0A309227-A9D8-4DDF-88DD-326B57B04379}.MinSizeRel|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{0A309227-A9D8-4DDF-88DD-326B57B04379}.MinSizeRel|Any CPU.Build.0 = Debug|Any CPU
|
||||
{0A309227-A9D8-4DDF-88DD-326B57B04379}.MinSizeRel|x64.ActiveCfg = Debug|Any CPU
|
||||
{0A309227-A9D8-4DDF-88DD-326B57B04379}.MinSizeRel|x64.Build.0 = Debug|Any CPU
|
||||
{0A309227-A9D8-4DDF-88DD-326B57B04379}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{0A309227-A9D8-4DDF-88DD-326B57B04379}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{0A309227-A9D8-4DDF-88DD-326B57B04379}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{0A309227-A9D8-4DDF-88DD-326B57B04379}.Release|x64.Build.0 = Release|Any CPU
|
||||
{0A309227-A9D8-4DDF-88DD-326B57B04379}.RelWithDebInfo|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{0A309227-A9D8-4DDF-88DD-326B57B04379}.RelWithDebInfo|Any CPU.Build.0 = Release|Any CPU
|
||||
{0A309227-A9D8-4DDF-88DD-326B57B04379}.RelWithDebInfo|x64.ActiveCfg = Release|Any CPU
|
||||
{0A309227-A9D8-4DDF-88DD-326B57B04379}.RelWithDebInfo|x64.Build.0 = Release|Any CPU
|
||||
{688870C8-9843-4F9E-8576-D39290AD0F25}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{688870C8-9843-4F9E-8576-D39290AD0F25}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{688870C8-9843-4F9E-8576-D39290AD0F25}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
|
@ -540,6 +558,7 @@ Global
|
|||
{7A75ACC4-3C2F-44E1-B492-0EC08704E9FF} = {17735A9D-BFD9-4585-A7CB-3208CA6EA8A7}
|
||||
{BC765FBF-AD7A-4A99-9902-5540C5A74181} = {ED2FE3E2-F7E7-4389-8231-B65123F2076F}
|
||||
{08A68C6A-86F6-4ED2-89A7-B166D33E9F85} = {0722D325-24C8-4E83-B5AF-0A083E7F0749}
|
||||
{0A309227-A9D8-4DDF-88DD-326B57B04379} = {ED2FE3E2-F7E7-4389-8231-B65123F2076F}
|
||||
{688870C8-9843-4F9E-8576-D39290AD0F25} = {ED2FE3E2-F7E7-4389-8231-B65123F2076F}
|
||||
{74F25188-BF63-4BF3-879B-B6CDB11ED608} = {ED2FE3E2-F7E7-4389-8231-B65123F2076F}
|
||||
{1EA9AF94-5494-40DD-A05B-9D564572CCFC} = {ED2FE3E2-F7E7-4389-8231-B65123F2076F}
|
||||
|
|
|
@ -13,6 +13,7 @@ chmod -R 755 $INSTALL_DESTINATION
|
|||
|
||||
ln -s $INSTALL_DESTINATION/bin/dotnet /usr/local/bin/
|
||||
ln -s $INSTALL_DESTINATION/bin/dotnet-compile /usr/local/bin/
|
||||
ln -s $INSTALL_DESTINATION/bin/dotnet-build /usr/local/bin/
|
||||
ln -s $INSTALL_DESTINATION/bin/dotnet-compile-csc /usr/local/bin/
|
||||
ln -s $INSTALL_DESTINATION/bin/dotnet-compile-fsc /usr/local/bin/
|
||||
ln -s $INSTALL_DESTINATION/bin/dotnet-new /usr/local/bin/
|
||||
|
|
|
@ -14,6 +14,7 @@ param(
|
|||
$Projects = @(
|
||||
"Microsoft.DotNet.Cli",
|
||||
"Microsoft.DotNet.Tools.Compiler",
|
||||
"Microsoft.DotNet.Tools.Builder",
|
||||
"Microsoft.DotNet.Tools.Compiler.Csc",
|
||||
"Microsoft.DotNet.Tools.Compiler.Fsc",
|
||||
"Microsoft.DotNet.Tools.Compiler.Native",
|
||||
|
|
|
@ -26,6 +26,7 @@ source "$DIR/../_common.sh"
|
|||
PROJECTS=( \
|
||||
Microsoft.DotNet.Cli \
|
||||
Microsoft.DotNet.Tools.Compiler \
|
||||
Microsoft.DotNet.Tools.Builder \
|
||||
Microsoft.DotNet.Tools.Compiler.Csc \
|
||||
Microsoft.DotNet.Tools.Compiler.Fsc \
|
||||
Microsoft.DotNet.Tools.Compiler.Native \
|
||||
|
|
|
@ -49,7 +49,7 @@ try {
|
|||
# Check prereqs
|
||||
if (!(Get-Command -ErrorAction SilentlyContinue cmake)) {
|
||||
throw @"
|
||||
cmake is required to build the native host 'corehost'"
|
||||
cmake is required to build the native host 'corehost'
|
||||
Download it from https://www.cmake.org
|
||||
"@
|
||||
}
|
||||
|
|
|
@ -21,9 +21,24 @@ namespace Microsoft.DotNet.Cli.Utils
|
|||
private readonly StreamForwarder _stdOut;
|
||||
private readonly StreamForwarder _stdErr;
|
||||
|
||||
public enum CommandResolutionStrategy
|
||||
{
|
||||
//command loaded from a nuget package
|
||||
NugetPackage,
|
||||
|
||||
//command loaded from the same directory as the executing assembly
|
||||
BaseDirectory,
|
||||
|
||||
//command loaded from path
|
||||
Path,
|
||||
|
||||
//command not found
|
||||
None
|
||||
}
|
||||
|
||||
private bool _running = false;
|
||||
|
||||
private Command(string executable, string args)
|
||||
private Command(string executable, string args, CommandResolutionStrategy resolutionStrategy)
|
||||
{
|
||||
// Set the things we need
|
||||
var psi = new ProcessStartInfo()
|
||||
|
@ -41,6 +56,8 @@ namespace Microsoft.DotNet.Cli.Utils
|
|||
|
||||
_stdOut = new StreamForwarder();
|
||||
_stdErr = new StreamForwarder();
|
||||
|
||||
ResolutionStrategy = resolutionStrategy;
|
||||
}
|
||||
|
||||
public static Command Create(string executable, IEnumerable<string> args, NuGetFramework framework = null)
|
||||
|
@ -50,20 +67,25 @@ namespace Microsoft.DotNet.Cli.Utils
|
|||
|
||||
public static Command Create(string executable, string args, NuGetFramework framework = null)
|
||||
{
|
||||
ResolveExecutablePath(ref executable, ref args, framework);
|
||||
|
||||
return new Command(executable, args);
|
||||
var resolutionStrategy = CommandResolutionStrategy.None;
|
||||
|
||||
ResolveExecutablePath(ref executable, ref args, ref resolutionStrategy, framework);
|
||||
|
||||
return new Command(executable, args, resolutionStrategy);
|
||||
}
|
||||
|
||||
private static void ResolveExecutablePath(ref string executable, ref string args, NuGetFramework framework = null)
|
||||
private static void ResolveExecutablePath(ref string executable, ref string args, ref CommandResolutionStrategy resolutionStrategy, NuGetFramework framework = null)
|
||||
{
|
||||
executable =
|
||||
ResolveExecutablePathFromProject(executable, framework) ??
|
||||
ResolveExecutableFromPath(executable, ref args);
|
||||
ResolveExecutablePathFromProject(executable, framework, ref resolutionStrategy) ??
|
||||
ResolveExecutableFromPath(executable, ref args, ref resolutionStrategy);
|
||||
}
|
||||
|
||||
private static string ResolveExecutableFromPath(string executable, ref string args)
|
||||
private static string ResolveExecutableFromPath(string executable, ref string args, ref CommandResolutionStrategy resolutionStrategy)
|
||||
{
|
||||
resolutionStrategy = CommandResolutionStrategy.Path;
|
||||
|
||||
foreach (string suffix in Constants.RunnableSuffixes)
|
||||
{
|
||||
var fullExecutable = Path.GetFullPath(Path.Combine(
|
||||
|
@ -72,6 +94,8 @@ namespace Microsoft.DotNet.Cli.Utils
|
|||
if (File.Exists(fullExecutable))
|
||||
{
|
||||
executable = fullExecutable;
|
||||
|
||||
resolutionStrategy = CommandResolutionStrategy.BaseDirectory;
|
||||
|
||||
// In priority order we've found the best runnable extension, so break.
|
||||
break;
|
||||
|
@ -93,7 +117,7 @@ namespace Microsoft.DotNet.Cli.Utils
|
|||
return executable;
|
||||
}
|
||||
|
||||
private static string ResolveExecutablePathFromProject(string executable, NuGetFramework framework)
|
||||
private static string ResolveExecutablePathFromProject(string executable, NuGetFramework framework, ref CommandResolutionStrategy resolutionStrategy)
|
||||
{
|
||||
if (framework == null) return null;
|
||||
|
||||
|
@ -125,6 +149,8 @@ namespace Microsoft.DotNet.Cli.Utils
|
|||
var commandPath = commandPackage.Library.Files
|
||||
.First(f => Path.GetFileName(f) == commandName + FileNameSuffixes.DotNet.Exe);
|
||||
|
||||
resolutionStrategy = CommandResolutionStrategy.NugetPackage;
|
||||
|
||||
return Path.Combine(projectContext.PackagesDirectory, commandPackage.Path, commandPath);
|
||||
}
|
||||
|
||||
|
@ -286,6 +312,10 @@ namespace Microsoft.DotNet.Cli.Utils
|
|||
return this;
|
||||
}
|
||||
|
||||
public CommandResolutionStrategy ResolutionStrategy { get; }
|
||||
|
||||
public string CommandName => _process.StartInfo.FileName;
|
||||
|
||||
private string FormatProcessInfo(ProcessStartInfo info)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(info.Arguments))
|
||||
|
|
67
src/Microsoft.DotNet.Cli.Utils/ProjectContextExtensions.cs
Normal file
67
src/Microsoft.DotNet.Cli.Utils/ProjectContextExtensions.cs
Normal file
|
@ -0,0 +1,67 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
|
||||
using System.IO;
|
||||
using Microsoft.DotNet.ProjectModel;
|
||||
using NuGet.Frameworks;
|
||||
|
||||
namespace Microsoft.DotNet.Cli.Utils
|
||||
{
|
||||
internal static class ProjectContextExtensions
|
||||
{
|
||||
public static string ProjectName(this ProjectContext context) => context.RootProject.Identity.Name;
|
||||
|
||||
public static string GetOutputPath(this ProjectContext context, string configuration, string currentOutputPath)
|
||||
{
|
||||
var outputPath = string.Empty;
|
||||
|
||||
if (string.IsNullOrEmpty(currentOutputPath))
|
||||
{
|
||||
outputPath = Path.Combine(
|
||||
GetDefaultRootOutputPath(context, currentOutputPath),
|
||||
Constants.BinDirectoryName,
|
||||
configuration,
|
||||
context.TargetFramework.GetTwoDigitShortFolderName());
|
||||
}
|
||||
else
|
||||
{
|
||||
outputPath = currentOutputPath;
|
||||
}
|
||||
|
||||
return outputPath;
|
||||
}
|
||||
|
||||
public static string GetIntermediateOutputPath(this ProjectContext context, string configuration, string intermediateOutputValue, string currentOutputPath)
|
||||
{
|
||||
var intermediateOutputPath = string.Empty;
|
||||
|
||||
if (string.IsNullOrEmpty(intermediateOutputValue))
|
||||
{
|
||||
intermediateOutputPath = Path.Combine(
|
||||
GetDefaultRootOutputPath(context, currentOutputPath),
|
||||
Constants.ObjDirectoryName,
|
||||
configuration,
|
||||
context.TargetFramework.GetTwoDigitShortFolderName());
|
||||
}
|
||||
else
|
||||
{
|
||||
intermediateOutputPath = intermediateOutputValue;
|
||||
}
|
||||
|
||||
return intermediateOutputPath;
|
||||
}
|
||||
|
||||
public static string GetDefaultRootOutputPath(ProjectContext context, string currentOutputPath)
|
||||
{
|
||||
string rootOutputPath = string.Empty;
|
||||
|
||||
if (string.IsNullOrEmpty(currentOutputPath))
|
||||
{
|
||||
rootOutputPath = context.ProjectFile.ProjectDirectory;
|
||||
}
|
||||
|
||||
return rootOutputPath;
|
||||
}
|
||||
}
|
||||
}
|
19
src/Microsoft.DotNet.Tools.Builder/BuilderCommandApp.cs
Normal file
19
src/Microsoft.DotNet.Tools.Builder/BuilderCommandApp.cs
Normal file
|
@ -0,0 +1,19 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using Microsoft.DotNet.Tools.Compiler;
|
||||
|
||||
namespace Microsoft.DotNet.Tools.Build
|
||||
{
|
||||
internal class BuilderCommandApp : CompilerCommandApp
|
||||
{
|
||||
private const string BuildProfileFlag = "--build-profile";
|
||||
|
||||
public bool BuildProfileValue => OptionHasValue(BuildProfileFlag);
|
||||
|
||||
public BuilderCommandApp(string name, string fullName, string description) : base(name, fullName, description)
|
||||
{
|
||||
AddNoValueOption(BuildProfileFlag, "Set this flag to print the incremental safety checks that prevent incremental compilation");
|
||||
}
|
||||
}
|
||||
}
|
253
src/Microsoft.DotNet.Tools.Builder/CompileContext.cs
Normal file
253
src/Microsoft.DotNet.Tools.Builder/CompileContext.cs
Normal file
|
@ -0,0 +1,253 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
using Microsoft.DotNet.Cli.Utils;
|
||||
using Microsoft.DotNet.ProjectModel;
|
||||
using Microsoft.DotNet.Tools.Compiler;
|
||||
using Microsoft.DotNet.ProjectModel.Utilities;
|
||||
|
||||
|
||||
namespace Microsoft.DotNet.Tools.Build
|
||||
{
|
||||
// Knows how to orchestrate compilation for a ProjectContext
|
||||
// Collects icnremental safety checks and transitively compiles a project context
|
||||
internal class CompileContext
|
||||
{
|
||||
|
||||
public static readonly string[] KnownCompilers = { "csc", "vbc", "fsc" };
|
||||
|
||||
private readonly ProjectContext _rootProject;
|
||||
private readonly BuilderCommandApp _args;
|
||||
private readonly Dictionary<string, ProjectDescription> _dependencies;
|
||||
private readonly string _outputPath;
|
||||
private readonly string _intermediateOutputPath;
|
||||
private readonly IncrementalPreconditions _preconditions;
|
||||
|
||||
public bool IsSafeForIncrementalCompilation => _preconditions.PreconditionsDetected();
|
||||
|
||||
public CompileContext(ProjectContext rootProject, BuilderCommandApp args)
|
||||
{
|
||||
_rootProject = rootProject;
|
||||
_args = args;
|
||||
|
||||
// Set up Output Paths. They are unique per each CompileContext
|
||||
// Todo: clone args and mutate the clone so the rest of this class does not have special treatment for output paths
|
||||
_outputPath = _rootProject.GetOutputPath(_args.ConfigValue, _args.OutputValue);
|
||||
_intermediateOutputPath = _rootProject.GetIntermediateOutputPath(_args.ConfigValue, _args.IntermediateValue, _args.OutputValue);
|
||||
|
||||
// Set up dependencies
|
||||
_dependencies = GetProjectDependenciesWithSources(_rootProject, _args.ConfigValue);
|
||||
|
||||
//gather preconditions
|
||||
_preconditions = GatherIncrementalPreconditions();
|
||||
}
|
||||
|
||||
public bool Compile(bool incremental)
|
||||
{
|
||||
CreateOutputDirectories();
|
||||
|
||||
//compile dependencies
|
||||
foreach (var dependency in Sort(_dependencies))
|
||||
{
|
||||
if (!InvokeCompileOnDependency(dependency, _outputPath, _intermediateOutputPath))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//compile project
|
||||
var success = InvokeCompileOnRootProject(_outputPath, _intermediateOutputPath);
|
||||
|
||||
PrintSummary(success);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
private void PrintSummary(bool success)
|
||||
{
|
||||
//todo: Ideally it's the builder's responsibility for adding the time elapsed. That way we avoid cross cutting display concerns between compile and build for printing time elapsed
|
||||
if (success)
|
||||
{
|
||||
Reporter.Output.Write(" " + _preconditions.LogMessage());
|
||||
Reporter.Output.WriteLine();
|
||||
}
|
||||
|
||||
Reporter.Output.WriteLine();
|
||||
}
|
||||
|
||||
private void CreateOutputDirectories()
|
||||
{
|
||||
Directory.CreateDirectory(_outputPath);
|
||||
Directory.CreateDirectory(_intermediateOutputPath);
|
||||
}
|
||||
|
||||
//todo make extension of ProjectContext?
|
||||
//returns map with dependencies: string projectName -> ProjectDescription
|
||||
private static Dictionary<string, ProjectDescription> GetProjectDependenciesWithSources(ProjectContext projectContext, string configuration)
|
||||
{
|
||||
var projects = new Dictionary<string, ProjectDescription>();
|
||||
|
||||
// Create the library exporter
|
||||
var exporter = projectContext.CreateExporter(configuration);
|
||||
|
||||
// Gather exports for the project
|
||||
var dependencies = exporter.GetDependencies().ToList();
|
||||
|
||||
// Build project references
|
||||
foreach (var dependency in dependencies)
|
||||
{
|
||||
var projectDependency = dependency.Library as ProjectDescription;
|
||||
|
||||
if (projectDependency != null && projectDependency.Project.Files.SourceFiles.Any())
|
||||
{
|
||||
projects[projectDependency.Identity.Name] = projectDependency;
|
||||
}
|
||||
}
|
||||
|
||||
return projects;
|
||||
}
|
||||
|
||||
private IncrementalPreconditions GatherIncrementalPreconditions()
|
||||
{
|
||||
var preconditions = new IncrementalPreconditions(_args.BuildProfileValue);
|
||||
|
||||
var projectsToCheck = GetProjectsToCheck();
|
||||
|
||||
foreach (var project in projectsToCheck)
|
||||
{
|
||||
CollectScriptPreconditions(project, preconditions);
|
||||
CollectCompilerNamePreconditions(project, preconditions);
|
||||
CheckPathProbing(project, preconditions);
|
||||
}
|
||||
|
||||
return preconditions;
|
||||
}
|
||||
|
||||
//check the entire project tree that needs to be compiled, duplicated for each framework
|
||||
private List<ProjectContext> GetProjectsToCheck()
|
||||
{
|
||||
//include initial root project
|
||||
var contextsToCheck = new List<ProjectContext>(1 + _dependencies.Count) {_rootProject};
|
||||
|
||||
//convert ProjectDescription to ProjectContext
|
||||
var dependencyContexts = _dependencies.Select
|
||||
(keyValuePair => ProjectContext.Create(keyValuePair.Value.Path, keyValuePair.Value.TargetFrameworkInfo.FrameworkName));
|
||||
|
||||
contextsToCheck.AddRange(dependencyContexts);
|
||||
|
||||
|
||||
return contextsToCheck;
|
||||
}
|
||||
|
||||
private void CheckPathProbing(ProjectContext project, IncrementalPreconditions preconditions)
|
||||
{
|
||||
var pathCommands = CompilerUtil.GetCommandsInvokedByCompile(project)
|
||||
.Select(commandName => Command.Create(commandName, "", project.TargetFramework))
|
||||
.Where(c => Command.CommandResolutionStrategy.Path.Equals(c.ResolutionStrategy));
|
||||
|
||||
foreach (var pathCommand in pathCommands)
|
||||
{
|
||||
preconditions.AddPathProbingPrecondition(project.ProjectName(), pathCommand.CommandName);
|
||||
}
|
||||
}
|
||||
|
||||
private void CollectCompilerNamePreconditions(ProjectContext project, IncrementalPreconditions preconditions)
|
||||
{
|
||||
var projectCompiler = CompilerUtil.ResolveCompilerName(project);
|
||||
|
||||
if (KnownCompilers.Any(knownCompiler => knownCompiler.Equals(projectCompiler, StringComparison.Ordinal)))
|
||||
{
|
||||
preconditions.AddUnknownCompilerPrecondition(project.ProjectName(), projectCompiler);
|
||||
}
|
||||
}
|
||||
|
||||
private void CollectScriptPreconditions(ProjectContext project, IncrementalPreconditions preconditions)
|
||||
{
|
||||
var preCompileScripts = project.ProjectFile.Scripts.GetOrEmpty(ScriptNames.PreCompile);
|
||||
var postCompileScripts = project.ProjectFile.Scripts.GetOrEmpty(ScriptNames.PostCompile);
|
||||
|
||||
if (preCompileScripts.Any())
|
||||
{
|
||||
preconditions.AddPrePostScriptPrecondition(project.ProjectName(), ScriptNames.PreCompile);
|
||||
}
|
||||
|
||||
if (postCompileScripts.Any())
|
||||
{
|
||||
preconditions.AddPrePostScriptPrecondition(project.ProjectName(), ScriptNames.PostCompile);
|
||||
}
|
||||
}
|
||||
|
||||
private bool InvokeCompileOnDependency(ProjectDescription projectDependency, string outputPath, string intermediateOutputPath)
|
||||
{
|
||||
var compileResult = Command.Create("dotnet-compile",
|
||||
$"--framework {projectDependency.Framework} " +
|
||||
$"--configuration {_args.ConfigValue} " +
|
||||
$"--output \"{outputPath}\" " +
|
||||
$"--temp-output \"{intermediateOutputPath}\" " +
|
||||
(_args.NoHostValue ? "--no-host " : string.Empty) +
|
||||
$"\"{projectDependency.Project.ProjectDirectory}\"")
|
||||
.ForwardStdOut()
|
||||
.ForwardStdErr()
|
||||
.Execute();
|
||||
|
||||
return compileResult.ExitCode == 0;
|
||||
}
|
||||
|
||||
private bool InvokeCompileOnRootProject(string outputPath, string intermediateOutputPath)
|
||||
{
|
||||
//todo: add methods to CompilerCommandApp to generate the arg string
|
||||
var compileResult = Command.Create("dotnet-compile",
|
||||
$"--framework {_rootProject.TargetFramework} " +
|
||||
$"--configuration {_args.ConfigValue} " +
|
||||
$"--output \"{outputPath}\" " +
|
||||
$"--temp-output \"{intermediateOutputPath}\" " +
|
||||
(_args.NoHostValue ? "--no-host " : string.Empty) +
|
||||
//nativeArgs
|
||||
(_args.IsNativeValue ? "--native " : string.Empty) +
|
||||
(_args.IsCppModeValue ? "--cpp " : string.Empty) +
|
||||
(!string.IsNullOrWhiteSpace(_args.ArchValue) ? $"--arch {_args.ArchValue} " : string.Empty) +
|
||||
(!string.IsNullOrWhiteSpace(_args.IlcArgsValue) ? $"--ilcargs \"{_args.IlcArgsValue}\" " : string.Empty) +
|
||||
(!string.IsNullOrWhiteSpace(_args.IlcPathValue) ? $"--ilcpath \"{_args.IlcPathValue}\" " : string.Empty) +
|
||||
(!string.IsNullOrWhiteSpace(_args.IlcSdkPathValue) ? $"--ilcsdkpath \"{_args.IlcSdkPathValue}\" " : string.Empty) +
|
||||
$"\"{_rootProject.ProjectDirectory}\"")
|
||||
.ForwardStdOut()
|
||||
.ForwardStdErr()
|
||||
.Execute();
|
||||
|
||||
return compileResult.ExitCode == 0;
|
||||
}
|
||||
|
||||
private static ISet<ProjectDescription> Sort(Dictionary<string, ProjectDescription> projects)
|
||||
{
|
||||
var outputs = new HashSet<ProjectDescription>();
|
||||
|
||||
foreach (var pair in projects)
|
||||
{
|
||||
Sort(pair.Value, projects, outputs);
|
||||
}
|
||||
|
||||
return outputs;
|
||||
}
|
||||
|
||||
private static void Sort(ProjectDescription project, Dictionary<string, ProjectDescription> projects, ISet<ProjectDescription> outputs)
|
||||
{
|
||||
// Sorts projects in dependency order so that we only build them once per chain
|
||||
foreach (var dependency in project.Dependencies)
|
||||
{
|
||||
ProjectDescription projectDependency;
|
||||
if (projects.TryGetValue(dependency.Name, out projectDependency))
|
||||
{
|
||||
Sort(projectDependency, projects, outputs);
|
||||
}
|
||||
}
|
||||
|
||||
outputs.Add(project);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Microsoft.DotNet.Cli.Utils;
|
||||
|
||||
namespace Microsoft.DotNet.Tools.Build
|
||||
{
|
||||
internal class IncrementalPreconditions
|
||||
{
|
||||
private readonly List<string> _preconditions;
|
||||
private readonly bool _isProfile;
|
||||
|
||||
public IncrementalPreconditions(bool isProfile)
|
||||
{
|
||||
_isProfile = isProfile;
|
||||
_preconditions = new List<string>();
|
||||
}
|
||||
|
||||
public void AddPrePostScriptPrecondition(string projectName, string scriptType)
|
||||
{
|
||||
_preconditions.Add($"[Pre / Post Scripts] Project {projectName} is using {scriptType} scripts.");
|
||||
}
|
||||
|
||||
public void AddUnknownCompilerPrecondition(string projectName, string compilerName)
|
||||
{
|
||||
_preconditions.Add($"[Unknown Compiler] Project {projectName} is using unknown compiler {compilerName}.");
|
||||
}
|
||||
|
||||
public void AddPathProbingPrecondition(string projectName, string commandName)
|
||||
{
|
||||
_preconditions.Add($"[PATH Probing] Project {projectName} is loading tool \"{commandName}\" from PATH");
|
||||
}
|
||||
|
||||
public bool PreconditionsDetected()
|
||||
{
|
||||
return _preconditions.Any();
|
||||
}
|
||||
|
||||
private string PreconditionsMessage()
|
||||
{
|
||||
var log = new StringBuilder();
|
||||
|
||||
log.AppendLine();
|
||||
log.Append("Incremental compilation has been disabled due to the following project properties:");
|
||||
|
||||
foreach (var precondition in _preconditions)
|
||||
{
|
||||
log.AppendLine();
|
||||
log.Append("\t" + precondition);
|
||||
}
|
||||
|
||||
log.AppendLine();
|
||||
log.AppendLine();
|
||||
|
||||
log.Append(
|
||||
"Incremental compilation will be automatically enabled if the above mentioned project properties are not used. " +
|
||||
"For more information on the properties and how to address them, please consult:\n" +
|
||||
@"https://github.com/cdmihai/cli/wiki/Addressing-Incremental-Compilation-Warnings");
|
||||
|
||||
log.AppendLine();
|
||||
log.AppendLine();
|
||||
|
||||
return log.ToString();
|
||||
}
|
||||
|
||||
public string LogMessage()
|
||||
{
|
||||
if (PreconditionsDetected())
|
||||
{
|
||||
return _isProfile ? PreconditionsMessage().Yellow() : "(The compilation time can be improved. Run \"dotnet build --profile\" for more information)";
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
|
||||
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.Props" Condition="'$(VSToolsPath)' != ''" />
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>0a309227-a9d8-4ddf-88dd-326b57b04379</ProjectGuid>
|
||||
<RootNamespace>Microsoft.DotNet.Tools.Build</RootNamespace>
|
||||
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">..\..\artifacts\obj\$(MSBuildProjectName)</BaseIntermediateOutputPath>
|
||||
<OutputPath Condition="'$(OutputPath)'=='' ">..\..\artifacts\bin\$(MSBuildProjectName)\</OutputPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.targets" Condition="'$(VSToolsPath)' != ''" />
|
||||
</Project>
|
45
src/Microsoft.DotNet.Tools.Builder/Program.cs
Normal file
45
src/Microsoft.DotNet.Tools.Builder/Program.cs
Normal file
|
@ -0,0 +1,45 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
using Microsoft.DotNet.Cli.Utils;
|
||||
using Microsoft.DotNet.ProjectModel;
|
||||
using Microsoft.DotNet.Tools.Compiler;
|
||||
|
||||
namespace Microsoft.DotNet.Tools.Build
|
||||
{
|
||||
public class Program
|
||||
{
|
||||
public static int Main(string[] args)
|
||||
{
|
||||
DebugHelper.HandleDebugSwitch(ref args);
|
||||
|
||||
try
|
||||
{
|
||||
var app = new BuilderCommandApp("dotnet build", ".NET Builder", "Builder for the .NET Platform. It performs incremental compilation if it's safe to do so. Otherwise it delegates to dotnet-compile which performs non-incremental compilation");
|
||||
return app.Execute(OnExecute, args);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
#if DEBUG
|
||||
Console.Error.WriteLine(ex);
|
||||
#else
|
||||
Console.Error.WriteLine(ex.Message);
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool OnExecute(List<ProjectContext> contexts, CompilerCommandApp args)
|
||||
{
|
||||
var compileContexts = contexts.Select(context => new CompileContext(context, (BuilderCommandApp)args)).ToList();
|
||||
|
||||
var incrementalSafe = compileContexts.All(c => c.IsSafeForIncrementalCompilation);
|
||||
|
||||
return compileContexts.All(c => c.Compile(incrementalSafe));
|
||||
}
|
||||
}
|
||||
}
|
32
src/Microsoft.DotNet.Tools.Builder/README.md
Normal file
32
src/Microsoft.DotNet.Tools.Builder/README.md
Normal file
|
@ -0,0 +1,32 @@
|
|||
dotnet-build
|
||||
===========
|
||||
|
||||
**NAME**
|
||||
dotnet-build -- Orchestrates the compilation of a project and all its dependencies.
|
||||
|
||||
**SYNOPSIS**
|
||||
dotnet build[options]
|
||||
|
||||
**DESCRIPTION**
|
||||
|
||||
The build verb orchestrates the compilation of a project: it gathers the dependencies of a project and decides which to compile.
|
||||
|
||||
Users should invoke the Build verb when they want the entire dependency graph compiled, and Compile when they want only a specific project compiled.
|
||||
|
||||
Before any compilation begins, the build verb analyzes the project and its dependencies for incremental safety checks. If all checks clear out, then build proceeds with incremental compilation of the project and its dependencies. Otherwise it falls back to non-incremental compilation. Via a profile flag users can choose to receive additional information on how they can improve their build times.
|
||||
|
||||
All the projects in the dependency graph that need compilation must pass the following safety checks in order for the compilation process to be incremental:
|
||||
- not use pre / post compile scripts
|
||||
- not load compilation tools from PATH (e.g., resgen, compilers)
|
||||
- use only known compilers (csc, vbc, fsc)
|
||||
|
||||
Please read the [documentation](https://github.com/dotnet/cli/blob/master/src/Microsoft.DotNet.Tools.Compiler/README.md) on Compile for details on compilation and project structure:
|
||||
|
||||
**Options**
|
||||
|
||||
Build inherits all the [Compile command line parameters](https://github.com/dotnet/cli/blob/master/src/Microsoft.DotNet.Tools.Compiler/README.md).
|
||||
|
||||
In addition Compile's parameters, Build adds the following flag:
|
||||
|
||||
--build-profile [exe | dynlib | lib]
|
||||
Prints out the incremental safety checks that users need to address in order for incremental compilation to be automatically turned on.
|
33
src/Microsoft.DotNet.Tools.Builder/project.json
Normal file
33
src/Microsoft.DotNet.Tools.Builder/project.json
Normal file
|
@ -0,0 +1,33 @@
|
|||
{
|
||||
"name": "dotnet-build",
|
||||
"version": "1.0.0-*",
|
||||
"compilationOptions": {
|
||||
"emitEntryPoint": true
|
||||
},
|
||||
"dependencies": {
|
||||
"NETStandard.Library": "1.0.0-rc2-23608",
|
||||
"System.Linq": "4.0.1-rc2-23608",
|
||||
"System.Reflection.Metadata": "1.1.0",
|
||||
|
||||
"Microsoft.DotNet.ProjectModel": "1.0.0-*",
|
||||
"Microsoft.DotNet.Compiler.Common": "1.0.0-*",
|
||||
"Microsoft.DotNet.Tools.Compiler": "1.0.0-*",
|
||||
"Microsoft.DotNet.Cli.Utils": {
|
||||
"type": "build",
|
||||
"version": "1.0.0-*"
|
||||
},
|
||||
"Microsoft.Extensions.CommandLineUtils.Sources": {
|
||||
"type": "build",
|
||||
"version": "1.0.0-*"
|
||||
}
|
||||
},
|
||||
"frameworks": {
|
||||
"dnxcore50": { }
|
||||
},
|
||||
"scripts": {
|
||||
"postcompile": [
|
||||
"../../scripts/build/place-binary \"%compile:OutputDir%/%project:Name%.dll\"",
|
||||
"../../scripts/build/place-binary \"%compile:OutputDir%/%project:Name%.pdb\""
|
||||
]
|
||||
}
|
||||
}
|
144
src/Microsoft.DotNet.Tools.Compiler/CompilerCommandApp.cs
Normal file
144
src/Microsoft.DotNet.Tools.Compiler/CompilerCommandApp.cs
Normal file
|
@ -0,0 +1,144 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Microsoft.Dnx.Runtime.Common.CommandLine;
|
||||
using Microsoft.DotNet.Cli.Utils;
|
||||
using Microsoft.DotNet.ProjectModel;
|
||||
using NuGet.Frameworks;
|
||||
using System.Linq;
|
||||
|
||||
//This class is responsible with defining the arguments for the Compile verb.
|
||||
//It knows how to interpret them and set default values
|
||||
namespace Microsoft.DotNet.Tools.Compiler
|
||||
{
|
||||
public delegate bool OnExecute(List<ProjectContext> contexts, CompilerCommandApp compilerCommand);
|
||||
|
||||
public class CompilerCommandApp
|
||||
{
|
||||
private readonly CommandLineApplication _app;
|
||||
|
||||
//options and arguments for compilation
|
||||
private CommandOption _outputOption;
|
||||
private CommandOption _intermediateOutputOption;
|
||||
private CommandOption _frameworkOption;
|
||||
private CommandOption _configurationOption;
|
||||
private CommandOption _noHostOption;
|
||||
private CommandArgument _projectArgument;
|
||||
private CommandOption _nativeOption;
|
||||
private CommandOption _archOption;
|
||||
private CommandOption _ilcArgsOption;
|
||||
private CommandOption _ilcPathOption;
|
||||
private CommandOption _ilcSdkPathOption;
|
||||
private CommandOption _appDepSdkPathOption;
|
||||
private CommandOption _cppModeOption;
|
||||
private CommandOption _cppCompilerFlagsOption;
|
||||
|
||||
//resolved values for the options and arguments
|
||||
public string ProjectPathValue { get; set; }
|
||||
public string OutputValue { get; set; }
|
||||
public string IntermediateValue { get; set; }
|
||||
public string ConfigValue { get; set; }
|
||||
public bool NoHostValue { get; set; }
|
||||
public bool IsNativeValue { get; set; }
|
||||
public string ArchValue { get; set; }
|
||||
public string IlcArgsValue { get; set; }
|
||||
public string IlcPathValue { get; set; }
|
||||
public string IlcSdkPathValue { get; set; }
|
||||
public bool IsCppModeValue { get; set; }
|
||||
public string AppDepSdkPathValue { get; set; }
|
||||
public string CppCompilerFlagsValue { get; set; }
|
||||
|
||||
//workaround: CommandLineApplication is internal therefore I cannot make _app protected so baseclasses can add their own params
|
||||
private readonly Dictionary<string, CommandOption> baseClassOptions;
|
||||
|
||||
public CompilerCommandApp(string name, string fullName, string description)
|
||||
{
|
||||
_app = new CommandLineApplication
|
||||
{
|
||||
Name = name,
|
||||
FullName = fullName,
|
||||
Description = description
|
||||
};
|
||||
|
||||
baseClassOptions = new Dictionary<string, CommandOption>();
|
||||
|
||||
AddCompileParameters();
|
||||
}
|
||||
|
||||
private void AddCompileParameters()
|
||||
{
|
||||
_app.HelpOption("-h|--help");
|
||||
|
||||
_outputOption = _app.Option("-o|--output <OUTPUT_DIR>", "Directory in which to place outputs", CommandOptionType.SingleValue);
|
||||
_intermediateOutputOption = _app.Option("-t|--temp-output <OUTPUT_DIR>", "Directory in which to place temporary outputs", CommandOptionType.SingleValue);
|
||||
_frameworkOption = _app.Option("-f|--framework <FRAMEWORK>", "Compile a specific framework", CommandOptionType.MultipleValue);
|
||||
_configurationOption = _app.Option("-c|--configuration <CONFIGURATION>", "Configuration under which to build", CommandOptionType.SingleValue);
|
||||
_noHostOption = _app.Option("--no-host", "Set this to skip publishing a runtime host when building for CoreCLR", CommandOptionType.NoValue);
|
||||
_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");
|
||||
|
||||
// Native Args
|
||||
_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);
|
||||
_ilcArgsOption = _app.Option("--ilcargs <ARGS>", "Command line arguments to be passed directly to ILCompiler.", CommandOptionType.SingleValue);
|
||||
_ilcPathOption = _app.Option("--ilcpath <PATH>", "Path to the folder containing custom built ILCompiler.", CommandOptionType.SingleValue);
|
||||
_ilcSdkPathOption = _app.Option("--ilcsdkpath <PATH>", "Path to the folder containing ILCompiler application dependencies.", CommandOptionType.SingleValue);
|
||||
_appDepSdkPathOption = _app.Option("--appdepsdkpath <PATH>", "Path to the folder containing ILCompiler application dependencies.", CommandOptionType.SingleValue);
|
||||
_cppModeOption = _app.Option("--cpp", "Flag to do native compilation with C++ code generator.", CommandOptionType.NoValue);
|
||||
_cppCompilerFlagsOption = _app.Option("--cppcompilerflags <flags>", "Additional flags to be passed to the native compiler.", CommandOptionType.SingleValue);
|
||||
}
|
||||
|
||||
public int Execute(OnExecute execute, string[] args)
|
||||
{
|
||||
_app.OnExecute(() =>
|
||||
{
|
||||
// Locate the project and get the name and full path
|
||||
ProjectPathValue = _projectArgument.Value;
|
||||
if (string.IsNullOrEmpty(ProjectPathValue))
|
||||
{
|
||||
ProjectPathValue = Directory.GetCurrentDirectory();
|
||||
}
|
||||
|
||||
OutputValue = _outputOption.Value();
|
||||
IntermediateValue = _intermediateOutputOption.Value();
|
||||
ConfigValue = _configurationOption.Value() ?? Constants.DefaultConfiguration;
|
||||
NoHostValue = _noHostOption.HasValue();
|
||||
|
||||
IsNativeValue = _nativeOption.HasValue();
|
||||
ArchValue = _archOption.Value();
|
||||
IlcArgsValue = _ilcArgsOption.Value();
|
||||
IlcPathValue = _ilcPathOption.Value();
|
||||
IlcSdkPathValue = _ilcSdkPathOption.Value();
|
||||
AppDepSdkPathValue = _appDepSdkPathOption.Value();
|
||||
IsCppModeValue = _cppModeOption.HasValue();
|
||||
CppCompilerFlagsValue = _cppCompilerFlagsOption.Value();
|
||||
|
||||
|
||||
// Load project contexts for each framework
|
||||
var contexts = _frameworkOption.HasValue() ?
|
||||
_frameworkOption.Values.Select(f => ProjectContext.Create(ProjectPathValue, NuGetFramework.Parse(f))) :
|
||||
ProjectContext.CreateContextForEachFramework(ProjectPathValue);
|
||||
|
||||
var success = execute(contexts.ToList(), this);
|
||||
|
||||
return success ? 0 : 1;
|
||||
});
|
||||
|
||||
return _app.Execute(args);
|
||||
}
|
||||
|
||||
//CommandOptionType is internal. Cannot pass it as argument. Therefore the method name encodes the option type.
|
||||
protected void AddNoValueOption(string optionTemplate, string descriptino){
|
||||
baseClassOptions[optionTemplate] = _app.Option(optionTemplate, descriptino, CommandOptionType.NoValue);
|
||||
}
|
||||
|
||||
protected bool OptionHasValue(string optionTemplate)
|
||||
{
|
||||
CommandOption option;
|
||||
|
||||
return baseClassOptions.TryGetValue(optionTemplate, out option) && option.HasValue();
|
||||
}
|
||||
}
|
||||
}
|
121
src/Microsoft.DotNet.Tools.Compiler/CompilerUtil.cs
Normal file
121
src/Microsoft.DotNet.Tools.Compiler/CompilerUtil.cs
Normal file
|
@ -0,0 +1,121 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
using Microsoft.DotNet.Cli.Utils;
|
||||
using Microsoft.DotNet.ProjectModel;
|
||||
using Microsoft.DotNet.Cli.Compiler.Common;
|
||||
using Microsoft.DotNet.ProjectModel.Compilation;
|
||||
using Microsoft.DotNet.Tools.Common;
|
||||
|
||||
//This class is responsible with defining the arguments for the Compile verb.
|
||||
//It knows how to interpret them and set default values
|
||||
|
||||
namespace Microsoft.DotNet.Tools.Compiler
|
||||
{
|
||||
public static class CompilerUtil
|
||||
{
|
||||
public static string ResolveCompilerName(ProjectContext context)
|
||||
{
|
||||
var compilerName = context.ProjectFile.CompilerName;
|
||||
compilerName = compilerName ?? "csc";
|
||||
|
||||
return compilerName;
|
||||
}
|
||||
|
||||
public struct NonCultureResgenIO
|
||||
{
|
||||
public readonly string InputFile;
|
||||
public readonly string MetadataName;
|
||||
|
||||
//is non-null only when resgen needs to be invoked (inputFile is .resx)
|
||||
public readonly string OutputFile;
|
||||
|
||||
public NonCultureResgenIO(string inputFile, string outputFile, string metadataName)
|
||||
{
|
||||
InputFile = inputFile;
|
||||
OutputFile = outputFile;
|
||||
MetadataName = metadataName;
|
||||
}
|
||||
}
|
||||
|
||||
//used in incremental compilation
|
||||
public static List<NonCultureResgenIO> GetNonCultureResources(Project project, string intermediateOutputPath)
|
||||
{
|
||||
return
|
||||
(from resourceFile in project.Files.ResourceFiles
|
||||
let inputFile = resourceFile.Key
|
||||
where string.IsNullOrEmpty(ResourceUtility.GetResourceCultureName(inputFile))
|
||||
let metadataName = GetResourceFileMetadataName(project, resourceFile)
|
||||
let outputFile = ResourceUtility.IsResxFile(inputFile) ? Path.Combine(intermediateOutputPath, metadataName) : null
|
||||
select new NonCultureResgenIO(inputFile, outputFile, metadataName)
|
||||
).ToList();
|
||||
}
|
||||
|
||||
public struct CultureResgenIO
|
||||
{
|
||||
public readonly string Culture;
|
||||
public readonly Dictionary<string, string> InputFileToMetadata;
|
||||
public readonly string OutputFile;
|
||||
|
||||
public CultureResgenIO(string culture, Dictionary<string, string> inputFileToMetadata, string outputFile)
|
||||
{
|
||||
Culture = culture;
|
||||
InputFileToMetadata = inputFileToMetadata;
|
||||
OutputFile = outputFile;
|
||||
}
|
||||
}
|
||||
|
||||
//used in incremental compilation
|
||||
public static List<CultureResgenIO> GetCultureResources(Project project, string outputPath)
|
||||
{
|
||||
return
|
||||
(from resourceFileGroup in project.Files.ResourceFiles.GroupBy(resourceFile => ResourceUtility.GetResourceCultureName(resourceFile.Key))
|
||||
let culture = resourceFileGroup.Key
|
||||
where !string.IsNullOrEmpty(culture)
|
||||
let inputFileToMetadata = resourceFileGroup.ToDictionary(r => r.Key, r => GetResourceFileMetadataName(project, r))
|
||||
let resourceOutputPath = Path.Combine(outputPath, culture)
|
||||
let outputFile = Path.Combine(resourceOutputPath, project.Name + ".resources.dll")
|
||||
select new CultureResgenIO(culture, inputFileToMetadata, outputFile)
|
||||
).ToList();
|
||||
}
|
||||
|
||||
//used in incremental compilation
|
||||
public static IList<string> GetReferencePathsForCultureResgen(List<LibraryExport> dependencies)
|
||||
{
|
||||
return dependencies.SelectMany(libraryExport => libraryExport.CompilationAssemblies).Select(r => r.ResolvedPath).ToList();
|
||||
}
|
||||
|
||||
public static string GetResourceFileMetadataName(Project project, KeyValuePair<string, string> resourceFile)
|
||||
{
|
||||
string resourceName = null;
|
||||
string rootNamespace = null;
|
||||
|
||||
string root = PathUtility.EnsureTrailingSlash(project.ProjectDirectory);
|
||||
string resourcePath = resourceFile.Key;
|
||||
if (string.IsNullOrEmpty(resourceFile.Value))
|
||||
{
|
||||
// No logical name, so use the file name
|
||||
resourceName = ResourceUtility.GetResourceName(root, resourcePath);
|
||||
rootNamespace = project.Name;
|
||||
}
|
||||
else
|
||||
{
|
||||
resourceName = ResourceManifestName.EnsureResourceExtension(resourceFile.Value, resourcePath);
|
||||
rootNamespace = null;
|
||||
}
|
||||
|
||||
var name = ResourceManifestName.CreateManifestName(resourceName, rootNamespace);
|
||||
return name;
|
||||
}
|
||||
|
||||
public static IEnumerable<string> GetCommandsInvokedByCompile(ProjectContext project)
|
||||
{
|
||||
return new List<string> {ResolveCompilerName(project)};
|
||||
}
|
||||
}
|
||||
}
|
|
@ -22,76 +22,15 @@ namespace Microsoft.DotNet.Tools.Compiler
|
|||
{
|
||||
public class Program
|
||||
{
|
||||
|
||||
public static int Main(string[] args)
|
||||
{
|
||||
DebugHelper.HandleDebugSwitch(ref args);
|
||||
|
||||
var app = new CommandLineApplication();
|
||||
app.Name = "dotnet compile";
|
||||
app.FullName = ".NET Compiler";
|
||||
app.Description = "Compiler for the .NET Platform";
|
||||
app.HelpOption("-h|--help");
|
||||
|
||||
var output = app.Option("-o|--output <OUTPUT_DIR>", "Directory in which to place outputs", CommandOptionType.SingleValue);
|
||||
var intermediateOutput = app.Option("-t|--temp-output <OUTPUT_DIR>", "Directory in which to place temporary outputs", CommandOptionType.SingleValue);
|
||||
var framework = app.Option("-f|--framework <FRAMEWORK>", "Compile a specific framework", CommandOptionType.MultipleValue);
|
||||
var configuration = app.Option("-c|--configuration <CONFIGURATION>", "Configuration under which to build", CommandOptionType.SingleValue);
|
||||
var noProjectDependencies = app.Option("--no-project-dependencies", "Skips building project references.", CommandOptionType.NoValue);
|
||||
var noHost = app.Option("--no-host", "Set this to skip publishing a runtime host when building for CoreCLR", CommandOptionType.NoValue);
|
||||
var project = app.Argument("<PROJECT>", "The project to compile, defaults to the current directory. Can be a path to a project.json or a project directory");
|
||||
|
||||
// Native Args
|
||||
var native = app.Option("-n|--native", "Compiles source to native machine code.", CommandOptionType.NoValue);
|
||||
var arch = app.Option("-a|--arch <ARCH>", "The architecture for which to compile. x64 only currently supported.", CommandOptionType.SingleValue);
|
||||
var ilcArgs = app.Option("--ilcargs <ARGS>", "Command line arguments to be passed directly to ILCompiler.", CommandOptionType.SingleValue);
|
||||
var ilcPath = app.Option("--ilcpath <PATH>", "Path to the folder containing custom built ILCompiler.", CommandOptionType.SingleValue);
|
||||
var ilcSdkPath = app.Option("--ilcsdkpath <PATH>", "Path to the folder containing custom built ILCompiler SDK.", CommandOptionType.SingleValue);
|
||||
var appDepSdkPath = app.Option("--appdepsdkpath <PATH>", "Path to the folder containing ILCompiler application dependencies.", CommandOptionType.SingleValue);
|
||||
var cppMode = app.Option("--cpp", "Flag to do native compilation with C++ code generator.", CommandOptionType.NoValue);
|
||||
var cppCompilerFlags = app.Option("--cppcompilerflags <flags>", "Additional flags to be passed to the native compiler.", CommandOptionType.SingleValue);
|
||||
|
||||
app.OnExecute(() =>
|
||||
{
|
||||
// Locate the project and get the name and full path
|
||||
var path = project.Value;
|
||||
if (string.IsNullOrEmpty(path))
|
||||
{
|
||||
path = Directory.GetCurrentDirectory();
|
||||
}
|
||||
|
||||
var buildProjectReferences = !noProjectDependencies.HasValue();
|
||||
var isNative = native.HasValue();
|
||||
var isCppMode = cppMode.HasValue();
|
||||
var archValue = arch.Value();
|
||||
var ilcArgsValue = ilcArgs.Value();
|
||||
var ilcPathValue = ilcPath.Value();
|
||||
var ilcSdkPathValue = ilcSdkPath.Value();
|
||||
var appDepSdkPathValue = appDepSdkPath.Value();
|
||||
var configValue = configuration.Value() ?? Constants.DefaultConfiguration;
|
||||
var outputValue = output.Value();
|
||||
var intermediateValue = intermediateOutput.Value();
|
||||
var cppCompilerFlagsValue = cppCompilerFlags.Value();
|
||||
|
||||
// Load project contexts for each framework and compile them
|
||||
bool success = true;
|
||||
var contexts = framework.HasValue() ?
|
||||
framework.Values.Select(f => ProjectContext.Create(path, NuGetFramework.Parse(f))) :
|
||||
ProjectContext.CreateContextForEachFramework(path);
|
||||
foreach (var context in contexts)
|
||||
{
|
||||
success &= Compile(context, configValue, outputValue, intermediateValue, buildProjectReferences, noHost.HasValue());
|
||||
if (isNative && success)
|
||||
{
|
||||
success &= CompileNative(context, configValue, outputValue, buildProjectReferences, intermediateValue, archValue, ilcArgsValue, ilcPathValue, ilcSdkPathValue, appDepSdkPathValue, isCppMode, cppCompilerFlagsValue);
|
||||
}
|
||||
}
|
||||
|
||||
return success ? 0 : 1;
|
||||
});
|
||||
|
||||
try
|
||||
{
|
||||
return app.Execute(args);
|
||||
var compilerCommandArgs = new CompilerCommandApp("dotnet compile", ".NET Compiler", "Compiler for the .NET Platform");
|
||||
return compilerCommandArgs.Execute(OnExecute, args);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
@ -104,31 +43,36 @@ namespace Microsoft.DotNet.Tools.Compiler
|
|||
}
|
||||
}
|
||||
|
||||
private static bool OnExecute(List<ProjectContext> contexts, CompilerCommandApp args)
|
||||
{
|
||||
var success = true;
|
||||
|
||||
foreach (var context in contexts)
|
||||
{
|
||||
success &= CompileProject(context, args);
|
||||
if (args.IsNativeValue && success)
|
||||
{
|
||||
success &= CompileNative(context, args);
|
||||
}
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
private static bool CompileNative(
|
||||
ProjectContext context,
|
||||
string configuration,
|
||||
string outputOptionValue,
|
||||
bool buildProjectReferences,
|
||||
string intermediateOutputValue,
|
||||
string archValue,
|
||||
string ilcArgsValue,
|
||||
string ilcPathValue,
|
||||
string ilcSdkPathValue,
|
||||
string appDepSdkPathValue,
|
||||
bool isCppMode,
|
||||
string cppCompilerFlagsValue)
|
||||
CompilerCommandApp args)
|
||||
{
|
||||
var outputPath = GetOutputPath(context, configuration, outputOptionValue);
|
||||
var nativeOutputPath = Path.Combine(GetOutputPath(context, configuration, outputOptionValue), "native");
|
||||
var outputPath = context.GetOutputPath(args.ConfigValue, args.OutputValue);
|
||||
var nativeOutputPath = Path.Combine(outputPath, "native");
|
||||
var intermediateOutputPath =
|
||||
GetIntermediateOutputPath(context, configuration, intermediateOutputValue, outputOptionValue);
|
||||
context.GetIntermediateOutputPath(args.ConfigValue, args.IntermediateValue, outputPath);
|
||||
|
||||
Directory.CreateDirectory(nativeOutputPath);
|
||||
Directory.CreateDirectory(intermediateOutputPath);
|
||||
|
||||
var compilationOptions = context.ProjectFile.GetCompilerOptions(context.TargetFramework, configuration);
|
||||
var compilationOptions = context.ProjectFile.GetCompilerOptions(context.TargetFramework, args.ConfigValue);
|
||||
var managedOutput =
|
||||
GetProjectOutput(context.ProjectFile, context.TargetFramework, configuration, outputPath);
|
||||
GetProjectOutput(context.ProjectFile, context.TargetFramework, args.ConfigValue, outputPath);
|
||||
|
||||
var nativeArgs = new List<string>();
|
||||
|
||||
|
@ -136,58 +80,58 @@ namespace Microsoft.DotNet.Tools.Compiler
|
|||
nativeArgs.Add($"{managedOutput}");
|
||||
|
||||
// ILC Args
|
||||
if (!string.IsNullOrWhiteSpace(ilcArgsValue))
|
||||
if (!string.IsNullOrWhiteSpace(args.IlcArgsValue))
|
||||
{
|
||||
nativeArgs.Add("--ilcargs");
|
||||
nativeArgs.Add($"{ilcArgsValue}");
|
||||
nativeArgs.Add($"{args.IlcArgsValue}");
|
||||
}
|
||||
|
||||
// ILC Path
|
||||
if (!string.IsNullOrWhiteSpace(ilcPathValue))
|
||||
if (!string.IsNullOrWhiteSpace(args.IlcPathValue))
|
||||
{
|
||||
nativeArgs.Add("--ilcpath");
|
||||
nativeArgs.Add(ilcPathValue);
|
||||
nativeArgs.Add(args.IlcPathValue);
|
||||
}
|
||||
|
||||
// ILC SDK Path
|
||||
if (!string.IsNullOrWhiteSpace(ilcSdkPathValue))
|
||||
if (!string.IsNullOrWhiteSpace(args.IlcSdkPathValue))
|
||||
{
|
||||
nativeArgs.Add("--ilcsdkpath");
|
||||
nativeArgs.Add(ilcSdkPathValue);
|
||||
nativeArgs.Add(args.IlcSdkPathValue);
|
||||
}
|
||||
|
||||
// AppDep SDK Path
|
||||
if (!string.IsNullOrWhiteSpace(appDepSdkPathValue))
|
||||
if (!string.IsNullOrWhiteSpace(args.AppDepSdkPathValue))
|
||||
{
|
||||
nativeArgs.Add("--appdepsdk");
|
||||
nativeArgs.Add(appDepSdkPathValue);
|
||||
nativeArgs.Add(args.AppDepSdkPathValue);
|
||||
}
|
||||
|
||||
// CodeGen Mode
|
||||
if(isCppMode)
|
||||
if(args.IsCppModeValue)
|
||||
{
|
||||
nativeArgs.Add("--mode");
|
||||
nativeArgs.Add("cpp");
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(cppCompilerFlagsValue))
|
||||
if (!string.IsNullOrWhiteSpace(args.CppCompilerFlagsValue))
|
||||
{
|
||||
nativeArgs.Add("--cppcompilerflags");
|
||||
nativeArgs.Add(cppCompilerFlagsValue);
|
||||
nativeArgs.Add(args.CppCompilerFlagsValue);
|
||||
}
|
||||
|
||||
// Configuration
|
||||
if (configuration != null)
|
||||
if (args.ConfigValue != null)
|
||||
{
|
||||
nativeArgs.Add("--configuration");
|
||||
nativeArgs.Add(configuration);
|
||||
nativeArgs.Add(args.ConfigValue);
|
||||
}
|
||||
|
||||
// Architecture
|
||||
if (archValue != null)
|
||||
if (args.ArchValue != null)
|
||||
{
|
||||
nativeArgs.Add("--arch");
|
||||
nativeArgs.Add(archValue);
|
||||
nativeArgs.Add(args.ArchValue);
|
||||
}
|
||||
|
||||
// Intermediate Path
|
||||
|
@ -214,65 +158,21 @@ namespace Microsoft.DotNet.Tools.Compiler
|
|||
return result.ExitCode == 0;
|
||||
}
|
||||
|
||||
private static bool Compile(ProjectContext context, string configuration, string outputOptionValue, string intermediateOutputValue, bool buildProjectReferences, bool noHost)
|
||||
private static bool CompileProject(ProjectContext context, CompilerCommandApp args)
|
||||
{
|
||||
// Set up Output Paths
|
||||
string outputPath = GetOutputPath(context, configuration, outputOptionValue);
|
||||
string intermediateOutputPath = GetIntermediateOutputPath(context, configuration, intermediateOutputValue, outputOptionValue);
|
||||
string outputPath = context.GetOutputPath(args.ConfigValue, args.OutputValue);
|
||||
string intermediateOutputPath = context.GetIntermediateOutputPath(args.ConfigValue, args.IntermediateValue, outputPath);
|
||||
|
||||
Directory.CreateDirectory(outputPath);
|
||||
Directory.CreateDirectory(intermediateOutputPath);
|
||||
|
||||
// Create the library exporter
|
||||
var exporter = context.CreateExporter(configuration);
|
||||
var exporter = context.CreateExporter(args.ConfigValue);
|
||||
|
||||
// Gather exports for the project
|
||||
var dependencies = exporter.GetDependencies().ToList();
|
||||
|
||||
if (buildProjectReferences)
|
||||
{
|
||||
var projects = new Dictionary<string, ProjectDescription>();
|
||||
|
||||
// Build project references
|
||||
foreach (var dependency in dependencies)
|
||||
{
|
||||
var projectDependency = dependency.Library as ProjectDescription;
|
||||
|
||||
if (projectDependency != null && projectDependency.Project.Files.SourceFiles.Any())
|
||||
{
|
||||
projects[projectDependency.Identity.Name] = projectDependency;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var projectDependency in Sort(projects))
|
||||
{
|
||||
// Skip compiling project dependencies since we've already figured out the build order
|
||||
var compileResult = Command.Create("dotnet-compile",
|
||||
$"--framework {projectDependency.Framework} " +
|
||||
$"--configuration {configuration} " +
|
||||
$"--output \"{outputPath}\" " +
|
||||
$"--temp-output \"{intermediateOutputPath}\" " +
|
||||
"--no-project-dependencies " +
|
||||
(noHost ? "--no-host " : string.Empty) +
|
||||
$"\"{projectDependency.Project.ProjectDirectory}\"")
|
||||
.ForwardStdOut()
|
||||
.ForwardStdErr()
|
||||
.Execute();
|
||||
|
||||
if (compileResult.ExitCode != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
projects.Clear();
|
||||
}
|
||||
|
||||
return CompileProject(context, configuration, outputPath, intermediateOutputPath, dependencies, noHost);
|
||||
}
|
||||
|
||||
private static bool CompileProject(ProjectContext context, string configuration, string outputPath, string intermediateOutputPath, List<LibraryExport> dependencies, bool noHost)
|
||||
{
|
||||
Reporter.Output.WriteLine($"Compiling {context.RootProject.Identity.Name.Yellow()} for {context.TargetFramework.DotNetFrameworkName.Yellow()}");
|
||||
var sw = Stopwatch.StartNew();
|
||||
|
||||
|
@ -300,7 +200,7 @@ namespace Microsoft.DotNet.Tools.Compiler
|
|||
}
|
||||
|
||||
// Get compilation options
|
||||
var outputName = GetProjectOutput(context.ProjectFile, context.TargetFramework, configuration, outputPath);
|
||||
var outputName = GetProjectOutput(context.ProjectFile, context.TargetFramework, args.ConfigValue, outputPath);
|
||||
|
||||
// Assemble args
|
||||
var compilerArgs = new List<string>()
|
||||
|
@ -309,7 +209,7 @@ namespace Microsoft.DotNet.Tools.Compiler
|
|||
$"--out:{outputName}"
|
||||
};
|
||||
|
||||
var compilationOptions = context.ProjectFile.GetCompilerOptions(context.TargetFramework, configuration);
|
||||
var compilationOptions = context.ProjectFile.GetCompilerOptions(context.TargetFramework, args.ConfigValue);
|
||||
|
||||
// Path to strong naming key in environment variable overrides path in project.json
|
||||
var environmentKeyFile = Environment.GetEnvironmentVariable(EnvironmentNames.StrongNameKeyFile);
|
||||
|
@ -340,7 +240,7 @@ namespace Microsoft.DotNet.Tools.Compiler
|
|||
{
|
||||
if (projectDependency.Project.Files.SourceFiles.Any())
|
||||
{
|
||||
var projectOutputPath = GetProjectOutput(projectDependency.Project, projectDependency.Framework, configuration, outputPath);
|
||||
var projectOutputPath = GetProjectOutput(projectDependency.Project, projectDependency.Framework, args.ConfigValue, outputPath);
|
||||
references.Add(projectOutputPath);
|
||||
}
|
||||
}
|
||||
|
@ -355,7 +255,7 @@ namespace Microsoft.DotNet.Tools.Compiler
|
|||
compilerArgs.AddRange(references.Select(r => $"--reference:{r}"));
|
||||
|
||||
var runtimeContext = ProjectContext.Create(context.ProjectDirectory, context.TargetFramework, new[] { RuntimeIdentifier.Current });
|
||||
var libraryExporter = runtimeContext.CreateExporter(configuration);
|
||||
var libraryExporter = runtimeContext.CreateExporter(args.ConfigValue);
|
||||
|
||||
if (compilationOptions.PreserveCompilationContext == true)
|
||||
{
|
||||
|
@ -384,7 +284,7 @@ namespace Microsoft.DotNet.Tools.Compiler
|
|||
}
|
||||
}
|
||||
|
||||
if (!AddResources(context.ProjectFile, compilerArgs, intermediateOutputPath))
|
||||
if (!AddNonCultureResources(context.ProjectFile, compilerArgs, intermediateOutputPath))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@ -392,8 +292,7 @@ namespace Microsoft.DotNet.Tools.Compiler
|
|||
var sourceFiles = context.ProjectFile.Files.SourceFiles;
|
||||
compilerArgs.AddRange(sourceFiles);
|
||||
|
||||
var compilerName = context.ProjectFile.CompilerName;
|
||||
compilerName = compilerName ?? "csc";
|
||||
var compilerName = CompilerUtil.ResolveCompilerName(context);
|
||||
|
||||
// Write RSP file
|
||||
var rsp = Path.Combine(intermediateOutputPath, $"dotnet-compile.{context.ProjectFile.Name}.rsp");
|
||||
|
@ -403,7 +302,7 @@ namespace Microsoft.DotNet.Tools.Compiler
|
|||
var contextVariables = new Dictionary<string, string>()
|
||||
{
|
||||
{ "compile:TargetFramework", context.TargetFramework.DotNetFrameworkName },
|
||||
{ "compile:Configuration", configuration },
|
||||
{ "compile:Configuration", args.ConfigValue },
|
||||
{ "compile:OutputFile", outputName },
|
||||
{ "compile:OutputDir", outputPath },
|
||||
{ "compile:ResponseFile", rsp }
|
||||
|
@ -444,10 +343,10 @@ namespace Microsoft.DotNet.Tools.Compiler
|
|||
|
||||
if (success)
|
||||
{
|
||||
success &= GenerateResourceAssemblies(context.ProjectFile, dependencies, outputPath, configuration);
|
||||
success &= GenerateCultureResourceAssemblies(context.ProjectFile, dependencies, outputPath);
|
||||
}
|
||||
|
||||
if (success && !noHost && compilationOptions.EmitEntryPoint.GetValueOrDefault())
|
||||
if (success && !args.NoHostValue && compilationOptions.EmitEntryPoint.GetValueOrDefault())
|
||||
{
|
||||
MakeRunnable(runtimeContext,
|
||||
outputPath,
|
||||
|
@ -481,58 +380,6 @@ namespace Microsoft.DotNet.Tools.Compiler
|
|||
return Path.Combine(outputPath, project.Name + outputExtension);
|
||||
}
|
||||
|
||||
private static string GetOutputPath(ProjectContext context, string configuration, string outputOptionValue)
|
||||
{
|
||||
var outputPath = string.Empty;
|
||||
|
||||
if (string.IsNullOrEmpty(outputOptionValue))
|
||||
{
|
||||
outputPath = Path.Combine(
|
||||
GetDefaultRootOutputPath(context, outputOptionValue),
|
||||
Constants.BinDirectoryName,
|
||||
configuration,
|
||||
context.TargetFramework.GetTwoDigitShortFolderName());
|
||||
}
|
||||
else
|
||||
{
|
||||
outputPath = outputOptionValue;
|
||||
}
|
||||
|
||||
return outputPath;
|
||||
}
|
||||
|
||||
private static string GetIntermediateOutputPath(ProjectContext context, string configuration, string intermediateOutputValue, string outputOptionValue)
|
||||
{
|
||||
var intermediateOutputPath = string.Empty;
|
||||
|
||||
if (string.IsNullOrEmpty(intermediateOutputValue))
|
||||
{
|
||||
intermediateOutputPath = Path.Combine(
|
||||
GetDefaultRootOutputPath(context, outputOptionValue),
|
||||
Constants.ObjDirectoryName,
|
||||
configuration,
|
||||
context.TargetFramework.GetTwoDigitShortFolderName());
|
||||
}
|
||||
else
|
||||
{
|
||||
intermediateOutputPath = intermediateOutputValue;
|
||||
}
|
||||
|
||||
return intermediateOutputPath;
|
||||
}
|
||||
|
||||
private static string GetDefaultRootOutputPath(ProjectContext context, string outputOptionValue)
|
||||
{
|
||||
string rootOutputPath = string.Empty;
|
||||
|
||||
if (string.IsNullOrEmpty(outputOptionValue))
|
||||
{
|
||||
rootOutputPath = context.ProjectFile.ProjectDirectory;
|
||||
}
|
||||
|
||||
return rootOutputPath;
|
||||
}
|
||||
|
||||
private static void CleanOrCreateDirectory(string path)
|
||||
{
|
||||
if (Directory.Exists(path))
|
||||
|
@ -686,147 +533,79 @@ namespace Microsoft.DotNet.Tools.Compiler
|
|||
|
||||
Reporter.Output.WriteLine();
|
||||
|
||||
Reporter.Output.WriteLine($"Time elapsed {sw.Elapsed}");
|
||||
Reporter.Output.WriteLine();
|
||||
Reporter.Output.Write($"Time elapsed {sw.Elapsed}");
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
private static bool AddResources(Project project, List<string> compilerArgs, string intermediateOutputPath)
|
||||
private static bool AddNonCultureResources(Project project, List<string> compilerArgs, string intermediateOutputPath)
|
||||
{
|
||||
var resgenFiles = CompilerUtil.GetNonCultureResources(project, intermediateOutputPath);
|
||||
|
||||
foreach (var resourceFile in project.Files.ResourceFiles)
|
||||
foreach (var resgenFile in resgenFiles)
|
||||
{
|
||||
var resourcePath = resourceFile.Key;
|
||||
|
||||
if (!string.IsNullOrEmpty(ResourceUtility.GetResourceCultureName(resourcePath)))
|
||||
if (ResourceUtility.IsResxFile(resgenFile.InputFile))
|
||||
{
|
||||
// Include only "neutral" resources into main assembly
|
||||
continue;
|
||||
var result =
|
||||
Command.Create("dotnet-resgen",
|
||||
$"\"{resgenFile.InputFile}\" -o \"{resgenFile.OutputFile}\" -v \"{project.Version.Version}\"")
|
||||
.ForwardStdErr()
|
||||
.ForwardStdOut()
|
||||
.Execute();
|
||||
|
||||
if (result.ExitCode != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
compilerArgs.Add($"--resource:\"{resgenFile.OutputFile}\",{Path.GetFileName(resgenFile.MetadataName)}");
|
||||
}
|
||||
else
|
||||
{
|
||||
compilerArgs.Add($"--resource:\"{resgenFile.InputFile}\",{Path.GetFileName(resgenFile.MetadataName)}");
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool GenerateCultureResourceAssemblies(Project project, List<LibraryExport> dependencies, string outputPath)
|
||||
{
|
||||
var referencePaths = CompilerUtil.GetReferencePathsForCultureResgen(dependencies);
|
||||
var resgenReferenceArgs = referencePaths.Select(referencePath => $"-r \"{referencePath}\"").ToList();
|
||||
|
||||
var cultureResgenFiles = CompilerUtil.GetCultureResources(project, outputPath);
|
||||
|
||||
foreach (var resgenFile in cultureResgenFiles)
|
||||
{
|
||||
var resourceOutputPath = Path.GetDirectoryName(resgenFile.OutputFile);
|
||||
|
||||
if (!Directory.Exists(resourceOutputPath))
|
||||
{
|
||||
Directory.CreateDirectory(resourceOutputPath);
|
||||
}
|
||||
|
||||
var name = GetResourceFileMetadataName(project, resourceFile);
|
||||
var arguments = new List<string>();
|
||||
|
||||
var fileName = resourcePath;
|
||||
arguments.AddRange(resgenReferenceArgs);
|
||||
arguments.Add($"-o \"{resgenFile.OutputFile}\"");
|
||||
arguments.Add($"-c {resgenFile.Culture}");
|
||||
arguments.Add($"-v {project.Version.Version}");
|
||||
arguments.AddRange(resgenFile.InputFileToMetadata.Select(fileToMetadata => $"\"{fileToMetadata.Key}\",{fileToMetadata.Value}"));
|
||||
|
||||
if (ResourceUtility.IsResxFile(fileName))
|
||||
{
|
||||
// {file}.resx -> {file}.resources
|
||||
var resourcesFile = Path.Combine(intermediateOutputPath, name);
|
||||
|
||||
var result = Command.Create("dotnet-resgen", $"\"{fileName}\" -o \"{resourcesFile}\" -v \"{project.Version.Version}\"")
|
||||
var result = Command.Create("dotnet-resgen", arguments)
|
||||
.ForwardStdErr()
|
||||
.ForwardStdOut()
|
||||
.Execute();
|
||||
|
||||
if (result.ExitCode != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Use this as the resource name instead
|
||||
fileName = resourcesFile;
|
||||
if (result.ExitCode != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
compilerArgs.Add($"--resource:\"{fileName}\",{name}");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static string GetResourceFileMetadataName(Project project, KeyValuePair<string, string> resourceFile)
|
||||
{
|
||||
string resourceName = null;
|
||||
string rootNamespace = null;
|
||||
|
||||
string root = PathUtility.EnsureTrailingSlash(project.ProjectDirectory);
|
||||
string resourcePath = resourceFile.Key;
|
||||
if (string.IsNullOrEmpty(resourceFile.Value))
|
||||
{
|
||||
// No logical name, so use the file name
|
||||
resourceName = ResourceUtility.GetResourceName(root, resourcePath);
|
||||
rootNamespace = project.Name;
|
||||
}
|
||||
else
|
||||
{
|
||||
resourceName = ResourceManifestName.EnsureResourceExtension(resourceFile.Value, resourcePath);
|
||||
rootNamespace = null;
|
||||
}
|
||||
|
||||
var name = ResourceManifestName.CreateManifestName(resourceName, rootNamespace);
|
||||
return name;
|
||||
}
|
||||
|
||||
private static bool GenerateResourceAssemblies(Project project, List<LibraryExport> dependencies, string outputPath, string configuration)
|
||||
{
|
||||
var references = dependencies.SelectMany(libraryExport => libraryExport.CompilationAssemblies);
|
||||
|
||||
foreach (var resourceFileGroup in project.Files.ResourceFiles.GroupBy(file => ResourceUtility.GetResourceCultureName(file.Key)))
|
||||
{
|
||||
var culture = resourceFileGroup.Key;
|
||||
if (!string.IsNullOrEmpty(culture))
|
||||
{
|
||||
var resourceOutputPath = Path.Combine(outputPath, culture);
|
||||
|
||||
if (!Directory.Exists(resourceOutputPath))
|
||||
{
|
||||
Directory.CreateDirectory(resourceOutputPath);
|
||||
}
|
||||
|
||||
var resourceOuputFile = Path.Combine(resourceOutputPath, project.Name + ".resources.dll");
|
||||
|
||||
var arguments = new List<string>();
|
||||
arguments.AddRange(references.Select(r => $"-r \"{r.ResolvedPath}\""));
|
||||
arguments.Add($"-o \"{resourceOuputFile}\"");
|
||||
arguments.Add($"-c {culture}");
|
||||
arguments.Add($"-v {project.Version.Version}");
|
||||
|
||||
foreach (var resourceFile in resourceFileGroup)
|
||||
{
|
||||
var metadataName = GetResourceFileMetadataName(project, resourceFile);
|
||||
arguments.Add($"\"{resourceFile.Key}\",{metadataName}");
|
||||
}
|
||||
|
||||
var result = Command.Create("dotnet-resgen", arguments)
|
||||
.ForwardStdErr()
|
||||
.ForwardStdOut()
|
||||
.Execute();
|
||||
if (result.ExitCode != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static ISet<ProjectDescription> Sort(Dictionary<string, ProjectDescription> projects)
|
||||
{
|
||||
var outputs = new HashSet<ProjectDescription>();
|
||||
|
||||
foreach (var pair in projects)
|
||||
{
|
||||
Sort(pair.Value, projects, outputs);
|
||||
}
|
||||
|
||||
return outputs;
|
||||
}
|
||||
|
||||
private static void Sort(ProjectDescription project, Dictionary<string, ProjectDescription> projects, ISet<ProjectDescription> outputs)
|
||||
{
|
||||
// Sorts projects in dependency order so that we only build them once per chain
|
||||
foreach (var dependency in project.Dependencies)
|
||||
{
|
||||
ProjectDescription projectDependency;
|
||||
if (projects.TryGetValue(dependency.Name, out projectDependency))
|
||||
{
|
||||
Sort(projectDependency, projects, outputs);
|
||||
}
|
||||
}
|
||||
|
||||
outputs.Add(project);
|
||||
}
|
||||
|
||||
private static DiagnosticMessage ParseDiagnostic(string projectRootPath, string line)
|
||||
{
|
||||
var error = CanonicalError.Parse(line);
|
||||
|
|
|
@ -2,13 +2,15 @@ dotnet-compile
|
|||
===========
|
||||
|
||||
**NAME**
|
||||
dotnet-compile -- Compiles source files to a binary format and saves to a target file.
|
||||
dotnet-compile -- Compiles source files for a single project to a binary format and saves to a target file.
|
||||
|
||||
**SYNOPSIS**
|
||||
dotnet compile [options]
|
||||
|
||||
**DESCRIPTION**
|
||||
The compile command compiles source files to a binary file, either IL byte code or native machine code, depending on the options provided. The default option is compilation to IL byte code, but may change in the future.
|
||||
The compile command compiles source files from a single project to a binary file, either IL byte code or native machine code, depending on the options provided. The default option is compilation to IL byte code, but may change in the future.
|
||||
|
||||
Users who want to benefit from incremental builds and who want to compile both the project and its dependencies should use the Build command.
|
||||
|
||||
The default IL [--il] output is a PE32 exe [exe], with the default extension of ".exe" on all OSes. The exe must include a public static void or public static int main entry point, or it is an error. The dll [dll] output option has the default extension of ".dll".
|
||||
|
||||
|
@ -48,4 +50,4 @@ Compiles source to IL byte code, which is (typically) portable across machine ty
|
|||
Specifies the filename to be used. It is an error not to specify an output filename. If no extension is provided, the default one is provided for the output type.
|
||||
|
||||
-v, --verbose
|
||||
Prints verbose logging information, to follow the flow of execution of the command.
|
||||
Prints verbose logging information, to follow the flow of execution of the command.
|
|
@ -101,7 +101,7 @@ namespace Microsoft.DotNet.Tools.Compiler
|
|||
|
||||
argsBuilder.Append($" \"{path}\"");
|
||||
|
||||
var result = Command.Create("dotnet-compile", argsBuilder.ToString())
|
||||
var result = Command.Create("dotnet-build", argsBuilder.ToString())
|
||||
.ForwardStdOut()
|
||||
.ForwardStdErr()
|
||||
.Execute();
|
||||
|
|
|
@ -119,7 +119,7 @@ namespace Microsoft.DotNet.Tools.Publish
|
|||
}
|
||||
|
||||
// Compile the project (and transitively, all it's dependencies)
|
||||
var result = Command.Create("dotnet-compile",
|
||||
var result = Command.Create("dotnet-build",
|
||||
$"--framework \"{context.TargetFramework.DotNetFrameworkName}\" " +
|
||||
$"--output \"{outputPath}\" " +
|
||||
$"--configuration \"{configuration}\" " +
|
||||
|
|
|
@ -90,7 +90,7 @@ namespace Microsoft.DotNet.Tools.Run
|
|||
var tempDir = Path.Combine(_context.ProjectDirectory, "bin", ".dotnetrun", Guid.NewGuid().ToString("N"));
|
||||
|
||||
// Compile to that directory
|
||||
var result = Command.Create($"dotnet-compile", $"--output \"{tempDir}\" --temp-output \"{tempDir}\" --framework \"{_context.TargetFramework}\" --configuration \"{Configuration}\" {_context.ProjectFile.ProjectDirectory}")
|
||||
var result = Command.Create($"dotnet-build", $"--output \"{tempDir}\" --temp-output \"{tempDir}\" --framework \"{_context.TargetFramework}\" --configuration \"{Configuration}\" {_context.ProjectFile.ProjectDirectory}")
|
||||
.ForwardStdOut(onlyIfVerbose: true)
|
||||
.ForwardStdErr()
|
||||
.Execute();
|
||||
|
|
|
@ -46,7 +46,7 @@ namespace ConsoleApplication
|
|||
{
|
||||
TestSetup();
|
||||
|
||||
TestRunCommand("dotnet", $"compile -o {OutputDirectory}");
|
||||
TestRunCommand("dotnet", $"build -o {OutputDirectory}");
|
||||
TestOutputExecutable(OutputDirectory);
|
||||
}
|
||||
|
||||
|
@ -60,7 +60,7 @@ namespace ConsoleApplication
|
|||
|
||||
TestSetup();
|
||||
|
||||
TestRunCommand("dotnet", $"compile --native -o {OutputDirectory}");
|
||||
TestRunCommand("dotnet", $"build --native -o {OutputDirectory}");
|
||||
|
||||
var nativeOut = Path.Combine(OutputDirectory, "native");
|
||||
TestOutputExecutable(nativeOut);
|
||||
|
@ -71,7 +71,7 @@ namespace ConsoleApplication
|
|||
{
|
||||
TestSetup();
|
||||
|
||||
TestRunCommand("dotnet", $"compile --native --cpp -o {OutputDirectory}");
|
||||
TestRunCommand("dotnet", $"build --native --cpp -o {OutputDirectory}");
|
||||
|
||||
var nativeOut = Path.Combine(OutputDirectory, "native");
|
||||
TestOutputExecutable(nativeOut);
|
||||
|
|
Loading…
Reference in a new issue