2015-12-17 14:28:11 +01: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 ;
using System.CommandLine ;
using System.Collections.Generic ;
using System.IO ;
using System.Linq ;
using System.Runtime.InteropServices ;
using System.Text ;
using Microsoft.DotNet.Cli.Compiler.Common ;
using Microsoft.DotNet.Cli.Utils ;
using Microsoft.DotNet.ProjectModel ;
namespace Microsoft.DotNet.Tools.Compiler.Fsc
{
2016-01-30 21:47:50 -08:00
public class CompileFscCommand
2015-12-17 14:28:11 +01:00
{
private const int ExitFailed = 1 ;
2016-01-30 21:47:50 -08:00
public static int Run ( string [ ] args )
2015-12-17 14:28:11 +01:00
{
DebugHelper . HandleDebugSwitch ( ref args ) ;
CommonCompilerOptions commonOptions = null ;
AssemblyInfoOptions assemblyInfoOptions = null ;
string tempOutDir = null ;
IReadOnlyList < string > references = Array . Empty < string > ( ) ;
IReadOnlyList < string > resources = Array . Empty < string > ( ) ;
IReadOnlyList < string > sources = Array . Empty < string > ( ) ;
string outputName = null ;
var help = false ;
var returnCode = 0 ;
string helpText = null ;
try
{
ArgumentSyntax . Parse ( args , syntax = >
{
syntax . HandleHelp = false ;
syntax . HandleErrors = false ;
commonOptions = CommonCompilerOptionsExtensions . Parse ( syntax ) ;
assemblyInfoOptions = AssemblyInfoOptions . Parse ( syntax ) ;
syntax . DefineOption ( "temp-output" , ref tempOutDir , "Compilation temporary directory" ) ;
syntax . DefineOption ( "out" , ref outputName , "Name of the output assembly" ) ;
syntax . DefineOptionList ( "reference" , ref references , "Path to a compiler metadata reference" ) ;
syntax . DefineOptionList ( "resource" , ref resources , "Resources to embed" ) ;
syntax . DefineOption ( "h|help" , ref help , "Help for compile native." ) ;
syntax . DefineParameterList ( "source-files" , ref sources , "Compilation sources" ) ;
helpText = syntax . GetHelpText ( ) ;
if ( tempOutDir = = null )
{
syntax . ReportError ( "Option '--temp-output' is required" ) ;
}
} ) ;
}
catch ( ArgumentSyntaxException exception )
{
Console . Error . WriteLine ( exception . Message ) ;
help = true ;
returnCode = ExitFailed ;
}
if ( help )
{
Console . WriteLine ( helpText ) ;
return returnCode ;
}
2016-01-08 11:03:14 -08:00
var translated = TranslateCommonOptions ( commonOptions , outputName ) ;
2015-12-17 14:28:11 +01:00
var allArgs = new List < string > ( translated ) ;
allArgs . AddRange ( GetDefaultOptions ( ) ) ;
// Generate assembly info
var assemblyInfo = Path . Combine ( tempOutDir , $"dotnet-compile.assemblyinfo.fs" ) ;
File . WriteAllText ( assemblyInfo , AssemblyInfoFileGenerator . GenerateFSharp ( assemblyInfoOptions ) ) ;
2016-01-22 14:04:04 -08:00
allArgs . Add ( $"{assemblyInfo}" ) ;
2015-12-17 14:28:11 +01:00
//HACK fsc raise error FS0208 if target exe doesnt have extension .exe
bool hackFS0208 = commonOptions . EmitEntryPoint = = true ;
string originalOutputName = outputName ;
if ( outputName ! = null )
{
if ( hackFS0208 )
{
outputName = Path . ChangeExtension ( outputName , ".exe" ) ;
}
2016-01-22 14:04:04 -08:00
allArgs . Add ( $"--out:" ) ;
allArgs . Add ( $"{outputName}" ) ;
2015-12-17 14:28:11 +01:00
}
2016-01-22 14:04:04 -08:00
foreach ( var reference in references )
{
allArgs . Add ( "-r" ) ;
allArgs . Add ( $"{reference}" ) ;
}
foreach ( var resource in resources )
{
allArgs . Add ( "--resource" ) ;
allArgs . Add ( $"{resource}" ) ;
}
foreach ( var source in sources )
{
allArgs . Add ( $"{source}" ) ;
}
2015-12-17 14:28:11 +01:00
var rsp = Path . Combine ( tempOutDir , "dotnet-compile-fsc.rsp" ) ;
File . WriteAllLines ( rsp , allArgs , Encoding . UTF8 ) ;
// Execute FSC!
2016-01-22 14:04:04 -08:00
var result = RunFsc ( allArgs )
2015-12-17 14:28:11 +01:00
. ForwardStdErr ( )
. ForwardStdOut ( )
. Execute ( ) ;
if ( hackFS0208 & & File . Exists ( outputName ) )
{
if ( File . Exists ( originalOutputName ) )
File . Delete ( originalOutputName ) ;
File . Move ( outputName , originalOutputName ) ;
}
return result . ExitCode ;
}
// TODO: Review if this is the place for default options
private static IEnumerable < string > GetDefaultOptions ( )
{
var args = new List < string > ( )
{
"--noframework" ,
"--nologo" ,
"--simpleresolution"
} ;
if ( RuntimeInformation . IsOSPlatform ( OSPlatform . Windows ) )
args . Add ( "--debug:full" ) ;
else
args . Add ( "--debug-" ) ;
return args ;
}
2016-01-08 11:03:14 -08:00
private static IEnumerable < string > TranslateCommonOptions ( CommonCompilerOptions options , string outputName )
2015-12-17 14:28:11 +01:00
{
List < string > commonArgs = new List < string > ( ) ;
if ( options . Defines ! = null )
{
commonArgs . AddRange ( options . Defines . Select ( def = > $"-d:{def}" ) ) ;
}
if ( options . Platform ! = null )
{
commonArgs . Add ( $"--platform:{options.Platform}" ) ;
}
if ( options . WarningsAsErrors = = true )
{
commonArgs . Add ( "--warnaserror" ) ;
}
if ( options . Optimize = = true )
{
commonArgs . Add ( "--optimize" ) ;
}
2016-01-08 11:03:14 -08:00
if ( options . GenerateXmlDocumentation = = true )
{
commonArgs . Add ( $"--doc:{Path.ChangeExtension(outputName, " xml ")}" ) ;
}
2015-12-17 14:28:11 +01:00
if ( options . EmitEntryPoint ! = true )
{
commonArgs . Add ( "--target:library" ) ;
}
else
{
commonArgs . Add ( "--target:exe" ) ;
//HACK we need default.win32manifest for exe
if ( RuntimeInformation . IsOSPlatform ( OSPlatform . Windows ) )
{
var win32manifestPath = Path . Combine ( AppContext . BaseDirectory , "default.win32manifest" ) ;
commonArgs . Add ( $"--win32manifest:\" { win32manifestPath } \ "" ) ;
}
}
2016-01-08 11:03:14 -08:00
2015-12-17 14:28:11 +01:00
return commonArgs ;
}
2016-01-22 14:04:04 -08:00
private static Command RunFsc ( List < string > fscArgs )
2015-12-17 14:28:11 +01:00
{
var corerun = Path . Combine ( AppContext . BaseDirectory , Constants . HostExecutableName ) ;
var fscExe = Path . Combine ( AppContext . BaseDirectory , "fsc.exe" ) ;
2016-01-22 14:04:04 -08:00
List < string > args = new List < string > ( ) ;
args . Add ( fscExe ) ;
args . AddRange ( fscArgs ) ;
return Command . Create ( corerun , args . ToArray ( ) ) ;
2015-12-17 14:28:11 +01:00
}
}
}