Create (de)serializer for CompilerOptions

This allows language-specific compile utilities to map a set of
common arguments to their compiler's specific argument set.
This commit is contained in:
Andy Gocke 2015-10-20 13:27:56 -07:00
parent b1d3ef4b75
commit 9d5f435ef5
12 changed files with 407 additions and 99 deletions

View file

@ -7,6 +7,7 @@
<add key="AspNetCIDev" value="https://www.myget.org/F/aspnetcidev/api/v3/index.json" />
<add key="roslyn-nightly" value="https://www.myget.org/F/roslyn-nightly/api/v3/index.json" />
<add key="api.nuget.org" value="https://api.nuget.org/v3/index.json" />
<add key="corefxlab" value="https://www.myget.org/F/netcore-package-prototyping/api/v3/index.json" />
</packageSources>
<activePackageSource>
<add key="AspNetCIDev" value="https://www.myget.org/F/aspnetcidev/api/v3/index.json" />

View file

@ -0,0 +1,160 @@
using System;
using System.Collections.Generic;
using System.CommandLine;
using System.Linq;
using Microsoft.DotNet.Cli.Compiler.Common;
using Microsoft.Extensions.ProjectModel;
namespace Microsoft.DotNet.Cli.Compiler.Common
{
public static class CommonCompilerOptionsExtensions
{
internal static readonly OptionTemplate s_definesTemplate = new OptionTemplate("define");
internal static readonly OptionTemplate s_languageVersionTemplate = new OptionTemplate("language-version");
internal static readonly OptionTemplate s_platformTemplate = new OptionTemplate("platform");
internal static readonly OptionTemplate s_allowUnsafeTemplate = new OptionTemplate("allow-unsafe");
internal static readonly OptionTemplate s_warningsAsErrorsTemplate = new OptionTemplate("warnings-as-errors");
internal static readonly OptionTemplate s_optimizeTemplate = new OptionTemplate("optimize");
internal static readonly OptionTemplate s_keyFileTemplate = new OptionTemplate("key-file");
internal static readonly OptionTemplate s_delaySignTemplate = new OptionTemplate("delay-sign");
internal static readonly OptionTemplate s_strongNameTemplate = new OptionTemplate("strong-name");
internal static readonly OptionTemplate s_emitEntryPointTemplate = new OptionTemplate("emit-entry-point");
public static CommonCompilerOptions Parse(ArgumentSyntax syntax)
{
IReadOnlyList<string> defines = null;
string languageVersion = null;
string platform = null;
bool? allowUnsafe = null;
bool? warningsAsErrors = null;
bool? optimize = null;
string keyFile = null;
bool? delaySign = null;
bool? strongName = null;
bool? emitEntryPoint = null;
Func<string, bool?> nullableBoolConverter = v => bool.Parse(v);
syntax.DefineOptionList(s_definesTemplate.LongName, ref defines, "Preprocessor definitions");
syntax.DefineOption(s_languageVersionTemplate.LongName, ref languageVersion,
"The version of the language used to compile");
syntax.DefineOption(s_platformTemplate.LongName, ref platform,
"The target platform");
syntax.DefineOption(s_allowUnsafeTemplate.LongName, ref allowUnsafe,
nullableBoolConverter, "Allow unsafe code");
syntax.DefineOption(s_warningsAsErrorsTemplate.LongName, ref warningsAsErrors,
nullableBoolConverter, "Turn all warnings into errors");
syntax.DefineOption(s_optimizeTemplate.LongName, ref optimize,
nullableBoolConverter, "Enable compiler optimizations");
syntax.DefineOption(s_keyFileTemplate.LongName, ref keyFile,
"Path to file containing the key to strong-name sign the output assembly");
syntax.DefineOption(s_delaySignTemplate.LongName, ref delaySign,
nullableBoolConverter, "Delay-sign the output assembly");
syntax.DefineOption(s_strongNameTemplate.LongName, ref strongName,
nullableBoolConverter, "Strong-name sign the output assembly");
syntax.DefineOption(s_emitEntryPointTemplate.LongName, ref emitEntryPoint,
nullableBoolConverter, "Output an executable console program");
return new CommonCompilerOptions
{
Defines = defines,
LanguageVersion = languageVersion,
Platform = platform,
AllowUnsafe = allowUnsafe,
WarningsAsErrors = warningsAsErrors,
Optimize = optimize,
KeyFile = keyFile,
DelaySign = delaySign,
StrongName = strongName,
EmitEntryPoint = emitEntryPoint
};
}
public static IEnumerable<string> SerializeToArgs(this CommonCompilerOptions options)
{
var defines = options.Defines;
var languageVersion = options.LanguageVersion;
var platform = options.Platform;
var allowUnsafe = options.AllowUnsafe;
var warningsAsErrors = options.WarningsAsErrors;
var optimize = options.Optimize;
var keyFile = options.KeyFile;
var delaySign = options.DelaySign;
var strongName = options.StrongName;
var emitEntryPoint = options.EmitEntryPoint;
var args = new List<string>();
if (defines != null)
{
args.AddRange(defines.Select(def => s_definesTemplate.ToLongArg(def)));
}
if (languageVersion != null)
{
args.Add(s_languageVersionTemplate.ToLongArg(languageVersion));
}
if (platform != null)
{
args.Add(s_platformTemplate.ToLongArg(platform));
}
if (allowUnsafe != null)
{
args.Add(s_allowUnsafeTemplate.ToLongArg(allowUnsafe));
}
if (warningsAsErrors != null)
{
args.Add(s_warningsAsErrorsTemplate.ToLongArg(warningsAsErrors));
}
if (optimize != null)
{
args.Add(s_optimizeTemplate.ToLongArg(optimize));
}
if (keyFile != null)
{
args.Add(s_keyFileTemplate.ToLongArg(keyFile));
}
if (delaySign != null)
{
args.Add(s_delaySignTemplate.ToLongArg(delaySign));
}
if (strongName != null)
{
args.Add(s_strongNameTemplate.ToLongArg(strongName));
}
if (emitEntryPoint != null)
{
args.Add(s_emitEntryPointTemplate.ToLongArg(emitEntryPoint));
}
return args;
}
}
}

View file

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.Props" Condition="'$(VSToolsPath)' != ''" />
<PropertyGroup Label="Globals">
<ProjectGuid>a16958e1-24c7-4f1e-b317-204ad91625dd</ProjectGuid>
<RootNamespace>Microsoft.Dotnet.Cli.Compiler.Common</RootNamespace>
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">..\..\artifacts\obj\$(MSBuildProjectName)</BaseIntermediateOutputPath>
<OutputPath Condition="'$(OutputPath)'=='' ">..\..\artifacts\bin\$(MSBuildProjectName)\</OutputPath>
</PropertyGroup>
<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>

View file

@ -0,0 +1,39 @@
using System;
namespace Microsoft.DotNet.Cli.Compiler.Common
{
internal class OptionTemplate
{
public string Template { get; }
public string ShortName { get; }
public string LongName { get; }
private static char[] s_separator = { '|' };
public OptionTemplate(string template)
{
Template = template;
foreach (var part in Template.Split(s_separator, 2, StringSplitOptions.RemoveEmptyEntries))
{
if (part.Length == 1)
{
ShortName = part;
}
else
{
LongName = part;
}
}
if (string.IsNullOrEmpty(LongName) && string.IsNullOrEmpty(ShortName))
{
throw new ArgumentException($"Invalid template pattern '{template}'", nameof(template));
}
}
public string ToLongArg(object value)
{
return $"--{LongName}:{value}";
}
}
}

View file

@ -0,0 +1,14 @@
{
"version": "1.0.0-*",
"dependencies": {
"System.CommandLine": "0.1-*",
"System.Linq": "4.0.1-beta-23428",
"System.Runtime": "4.0.21-beta-23428",
"Microsoft.DotNet.ProjectModel": "1.0.0-*",
},
"frameworks": {
"dnxcore50": { }
}
}

View file

@ -1,12 +1,13 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Linq;
namespace Microsoft.Extensions.ProjectModel
{
public class CompilerOptions
public class CommonCompilerOptions
{
public IEnumerable<string> Defines { get; set; }
@ -28,9 +29,9 @@ namespace Microsoft.Extensions.ProjectModel
public bool? EmitEntryPoint { get; set; }
public static CompilerOptions Combine(params CompilerOptions[] options)
public static CommonCompilerOptions Combine(params CommonCompilerOptions[] options)
{
var result = new CompilerOptions();
var result = new CommonCompilerOptions();
foreach (var option in options)
{
// Skip null options
@ -94,5 +95,6 @@ namespace Microsoft.Extensions.ProjectModel
return result;
}
}
}
}

View file

@ -17,9 +17,9 @@ namespace Microsoft.Extensions.ProjectModel
// REVIEW: It's kinda hacky making these internal but the reader needs to set them
internal Dictionary<NuGetFramework, TargetFrameworkInformation> _targetFrameworks = new Dictionary<NuGetFramework, TargetFrameworkInformation>();
internal Dictionary<string, CompilerOptions> _compilerOptionsByConfiguration = new Dictionary<string, CompilerOptions>(StringComparer.OrdinalIgnoreCase);
internal Dictionary<string, CommonCompilerOptions> _compilerOptionsByConfiguration = new Dictionary<string, CommonCompilerOptions>(StringComparer.OrdinalIgnoreCase);
internal CompilerOptions _defaultCompilerOptions;
internal CommonCompilerOptions _defaultCompilerOptions;
internal TargetFrameworkInformation _defaultTargetFrameworkConfiguration;
public Project()
@ -92,7 +92,7 @@ namespace Microsoft.Extensions.ProjectModel
return _compilerOptionsByConfiguration.Keys;
}
public CompilerOptions GetCompilerOptions(NuGetFramework targetFramework,
public CommonCompilerOptions GetCompilerOptions(NuGetFramework targetFramework,
string configurationName)
{
// Get all project options and combine them
@ -101,7 +101,7 @@ namespace Microsoft.Extensions.ProjectModel
var targetFrameworkOptions = targetFramework != null ? GetCompilerOptions(targetFramework) : null;
// Combine all of the options
return CompilerOptions.Combine(rootOptions, configurationOptions, targetFrameworkOptions);
return CommonCompilerOptions.Combine(rootOptions, configurationOptions, targetFrameworkOptions);
}
public TargetFrameworkInformation GetTargetFramework(NuGetFramework targetFramework)
@ -115,14 +115,14 @@ namespace Microsoft.Extensions.ProjectModel
return targetFrameworkInfo ?? _defaultTargetFrameworkConfiguration;
}
private CompilerOptions GetCompilerOptions()
private CommonCompilerOptions GetCompilerOptions()
{
return _defaultCompilerOptions;
}
private CompilerOptions GetCompilerOptions(string configurationName)
private CommonCompilerOptions GetCompilerOptions(string configurationName)
{
CompilerOptions options;
CommonCompilerOptions options;
if (_compilerOptionsByConfiguration.TryGetValue(configurationName, out options))
{
return options;
@ -131,7 +131,7 @@ namespace Microsoft.Extensions.ProjectModel
return null;
}
private CompilerOptions GetCompilerOptions(NuGetFramework frameworkName)
private CommonCompilerOptions GetCompilerOptions(NuGetFramework frameworkName)
{
return GetTargetFramework(frameworkName)?.CompilerOptions;
}

View file

@ -344,7 +344,7 @@ namespace Microsoft.Extensions.ProjectModel
private void BuildTargetFrameworksAndConfigurations(Project project, JsonObject projectJsonObject, ICollection<DiagnosticMessage> diagnostics)
{
// Get the shared compilationOptions
project._defaultCompilerOptions = GetCompilationOptions(projectJsonObject) ?? new CompilerOptions();
project._defaultCompilerOptions = GetCompilationOptions(projectJsonObject) ?? new CommonCompilerOptions();
project._defaultTargetFrameworkConfiguration = new TargetFrameworkInformation
{
@ -352,13 +352,13 @@ namespace Microsoft.Extensions.ProjectModel
};
// Add default configurations
project._compilerOptionsByConfiguration["Debug"] = new CompilerOptions
project._compilerOptionsByConfiguration["Debug"] = new CommonCompilerOptions
{
Defines = new[] { "DEBUG", "TRACE" },
Optimize = false
};
project._compilerOptionsByConfiguration["Release"] = new CompilerOptions
project._compilerOptionsByConfiguration["Release"] = new CommonCompilerOptions
{
Defines = new[] { "RELEASE", "TRACE" },
Optimize = true
@ -439,7 +439,7 @@ namespace Microsoft.Extensions.ProjectModel
{
// If no compilation options are provided then figure them out from the node
var compilerOptions = GetCompilationOptions(frameworkValue) ??
new CompilerOptions();
new CommonCompilerOptions();
var frameworkName = NuGetFramework.Parse(frameworkKey);
@ -504,7 +504,7 @@ namespace Microsoft.Extensions.ProjectModel
return true;
}
private static CompilerOptions GetCompilationOptions(JsonObject rawObject)
private static CommonCompilerOptions GetCompilationOptions(JsonObject rawObject)
{
var rawOptions = rawObject.ValueAsJsonObject("compilationOptions");
if (rawOptions == null)
@ -512,7 +512,7 @@ namespace Microsoft.Extensions.ProjectModel
return null;
}
return new CompilerOptions
return new CommonCompilerOptions
{
Defines = rawOptions.ValueAsStringArray("define"),
LanguageVersion = rawOptions.ValueAsString("languageVersion"),

View file

@ -13,7 +13,7 @@ namespace Microsoft.Extensions.ProjectModel
public IReadOnlyList<LibraryRange> Dependencies { get; set; }
public CompilerOptions CompilerOptions { get; set; }
public CommonCompilerOptions CompilerOptions { get; set; }
public int Line { get; set; }
@ -26,4 +26,4 @@ namespace Microsoft.Extensions.ProjectModel
public string PdbPath { get; set; }
}
}
}

View file

@ -1,36 +1,162 @@
using System;
using System.CommandLine;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using Microsoft.Dnx.Runtime.Common.CommandLine;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using Microsoft.DotNet.Cli.Compiler.Common;
using Microsoft.DotNet.Cli.Utils;
using Microsoft.Extensions.ProjectModel;
namespace Microsoft.DotNet.Tools.Compiler.Csc
{
public class Program
{
private const int ExitFailed = 1;
public static int Main(string[] args)
{
DebugHelper.HandleDebugSwitch(ref args);
var app = new CommandLineApplication();
app.Name = "dotnet compile csc";
app.FullName = "CSharp compiler";
app.Description = "CSharp Compiler for the .NET Platform";
app.HelpOption("-h|--help");
CommonCompilerOptions commonOptions = 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 responseFileArg = app.Argument("<CONFIG>", "The response file to pass to the compiler.");
app.OnExecute(() =>
try
{
// Execute CSC!
var result = RunCsc($"-noconfig @\"{responseFileArg.Value}\"")
.ForwardStdErr()
.ForwardStdOut()
.Execute();
ArgumentSyntax.Parse(args, syntax =>
{
commonOptions = CommonCompilerOptionsExtensions.Parse(syntax);
return result.ExitCode;
});
syntax.DefineOption("temp-output", ref tempOutDir, "Compilation temporary directory");
return app.Execute(args);
syntax.DefineOption("out", ref outputName, "Name of the output assembly");
syntax.DefineOptionList("r|reference", ref references, "Path to a compiler metadata reference");
syntax.DefineOptionList("resource", ref resources, "Resources to embed");
syntax.DefineParameterList("source-files", ref sources, "Compilation sources");
if (tempOutDir == null)
{
syntax.ReportError("Option '--temp-output' is required");
}
});
}
catch (ArgumentSyntaxException)
{
return ExitFailed;
}
var translated = TranslateCommonOptions(commonOptions);
var allArgs = new List<string>(translated);
allArgs.AddRange(GetDefaultOptions());
if (outputName != null)
{
allArgs.Add($"-out:{outputName}");
}
allArgs.AddRange(references.Select(r => $"-r:{r}"));
allArgs.AddRange(resources.Select(resource => $"-resource:{resource}"));
allArgs.AddRange(sources);
var rsp = Path.Combine(tempOutDir.Trim('"'), "dotnet-compile-csc.rsp");
File.WriteAllLines(rsp, allArgs, Encoding.UTF8);
// Execute CSC!
var result = RunCsc($"-noconfig @\"{rsp}\"")
.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",
"-nologo"
};
args.Add(RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
? "-debug:full"
: "-debug:portable");
// TODO: Move mono args to mcs compiler
args.Add("-nowarn:CS1701");
args.Add("-nowarn:CS1702");
args.Add("-nowarn:CS1705");
return args;
}
private static IEnumerable<string> TranslateCommonOptions(CommonCompilerOptions options)
{
List<string> commonArgs = new List<string>();
if (options.Defines != null)
{
commonArgs.AddRange(options.Defines.Select(def => $"-d:{def}"));
}
if (options.LanguageVersion != null)
{
commonArgs.Add($"-langversion:{options.LanguageVersion}");
}
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}\"");
}
if (options.DelaySign == true)
{
commonArgs.Add("-delaysign");
}
// TODO: What is this? What does it mean to sign without a key?
// Is this "OSS" signing?
// if (options.StrongName)
if (options.EmitEntryPoint != true)
{
commonArgs.Add("-t:library");
}
return commonArgs;
}
private static Command RunCsc(string cscArgs)

View file

@ -3,9 +3,10 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using Microsoft.Dnx.Runtime.Common.CommandLine;
using Microsoft.DotNet.Cli.Utils;
using Microsoft.DotNet.Cli.Compiler.Common;
using Microsoft.DotNet.Tools.Common;
using Microsoft.Extensions.ProjectModel;
using Microsoft.Extensions.ProjectModel.Compilation;
@ -172,19 +173,13 @@ namespace Microsoft.DotNet.Tools.Compiler
// Assemble args
var compilerArgs = new List<string>()
{
"-nostdlib",
"-nologo",
$"-out:\"{outputName}\""
$"--temp-output:\"{intermediateOutputPath}\"",
$"--out:\"{outputName}\""
};
// Default suppressions, some versions of mono don't support these
compilerArgs.Add("-nowarn:CS1701");
compilerArgs.Add("-nowarn:CS1702");
compilerArgs.Add("-nowarn:CS1705");
var compilationOptions = context.ProjectFile.GetCompilerOptions(context.TargetFramework, configuration);
// Add compilation options to the args
ApplyCompilationOptions(compilationOptions, compilerArgs);
compilerArgs.AddRange(compilationOptions.SerializeToArgs());
foreach (var dependency in dependencies)
{
@ -218,10 +213,10 @@ namespace Microsoft.DotNet.Tools.Compiler
compilerName = compilerName ?? "csc";
// Write RSP file
var rsp = Path.Combine(intermediateOutputPath, $"dotnet-compile.{compilerName}.{context.ProjectFile.Name}.rsp");
var rsp = Path.Combine(intermediateOutputPath, $"dotnet-compile.{context.ProjectFile.Name}.rsp");
File.WriteAllLines(rsp, compilerArgs);
var result = Command.Create($"dotnet-compile-{compilerName}", $"\"{rsp}\"")
var result = Command.Create($"dotnet-compile-{compilerName}", $"@\"{rsp}\"")
.OnErrorLine(line =>
{
var diagnostic = ParseDiagnostic(context.ProjectDirectory, line);
@ -576,56 +571,6 @@ namespace Microsoft.DotNet.Tools.Compiler
}
}
private static void ApplyCompilationOptions(CompilerOptions compilationOptions, List<string> compilerArgs)
{
var targetType = compilationOptions.EmitEntryPoint.GetValueOrDefault() ? "exe" : "library";
compilerArgs.Add($"-target:{targetType}");
if (compilationOptions.AllowUnsafe.GetValueOrDefault())
{
compilerArgs.Add("-unsafe+");
}
compilerArgs.AddRange(compilationOptions.Defines.Select(d => $"-d:{d}"));
if (compilationOptions.Optimize.GetValueOrDefault())
{
compilerArgs.Add("-optimize");
}
if (!string.IsNullOrEmpty(compilationOptions.Platform))
{
compilerArgs.Add($"-platform:{compilationOptions.Platform}");
}
if (compilationOptions.WarningsAsErrors.GetValueOrDefault())
{
compilerArgs.Add("-warnaserror");
}
if (compilationOptions.DelaySign.GetValueOrDefault())
{
compilerArgs.Add("-delaysign+");
}
if (!string.IsNullOrEmpty(compilationOptions.KeyFile))
{
compilerArgs.Add($"-keyFile:\"{compilationOptions.KeyFile}\"");
}
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
compilerArgs.Add("-debug:full");
}
else
{
compilerArgs.Add("-debug:portable");
}
// TODO: OSS signing
}
private static void ShowDependencyInfo(IEnumerable<LibraryExport> dependencies)
{
foreach (var dependency in dependencies)

View file

@ -14,6 +14,7 @@
"System.IO.FileSystem": "4.0.1-beta-23502",
"Microsoft.DotNet.ProjectModel": "1.0.0-*",
"Microsoft.DotNet.Compiler.Common": "1.0.0-*",
"Microsoft.DotNet.Cli.Utils": {
"type": "build",
"version": "1.0.0-*"