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-20 13:27:56 -07:00
using System.CommandLine ;
using System.Collections.Generic ;
2015-10-18 01:17:13 -07:00
using System.IO ;
2015-10-20 13:27:56 -07:00
using System.Linq ;
using System.Runtime.InteropServices ;
using System.Text ;
using Microsoft.DotNet.Cli.Compiler.Common ;
2015-10-18 01:17:13 -07:00
using Microsoft.DotNet.Cli.Utils ;
2015-11-27 16:19:54 -08:00
using Microsoft.DotNet.ProjectModel ;
2015-10-18 01:17:13 -07:00
namespace Microsoft.DotNet.Tools.Compiler.Csc
{
2016-01-30 21:47:50 -08:00
public class CompileCscCommand
2015-10-18 01:17:13 -07:00
{
2015-10-20 13:27:56 -07:00
private const int ExitFailed = 1 ;
2016-01-30 21:47:50 -08:00
public static int Run ( string [ ] args )
2015-10-18 01:17:13 -07:00
{
DebugHelper . HandleDebugSwitch ( ref args ) ;
2015-10-20 13:27:56 -07:00
CommonCompilerOptions commonOptions = null ;
2015-12-14 10:05:38 -08:00
AssemblyInfoOptions assemblyInfoOptions = null ;
2015-10-20 13:27:56 -07:00
string tempOutDir = null ;
IReadOnlyList < string > references = Array . Empty < string > ( ) ;
IReadOnlyList < string > resources = Array . Empty < string > ( ) ;
IReadOnlyList < string > sources = Array . Empty < string > ( ) ;
2016-01-18 15:14:19 -08:00
IReadOnlyList < string > analyzers = Array . Empty < string > ( ) ;
2015-10-20 13:27:56 -07:00
string outputName = null ;
2015-12-16 15:45:24 -08:00
var help = false ;
var returnCode = 0 ;
string helpText = null ;
2015-10-20 13:27:56 -07:00
try
{
ArgumentSyntax . Parse ( args , syntax = >
{
2015-12-16 15:45:24 -08:00
syntax . HandleHelp = false ;
syntax . HandleErrors = false ;
2015-10-20 13:27:56 -07:00
commonOptions = CommonCompilerOptionsExtensions . Parse ( syntax ) ;
2015-12-14 10:05:38 -08:00
assemblyInfoOptions = AssemblyInfoOptions . Parse ( syntax ) ;
2015-10-20 13:27:56 -07:00
syntax . DefineOption ( "temp-output" , ref tempOutDir , "Compilation temporary directory" ) ;
syntax . DefineOption ( "out" , ref outputName , "Name of the output assembly" ) ;
2015-11-07 01:52:59 -08:00
syntax . DefineOptionList ( "reference" , ref references , "Path to a compiler metadata reference" ) ;
2016-01-31 01:25:01 -08:00
2016-01-18 15:14:19 -08:00
syntax . DefineOptionList ( "analyzer" , ref analyzers , "Path to an analyzer assembly" ) ;
2015-10-20 13:27:56 -07:00
syntax . DefineOptionList ( "resource" , ref resources , "Resources to embed" ) ;
2015-10-18 01:17:13 -07:00
2015-12-16 15:45:24 -08:00
syntax . DefineOption ( "h|help" , ref help , "Help for compile native." ) ;
2015-10-20 13:27:56 -07:00
syntax . DefineParameterList ( "source-files" , ref sources , "Compilation sources" ) ;
2015-12-16 15:45:24 -08:00
helpText = syntax . GetHelpText ( ) ;
2015-10-20 13:27:56 -07:00
if ( tempOutDir = = null )
{
syntax . ReportError ( "Option '--temp-output' is required" ) ;
}
} ) ;
}
2015-12-16 15:45:24 -08:00
catch ( ArgumentSyntaxException exception )
2015-10-18 01:17:13 -07:00
{
2015-12-16 15:45:24 -08:00
Console . Error . WriteLine ( exception . Message ) ;
help = true ;
returnCode = ExitFailed ;
}
if ( help )
{
Console . WriteLine ( helpText ) ;
return returnCode ;
2015-10-20 13:27:56 -07:00
}
2016-02-17 16:32:33 -08:00
var translated = TranslateCommonOptions ( commonOptions , outputName ) ;
2015-10-20 13:27:56 -07:00
var allArgs = new List < string > ( translated ) ;
allArgs . AddRange ( GetDefaultOptions ( ) ) ;
2015-12-14 10:05:38 -08:00
// Generate assembly info
2016-02-17 16:32:33 -08:00
var assemblyInfo = Path . Combine ( tempOutDir , $"dotnet-compile.assemblyinfo.cs" ) ;
2016-03-02 15:53:59 -08:00
2016-02-04 22:05:17 +01:00
File . WriteAllText ( assemblyInfo , AssemblyInfoFileGenerator . GenerateCSharp ( assemblyInfoOptions , sources ) ) ;
2015-12-14 10:05:38 -08:00
allArgs . Add ( $"\" { assemblyInfo } \ "" ) ;
2015-10-20 13:27:56 -07:00
if ( outputName ! = null )
{
2016-02-17 16:32:33 -08:00
allArgs . Add ( $"-out:\" { outputName } \ "" ) ;
2015-10-20 13:27:56 -07:00
}
2015-10-18 01:17:13 -07:00
2016-02-17 16:32:33 -08:00
allArgs . AddRange ( analyzers . Select ( a = > $"-a:\" { a } \ "" ) ) ;
allArgs . AddRange ( references . Select ( r = > $"-r:\" { r } \ "" ) ) ;
// Resource has two parts separated by a comma
// Only the first should be quoted. This is handled
// in dotnet-compile where the information is present.
2016-02-16 10:57:11 -08:00
allArgs . AddRange ( resources . Select ( resource = > $"-resource:{resource}" ) ) ;
2016-03-02 15:53:59 -08:00
2016-02-17 16:32:33 -08:00
allArgs . AddRange ( sources . Select ( s = > $"\" { s } \ "" ) ) ;
2015-10-20 13:27:56 -07:00
2016-02-17 16:32:33 -08:00
var rsp = Path . Combine ( tempOutDir , "dotnet-compile-csc.rsp" ) ;
2015-10-20 13:27:56 -07:00
File . WriteAllLines ( rsp , allArgs , Encoding . UTF8 ) ;
// Execute CSC!
2016-01-22 14:04:04 -08:00
var result = RunCsc ( new string [ ] { $"-noconfig" , "@" + $"{rsp}" } )
2015-10-20 13:27:56 -07:00
. ForwardStdErr ( )
. ForwardStdOut ( )
. Execute ( ) ;
return result . ExitCode ;
}
// TODO: Review if this is the place for default options
private static IEnumerable < string > GetDefaultOptions ( )
{
var args = new List < string > ( )
{
"-nostdlib" ,
2016-02-12 14:28:05 -08:00
"-nologo" ,
2015-10-20 13:27:56 -07:00
} ;
return args ;
}
2016-01-08 11:03:14 -08:00
private static IEnumerable < string > TranslateCommonOptions ( CommonCompilerOptions options , string outputName )
2015-10-20 13:27:56 -07:00
{
List < string > commonArgs = new List < string > ( ) ;
if ( options . Defines ! = null )
{
commonArgs . AddRange ( options . Defines . Select ( def = > $"-d:{def}" ) ) ;
}
2016-01-13 15:56:02 -08:00
if ( options . SuppressWarnings ! = null )
{
commonArgs . AddRange ( options . SuppressWarnings . Select ( w = > $"-nowarn:{w}" ) ) ;
}
2016-01-31 01:25:01 -08:00
// Additional arguments are added verbatim
if ( options . AdditionalArguments ! = null )
{
commonArgs . AddRange ( options . AdditionalArguments ) ;
}
2015-10-20 13:27:56 -07:00
if ( options . LanguageVersion ! = null )
{
2015-11-06 05:53:16 -08:00
commonArgs . Add ( $"-langversion:{GetLanguageVersion(options.LanguageVersion)}" ) ;
2015-10-20 13:27:56 -07:00
}
if ( options . Platform ! = null )
{
commonArgs . Add ( $"-platform:{options.Platform}" ) ;
}
if ( options . AllowUnsafe = = true )
{
commonArgs . Add ( "-unsafe" ) ;
}
if ( options . WarningsAsErrors = = true )
{
commonArgs . Add ( "-warnaserror" ) ;
}
if ( options . Optimize = = true )
{
commonArgs . Add ( "-optimize" ) ;
}
if ( options . KeyFile ! = null )
{
commonArgs . Add ( $"-keyfile:\" { options . KeyFile } \ "" ) ;
2015-11-18 00:00:31 -08:00
// If we're not on Windows, full signing isn't supported, so we'll
// public sign, unless the public sign switch has explicitly been
// set to false
if ( ! RuntimeInformation . IsOSPlatform ( OSPlatform . Windows ) & &
options . PublicSign = = null )
{
commonArgs . Add ( "-publicsign" ) ;
}
2015-10-20 13:27:56 -07:00
}
if ( options . DelaySign = = true )
{
commonArgs . Add ( "-delaysign" ) ;
}
2015-11-17 23:37:39 -08:00
if ( options . PublicSign = = true )
{
commonArgs . Add ( "-publicsign" ) ;
}
2015-10-20 13:27:56 -07:00
2016-01-08 11:03:14 -08:00
if ( options . GenerateXmlDocumentation = = true )
{
2016-02-17 16:32:33 -08:00
commonArgs . Add ( $"-doc:{Path.ChangeExtension(outputName, " xml ")}" ) ;
2016-01-08 11:03:14 -08:00
}
2015-10-20 13:27:56 -07:00
if ( options . EmitEntryPoint ! = true )
{
commonArgs . Add ( "-t:library" ) ;
}
2015-10-18 01:17:13 -07:00
2016-03-02 15:53:59 -08:00
commonArgs . Add ( ( string . IsNullOrEmpty ( options . DebugType ) | | options . DebugType = = "portable" )
? "-debug:portable"
: "-debug:full" ) ;
2015-10-20 13:27:56 -07:00
return commonArgs ;
2015-10-18 01:17:13 -07:00
}
2015-11-06 05:53:16 -08:00
private static string GetLanguageVersion ( string languageVersion )
{
// project.json supports the enum that the roslyn APIs expose
if ( languageVersion ? . StartsWith ( "csharp" , StringComparison . OrdinalIgnoreCase ) = = true )
{
// We'll be left with the number csharp6 = 6
return languageVersion . Substring ( "csharp" . Length ) ;
}
return languageVersion ;
}
2016-01-22 14:04:04 -08:00
private static Command RunCsc ( string [ ] cscArgs )
2015-10-18 01:17:13 -07:00
{
// Locate CoreRun
2015-11-10 17:30:01 -08:00
return Command . Create ( "csc" , cscArgs ) ;
2015-10-18 01:17:13 -07:00
}
}
}