2015-11-16 11:21:57 -08:00
// 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 ;
2015-10-13 14:31:29 -07:00
using System.Collections.Generic ;
2015-11-03 01:08:57 -08:00
using System.Diagnostics ;
2015-10-06 10:46:43 -07:00
using System.IO ;
using System.Linq ;
2015-10-20 13:27:56 -07:00
2015-10-06 10:46:43 -07:00
using Microsoft.Dnx.Runtime.Common.CommandLine ;
using Microsoft.DotNet.Cli.Utils ;
2015-10-20 13:27:56 -07:00
using Microsoft.DotNet.Cli.Compiler.Common ;
2015-10-18 19:02:09 -07:00
using Microsoft.DotNet.Tools.Common ;
2015-11-27 16:19:54 -08:00
using Microsoft.DotNet.ProjectModel ;
using Microsoft.DotNet.ProjectModel.Compilation ;
2015-10-13 14:31:29 -07:00
using NuGet.Frameworks ;
2015-11-29 10:58:13 -08:00
using Microsoft.DotNet.ProjectModel.Utilities ;
2015-10-06 10:46:43 -07:00
namespace Microsoft.DotNet.Tools.Compiler
{
public class Program
{
public static int Main ( string [ ] args )
{
2015-10-13 14:31:29 -07:00
DebugHelper . HandleDebugSwitch ( ref args ) ;
2015-10-06 10:46:43 -07:00
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 ) ;
2015-11-01 05:50:39 -08:00
var intermediateOutput = app . Option ( "-t|--temp-output <OUTPUT_DIR>" , "Directory in which to place temporary outputs" , CommandOptionType . SingleValue ) ;
2015-10-13 14:31:29 -07:00
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 ) ;
2015-10-17 23:43:28 -07:00
var noProjectDependencies = app . Option ( "--no-project-dependencies" , "Skips building project references." , CommandOptionType . NoValue ) ;
2015-11-30 12:17:41 -08:00
var noHost = app . Option ( "--no-host" , "Set this to skip publishing a runtime host when building for CoreCLR" , CommandOptionType . NoValue ) ;
2015-10-06 10:46:43 -07:00
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" ) ;
2015-11-17 15:37:30 -08:00
// Native Args
2015-10-30 15:13:53 -07:00
var native = app . Option ( "-n|--native" , "Compiles source to native machine code." , CommandOptionType . NoValue ) ;
2015-11-17 15:37:30 -08:00
var arch = app . Option ( "-a|--arch <ARCH>" , "The architecture for which to compile. x64 only currently supported." , CommandOptionType . SingleValue ) ;
2015-11-19 17:31:40 -08:00
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 ) ;
2015-11-27 00:28:27 -08:00
var ilcSdkPath = app . Option ( "--ilcsdkpath <PATH>" , "Path to the folder containing ILCompiler application dependencies." , CommandOptionType . SingleValue ) ;
2015-11-17 15:37:30 -08:00
var cppMode = app . Option ( "--cpp" , "Flag to do native compilation with C++ code generator." , CommandOptionType . NoValue ) ;
2015-10-06 10:46:43 -07:00
app . OnExecute ( ( ) = >
{
// Locate the project and get the name and full path
var path = project . Value ;
if ( string . IsNullOrEmpty ( path ) )
{
path = Directory . GetCurrentDirectory ( ) ;
}
2015-10-13 14:31:29 -07:00
2015-10-18 07:41:59 -07:00
var buildProjectReferences = ! noProjectDependencies . HasValue ( ) ;
2015-10-29 12:03:01 -07:00
var isNative = native . HasValue ( ) ;
2015-11-17 15:37:30 -08:00
var isCppMode = cppMode . HasValue ( ) ;
var archValue = arch . Value ( ) ;
var ilcArgsValue = ilcArgs . Value ( ) ;
2015-11-19 17:31:40 -08:00
var ilcPathValue = ilcPath . Value ( ) ;
2015-11-27 00:28:27 -08:00
var ilcSdkPathValue = ilcSdkPath . Value ( ) ;
2015-10-30 10:34:02 -07:00
var configValue = configuration . Value ( ) ? ? Constants . DefaultConfiguration ;
var outputValue = output . Value ( ) ;
2015-11-17 15:37:30 -08:00
var intermediateValue = intermediateOutput . Value ( ) ;
2015-10-29 12:03:01 -07:00
2015-10-13 14:31:29 -07:00
// Load project contexts for each framework and compile them
bool success = true ;
2015-10-30 10:34:02 -07:00
var contexts = framework . HasValue ( ) ?
framework . Values . Select ( f = > ProjectContext . Create ( path , NuGetFramework . Parse ( f ) ) ) :
ProjectContext . CreateContextForEachFramework ( path ) ;
foreach ( var context in contexts )
2015-10-06 10:46:43 -07:00
{
2015-12-07 14:18:09 -08:00
success & = Compile ( context , configValue , outputValue , intermediateValue , buildProjectReferences , noHost . HasValue ( ) ) ;
2015-11-06 17:30:11 -08:00
if ( isNative & & success )
2015-10-13 14:31:29 -07:00
{
2015-11-27 00:28:27 -08:00
success & = CompileNative ( context , configValue , outputValue , buildProjectReferences , intermediateValue , archValue , ilcArgsValue , ilcPathValue , ilcSdkPathValue , isCppMode ) ;
2015-10-13 14:31:29 -07:00
}
2015-10-06 10:46:43 -07:00
}
2015-11-01 16:21:10 -08:00
2015-10-13 14:31:29 -07:00
return success ? 0 : 1 ;
2015-10-06 10:46:43 -07:00
} ) ;
try
{
return app . Execute ( args ) ;
}
2015-10-18 02:06:15 -07:00
catch ( Exception ex )
2015-10-06 10:46:43 -07:00
{
2015-10-20 01:43:37 -07:00
#if DEBUG
Console . Error . WriteLine ( ex ) ;
#else
2015-10-06 10:46:43 -07:00
Console . Error . WriteLine ( ex . Message ) ;
2015-10-20 01:43:37 -07:00
#endif
2015-10-06 10:46:43 -07:00
return 1 ;
}
}
2015-11-18 11:56:35 -08:00
private static bool CompileNative (
ProjectContext context ,
string configuration ,
string outputOptionValue ,
bool buildProjectReferences ,
string intermediateOutputValue ,
string archValue ,
string ilcArgsValue ,
2015-11-19 17:31:40 -08:00
string ilcPathValue ,
2015-11-27 00:28:27 -08:00
string ilcSdkPathValue ,
2015-11-18 11:56:35 -08:00
bool isCppMode )
2015-10-30 14:48:09 -07:00
{
2015-11-18 11:56:35 -08:00
var outputPath = GetOutputPath ( context , configuration , outputOptionValue ) ;
var nativeOutputPath = Path . Combine ( GetOutputPath ( context , configuration , outputOptionValue ) , "native" ) ;
var intermediateOutputPath =
GetIntermediateOutputPath ( context , configuration , intermediateOutputValue , outputOptionValue ) ;
2015-11-17 15:37:30 -08:00
2015-11-18 11:56:35 -08:00
Directory . CreateDirectory ( nativeOutputPath ) ;
2015-11-17 15:37:30 -08:00
Directory . CreateDirectory ( intermediateOutputPath ) ;
2015-11-01 16:21:10 -08:00
2015-10-29 13:29:38 -07:00
var compilationOptions = context . ProjectFile . GetCompilerOptions ( context . TargetFramework , configuration ) ;
2015-11-18 11:56:35 -08:00
var managedOutput =
GetProjectOutput ( context . ProjectFile , context . TargetFramework , configuration , outputPath ) ;
2015-11-17 15:37:30 -08:00
var nativeArgs = new List < string > ( ) ;
// Input Assembly
2015-11-18 11:56:35 -08:00
nativeArgs . Add ( $"{managedOutput}" ) ;
2015-11-17 15:37:30 -08:00
// ILC Args
2015-11-18 11:56:35 -08:00
if ( ! string . IsNullOrWhiteSpace ( ilcArgsValue ) )
{
nativeArgs . Add ( "--ilcargs" ) ;
nativeArgs . Add ( $"{ilcArgsValue}" ) ;
}
2015-11-19 17:31:40 -08:00
// ILC Path
if ( ! string . IsNullOrWhiteSpace ( ilcPathValue ) )
{
nativeArgs . Add ( "--ilcpath" ) ;
nativeArgs . Add ( ilcPathValue ) ;
}
2015-11-17 15:37:30 -08:00
2015-11-27 00:28:27 -08:00
// ILC SDK Path
if ( ! string . IsNullOrWhiteSpace ( ilcSdkPathValue ) )
{
nativeArgs . Add ( "--appdepsdk" ) ;
nativeArgs . Add ( ilcSdkPathValue ) ;
}
2015-11-17 15:37:30 -08:00
// CodeGen Mode
if ( isCppMode )
{
nativeArgs . Add ( "--mode" ) ;
nativeArgs . Add ( "cpp" ) ;
}
// Configuration
if ( configuration ! = null )
{
nativeArgs . Add ( "--configuration" ) ;
nativeArgs . Add ( configuration ) ;
}
// Architecture
if ( archValue ! = null )
{
nativeArgs . Add ( "--arch" ) ;
nativeArgs . Add ( archValue ) ;
}
// Intermediate Path
nativeArgs . Add ( "--temp-output" ) ;
2015-11-18 11:56:35 -08:00
nativeArgs . Add ( $"{intermediateOutputPath}" ) ;
2015-11-17 15:37:30 -08:00
// Output Path
nativeArgs . Add ( "--output" ) ;
2015-11-18 11:56:35 -08:00
nativeArgs . Add ( $"{nativeOutputPath}" ) ;
2015-11-17 15:45:54 -08:00
2015-11-17 15:37:30 -08:00
// Write Response File
var rsp = Path . Combine ( intermediateOutputPath , $"dotnet-compile-native.{context.ProjectFile.Name}.rsp" ) ;
File . WriteAllLines ( rsp , nativeArgs ) ;
// TODO Add -r assembly.dll for all Nuget References
// Need CoreRT Framework published to nuget
2015-11-01 16:21:10 -08:00
2015-10-29 12:03:01 -07:00
// Do Native Compilation
2015-11-18 11:56:35 -08:00
var result = Command . Create ( "dotnet-compile-native" , $"--rsp \" { rsp } \ "" )
2015-10-29 12:03:01 -07:00
. ForwardStdErr ( )
. ForwardStdOut ( )
. Execute ( ) ;
2015-11-01 16:21:10 -08:00
2015-10-29 13:29:38 -07:00
return result . ExitCode = = 0 ;
2015-10-29 12:03:01 -07:00
}
2015-11-30 12:17:41 -08:00
private static bool Compile ( ProjectContext context , string configuration , string outputOptionValue , string intermediateOutputValue , bool buildProjectReferences , bool noHost )
2015-10-06 10:46:43 -07:00
{
2015-11-01 05:40:19 -08:00
// Set up Output Paths
2015-10-29 12:03:01 -07:00
string outputPath = GetOutputPath ( context , configuration , outputOptionValue ) ;
2015-11-01 05:50:39 -08:00
string intermediateOutputPath = GetIntermediateOutputPath ( context , configuration , intermediateOutputValue , outputOptionValue ) ;
2015-10-29 12:03:01 -07:00
2015-11-01 05:40:19 -08:00
Directory . CreateDirectory ( outputPath ) ;
Directory . CreateDirectory ( intermediateOutputPath ) ;
2015-10-29 12:03:01 -07:00
2015-10-13 14:31:29 -07:00
// Create the library exporter
var exporter = context . CreateExporter ( configuration ) ;
// Gather exports for the project
2015-11-01 16:21:10 -08:00
var dependencies = exporter . GetDependencies ( ) . ToList ( ) ;
2015-10-13 14:31:29 -07:00
2015-10-18 07:41:59 -07:00
if ( buildProjectReferences )
2015-10-06 10:46:43 -07:00
{
2015-10-17 23:43:28 -07:00
var projects = new Dictionary < string , ProjectDescription > ( ) ;
// Build project references
2015-11-01 05:40:19 -08:00
foreach ( var dependency in dependencies )
2015-10-13 14:31:29 -07:00
{
2015-10-17 23:43:28 -07:00
var projectDependency = dependency . Library as ProjectDescription ;
2015-11-01 05:40:19 -08:00
if ( projectDependency ! = null & & projectDependency . Project . Files . SourceFiles . Any ( ) )
2015-10-17 23:43:28 -07:00
{
projects [ projectDependency . Identity . Name ] = projectDependency ;
}
}
foreach ( var projectDependency in Sort ( projects ) )
{
// Skip compiling project dependencies since we've already figured out the build order
2015-11-30 12:17:41 -08:00
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 } \ "" )
2015-10-17 23:43:28 -07:00
. ForwardStdOut ( )
. ForwardStdErr ( )
2015-10-21 03:11:27 -07:00
. Execute ( ) ;
2015-10-17 23:43:28 -07:00
2015-10-13 14:31:29 -07:00
if ( compileResult . ExitCode ! = 0 )
{
return false ;
}
}
2015-10-18 07:41:59 -07:00
projects . Clear ( ) ;
2015-10-06 10:46:43 -07:00
}
2015-11-30 12:17:41 -08:00
return CompileProject ( context , configuration , outputPath , intermediateOutputPath , dependencies , noHost ) ;
2015-11-17 18:02:08 -08:00
}
2015-11-30 12:17:41 -08:00
private static bool CompileProject ( ProjectContext context , string configuration , string outputPath , string intermediateOutputPath , List < LibraryExport > dependencies , bool noHost )
2015-11-17 18:02:08 -08:00
{
2015-11-01 16:21:10 -08:00
Reporter . Output . WriteLine ( $"Compiling {context.RootProject.Identity.Name.Yellow()} for {context.TargetFramework.DotNetFrameworkName.Yellow()}" ) ;
2015-11-03 01:08:57 -08:00
var sw = Stopwatch . StartNew ( ) ;
2015-11-01 16:21:10 -08:00
2015-11-07 23:53:05 -08:00
var diagnostics = new List < DiagnosticMessage > ( ) ;
var missingFrameworkDiagnostics = new List < DiagnosticMessage > ( ) ;
// Collect dependency diagnostics
foreach ( var diag in context . LibraryManager . GetAllDiagnostics ( ) )
{
if ( diag . ErrorCode = = ErrorCodes . DOTNET1011 | |
diag . ErrorCode = = ErrorCodes . DOTNET1012 )
{
missingFrameworkDiagnostics . Add ( diag ) ;
}
diagnostics . Add ( diag ) ;
}
if ( missingFrameworkDiagnostics . Count > 0 )
{
// The framework isn't installed so we should short circuit the rest of the compilation
// so we don't get flooded with errors
PrintSummary ( missingFrameworkDiagnostics , sw ) ;
return false ;
}
2015-10-13 14:31:29 -07:00
// Dump dependency data
2015-11-17 18:02:08 -08:00
ShowDependencyInfo ( dependencies ) ;
2015-10-13 14:31:29 -07:00
// Get compilation options
2015-11-01 05:40:19 -08:00
var outputName = GetProjectOutput ( context . ProjectFile , context . TargetFramework , configuration , outputPath ) ;
2015-10-21 15:21:14 -07:00
2015-10-18 01:17:13 -07:00
// Assemble args
var compilerArgs = new List < string > ( )
2015-10-06 10:46:43 -07:00
{
2015-11-04 12:04:51 -08:00
$"--temp-output:{intermediateOutputPath}" ,
$"--out:{outputName}"
2015-10-13 14:31:29 -07:00
} ;
2015-10-18 19:02:09 -07:00
2015-11-01 05:40:19 -08:00
var compilationOptions = context . ProjectFile . GetCompilerOptions ( context . TargetFramework , configuration ) ;
2015-11-06 05:24:13 -08:00
if ( ! string . IsNullOrEmpty ( compilationOptions . KeyFile ) )
{
// Resolve full path to key file
compilationOptions . KeyFile = Path . GetFullPath ( Path . Combine ( context . ProjectFile . ProjectDirectory , compilationOptions . KeyFile ) ) ;
}
2015-11-17 18:02:08 -08:00
2015-10-13 14:31:29 -07:00
// Add compilation options to the args
2015-10-20 13:27:56 -07:00
compilerArgs . AddRange ( compilationOptions . SerializeToArgs ( ) ) ;
2015-10-13 14:31:29 -07:00
foreach ( var dependency in dependencies )
{
2015-11-01 05:40:19 -08:00
var projectDependency = dependency . Library as ProjectDescription ;
if ( projectDependency ! = null )
{
if ( projectDependency . Project . Files . SourceFiles . Any ( ) )
{
var projectOutputPath = GetProjectOutput ( projectDependency . Project , projectDependency . Framework , configuration , outputPath ) ;
2015-11-07 01:52:59 -08:00
compilerArgs . Add ( $"--reference:{projectOutputPath}" ) ;
2015-11-01 05:40:19 -08:00
}
}
else
{
2015-11-07 01:52:59 -08:00
compilerArgs . AddRange ( dependency . CompilationAssemblies . Select ( r = > $"--reference:{r.ResolvedPath}" ) ) ;
2015-11-01 05:40:19 -08:00
}
2015-11-04 12:04:51 -08:00
compilerArgs . AddRange ( dependency . SourceReferences ) ;
2015-10-06 10:46:43 -07:00
}
2015-10-18 21:07:48 -07:00
if ( ! AddResources ( context . ProjectFile , compilerArgs , intermediateOutputPath ) )
{
return false ;
}
2015-10-18 19:02:09 -07:00
2015-11-07 01:52:59 -08:00
// Add project source files
var sourceFiles = context . ProjectFile . Files . SourceFiles ;
compilerArgs . AddRange ( sourceFiles ) ;
2015-10-23 15:21:49 -07:00
var compilerName = context . ProjectFile . CompilerName ;
2015-10-26 09:23:58 -07:00
compilerName = compilerName ? ? "csc" ;
2015-10-13 14:31:29 -07:00
// Write RSP file
2015-10-20 13:27:56 -07:00
var rsp = Path . Combine ( intermediateOutputPath , $"dotnet-compile.{context.ProjectFile.Name}.rsp" ) ;
2015-10-18 01:17:13 -07:00
File . WriteAllLines ( rsp , compilerArgs ) ;
2015-11-17 18:02:08 -08:00
// Run pre-compile event
var contextVariables = new Dictionary < string , string > ( )
{
{ "compile:TargetFramework" , context . TargetFramework . DotNetFrameworkName } ,
{ "compile:Configuration" , configuration } ,
{ "compile:OutputFile" , outputName } ,
{ "compile:OutputDir" , outputPath } ,
{ "compile:ResponseFile" , rsp }
} ;
RunScripts ( context , ScriptNames . PreCompile , contextVariables ) ;
2015-10-20 13:27:56 -07:00
var result = Command . Create ( $"dotnet-compile-{compilerName}" , $"@\" { rsp } \ "" )
2015-11-17 18:02:08 -08:00
. OnErrorLine ( line = >
{
var diagnostic = ParseDiagnostic ( context . ProjectDirectory , line ) ;
if ( diagnostic ! = null )
{
diagnostics . Add ( diagnostic ) ;
}
else
{
Reporter . Error . WriteLine ( line ) ;
}
} )
. OnOutputLine ( line = >
{
var diagnostic = ParseDiagnostic ( context . ProjectDirectory , line ) ;
if ( diagnostic ! = null )
{
diagnostics . Add ( diagnostic ) ;
}
else
{
Reporter . Output . WriteLine ( line ) ;
}
} ) . Execute ( ) ;
// Run post-compile event
contextVariables [ "compile:CompilerExitCode" ] = result . ExitCode . ToString ( ) ;
RunScripts ( context , ScriptNames . PostCompile , contextVariables ) ;
2015-10-18 01:17:13 -07:00
2015-10-21 15:21:14 -07:00
var success = result . ExitCode = = 0 ;
2015-11-30 12:17:41 -08:00
if ( success & & ! noHost & & compilationOptions . EmitEntryPoint . GetValueOrDefault ( ) )
2015-10-21 15:21:14 -07:00
{
2015-11-01 02:04:26 -08:00
var runtimeContext = ProjectContext . Create ( context . ProjectDirectory , context . TargetFramework , new [ ] { RuntimeIdentifier . Current } ) ;
2015-11-01 16:21:10 -08:00
MakeRunnable ( runtimeContext ,
outputPath ,
2015-11-01 02:04:26 -08:00
runtimeContext . CreateExporter ( configuration ) ) ;
2015-10-21 15:21:14 -07:00
}
2015-11-12 03:50:38 -08:00
return PrintSummary ( diagnostics , sw , success ) ;
2015-10-21 00:26:57 -07:00
}
2015-11-01 05:40:19 -08:00
2015-11-17 18:02:08 -08:00
private static void RunScripts ( ProjectContext context , string name , Dictionary < string , string > contextVariables )
{
foreach ( var script in context . ProjectFile . Scripts . GetOrEmpty ( name ) )
{
ScriptExecutor . CreateCommandForScript ( context . ProjectFile , script , contextVariables )
. ForwardStdErr ( )
. ForwardStdOut ( )
. Execute ( ) ;
}
}
2015-11-01 05:40:19 -08:00
private static string GetProjectOutput ( Project project , NuGetFramework framework , string configuration , string outputPath )
{
var compilationOptions = project . GetCompilerOptions ( framework , configuration ) ;
var outputExtension = ".dll" ;
if ( framework . IsDesktop ( ) & & compilationOptions . EmitEntryPoint . GetValueOrDefault ( ) )
{
outputExtension = ".exe" ;
}
return Path . Combine ( outputPath , project . Name + outputExtension ) ;
}
2015-10-29 12:03:01 -07:00
private static string GetOutputPath ( ProjectContext context , string configuration , string outputOptionValue )
{
2015-10-30 14:48:09 -07:00
var outputPath = string . Empty ;
2015-10-21 00:26:57 -07:00
2015-10-29 12:03:01 -07:00
if ( string . IsNullOrEmpty ( outputOptionValue ) )
{
outputPath = Path . Combine (
GetDefaultRootOutputPath ( context , outputOptionValue ) ,
Constants . BinDirectoryName ,
configuration ,
context . TargetFramework . GetTwoDigitShortFolderName ( ) ) ;
}
else
{
outputPath = outputOptionValue ;
}
return outputPath ;
}
2015-11-01 05:50:39 -08:00
private static string GetIntermediateOutputPath ( ProjectContext context , string configuration , string intermediateOutputValue , string outputOptionValue )
2015-10-29 12:03:01 -07:00
{
2015-11-01 05:40:19 -08:00
var intermediateOutputPath = string . Empty ;
2015-10-29 12:03:01 -07:00
2015-11-01 05:50:39 -08:00
if ( string . IsNullOrEmpty ( intermediateOutputValue ) )
2015-10-29 12:03:01 -07:00
{
intermediateOutputPath = Path . Combine (
GetDefaultRootOutputPath ( context , outputOptionValue ) ,
Constants . ObjDirectoryName ,
configuration ,
context . TargetFramework . GetTwoDigitShortFolderName ( ) ) ;
}
else
{
2015-11-01 05:50:39 -08:00
intermediateOutputPath = intermediateOutputValue ;
2015-10-29 12:03:01 -07:00
}
return intermediateOutputPath ;
}
2015-10-29 13:29:38 -07:00
private static string GetDefaultRootOutputPath ( ProjectContext context , string outputOptionValue )
2015-10-29 12:03:01 -07:00
{
2015-11-01 05:40:19 -08:00
string rootOutputPath = string . Empty ;
2015-10-29 12:03:01 -07:00
if ( string . IsNullOrEmpty ( outputOptionValue ) )
{
2015-11-01 16:21:10 -08:00
rootOutputPath = context . ProjectFile . ProjectDirectory ;
2015-10-29 12:03:01 -07:00
}
return rootOutputPath ;
}
private static void CleanOrCreateDirectory ( string path )
{
if ( Directory . Exists ( path ) )
2015-11-01 16:21:10 -08:00
{
2015-10-30 14:56:10 -07:00
try
{
Directory . Delete ( path , recursive : true ) ;
}
2015-11-01 16:21:10 -08:00
catch ( Exception e )
2015-10-30 14:56:10 -07:00
{
Console . WriteLine ( "Unable to remove directory: " + path ) ;
Console . WriteLine ( e . Message ) ;
}
2015-10-29 12:03:01 -07:00
}
2015-11-01 16:21:10 -08:00
2015-10-29 13:29:38 -07:00
Directory . CreateDirectory ( path ) ;
2015-10-29 12:03:01 -07:00
}
2015-11-01 02:04:26 -08:00
2015-11-01 04:06:12 -08:00
private static void MakeRunnable ( ProjectContext runtimeContext , string outputPath , LibraryExporter exporter )
2015-11-01 02:04:26 -08:00
{
2015-11-06 06:15:26 -08:00
CopyContents ( runtimeContext , outputPath ) ;
2015-11-01 02:04:26 -08:00
if ( runtimeContext . TargetFramework . IsDesktop ( ) )
{
// On desktop we need to copy dependencies since we don't own the host
2015-11-01 16:21:10 -08:00
foreach ( var export in exporter . GetDependencies ( ) )
2015-11-01 02:04:26 -08:00
{
2015-11-01 16:21:10 -08:00
CopyExport ( outputPath , export ) ;
2015-11-01 02:04:26 -08:00
}
2015-12-10 01:44:50 -08:00
2015-12-09 09:44:50 -08:00
GenerateBindingRedirects ( runtimeContext , outputPath , exporter ) ;
2015-11-01 02:04:26 -08:00
}
else
{
2015-11-01 04:06:12 -08:00
EmitHost ( runtimeContext , outputPath , exporter ) ;
2015-11-01 02:04:26 -08:00
}
}
2015-11-01 16:21:10 -08:00
2015-12-09 09:44:50 -08:00
private static void GenerateBindingRedirects ( ProjectContext runtimeContext , string outputPath , LibraryExporter exporter )
{
var generator = new BindingRedirectGenerator ( ) ;
var config = generator . Generate ( exporter . GetAllExports ( ) ) ;
2015-12-10 01:44:50 -08:00
if ( config ! = null )
2015-12-09 09:44:50 -08:00
{
2015-12-10 01:44:50 -08:00
// TODO: Handle existing App.config file transformation
// We have something to generate
var path = Path . Combine ( outputPath , runtimeContext . ProjectFile . Name + ".exe.config" ) ;
using ( var stream = File . Create ( path ) )
{
config . Save ( stream ) ;
}
2015-12-09 09:44:50 -08:00
}
}
2015-11-01 16:21:10 -08:00
private static void CopyExport ( string outputPath , LibraryExport export )
{
CopyFiles ( export . RuntimeAssemblies , outputPath ) ;
CopyFiles ( export . NativeLibraries , outputPath ) ;
}
2015-11-01 04:06:12 -08:00
private static void EmitHost ( ProjectContext runtimeContext , string outputPath , LibraryExporter exporter )
2015-10-21 15:21:14 -07:00
{
// Write the Host information file (basically a simplified form of the lock file)
2015-11-01 02:04:26 -08:00
var lines = new List < string > ( ) ;
2015-11-01 16:21:10 -08:00
foreach ( var export in exporter . GetAllExports ( ) )
2015-10-21 15:21:14 -07:00
{
2015-11-01 16:21:10 -08:00
if ( export . Library = = runtimeContext . RootProject )
2015-11-01 04:06:12 -08:00
{
2015-11-01 05:40:19 -08:00
continue ;
2015-11-01 04:06:12 -08:00
}
2015-11-01 16:21:10 -08:00
if ( export . Library is ProjectDescription )
{
// Copy project dependencies to the output folder
CopyFiles ( export . RuntimeAssemblies , outputPath ) ;
CopyFiles ( export . NativeLibraries , outputPath ) ;
}
2015-11-01 04:06:12 -08:00
else
{
lines . AddRange ( GenerateLines ( export , export . RuntimeAssemblies , "runtime" ) ) ;
lines . AddRange ( GenerateLines ( export , export . NativeLibraries , "native" ) ) ;
}
2015-10-21 15:21:14 -07:00
}
2015-11-01 04:06:12 -08:00
File . WriteAllLines ( Path . Combine ( outputPath , runtimeContext . ProjectFile . Name + ".deps" ) , lines ) ;
2015-10-21 15:21:14 -07:00
// Copy the host in
2015-11-01 04:06:12 -08:00
CopyHost ( Path . Combine ( outputPath , runtimeContext . ProjectFile . Name + Constants . ExeSuffix ) ) ;
2015-10-21 15:21:14 -07:00
}
private static void CopyHost ( string target )
{
var hostPath = Path . Combine ( AppContext . BaseDirectory , Constants . HostExecutableName ) ;
2015-11-01 05:40:19 -08:00
File . Copy ( hostPath , target , overwrite : true ) ;
2015-10-21 15:21:14 -07:00
}
private static IEnumerable < string > GenerateLines ( LibraryExport export , IEnumerable < LibraryAsset > items , string type )
{
return items . Select ( item = >
EscapeCsv ( export . Library . Identity . Type . Value ) + "," +
EscapeCsv ( export . Library . Identity . Name ) + "," +
EscapeCsv ( export . Library . Identity . Version . ToNormalizedString ( ) ) + "," +
EscapeCsv ( export . Library . Hash ) + "," +
EscapeCsv ( type ) + "," +
EscapeCsv ( item . Name ) + "," +
EscapeCsv ( item . RelativePath ) + "," ) ;
}
private static string EscapeCsv ( string input )
{
return "\"" + input . Replace ( "\\" , "\\\\" ) . Replace ( "\"" , "\\\"" ) + "\"" ;
}
2015-11-12 03:50:38 -08:00
private static bool PrintSummary ( List < DiagnosticMessage > diagnostics , Stopwatch sw , bool success = true )
2015-10-21 00:26:57 -07:00
{
2015-11-07 23:53:05 -08:00
PrintDiagnostics ( diagnostics ) ;
2015-11-01 16:21:10 -08:00
Reporter . Output . WriteLine ( ) ;
2015-10-21 00:26:57 -07:00
var errorCount = diagnostics . Count ( d = > d . Severity = = DiagnosticMessageSeverity . Error ) ;
var warningCount = diagnostics . Count ( d = > d . Severity = = DiagnosticMessageSeverity . Warning ) ;
2015-11-07 23:53:05 -08:00
if ( errorCount > 0 | | ! success )
2015-10-21 00:26:57 -07:00
{
Reporter . Output . WriteLine ( "Compilation failed." . Red ( ) ) ;
2015-11-12 03:50:38 -08:00
success = false ;
2015-10-21 00:26:57 -07:00
}
else
2015-10-18 07:32:42 -07:00
{
2015-10-21 00:26:57 -07:00
Reporter . Output . WriteLine ( "Compilation succeeded." . Green ( ) ) ;
2015-10-18 07:32:42 -07:00
}
2015-10-21 00:26:57 -07:00
Reporter . Output . WriteLine ( $" {warningCount} Warning(s)" ) ;
Reporter . Output . WriteLine ( $" {errorCount} Error(s)" ) ;
2015-11-01 16:21:10 -08:00
Reporter . Output . WriteLine ( ) ;
2015-11-07 23:53:05 -08:00
Reporter . Output . WriteLine ( $"Time elapsed {sw.Elapsed}" ) ;
Reporter . Output . WriteLine ( ) ;
2015-11-12 03:50:38 -08:00
return success ;
2015-10-13 14:31:29 -07:00
}
2015-10-18 21:07:48 -07:00
private static bool AddResources ( Project project , List < string > compilerArgs , string intermediateOutputPath )
2015-10-18 19:02:09 -07:00
{
string root = PathUtility . EnsureTrailingSlash ( project . ProjectDirectory ) ;
foreach ( var resourceFile in project . Files . ResourceFiles )
{
string resourceName = null ;
string rootNamespace = null ;
var resourcePath = resourceFile . Key ;
if ( string . IsNullOrEmpty ( resourceFile . Value ) )
{
// No logical name, so use the file name
resourceName = ResourcePathUtility . GetResourceName ( root , resourcePath ) ;
rootNamespace = project . Name ;
}
else
{
resourceName = CreateCSharpManifestResourceName . EnsureResourceExtension ( resourceFile . Value , resourcePath ) ;
rootNamespace = null ;
}
var name = CreateCSharpManifestResourceName . CreateManifestName ( resourceName , rootNamespace ) ;
var fileName = resourcePath ;
2015-10-18 21:07:48 -07:00
if ( ResourcePathUtility . IsResxResourceFile ( fileName ) )
{
var ext = Path . GetExtension ( fileName ) ;
if ( string . Equals ( ext , ".resx" , StringComparison . OrdinalIgnoreCase ) )
{
// {file}.resx -> {file}.resources
var resourcesFile = Path . Combine ( intermediateOutputPath , name ) ;
2015-12-01 22:12:04 +05:00
var result = Command . Create ( "dotnet-resgen" , $"\" { fileName } \ " \"{resourcesFile}\"" )
2015-10-18 21:07:48 -07:00
. ForwardStdErr ( )
. ForwardStdOut ( )
2015-10-21 03:11:27 -07:00
. Execute ( ) ;
2015-10-18 21:07:48 -07:00
if ( result . ExitCode ! = 0 )
{
return false ;
}
// Use this as the resource name instead
fileName = resourcesFile ;
}
}
2015-11-07 01:52:59 -08:00
compilerArgs . Add ( $"--resource:\" { fileName } \ ",{name}" ) ;
2015-10-18 19:02:09 -07:00
}
2015-10-18 21:07:48 -07:00
return true ;
2015-10-18 19:02:09 -07:00
}
2015-10-17 23:43:28 -07:00
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 ) ;
}
2015-10-21 00:26:57 -07:00
private static DiagnosticMessage ParseDiagnostic ( string projectRootPath , string line )
{
var error = CanonicalError . Parse ( line ) ;
if ( error ! = null )
{
var severity = error . category = = CanonicalError . Parts . Category . Error ?
DiagnosticMessageSeverity . Error : DiagnosticMessageSeverity . Warning ;
2015-10-21 15:21:14 -07:00
2015-10-21 00:26:57 -07:00
return new DiagnosticMessage (
error . code ,
error . text ,
Path . IsPathRooted ( error . origin ) ? line : projectRootPath + Path . DirectorySeparatorChar + line ,
Path . Combine ( projectRootPath , error . origin ) ,
severity ,
error . line ,
error . column ,
error . endColumn ,
error . endLine ,
source : null ) ;
}
return null ;
}
2015-10-21 15:21:14 -07:00
2015-11-07 23:53:05 -08:00
private static void PrintDiagnostics ( List < DiagnosticMessage > diagnostics )
{
foreach ( var diag in diagnostics )
{
PrintDiagnostic ( diag ) ;
}
}
2015-11-17 18:02:08 -08:00
2015-10-21 00:26:57 -07:00
private static void PrintDiagnostic ( DiagnosticMessage diag )
{
switch ( diag . Severity )
{
case DiagnosticMessageSeverity . Info :
Reporter . Error . WriteLine ( diag . FormattedMessage ) ;
break ;
case DiagnosticMessageSeverity . Warning :
Reporter . Error . WriteLine ( diag . FormattedMessage . Yellow ( ) . Bold ( ) ) ;
break ;
case DiagnosticMessageSeverity . Error :
Reporter . Error . WriteLine ( diag . FormattedMessage . Red ( ) . Bold ( ) ) ;
break ;
}
}
2015-10-13 14:31:29 -07:00
private static void ShowDependencyInfo ( IEnumerable < LibraryExport > dependencies )
{
2015-11-17 18:02:08 -08:00
if ( CommandContext . IsVerbose ( ) )
2015-10-13 14:31:29 -07:00
{
2015-11-17 18:02:08 -08:00
foreach ( var dependency in dependencies )
2015-10-13 14:31:29 -07:00
{
2015-11-17 18:02:08 -08:00
if ( ! dependency . Library . Resolved )
2015-10-13 14:31:29 -07:00
{
2015-11-17 18:02:08 -08:00
Reporter . Verbose . WriteLine ( $" Unable to resolve dependency {dependency.Library.Identity.ToString().Red().Bold()}" ) ;
Reporter . Verbose . WriteLine ( "" ) ;
2015-10-13 14:31:29 -07:00
}
2015-11-17 18:02:08 -08:00
else
2015-10-13 14:31:29 -07:00
{
2015-11-17 18:02:08 -08:00
Reporter . Verbose . WriteLine ( $" Using {dependency.Library.Identity.Type.Value.Cyan().Bold()} dependency {dependency.Library.Identity.ToString().Cyan().Bold()}" ) ;
Reporter . Verbose . WriteLine ( $" Path: {dependency.Library.Path}" ) ;
foreach ( var metadataReference in dependency . CompilationAssemblies )
{
Reporter . Verbose . WriteLine ( $" Assembly: {metadataReference}" ) ;
}
foreach ( var sourceReference in dependency . SourceReferences )
{
Reporter . Verbose . WriteLine ( $" Source: {sourceReference}" ) ;
}
Reporter . Verbose . WriteLine ( "" ) ;
2015-10-13 14:31:29 -07:00
}
}
}
2015-10-06 10:46:43 -07:00
}
2015-11-01 02:04:26 -08:00
private static void CopyFiles ( IEnumerable < LibraryAsset > files , string outputPath )
{
foreach ( var file in files )
{
File . Copy ( file . ResolvedPath , Path . Combine ( outputPath , Path . GetFileName ( file . ResolvedPath ) ) , overwrite : true ) ;
}
}
2015-11-06 06:15:26 -08:00
private static void CopyContents ( ProjectContext context , string outputPath )
{
var sourceFiles = context . ProjectFile . Files . GetCopyToOutputFiles ( ) ;
Copy ( sourceFiles , context . ProjectDirectory , outputPath ) ;
}
private static void Copy ( IEnumerable < string > sourceFiles , string sourceDirectory , string targetDirectory )
{
if ( sourceFiles = = null )
{
throw new ArgumentNullException ( nameof ( sourceFiles ) ) ;
}
sourceDirectory = EnsureTrailingSlash ( sourceDirectory ) ;
targetDirectory = EnsureTrailingSlash ( targetDirectory ) ;
foreach ( var sourceFilePath in sourceFiles )
{
var fileName = Path . GetFileName ( sourceFilePath ) ;
var targetFilePath = sourceFilePath . Replace ( sourceDirectory , targetDirectory ) ;
var targetFileParentFolder = Path . GetDirectoryName ( targetFilePath ) ;
// Create directory before copying a file
if ( ! Directory . Exists ( targetFileParentFolder ) )
{
Directory . CreateDirectory ( targetFileParentFolder ) ;
}
File . Copy (
sourceFilePath ,
targetFilePath ,
overwrite : true ) ;
// clear read-only bit if set
var fileAttributes = File . GetAttributes ( targetFilePath ) ;
if ( ( fileAttributes & FileAttributes . ReadOnly ) = = FileAttributes . ReadOnly )
{
File . SetAttributes ( targetFilePath , fileAttributes & ~ FileAttributes . ReadOnly ) ;
}
}
}
private static string EnsureTrailingSlash ( string path )
{
return EnsureTrailingCharacter ( path , Path . DirectorySeparatorChar ) ;
}
private static string EnsureTrailingCharacter ( string path , char trailingCharacter )
{
if ( path = = null )
{
throw new ArgumentNullException ( nameof ( path ) ) ;
}
// if the path is empty, we want to return the original string instead of a single trailing character.
if ( path . Length = = 0 | | path [ path . Length - 1 ] = = trailingCharacter )
{
return path ;
}
return path + trailingCharacter ;
}
2015-10-06 10:46:43 -07:00
}
}