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 ) )
{
var compileResult = Command . Create ( "dotnet-compile" , $"--framework \" { projectDependency . Framework } \ " --configuration \"{configuration}\" {projectDependency.Project.ProjectDirectory}" )
. 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 (
context . Project . ProjectDirectory ,
"bin" ,
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
var compilationOptions = context . Project . GetCompilerOptions ( context . TargetFramework , configuration ) ;
var outputName = Path . Combine ( outputPath , context . Project . Name + ".dll" ) ;
// Assemble csc args
var cscArgs = new List < string > ( )
2015-10-06 10:46:43 -07:00
{
2015-10-13 14:31:29 -07:00
"-nostdlib" ,
"-nologo" ,
$"-out:{outputName}"
} ;
// Add compilation options to the args
ApplyCompilationOptions ( compilationOptions , cscArgs ) ;
foreach ( var dependency in dependencies )
{
cscArgs . AddRange ( dependency . CompilationAssemblies . Select ( r = > $"-r:{r}" ) ) ;
cscArgs . AddRange ( dependency . SourceReferences ) ;
2015-10-06 10:46:43 -07:00
}
2015-10-13 14:31:29 -07:00
// Add project source files
cscArgs . AddRange ( context . Project . Files . SourceFiles ) ;
// Write RSP file
var rsp = Path . Combine ( outputPath , "csc.rsp" ) ;
File . WriteAllLines ( rsp , cscArgs ) ;
// Execute CSC!
var result = Command . Create ( "csc" , $"-noconfig @{rsp}" )
. ForwardStdErr ( )
. ForwardStdOut ( )
2015-10-06 10:46:43 -07:00
. RunAsync ( )
. Result ;
2015-10-13 14:31:29 -07:00
return result . ExitCode = = 0 ;
}
private static void ApplyCompilationOptions ( CompilerOptions compilationOptions , List < string > cscArgs )
{
var targetType = ( compilationOptions . EmitEntryPoint ? ? false ) ? "exe" : "library" ;
cscArgs . Add ( $"-target:{targetType}" ) ;
if ( compilationOptions . AllowUnsafe = = true )
{
cscArgs . Add ( "-unsafe+" ) ;
}
cscArgs . AddRange ( compilationOptions . Defines . Select ( d = > $"-d:{d}" ) ) ;
if ( compilationOptions . Optimize = = true )
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}" ) ;
}
if ( compilationOptions . WarningsAsErrors = = true )
{
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
}
}
}