Switch from ICompilerOptions to more complete IProviderOptions.

This commit is contained in:
Steve Molloy 2020-04-20 20:00:05 -07:00
Родитель af16a9ddfc
Коммит 8b81673abe
15 изменённых файлов: 344 добавлений и 183 удалений

Просмотреть файл

@ -11,6 +11,7 @@ namespace Microsoft.CodeDom.Providers.DotNetCompilerPlatformTest {
private const int DefaultCompilerServerTTL = 0; // set TTL to 0 to turn of keepalive switch
//smolloy debug degub todo - these two lines, and the next two properties are obsolete.
private static ICompilerSettings _csc = new CompilerSettings(CompilerFullPath(@"csc.exe"), DefaultCompilerServerTTL);
private static ICompilerSettings _vb = new CompilerSettings(CompilerFullPath(@"vbc.exe"), DefaultCompilerServerTTL);

Просмотреть файл

@ -0,0 +1,31 @@
using System;
using System.IO;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
namespace DotNetCompilerPlatformTasks
{
public class CheckIfVBCSCompilerWillOverride : Task
{
[Required]
public string Src { get; set; }
[Required]
public string Dest { get; set; }
[Output]
public bool WillOverride { get; set; }
public override bool Execute()
{
WillOverride = false;
try
{
WillOverride = File.Exists(Src) && File.Exists(Dest) && (File.GetLastWriteTime(Src) != File.GetLastWriteTime(Dest));
}
catch { return false; }
return true;
}
}
}

Просмотреть файл

@ -1,6 +1,5 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Management;
using Microsoft.Build.Framework;
@ -52,28 +51,4 @@ namespace DotNetCompilerPlatformTasks
return true;
}
}
public class CheckIfVBCSCompilerWillOverride : Task
{
[Required]
public string Src { get; set; }
[Required]
public string Dest { get; set; }
[Output]
public bool WillOverride { get; set; }
public override bool Execute()
{
WillOverride = false;
try
{
WillOverride = File.Exists(Src) && File.Exists(Dest) && (File.GetLastWriteTime(Src) != File.GetLastWriteTime(Dest));
}
catch { return false; }
return true;
}
}
}

Просмотреть файл

@ -1,12 +1,7 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Collections.Specialized;
using System.Web.Configuration;
namespace Microsoft.CodeDom.Providers.DotNetCompilerPlatform {
@ -32,6 +27,8 @@ namespace Microsoft.CodeDom.Providers.DotNetCompilerPlatform {
lock (_lock) {
if (!_settingsInitialized) {
try {
// I think it should be safe to straight up use regular ConfigurationManager here...
// but if it ain't broke, don't fix it.
LoadSettings(WebConfigurationManager.AppSettings);
}
finally {

Просмотреть файл

@ -11,22 +11,30 @@ namespace Microsoft.CodeDom.Providers.DotNetCompilerPlatform {
/// </summary>
[DesignerCategory("code")]
public sealed class CSharpCodeProvider : Microsoft.CSharp.CSharpCodeProvider {
private ICompilerSettings _compilerSettings;
private IProviderOptions _providerOptions;
/// <summary>
/// Default Constructor
/// </summary>
public CSharpCodeProvider()
: this(null) {
: this((IProviderOptions)null) {
}
/// <summary>
/// Creates an instance using the given ICompilerSettings
/// </summary>
/// <param name="compilerSettings"></param>
[Obsolete("ICompilerSettings is obsolete. Please update code to use IProviderOptions instead.", false)]
public CSharpCodeProvider(ICompilerSettings compilerSettings = null) {
_compilerSettings = compilerSettings == null ? CompilationSettingsHelper.CSC2 : compilerSettings;
_providerOptions = compilerSettings == null ? CompilationUtil.CSC2 : new ProviderOptions(compilerSettings);
}
/// <summary>
/// Creates an instance using the given IProviderOptions
/// </summary>
/// <param name="providerOptions"></param>
public CSharpCodeProvider(IProviderOptions providerOptions = null) {
_providerOptions = providerOptions == null ? CompilationUtil.CSC2 : providerOptions;
}
/// <summary>
@ -35,7 +43,7 @@ namespace Microsoft.CodeDom.Providers.DotNetCompilerPlatform {
/// <returns>An instance of the .NET Compiler Platform C# code compiler</returns>
[Obsolete("Callers should not use the ICodeCompiler interface and should instead use the methods directly on the CodeDomProvider class.")]
public override ICodeCompiler CreateCompiler() {
return new CSharpCompiler(this, _compilerSettings);
return new CSharpCompiler(this, _providerOptions);
}
}
}

Просмотреть файл

@ -14,8 +14,8 @@ namespace Microsoft.CodeDom.Providers.DotNetCompilerPlatform {
private static volatile Regex outputRegWithFileAndLine;
private static volatile Regex outputRegSimple;
public CSharpCompiler(CodeDomProvider codeDomProvider, ICompilerSettings compilerSettings = null)
: base(codeDomProvider, compilerSettings) {
public CSharpCompiler(CodeDomProvider codeDomProvider, IProviderOptions providerOptions = null)
: base(codeDomProvider, providerOptions) {
}
protected override string FileExtension {

Просмотреть файл

@ -16,14 +16,15 @@ using System.Text;
namespace Microsoft.CodeDom.Providers.DotNetCompilerPlatform {
internal abstract class Compiler : ICodeCompiler {
private readonly CodeDomProvider _codeDomProvider;
private readonly ICompilerSettings _compilerSettings;
private readonly IProviderOptions _providerOptions;
private string _compilerFullPath = null;
private const string CLR_PROFILING_SETTING = "COR_ENABLE_PROFILING";
private const string DISABLE_PROFILING = "0";
public Compiler(CodeDomProvider codeDomProvider, ICompilerSettings compilerSettings) {
public Compiler(CodeDomProvider codeDomProvider, IProviderOptions providerOptions)
{
this._codeDomProvider = codeDomProvider;
this._compilerSettings = compilerSettings;
this._providerOptions = providerOptions;
}
public CompilerResults CompileAssemblyFromDom(CompilerParameters options, CodeCompileUnit compilationUnit) {
@ -132,7 +133,7 @@ namespace Microsoft.CodeDom.Providers.DotNetCompilerPlatform {
protected virtual string CompilerName {
get {
if (null == _compilerFullPath) {
_compilerFullPath = _compilerSettings.CompilerFullPath;
_compilerFullPath = _providerOptions.CompilerFullPath;
// Try opening the file to make sure the compiler exist. This will throw an exception
// if it doesn't
@ -277,8 +278,8 @@ namespace Microsoft.CodeDom.Providers.DotNetCompilerPlatform {
}
// Appending TTL to the command line arguments.
if (_compilerSettings.CompilerServerTimeToLive > 0) {
args = string.Format("/shared /keepalive:\"{0}\" {1}", _compilerSettings.CompilerServerTimeToLive, args);
if (_providerOptions.CompilerServerTimeToLive > 0) {
args = string.Format("/shared /keepalive:\"{0}\" {1}", _providerOptions.CompilerServerTimeToLive, args);
}
Compile(options,

Просмотреть файл

@ -57,7 +57,8 @@
<Compile Include="CSharpCompiler.cs" />
<Compile Include="CSharpCodeProvider.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Util\CompilationSettings.cs" />
<Compile Include="Util\IProviderOptions.cs" />
<Compile Include="Util\ProviderOptions.cs" />
<Compile Include="Util\CompilationUtil.cs" />
<Compile Include="Util\ICompilerSettings.cs" />
<Compile Include="VBCodeProvider.cs" />

Просмотреть файл

@ -1,123 +0,0 @@
// 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.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
namespace Microsoft.CodeDom.Providers.DotNetCompilerPlatform {
internal sealed class CompilerSettings : ICompilerSettings {
private readonly string _compilerFullPath;
private readonly int _compilerServerTimeToLive = 0; // seconds
public CompilerSettings(string compilerFullPath, int compilerServerTimeToLive) {
if (string.IsNullOrEmpty(compilerFullPath)) {
throw new ArgumentNullException("compilerFullPath");
}
_compilerFullPath = compilerFullPath;
_compilerServerTimeToLive = compilerServerTimeToLive;
}
string ICompilerSettings.CompilerFullPath {
get {
return _compilerFullPath;
}
}
int ICompilerSettings.CompilerServerTimeToLive {
get{
return _compilerServerTimeToLive;
}
}
}
internal static class CompilationSettingsHelper {
private const int DefaultCompilerServerTTL = 10; //seconds
private const int DefaultCompilerServerTTLInDevEnvironment = 60 * 15;
private const string DevEnvironmentVariableName = "DEV_ENVIRONMENT";
private const string DebuggerAttachedEnvironmentVariable = "IN_DEBUG_MODE";
private const string CustomTTLEnvironmentVariableName = "VBCSCOMPILER_TTL"; // the setting value is in seconds
// Full path of the directory that contains the Roslyn binaries
// and the hosting process has permission to access that path
private const string CustomRoslynCompilerLocation = "ROSLYN_COMPILER_LOCATION";
private static ICompilerSettings _csc;
private static ICompilerSettings _vb;
static CompilationSettingsHelper() {
var ttl = DefaultCompilerServerTTL;
var devEnvironmentSetting = Environment.GetEnvironmentVariable(DevEnvironmentVariableName, EnvironmentVariableTarget.Process);
var debuggerAttachedEnvironmentSetting = Environment.GetEnvironmentVariable(DebuggerAttachedEnvironmentVariable,
EnvironmentVariableTarget.Process);
var customTtlSetting = Environment.GetEnvironmentVariable(CustomTTLEnvironmentVariableName, EnvironmentVariableTarget.Process);
var isDebuggerAttached = IsDebuggerAttached;
int customTtl;
// custom TTL setting always win
if(int.TryParse(customTtlSetting, out customTtl))
{
ttl = customTtl;
}
else
{
if (!string.IsNullOrEmpty(devEnvironmentSetting) ||
!string.IsNullOrEmpty(debuggerAttachedEnvironmentSetting) ||
isDebuggerAttached)
{
ttl = DefaultCompilerServerTTLInDevEnvironment;
}
}
// locate Roslyn compilers order: 1. environment variable 2. appsetting 3. default location
var customRoslynCompilerLocation = Environment.GetEnvironmentVariable(CustomRoslynCompilerLocation, EnvironmentVariableTarget.Process);
if(string.IsNullOrEmpty(customRoslynCompilerLocation))
{
customRoslynCompilerLocation = AppSettings.RoslynCompilerLocation;
}
if(!string.IsNullOrEmpty(customRoslynCompilerLocation))
{
_csc = new CompilerSettings($"{customRoslynCompilerLocation}\\csc.exe", ttl);
_vb = new CompilerSettings($"{customRoslynCompilerLocation}\\vbc.exe", ttl);
}
else
{
_csc = new CompilerSettings(CompilerFullPath(@"bin\roslyn\csc.exe"), ttl);
_vb = new CompilerSettings(CompilerFullPath(@"bin\roslyn\vbc.exe"), ttl);
}
if (isDebuggerAttached) {
Environment.SetEnvironmentVariable(DebuggerAttachedEnvironmentVariable, "1", EnvironmentVariableTarget.Process);
}
}
public static ICompilerSettings CSC2 {
get {
return _csc;
}
}
public static ICompilerSettings VBC2 {
get {
return _vb;
}
}
private static string CompilerFullPath(string relativePath) {
string compilerFullPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, relativePath);
return compilerFullPath;
}
private static bool IsDebuggerAttached {
get {
return IsDebuggerPresent() || Debugger.IsAttached;
}
}
[DllImport("kernel32.dll")]
private extern static bool IsDebuggerPresent();
}
}

Просмотреть файл

@ -1,17 +1,157 @@
// 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.CodeDom.Compiler;
using System;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
namespace Microsoft.CodeDom.Providers.DotNetCompilerPlatform {
internal static class CompilationUtil {
internal static void PrependCompilerOption(CompilerParameters compilParams, string compilerOptions) {
if (compilParams.CompilerOptions == null) {
private const int DefaultCompilerServerTTL = 10; // 10 seconds
private const int DefaultCompilerServerTTLInDevEnvironment = 60 * 15; // 15 minutes
static CompilationUtil()
{
CSC2 = GetProviderOptionsFor("cs");
VBC2 = GetProviderOptionsFor("vb");
if (IsDebuggerAttached)
{
Environment.SetEnvironmentVariable("IN_DEBUG_MODE", "1", EnvironmentVariableTarget.Process);
}
}
public static IProviderOptions CSC2 { get; }
public static IProviderOptions VBC2 { get; }
public static IProviderOptions GetProviderOptionsFor(string fileExt)
{
//
// AllOptions
//
IDictionary<string, string> options = GetProviderOptionsCollection(fileExt);
//
// CompilerFullPath
//
string compilerFullPath = Environment.GetEnvironmentVariable("ROSLYN_COMPILER_LOCATION");
if (String.IsNullOrEmpty(compilerFullPath))
compilerFullPath = AppSettings.RoslynCompilerLocation;
if (String.IsNullOrEmpty(compilerFullPath))
options.TryGetValue("CompilerLocation", out compilerFullPath);
if (String.IsNullOrEmpty(compilerFullPath))
compilerFullPath = CompilerFullPath(@"bin\roslyn\csc.exe");
//
// CompilerServerTimeToLive - default 10 seconds in production, 15 minutes in dev environment.
//
int ttl;
string ttlstr = Environment.GetEnvironmentVariable("VBCSCOMPILER_TTL");
if (String.IsNullOrEmpty(ttlstr))
options.TryGetValue("CompilerServerTTL", out ttlstr);
if (!Int32.TryParse(ttlstr, out ttl)) {
ttl = DefaultCompilerServerTTL;
if (!String.IsNullOrEmpty(Environment.GetEnvironmentVariable("DEV_ENVIRONMENT")) ||
!String.IsNullOrEmpty(Environment.GetEnvironmentVariable("IN_DEBUG_MODE")) ||
CompilationUtil.IsDebuggerAttached)
{
ttl = DefaultCompilerServerTTLInDevEnvironment;
}
}
//
// CompilerVersion - if this is null, we don't care.
//
string compilerVersion;
options.TryGetValue("CompilerVersion", out compilerVersion); // Failure to parse sets to null
//
// WarnAsError - default false.
//
bool warnAsError = false;
if (options.TryGetValue("WarnAsError", out string sWAE)) {
Boolean.TryParse(sWAE, out warnAsError); // Failure to parse sets to 'false'
}
//
// UseAspNetSettings - default true. This was meant to be an ASP.Net support package first and foremost.
//
bool useAspNetSettings = true;
if (options.TryGetValue("UseAspNetSettings", out string sUANS))
{
// Failure to parse sets to 'false', but we want to keep the default 'true'.
if (!Boolean.TryParse(sUANS, out useAspNetSettings))
useAspNetSettings = true;
}
ProviderOptions providerOptions = new ProviderOptions() {
CompilerFullPath = compilerFullPath,
CompilerServerTimeToLive = ttl,
CompilerVersion = compilerVersion,
WarnAsError = warnAsError,
UseAspNetSettings = useAspNetSettings,
AllOptions = options
};
return providerOptions;
}
internal static IDictionary<string, string> GetProviderOptionsCollection(string fileExt)
{
Dictionary<string, string> opts = new Dictionary<string, string>();
if (!CodeDomProvider.IsDefinedExtension(fileExt))
return new ReadOnlyDictionary<string, string>(opts);
CompilerInfo ci = CodeDomProvider.GetCompilerInfo(CodeDomProvider.GetLanguageFromExtension(fileExt));
if (ci == null)
return new ReadOnlyDictionary<string, string>(opts);
// There is a fun little comment about this property in the framework code about making it
// public after 3.5. Guess that didn't happen. Oh well. :)
PropertyInfo pi = ci.GetType().GetProperty("ProviderOptions",
BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.IgnoreCase | BindingFlags.Instance);
if (pi == null)
return new ReadOnlyDictionary<string, string>(opts);
return new ReadOnlyDictionary<string, string>((IDictionary<string, string>)pi.GetValue(ci, null));
}
internal static void PrependCompilerOption(CompilerParameters compilParams, string compilerOptions)
{
if (compilParams.CompilerOptions == null)
{
compilParams.CompilerOptions = compilerOptions;
}
else {
else
{
compilParams.CompilerOptions = compilerOptions + " " + compilParams.CompilerOptions;
}
}
internal static string CompilerFullPath(string relativePath)
{
string compilerFullPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, relativePath);
return compilerFullPath;
}
internal static bool IsDebuggerAttached
{
get {
return IsDebuggerPresent() || Debugger.IsAttached;
}
}
[DllImport("kernel32.dll")]
private extern static bool IsDebuggerPresent();
}
}

Просмотреть файл

@ -1,11 +1,14 @@
// 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;
namespace Microsoft.CodeDom.Providers.DotNetCompilerPlatform {
/// <summary>
/// Provides settings for the C# and VB CodeProviders
/// </summary>
[Obsolete("ICompilerSettings is obsolete. Please update code to use IProviderOptions instead.", false)]
public interface ICompilerSettings {
/// <summary>

Просмотреть файл

@ -0,0 +1,48 @@
// 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.Collections.Generic;
namespace Microsoft.CodeDom.Providers.DotNetCompilerPlatform {
/// <summary>
/// Provides settings for the C# and VB CodeProviders
/// </summary>
public interface IProviderOptions : ICompilerSettings {
// These come from ICompilerSettings.
///// <summary>
///// The full path to csc.exe or vbc.exe
///// </summary>
//string CompilerFullPath { get; }
///// <summary>
///// TTL in seconds
///// </summary>
//int CompilerServerTimeToLive { get; }
/// <summary>
/// A string representing the in-box .Net Framework compiler version to be used.
/// Not applicable to this Roslyn-based package which contains it's own compiler.
/// </summary>
string CompilerVersion { get; }
/// <summary>
/// Returns true if the codedom provider has warnAsError set to true
/// </summary>
bool WarnAsError { get; }
/// <summary>
/// Returns true if the codedom provider is requesting to use similar default
/// compiler options as ASP.Net does with in-box .Net Framework compilers.
/// These options are programatically enforced on top of parameters passed
/// in to the codedom provider.
/// </summary>
bool UseAspNetSettings { get; }
/// <summary>
/// Returns the entire set of options - known or not - as configured in &lt;providerOptions&gt;
/// </summary>
IDictionary<string, string> AllOptions { get; }
}
}

Просмотреть файл

@ -0,0 +1,70 @@
// 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.Collections.Generic;
using System.Collections.ObjectModel;
using System.Threading;
namespace Microsoft.CodeDom.Providers.DotNetCompilerPlatform {
public sealed class ProviderOptions : IProviderOptions {
private IDictionary<string, string> _allOptions;
public ProviderOptions()
{
this.CompilerFullPath = null;
this.CompilerVersion = null;
this.WarnAsError = false;
this.AllOptions = null;
// This results in no keep-alive for the compiler. This will likely result in
// slower performance for any program that calls out the the compiler any
// significant number of times. This is why the CompilerUtil.GetProviderOptionsFor
// does not leave this as 0.
this.CompilerServerTimeToLive = 0;
// This is different from the default that the CompilerUtil.GetProviderOptionsFor
// factory method uses. The primary known user of the factory method is us, and
// this package is first intended to support ASP.Net. However, if somebody is
// creating an instance of this directly, they are probably not an ASP.Net
// project. Thus the different default here.
this.UseAspNetSettings = false;
}
public ProviderOptions(IProviderOptions opts) : this()
{
this.CompilerFullPath = opts.CompilerFullPath;
this.CompilerServerTimeToLive = opts.CompilerServerTimeToLive;
this.CompilerVersion = opts.CompilerVersion;
this.WarnAsError = opts.WarnAsError;
this.UseAspNetSettings = opts.UseAspNetSettings;
this.AllOptions = opts.AllOptions;
}
internal ProviderOptions(ICompilerSettings settings) : this()
{
this.CompilerFullPath = settings.CompilerFullPath;
this.CompilerServerTimeToLive = settings.CompilerServerTimeToLive;
}
public string CompilerFullPath { get; internal set; }
public int CompilerServerTimeToLive { get; internal set; }
// smolloy todo debug degub - we don't use this. It is used by the framework. Do we care to call it out like this?
public string CompilerVersion { get; internal set; }
public bool WarnAsError { get; internal set; }
public bool UseAspNetSettings { get; internal set; }
public IDictionary<string, string> AllOptions {
get {
return _allOptions;
}
internal set {
_allOptions = new ReadOnlyDictionary<string, string>(value);
}
}
}
}

Просмотреть файл

@ -11,21 +11,30 @@ namespace Microsoft.CodeDom.Providers.DotNetCompilerPlatform {
/// </summary>
[DesignerCategory("code")]
public sealed class VBCodeProvider : Microsoft.VisualBasic.VBCodeProvider {
private ICompilerSettings _compilerSettings;
private IProviderOptions _providerOptions;
/// <summary>
/// Default Constructor
/// </summary>
public VBCodeProvider()
: this(null) {
: this((IProviderOptions)null) {
}
/// <summary>
/// Creates an instance using the given ICompilerSettings
/// </summary>
/// <param name="compilerSettings"></param>
public VBCodeProvider(ICompilerSettings compilerSettings = null) {
_compilerSettings = compilerSettings == null ? CompilationSettingsHelper.VBC2 : compilerSettings;
/// <summary>
/// Creates an instance using the given ICompilerSettings
/// </summary>
/// <param name="compilerSettings"></param>
[Obsolete("ICompilerSettings is obsolete. Please update code to use IProviderOptions instead.", false)]
public VBCodeProvider(ICompilerSettings compilerSettings = null) {
_providerOptions = compilerSettings == null ? CompilationUtil.VBC2 : new ProviderOptions(compilerSettings);
}
/// <summary>
/// Creates an instance using the given ICompilerSettings
/// </summary>
/// <param name="providerOptions"></param>
public VBCodeProvider(IProviderOptions providerOptions = null) {
_providerOptions = providerOptions == null ? CompilationUtil.VBC2 : providerOptions;
}
/// <summary>
@ -34,7 +43,7 @@ namespace Microsoft.CodeDom.Providers.DotNetCompilerPlatform {
/// <returns>An instance of the .NET Compiler Platform VB code compiler</returns>
[Obsolete("Callers should not use the ICodeCompiler interface and should instead use the methods directly on the CodeDomProvider class.")]
public override ICodeCompiler CreateCompiler() {
return new VBCompiler(this, _compilerSettings);
return new VBCompiler(this, _providerOptions);
}
}
}

Просмотреть файл

@ -19,8 +19,8 @@ namespace Microsoft.CodeDom.Providers.DotNetCompilerPlatform {
private static volatile Regex outputReg;
public VBCompiler(CodeDomProvider codeDomProvider, ICompilerSettings compilerSettings = null)
: base(codeDomProvider, compilerSettings) {
public VBCompiler(CodeDomProvider codeDomProvider, IProviderOptions providerOptions = null)
: base(codeDomProvider, providerOptions) {
}
protected override string FileExtension {