2015-10-06 10:46:43 -07:00
using System ;
2015-10-13 14:31:29 -07:00
using System.Collections.Generic ;
2015-10-06 10:46:43 -07:00
using System.IO ;
using System.Linq ;
using Microsoft.Dnx.Runtime.Common.CommandLine ;
using Microsoft.DotNet.Cli.Utils ;
2015-10-13 14:31:29 -07:00
using Microsoft.Extensions.ProjectModel ;
using Microsoft.Extensions.ProjectModel.Compilation ;
using NuGet.Frameworks ;
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-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-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" ) ;
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
// Load project contexts for each framework and compile them
bool success = true ;
if ( framework . HasValue ( ) )
2015-10-06 10:46:43 -07:00
{
2015-10-13 14:31:29 -07:00
foreach ( var context in framework . Values . Select ( f = > ProjectContext . Create ( path , NuGetFramework . Parse ( f ) ) ) )
{
success & = Compile ( context , configuration . Value ( ) ? ? Constants . DefaultConfiguration , output . Value ( ) ) ;
}
2015-10-06 10:46:43 -07:00
}
2015-10-13 14:31:29 -07:00
else
2015-10-06 10:46:43 -07:00
{
2015-10-13 14:31:29 -07:00
foreach ( var context in ProjectContext . CreateContextForEachFramework ( path ) )
{
success & = Compile ( context , configuration . Value ( ) ? ? Constants . DefaultConfiguration , output . Value ( ) ) ;
}
2015-10-06 10:46:43 -07: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 ) ;
}
catch ( OperationCanceledException ex )
{
Console . Error . WriteLine ( ex . Message ) ;
return 1 ;
}
}
2015-10-13 14:31:29 -07:00
private static bool Compile ( ProjectContext context , string configuration , string outputPath )
2015-10-06 10:46:43 -07:00
{
2015-10-13 14:31:29 -07:00
Reporter . Output . WriteLine ( $"Building {context.RootProject.Identity.Name.Yellow()} for {context.TargetFramework.DotNetFrameworkName.Yellow()}" ) ;
// Create the library exporter
var exporter = context . CreateExporter ( configuration ) ;
// Gather exports for the project
var dependencies = exporter . GetCompilationDependencies ( ) . ToList ( ) ;
// Hackily trigger builds of the dependent projects
foreach ( var dependency in dependencies . Where ( d = > d . CompilationAssemblies . Any ( ) ) )
2015-10-06 10:46:43 -07:00
{
2015-10-13 14:31:29 -07:00
var projectDependency = dependency . Library as ProjectDescription ;
if ( projectDependency ! = null & & ! string . Equals ( projectDependency . Identity . Name , context . RootProject . Identity . Name , StringComparison . Ordinal ) )
{
2015-10-16 04:21:40 -07:00
var compileResult = Command . Create ( "dotnet-compile" , $"--framework {projectDependency.Framework} --configuration {configuration} {projectDependency.Project.ProjectDirectory}" )
2015-10-13 14:31:29 -07:00
. ForwardStdOut ( )
. ForwardStdErr ( )
. RunAsync ( )
. Result ;
if ( compileResult . ExitCode ! = 0 )
{
Console . Error . WriteLine ( $"Failed to compile dependency: {projectDependency.Identity.Name}" ) ;
return false ;
}
}
2015-10-06 10:46:43 -07:00
}
2015-10-13 14:31:29 -07:00
// Dump dependency data
ShowDependencyInfo ( dependencies ) ;
// Hackily generate the output path
2015-10-06 10:46:43 -07:00
if ( string . IsNullOrEmpty ( outputPath ) )
{
2015-10-13 14:31:29 -07:00
outputPath = Path . Combine (
2015-10-15 12:56:07 -07:00
context . ProjectFile . ProjectDirectory ,
Constants . BinDirectoryName ,
2015-10-13 14:31:29 -07:00
configuration ,
context . TargetFramework . GetTwoDigitShortFolderName ( ) ) ;
2015-10-06 10:46:43 -07:00
}
if ( ! Directory . Exists ( outputPath ) )
{
Directory . CreateDirectory ( outputPath ) ;
}
2015-10-13 14:31:29 -07:00
// Get compilation options
2015-10-15 12:56:07 -07:00
var compilationOptions = context . ProjectFile . GetCompilerOptions ( context . TargetFramework , configuration ) ;
var outputName = Path . Combine ( outputPath , context . ProjectFile . Name + ".dll" ) ;
2015-10-13 14:31:29 -07:00
// Assemble csc args
var cscArgs = new List < string > ( )
2015-10-06 10:46:43 -07:00
{
2015-10-17 03:09:54 -07:00
// Default suppressions
"-nowarn:CS1701" ,
"-nowarn:CS1702" ,
"-nowarn:CS1705" ,
2015-10-13 14:31:29 -07:00
"-nostdlib" ,
"-nologo" ,
2015-10-15 12:56:07 -07:00
$"-out:\" { outputName } \ ""
2015-10-13 14:31:29 -07:00
} ;
// Add compilation options to the args
ApplyCompilationOptions ( compilationOptions , cscArgs ) ;
foreach ( var dependency in dependencies )
{
2015-10-15 12:56:07 -07:00
cscArgs . AddRange ( dependency . CompilationAssemblies . Select ( r = > $"-r:\" { r } \ "" ) ) ;
2015-10-13 14:31:29 -07:00
cscArgs . AddRange ( dependency . SourceReferences ) ;
2015-10-06 10:46:43 -07:00
}
2015-10-13 14:31:29 -07:00
// Add project source files
2015-10-15 12:56:07 -07:00
cscArgs . AddRange ( context . ProjectFile . Files . SourceFiles ) ;
2015-10-13 14:31:29 -07:00
// Write RSP file
2015-10-15 12:56:07 -07:00
var rsp = Path . Combine ( outputPath , "dotnet-compile.csc.rsp" ) ;
2015-10-13 14:31:29 -07:00
File . WriteAllLines ( rsp , cscArgs ) ;
// Execute CSC!
2015-10-15 16:12:56 -07:00
var result = RunCsc ( $"-noconfig @\" { rsp } \ "" )
2015-10-13 14:31:29 -07:00
. ForwardStdErr ( )
. ForwardStdOut ( )
2015-10-06 10:46:43 -07:00
. RunAsync ( )
. Result ;
2015-10-13 14:31:29 -07:00
return result . ExitCode = = 0 ;
}
2015-10-15 16:12:56 -07:00
private static Command RunCsc ( string cscArgs )
{
2015-10-16 11:11:07 -07:00
// Locate CoreRun
2015-10-16 15:30:28 -07:00
string hostRoot = Environment . GetEnvironmentVariable ( "DOTNET_CSC_PATH" ) ;
2015-10-17 03:09:54 -07:00
if ( string . IsNullOrEmpty ( hostRoot ) )
2015-10-16 11:11:07 -07:00
{
hostRoot = AppContext . BaseDirectory ;
}
2015-10-16 15:30:28 -07:00
var corerun = Path . Combine ( hostRoot , Constants . CoreRunName ) ;
var cscExe = Path . Combine ( hostRoot , "csc.exe" ) ;
2015-10-16 00:10:03 -07:00
return File . Exists ( corerun ) & & File . Exists ( cscExe )
? Command . Create ( corerun , $@"""{cscExe}"" {cscArgs}" )
2015-10-16 15:30:28 -07:00
: Command . Create ( "csc" , cscArgs ) ;
2015-10-15 16:12:56 -07:00
}
2015-10-13 14:31:29 -07:00
private static void ApplyCompilationOptions ( CompilerOptions compilationOptions , List < string > cscArgs )
{
2015-10-15 12:56:07 -07:00
var targetType = compilationOptions . EmitEntryPoint . GetValueOrDefault ( ) ? "exe" : "library" ;
2015-10-13 14:31:29 -07:00
cscArgs . Add ( $"-target:{targetType}" ) ;
2015-10-15 12:56:07 -07:00
if ( compilationOptions . AllowUnsafe . GetValueOrDefault ( ) )
2015-10-13 14:31:29 -07:00
{
cscArgs . Add ( "-unsafe+" ) ;
}
cscArgs . AddRange ( compilationOptions . Defines . Select ( d = > $"-d:{d}" ) ) ;
2015-10-15 12:56:07 -07:00
if ( compilationOptions . Optimize . GetValueOrDefault ( ) )
2015-10-06 10:46:43 -07:00
{
2015-10-13 14:31:29 -07:00
cscArgs . Add ( "-optimize" ) ;
2015-10-06 10:46:43 -07:00
}
2015-10-13 14:31:29 -07:00
if ( ! string . IsNullOrEmpty ( compilationOptions . Platform ) )
{
cscArgs . Add ( $"-platform:{compilationOptions.Platform}" ) ;
}
2015-10-15 12:56:07 -07:00
if ( compilationOptions . WarningsAsErrors . GetValueOrDefault ( ) )
2015-10-13 14:31:29 -07:00
{
cscArgs . Add ( "-warnaserror" ) ;
}
}
2015-10-06 10:46:43 -07:00
2015-10-13 14:31:29 -07:00
private static void ShowDependencyInfo ( IEnumerable < LibraryExport > dependencies )
{
foreach ( var dependency in dependencies )
{
if ( ! dependency . Library . Resolved )
2015-10-06 10:46:43 -07:00
{
2015-10-13 14:31:29 -07:00
Reporter . Error . WriteLine ( $" Unable to resolve dependency {dependency.Library.Identity.ToString().Red().Bold()}" ) ;
Reporter . Error . WriteLine ( "" ) ;
2015-10-06 10:46:43 -07:00
}
2015-10-13 14:31:29 -07:00
else
{
Reporter . Output . WriteLine ( $" Using {dependency.Library.Identity.Type.Value.Cyan().Bold()} dependency {dependency.Library.Identity.ToString().Cyan().Bold()}" ) ;
Reporter . Output . WriteLine ( $" Path: {dependency.Library.Path}" ) ;
2015-10-06 10:46:43 -07:00
2015-10-13 14:31:29 -07:00
foreach ( var metadataReference in dependency . CompilationAssemblies )
{
Reporter . Output . WriteLine ( $" Assembly: {metadataReference}" ) ;
}
foreach ( var sourceReference in dependency . SourceReferences )
{
Reporter . Output . WriteLine ( $" Source: {sourceReference}" ) ;
}
Reporter . Output . WriteLine ( "" ) ;
}
}
2015-10-06 10:46:43 -07:00
}
}
}