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.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 ;
2016-04-19 22:51:32 -05:00
using Microsoft.DotNet.Cli.CommandLine ;
2015-10-20 13:27:56 -07:00
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 ) ;
2016-04-19 22:51:32 -05:00
CommandLineApplication app = new CommandLineApplication ( ) ;
app . Name = "dotnet compile-csc" ;
app . FullName = ".NET C# Compiler" ;
app . Description = "C# Compiler for the .NET Platform" ;
2016-04-20 00:28:34 -05:00
app . HandleResponseFiles = true ;
2016-04-19 22:51:32 -05:00
app . HelpOption ( "-h|--help" ) ;
2015-10-20 13:27:56 -07:00
2016-04-19 22:51:32 -05:00
CommonCompilerOptionsCommandLine commonCompilerCommandLine = CommonCompilerOptionsCommandLine . AddOptions ( app ) ;
AssemblyInfoOptionsCommandLine assemblyInfoCommandLine = AssemblyInfoOptionsCommandLine . AddOptions ( app ) ;
2015-12-16 15:45:24 -08:00
2016-04-19 22:51:32 -05:00
CommandOption tempOutput = app . Option ( "--temp-output <arg>" , "Compilation temporary directory" , CommandOptionType . SingleValue ) ;
CommandOption outputName = app . Option ( "--out <arg>" , "Name of the output assembly" , CommandOptionType . SingleValue ) ;
CommandOption references = app . Option ( "--reference <arg>..." , "Path to a compiler metadata reference" , CommandOptionType . MultipleValue ) ;
CommandOption analyzers = app . Option ( "--analyzer <arg>..." , "Path to an analyzer assembly" , CommandOptionType . MultipleValue ) ;
CommandOption resources = app . Option ( "--resource <arg>..." , "Resources to embed" , CommandOptionType . MultipleValue ) ;
CommandArgument sources = app . Argument ( "<source-files>..." , "Compilation sources" , multipleValues : true ) ;
2015-10-20 13:27:56 -07:00
2016-04-19 22:51:32 -05:00
app . OnExecute ( ( ) = >
{
if ( ! tempOutput . HasValue ( ) )
{
Reporter . Error . WriteLine ( "Option '--temp-output' is required" ) ;
return ExitFailed ;
}
2015-12-14 10:05:38 -08:00
2016-04-19 22:51:32 -05:00
CommonCompilerOptions commonOptions = commonCompilerCommandLine . GetOptionValues ( ) ;
2015-10-20 13:27:56 -07:00
2016-04-19 22:51:32 -05:00
AssemblyInfoOptions assemblyInfoOptions = assemblyInfoCommandLine . GetOptionValues ( ) ;
2015-10-20 13:27:56 -07:00
2016-04-19 22:51:32 -05:00
var translated = TranslateCommonOptions ( commonOptions , outputName . Value ( ) ) ;
2016-01-31 01:25:01 -08:00
2016-04-19 22:51:32 -05:00
var allArgs = new List < string > ( translated ) ;
allArgs . AddRange ( GetDefaultOptions ( ) ) ;
2015-10-20 13:27:56 -07:00
2016-04-19 22:51:32 -05:00
// Generate assembly info
var assemblyInfo = Path . Combine ( tempOutput . Value ( ) , $"dotnet-compile.assemblyinfo.cs" ) ;
2015-10-18 01:17:13 -07:00
2016-04-19 22:51:32 -05:00
File . WriteAllText ( assemblyInfo , AssemblyInfoFileGenerator . GenerateCSharp ( assemblyInfoOptions , sources . Values ) ) ;
2015-12-16 15:45:24 -08:00
2016-04-19 22:51:32 -05:00
allArgs . Add ( $"\" { assemblyInfo } \ "" ) ;
2015-12-16 15:45:24 -08:00
2016-04-19 22:51:32 -05:00
if ( outputName . HasValue ( ) )
{
allArgs . Add ( $"-out:\" { outputName . Value ( ) } \ "" ) ;
}
2015-12-16 15:45:24 -08:00
2016-04-19 22:51:32 -05:00
allArgs . AddRange ( analyzers . Values . Select ( a = > $"-a:\" { a } \ "" ) ) ;
allArgs . AddRange ( references . Values . Select ( r = > $"-r:\" { r } \ "" ) ) ;
2015-12-16 15:45:24 -08:00
2016-04-19 22:51:32 -05:00
// 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.
allArgs . AddRange ( resources . Values . Select ( resource = > $"-resource:{resource}" ) ) ;
2015-12-16 15:45:24 -08:00
2016-04-19 22:51:32 -05:00
allArgs . AddRange ( sources . Values . Select ( s = > $"\" { s } \ "" ) ) ;
2015-10-20 13:27:56 -07:00
2016-04-19 22:51:32 -05:00
var rsp = Path . Combine ( tempOutput . Value ( ) , "dotnet-compile-csc.rsp" ) ;
2015-10-20 13:27:56 -07:00
2016-04-19 22:51:32 -05:00
File . WriteAllLines ( rsp , allArgs , Encoding . UTF8 ) ;
2015-10-20 13:27:56 -07:00
2016-04-19 22:51:32 -05:00
// Execute CSC!
var result = RunCsc ( new string [ ] { $"-noconfig" , "@" + $"{rsp}" } )
. ForwardStdErr ( )
. ForwardStdOut ( )
. Execute ( ) ;
2016-03-02 15:53:59 -08:00
2016-04-19 22:51:32 -05:00
return result . ExitCode ;
} ) ;
2016-02-04 22:05:17 +01:00
2016-04-19 22:51:32 -05:00
try
2015-10-20 13:27:56 -07:00
{
2016-04-19 22:51:32 -05:00
return app . Execute ( args ) ;
}
catch ( Exception ex )
{
#if DEBUG
Reporter . Error . WriteLine ( ex . ToString ( ) ) ;
#else
Reporter . Error . WriteLine ( ex . Message ) ;
#endif
return ExitFailed ;
2015-10-20 13:27:56 -07:00
}
}
// 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-04 15:33:06 -08:00
if ( string . IsNullOrEmpty ( options . DebugType ) )
{
commonArgs . Add ( RuntimeInformation . IsOSPlatform ( OSPlatform . Windows )
? "-debug:full"
: "-debug:portable" ) ;
}
else
{
commonArgs . Add ( options . DebugType = = "portable"
? "-debug:portable"
: "-debug:full" ) ;
}
2016-03-02 15:53:59 -08:00
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
2016-03-24 09:36:05 -07:00
return Command . Create ( "csc.dll" , cscArgs ) ;
2015-10-18 01:17:13 -07:00
}
}
}