// Copyright (c) Microsoft. 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.Collections.Immutable; using System.Linq; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis { /// /// Represents compilation options common to C# and VB. /// public abstract class CompilationOptions { /// /// The kind of assembly generated when emitted. /// public OutputKind OutputKind { get; protected set; } /// /// Name of the primary module, or null if a default name should be used. /// /// /// The name usually (but not necessarily) includes an extension, e.g. "MyModule.dll". /// /// If is null the actual name written to metadata /// is derived from the name of the compilation () /// by appending a default extension for . /// public string ModuleName { get; protected set; } /// /// The full name of a global implicit class (script class). This class implicitly encapsulates top-level statements, /// type declarations, and member declarations. Could be a namespace qualified name. /// public string ScriptClassName { get; protected set; } /// /// The full name of a type that declares static Main method. Must be a valid non-generic namespace-qualified name. /// Null if any static Main method is a candidate for an entry point. /// public string MainTypeName { get; protected set; } // Note that we avoid using default(ImmutableArray) for unspecified value since // such value is currently not serializable by JSON serializer. /// /// Specifies public key used to generate strong name for the compilation assembly, or empty if not specified. /// /// /// If specified the values of and /// must be null. If is true the assembly is marked as fully signed /// but only signed with the public key (aka "OSS signing"). /// public ImmutableArray CryptoPublicKey { get; protected set; } /// /// The name of the file containing the public and private keys to use to generate strong name of the /// compilation assembly and to sign it. /// /// /// /// To sign the output supply either one of or . /// but not both. If both are specified is ignored. /// /// /// If is also set, must be the absolute /// path to key file. /// /// public string CryptoKeyFile { get; protected set; } /// /// The CSP container containing the key with which to sign the output. /// /// /// /// To sign the output supply either one of or . /// but not both. If both are specified is ignored. /// /// /// This setting is obsolete and only supported on Microsoft Windows platform. /// Use to generate assemblies with strong name and /// a signing tool (Microsoft .NET Framework Strong Name Utility (sn.exe) or equivalent) to sign them. /// /// public string CryptoKeyContainer { get; protected set; } /// /// Mark the compilation assembly as delay-signed. /// /// /// If true the resulting assembly is marked as delay signed. /// /// If false and , , or is specified /// or attribute System.Reflection.AssemblyKeyFileAttribute or System.Reflection.AssemblyKeyNameAttribute is applied to the /// compilation assembly in source the resulting assembly is signed accordingly to the specified values/attributes. /// /// If null the semantics is specified by the value of attribute System.Reflection.AssemblyDelaySignAttribute /// applied to the compilation assembly in source. If the attribute is not present the value defaults to "false". /// public bool? DelaySign { get; protected set; } /// /// Mark the compilation assembly as fully signed, but only sign with the public key. /// /// /// /// If true, the assembly is marked as signed, but is only signed with the public key. /// /// /// The key must be provided through either an absolute path in /// or directly via . /// /// public bool PublicSign { get; protected set; } /// /// Whether bounds checking on integer arithmetic is enforced by default or not. /// public bool CheckOverflow { get; protected set; } /// /// Specifies which version of the common language runtime (CLR) can run the assembly. /// public Platform Platform { get; protected set; } /// /// Specifies whether or not optimizations should be performed on the output IL. /// This is independent of whether or not PDB information is generated. /// public OptimizationLevel OptimizationLevel { get; protected set; } /// /// Global warning report option /// public ReportDiagnostic GeneralDiagnosticOption { get; protected set; } /// /// Global warning level (from 0 to 4). /// public int WarningLevel { get; protected set; } /// /// Specifies whether building compilation may use multiple threads. /// public bool ConcurrentBuild { get; protected set; } /// /// Specifies whether the compilation should be deterministic. /// public bool Deterministic { get; protected set; } /// /// Used for time-based version generation when contains a wildcard. /// If equal to default() the actual current local time will be used. /// internal DateTime CurrentLocalTime { get; private set; } internal DateTime CurrentLocalTime_internal_protected_set { set { CurrentLocalTime = value; } } /// /// Emit extended custom debug information to the PDB file. /// internal bool ExtendedCustomDebugInformation { get; private set; } // TODO: change visibility of the ExtendedCustomDebugInformation setter to internal & protected internal bool ExtendedCustomDebugInformation_internal_protected_set { set { ExtendedCustomDebugInformation = value; } } /// /// Emit mode that favors debuggability. /// internal bool DebugPlusMode { get; private set; } // TODO: change visibility of the DebugPlusMode setter to internal & protected internal bool DebugPlusMode_internal_protected_set { set { DebugPlusMode = value; } } /// /// Import internal/private members from all references regardless of "internals-visible-to" relationship. /// internal MetadataImportOptions MetadataImportOptions { get; private set; } // TODO: change visibility of the MetadataImportOptions setter to internal & protected internal MetadataImportOptions MetadataImportOptions_internal_protected_set { set { MetadataImportOptions = value; } } /// /// Apply additional disambiguation rules during resolution of referenced assemblies. /// internal bool ReferencesSupersedeLowerVersions { get; private set; } // TODO: change visibility of the ReferencesSupersedeLowerVersions setter to internal & protected internal bool ReferencesSupersedeLowerVersions_internal_protected_set { set { ReferencesSupersedeLowerVersions = value; } } /// /// Modifies the incoming diagnostic, for example escalating its severity, or discarding it (returning null) based on the compilation options. /// /// /// The modified diagnostic, or null internal abstract Diagnostic FilterDiagnostic(Diagnostic diagnostic); /// /// Warning report option for each warning. /// public ImmutableDictionary SpecificDiagnosticOptions { get; protected set; } /// /// Whether diagnostics suppressed in source, i.e. is true, should be reported. /// public bool ReportSuppressedDiagnostics { get; protected set; } /// /// Resolves paths to metadata references specified in source via #r directives. /// Null if the compilation can't contain references to metadata other than those explicitly passed to its factory (such as #r directives in sources). /// public MetadataReferenceResolver MetadataReferenceResolver { get; protected set; } /// /// Gets the resolver for resolving XML document references for the compilation. /// Null if the compilation is not allowed to contain XML file references, such as XML doc comment include tags and permission sets stored in an XML file. /// public XmlReferenceResolver XmlReferenceResolver { get; protected set; } /// /// Gets the resolver for resolving source document references for the compilation. /// Null if the compilation is not allowed to contain source file references, such as #line pragmas and #load directives. /// public SourceReferenceResolver SourceReferenceResolver { get; protected set; } /// /// Provides strong name and signature the source assembly. /// Null if assembly signing is not supported. /// public StrongNameProvider StrongNameProvider { get; protected set; } /// /// Used to compare assembly identities. May implement unification and portability policies specific to the target platform. /// if not specified. /// public AssemblyIdentityComparer AssemblyIdentityComparer { get; protected set; } /// /// A set of strings designating experimental compiler features that are to be enabled. /// [Obsolete] protected internal ImmutableArray Features { get { throw new NotImplementedException(); } protected set { throw new NotImplementedException(); } } private readonly Lazy> _lazyErrors; // Expects correct arguments. internal CompilationOptions( OutputKind outputKind, bool reportSuppressedDiagnostics, string moduleName, string mainTypeName, string scriptClassName, string cryptoKeyContainer, string cryptoKeyFile, ImmutableArray cryptoPublicKey, bool? delaySign, bool publicSign, OptimizationLevel optimizationLevel, bool checkOverflow, Platform platform, ReportDiagnostic generalDiagnosticOption, int warningLevel, ImmutableDictionary specificDiagnosticOptions, bool concurrentBuild, bool deterministic, DateTime currentLocalTime, bool extendedCustomDebugInformation, bool debugPlusMode, XmlReferenceResolver xmlReferenceResolver, SourceReferenceResolver sourceReferenceResolver, MetadataReferenceResolver metadataReferenceResolver, AssemblyIdentityComparer assemblyIdentityComparer, StrongNameProvider strongNameProvider, MetadataImportOptions metadataImportOptions, bool referencesSupersedeLowerVersions) { this.OutputKind = outputKind; this.ModuleName = moduleName; this.MainTypeName = mainTypeName; this.ScriptClassName = scriptClassName ?? WellKnownMemberNames.DefaultScriptClassName; this.CryptoKeyContainer = cryptoKeyContainer; this.CryptoKeyFile = cryptoKeyFile; this.CryptoPublicKey = cryptoPublicKey.NullToEmpty(); this.DelaySign = delaySign; this.CheckOverflow = checkOverflow; this.Platform = platform; this.GeneralDiagnosticOption = generalDiagnosticOption; this.WarningLevel = warningLevel; this.SpecificDiagnosticOptions = specificDiagnosticOptions; this.ReportSuppressedDiagnostics = reportSuppressedDiagnostics; this.OptimizationLevel = optimizationLevel; this.ConcurrentBuild = concurrentBuild; this.Deterministic = deterministic; this.CurrentLocalTime = currentLocalTime; this.ExtendedCustomDebugInformation = extendedCustomDebugInformation; this.DebugPlusMode = debugPlusMode; this.XmlReferenceResolver = xmlReferenceResolver; this.SourceReferenceResolver = sourceReferenceResolver; this.MetadataReferenceResolver = metadataReferenceResolver; this.StrongNameProvider = strongNameProvider; this.AssemblyIdentityComparer = assemblyIdentityComparer ?? AssemblyIdentityComparer.Default; this.MetadataImportOptions = metadataImportOptions; this.ReferencesSupersedeLowerVersions = referencesSupersedeLowerVersions; this.PublicSign = publicSign; _lazyErrors = new Lazy>(() => { var builder = ArrayBuilder.GetInstance(); ValidateOptions(builder); return builder.ToImmutableAndFree(); }); } internal bool CanReuseCompilationReferenceManager(CompilationOptions other) { // This condition has to include all options the Assembly Manager depends on when binding references. // In addition, the assembly name is determined based upon output kind. It is special for netmodules. // Can't reuse when file resolver or identity comparers change. // Can reuse even if StrongNameProvider changes. When resolving a cyclic reference only the simple name is considered, not the strong name. return this.MetadataImportOptions == other.MetadataImportOptions && this.ReferencesSupersedeLowerVersions == other.ReferencesSupersedeLowerVersions && this.OutputKind.IsNetModule() == other.OutputKind.IsNetModule() && object.Equals(this.XmlReferenceResolver, other.XmlReferenceResolver) && object.Equals(this.MetadataReferenceResolver, other.MetadataReferenceResolver) && object.Equals(this.AssemblyIdentityComparer, other.AssemblyIdentityComparer); } /// /// Gets the source language ("C#" or "Visual Basic"). /// public abstract string Language { get; } internal bool EnableEditAndContinue { get { return OptimizationLevel == OptimizationLevel.Debug; } } internal static bool IsValidFileAlignment(int value) { switch (value) { case 512: case 1024: case 2048: case 4096: case 8192: return true; default: return false; } } internal abstract ImmutableArray GetImports(); /// /// Creates a new options instance with the specified general diagnostic option. /// public CompilationOptions WithGeneralDiagnosticOption(ReportDiagnostic value) { return CommonWithGeneralDiagnosticOption(value); } /// /// Creates a new options instance with the specified diagnostic-specific options. /// public CompilationOptions WithSpecificDiagnosticOptions(ImmutableDictionary value) { return CommonWithSpecificDiagnosticOptions(value); } /// /// Creates a new options instance with the specified diagnostic-specific options. /// public CompilationOptions WithSpecificDiagnosticOptions(IEnumerable> value) { return CommonWithSpecificDiagnosticOptions(value); } /// /// Creates a new options instance with the specified suppressed diagnostics reporting option. /// public CompilationOptions WithReportSuppressedDiagnostics(bool value) { return CommonWithReportSuppressedDiagnostics(value); } /// /// Creates a new options instance with the concurrent build property set accordingly. /// public CompilationOptions WithConcurrentBuild(bool concurrent) { return CommonWithConcurrentBuild(concurrent); } /// /// Creates a new options instance with the deterministic property set accordingly. /// public CompilationOptions WithDeterministic(bool deterministic) { return CommonWithDeterministic(deterministic); } /// /// Creates a new options instance with the specified output kind. /// public CompilationOptions WithOutputKind(OutputKind kind) { return CommonWithOutputKind(kind); } /// /// Creates a new options instance with the specified platform. /// public CompilationOptions WithPlatform(Platform platform) { return CommonWithPlatform(platform); } /// /// Creates a new options instance with the specified public sign setting. /// public CompilationOptions WithPublicSign(bool publicSign) => CommonWithPublicSign(publicSign); /// /// Creates a new options instance with optimizations enabled or disabled. /// public CompilationOptions WithOptimizationLevel(OptimizationLevel value) { return CommonWithOptimizationLevel(value); } public CompilationOptions WithXmlReferenceResolver(XmlReferenceResolver resolver) { return CommonWithXmlReferenceResolver(resolver); } public CompilationOptions WithSourceReferenceResolver(SourceReferenceResolver resolver) { return CommonWithSourceReferenceResolver(resolver); } public CompilationOptions WithMetadataReferenceResolver(MetadataReferenceResolver resolver) { return CommonWithMetadataReferenceResolver(resolver); } public CompilationOptions WithAssemblyIdentityComparer(AssemblyIdentityComparer comparer) { return CommonWithAssemblyIdentityComparer(comparer); } public CompilationOptions WithStrongNameProvider(StrongNameProvider provider) { return CommonWithStrongNameProvider(provider); } public CompilationOptions WithModuleName(string moduleName) { return CommonWithModuleName(moduleName); } public CompilationOptions WithMainTypeName(string mainTypeName) { return CommonWithMainTypeName(mainTypeName); } public CompilationOptions WithScriptClassName(string scriptClassName) { return CommonWithScriptClassName(scriptClassName); } public CompilationOptions WithCryptoKeyContainer(string cryptoKeyContainer) { return CommonWithCryptoKeyContainer(cryptoKeyContainer); } public CompilationOptions WithCryptoKeyFile(string cryptoKeyFile) { return CommonWithCryptoKeyFile(cryptoKeyFile); } public CompilationOptions WithCryptoPublicKey(ImmutableArray cryptoPublicKey) { return CommonWithCryptoPublicKey(cryptoPublicKey); } public CompilationOptions WithDelaySign(bool? delaySign) { return CommonWithDelaySign(delaySign); } public CompilationOptions WithOverflowChecks(bool checkOverflow) { return CommonWithCheckOverflow(checkOverflow); } protected abstract CompilationOptions CommonWithConcurrentBuild(bool concurrent); protected abstract CompilationOptions CommonWithDeterministic(bool deterministic); protected abstract CompilationOptions CommonWithOutputKind(OutputKind kind); protected abstract CompilationOptions CommonWithPlatform(Platform platform); protected abstract CompilationOptions CommonWithPublicSign(bool publicSign); protected abstract CompilationOptions CommonWithOptimizationLevel(OptimizationLevel value); protected abstract CompilationOptions CommonWithXmlReferenceResolver(XmlReferenceResolver resolver); protected abstract CompilationOptions CommonWithSourceReferenceResolver(SourceReferenceResolver resolver); protected abstract CompilationOptions CommonWithMetadataReferenceResolver(MetadataReferenceResolver resolver); protected abstract CompilationOptions CommonWithAssemblyIdentityComparer(AssemblyIdentityComparer comparer); protected abstract CompilationOptions CommonWithStrongNameProvider(StrongNameProvider provider); protected abstract CompilationOptions CommonWithGeneralDiagnosticOption(ReportDiagnostic generalDiagnosticOption); protected abstract CompilationOptions CommonWithSpecificDiagnosticOptions(ImmutableDictionary specificDiagnosticOptions); protected abstract CompilationOptions CommonWithSpecificDiagnosticOptions(IEnumerable> specificDiagnosticOptions); protected abstract CompilationOptions CommonWithReportSuppressedDiagnostics(bool reportSuppressedDiagnostics); protected abstract CompilationOptions CommonWithModuleName(string moduleName); protected abstract CompilationOptions CommonWithMainTypeName(string mainTypeName); protected abstract CompilationOptions CommonWithScriptClassName(string scriptClassName); protected abstract CompilationOptions CommonWithCryptoKeyContainer(string cryptoKeyContainer); protected abstract CompilationOptions CommonWithCryptoKeyFile(string cryptoKeyFile); protected abstract CompilationOptions CommonWithCryptoPublicKey(ImmutableArray cryptoPublicKey); protected abstract CompilationOptions CommonWithDelaySign(bool? delaySign); protected abstract CompilationOptions CommonWithCheckOverflow(bool checkOverflow); [Obsolete] protected abstract CompilationOptions CommonWithFeatures(ImmutableArray features); /// /// Performs validation of options compatibilities and generates diagnostics if needed /// internal abstract void ValidateOptions(ArrayBuilder builder); internal void ValidateOptions(ArrayBuilder builder, CommonMessageProvider messageProvider) { if (!CryptoPublicKey.IsEmpty) { if (CryptoKeyFile != null) { builder.Add(messageProvider.CreateDiagnostic(messageProvider.ERR_MutuallyExclusiveOptions, Location.None, nameof(CryptoPublicKey), nameof(CryptoKeyFile))); } if (CryptoKeyContainer != null) { builder.Add(messageProvider.CreateDiagnostic(messageProvider.ERR_MutuallyExclusiveOptions, Location.None, nameof(CryptoPublicKey), nameof(CryptoKeyContainer))); } } if (PublicSign) { if (CryptoKeyFile != null && !PathUtilities.IsAbsolute(CryptoKeyFile)) { builder.Add(messageProvider.CreateDiagnostic(messageProvider.ERR_OptionMustBeAbsolutePath, Location.None, nameof(CryptoKeyFile))); } if (CryptoKeyContainer != null) { builder.Add(messageProvider.CreateDiagnostic(messageProvider.ERR_MutuallyExclusiveOptions, Location.None, nameof(PublicSign), nameof(CryptoKeyContainer))); } if (DelaySign == true) { builder.Add(messageProvider.CreateDiagnostic(messageProvider.ERR_MutuallyExclusiveOptions, Location.None, nameof(PublicSign), nameof(DelaySign))); } } } /// /// Errors collection related to an incompatible set of compilation options /// public ImmutableArray Errors { get { return _lazyErrors.Value; } } public abstract override bool Equals(object obj); protected bool EqualsHelper(CompilationOptions other) { if (object.ReferenceEquals(other, null)) { return false; } // NOTE: StringComparison.Ordinal is used for type name comparisons, even for VB. That's because // a change in the canonical case should still change the option. bool equal = this.CheckOverflow == other.CheckOverflow && this.ConcurrentBuild == other.ConcurrentBuild && this.Deterministic == other.Deterministic && this.CurrentLocalTime == other.CurrentLocalTime && this.ExtendedCustomDebugInformation == other.ExtendedCustomDebugInformation && this.DebugPlusMode == other.DebugPlusMode && string.Equals(this.CryptoKeyContainer, other.CryptoKeyContainer, StringComparison.Ordinal) && string.Equals(this.CryptoKeyFile, other.CryptoKeyFile, StringComparison.Ordinal) && this.CryptoPublicKey.SequenceEqual(other.CryptoPublicKey) && this.DelaySign == other.DelaySign && this.GeneralDiagnosticOption == other.GeneralDiagnosticOption && string.Equals(this.MainTypeName, other.MainTypeName, StringComparison.Ordinal) && this.MetadataImportOptions == other.MetadataImportOptions && this.ReferencesSupersedeLowerVersions == other.ReferencesSupersedeLowerVersions && string.Equals(this.ModuleName, other.ModuleName, StringComparison.Ordinal) && this.OptimizationLevel == other.OptimizationLevel && this.OutputKind == other.OutputKind && this.Platform == other.Platform && this.ReportSuppressedDiagnostics == other.ReportSuppressedDiagnostics && string.Equals(this.ScriptClassName, other.ScriptClassName, StringComparison.Ordinal) && this.SpecificDiagnosticOptions.SequenceEqual(other.SpecificDiagnosticOptions, (left, right) => (left.Key == right.Key) && (left.Value == right.Value)) && this.WarningLevel == other.WarningLevel && object.Equals(this.MetadataReferenceResolver, other.MetadataReferenceResolver) && object.Equals(this.XmlReferenceResolver, other.XmlReferenceResolver) && object.Equals(this.SourceReferenceResolver, other.SourceReferenceResolver) && object.Equals(this.StrongNameProvider, other.StrongNameProvider) && object.Equals(this.AssemblyIdentityComparer, other.AssemblyIdentityComparer) && this.PublicSign == other.PublicSign; return equal; } public abstract override int GetHashCode(); protected int GetHashCodeHelper() { return Hash.Combine(this.CheckOverflow, Hash.Combine(this.ConcurrentBuild, Hash.Combine(this.Deterministic, Hash.Combine(this.CurrentLocalTime.GetHashCode(), Hash.Combine(this.ExtendedCustomDebugInformation, Hash.Combine(this.DebugPlusMode, Hash.Combine(this.CryptoKeyContainer != null ? StringComparer.Ordinal.GetHashCode(this.CryptoKeyContainer) : 0, Hash.Combine(this.CryptoKeyFile != null ? StringComparer.Ordinal.GetHashCode(this.CryptoKeyFile) : 0, Hash.Combine(Hash.CombineValues(this.CryptoPublicKey, 16), Hash.Combine((int)this.GeneralDiagnosticOption, Hash.Combine(this.MainTypeName != null ? StringComparer.Ordinal.GetHashCode(this.MainTypeName) : 0, Hash.Combine((int)this.MetadataImportOptions, Hash.Combine(this.ReferencesSupersedeLowerVersions, Hash.Combine(this.ModuleName != null ? StringComparer.Ordinal.GetHashCode(this.ModuleName) : 0, Hash.Combine((int)this.OptimizationLevel, Hash.Combine((int)this.OutputKind, Hash.Combine((int)this.Platform, Hash.Combine(this.ReportSuppressedDiagnostics, Hash.Combine(this.ScriptClassName != null ? StringComparer.Ordinal.GetHashCode(this.ScriptClassName) : 0, Hash.Combine(Hash.CombineValues(this.SpecificDiagnosticOptions), Hash.Combine(this.WarningLevel, Hash.Combine(this.MetadataReferenceResolver, Hash.Combine(this.XmlReferenceResolver, Hash.Combine(this.SourceReferenceResolver, Hash.Combine(this.StrongNameProvider, Hash.Combine(this.AssemblyIdentityComparer, Hash.Combine(this.PublicSign, 0))))))))))))))))))))))))))); } public static bool operator ==(CompilationOptions left, CompilationOptions right) { return object.Equals(left, right); } public static bool operator !=(CompilationOptions left, CompilationOptions right) { return !object.Equals(left, right); } } }