|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Immutable;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.Emit
{
/// <summary>
/// Represents compilation emit options.
/// </summary>
public sealed class EmitOptions : IEquatable<EmitOptions>
{
internal static readonly EmitOptions Default = PlatformInformation.IsWindows
? new EmitOptions()
: new EmitOptions().WithDebugInformationFormat(DebugInformationFormat.PortablePdb);
/// <summary>
/// True to emit an assembly excluding executable code such as method bodies.
/// </summary>
public bool EmitMetadataOnly { get; private set; }
/// <summary>
/// Tolerate errors, producing a PE stream and a success result even in the presence of (some) errors.
/// </summary>
public bool TolerateErrors { get; private set; }
/// <summary>
/// Unless set (private) members that don't affect the language semantics of the resulting assembly will be excluded
/// when emitting metadata-only assemblies as primary output (with <see cref="EmitMetadataOnly"/> on).
/// If emitting a secondary output, this flag is required to be false.
/// </summary>
public bool IncludePrivateMembers { get; private set; }
/// <summary>
/// Type of instrumentation that should be added to the output binary.
/// </summary>
public ImmutableArray<InstrumentationKind> InstrumentationKinds { get; private set; }
/// <summary>
/// Subsystem version
/// </summary>
public SubsystemVersion SubsystemVersion { get; private set; }
/// <summary>
/// Specifies the size of sections in the output file.
/// </summary>
/// <remarks>
/// Valid values are 0, 512, 1024, 2048, 4096 and 8192.
/// If the value is 0 the file alignment is determined based upon the value of <see cref="Platform"/>.
/// </remarks>
public int FileAlignment { get; private set; }
/// <summary>
/// True to enable high entropy virtual address space for the output binary.
/// </summary>
public bool HighEntropyVirtualAddressSpace { get; private set; }
/// <summary>
/// Specifies the preferred base address at which to load the output DLL.
/// </summary>
public ulong BaseAddress { get; private set; }
/// <summary>
/// Debug information format.
/// </summary>
public DebugInformationFormat DebugInformationFormat { get; private set; }
/// <summary>
/// Assembly name override - file name and extension. If not specified the compilation name is used.
/// </summary>
/// <remarks>
/// By default the name of the output assembly is <see cref="Compilation.AssemblyName"/>. Only in rare cases it is necessary
/// to override the name.
///
/// CAUTION: If this is set to a (non-null) value other than the existing compilation output name, then internals-visible-to
/// and assembly references may not work as expected. In particular, things that were visible at bind time, based on the
/// name of the compilation, may not be visible at runtime and vice-versa.
/// </remarks>
public string? OutputNameOverride { get; private set; }
/// <summary>
/// The name of the PDB file to be embedded in the PE image, or null to use the default.
/// </summary>
/// <remarks>
/// If not specified the file name of the source module with an extension changed to "pdb" is used.
/// </remarks>
public string? PdbFilePath { get; private set; }
/// <summary>
/// A crypto hash algorithm used to calculate PDB Checksum stored in the PE/COFF File.
/// If not specified (the value is <c>default(HashAlgorithmName)</c>) the checksum is not calculated.
/// </summary>
public HashAlgorithmName PdbChecksumAlgorithm { get; private set; }
/// <summary>
/// Runtime metadata version.
/// </summary>
public string? RuntimeMetadataVersion { get; private set; }
/// <summary>
/// The encoding used to parse source files that do not have a Byte Order Mark. If specified,
/// is stored in the emitted PDB in order to allow recreating the original compilation.
/// </summary>
public Encoding? DefaultSourceFileEncoding { get; private set; }
/// <summary>
/// If <see cref="DefaultSourceFileEncoding"/> is not specified, the encoding used to parse source files
/// that do not declare their encoding via Byte Order Mark and are not UTF-8 encoded.
/// </summary>
public Encoding? FallbackSourceFileEncoding { get; private set; }
/// <summary>
/// Test only - allows us to test <see cref="InstrumentationKindExtensions.LocalStateTracing"/>.
/// </summary>
private bool _testOnly_AllowLocalStateTracing;
// 1.2 BACKCOMPAT OVERLOAD -- DO NOT TOUCH
public EmitOptions(
bool metadataOnly,
DebugInformationFormat debugInformationFormat,
string pdbFilePath,
string outputNameOverride,
int fileAlignment,
ulong baseAddress,
bool highEntropyVirtualAddressSpace,
SubsystemVersion subsystemVersion,
string runtimeMetadataVersion,
bool tolerateErrors,
bool includePrivateMembers)
: this(
metadataOnly,
debugInformationFormat,
pdbFilePath,
outputNameOverride,
fileAlignment,
baseAddress,
highEntropyVirtualAddressSpace,
subsystemVersion,
runtimeMetadataVersion,
tolerateErrors,
includePrivateMembers,
instrumentationKinds: ImmutableArray<InstrumentationKind>.Empty)
{
}
// 2.7 BACKCOMPAT OVERLOAD -- DO NOT TOUCH
public EmitOptions(
bool metadataOnly,
DebugInformationFormat debugInformationFormat,
string pdbFilePath,
string outputNameOverride,
int fileAlignment,
ulong baseAddress,
bool highEntropyVirtualAddressSpace,
SubsystemVersion subsystemVersion,
string runtimeMetadataVersion,
bool tolerateErrors,
bool includePrivateMembers,
ImmutableArray<InstrumentationKind> instrumentationKinds)
: this(
metadataOnly,
debugInformationFormat,
pdbFilePath,
outputNameOverride,
fileAlignment,
baseAddress,
highEntropyVirtualAddressSpace,
subsystemVersion,
runtimeMetadataVersion,
tolerateErrors,
includePrivateMembers,
instrumentationKinds,
pdbChecksumAlgorithm: null)
{
}
// 3.7 BACKCOMPAT OVERLOAD -- DO NOT TOUCH
public EmitOptions(
bool metadataOnly,
DebugInformationFormat debugInformationFormat,
string? pdbFilePath,
string? outputNameOverride,
int fileAlignment,
ulong baseAddress,
bool highEntropyVirtualAddressSpace,
SubsystemVersion subsystemVersion,
string? runtimeMetadataVersion,
bool tolerateErrors,
bool includePrivateMembers,
ImmutableArray<InstrumentationKind> instrumentationKinds,
HashAlgorithmName? pdbChecksumAlgorithm)
: this(
metadataOnly,
debugInformationFormat,
pdbFilePath,
outputNameOverride,
fileAlignment,
baseAddress,
highEntropyVirtualAddressSpace,
subsystemVersion,
runtimeMetadataVersion,
tolerateErrors,
includePrivateMembers,
instrumentationKinds,
pdbChecksumAlgorithm,
defaultSourceFileEncoding: null,
fallbackSourceFileEncoding: null)
{
}
public EmitOptions(
bool metadataOnly = false,
DebugInformationFormat debugInformationFormat = 0,
string? pdbFilePath = null,
string? outputNameOverride = null,
int fileAlignment = 0,
ulong baseAddress = 0,
bool highEntropyVirtualAddressSpace = false,
SubsystemVersion subsystemVersion = default,
string? runtimeMetadataVersion = null,
bool tolerateErrors = false,
bool includePrivateMembers = true,
ImmutableArray<InstrumentationKind> instrumentationKinds = default,
HashAlgorithmName? pdbChecksumAlgorithm = null,
Encoding? defaultSourceFileEncoding = null,
Encoding? fallbackSourceFileEncoding = null)
{
EmitMetadataOnly = metadataOnly;
DebugInformationFormat = (debugInformationFormat == 0) ? DebugInformationFormat.Pdb : debugInformationFormat;
PdbFilePath = pdbFilePath;
OutputNameOverride = outputNameOverride;
FileAlignment = fileAlignment;
BaseAddress = baseAddress;
HighEntropyVirtualAddressSpace = highEntropyVirtualAddressSpace;
SubsystemVersion = subsystemVersion;
RuntimeMetadataVersion = runtimeMetadataVersion;
TolerateErrors = tolerateErrors;
IncludePrivateMembers = includePrivateMembers;
InstrumentationKinds = instrumentationKinds.NullToEmpty();
PdbChecksumAlgorithm = pdbChecksumAlgorithm ?? HashAlgorithmName.SHA256;
DefaultSourceFileEncoding = defaultSourceFileEncoding;
FallbackSourceFileEncoding = fallbackSourceFileEncoding;
}
private EmitOptions(EmitOptions other) : this(
other.EmitMetadataOnly,
other.DebugInformationFormat,
other.PdbFilePath,
other.OutputNameOverride,
other.FileAlignment,
other.BaseAddress,
other.HighEntropyVirtualAddressSpace,
other.SubsystemVersion,
other.RuntimeMetadataVersion,
other.TolerateErrors,
other.IncludePrivateMembers,
other.InstrumentationKinds,
other.PdbChecksumAlgorithm,
other.DefaultSourceFileEncoding,
other.FallbackSourceFileEncoding)
{
}
internal void TestOnly_AllowLocalStateTracing()
=> _testOnly_AllowLocalStateTracing = true;
public override bool Equals(object? obj)
{
return Equals(obj as EmitOptions);
}
public bool Equals(EmitOptions? other)
{
if (ReferenceEquals(other, null))
{
return false;
}
return
EmitMetadataOnly == other.EmitMetadataOnly &&
BaseAddress == other.BaseAddress &&
FileAlignment == other.FileAlignment &&
HighEntropyVirtualAddressSpace == other.HighEntropyVirtualAddressSpace &&
SubsystemVersion.Equals(other.SubsystemVersion) &&
DebugInformationFormat == other.DebugInformationFormat &&
PdbFilePath == other.PdbFilePath &&
PdbChecksumAlgorithm == other.PdbChecksumAlgorithm &&
OutputNameOverride == other.OutputNameOverride &&
RuntimeMetadataVersion == other.RuntimeMetadataVersion &&
TolerateErrors == other.TolerateErrors &&
IncludePrivateMembers == other.IncludePrivateMembers &&
InstrumentationKinds.NullToEmpty().SequenceEqual(other.InstrumentationKinds.NullToEmpty(), (a, b) => a == b) &&
DefaultSourceFileEncoding == other.DefaultSourceFileEncoding &&
FallbackSourceFileEncoding == other.FallbackSourceFileEncoding;
}
public override int GetHashCode()
{
return Hash.Combine(EmitMetadataOnly,
Hash.Combine(BaseAddress.GetHashCode(),
Hash.Combine(FileAlignment,
Hash.Combine(HighEntropyVirtualAddressSpace,
Hash.Combine(SubsystemVersion.GetHashCode(),
Hash.Combine((int)DebugInformationFormat,
Hash.Combine(PdbFilePath,
Hash.Combine(PdbChecksumAlgorithm.GetHashCode(),
Hash.Combine(OutputNameOverride,
Hash.Combine(RuntimeMetadataVersion,
Hash.Combine(TolerateErrors,
Hash.Combine(IncludePrivateMembers,
Hash.Combine(Hash.CombineValues(InstrumentationKinds),
Hash.Combine(DefaultSourceFileEncoding,
Hash.Combine(FallbackSourceFileEncoding, 0)))))))))))))));
}
public static bool operator ==(EmitOptions? left, EmitOptions? right)
{
return object.Equals(left, right);
}
public static bool operator !=(EmitOptions? left, EmitOptions? right)
{
return !object.Equals(left, right);
}
internal void ValidateOptions(DiagnosticBag diagnostics, CommonMessageProvider messageProvider, bool isDeterministic)
{
if (!DebugInformationFormat.IsValid())
{
diagnostics.Add(messageProvider.CreateDiagnostic(messageProvider.ERR_InvalidDebugInformationFormat, Location.None, (int)DebugInformationFormat));
}
foreach (var instrumentationKind in InstrumentationKinds)
{
if (instrumentationKind == InstrumentationKindExtensions.LocalStateTracing && _testOnly_AllowLocalStateTracing)
{
continue;
}
if (!instrumentationKind.IsValid())
{
diagnostics.Add(messageProvider.CreateDiagnostic(messageProvider.ERR_InvalidInstrumentationKind, Location.None, (int)instrumentationKind));
}
}
if (OutputNameOverride != null)
{
MetadataHelpers.CheckAssemblyOrModuleName(OutputNameOverride, messageProvider, messageProvider.ERR_InvalidOutputName, diagnostics);
}
if (FileAlignment != 0 && !IsValidFileAlignment(FileAlignment))
{
diagnostics.Add(messageProvider.CreateDiagnostic(messageProvider.ERR_InvalidFileAlignment, Location.None, FileAlignment));
}
if (!SubsystemVersion.Equals(SubsystemVersion.None) && !SubsystemVersion.IsValid)
{
diagnostics.Add(messageProvider.CreateDiagnostic(messageProvider.ERR_InvalidSubsystemVersion, Location.None, SubsystemVersion.ToString()));
}
if (PdbChecksumAlgorithm.Name != null)
{
try
{
IncrementalHash.CreateHash(PdbChecksumAlgorithm).Dispose();
}
catch
{
diagnostics.Add(messageProvider.CreateDiagnostic(messageProvider.ERR_InvalidHashAlgorithmName, Location.None, PdbChecksumAlgorithm.ToString()));
}
}
else if (isDeterministic)
{
diagnostics.Add(messageProvider.CreateDiagnostic(messageProvider.ERR_InvalidHashAlgorithmName, Location.None, ""));
}
}
internal static bool IsValidFileAlignment(int value)
{
switch (value)
{
case 512:
case 1024:
case 2048:
case 4096:
case 8192:
return true;
default:
return false;
}
}
public EmitOptions WithEmitMetadataOnly(bool value)
{
if (EmitMetadataOnly == value)
{
return this;
}
return new EmitOptions(this) { EmitMetadataOnly = value };
}
public EmitOptions WithPdbFilePath(string path)
{
if (PdbFilePath == path)
{
return this;
}
return new EmitOptions(this) { PdbFilePath = path };
}
public EmitOptions WithPdbChecksumAlgorithm(HashAlgorithmName name)
{
if (PdbChecksumAlgorithm == name)
{
return this;
}
return new EmitOptions(this) { PdbChecksumAlgorithm = name };
}
public EmitOptions WithOutputNameOverride(string outputName)
{
if (OutputNameOverride == outputName)
{
return this;
}
return new EmitOptions(this) { OutputNameOverride = outputName };
}
public EmitOptions WithDebugInformationFormat(DebugInformationFormat format)
{
if (DebugInformationFormat == format)
{
return this;
}
return new EmitOptions(this) { DebugInformationFormat = format };
}
/// <summary>
/// Sets the byte alignment for portable executable file sections.
/// </summary>
/// <param name="value">Can be one of the following values: 0, 512, 1024, 2048, 4096, 8192</param>
public EmitOptions WithFileAlignment(int value)
{
if (FileAlignment == value)
{
return this;
}
return new EmitOptions(this) { FileAlignment = value };
}
public EmitOptions WithBaseAddress(ulong value)
{
if (BaseAddress == value)
{
return this;
}
return new EmitOptions(this) { BaseAddress = value };
}
public EmitOptions WithHighEntropyVirtualAddressSpace(bool value)
{
if (HighEntropyVirtualAddressSpace == value)
{
return this;
}
return new EmitOptions(this) { HighEntropyVirtualAddressSpace = value };
}
public EmitOptions WithSubsystemVersion(SubsystemVersion subsystemVersion)
{
if (subsystemVersion.Equals(SubsystemVersion))
{
return this;
}
return new EmitOptions(this) { SubsystemVersion = subsystemVersion };
}
public EmitOptions WithRuntimeMetadataVersion(string version)
{
if (RuntimeMetadataVersion == version)
{
return this;
}
return new EmitOptions(this) { RuntimeMetadataVersion = version };
}
public EmitOptions WithTolerateErrors(bool value)
{
if (TolerateErrors == value)
{
return this;
}
return new EmitOptions(this) { TolerateErrors = value };
}
public EmitOptions WithIncludePrivateMembers(bool value)
{
if (IncludePrivateMembers == value)
{
return this;
}
return new EmitOptions(this) { IncludePrivateMembers = value };
}
public EmitOptions WithInstrumentationKinds(ImmutableArray<InstrumentationKind> instrumentationKinds)
{
if (InstrumentationKinds == instrumentationKinds)
{
return this;
}
return new EmitOptions(this) { InstrumentationKinds = instrumentationKinds };
}
public EmitOptions WithDefaultSourceFileEncoding(Encoding? defaultSourceFileEncoding)
{
if (DefaultSourceFileEncoding == defaultSourceFileEncoding)
{
return this;
}
return new EmitOptions(this) { DefaultSourceFileEncoding = defaultSourceFileEncoding };
}
public EmitOptions WithFallbackSourceFileEncoding(Encoding? fallbackSourceFileEncoding)
{
if (FallbackSourceFileEncoding == fallbackSourceFileEncoding)
{
return this;
}
return new EmitOptions(this) { FallbackSourceFileEncoding = fallbackSourceFileEncoding };
}
}
}
|