<InstallerTasksAssemblyPath Condition="'$(MSBuildRuntimeType)' == 'Core'">$([MSBuild]::NormalizePath('$(ArtifactsBinDir)', 'installer.tasks', 'Debug', '$(NetCoreAppToolCurrent)', 'installer.tasks.dll'))</InstallerTasksAssemblyPath>
<InstallerTasksAssemblyPath Condition="'$(MSBuildRuntimeType)' != 'Core'">$([MSBuild]::NormalizePath('$(ArtifactsBinDir)', 'installer.tasks', 'Debug', 'net461', 'installer.tasks.dll'))</InstallerTasksAssemblyPath>
+ <Crossgen2SdkOverridePropsPath Condition="'$(MSBuildRuntimeType)' == 'Core'">$([MSBuild]::NormalizePath('$(ArtifactsBinDir)', 'Crossgen2Tasks', 'Debug', '$(NetCoreAppToolCurrent)', 'Microsoft.NET.CrossGen.props'))</Crossgen2SdkOverridePropsPath>
+ <Crossgen2SdkOverrideTargetsPath Condition="'$(MSBuildRuntimeType)' == 'Core'">$([MSBuild]::NormalizePath('$(ArtifactsBinDir)', 'Crossgen2Tasks', 'Debug', '$(NetCoreAppToolCurrent)', 'Microsoft.NET.CrossGen.targets'))</Crossgen2SdkOverrideTargetsPath>
<AppleAppBuilderTasksAssemblyPath>$([MSBuild]::NormalizePath('$(AppleAppBuilderDir)', 'AppleAppBuilder.dll'))</AppleAppBuilderTasksAssemblyPath>
<AndroidAppBuilderTasksAssemblyPath>$([MSBuild]::NormalizePath('$(AndroidAppBuilderDir)', 'AndroidAppBuilder.dll'))</AndroidAppBuilderTasksAssemblyPath>
<WasmAppBuilderTasksAssemblyPath>$([MSBuild]::NormalizePath('$(WasmAppBuilderDir)', 'WasmAppBuilder.dll'))</WasmAppBuilderTasksAssemblyPath>
<Choose>
<When Condition="$(_subset.Contains('+packs.product+'))">
+ <ItemGroup Condition="'$(RuntimeFlavor)' != 'Mono'">
+ <SharedFrameworkProjectToBuild Include="$(InstallerProjectRoot)pkg\sfx\Microsoft.NETCore.App\Microsoft.NETCore.App.Runtime.Composite.sfxproj" />
+ <SharedFrameworkProjectToBuild Include="$(InstallerProjectRoot)pkg\sfx\bundle\Microsoft.NETCore.App.Composite.Bundle.bundleproj" />
+ </ItemGroup>
<ItemGroup Condition="'$(PgoInstrument)' != 'true'">
<SharedFrameworkProjectToBuild Condition="'$(BuildMonoAOTCrossCompilerOnly)' != 'true'" Include="$(InstallerProjectRoot)pkg\sfx\Microsoft.NETCore.App\Microsoft.NETCore.App.Ref.sfxproj" />
<SharedFrameworkProjectToBuild Condition="'$(RuntimeFlavor)' != 'Mono'" Include="$(InstallerProjectRoot)pkg\sfx\Microsoft.NETCore.App\Microsoft.NETCore.App.Host.sfxproj" />
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Collections.Immutable;
+
+namespace ILCompiler
+{
+ public class CompositeImageSettings
+ {
+ public ImmutableArray<byte> PublicKey;
+ public Version AssemblyVersion;
+ }
+}
\ No newline at end of file
builder.EmitReloc(factory.ManifestMetadataTable, RelocType.IMAGE_REL_SYMBOL_SIZE);
// Flags
- builder.EmitUInt(0);
+ if (factory.CompositeImageSettings.PublicKey != null)
+ {
+ const uint COMIMAGE_FLAGS_STRONGNAMESIGNED = 8;
+ builder.EmitUInt(COMIMAGE_FLAGS_STRONGNAMESIGNED);
+ }
+ else
+ {
+ builder.EmitUInt(0);
+ }
// Entrypoint
builder.EmitInt(0);
MetadataBuilder metadataBuilder = new MetadataBuilder();
+ AssemblyHashAlgorithm hashAlgorithm = AssemblyHashAlgorithm.None;
+ BlobHandle publicKeyBlob = default(BlobHandle);
+ AssemblyFlags manifestAssemblyFlags = default(AssemblyFlags);
+ Version manifestAssemblyVersion = new Version(0, 0, 0, 0);
+
+ if ((factory.CompositeImageSettings != null) && factory.CompilationModuleGroup.IsCompositeBuildMode)
+ {
+ if (factory.CompositeImageSettings.PublicKey != null)
+ {
+ hashAlgorithm = AssemblyHashAlgorithm.Sha1;
+ publicKeyBlob = metadataBuilder.GetOrAddBlob(factory.CompositeImageSettings.PublicKey);
+ manifestAssemblyFlags |= AssemblyFlags.PublicKey;
+ }
+
+ if (factory.CompositeImageSettings.AssemblyVersion != null)
+ {
+ manifestAssemblyVersion = factory.CompositeImageSettings.AssemblyVersion;
+ }
+ }
+
string manifestMetadataAssemblyName = "ManifestMetadata";
metadataBuilder.AddAssembly(
metadataBuilder.GetOrAddString(manifestMetadataAssemblyName),
- new Version(0, 0, 0, 0),
+ manifestAssemblyVersion,
culture: default(StringHandle),
- publicKey: default(BlobHandle),
- flags: default(AssemblyFlags),
- hashAlgorithm: AssemblyHashAlgorithm.None);
+ publicKey: publicKeyBlob,
+ flags: manifestAssemblyFlags,
+ hashAlgorithm: hashAlgorithm);
metadataBuilder.AddModule(
0,
public MetadataManager MetadataManager { get; }
+ public CompositeImageSettings CompositeImageSettings { get; set; }
+
public bool MarkingComplete => _markingComplete;
public void SetMarkingComplete()
private ReadyToRunFileLayoutAlgorithm _r2rFileLayoutAlgorithm;
private int _customPESectionAlignment;
private bool _verifyTypeAndFieldLayout;
+ private CompositeImageSettings _compositeImageSettings;
private string _jitPath;
private string _outputFile;
return this;
}
+ public ReadyToRunCodegenCompilationBuilder UseCompositeImageSettings(CompositeImageSettings compositeImageSettings)
+ {
+ _compositeImageSettings = compositeImageSettings;
+ return this;
+ }
+
public override ICompilation ToCompilation()
{
// TODO: only copy COR headers for single-assembly build and for composite build with embedded MSIL
win32Resources,
flags);
+ factory.CompositeImageSettings = _compositeImageSettings;
+
IComparer<DependencyNodeCore<NodeFactory>> comparer = new SortableDependencyNode.ObjectNodeComparer(new CompilerComparer());
DependencyAnalyzerBase<NodeFactory> graph = CreateDependencyGraph(factory, comparer);
<Compile Include="ObjectWriter\MapFileBuilder.cs" />
<Compile Include="CodeGen\ReadyToRunObjectWriter.cs" />
<Compile Include="Compiler\CompilationModuleGroup.ReadyToRun.cs" />
+ <Compile Include="Compiler\CompositeImageSettings.cs" />
<Compile Include="Compiler\CryptographicHashProvider.cs" />
<Compile Include="Compiler\DependencyAnalysis\AllMethodsOnTypeNode.cs" />
<Compile Include="Compiler\DependencyAnalysis\ArrayOfEmbeddedDataNode.cs" />
public bool CompileBubbleGenerics;
public bool Verbose;
public bool Composite;
+ public string CompositeKeyFile;
public bool CompileNoMethods;
public bool EmbedPgoData;
public bool OutNearInput;
syntax.DefineOption("Ot|optimize-time", ref OptimizeTime, SR.OptimizeSpeedOption);
syntax.DefineOption("inputbubble", ref InputBubble, SR.InputBubbleOption);
syntax.DefineOption("composite", ref Composite, SR.CompositeBuildMode);
+ syntax.DefineOption("compositekeyfile", ref CompositeKeyFile, SR.CompositeKeyFile);
syntax.DefineOption("compile-no-methods", ref CompileNoMethods, SR.CompileNoMethodsOption);
syntax.DefineOption("out-near-input", ref OutNearInput, SR.OutNearInputOption);
syntax.DefineOption("single-file-compilation", ref SingleFileCompilation, SR.SingleFileCompilationOption);
using System;
using System.Collections.Generic;
+using System.Collections.Immutable;
using System.IO;
using System.Reflection.Metadata;
using System.Reflection.PortableExecutable;
optimizationMode = ((EcmaAssembly)inputModules[0].Assembly).HasOptimizationsDisabled() ? OptimizationMode.None : OptimizationMode.Blended;
}
+ CompositeImageSettings compositeImageSettings = new CompositeImageSettings();
+
+ if (_commandLineOptions.CompositeKeyFile != null)
+ {
+ ImmutableArray<byte> compositeStrongNameKey = File.ReadAllBytes(_commandLineOptions.CompositeKeyFile).ToImmutableArray();
+ if (!IsValidPublicKey(compositeStrongNameKey))
+ {
+ throw new Exception(string.Format(SR.ErrorCompositeKeyFileNotPublicKey));
+ }
+
+ compositeImageSettings.PublicKey = compositeStrongNameKey;
+ }
+
//
// Compile
//
.UseParallelism(_commandLineOptions.Parallelism)
.UseProfileData(profileDataManager)
.FileLayoutAlgorithms(_methodLayout, _fileLayout)
+ .UseCompositeImageSettings(compositeImageSettings)
.UseJitPath(_commandLineOptions.JitPath)
.UseInstructionSetSupport(instructionSetSupport)
.UseCustomPESectionAlignment(_commandLineOptions.CustomPESectionAlignment)
return false;
}
+ private enum AlgorithmClass
+ {
+ Signature = 1,
+ Hash = 4,
+ }
+
+ private enum AlgorithmSubId
+ {
+ Sha1Hash = 4,
+ MacHash = 5,
+ RipeMdHash = 6,
+ RipeMd160Hash = 7,
+ Ssl3ShaMD5Hash = 8,
+ HmacHash = 9,
+ Tls1PrfHash = 10,
+ HashReplacOwfHash = 11,
+ Sha256Hash = 12,
+ Sha384Hash = 13,
+ Sha512Hash = 14,
+ }
+
+ private struct AlgorithmId
+ {
+ // From wincrypt.h
+ private const int AlgorithmClassOffset = 13;
+ private const int AlgorithmClassMask = 0x7;
+ private const int AlgorithmSubIdOffset = 0;
+ private const int AlgorithmSubIdMask = 0x1ff;
+
+ private readonly uint _flags;
+
+ public const int RsaSign = 0x00002400;
+ public const int Sha = 0x00008004;
+
+ public bool IsSet
+ {
+ get { return _flags != 0; }
+ }
+
+ public AlgorithmClass Class
+ {
+ get { return (AlgorithmClass)((_flags >> AlgorithmClassOffset) & AlgorithmClassMask); }
+ }
+
+ public AlgorithmSubId SubId
+ {
+ get { return (AlgorithmSubId)((_flags >> AlgorithmSubIdOffset) & AlgorithmSubIdMask); }
+ }
+
+ public AlgorithmId(uint flags)
+ {
+ _flags = flags;
+ }
+ }
+
+ private static readonly ImmutableArray<byte> s_ecmaKey = ImmutableArray.Create(new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0 });
+
+ private const int SnPublicKeyBlobSize = 13;
+
+ // From wincrypt.h
+ private const byte PublicKeyBlobId = 0x06;
+ private const byte PrivateKeyBlobId = 0x07;
+
+ // internal for testing
+ internal const int s_publicKeyHeaderSize = SnPublicKeyBlobSize - 1;
+
+ // From StrongNameInternal.cpp
+ // Checks to see if a public key is a valid instance of a PublicKeyBlob as
+ // defined in StongName.h
+ internal static bool IsValidPublicKey(ImmutableArray<byte> blob)
+ {
+ // The number of public key bytes must be at least large enough for the header and one byte of data.
+ if (blob.IsDefault || blob.Length < s_publicKeyHeaderSize + 1)
+ {
+ return false;
+ }
+
+ var blobReader = new BinaryReader(new MemoryStream(blob.ToArray()));
+
+ // Signature algorithm ID
+ var sigAlgId = blobReader.ReadUInt32();
+ // Hash algorithm ID
+ var hashAlgId = blobReader.ReadUInt32();
+ // Size of public key data in bytes, not including the header
+ var publicKeySize = blobReader.ReadUInt32();
+ // publicKeySize bytes of public key data
+ var publicKey = blobReader.ReadByte();
+
+ // The number of public key bytes must be the same as the size of the header plus the size of the public key data.
+ if (blob.Length != s_publicKeyHeaderSize + publicKeySize)
+ {
+ return false;
+ }
+
+ // Check for the ECMA key, which does not obey the invariants checked below.
+ if (System.Linq.Enumerable.SequenceEqual(blob, s_ecmaKey))
+ {
+ return true;
+ }
+
+ // The public key must be in the wincrypto PUBLICKEYBLOB format
+ if (publicKey != PublicKeyBlobId)
+ {
+ return false;
+ }
+
+ var signatureAlgorithmId = new AlgorithmId(sigAlgId);
+ if (signatureAlgorithmId.IsSet && signatureAlgorithmId.Class != AlgorithmClass.Signature)
+ {
+ return false;
+ }
+
+ var hashAlgorithmId = new AlgorithmId(hashAlgId);
+ if (hashAlgorithmId.IsSet && (hashAlgorithmId.Class != AlgorithmClass.Hash || hashAlgorithmId.SubId < AlgorithmSubId.Sha1Hash))
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+
private static int Main(string[] args)
{
#if DEBUG
<data name="CompositeBuildMode" xml:space="preserve">
<value>Emit a composite R2R image comprising a number of input assemblies</value>
</data>
+ <data name="CompositeKeyFile" xml:space="preserve">
+ <value>KeyFile(.snk) for specifiying a Public Key for the composite image created</value>
+ </data>
+ <data name="ErrorCompositeKeyFileNotPublicKey" xml:space="preserve">
+ <value>--CompositeKeyFile does not specify a valid public key</value>
+ </data>
<data name="InputFilesToCompile" xml:space="preserve">
<value>Input file(s) to compile</value>
</data>
<Target Name="ReturnProductVersion" Returns="$(Version)" />
<ItemGroup Condition="'$(RuntimeFlavor)' != 'Mono'">
- <PackageReference Include="Microsoft.DotNet.Build.Tasks.Installers" Version="$(MicrosoftDotNetBuildTasksInstallersVersion)" />
+ <PackageReference Condition="'$(SkipInstallersPackageReference)' != 'true'" Include="Microsoft.DotNet.Build.Tasks.Installers" Version="$(MicrosoftDotNetBuildTasksInstallersVersion)" />
<PackageReference Include="Microsoft.DotNet.Build.Tasks.Archives" Version="$(MicrosoftDotNetBuildTasksArchivesVersion)" />
</ItemGroup>
<NativeRuntimeAsset Include="$(_diaSymReaderTargetArchPath)" TargetPath="tools/" />
</ItemGroup>
+ <Import Project="$(Crossgen2SdkOverridePropsPath)" />
<Import Project="Sdk.targets" Sdk="Microsoft.NET.Sdk" />
<Import Project="Sdk.targets" Sdk="Microsoft.DotNet.SharedFramework.Sdk" />
+ <Import Project="$(Crossgen2SdkOverrideTargetsPath)" />
<Import Project="ReadyToRun.targets" />
<Target Name="GetFilesToPublish">
--- /dev/null
+<Project>
+ <Import Project="Sdk.props" Sdk="Microsoft.NET.Sdk" />
+ <Import Project="Sdk.props" Sdk="Microsoft.DotNet.SharedFramework.Sdk" />
+
+ <PropertyGroup>
+ <PlatformPackageType>RuntimePack</PlatformPackageType>
+ <ArchiveName>dotnet-runtime-internal-composite</ArchiveName>
+ <OverridePackageId>$(SharedFrameworkName).Composite</OverridePackageId>
+ <OverridePackageId Condition="'$(PgoInstrument)' != ''">$(SharedFrameworkName).Composite.PGO</OverridePackageId>
+ <GenerateSymbolsArchive>true</GenerateSymbolsArchive>
+ <SymbolsArchiveName>dotnet-runtime-composite-symbols</SymbolsArchiveName>
+ <UseTemplatedPlatformManifest>true</UseTemplatedPlatformManifest>
+ <ForcePublishReadyToRunComposite>true</ForcePublishReadyToRunComposite>
+ <SkipBuild Condition="'$(RuntimeFlavor)' == 'Mono' or '$(PgoInstrument)' != ''">true</SkipBuild>
+ <SkipInstallersPackageReference>true</SkipInstallersPackageReference>
+ <PermitDllAndExeFilesLackingFileVersion>true</PermitDllAndExeFilesLackingFileVersion> <!-- The composite image does not have a FileVersion resource -->
+ </PropertyGroup>
+
+ <ItemGroup>
+ <PlatformManifestFileEntry Include="Microsoft.NETCore.App.Composite.r2r.dll" IsNative="true" />
+ <PublishReadyToRunCompositeExclusions Include="System.Reflection.Metadata.dll" />
+ <PublishReadyToRunCompositeExclusions Include="System.Collections.Immutable.dll" />
+ </ItemGroup>
+
+ <ItemGroup>
+ <!-- Exclude the composite image from the closure check as it includes references to already excluded shim assemblies -->
+ <ExcludeFromClosure Include="Microsoft.NETCore.App.Composite.r2r" />
+ </ItemGroup>
+
+ <Import Project="Microsoft.NETCore.App.Runtime.props" />
+
+ <ItemGroup>
+ <PublishReadyToRunCrossgen2ExtraArgsList Include="--compositekeyfile:$(AssemblyOriginatorKeyFile)"/>
+ </ItemGroup>
+
+ <Import Project="ReadyToRun.targets" />
+</Project>
--- /dev/null
+<Project>
+ <PropertyGroup>
+ <IncludeFallbacksInDepsFile>true</IncludeFallbacksInDepsFile>
+ <GetSharedFrameworkFilesForReadyToRunDependsOn>
+ AddRuntimeFilesToPackage;
+ AddFrameworkFilesToPackage
+ </GetSharedFrameworkFilesForReadyToRunDependsOn>
+ <PublishReadyToRun Condition="'$(RuntimeFlavor)' != 'Mono'">true</PublishReadyToRun>
+ <PublishReadyToRun Condition="'$(RuntimeFlavor)' == 'Mono'">false</PublishReadyToRun>
+ <!-- Disable crossgen on FreeBSD, NetBSD, illumos and Solaris for now. This can be revisited when we have full support. -->
+ <PublishReadyToRun Condition="'$(TargetOS)'=='FreeBSD' Or '$(TargetOS)'=='NetBSD' Or '$(TargetOS)'=='illumos' Or '$(TargetOS)'=='Solaris'">false</PublishReadyToRun>
+ <!-- These components are installed by the root shared framework, but not others. -->
+ <IncludeWerRelatedKeys>true</IncludeWerRelatedKeys>
+ <IncludeBreadcrumbStoreFolder>true</IncludeBreadcrumbStoreFolder>
+ <MacOSPackageDescription>The .NET Shared Framework</MacOSPackageDescription>
+ </PropertyGroup>
+
+ <PropertyGroup Condition="'$(RuntimeFlavor)' == 'Mono'">
+ <RuntimeSpecificFrameworkSuffix>Mono</RuntimeSpecificFrameworkSuffix>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(MonoEnableLLVM)' == 'true' and '$(RuntimeFlavor)' == 'Mono' and '$(TargetsMobile)' != 'true' and '$(TargetsBrowser)' != 'true'">
+ <RuntimeSpecificFrameworkSuffix>Mono.LLVM</RuntimeSpecificFrameworkSuffix>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(MonoBundleLLVMOptimizer)' == 'true' and '$(RuntimeFlavor)' == 'Mono' and '$(TargetsMobile)' != 'true' and '$(TargetsBrowser)' != 'true'">
+ <RuntimeSpecificFrameworkSuffix>Mono.LLVM.AOT</RuntimeSpecificFrameworkSuffix>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(RuntimeSpecificFrameworkSuffix)' != ''">
+ <OverridePackageId>$(SharedFrameworkName).Runtime.$(RuntimeSpecificFrameworkSuffix).$(RuntimeIdentifier)</OverridePackageId>
+ </PropertyGroup>
+
+ <!--
+ hostpolicy and hostfxr aren't in the platform manifest in the ref pack and cannot be without breaking things upstack.
+ We add the entries here to ensure that we don't fail the validation that every file included in the runtime pack is in the platform manifest
+ without adding the entries to the manifest in the ref pack.
+ -->
+ <ItemGroup>
+ <PlatformManifestFileEntry Include="hostpolicy.dll" IsNative="true" />
+ <PlatformManifestFileEntry Include="libhostpolicy.so" IsNative="true" />
+ <PlatformManifestFileEntry Include="libhostpolicy.dylib" IsNative="true" />
+ <PlatformManifestFileEntry Include="hostfxr.dll" IsNative="true" />
+ <PlatformManifestFileEntry Include="libhostfxr.so" IsNative="true" />
+ <PlatformManifestFileEntry Include="libhostfxr.dylib" IsNative="true" />
+ </ItemGroup>
+
+ <Target Name="AddLinuxPackageInformation" BeforeTargets="GetDebInstallerJsonProperties;GetRpmInstallerJsonProperties">
+ <ItemGroup>
+ <LinuxPackageDependency Include="dotnet-hostfxr-$(MajorVersion).$(MinorVersion);dotnet-runtime-deps-$(MajorVersion).$(MinorVersion)" Version="$(InstallerPackageVersion)" />
+ </ItemGroup>
+ </Target>
+
+ <!-- Mobile uses a different hosting model, so we don't include the .NET host components. -->
+ <ItemGroup Condition="'$(TargetsMobile)' != 'true'">
+ <NativeRuntimeAsset Include="$(DotNetHostBinDir)/$(LibPrefix)hostpolicy$(LibSuffix)" />
+ <NativeRuntimeAsset Include="$(DotNetHostBinDir)/$(LibPrefix)hostfxr$(LibSuffix)" PackOnly="true" />
+ </ItemGroup>
+
+ <Target Name="AddRuntimeFilesToPackage" DependsOnTargets="ResolveRuntimeFilesFromLocalBuild">
+ <ItemGroup>
+ <RuntimeFiles Condition="'%(RuntimeFiles.IsNative)' == 'true'">
+ <TargetPath>runtimes/$(RuntimeIdentifier)/native</TargetPath>
+ </RuntimeFiles>
+
+ <RuntimeFiles Condition="'%(FileName)' == 'crossgen'">
+ <TargetPath>tools</TargetPath>
+ </RuntimeFiles>
+
+ <RuntimeFiles Condition="'$(TargetsMobile)' == 'true'"
+ Include="@(MonoIncludeFiles)"
+ ExcludeFromDataFiles="true">
+ <TargetPath>runtimes/$(RuntimeIdentifier)/native/include/%(RecursiveDir)</TargetPath>
+ </RuntimeFiles>
+
+ <CoreCLRCrossTargetFiles PackOnly="true" />
+ <CoreCLRCrossTargetFiles Condition="'%(FileName)' == 'clrjit' or '%(FileName)' == 'libclrjit'">
+ <TargetPath>runtimes/$(CoreCLRCrossTargetComponentDirName)_$(TargetArchitecture)/native</TargetPath>
+ </CoreCLRCrossTargetFiles>
+ <CoreCLRCrossTargetFiles Condition="'%(FileName)' == 'crossgen'">
+ <TargetPath>tools/$(CoreCLRCrossTargetComponentDirName)_$(TargetArchitecture)</TargetPath>
+ </CoreCLRCrossTargetFiles>
+ <CoreCLRCrossTargetFiles Condition="$([System.String]::new('%(FileName)').StartsWith('mscordaccore')) and '$(TargetsWindows)' == 'true'">
+ <TargetPath>tools/$(CoreCLRCrossTargetComponentDirName)_$(TargetArchitecture)</TargetPath>
+ </CoreCLRCrossTargetFiles>
+ <CoreCLRCrossTargetFiles Condition="'%(FileName)%(Extension)' == 'mscordbi.dll' and '$(TargetsWindows)' == 'true'">
+ <TargetPath>tools/$(CoreCLRCrossTargetComponentDirName)_$(TargetArchitecture)</TargetPath>
+ </CoreCLRCrossTargetFiles>
+ <CoreCLROptimizationFiles Include="$(CoreCLRArtifactsPath)StandardOptimizationData.mibc"
+ Condition="Exists('$(CoreCLRArtifactsPath)StandardOptimizationData.mibc')">
+ <TargetPath>tools</TargetPath>
+ </CoreCLROptimizationFiles>
+ <ReferenceCopyLocalPaths Include="@(RuntimeFiles);@(CoreCLRCrossTargetFiles);@(CoreCLROptimizationFiles)" />
+ </ItemGroup>
+ </Target>
+
+ <Target Name="AddFrameworkFilesToPackage" DependsOnTargets="ResolveLibrariesFromLocalBuild">
+ <ItemGroup>
+ <ReferenceCopyLocalPaths Include="@(LibrariesRuntimeFiles)"
+ Condition="'%(LibrariesRuntimeFiles.Extension)' != '.a' or '$(TargetsMobile)' == 'true'">
+ <TargetPath Condition="'%(LibrariesRuntimeFiles.NativeSubDirectory)' != ''">runtimes/$(RuntimeIdentifier)/native/%(LibrariesRuntimeFiles.NativeSubDirectory)%(RecursiveDir)</TargetPath>
+ </ReferenceCopyLocalPaths>
+ </ItemGroup>
+ </Target>
+
+ <PropertyGroup Condition="'$(TargetOS)' == 'windows'">
+ <!-- DiaSymReader for the host architecture, which is used for [cross-]compilation -->
+ <_diaSymArch>$(_hostArch)</_diaSymArch>
+ <_diaSymReaderPath>$(PkgMicrosoft_DiaSymReader_Native)/runtimes/win/native/Microsoft.DiaSymReader.Native.$(_diaSymArch).dll</_diaSymReaderPath>
+
+ <!-- DiaSymReader for the target architecture, which is placed into the package -->
+ <_diaSymTargetArch>$(TargetArchitecture)</_diaSymTargetArch>
+ <_diaSymTargetArch Condition="'$(TargetArchitecture)' == 'x64'">amd64</_diaSymTargetArch>
+ <_diaSymReaderTargetArchPath>$(PkgMicrosoft_DiaSymReader_Native)/runtimes/win/native/Microsoft.DiaSymReader.Native.$(_diaSymTargetArch).dll</_diaSymReaderTargetArchPath>
+ </PropertyGroup>
+
+ <ItemGroup Condition="'$(TargetOS)' == 'windows'">
+ <NativeRuntimeAsset Include="$(_diaSymReaderTargetArchPath)" />
+ <NativeRuntimeAsset Include="$(_diaSymReaderPath)" Condition="'$(CoreCLRCrossTargetComponentDirName)' != ''">
+ <TargetPath>runtimes/$(CoreCLRCrossTargetComponentDirName)_$(TargetArchitecture)/native</TargetPath>
+ </NativeRuntimeAsset>
+ </ItemGroup>
+
+ <!-- VS uses this file to show the target framework in the drop down. -->
+ <Target Name="CreateDotVersionFile"
+ DependsOnTargets="InitializeSourceControlInformationFromSourceControlManager"
+ BeforeTargets="GetFilesToPublish"
+ Condition="'$(DisableSourceLink)' != 'true'">
+ <ItemGroup>
+ <_VersionFile Include="$(IntermediateOutputPath).version" TargetPath="shared/$(SharedFrameworkName)/$(Version)/" />
+ </ItemGroup>
+ <WriteLinesToFile
+ Lines="$(SourceRevisionId);$(Version)"
+ File="@(_VersionFile)"
+ Overwrite="true"
+ WriteOnlyWhenDifferent="true" />
+ <ItemGroup>
+ <FilesToPublish Include="@(_VersionFile)" />
+ <FileWrites Include="@(_VersionFile)" />
+ </ItemGroup>
+ </Target>
+
+ <Import Project="$(Crossgen2SdkOverridePropsPath)" />
+ <Import Project="Sdk.targets" Sdk="Microsoft.NET.Sdk" />
+ <Import Project="Sdk.targets" Sdk="Microsoft.DotNet.SharedFramework.Sdk" />
+ <Import Project="$(Crossgen2SdkOverrideTargetsPath)" />
+ <PropertyGroup>
+ <PublishReadyToRunComposite Condition="$(ForcePublishReadyToRunComposite) == 'true'">true</PublishReadyToRunComposite>
+ </PropertyGroup>
+</Project>
<SymbolsArchiveName>dotnet-runtime-symbols</SymbolsArchiveName>
<VSInsertionShortComponentName>NetCore.SharedFramework</VSInsertionShortComponentName>
<UseTemplatedPlatformManifest>true</UseTemplatedPlatformManifest>
- <IncludeFallbacksInDepsFile>true</IncludeFallbacksInDepsFile>
- <GetSharedFrameworkFilesForReadyToRunDependsOn>
- AddRuntimeFilesToPackage;
- AddFrameworkFilesToPackage
- </GetSharedFrameworkFilesForReadyToRunDependsOn>
- <PublishReadyToRun Condition="'$(RuntimeFlavor)' != 'Mono'">true</PublishReadyToRun>
- <PublishReadyToRun Condition="'$(RuntimeFlavor)' == 'Mono'">false</PublishReadyToRun>
- <!-- Disable crossgen on FreeBSD, NetBSD, illumos and Solaris for now. This can be revisited when we have full support. -->
- <PublishReadyToRun Condition="'$(TargetOS)'=='FreeBSD' Or '$(TargetOS)'=='NetBSD' Or '$(TargetOS)'=='illumos' Or '$(TargetOS)'=='Solaris'">false</PublishReadyToRun>
- <!-- These components are installed by the root shared framework, but not others. -->
- <IncludeWerRelatedKeys>true</IncludeWerRelatedKeys>
- <IncludeBreadcrumbStoreFolder>true</IncludeBreadcrumbStoreFolder>
- <MacOSPackageDescription>The .NET Shared Framework</MacOSPackageDescription>
</PropertyGroup>
- <PropertyGroup Condition="'$(RuntimeFlavor)' == 'Mono'">
- <RuntimeSpecificFrameworkSuffix>Mono</RuntimeSpecificFrameworkSuffix>
- </PropertyGroup>
- <PropertyGroup Condition="'$(MonoEnableLLVM)' == 'true' and '$(RuntimeFlavor)' == 'Mono' and '$(TargetsMobile)' != 'true' and '$(TargetsBrowser)' != 'true'">
- <RuntimeSpecificFrameworkSuffix>Mono.LLVM</RuntimeSpecificFrameworkSuffix>
- </PropertyGroup>
- <PropertyGroup Condition="'$(MonoBundleLLVMOptimizer)' == 'true' and '$(RuntimeFlavor)' == 'Mono' and '$(TargetsMobile)' != 'true' and '$(TargetsBrowser)' != 'true'">
- <RuntimeSpecificFrameworkSuffix>Mono.LLVM.AOT</RuntimeSpecificFrameworkSuffix>
- </PropertyGroup>
- <PropertyGroup Condition="'$(RuntimeSpecificFrameworkSuffix)' != ''">
- <OverridePackageId>$(SharedFrameworkName).Runtime.$(RuntimeSpecificFrameworkSuffix).$(RuntimeIdentifier)</OverridePackageId>
- </PropertyGroup>
-
- <!--
- hostpolicy and hostfxr aren't in the platform manifest in the ref pack and cannot be without breaking things upstack.
- We add the entries here to ensure that we don't fail the validation that every file included in the runtime pack is in the platform manifest
- without adding the entries to the manifest in the ref pack.
- -->
- <ItemGroup>
- <PlatformManifestFileEntry Include="hostpolicy.dll" IsNative="true" />
- <PlatformManifestFileEntry Include="libhostpolicy.so" IsNative="true" />
- <PlatformManifestFileEntry Include="libhostpolicy.dylib" IsNative="true" />
- <PlatformManifestFileEntry Include="hostfxr.dll" IsNative="true" />
- <PlatformManifestFileEntry Include="libhostfxr.so" IsNative="true" />
- <PlatformManifestFileEntry Include="libhostfxr.dylib" IsNative="true" />
- </ItemGroup>
-
- <Target Name="AddLinuxPackageInformation" BeforeTargets="GetDebInstallerJsonProperties;GetRpmInstallerJsonProperties">
- <ItemGroup>
- <LinuxPackageDependency Include="dotnet-hostfxr-$(MajorVersion).$(MinorVersion);dotnet-runtime-deps-$(MajorVersion).$(MinorVersion)" Version="$(InstallerPackageVersion)" />
- </ItemGroup>
- </Target>
-
- <!-- Mobile uses a different hosting model, so we don't include the .NET host components. -->
- <ItemGroup Condition="'$(TargetsMobile)' != 'true'">
- <NativeRuntimeAsset Include="$(DotNetHostBinDir)/$(LibPrefix)hostpolicy$(LibSuffix)" />
- <NativeRuntimeAsset Include="$(DotNetHostBinDir)/$(LibPrefix)hostfxr$(LibSuffix)" PackOnly="true" />
- </ItemGroup>
-
- <Target Name="AddRuntimeFilesToPackage" DependsOnTargets="ResolveRuntimeFilesFromLocalBuild">
- <ItemGroup>
- <RuntimeFiles Condition="'%(RuntimeFiles.IsNative)' == 'true'">
- <TargetPath>runtimes/$(RuntimeIdentifier)/native</TargetPath>
- </RuntimeFiles>
-
- <RuntimeFiles Condition="'%(FileName)' == 'crossgen'">
- <TargetPath>tools</TargetPath>
- </RuntimeFiles>
-
- <RuntimeFiles Condition="'$(TargetsMobile)' == 'true'"
- Include="@(MonoIncludeFiles)"
- ExcludeFromDataFiles="true">
- <TargetPath>runtimes/$(RuntimeIdentifier)/native/include/%(RecursiveDir)</TargetPath>
- </RuntimeFiles>
-
- <CoreCLRCrossTargetFiles PackOnly="true" />
- <CoreCLRCrossTargetFiles Condition="'%(FileName)' == 'clrjit' or '%(FileName)' == 'libclrjit'">
- <TargetPath>runtimes/$(CoreCLRCrossTargetComponentDirName)_$(TargetArchitecture)/native</TargetPath>
- </CoreCLRCrossTargetFiles>
- <CoreCLRCrossTargetFiles Condition="'%(FileName)' == 'crossgen'">
- <TargetPath>tools/$(CoreCLRCrossTargetComponentDirName)_$(TargetArchitecture)</TargetPath>
- </CoreCLRCrossTargetFiles>
- <CoreCLRCrossTargetFiles Condition="$([System.String]::new('%(FileName)').StartsWith('mscordaccore')) and '$(TargetsWindows)' == 'true'">
- <TargetPath>tools/$(CoreCLRCrossTargetComponentDirName)_$(TargetArchitecture)</TargetPath>
- </CoreCLRCrossTargetFiles>
- <CoreCLRCrossTargetFiles Condition="'%(FileName)%(Extension)' == 'mscordbi.dll' and '$(TargetsWindows)' == 'true'">
- <TargetPath>tools/$(CoreCLRCrossTargetComponentDirName)_$(TargetArchitecture)</TargetPath>
- </CoreCLRCrossTargetFiles>
- <CoreCLROptimizationFiles Include="$(CoreCLRArtifactsPath)StandardOptimizationData.mibc"
- Condition="Exists('$(CoreCLRArtifactsPath)StandardOptimizationData.mibc')">
- <TargetPath>tools</TargetPath>
- </CoreCLROptimizationFiles>
- <ReferenceCopyLocalPaths Include="@(RuntimeFiles);@(CoreCLRCrossTargetFiles);@(CoreCLROptimizationFiles)" />
- </ItemGroup>
- </Target>
-
- <Target Name="AddFrameworkFilesToPackage" DependsOnTargets="ResolveLibrariesFromLocalBuild">
- <ItemGroup>
- <ReferenceCopyLocalPaths Include="@(LibrariesRuntimeFiles)"
- Condition="'%(LibrariesRuntimeFiles.Extension)' != '.a' or '$(TargetsMobile)' == 'true'">
- <TargetPath Condition="'%(LibrariesRuntimeFiles.NativeSubDirectory)' != ''">runtimes/$(RuntimeIdentifier)/native/%(LibrariesRuntimeFiles.NativeSubDirectory)%(RecursiveDir)</TargetPath>
- </ReferenceCopyLocalPaths>
- </ItemGroup>
- </Target>
-
- <PropertyGroup Condition="'$(TargetOS)' == 'windows'">
- <!-- DiaSymReader for the host architecture, which is used for [cross-]compilation -->
- <_diaSymArch>$(_hostArch)</_diaSymArch>
- <_diaSymReaderPath>$(PkgMicrosoft_DiaSymReader_Native)/runtimes/win/native/Microsoft.DiaSymReader.Native.$(_diaSymArch).dll</_diaSymReaderPath>
-
- <!-- DiaSymReader for the target architecture, which is placed into the package -->
- <_diaSymTargetArch>$(TargetArchitecture)</_diaSymTargetArch>
- <_diaSymTargetArch Condition="'$(TargetArchitecture)' == 'x64'">amd64</_diaSymTargetArch>
- <_diaSymReaderTargetArchPath>$(PkgMicrosoft_DiaSymReader_Native)/runtimes/win/native/Microsoft.DiaSymReader.Native.$(_diaSymTargetArch).dll</_diaSymReaderTargetArchPath>
- </PropertyGroup>
-
- <ItemGroup Condition="'$(TargetOS)' == 'windows'">
- <NativeRuntimeAsset Include="$(_diaSymReaderTargetArchPath)" />
- <NativeRuntimeAsset Include="$(_diaSymReaderPath)" Condition="'$(CoreCLRCrossTargetComponentDirName)' != ''">
- <TargetPath>runtimes/$(CoreCLRCrossTargetComponentDirName)_$(TargetArchitecture)/native</TargetPath>
- </NativeRuntimeAsset>
- </ItemGroup>
-
- <!-- VS uses this file to show the target framework in the drop down. -->
- <Target Name="CreateDotVersionFile"
- DependsOnTargets="InitializeSourceControlInformationFromSourceControlManager"
- BeforeTargets="GetFilesToPublish"
- Condition="'$(DisableSourceLink)' != 'true'">
- <ItemGroup>
- <_VersionFile Include="$(IntermediateOutputPath).version" TargetPath="shared/$(SharedFrameworkName)/$(Version)/" />
- </ItemGroup>
- <WriteLinesToFile
- Lines="$(SourceRevisionId);$(Version)"
- File="@(_VersionFile)"
- Overwrite="true"
- WriteOnlyWhenDifferent="true" />
- <ItemGroup>
- <FilesToPublish Include="@(_VersionFile)" />
- <FileWrites Include="@(_VersionFile)" />
- </ItemGroup>
- </Target>
-
- <Import Project="Sdk.targets" Sdk="Microsoft.NET.Sdk" />
- <Import Project="Sdk.targets" Sdk="Microsoft.DotNet.SharedFramework.Sdk" />
+ <Import Project="Microsoft.NETCore.App.Runtime.props" />
<Import Project="ReadyToRun.targets" />
</Project>
--- /dev/null
+<Project Sdk="Microsoft.Build.NoTargets">
+
+ <PropertyGroup>
+ <SkipBuild Condition="'$(RuntimeFlavor)' == 'Mono' or '$(PgoInstrument)' == 'true'">true</SkipBuild>
+ <IsShipping Condition="'$(PgoInstrument)' == 'true'">false</IsShipping>
+ <ArchiveName>dotnet-runtime-composite</ArchiveName>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <PackageReference Include="Microsoft.DotNet.Build.Tasks.Archives" Version="$(MicrosoftDotNetBuildTasksArchivesVersion)" />
+ </ItemGroup>
+
+ <ItemGroup>
+ <BundleComponentReference Include="../Microsoft.NETCore.App/Microsoft.NETCore.App.Runtime.Composite.sfxproj" />
+ <BundleComponentReference Include="../installers/dotnet-hostfxr.proj" />
+ <BundleComponentReference Include="../installers/dotnet-host.proj" />
+ </ItemGroup>
+
+ <Target Name="PublishToDisk">
+ <Error Condition="'$(OutputPath)' == ''" Text="Publishing to disk requires the OutputPath to be set to the root of the path to write to." />
+
+ <MSBuild Projects="@(BundleComponentReference)"
+ Targets="PublishToDisk"
+ Properties="OutputPath=$(OutputPath)" />
+ </Target>
+
+ <Target Name="PublishSymbolsToDisk">
+ <Error Condition="'$(SymbolsOutputPath)' == ''" Text="Publishing to disk requires the SymbolsOutputPath to be set to the root of the path to write to." />
+
+ <MSBuild Projects="@(BundleComponentReference)"
+ Targets="PublishSymbolsToDisk"
+ Properties="SymbolsOutputPath=$(SymbolsOutputPath)" />
+ </Target>
+
+</Project>
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Globalization;
+
+namespace Microsoft.NET.Build.Tasks
+{
+ /// <summary>
+ /// Represents an error that is neither avoidable in all cases nor indicative of a bug in this library.
+ /// It will be logged as a plain build error without the exception type or stack.
+ /// </summary>
+ internal class BuildErrorException : Exception
+ {
+ public BuildErrorException()
+ {
+ }
+
+ public BuildErrorException(string message) : base(message)
+ {
+ }
+
+ public BuildErrorException(string message, Exception innerException) : base(message, innerException)
+ {
+ }
+
+ public BuildErrorException(string format, params string[] args)
+ : this(string.Format(CultureInfo.CurrentCulture, format, args))
+ {
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using Microsoft.Build.Utilities;
+
+namespace Microsoft.NET.Build.Tasks
+{
+ internal sealed class LogAdapter : Logger
+ {
+ private TaskLoggingHelper _taskLogger;
+
+ public LogAdapter(TaskLoggingHelper taskLogger)
+ {
+ _taskLogger = taskLogger;
+ }
+
+ protected override void LogCore(in Message message)
+ {
+ switch (message.Level)
+ {
+ case MessageLevel.Error:
+ _taskLogger.LogError(
+ subcategory: default,
+ errorCode: message.Code,
+ helpKeyword: default,
+ file: message.File,
+ lineNumber: default,
+ columnNumber: default,
+ endLineNumber: default,
+ endColumnNumber: default,
+ message: message.Text);
+ break;
+
+ case MessageLevel.Warning:
+ _taskLogger.LogWarning(
+ subcategory: default,
+ warningCode: message.Code,
+ helpKeyword: default,
+ file: message.File,
+ lineNumber: default,
+ columnNumber: default,
+ endLineNumber: default,
+ endColumnNumber: default,
+ message: message.Text);
+ break;
+
+ case MessageLevel.HighImportance:
+ case MessageLevel.NormalImportance:
+ case MessageLevel.LowImportance:
+ if (message.Code == null && message.File == null)
+ {
+ // use shorter overload when there is no code and no file. Otherwise, msbuild
+ // will display:
+ //
+ // <project file>(<line>,<colunmn>): message : <text>
+ _taskLogger.LogMessage(message.Level.ToImportance(), message.Text);
+ }
+ else
+ {
+ _taskLogger.LogMessage(
+ subcategory: default,
+ code: message.Code,
+ helpKeyword: default,
+ file: message.File,
+ lineNumber: default,
+ columnNumber: default,
+ endLineNumber: default,
+ endColumnNumber: default,
+ importance: message.Level.ToImportance(),
+ message: message.Text);
+ }
+ break;
+
+ default:
+ throw new ArgumentException(
+ $"Message \"{message.Code}: {message.Text}\" logged with invalid Level=${message.Level}",
+ paramName: nameof(message));
+ }
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Diagnostics;
+using Microsoft.Build.Framework;
+using Microsoft.Build.Utilities;
+
+namespace Microsoft.NET.Build.Tasks
+{
+ /// <summary>
+ /// Replacement and abstraction for <see cref="TaskLoggingHelper"/> in our
+ /// build tasks.
+ /// </summary>
+ /// <remarks>
+ /// Source compatible with usual Log.LogXxx MSBuild task code. (Subset of
+ /// API chosen based on actual usage in SDK, and with a deliberate goal of
+ /// eliminating some of the excessive overloading in TaskLoggingHelper.
+ ///
+ /// <see cref="Message"/> replaces the need for overloads taking over 10
+ /// arguments.
+ ///
+ /// Also, string[] is used instead of object[] to avoid issues like passing
+ /// the importance out of order as a format argument.
+ ///
+ /// <see cref="Log"/> allows choosing Error/Warning/Message dynamically at a
+ /// single call site.
+ ///
+ /// Extracts error codes from the message prefix, and enforces that all of
+ /// our messages have a NETSDK code.
+ ///
+ /// Example:
+ /// C#
+ /// Log.LogError(Strings.SomethingIsWrong);
+ ///
+ /// Strings.resx:
+ /// Resource name: SomethingIsWrong
+ /// Resource value: NETSDK1234: Something is wrong.
+ ///
+ /// Results in LogCore getting a Message instance with Code="NETSDK1234"
+ /// and Text="Something is wrong."
+ ///
+ /// Pattern inspired by <se cref="TaskLoggingHelper.LogErrorWithCodeFromResources"/>,
+ /// but retains completion via generated <see cref="Strings"/> instead of
+ /// passing resource keys by name.
+ ///
+ /// All actual logging is deferred to subclass in <see cref="LogCore"/>,
+ /// which allows unit tests to verify task logging while mocking a single
+ /// method. <see cref="TaskBase"/> adapts that to <see
+ /// cref="TaskLoggingHelper"/>.
+ /// </remarks>
+ internal abstract class Logger
+ {
+ public bool HasLoggedErrors { get; private set; }
+
+ public void LogMessage(string format, params string[] args)
+ => Log(CreateMessage(MessageLevel.NormalImportance, format, args));
+
+ public void LogMessage(MessageImportance importance, string format, params string[] args)
+ => Log(CreateMessage(importance.ToLevel(), format, args));
+
+ public void LogWarning(string format, params string[] args)
+ => Log(CreateMessage(MessageLevel.Warning, format, args));
+
+ public void LogError(string format, params string[] args)
+ => Log(CreateMessage(MessageLevel.Error, format, args));
+
+ public void Log(in Message message)
+ {
+ HasLoggedErrors |= message.Level == MessageLevel.Error;
+ LogCore(message);
+ }
+
+ protected abstract void LogCore(in Message message);
+
+ private static Message CreateMessage(MessageLevel level, string format, string[] args)
+ {
+ string code;
+
+ if (format.Length >= 12
+ && format[0] == 'N'
+ && format[1] == 'E'
+ && format[2] == 'T'
+ && format[3] == 'S'
+ && format[4] == 'D'
+ && format[5] == 'K'
+ && IsAsciiDigit(format[6])
+ && IsAsciiDigit(format[7])
+ && IsAsciiDigit(format[8])
+ && IsAsciiDigit(format[9])
+ && format[10] == ':'
+ && format[11] == ' ')
+ {
+ code = format.Substring(0, 10);
+ format = format.Substring(12);
+ }
+ else
+ {
+ code = null;
+ }
+
+ DebugThrowMissingOrIncorrectCode(code, format, level);
+
+ return new Message(
+ level,
+ text: string.Format(format, args),
+ code: code);
+ }
+
+ [Conditional("DEBUG")]
+ private static void DebugThrowMissingOrIncorrectCode(string code, string message, MessageLevel level)
+ {
+ // NB: This is not localized because it represents a bug in our code base, not a user error.
+ // To log message with external codes, use Log.Log(in Message, string[]) directly.
+ // It is not a Debug.Assert because it doesn't render well in unit tests.
+
+ switch (level)
+ {
+ case MessageLevel.Error:
+ case MessageLevel.Warning:
+ if (code == null)
+ {
+ throw new ArgumentException(
+ "Message is not prefixed with NETSDK error code or error code is formatted incorrectly: "
+ + message);
+ }
+ break;
+
+ default:
+ if (code != null)
+ {
+ throw new ArgumentException(
+ "Message is prefixed with NETSDK error, but error codes should not be used for informational messages: "
+ + $"{code}:{message}");
+ }
+ break;
+ }
+ }
+
+ private static bool IsAsciiDigit(char c)
+ => c >= '0' && c <= '9';
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace Microsoft.NET.Build.Tasks
+{
+ internal readonly struct Message
+ {
+ public readonly MessageLevel Level;
+ public readonly string Code;
+ public readonly string Text;
+ public readonly string File;
+
+ public Message(
+ MessageLevel level,
+ string text,
+ string code = default,
+ string file = default)
+ {
+ Level = level;
+ Code = code;
+ Text = text;
+ File = file;
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using Microsoft.Build.Framework;
+
+namespace Microsoft.NET.Build.Tasks
+{
+ internal enum MessageLevel
+ {
+ // For efficient conversion, positive values map directly to MessageImportance:
+ LowImportance = MessageImportance.Low,
+ NormalImportance = MessageImportance.Normal,
+ HighImportance = MessageImportance.High,
+
+ // And negative values are for levels that are not informational (warning/error):
+ Warning = -1,
+ Error = -2,
+ }
+
+ internal static class MessageLevelExtensions
+ {
+ public static MessageLevel ToLevel(this MessageImportance importance)
+ => (MessageLevel)(importance);
+
+ public static MessageImportance ToImportance(this MessageLevel level)
+ => level >= 0 ? (MessageImportance)level : throw new InvalidCastException();
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace Microsoft.NET.Build.Tasks
+{
+ internal static class MetadataKeys
+ {
+ // General Metadata
+ public const string Name = "Name";
+ public const string Type = "Type";
+ public const string Version = "Version";
+ public const string FileGroup = "FileGroup";
+ public const string Path = "Path";
+ public const string ResolvedPath = "ResolvedPath";
+ public const string IsImplicitlyDefined = "IsImplicitlyDefined";
+ public const string IsTopLevelDependency = "IsTopLevelDependency";
+ public const string AllowExplicitVersion = "AllowExplicitVersion";
+ public const string RelativePath = "RelativePath";
+ public const string DiagnosticLevel = "DiagnosticLevel";
+
+ // Target Metadata
+ public const string RuntimeIdentifier = "RuntimeIdentifier";
+ public const string TargetFrameworkMoniker = "TargetFrameworkMoniker";
+ public const string TargetFramework = "TargetFramework";
+ public const string FrameworkName = "FrameworkName";
+ public const string FrameworkVersion = "FrameworkVersion";
+ public const string IsTrimmable = "IsTrimmable";
+ public const string RuntimeFrameworkName = "RuntimeFrameworkName";
+ public const string RuntimePackRuntimeIdentifiers = "RuntimePackRuntimeIdentifiers";
+
+ // SDK Metadata
+ public const string SDKPackageItemSpec = "SDKPackageItemSpec";
+ public const string OriginalItemSpec = "OriginalItemSpec";
+ public const string SDKRootFolder = "SDKRootFolder";
+ public const string ShimRuntimeIdentifier = "ShimRuntimeIdentifier";
+ public const string RuntimePackAlwaysCopyLocal = "RuntimePackAlwaysCopyLocal";
+
+ // Foreign Keys
+ public const string ParentTarget = "ParentTarget";
+ public const string ParentTargetLibrary = "ParentTargetLibrary";
+ public const string ParentPackage = "ParentPackage";
+
+ // Tags
+ public const string Analyzer = "Analyzer";
+ public const string AnalyzerLanguage = "AnalyzerLanguage";
+ public const string TransitiveProjectReference = "TransitiveProjectReference";
+
+ // Diagnostics
+ public const string DiagnosticCode = "DiagnosticCode";
+ public const string Message = "Message";
+ public const string FilePath = "FilePath";
+ public const string Severity = "Severity";
+ public const string StartLine = "StartLine";
+ public const string StartColumn = "StartColumn";
+ public const string EndLine = "EndLine";
+ public const string EndColumn = "EndColumn";
+
+ // Publish Target Manifest
+ public const string RuntimeStoreManifestNames = "RuntimeStoreManifestNames";
+
+ // Conflict Resolution
+ public const string OverriddenPackages = "OverriddenPackages";
+
+ // Package assets
+ public const string NuGetIsFrameworkReference = "NuGetIsFrameworkReference";
+ public const string NuGetPackageId = "NuGetPackageId";
+ public const string NuGetPackageVersion = "NuGetPackageVersion";
+ public const string NuGetSourceType = "NuGetSourceType";
+ public const string PathInPackage = "PathInPackage";
+ public const string PackageDirectory = "PackageDirectory";
+ public const string Publish = "Publish";
+
+ // References
+ public const string ExternallyResolved = "ExternallyResolved";
+ public const string HintPath = "HintPath";
+ public const string MSBuildSourceProjectFile = "MSBuildSourceProjectFile";
+ public const string Private = "Private";
+ public const string Pack = "Pack";
+ public const string ReferenceSourceTarget = "ReferenceSourceTarget";
+ public const string TargetPath = "TargetPath";
+ public const string CopyLocal = "CopyLocal";
+
+ // Targeting packs
+ public const string PackageConflictPreferredPackages = "PackageConflictPreferredPackages";
+
+ // Runtime packs
+ public const string DropFromSingleFile = "DropFromSingleFile";
+ public const string RuntimePackLabels = "RuntimePackLabels";
+ public const string AdditionalFrameworkReferences = "AdditionalFrameworkReferences";
+
+ // Content files
+ public const string PPOutputPath = "PPOutputPath";
+ public const string CodeLanguage = "CodeLanguage";
+ public const string CopyToOutput = "CopyToOutput";
+ public const string BuildAction = "BuildAction";
+ public const string OutputPath = "OutputPath";
+ public const string CopyToPublishDirectory = "CopyToPublishDirectory";
+ public const string ExcludeFromSingleFile = "ExcludeFromSingleFile";
+
+ // Resource assemblies
+ public const string Culture = "Culture";
+ // The DestinationSubDirectory is the directory containing the asset, relative to the destination folder.
+ public const string DestinationSubDirectory = "DestinationSubDirectory";
+
+ // Copy local assets
+ // The DestinationSubPath is the path to the asset, relative to the destination folder.
+ public const string DestinationSubPath = "DestinationSubPath";
+ public const string AssetType = "AssetType";
+
+ public const string ReferenceOnly = "ReferenceOnly";
+
+ public const string Aliases = "Aliases";
+
+ // ReadyToRun
+ public const string DotNetHostPath = "DotNetHostPath";
+ public const string JitPath = "JitPath";
+ public const string TargetOS = "TargetOS";
+ public const string TargetArch = "TargetArch";
+ public const string DiaSymReader = "DiaSymReader";
+ public const string CreatePDBCommand = "CreatePDBCommand";
+ public const string OutputR2RImage = "OutputR2RImage";
+ public const string OutputPDBImage = "OutputPDBImage";
+ public const string EmitSymbols = "EmitSymbols";
+ public const string IsVersion5 = "IsVersion5";
+ public const string CreateCompositeImage = "CreateCompositeImage";
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using NuGet.Frameworks;
+using NuGet.Packaging.Core;
+using NuGet.ProjectModel;
+using NuGet.RuntimeModel;
+
+namespace Microsoft.NET.Build.Tasks
+{
+ internal static partial class NuGetUtils
+ {
+ public static bool IsPlaceholderFile(string path)
+ {
+ // PERF: avoid allocations here as we check this for every file in project.assets.json
+ if (!path.EndsWith("_._", StringComparison.Ordinal))
+ {
+ return false;
+ }
+
+ if (path.Length == 3)
+ {
+ return true;
+ }
+
+ char separator = path[path.Length - 4];
+ return separator == '\\' || separator == '/';
+ }
+
+ public static string GetLockFileLanguageName(string projectLanguage)
+ {
+ switch (projectLanguage)
+ {
+ case "C#": return "cs";
+ case "F#": return "fs";
+ default: return projectLanguage?.ToLowerInvariant();
+ }
+ }
+
+ public static NuGetFramework ParseFrameworkName(string frameworkName)
+ {
+ return frameworkName == null ? null : NuGetFramework.Parse(frameworkName);
+ }
+
+ public static bool IsApplicableAnalyzer(string file, string projectLanguage)
+ {
+ // This logic is preserved from previous implementations.
+ // See https://github.com/NuGet/Home/issues/6279#issuecomment-353696160 for possible issues with it.
+
+ bool IsAnalyzer()
+ {
+ return file.StartsWith("analyzers", StringComparison.Ordinal)
+ && file.EndsWith(".dll", StringComparison.OrdinalIgnoreCase)
+ && !file.EndsWith(".resources.dll", StringComparison.OrdinalIgnoreCase);
+ }
+
+ bool CS() => file.Contains("/cs/", StringComparison.OrdinalIgnoreCase);
+ bool VB() => file.Contains("/vb/", StringComparison.OrdinalIgnoreCase);
+
+ bool FileMatchesProjectLanguage()
+ {
+ switch (projectLanguage)
+ {
+ case "C#":
+ return CS() || !VB();
+
+ case "VB":
+ return VB() || !CS();
+
+ default:
+ return false;
+ }
+ }
+
+ return IsAnalyzer() && FileMatchesProjectLanguage();
+ }
+
+ public static string GetBestMatchingRid(RuntimeGraph runtimeGraph, string runtimeIdentifier,
+ IEnumerable<string> availableRuntimeIdentifiers, out bool wasInGraph)
+ {
+ wasInGraph = runtimeGraph.Runtimes.ContainsKey(runtimeIdentifier);
+
+ HashSet<string> availableRids = new HashSet<string>(availableRuntimeIdentifiers);
+ foreach (var candidateRuntimeIdentifier in runtimeGraph.ExpandRuntime(runtimeIdentifier))
+ {
+ if (availableRids.Contains(candidateRuntimeIdentifier))
+ {
+ return candidateRuntimeIdentifier;
+ }
+ }
+
+ // No compatible RID found in availableRuntimeIdentifiers
+ return null;
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+using Microsoft.Build.Framework;
+using NuGet.RuntimeModel;
+
+namespace Microsoft.NET.Build.Tasks
+{
+ internal class RuntimeGraphCache
+ {
+ private IBuildEngine4 _buildEngine;
+ private Logger _log;
+
+ public RuntimeGraphCache(TaskBase task)
+ {
+ _buildEngine = task.BuildEngine4;
+ _log = task.Log;
+ }
+
+ public RuntimeGraph GetRuntimeGraph(string runtimeJsonPath)
+ {
+ if (string.IsNullOrEmpty(runtimeJsonPath))
+ {
+ throw new ArgumentNullException(nameof(runtimeJsonPath));
+ }
+ if (!Path.IsPathRooted(runtimeJsonPath))
+ {
+ throw new BuildErrorException("Path not rooted: {0}", runtimeJsonPath);
+ }
+
+ string key = GetTaskObjectKey(runtimeJsonPath);
+
+ RuntimeGraph result;
+ object existingRuntimeGraphTaskObject = _buildEngine.GetRegisteredTaskObject(key, RegisteredTaskObjectLifetime.AppDomain);
+ if (existingRuntimeGraphTaskObject == null)
+ {
+ result = JsonRuntimeFormat.ReadRuntimeGraph(runtimeJsonPath);
+
+ _buildEngine.RegisterTaskObject(key, result, RegisteredTaskObjectLifetime.AppDomain, true);
+ }
+ else
+ {
+ result = (RuntimeGraph)existingRuntimeGraphTaskObject;
+ }
+
+ return result;
+ }
+
+ private static string GetTaskObjectKey(string runtimeJsonPath)
+ {
+ return $"{nameof(RuntimeGraphCache)}:{runtimeJsonPath}";
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using Microsoft.Build.Utilities;
+using Microsoft.Build.Framework;
+using System.Collections.Generic;
+using System.Globalization;
+
+namespace Microsoft.NET.Build.Tasks
+{
+ public abstract class TaskBase : Task
+ {
+ private Logger _logger;
+
+ internal TaskBase(Logger logger = null)
+ {
+ _logger = logger;
+ }
+
+ internal new Logger Log
+ {
+ get
+ {
+ if (_logger == null)
+ {
+ _logger = new LogAdapter(base.Log);
+ }
+
+ return _logger;
+ }
+ }
+
+ public override bool Execute()
+ {
+ try
+ {
+ ExecuteCore();
+ }
+ catch (BuildErrorException e)
+ {
+ Log.LogError(e.Message);
+ }
+ catch (Exception e)
+ {
+ LogErrorTelemetry("taskBaseCatchException", e);
+ throw;
+ }
+
+ return !Log.HasLoggedErrors;
+ }
+
+ private void LogErrorTelemetry(string eventName, Exception e)
+ {
+ (BuildEngine as IBuildEngine5)?.LogTelemetry(eventName, new Dictionary<string, string> {
+ {"exceptionType", e.GetType().ToString() },
+ {"detail", ExceptionToStringWithoutMessage(e) }});
+ }
+
+ private static string ExceptionToStringWithoutMessage(Exception e)
+ {
+ const string AggregateException_ToString = "{0}{1}---> (Inner Exception #{2}) {3}{4}{5}";
+ if (e is AggregateException aggregate)
+ {
+ string text = NonAggregateExceptionToStringWithoutMessage(aggregate);
+
+ for (int i = 0; i < aggregate.InnerExceptions.Count; i++)
+ {
+ text = string.Format(CultureInfo.InvariantCulture,
+ AggregateException_ToString,
+ text,
+ Environment.NewLine,
+ i,
+ ExceptionToStringWithoutMessage(aggregate.InnerExceptions[i]),
+ "<---",
+ Environment.NewLine);
+ }
+
+ return text;
+ }
+ else
+ {
+ return NonAggregateExceptionToStringWithoutMessage(e);
+ }
+ }
+
+ private static string NonAggregateExceptionToStringWithoutMessage(Exception e)
+ {
+ string s;
+ const string Exception_EndOfInnerExceptionStack = "--- End of inner exception stack trace ---";
+
+
+ s = e.GetType().ToString();
+
+ if (e.InnerException != null)
+ {
+ s = s + " ---> " + ExceptionToStringWithoutMessage(e.InnerException) + Environment.NewLine +
+ " " + Exception_EndOfInnerExceptionStack;
+
+ }
+
+ var stackTrace = e.StackTrace;
+
+ if (stackTrace != null)
+ {
+ s += Environment.NewLine + stackTrace;
+ }
+
+ return s;
+ }
+
+ protected abstract void ExecuteCore();
+ }
+}
--- /dev/null
+<Project Sdk="Microsoft.NET.Sdk">
+ <PropertyGroup>
+ <TargetFrameworks>$(NetCoreAppToolCurrent)</TargetFrameworks>
+ <OutputType>Library</OutputType>
+ <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
+ <NoWarn>$(NoWarn),CA1050</NoWarn>
+
+ <!-- Ignore nullable warnings on net4* -->
+ <NoWarn Condition="$(TargetFramework.StartsWith('net4'))">$(NoWarn),CS8604,CS8602</NoWarn>
+ </PropertyGroup>
+ <ItemGroup>
+ <PackageReference Include="Microsoft.Build" Version="$(RefOnlyMicrosoftBuildVersion)" />
+ <PackageReference Include="Microsoft.Build.Framework" Version="$(RefOnlyMicrosoftBuildFrameworkVersion)" />
+ <PackageReference Include="Microsoft.Build.Tasks.Core" Version="$(RefOnlyMicrosoftBuildTasksCoreVersion)" />
+ <PackageReference Include="Microsoft.Build.Utilities.Core" Version="$(RefOnlyMicrosoftBuildUtilitiesCoreVersion)" />
+ <PackageReference Include="NuGet.ProjectModel" Version="$(RefOnlyNugetProjectModelVersion)" />
+ </ItemGroup>
+ <ItemGroup>
+ <Content Include="Microsoft.NET.CrossGen.targets">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
+ <Content Include="ShimFilesSimulatingLogicInSdkRepo/Microsoft.NET.CrossGen.props">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ <Link>Microsoft.NET.CrossGen.props</Link>
+ </Content>
+ </ItemGroup>
+</Project>
--- /dev/null
+<!--
+***********************************************************************************************
+Microsoft.NET.CrossGen.targets
+
+WARNING: DO NOT MODIFY this file unless you are knowledgeable about MSBuild and have
+ created a backup copy. Incorrect changes to this file will make it
+ impossible to load or build your projects from the command-line or the IDE.
+
+Copyright (c) .NET Foundation. All rights reserved.
+***********************************************************************************************
+-->
+<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+
+ <PropertyGroup Condition="'$(PublishReadyToRun)' == 'true' and '$(TargetFrameworkIdentifier)' == '.NETCoreApp' and '$(_TargetFrameworkVersionWithoutV)' >= '3.0'">
+ <!-- For .NET 6 and higher, default to using Crossgen2 in non-composite mode -->
+ <PublishReadyToRunUseCrossgen2 Condition="'$(PublishReadyToRunUseCrossgen2)' == '' and '$(_TargetFrameworkVersionWithoutV)' >= '6.0'">true</PublishReadyToRunUseCrossgen2>
+ <PublishReadyToRunComposite Condition="'$(PublishReadyToRunComposite)' == '' and '$(_TargetFrameworkVersionWithoutV)' >= '6.0'">false</PublishReadyToRunComposite>
+ <PublishReadyToRunComposite Condition="'$(PublishReadyToRunComposite)' == ''">true</PublishReadyToRunComposite>
+ <PublishReadyToRunComposite Condition="'$(PublishReadyToRunUseCrossgen2)' != 'true' or '$(SelfContained)' != 'true'">false</PublishReadyToRunComposite>
+ </PropertyGroup>
+
+ <!--
+ ============================================================
+ PrepOptimizer
+
+ Sets up the common infrastructure for the optimization phase
+ Outputs:
+ JitPath
+ Crossgen
+ ============================================================
+ -->
+
+ <Target Name="PrepOptimizer"
+ DependsOnTargets="_RestoreCrossgen;"
+ Condition="$(SkipOptimization) != 'true' ">
+ <!-- Get the coreclr path -->
+ <ItemGroup>
+ <_CoreclrResolvedPath Include="@(CrossgenResolvedAssembliesToPublish)"
+ Condition="'%(CrossgenResolvedAssembliesToPublish.Filename)'=='coreclr'" />
+ <_CoreclrResolvedPath Include="@(CrossgenResolvedAssembliesToPublish)"
+ Condition="'%(CrossgenResolvedAssembliesToPublish.Filename)'=='libcoreclr'" />
+ <_JitResolvedPath Include="@(CrossgenResolvedAssembliesToPublish)"
+ Condition="'%(CrossgenResolvedAssembliesToPublish.Filename)'=='clrjit'" />
+ <_JitResolvedPath Include="@(CrossgenResolvedAssembliesToPublish)"
+ Condition="'%(CrossgenResolvedAssembliesToPublish.Filename)'=='libclrjit'" />
+ </ItemGroup>
+
+ <NETSdkError Condition="'@(_CoreclrResolvedPath->Count())' > 1"
+ ResourceName="MultipleFilesResolved"
+ FormatArguments="coreclr" />
+
+ <NETSdkError Condition="'@(_CoreclrResolvedPath)'== ''"
+ ResourceName="UnableToFindResolvedPath"
+ FormatArguments="coreclr" />
+
+ <NETSdkError Condition="'@(_JitResolvedPath->Count())' > 1"
+ ResourceName="MultipleFilesResolved"
+ FormatArguments="jit" />
+
+ <NETSdkError Condition="'@(_JitResolvedPath)'== ''"
+ ResourceName="UnableToFindResolvedPath"
+ FormatArguments="jit" />
+
+ <!-- Get the crossgen and jit path-->
+ <PropertyGroup>
+ <_CoreclrPath>@(_CoreclrResolvedPath)</_CoreclrPath>
+ <JitPath>@(_JitResolvedPath)</JitPath>
+ <_CoreclrDir>$([System.IO.Path]::GetDirectoryName($(_CoreclrPath)))</_CoreclrDir>
+ <_CoreclrPkgDir>$([System.IO.Path]::Combine($(_CoreclrDir),"..\..\..\"))</_CoreclrPkgDir>
+ <CrossgenDir>$([System.IO.Path]::Combine($(_CoreclrPkgDir),"tools"))</CrossgenDir>
+ <!-- TODO override with rid specific tools path for x-arch -->
+ <Crossgen>$([System.IO.Path]::Combine($(CrossgenDir),"crossgen"))</Crossgen>
+ <Crossgen Condition="$([MSBuild]::IsOSPlatform(`Windows`))">$([System.IO.Path]::Combine($(CrossgenDir),"crossgen.exe"))</Crossgen>
+ </PropertyGroup>
+
+ <NETSdkError Condition="!Exists($(Crossgen))"
+ ResourceName="UnableToFindResolvedPath"
+ FormatArguments="$(Crossgen)" />
+
+ <!-- Copy crossgen into the netcoreapp folder to ensure it can load Microsoft.DiaSymReader.Native when creating PDBs -->
+ <Copy SourceFiles="$(Crossgen)"
+ DestinationFolder="$(_NetCoreRefDir)"
+ OverwriteReadOnlyFiles="$(OverwriteReadOnlyFiles)"
+ Retries="$(CopyRetryCount)"
+ RetryDelayMilliseconds="$(CopyRetryDelayMilliseconds)"
+ UseHardlinksIfPossible="$(CreateHardLinksForPublishFilesIfPossible)"
+ UseSymboliclinksIfPossible="$(CreateSymbolicLinksForPublishFilesIfPossible)">
+ <Output TaskParameter="DestinationFiles" ItemName="FileWrites"/>
+ </Copy>
+
+ <PropertyGroup>
+ <Crossgen>$([System.IO.Path]::GetFullPath($([System.IO.Path]::Combine($(_NetCoreRefDir), $([System.IO.Path]::GetFileName($(Crossgen)))))))</Crossgen>
+ </PropertyGroup>
+ </Target>
+
+ <!--
+ ============================================================
+ _RunOptimizer
+
+ Start the optimization phase
+ ============================================================
+ -->
+
+ <Target Name="_RunOptimizer"
+ DependsOnTargets="_InitializeBasicProps;
+ _ComputeResolvedFilesToStoreTypes;
+ _SetupStageForCrossgen"
+ Condition="$(SkipOptimization) != 'true' ">
+ <ItemGroup>
+ <AssembliestoCrossgen Include="$(MSBuildProjectFullPath)">
+ <Properties>
+ CrossgenExe=$(Crossgen);
+ CrossgenJit=$(JitPath);
+ CrossgenInputAssembly=%(_ManagedResolvedFilesToOptimize.Fullpath);
+ CrossgenOutputAssembly=$(_RuntimeOptimizedDir)$(DirectorySeparatorChar)%(_ManagedResolvedFilesToOptimize.FileName)%(_ManagedResolvedFilesToOptimize.Extension);
+ CrossgenSubOutputPath=%(_ManagedResolvedFilesToOptimize.DestinationSubPath);
+ _RuntimeOptimizedDir=$(_RuntimeOptimizedDir);
+ PublishDir=$(StoreStagingDir);
+ CrossgenPlatformAssembliesPath=$(_RuntimeRefDir)$(PathSeparator)$(_NetCoreRefDir);
+ CreateProfilingSymbols=$(CreateProfilingSymbols);
+ StoreSymbolsStagingDir=$(StoreSymbolsStagingDir);
+ _RuntimeSymbolsDir=$(_RuntimeSymbolsDir)
+ </Properties>
+ </AssembliestoCrossgen>
+ </ItemGroup>
+
+ <!-- CrossGen the assemblies -->
+ <MSBuild Projects="@(AssembliestoCrossgen)"
+ Targets="RunCrossGen"
+ BuildInParallel="$(BuildInParallel)"
+ Condition="'@(_ManagedResolvedFilesToOptimize)' != ''"/>
+
+ </Target>
+
+ <!--
+ ============================================================
+ RunCrossGen
+ Target Encapsulating the crossgen command
+ ============================================================
+ -->
+
+ <Target Name="RunCrossGen"
+ DependsOnTargets="_InitializeBasicProps;">
+
+ <PropertyGroup>
+ <CrossgenProfilingSymbolsOutputDirectory>$([System.IO.Path]::GetDirectoryName($(_RuntimeSymbolsDir)\$(CrossgenSubOutputPath)))</CrossgenProfilingSymbolsOutputDirectory>
+ <CrossgenSymbolsStagingDirectory>$([System.IO.Path]::GetDirectoryName($(StoreSymbolsStagingDir)\$(CrossgenSubOutputPath)))</CrossgenSymbolsStagingDirectory>
+ <CrossgenCommandline>$(CrossgenExe) -nologo -readytorun -in "$(CrossgenInputAssembly)" -out "$(CrossgenOutputAssembly)" -jitpath "$(CrossgenJit)" -platform_assemblies_paths "$(CrossgenPlatformAssembliesPath)"</CrossgenCommandline>
+ <CreateProfilingSymbolsOptionName Condition="$([MSBuild]::IsOSPlatform(`Windows`))">CreatePDB</CreateProfilingSymbolsOptionName>
+ <CreateProfilingSymbolsOptionName Condition="'$(CreateProfilingSymbolsOptionName)' == ''">CreatePerfMap</CreateProfilingSymbolsOptionName>
+ </PropertyGroup>
+
+ <Message Text="CrossgenCommandline: $(CrossgenCommandline)"/>
+
+ <!--Optimization skip if the assembly is already present in the final output directory-->
+ <Exec
+ Command="$(CrossgenCommandline)"
+ Condition="!Exists($([System.IO.Path]::Combine($(PublishDir),$(CrossgenSubOutputPath))))"
+ IgnoreStandardErrorWarningFormat="true" />
+
+ <Copy SourceFiles = "$(CrossgenOutputAssembly)"
+ DestinationFiles="$(PublishDir)\$(CrossgenSubOutputPath)"
+ OverwriteReadOnlyFiles="$(OverwriteReadOnlyFiles)"
+ Retries="$(CopyRetryCount)"
+ RetryDelayMilliseconds="$(CopyRetryDelayMilliseconds)"
+ Condition="!Exists($([System.IO.Path]::Combine($(PublishDir),$(CrossgenSubOutputPath))))">
+
+ <Output TaskParameter="DestinationFiles" ItemName="FileWrites"/>
+ </Copy>
+
+ <!-- Create profiling symbols if requested -->
+ <MakeDir Directories="$(CrossgenProfilingSymbolsOutputDirectory)"
+ Condition="'$(CreateProfilingSymbols)' == 'true' and Exists($(CrossgenOutputAssembly))" />
+ <Exec Command="$(CrossgenExe) -nologo -readytorun -platform_assemblies_paths $(CrossgenPlatformAssembliesPath) -$(CreateProfilingSymbolsOptionName) $(CrossgenProfilingSymbolsOutputDirectory) $(CrossgenOutputAssembly)"
+ Condition="'$(CreateProfilingSymbols)' == 'true' and Exists($(CrossgenOutputAssembly))"
+ IgnoreStandardErrorWarningFormat="true" />
+
+ <ItemGroup>
+ <_ProfilingSymbols Include="$(CrossgenProfilingSymbolsOutputDirectory)\*"
+ Condition="'$(CreateProfilingSymbols)' == 'true'" />
+ </ItemGroup>
+
+ <Copy SourceFiles="@(_ProfilingSymbols)"
+ DestinationFolder="$(CrossgenSymbolsStagingDirectory)"
+ Condition="'$(CreateProfilingSymbols)' == 'true'"
+ OverwriteReadOnlyFiles="$(OverwriteReadOnlyFiles)"
+ Retries="$(CopyRetryCount)"
+ RetryDelayMilliseconds="$(CopyRetryDelayMilliseconds)">
+ <Output TaskParameter="DestinationFiles" ItemName="FileWrites"/>
+ </Copy>
+ </Target>
+
+ <Target Name="_InitializeBasicProps">
+ <PropertyGroup>
+ <PathSeparator>$([System.IO.Path]::PathSeparator)</PathSeparator>
+ <DirectorySeparatorChar>$([System.IO.Path]::DirectorySeparatorChar)</DirectorySeparatorChar>
+ </PropertyGroup>
+ </Target>
+
+ <!--
+ ============================================================
+ _GetCrossgenProps
+ Generates props used by Crossgen
+ ============================================================
+ -->
+
+ <Target Name="_GetCrossgenProps"
+ Condition="$(SkipOptimization) != 'true' ">
+
+ <PropertyGroup>
+ <_CrossProjFileDir>$([System.IO.Path]::Combine($(ComposeWorkingDir),"Optimize"))</_CrossProjFileDir>
+ <_NetCoreRefDir>$([System.IO.Path]::Combine($(_CrossProjFileDir), "netcoreapp"))</_NetCoreRefDir> <!-- flat netcore app assemblies-->
+ </PropertyGroup>
+ <MakeDir Directories="$(_CrossProjFileDir)"/>
+ <PropertyGroup>
+ <_CrossProjAssetsFile>$([System.IO.Path]::Combine($(_CrossProjFileDir), project.assets.json))</_CrossProjAssetsFile>
+ </PropertyGroup>
+
+
+ </Target>
+
+ <!--
+ ============================================================
+ _SetupStageForCrossgen
+ ============================================================
+ -->
+
+ <Target Name="_SetupStageForCrossgen"
+ DependsOnTargets="_GetCrossgenProps;">
+ <PropertyGroup>
+ <_RuntimeRefDir>$([System.IO.Path]::Combine($(StoreWorkerWorkingDir), "runtimeref"))</_RuntimeRefDir> <!-- flat app managed assemblies -->
+ <_RuntimeOptimizedDir>$([System.IO.Path]::Combine($(StoreWorkerWorkingDir), "runtimopt"))</_RuntimeOptimizedDir> <!-- optimized app managed assemblies in nuget cache layout -->
+ <_RuntimeSymbolsDir>$([System.IO.Path]::Combine($(StoreWorkerWorkingDir), "runtimesymbols"))</_RuntimeSymbolsDir>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <_ManagedResolvedFilesToOptimize Include="@(_ManagedResolvedFileToPublishCandidates)" />
+ </ItemGroup>
+
+ <MakeDir Directories="$(_RuntimeOptimizedDir)"/>
+ <MakeDir Directories="$(_RuntimeSymbolsDir)"
+ Condition="'$(CreateProfilingSymbols)' == 'true'" />
+
+ <!-- Copy managed files to a flat temp directory for passing it as ref -->
+ <Copy SourceFiles = "@(_ManagedResolvedFilesToOptimize)"
+ DestinationFolder="$(_RuntimeRefDir)"
+ OverwriteReadOnlyFiles="$(OverwriteReadOnlyFiles)"
+ Retries="$(CopyRetryCount)"
+ RetryDelayMilliseconds="$(CopyRetryDelayMilliseconds)"
+ UseHardlinksIfPossible="$(CreateHardLinksForPublishFilesIfPossible)"
+ UseSymboliclinksIfPossible="$(CreateSymbolicLinksForPublishFilesIfPossible)">
+
+ <Output TaskParameter="DestinationFiles" ItemName="FileWrites"/>
+ </Copy>
+
+ </Target>
+
+ <!--
+ ============================================================
+ _RestoreCrossgen
+ Restores netcoreapp and publishes it to a temp directory
+ ============================================================
+ -->
+
+ <Target Name="_RestoreCrossgen"
+ DependsOnTargets="PrepforRestoreForComposeStore;
+ _SetupStageForCrossgen;
+ ProcessFrameworkReferences;
+ ApplyImplicitVersions;
+ _ComputePackageReferencePublish"
+ Condition="$(SkipOptimization) != 'true' ">
+
+ <ItemGroup>
+ <!-- Filter package references to the one for the platform library, in order to find the right version -->
+ <PackageReferenceForCrossGen Include="@(PackageReference)" Condition="'%(Identity)' == '$(MicrosoftNETPlatformLibrary)'" />
+ </ItemGroup>
+
+ <MSBuild Projects="$(MSBuildProjectFullPath)"
+ Targets="Restore"
+ Properties="RuntimeIdentifiers=$(RuntimeIdentifier);
+ RestoreGraphProjectInput=$(MSBuildProjectFullPath);
+ RestoreOutputPath=$(_CrossProjFileDir);
+ StorePackageName=$(MicrosoftNETPlatformLibrary);
+ StorePackageVersion=%(PackageReferenceForCrossGen.Version);"/>
+
+ <!-- For future: Use ResolvePackageAssets instead of ResolveCopyLocalAssets, delete ResolveCopyLocalAssets task -->
+ <ResolveCopyLocalAssets Condition="'$(_TargetFrameworkVersionWithoutV)' < '3.0'"
+ AssetsFilePath="$(_CrossProjAssetsFile)"
+ TargetFramework="$(_TFM)"
+ RuntimeIdentifier="$(RuntimeIdentifier)"
+ PlatformLibraryName="$(MicrosoftNETPlatformLibrary)"
+ RuntimeFrameworks="@(RuntimeFramework)"
+ ExcludedPackageReferences="@(_ExcludeFromPublishPackageReference)"
+ IsSelfContained="$(SelfContained)"
+ PreserveStoreLayout="false">
+
+ <Output TaskParameter="ResolvedAssets" ItemName="CrossgenResolvedAssembliesToPublish" />
+ </ResolveCopyLocalAssets>
+
+ <GetPackageDirectory Condition="'$(_TargetFrameworkVersionWithoutV)' >= '3.0'"
+ Items="@(RuntimePack)"
+ AssetsFileWithAdditionalPackageFolders="$(_CrossProjAssetsFile)">
+ <Output TaskParameter="Output" ItemName="_CrossgenRuntimePack" />
+ </GetPackageDirectory>
+
+ <ResolveRuntimePackAssets Condition="'$(_TargetFrameworkVersionWithoutV)' >= '3.0'"
+ FrameworkReferences="@(FrameworkReference)"
+ ResolvedRuntimePacks="@(_CrossgenRuntimePack)">
+ <Output TaskParameter="RuntimePackAssets" ItemName="CrossgenResolvedAssembliesToPublish" />
+ </ResolveRuntimePackAssets>
+
+ <!-- Copy managed files to a flat temp directory for passing it as ref for crossgen -->
+ <Copy SourceFiles = "@(CrossgenResolvedAssembliesToPublish)"
+ DestinationFolder="$(_NetCoreRefDir)"
+ OverwriteReadOnlyFiles="$(OverwriteReadOnlyFiles)"
+ Retries="$(CopyRetryCount)"
+ RetryDelayMilliseconds="$(CopyRetryDelayMilliseconds)"
+ UseHardlinksIfPossible="$(CreateHardLinksForPublishFilesIfPossible)"
+ UseSymboliclinksIfPossible="$(CreateSymbolicLinksForPublishFilesIfPossible)">
+
+ <Output TaskParameter="DestinationFiles" ItemName="FileWrites"/>
+ </Copy>
+ </Target>
+
+ <PropertyGroup>
+ <MicrosoftNETCrossgenBuildTasksAssembly Condition="'$(MSBuildRuntimeType)' == 'Core'">$(MSBuildThisFileDirectory)..\tasks\net6.0\Microsoft.NET.Sdk.Crossgen.dll</MicrosoftNETCrossgenBuildTasksAssembly>
+ <MicrosoftNETCrossgenBuildTasksAssembly Condition="'$(MSBuildRuntimeType)' != 'Core'">$(MSBuildThisFileDirectory)..\tasks\net472\Microsoft.NET.Sdk.Crossgen.dll</MicrosoftNETCrossgenBuildTasksAssembly>
+ </PropertyGroup>
+
+ <!--
+ ============================================================
+ CreateReadyToRunImages
+
+ Create ReadyToRun images for managed assemblies in _ResolvedFileToPublishAlways and _ResolvedFileToPublishPreserveNewest.
+ ============================================================
+ -->
+ <Target Name="CreateReadyToRunImages"
+ Condition="'$(_TargetFrameworkVersionWithoutV)' >= '3.0' And '$(PublishReadyToRun)' == 'true' And '$(TargetFrameworkIdentifier)' == '.NETCoreApp'"
+ DependsOnTargets="_PrepareForReadyToRunCompilation;
+ _CreateR2RImages;
+ _CreateR2RSymbols">
+
+ <AllowEmptyTelemetry EventName="ReadyToRun" EventData="PublishReadyToRunUseCrossgen2=$(PublishReadyToRunUseCrossgen2);Crossgen2PackVersion=%(ResolvedCrossgen2Pack.NuGetPackageVersion);CompileListCount=@(_ReadyToRunCompileList->Count());FailedCount=@(_ReadyToRunCompilationFailures->Count())" />
+
+ <NETSdkError Condition="'@(_ReadyToRunCompilationFailures)' != ''" ResourceName="ReadyToRunCompilationFailed" />
+
+ <NETSdkInformation Condition="'$(_ReadyToRunCompilerHasWarnings)' != ''" ResourceName="ReadyToRunCompilationHasWarnings_Info" />
+
+ <ItemGroup>
+ <!--
+ Note: we only remove the entries for the IL images and replace them with the entries for the R2R images.
+ We do not do the same for PDBs, because the native PDBs created by the R2R compiler complement the IL PDBs
+ and do not replace them. IL PDBs are still required for debugging. Native PDBs emitted by the R2R compiler are
+ only used for profiling purposes.
+ -->
+ <ResolvedFileToPublish Remove="@(_ReadyToRunCompositeBuildInput)" />
+ <ResolvedFileToPublish Remove="@(_ReadyToRunCompileList)" />
+ <ResolvedFileToPublish Include="@(_ReadyToRunFilesToPublish)" />
+ </ItemGroup>
+
+ </Target>
+
+ <!--
+ ============================================================
+ _PrepareForReadyToRunCompilation
+
+ Prepare build for ReadyToRun compilations. Builds list of assemblies to compile, and computes paths to ReadyToRun compiler bits
+ ============================================================
+ -->
+ <UsingTask Condition="'$(Crossgen2TasksOverriden)' != 'true'" TaskName="PrepareForReadyToRunCompilation" AssemblyFile="$(MicrosoftNETBuildTasksAssembly)"/>
+ <Target Name="_PrepareForReadyToRunCompilation" DependsOnTargets="ResolveReadyToRunCompilers;_ComputeManagedRuntimePackAssemblies;_ComputeAssembliesToPostprocessOnPublish">
+
+ <PropertyGroup>
+ <_ReadyToRunOutputPath>$(IntermediateOutputPath)R2R</_ReadyToRunOutputPath>
+ </PropertyGroup>
+
+ <MakeDir Directories="$(_ReadyToRunOutputPath)" />
+
+ <ItemGroup>
+ <_ReadyToRunImplementationAssemblies Include="@(ResolvedFileToPublish->WithMetadataValue('PostprocessAssembly', 'true'))" />
+ </ItemGroup>
+
+ <!-- Even if app is not self-contained, crossgen requires closure of implementation assemblies. Resolve conflicts
+ of the runtime pack assets as though we were copying them locally, and add them to the R2R implementation
+ assembly list. -->
+ <ItemGroup Condition="'$(SelfContained)' != 'true'">
+ <_ReadyToRunImplementationAssemblies Include="@(_ManagedRuntimePackAssembly)" ReferenceOnly="true" />
+ </ItemGroup>
+
+ <ResolvePackageFileConflicts Condition="'$(SelfContained)' != 'true'"
+ ReferenceCopyLocalPaths="@(_ReadyToRunImplementationAssemblies)">
+ <Output TaskParameter="ReferenceCopyLocalPathsWithoutConflicts" ItemName="_ReadyToRunImplementationAssembliesWithoutConflicts" />
+ </ResolvePackageFileConflicts>
+
+ <ItemGroup Condition="'$(SelfContained)' != 'true'">
+ <_ReadyToRunImplementationAssemblies Remove="@(_ReadyToRunImplementationAssemblies)" />
+ <_ReadyToRunImplementationAssemblies Include="@(_ReadyToRunImplementationAssembliesWithoutConflicts)" />
+ </ItemGroup>
+
+ <PrepareForReadyToRunCompilation CrossgenTool="@(CrossgenTool)"
+ Crossgen2Tool="@(Crossgen2Tool)"
+ OutputPath="$(_ReadyToRunOutputPath)"
+ MainAssembly="@(IntermediateAssembly)"
+ Assemblies="@(_ReadyToRunImplementationAssemblies)"
+ ExcludeList="@(PublishReadyToRunExclude)"
+ EmitSymbols="$(PublishReadyToRunEmitSymbols)"
+ IncludeSymbolsInSingleFile="$(IncludeSymbolsInSingleFile)"
+ ReadyToRunUseCrossgen2="$(PublishReadyToRunUseCrossgen2)"
+ Crossgen2Composite="$(PublishReadyToRunComposite)"
+ PublishReadyToRunCompositeExclusions="@(PublishReadyToRunCompositeExclusions)">
+
+ <Output TaskParameter="ReadyToRunCompileList" ItemName="_ReadyToRunCompileList" />
+ <Output TaskParameter="ReadyToRunSymbolsCompileList" ItemName="_ReadyToRunSymbolsCompileList" />
+
+ <Output TaskParameter="ReadyToRunFilesToPublish" ItemName="_ReadyToRunFilesToPublish" />
+ <Output TaskParameter="ReadyToRunAssembliesToReference" ItemName="_ReadyToRunAssembliesToReference" />
+ <Output TaskParameter="ReadyToRunCompositeBuildReferences" ItemName="_ReadyToRunCompositeBuildReferences" />
+ <Output TaskParameter="ReadyToRunCompositeBuildInput" ItemName="_ReadyToRunCompositeBuildInput" />
+
+ </PrepareForReadyToRunCompilation>
+ </Target>
+
+ <UsingTask Condition="'$(Crossgen2TasksOverriden)' != 'true'" TaskName="ResolveReadyToRunCompilers" AssemblyFile="$(MicrosoftNETBuildTasksAssembly)" />
+ <Target Name="ResolveReadyToRunCompilers">
+ <ResolveReadyToRunCompilers RuntimePacks="@(ResolvedRuntimePack)"
+ Crossgen2Packs="@(ResolvedCrossgen2Pack)"
+ TargetingPacks="@(ResolvedTargetingPack)"
+ RuntimeGraphPath="$(BundledRuntimeIdentifierGraphFile)"
+ NETCoreSdkRuntimeIdentifier="$(NETCoreSdkRuntimeIdentifier)"
+ EmitSymbols="$(PublishReadyToRunEmitSymbols)"
+ ReadyToRunUseCrossgen2="$(PublishReadyToRunUseCrossgen2)">
+
+ <Output TaskParameter="CrossgenTool" ItemName="CrossgenTool" />
+ <Output TaskParameter="Crossgen2Tool" ItemName="Crossgen2Tool" />
+ </ResolveReadyToRunCompilers>
+ </Target>
+
+ <!--
+ ============================================================
+ _CreateR2RImages
+
+ Compiles assemblies in the _ReadyToRunCompileList list into ReadyToRun images
+ ============================================================
+ -->
+ <UsingTask Condition="'$(Crossgen2TasksOverriden)' != 'true'" TaskName="RunReadyToRunCompiler" AssemblyFile="$(MicrosoftNETBuildTasksAssembly)" />
+ <Target Name="_CreateR2RImages"
+ Inputs="@(_ReadyToRunCompileList);@(_ReadyToRunCompositeBuildInput)"
+ Outputs="%(_ReadyToRunCompileList.OutputR2RImage);%(_ReadyToRunCompileList.OutputPDBImage)">
+
+ <RunReadyToRunCompiler CrossgenTool="@(CrossgenTool)"
+ Crossgen2Tool="@(Crossgen2Tool)"
+ UseCrossgen2="$(PublishReadyToRunUseCrossgen2)"
+ Crossgen2ExtraCommandLineArgs="$(PublishReadyToRunCrossgen2ExtraArgs)"
+ ImplementationAssemblyReferences="@(_ReadyToRunAssembliesToReference)"
+ ShowCompilerWarnings="$(PublishReadyToRunShowWarnings)"
+ CompilationEntry="@(_ReadyToRunCompileList)"
+ ContinueOnError="ErrorAndContinue"
+ ReadyToRunCompositeBuildReferences="@(_ReadyToRunCompositeBuildReferences)"
+ ReadyToRunCompositeBuildInput="@(_ReadyToRunCompositeBuildInput)">
+ <Output TaskParameter="ExitCode" PropertyName="_ReadyToRunCompilerExitCode" />
+ <Output TaskParameter="WarningsDetected" PropertyName="_ReadyToRunWarningsDetected" />
+ </RunReadyToRunCompiler>
+
+ <PropertyGroup>
+ <!-- Use distinct property here as any of the invocations can set it -->
+ <_ReadyToRunCompilerHasWarnings Condition="'$(_ReadyToRunWarningsDetected)' == 'true'">true</_ReadyToRunCompilerHasWarnings>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <_ReadyToRunCompilationFailures Condition="'$(_ReadyToRunCompilerExitCode)' != '' And $(_ReadyToRunCompilerExitCode) != 0"
+ Include="@(_ReadyToRunCompileList)" />
+ </ItemGroup>
+ </Target>
+
+ <!--
+ ============================================================
+ _CreateR2RSymbols
+
+ Emit native symbols for ReadyToRun images in the _ReadyToRunSymbolsCompileList list
+ ============================================================
+ -->
+ <Target Name="_CreateR2RSymbols"
+ Inputs="@(_ReadyToRunSymbolsCompileList)"
+ Outputs="%(_ReadyToRunSymbolsCompileList.OutputPDBImage)"
+ Condition="'$(PublishReadyToRunUseCrossgen2)' != 'true' or '@(Crossgen2Tool -> '%(IsVersion5)')' == 'true'">
+ <RunReadyToRunCompiler CrossgenTool="@(CrossgenTool)"
+ Crossgen2Tool="@(Crossgen2Tool)"
+ UseCrossgen2="$(PublishReadyToRunUseCrossgen2)"
+ Crossgen2ExtraCommandLineArgs="$(PublishReadyToRunCrossgen2ExtraArgs)"
+ ImplementationAssemblyReferences="@(_ReadyToRunAssembliesToReference)"
+ ShowCompilerWarnings="$(PublishReadyToRunShowWarnings)"
+ CompilationEntry="@(_ReadyToRunSymbolsCompileList)"
+ ContinueOnError="ErrorAndContinue">
+ <Output TaskParameter="ExitCode" PropertyName="_ReadyToRunCompilerExitCode" />
+ <Output TaskParameter="WarningsDetected" PropertyName="_ReadyToRunWarningsDetected" />
+ </RunReadyToRunCompiler>
+
+ <PropertyGroup>
+ <!-- Use distinct property here as any of the invocations can set it -->
+ <_ReadyToRunCompilerHasWarnings Condition="'$(_ReadyToRunWarningsDetected)' == 'true'">true</_ReadyToRunCompilerHasWarnings>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <_ReadyToRunCompilationFailures Condition="'$(_ReadyToRunCompilerExitCode)' != '' And $(_ReadyToRunCompilerExitCode) != 0"
+ Include="@(_ReadyToRunSymbolsCompileList)" />
+ </ItemGroup>
+ </Target>
+</Project>
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.IO;
+using System.Runtime.InteropServices;
+using System.Reflection.PortableExecutable;
+using Microsoft.Build.Framework;
+using Microsoft.Build.Utilities;
+using System.Reflection.Metadata;
+using System.Reflection;
+
+namespace Microsoft.NET.Build.Tasks
+{
+ public class PrepareForReadyToRunCompilation : TaskBase
+ {
+ [Required]
+ public ITaskItem MainAssembly { get; set; }
+ public ITaskItem[] Assemblies { get; set; }
+ public string[] ExcludeList { get; set; }
+ public bool EmitSymbols { get; set; }
+ public bool ReadyToRunUseCrossgen2 { get; set; }
+ public bool Crossgen2Composite { get; set; }
+
+ [Required]
+ public string OutputPath { get; set; }
+ [Required]
+ public bool IncludeSymbolsInSingleFile { get; set; }
+
+ public string[] PublishReadyToRunCompositeExclusions { get; set; }
+
+ public ITaskItem CrossgenTool { get; set; }
+ public ITaskItem Crossgen2Tool { get; set; }
+
+ // Output lists of files to compile. Currently crossgen has to run in two steps, the first to generate the R2R image
+ // and the second to create native PDBs for the compiled images (the output of the first step is an input to the second step)
+ [Output]
+ public ITaskItem[] ReadyToRunCompileList => _compileList.ToArray();
+ [Output]
+ public ITaskItem[] ReadyToRunSymbolsCompileList => _symbolsCompileList.ToArray();
+
+ // Output files to publish after compilation. These lists are equivalent to the input list, but contain the new
+ // paths to the compiled R2R images and native PDBs.
+ [Output]
+ public ITaskItem[] ReadyToRunFilesToPublish => _r2rFiles.ToArray();
+
+ [Output]
+ public ITaskItem[] ReadyToRunAssembliesToReference => _r2rReferences.ToArray();
+
+ [Output]
+ public ITaskItem[] ReadyToRunCompositeBuildReferences => _r2rCompositeReferences.ToArray();
+
+ [Output]
+ public ITaskItem[] ReadyToRunCompositeBuildInput => _r2rCompositeInput.ToArray();
+
+ private bool _crossgen2IsVersion5;
+
+ private List<ITaskItem> _compileList = new List<ITaskItem>();
+ private List<ITaskItem> _symbolsCompileList = new List<ITaskItem>();
+ private List<ITaskItem> _r2rFiles = new List<ITaskItem>();
+ private List<ITaskItem> _r2rReferences = new List<ITaskItem>();
+ private List<ITaskItem> _r2rCompositeReferences = new List<ITaskItem>();
+ private List<ITaskItem> _r2rCompositeInput = new List<ITaskItem>();
+
+ protected override void ExecuteCore()
+ {
+ if (ReadyToRunUseCrossgen2)
+ {
+ string isVersion5 = Crossgen2Tool.GetMetadata(MetadataKeys.IsVersion5);
+ _crossgen2IsVersion5 = !string.IsNullOrEmpty(isVersion5) && bool.Parse(isVersion5);
+
+ if (Crossgen2Composite && EmitSymbols && _crossgen2IsVersion5)
+ {
+ Log.LogError(Strings.Crossgen5CannotEmitSymbolsInCompositeMode);
+ return;
+ }
+ }
+
+ string diaSymReaderPath = CrossgenTool?.GetMetadata(MetadataKeys.DiaSymReader);
+
+ bool hasValidDiaSymReaderLib =
+ ReadyToRunUseCrossgen2 && !_crossgen2IsVersion5 ||
+ !string.IsNullOrEmpty(diaSymReaderPath) && File.Exists(diaSymReaderPath);
+
+ // Process input lists of files
+ ProcessInputFileList(Assemblies, _compileList, _symbolsCompileList, _r2rFiles, _r2rReferences, _r2rCompositeReferences, _r2rCompositeInput, hasValidDiaSymReaderLib);
+ }
+
+ private void ProcessInputFileList(
+ ITaskItem[] inputFiles,
+ List<ITaskItem> imageCompilationList,
+ List<ITaskItem> symbolsCompilationList,
+ List<ITaskItem> r2rFilesPublishList,
+ List<ITaskItem> r2rReferenceList,
+ List<ITaskItem> r2rCompositeReferenceList,
+ List<ITaskItem> r2rCompositeInputList,
+ bool hasValidDiaSymReaderLib)
+ {
+ if (inputFiles == null)
+ {
+ return;
+ }
+
+ var exclusionSet = ExcludeList == null || Crossgen2Composite ? null : new HashSet<string>(ExcludeList, StringComparer.OrdinalIgnoreCase);
+ var compositeExclusionSet = PublishReadyToRunCompositeExclusions == null || !Crossgen2Composite ? null : new HashSet<string>(PublishReadyToRunCompositeExclusions, StringComparer.OrdinalIgnoreCase);
+
+ foreach (var file in inputFiles)
+ {
+ var eligibility = GetInputFileEligibility(file, Crossgen2Composite, exclusionSet, compositeExclusionSet);
+
+ if (eligibility.NoEligibility)
+ {
+ continue;
+ }
+
+ if (eligibility.IsReference)
+ r2rReferenceList.Add(file);
+
+ if (eligibility.IsReference && !eligibility.ReferenceHiddenFromCompositeBuild && !eligibility.Compile)
+ r2rCompositeReferenceList.Add(file);
+
+ if (!eligibility.Compile)
+ {
+ continue;
+ }
+
+ var outputR2RImageRelativePath = file.GetMetadata(MetadataKeys.RelativePath);
+ var outputR2RImage = Path.Combine(OutputPath, outputR2RImageRelativePath);
+
+ string outputPDBImage = null;
+ string outputPDBImageRelativePath = null;
+ string crossgen1CreatePDBCommand = null;
+
+ if (EmitSymbols)
+ {
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && hasValidDiaSymReaderLib)
+ {
+ outputPDBImage = Path.ChangeExtension(outputR2RImage, "ni.pdb");
+ outputPDBImageRelativePath = Path.ChangeExtension(outputR2RImageRelativePath, "ni.pdb");
+ crossgen1CreatePDBCommand = $"/CreatePDB \"{Path.GetDirectoryName(outputPDBImage)}\"";
+ }
+ else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
+ {
+ using (FileStream fs = new FileStream(file.ItemSpec, FileMode.Open, FileAccess.Read))
+ {
+ PEReader pereader = new PEReader(fs);
+ MetadataReader mdReader = pereader.GetMetadataReader();
+ Guid mvid = mdReader.GetGuid(mdReader.GetModuleDefinition().Mvid);
+
+ outputPDBImage = Path.ChangeExtension(outputR2RImage, "ni.{" + mvid + "}.map");
+ outputPDBImageRelativePath = Path.ChangeExtension(outputR2RImageRelativePath, "ni.{" + mvid + "}.map");
+ crossgen1CreatePDBCommand = $"/CreatePerfMap \"{Path.GetDirectoryName(outputPDBImage)}\"";
+ }
+ }
+ }
+
+ if (eligibility.CompileSeparately)
+ {
+ // This TaskItem is the IL->R2R entry, for an input assembly that needs to be compiled into a R2R image. This will be used as
+ // an input to the ReadyToRunCompiler task
+ TaskItem r2rCompilationEntry = new TaskItem(file);
+ r2rCompilationEntry.SetMetadata(MetadataKeys.OutputR2RImage, outputR2RImage);
+ if (outputPDBImage != null && ReadyToRunUseCrossgen2 && !_crossgen2IsVersion5)
+ {
+ r2rCompilationEntry.SetMetadata(MetadataKeys.EmitSymbols, "true");
+ r2rCompilationEntry.SetMetadata(MetadataKeys.OutputPDBImage, outputPDBImage);
+ }
+ r2rCompilationEntry.RemoveMetadata(MetadataKeys.OriginalItemSpec);
+ imageCompilationList.Add(r2rCompilationEntry);
+ }
+ else if (eligibility.CompileIntoCompositeImage)
+ {
+ r2rCompositeInputList.Add(file);
+ }
+
+ // This TaskItem corresponds to the output R2R image. It is equivalent to the input TaskItem, only the ItemSpec for it points to the new path
+ // for the newly created R2R image
+ TaskItem r2rFileToPublish = new TaskItem(file);
+ r2rFileToPublish.ItemSpec = outputR2RImage;
+ r2rFileToPublish.RemoveMetadata(MetadataKeys.OriginalItemSpec);
+ r2rFilesPublishList.Add(r2rFileToPublish);
+
+ // Note: ReadyToRun PDB/Map files are not needed for debugging. They are only used for profiling, therefore the default behavior is to not generate them
+ // unless an explicit PublishReadyToRunEmitSymbols flag is enabled by the app developer. There is also another way to profile that the runtime supports, which does
+ // not rely on the native PDBs/Map files, so creating them is really an opt-in option, typically used by advanced users.
+ // For debugging, only the IL PDBs are required.
+ if (eligibility.CompileSeparately && outputPDBImage != null)
+ {
+ if (!ReadyToRunUseCrossgen2 || _crossgen2IsVersion5)
+ {
+ // This TaskItem is the R2R->R2RPDB entry, for a R2R image that was just created, and for which we need to create native PDBs. This will be used as
+ // an input to the ReadyToRunCompiler task
+ TaskItem pdbCompilationEntry = new TaskItem(file);
+ pdbCompilationEntry.ItemSpec = outputR2RImage;
+ pdbCompilationEntry.SetMetadata(MetadataKeys.OutputPDBImage, outputPDBImage);
+ pdbCompilationEntry.SetMetadata(MetadataKeys.CreatePDBCommand, crossgen1CreatePDBCommand);
+ symbolsCompilationList.Add(pdbCompilationEntry);
+ }
+
+ // This TaskItem corresponds to the output PDB image. It is equivalent to the input TaskItem, only the ItemSpec for it points to the new path
+ // for the newly created PDB image.
+ TaskItem r2rSymbolsFileToPublish = new TaskItem(file);
+ r2rSymbolsFileToPublish.ItemSpec = outputPDBImage;
+ r2rSymbolsFileToPublish.SetMetadata(MetadataKeys.RelativePath, outputPDBImageRelativePath);
+ r2rSymbolsFileToPublish.RemoveMetadata(MetadataKeys.OriginalItemSpec);
+ if (!IncludeSymbolsInSingleFile)
+ {
+ r2rSymbolsFileToPublish.SetMetadata(MetadataKeys.ExcludeFromSingleFile, "true");
+ }
+
+ r2rFilesPublishList.Add(r2rSymbolsFileToPublish);
+ }
+ }
+
+ if (Crossgen2Composite)
+ {
+ MainAssembly.SetMetadata(MetadataKeys.RelativePath, Path.GetFileName(MainAssembly.ItemSpec));
+
+ var compositeR2RImageRelativePath = MainAssembly.GetMetadata(MetadataKeys.RelativePath);
+ compositeR2RImageRelativePath = Path.ChangeExtension(compositeR2RImageRelativePath, "r2r" + Path.GetExtension(compositeR2RImageRelativePath));
+ var compositeR2RImage = Path.Combine(OutputPath, compositeR2RImageRelativePath);
+
+ TaskItem r2rCompilationEntry = new TaskItem(MainAssembly);
+ r2rCompilationEntry.ItemSpec = r2rCompositeInputList[0].ItemSpec;
+ r2rCompilationEntry.SetMetadata(MetadataKeys.OutputR2RImage, compositeR2RImage);
+ r2rCompilationEntry.SetMetadata(MetadataKeys.CreateCompositeImage, "true");
+ r2rCompilationEntry.RemoveMetadata(MetadataKeys.OriginalItemSpec);
+
+ if (EmitSymbols)
+ {
+ string compositePDBImage = null;
+ string compositePDBRelativePath = null;
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && hasValidDiaSymReaderLib)
+ {
+ compositePDBImage = Path.ChangeExtension(compositeR2RImage, ".ni.pdb");
+ compositePDBRelativePath = Path.ChangeExtension(compositeR2RImageRelativePath, ".ni.pdb");
+ }
+ else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
+ {
+ compositePDBImage = Path.ChangeExtension(compositeR2RImage, ".ni.{composite}.map");
+ compositePDBRelativePath = Path.ChangeExtension(compositeR2RImageRelativePath, ".ni.{composite}.map");
+ }
+
+ if (compositePDBImage != null && ReadyToRunUseCrossgen2 && !_crossgen2IsVersion5)
+ {
+ r2rCompilationEntry.SetMetadata(MetadataKeys.EmitSymbols, "true");
+ r2rCompilationEntry.SetMetadata(MetadataKeys.OutputPDBImage, compositePDBImage);
+
+ // Publish composite PDB file
+ TaskItem r2rSymbolsFileToPublish = new TaskItem(MainAssembly);
+ r2rSymbolsFileToPublish.ItemSpec = compositePDBImage;
+ r2rSymbolsFileToPublish.SetMetadata(MetadataKeys.RelativePath, compositePDBRelativePath);
+ r2rSymbolsFileToPublish.RemoveMetadata(MetadataKeys.OriginalItemSpec);
+ if (!IncludeSymbolsInSingleFile)
+ {
+ r2rSymbolsFileToPublish.SetMetadata(MetadataKeys.ExcludeFromSingleFile, "true");
+ }
+
+ r2rFilesPublishList.Add(r2rSymbolsFileToPublish);
+ }
+ }
+
+ imageCompilationList.Add(r2rCompilationEntry);
+
+ // Publish it
+ TaskItem compositeR2RFileToPublish = new TaskItem(MainAssembly);
+ compositeR2RFileToPublish.ItemSpec = compositeR2RImage;
+ compositeR2RFileToPublish.RemoveMetadata(MetadataKeys.OriginalItemSpec);
+ compositeR2RFileToPublish.SetMetadata(MetadataKeys.RelativePath, compositeR2RImageRelativePath);
+ r2rFilesPublishList.Add(compositeR2RFileToPublish);
+ }
+ }
+
+ private struct Eligibility
+ {
+ [Flags]
+ private enum EligibilityEnum
+ {
+ None = 0,
+ Reference = 1,
+ HideReferenceFromComposite = 2,
+ CompileSeparately = 4,
+ CompileIntoCompositeImage = 8,
+ }
+
+ private readonly EligibilityEnum _flags;
+
+ public static Eligibility None => new Eligibility(EligibilityEnum.None);
+
+ public bool NoEligibility => _flags == EligibilityEnum.None;
+ public bool IsReference => (_flags & EligibilityEnum.Reference) == EligibilityEnum.Reference;
+ public bool ReferenceHiddenFromCompositeBuild => (_flags & EligibilityEnum.HideReferenceFromComposite) == EligibilityEnum.HideReferenceFromComposite;
+ public bool CompileIntoCompositeImage => (_flags & EligibilityEnum.CompileIntoCompositeImage) == EligibilityEnum.CompileIntoCompositeImage;
+ public bool CompileSeparately => (_flags & EligibilityEnum.CompileSeparately) == EligibilityEnum.CompileSeparately;
+ public bool Compile => CompileIntoCompositeImage || CompileSeparately;
+
+ private Eligibility(EligibilityEnum flags)
+ {
+ _flags = flags;
+ }
+
+ public static Eligibility CreateReferenceEligibility(bool hideFromCompositeBuilds)
+ {
+ if (hideFromCompositeBuilds)
+ return new Eligibility(EligibilityEnum.Reference | EligibilityEnum.HideReferenceFromComposite);
+ else
+ return new Eligibility(EligibilityEnum.Reference);
+ }
+
+ public static Eligibility CreateCompileEligibility(bool doNotBuildIntoComposite)
+ {
+ if (doNotBuildIntoComposite)
+ return new Eligibility(EligibilityEnum.Reference | EligibilityEnum.HideReferenceFromComposite | EligibilityEnum.CompileSeparately);
+ else
+ return new Eligibility(EligibilityEnum.Reference | EligibilityEnum.CompileIntoCompositeImage);
+ }
+ };
+
+ private static bool IsNonCompositeReadyToRunImage(PEReader peReader)
+ {
+ if (peReader.PEHeaders == null)
+ return false;
+
+ if (peReader.PEHeaders.CorHeader == null)
+ return false;
+
+ if ((peReader.PEHeaders.CorHeader.Flags & CorFlags.ILLibrary) == 0)
+ {
+ // This is likely a composite image, but those can't be re-r2r'd
+ return false;
+ }
+ else
+ {
+ return peReader.PEHeaders.CorHeader.ManagedNativeHeaderDirectory.Size != 0;
+ }
+ }
+
+ private static Eligibility GetInputFileEligibility(ITaskItem file, bool compositeCompile, HashSet<string> exclusionSet, HashSet<string> r2rCompositeExclusionSet)
+ {
+ // Check to see if this is a valid ILOnly image that we can compile
+ using (FileStream fs = new FileStream(file.ItemSpec, FileMode.Open, FileAccess.Read))
+ {
+ try
+ {
+ using (var pereader = new PEReader(fs))
+ {
+ if (!pereader.HasMetadata)
+ {
+ return Eligibility.None;
+ }
+
+ MetadataReader mdReader = pereader.GetMetadataReader();
+ if (!mdReader.IsAssembly)
+ {
+ return Eligibility.None;
+ }
+
+ if (IsReferenceAssembly(mdReader))
+ {
+ // crossgen can only take implementation assemblies, even as references
+ return Eligibility.None;
+ }
+
+ bool excludeFromR2R = (exclusionSet != null && exclusionSet.Contains(Path.GetFileName(file.ItemSpec)));
+ bool excludeFromComposite = (r2rCompositeExclusionSet != null && r2rCompositeExclusionSet.Contains(Path.GetFileName(file.ItemSpec))) || excludeFromR2R;
+
+ if ((pereader.PEHeaders.CorHeader.Flags & CorFlags.ILOnly) != CorFlags.ILOnly)
+ {
+ // This can happen due to C++/CLI binaries or due to previously R2R compiled binaries.
+
+ if (!IsNonCompositeReadyToRunImage(pereader))
+ {
+ // For C++/CLI always treat as only a reference
+ return Eligibility.CreateReferenceEligibility(excludeFromComposite);
+ }
+ else
+ {
+ // If previously compiled as R2R, treat as reference if this would be compiled seperately
+ if (!compositeCompile || excludeFromComposite)
+ {
+ return Eligibility.CreateReferenceEligibility(excludeFromComposite);
+ }
+ }
+ }
+
+ if (file.HasMetadataValue(MetadataKeys.ReferenceOnly, "true"))
+ {
+ return Eligibility.CreateReferenceEligibility(excludeFromComposite);
+ }
+
+ if (excludeFromR2R)
+ {
+ return Eligibility.CreateReferenceEligibility(excludeFromComposite);
+ }
+
+ // save these most expensive checks for last. We don't want to scan all references for IL code
+ if (ReferencesWinMD(mdReader) || !HasILCode(pereader, mdReader))
+ {
+ // Forwarder assemblies are not separately compiled via R2R, but when performing composite compilation, they are included in the bundle
+ if (excludeFromComposite || !compositeCompile)
+ return Eligibility.CreateReferenceEligibility(excludeFromComposite);
+ }
+
+ return Eligibility.CreateCompileEligibility(!compositeCompile || excludeFromComposite);
+ }
+ }
+ catch (BadImageFormatException)
+ {
+ // Not a valid assembly file
+ return Eligibility.None;
+ }
+ }
+ }
+
+ private static bool IsReferenceAssembly(MetadataReader mdReader)
+ {
+ foreach (var attributeHandle in mdReader.GetAssemblyDefinition().GetCustomAttributes())
+ {
+ EntityHandle attributeCtor = mdReader.GetCustomAttribute(attributeHandle).Constructor;
+
+ StringHandle attributeTypeName = default;
+ StringHandle attributeTypeNamespace = default;
+
+ if (attributeCtor.Kind == HandleKind.MemberReference)
+ {
+ EntityHandle attributeMemberParent = mdReader.GetMemberReference((MemberReferenceHandle)attributeCtor).Parent;
+ if (attributeMemberParent.Kind == HandleKind.TypeReference)
+ {
+ TypeReference attributeTypeRef = mdReader.GetTypeReference((TypeReferenceHandle)attributeMemberParent);
+ attributeTypeName = attributeTypeRef.Name;
+ attributeTypeNamespace = attributeTypeRef.Namespace;
+ }
+ }
+ else if (attributeCtor.Kind == HandleKind.MethodDefinition)
+ {
+ TypeDefinitionHandle attributeTypeDefHandle = mdReader.GetMethodDefinition((MethodDefinitionHandle)attributeCtor).GetDeclaringType();
+ TypeDefinition attributeTypeDef = mdReader.GetTypeDefinition(attributeTypeDefHandle);
+ attributeTypeName = attributeTypeDef.Name;
+ attributeTypeNamespace = attributeTypeDef.Namespace;
+ }
+
+ if (!attributeTypeName.IsNil &&
+ !attributeTypeNamespace.IsNil &&
+ mdReader.StringComparer.Equals(attributeTypeName, "ReferenceAssemblyAttribute") &&
+ mdReader.StringComparer.Equals(attributeTypeNamespace, "System.Runtime.CompilerServices"))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private static bool ReferencesWinMD(MetadataReader mdReader)
+ {
+ foreach (var assemblyRefHandle in mdReader.AssemblyReferences)
+ {
+ AssemblyReference assemblyRef = mdReader.GetAssemblyReference(assemblyRefHandle);
+ if ((assemblyRef.Flags & AssemblyFlags.WindowsRuntime) == AssemblyFlags.WindowsRuntime)
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private static bool HasILCode(PEReader peReader, MetadataReader mdReader)
+ {
+ foreach (var methoddefHandle in mdReader.MethodDefinitions)
+ {
+ MethodDefinition methodDef = mdReader.GetMethodDefinition(methoddefHandle);
+ if (methodDef.RelativeVirtualAddress > 0)
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+ }
+}
--- /dev/null
+The files in this directory are equivalent/identical to corresponding files in the sdk repo. They should be kept equivalent.
+
+The files in the subdirectory CommoonFilesPulledFromSdkRepo do not need to be updated aggressively as they change.
+The files in the subdirectory ShimFilesSimulatingLogicInSdkRepo are simply in place to make the functionality be close enough so that these tasks can easily be compiled.
\ No newline at end of file
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.ComponentModel;
+using System.IO;
+using System.Linq;
+using System.Runtime.InteropServices;
+
+using Microsoft.Build.Framework;
+using Microsoft.Build.Utilities;
+using NuGet.Versioning;
+
+namespace Microsoft.NET.Build.Tasks
+{
+ public class ResolveReadyToRunCompilers : TaskBase
+ {
+ public bool EmitSymbols { get; set; }
+ public bool ReadyToRunUseCrossgen2 { get; set; }
+
+ [Required]
+ public ITaskItem[] RuntimePacks { get; set; }
+ public ITaskItem[] Crossgen2Packs { get; set; }
+ [Required]
+ public ITaskItem[] TargetingPacks { get; set; }
+ [Required]
+ public string RuntimeGraphPath { get; set; }
+ [Required]
+ public string NETCoreSdkRuntimeIdentifier { get; set; }
+
+ [Output]
+ public ITaskItem CrossgenTool { get; set; }
+ [Output]
+ public ITaskItem Crossgen2Tool { get; set; }
+
+ internal struct CrossgenToolInfo
+ {
+ public string ToolPath;
+ public string PackagePath;
+ public string ClrJitPath;
+ public string DiaSymReaderPath;
+ }
+
+ private ITaskItem _runtimePack;
+ private ITaskItem _crossgen2Pack;
+ private string _targetRuntimeIdentifier;
+ private string _targetPlatform;
+ private string _hostRuntimeIdentifier;
+
+ private CrossgenToolInfo _crossgenTool;
+ private CrossgenToolInfo _crossgen2Tool;
+
+ private Architecture _targetArchitecture;
+ private bool _crossgen2IsVersion5;
+
+ protected override void ExecuteCore()
+ {
+ _runtimePack = GetNETCoreAppRuntimePack();
+ _crossgen2Pack = Crossgen2Packs?.FirstOrDefault();
+ _targetRuntimeIdentifier = _runtimePack?.GetMetadata(MetadataKeys.RuntimeIdentifier);
+
+ // Get the list of runtime identifiers that we support and can target
+ ITaskItem targetingPack = GetNETCoreAppTargetingPack();
+ string supportedRuntimeIdentifiers = targetingPack?.GetMetadata(MetadataKeys.RuntimePackRuntimeIdentifiers);
+
+ var runtimeGraph = new RuntimeGraphCache(this).GetRuntimeGraph(RuntimeGraphPath);
+ var supportedRIDsList = supportedRuntimeIdentifiers == null ? Array.Empty<string>() : supportedRuntimeIdentifiers.Split(';');
+
+ // Get the best RID for the host machine, which will be used to validate that we can run crossgen for the target platform and architecture
+ _hostRuntimeIdentifier = NuGetUtils.GetBestMatchingRid(
+ runtimeGraph,
+ NETCoreSdkRuntimeIdentifier,
+ supportedRIDsList,
+ out _);
+
+ if (_hostRuntimeIdentifier == null || _targetRuntimeIdentifier == null)
+ {
+ Log.LogError(Strings.ReadyToRunNoValidRuntimePackageError);
+ return;
+ }
+
+ if (ReadyToRunUseCrossgen2)
+ {
+ if (!ValidateCrossgen2Support())
+ {
+ return;
+ }
+
+ // In .NET 5 Crossgen2 did not support emitting native symbols, so we use Crossgen to emit them
+ if (_crossgen2IsVersion5 && EmitSymbols && !ValidateCrossgenSupport())
+ {
+ return;
+ }
+ }
+ else
+ {
+ if (!ValidateCrossgenSupport())
+ {
+ return;
+ }
+ }
+ }
+
+ private bool ValidateCrossgenSupport()
+ {
+ _crossgenTool.PackagePath = _runtimePack?.GetMetadata(MetadataKeys.PackageDirectory);
+ if (_crossgenTool.PackagePath == null)
+ {
+ Log.LogError(Strings.ReadyToRunNoValidRuntimePackageError);
+ return false;
+ }
+
+ if (!ExtractTargetPlatformAndArchitecture(_targetRuntimeIdentifier, out _targetPlatform, out _targetArchitecture) ||
+ !ExtractTargetPlatformAndArchitecture(_hostRuntimeIdentifier, out string hostPlatform, out Architecture hostArchitecture) ||
+ _targetPlatform != hostPlatform ||
+ !GetCrossgenComponentsPaths())
+ {
+ Log.LogError(Strings.ReadyToRunTargetNotSupportedError);
+ return false;
+ }
+
+ // Create tool task item
+ CrossgenTool = new TaskItem(_crossgenTool.ToolPath);
+ CrossgenTool.SetMetadata(MetadataKeys.JitPath, _crossgenTool.ClrJitPath);
+ if (!string.IsNullOrEmpty(_crossgenTool.DiaSymReaderPath))
+ {
+ CrossgenTool.SetMetadata(MetadataKeys.DiaSymReader, _crossgenTool.DiaSymReaderPath);
+ }
+
+ return true;
+ }
+
+ private bool ValidateCrossgen2Support()
+ {
+ _crossgen2Tool.PackagePath = _crossgen2Pack?.GetMetadata(MetadataKeys.PackageDirectory);
+ if (_crossgen2Tool.PackagePath == null ||
+ !NuGetVersion.TryParse(_crossgen2Pack.GetMetadata(MetadataKeys.NuGetPackageVersion), out NuGetVersion crossgen2PackVersion))
+ {
+ Log.LogError(Strings.ReadyToRunNoValidRuntimePackageError);
+ return false;
+ }
+
+ bool version5 = crossgen2PackVersion.Major < 6;
+ bool isSupportedTarget = ExtractTargetPlatformAndArchitecture(_targetRuntimeIdentifier, out _targetPlatform, out _targetArchitecture);
+ string targetOS = _targetPlatform switch
+ {
+ "linux" => "linux",
+ "linux-musl" => "linux",
+ "osx" => "osx",
+ "win" => "windows",
+ _ => null
+ };
+
+ // In .NET 5 Crossgen2 supported only the following host->target compilation scenarios:
+ // win-x64 -> win-x64
+ // linux-x64 -> linux-x64
+ // linux-musl-x64 -> linux-musl-x64
+ isSupportedTarget = isSupportedTarget &&
+ targetOS != null &&
+ (!version5 || _targetRuntimeIdentifier == _hostRuntimeIdentifier) &&
+ GetCrossgen2ComponentsPaths(version5);
+
+ if (!isSupportedTarget)
+ {
+ Log.LogError(Strings.ReadyToRunTargetNotSupportedError);
+ return false;
+ }
+
+ // Create tool task item
+ Crossgen2Tool = new TaskItem(_crossgen2Tool.ToolPath);
+ Crossgen2Tool.SetMetadata(MetadataKeys.IsVersion5, version5.ToString());
+ if (version5)
+ {
+ Crossgen2Tool.SetMetadata(MetadataKeys.JitPath, _crossgen2Tool.ClrJitPath);
+ }
+ else
+ {
+ Crossgen2Tool.SetMetadata(MetadataKeys.TargetOS, targetOS);
+ Crossgen2Tool.SetMetadata(MetadataKeys.TargetArch, ArchitectureToString(_targetArchitecture));
+ }
+
+ _crossgen2IsVersion5 = version5;
+ return true;
+ }
+
+ private ITaskItem GetNETCoreAppRuntimePack()
+ {
+ return GetNETCoreAppPack(RuntimePacks, MetadataKeys.FrameworkName);
+ }
+
+ private ITaskItem GetNETCoreAppTargetingPack()
+ {
+ return GetNETCoreAppPack(TargetingPacks, MetadataKeys.RuntimeFrameworkName);
+ }
+
+ private static ITaskItem GetNETCoreAppPack(ITaskItem[] packs, string metadataKey)
+ {
+ return packs.SingleOrDefault(
+ pack => pack.GetMetadata(metadataKey)
+ .Equals("Microsoft.NETCore.App", StringComparison.OrdinalIgnoreCase));
+ }
+
+ private static bool ExtractTargetPlatformAndArchitecture(string runtimeIdentifier, out string platform, out Architecture architecture)
+ {
+ platform = null;
+ architecture = default;
+
+ // This will split RID like "linux-musl-arm64" into "linux-musl" and "arm64" components
+ int separator = runtimeIdentifier.LastIndexOf('-');
+ if (separator < 0)
+ {
+ return false;
+ }
+
+ string architectureStr = runtimeIdentifier.Substring(separator + 1).ToLowerInvariant();
+
+ switch (architectureStr)
+ {
+ case "arm":
+ architecture = Architecture.Arm;
+ break;
+ case "arm64":
+ architecture = Architecture.Arm64;
+ break;
+ case "x64":
+ architecture = Architecture.X64;
+ break;
+ case "x86":
+ architecture = Architecture.X86;
+ break;
+ default:
+ return false;
+ }
+
+ platform = runtimeIdentifier.Substring(0, separator).ToLowerInvariant();
+ return true;
+ }
+
+ private bool GetCrossgenComponentsPaths()
+ {
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+ {
+ if (_targetArchitecture == Architecture.Arm)
+ {
+ if (RuntimeInformation.OSArchitecture == _targetArchitecture)
+ {
+ // We can run native arm32 bits on an arm64 host in WOW mode
+ _crossgenTool.ToolPath = Path.Combine(_crossgenTool.PackagePath, "tools", "crossgen.exe");
+ _crossgenTool.ClrJitPath = Path.Combine(_crossgenTool.PackagePath, "runtimes", _targetRuntimeIdentifier, "native", "clrjit.dll");
+ _crossgenTool.DiaSymReaderPath = Path.Combine(_crossgenTool.PackagePath, "runtimes", _targetRuntimeIdentifier, "native", "Microsoft.DiaSymReader.Native.arm.dll");
+ }
+ else
+ {
+ // We can use the x86-hosted crossgen compiler to target ARM
+ _crossgenTool.ToolPath = Path.Combine(_crossgenTool.PackagePath, "tools", "x86_arm", "crossgen.exe");
+ _crossgenTool.ClrJitPath = Path.Combine(_crossgenTool.PackagePath, "runtimes", "x86_arm", "native", "clrjit.dll");
+ _crossgenTool.DiaSymReaderPath = Path.Combine(_crossgenTool.PackagePath, "runtimes", "x86_arm", "native", "Microsoft.DiaSymReader.Native.x86.dll");
+ }
+ }
+ else if (_targetArchitecture == Architecture.Arm64)
+ {
+ if (RuntimeInformation.OSArchitecture == _targetArchitecture)
+ {
+ _crossgenTool.ToolPath = Path.Combine(_crossgenTool.PackagePath, "tools", "crossgen.exe");
+ _crossgenTool.ClrJitPath = Path.Combine(_crossgenTool.PackagePath, "runtimes", _targetRuntimeIdentifier, "native", "clrjit.dll");
+ _crossgenTool.DiaSymReaderPath = Path.Combine(_crossgenTool.PackagePath, "runtimes", _targetRuntimeIdentifier, "native", "Microsoft.DiaSymReader.Native.arm64.dll");
+ }
+ else
+ {
+ // We only have 64-bit hosted compilers for ARM64.
+ if (RuntimeInformation.OSArchitecture != Architecture.X64)
+ {
+ return false;
+ }
+
+ _crossgenTool.ToolPath = Path.Combine(_crossgenTool.PackagePath, "tools", "x64_arm64", "crossgen.exe");
+ _crossgenTool.ClrJitPath = Path.Combine(_crossgenTool.PackagePath, "runtimes", "x64_arm64", "native", "clrjit.dll");
+ _crossgenTool.DiaSymReaderPath = Path.Combine(_crossgenTool.PackagePath, "runtimes", "x64_arm64", "native", "Microsoft.DiaSymReader.Native.amd64.dll");
+ }
+ }
+ else
+ {
+ _crossgenTool.ToolPath = Path.Combine(_crossgenTool.PackagePath, "tools", "crossgen.exe");
+ _crossgenTool.ClrJitPath = Path.Combine(_crossgenTool.PackagePath, "runtimes", _targetRuntimeIdentifier, "native", "clrjit.dll");
+ if (_targetArchitecture == Architecture.X64)
+ {
+ _crossgenTool.DiaSymReaderPath = Path.Combine(_crossgenTool.PackagePath, "runtimes", _targetRuntimeIdentifier, "native", "Microsoft.DiaSymReader.Native.amd64.dll");
+ }
+ else
+ {
+ _crossgenTool.DiaSymReaderPath = Path.Combine(_crossgenTool.PackagePath, "runtimes", _targetRuntimeIdentifier, "native", "Microsoft.DiaSymReader.Native.x86.dll");
+ }
+ }
+ }
+ else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
+ {
+ if (_targetArchitecture == Architecture.Arm || _targetArchitecture == Architecture.Arm64)
+ {
+ if (RuntimeInformation.OSArchitecture == _targetArchitecture)
+ {
+ _crossgenTool.ToolPath = Path.Combine(_crossgenTool.PackagePath, "tools", "crossgen");
+ _crossgenTool.ClrJitPath = Path.Combine(_crossgenTool.PackagePath, "runtimes", _targetRuntimeIdentifier, "native", "libclrjit.so");
+ }
+ else if (RuntimeInformation.OSArchitecture == Architecture.X64)
+ {
+ string xarchPath = (_targetArchitecture == Architecture.Arm ? "x64_arm" : "x64_arm64");
+ _crossgenTool.ToolPath = Path.Combine(_crossgenTool.PackagePath, "tools", xarchPath, "crossgen");
+ _crossgenTool.ClrJitPath = Path.Combine(_crossgenTool.PackagePath, "runtimes", xarchPath, "native", "libclrjit.so");
+ }
+ else
+ {
+ return false;
+ }
+ }
+ else
+ {
+ _crossgenTool.ToolPath = Path.Combine(_crossgenTool.PackagePath, "tools", "crossgen");
+ _crossgenTool.ClrJitPath = Path.Combine(_crossgenTool.PackagePath, "runtimes", _targetRuntimeIdentifier, "native", "libclrjit.so");
+ }
+ }
+ else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
+ {
+ // Only x64 supported for OSX
+ if (_targetArchitecture != Architecture.X64 || RuntimeInformation.OSArchitecture != Architecture.X64)
+ {
+ return false;
+ }
+
+ _crossgenTool.ToolPath = Path.Combine(_crossgenTool.PackagePath, "tools", "crossgen");
+ _crossgenTool.ClrJitPath = Path.Combine(_crossgenTool.PackagePath, "runtimes", _targetRuntimeIdentifier, "native", "libclrjit.dylib");
+ }
+ else
+ {
+ // Unknown platform
+ return false;
+ }
+
+ return File.Exists(_crossgenTool.ToolPath) && File.Exists(_crossgenTool.ClrJitPath);
+ }
+
+ private bool GetCrossgen2ComponentsPaths(bool version5)
+ {
+ string toolFileName, v5_clrJitFileNamePattern;
+
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+ {
+ toolFileName = "crossgen2.exe";
+ v5_clrJitFileNamePattern = "clrjit-{0}.dll";
+ }
+ else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
+ {
+ toolFileName = "crossgen2";
+ v5_clrJitFileNamePattern = "libclrjit-{0}.so";
+ }
+ else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
+ {
+ toolFileName = "crossgen2";
+ v5_clrJitFileNamePattern = "libclrjit-{0}.dylib";
+ }
+ else
+ {
+ // Unknown platform
+ return false;
+ }
+
+ if (version5)
+ {
+ string clrJitFileName = string.Format(v5_clrJitFileNamePattern, GetTargetSpecForVersion5());
+ _crossgen2Tool.ClrJitPath = Path.Combine(_crossgen2Tool.PackagePath, "tools", clrJitFileName);
+ if (!File.Exists(_crossgen2Tool.ClrJitPath))
+ {
+ return false;
+ }
+ }
+
+ _crossgen2Tool.ToolPath = Path.Combine(_crossgen2Tool.PackagePath, "tools", toolFileName);
+ return File.Exists(_crossgen2Tool.ToolPath);
+ }
+
+ // Keep in sync with JitConfigProvider.GetTargetSpec in .NET 5
+ private string GetTargetSpecForVersion5()
+ {
+ string targetOSComponent = (_targetPlatform == "win" ? "win" : "unix");
+ string targetArchComponent = ArchitectureToString(_targetArchitecture);
+ return targetOSComponent + '-' + targetArchComponent;
+ }
+
+ private static string ArchitectureToString(Architecture architecture)
+ {
+ return architecture switch
+ {
+ Architecture.X86 => "x86",
+ Architecture.X64 => "x64",
+ Architecture.Arm => "arm",
+ Architecture.Arm64 => "arm64",
+ _ => null
+ };
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.IO;
+using System.Runtime.InteropServices;
+using System.Text;
+
+using Microsoft.Build.Framework;
+using Microsoft.Build.Utilities;
+
+namespace Microsoft.NET.Build.Tasks
+{
+ public class RunReadyToRunCompiler : ToolTask
+ {
+ public ITaskItem CrossgenTool { get; set; }
+ public ITaskItem Crossgen2Tool { get; set; }
+
+ [Required]
+ public ITaskItem CompilationEntry { get; set; }
+ [Required]
+ public ITaskItem[] ImplementationAssemblyReferences { get; set; }
+ public ITaskItem[] ReadyToRunCompositeBuildReferences { get; set; }
+ public ITaskItem[] ReadyToRunCompositeBuildInput { get; set; }
+ public bool ShowCompilerWarnings { get; set; }
+ public bool UseCrossgen2 { get; set; }
+ public string Crossgen2ExtraCommandLineArgs { get; set; }
+
+ [Output]
+ public bool WarningsDetected { get; set; }
+
+ private bool _emitSymbols;
+ private string _inputAssembly;
+ private string _outputR2RImage;
+ private string _outputPDBImage;
+ private string _createPDBCommand;
+ private bool _createCompositeImage;
+
+ private bool IsPdbCompilation => !string.IsNullOrEmpty(_createPDBCommand);
+ private bool ActuallyUseCrossgen2 => UseCrossgen2 && !IsPdbCompilation;
+
+ private string DotNetHostPath => Crossgen2Tool?.GetMetadata(MetadataKeys.DotNetHostPath);
+
+ private bool Crossgen2IsVersion5
+ {
+ get
+ {
+ string version5 = Crossgen2Tool?.GetMetadata(MetadataKeys.IsVersion5);
+ return !string.IsNullOrEmpty(version5) && bool.Parse(version5);
+ }
+ }
+
+ protected override string ToolName
+ {
+ get
+ {
+ if (ActuallyUseCrossgen2)
+ {
+ string hostPath = DotNetHostPath;
+ if (!string.IsNullOrEmpty(hostPath))
+ {
+ return hostPath;
+ }
+ return Crossgen2Tool.ItemSpec;
+ }
+ return CrossgenTool.ItemSpec;
+ }
+ }
+
+ protected override string GenerateFullPathToTool() => ToolName;
+
+ private string DiaSymReader => CrossgenTool.GetMetadata(MetadataKeys.DiaSymReader);
+
+ public RunReadyToRunCompiler()
+ {
+ LogStandardErrorAsError = true;
+ }
+
+ protected override bool ValidateParameters()
+ {
+ string emitSymbolsMetadata = CompilationEntry.GetMetadata(MetadataKeys.EmitSymbols);
+ _emitSymbols = !string.IsNullOrEmpty(emitSymbolsMetadata) && bool.Parse(emitSymbolsMetadata);
+ _createPDBCommand = CompilationEntry.GetMetadata(MetadataKeys.CreatePDBCommand);
+ string createCompositeImageMetadata = CompilationEntry.GetMetadata(MetadataKeys.CreateCompositeImage);
+ _createCompositeImage = !string.IsNullOrEmpty(createCompositeImageMetadata) && bool.Parse(createCompositeImageMetadata);
+
+ if (IsPdbCompilation && CrossgenTool == null)
+ {
+ // PDB compilation is a step specific to Crossgen1 and 5.0 Crossgen2
+ // which didn't support PDB generation. 6.0 Crossgen2 produces symbols
+ // directly during native compilation.
+ Log.LogError(Strings.CrossgenToolMissingInPDBCompilationMode);
+ return false;
+ }
+
+ if (ActuallyUseCrossgen2)
+ {
+ if (Crossgen2Tool == null)
+ {
+ Log.LogError(Strings.Crossgen2ToolMissingWhenUseCrossgen2IsSet);
+ return false;
+ }
+ if (!File.Exists(Crossgen2Tool.ItemSpec))
+ {
+ Log.LogError(Strings.Crossgen2ToolExecutableNotFound, Crossgen2Tool.ItemSpec);
+ return false;
+ }
+ string hostPath = DotNetHostPath;
+ if (!string.IsNullOrEmpty(hostPath) && !File.Exists(hostPath))
+ {
+ Log.LogError(Strings.DotNetHostExecutableNotFound, hostPath);
+ return false;
+ }
+ string jitPath = Crossgen2Tool.GetMetadata(MetadataKeys.JitPath);
+ if (!string.IsNullOrEmpty(jitPath))
+ {
+ if (!File.Exists(jitPath))
+ {
+ Log.LogError(Strings.JitLibraryNotFound, jitPath);
+ return false;
+ }
+ }
+ else if (Crossgen2IsVersion5)
+ {
+ // We expect JitPath to be set for .NET 5 and {TargetOS, TargetArch} to be set for .NET 6 and later
+ Log.LogError(Strings.Crossgen2MissingRequiredMetadata, MetadataKeys.JitPath);
+ return false;
+ }
+ else
+ {
+ // For smooth switchover we accept both JitPath and TargetOS / TargetArch in .NET 6 Crossgen2
+ if (string.IsNullOrEmpty(Crossgen2Tool.GetMetadata(MetadataKeys.TargetOS)))
+ {
+ Log.LogError(Strings.Crossgen2MissingRequiredMetadata, MetadataKeys.TargetOS);
+ return false;
+ }
+ if (string.IsNullOrEmpty(Crossgen2Tool.GetMetadata(MetadataKeys.TargetArch)))
+ {
+ Log.LogError(Strings.Crossgen2MissingRequiredMetadata, MetadataKeys.TargetArch);
+ return false;
+ }
+ }
+ }
+ else
+ {
+ if (CrossgenTool == null)
+ {
+ Log.LogError(Strings.CrossgenToolMissingWhenUseCrossgen2IsNotSet);
+ return false;
+ }
+ if (!File.Exists(CrossgenTool.ItemSpec))
+ {
+ Log.LogError(Strings.CrossgenToolExecutableNotFound, CrossgenTool.ItemSpec);
+ return false;
+ }
+ if (!File.Exists(CrossgenTool.GetMetadata(MetadataKeys.JitPath)))
+ {
+ Log.LogError(Strings.JitLibraryNotFound, MetadataKeys.JitPath);
+ return false;
+ }
+ }
+
+ _outputPDBImage = CompilationEntry.GetMetadata(MetadataKeys.OutputPDBImage);
+
+ if (IsPdbCompilation)
+ {
+ _outputR2RImage = CompilationEntry.ItemSpec;
+
+ if (!string.IsNullOrEmpty(DiaSymReader) && !File.Exists(DiaSymReader))
+ {
+ Log.LogError(Strings.DiaSymReaderLibraryNotFound, DiaSymReader);
+ return false;
+ }
+
+ // R2R image has to be created before emitting native symbols (crossgen needs this as an input argument)
+ if (string.IsNullOrEmpty(_outputPDBImage))
+ {
+ Log.LogError(Strings.MissingOutputPDBImagePath);
+ }
+
+ if (!File.Exists(_outputR2RImage))
+ {
+ Log.LogError(Strings.PDBGeneratorInputExecutableNotFound, _outputR2RImage);
+ return false;
+ }
+ }
+ else
+ {
+ _outputR2RImage = CompilationEntry.GetMetadata(MetadataKeys.OutputR2RImage);
+
+ if (!_createCompositeImage)
+ {
+ _inputAssembly = CompilationEntry.ItemSpec;
+ if (!File.Exists(_inputAssembly))
+ {
+ Log.LogError(Strings.InputAssemblyNotFound, _inputAssembly);
+ return false;
+ }
+ }
+ else
+ {
+ _inputAssembly = "CompositeImage";
+ }
+
+ if (string.IsNullOrEmpty(_outputR2RImage))
+ {
+ Log.LogError(Strings.MissingOutputR2RImageFileName);
+ return false;
+ }
+
+ if (_emitSymbols && string.IsNullOrEmpty(_outputPDBImage))
+ {
+ Log.LogError(Strings.MissingOutputPDBImagePath);
+ }
+ }
+
+ return true;
+ }
+
+ private string GetAssemblyReferencesCommands()
+ {
+ StringBuilder result = new StringBuilder();
+
+ var references = _createCompositeImage ? ReadyToRunCompositeBuildReferences : ImplementationAssemblyReferences;
+
+ if (references != null)
+ {
+ foreach (var reference in (_createCompositeImage ? ReadyToRunCompositeBuildReferences : ImplementationAssemblyReferences))
+ {
+ // When generating PDBs, we must not add a reference to the IL version of the R2R image for which we're trying to generate a PDB
+ if (IsPdbCompilation && string.Equals(Path.GetFileName(reference.ItemSpec), Path.GetFileName(_outputR2RImage), StringComparison.OrdinalIgnoreCase))
+ continue;
+
+ if (UseCrossgen2 && !IsPdbCompilation)
+ {
+ result.AppendLine($"-r:\"{reference}\"");
+ }
+ else
+ {
+ result.AppendLine($"-r \"{reference}\"");
+ }
+ }
+ }
+
+ return result.ToString();
+ }
+
+ protected override string GenerateCommandLineCommands()
+ {
+ if (ActuallyUseCrossgen2 && !string.IsNullOrEmpty(DotNetHostPath))
+ {
+ return $"\"{Crossgen2Tool.ItemSpec}\"";
+ }
+ return null;
+ }
+
+ protected override string GenerateResponseFileCommands()
+ {
+ // Crossgen2 5.0 doesn't support PDB generation so Crossgen1 is used for that purpose.
+ if (ActuallyUseCrossgen2)
+ {
+ return GenerateCrossgen2ResponseFile();
+ }
+ else
+ {
+ return GenerateCrossgenResponseFile();
+ }
+ }
+
+ private string GenerateCrossgenResponseFile()
+ {
+ StringBuilder result = new StringBuilder();
+
+ result.AppendLine("/nologo");
+
+ if (IsPdbCompilation)
+ {
+ result.Append(GetAssemblyReferencesCommands());
+
+ if (!string.IsNullOrEmpty(DiaSymReader))
+ {
+ result.AppendLine($"/DiasymreaderPath \"{DiaSymReader}\"");
+ }
+
+ result.AppendLine(_createPDBCommand);
+ result.AppendLine($"\"{_outputR2RImage}\"");
+ }
+ else
+ {
+ result.AppendLine("/MissingDependenciesOK");
+ result.AppendLine($"/JITPath \"{CrossgenTool.GetMetadata(MetadataKeys.JitPath)}\"");
+ result.Append(GetAssemblyReferencesCommands());
+ result.AppendLine($"/out \"{_outputR2RImage}\"");
+ result.AppendLine($"\"{_inputAssembly}\"");
+ }
+
+ return result.ToString();
+ }
+
+ private string GenerateCrossgen2ResponseFile()
+ {
+ StringBuilder result = new StringBuilder();
+
+ string jitPath = Crossgen2Tool.GetMetadata(MetadataKeys.JitPath);
+ if (!string.IsNullOrEmpty(jitPath))
+ {
+ result.AppendLine($"--jitpath:\"{jitPath}\"");
+ }
+ else
+ {
+ result.AppendLine($"--targetos:{Crossgen2Tool.GetMetadata(MetadataKeys.TargetOS)}");
+ result.AppendLine($"--targetarch:{Crossgen2Tool.GetMetadata(MetadataKeys.TargetArch)}");
+ }
+
+ result.AppendLine("-O");
+
+ // 5.0 Crossgen2 doesn't support PDB generation.
+ if (!Crossgen2IsVersion5 && _emitSymbols)
+ {
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+ {
+ result.AppendLine("--pdb");
+ result.AppendLine($"--pdb-path:{Path.GetDirectoryName(_outputPDBImage)}");
+ }
+ else
+ {
+ result.AppendLine("--perfmap");
+ result.AppendLine($"--perfmap-path:{Path.GetDirectoryName(_outputPDBImage)}");
+ }
+ }
+
+ if (!string.IsNullOrEmpty(Crossgen2ExtraCommandLineArgs))
+ {
+ foreach (string extraArg in Crossgen2ExtraCommandLineArgs.Split(new char[]{';'}, StringSplitOptions.RemoveEmptyEntries))
+ {
+ result.AppendLine(extraArg);
+ }
+ }
+
+ if (_createCompositeImage)
+ {
+ result.AppendLine("--composite");
+
+ // Crossgen2 v5 only supported compilation with --inputbubble specified
+ if (Crossgen2IsVersion5)
+ result.AppendLine("--inputbubble");
+
+ result.AppendLine($"--out:\"{_outputR2RImage}\"");
+
+ result.Append(GetAssemblyReferencesCommands());
+
+ // Note: do not add double quotes around the input assembly, even if the file path contains spaces. The command line
+ // parsing logic will append this string to the working directory if it's a relative path, so any double quotes will result in errors.
+ foreach (var reference in ReadyToRunCompositeBuildInput)
+ {
+ result.AppendLine(reference.ItemSpec);
+ }
+ }
+ else
+ {
+ result.Append(GetAssemblyReferencesCommands());
+ result.AppendLine($"--out:\"{_outputR2RImage}\"");
+
+ // Note: do not add double quotes around the input assembly, even if the file path contains spaces. The command line
+ // parsing logic will append this string to the working directory if it's a relative path, so any double quotes will result in errors.
+ result.AppendLine($"{_inputAssembly}");
+ }
+
+ return result.ToString();
+ }
+
+ protected override int ExecuteTool(string pathToTool, string responseFileCommands, string commandLineCommands)
+ {
+ // Ensure output sub-directories exists - Crossgen does not create directories for output files. Any relative path used with the
+ // '/out' parameter has to have an existing directory.
+ Directory.CreateDirectory(Path.GetDirectoryName(_outputR2RImage));
+
+ WarningsDetected = false;
+
+ return base.ExecuteTool(pathToTool, responseFileCommands, commandLineCommands);
+ }
+
+ protected override void LogEventsFromTextOutput(string singleLine, MessageImportance messageImportance)
+ {
+ if (!ShowCompilerWarnings && singleLine.IndexOf("warning:", StringComparison.OrdinalIgnoreCase) != -1)
+ {
+ Log.LogMessage(MessageImportance.Normal, singleLine);
+ WarningsDetected = true;
+ }
+ else
+ {
+ base.LogEventsFromTextOutput(singleLine, messageImportance);
+ }
+ }
+ }
+}
--- /dev/null
+<!--
+***********************************************************************************************
+Microsoft.NET.CrossGen.targets
+
+WARNING: DO NOT MODIFY this file unless you are knowledgeable about MSBuild and have
+ created a backup copy. Incorrect changes to this file will make it
+ impossible to load or build your projects from the command-line or the IDE.
+
+Copyright (c) .NET Foundation. All rights reserved.
+***********************************************************************************************
+-->
+<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <PropertyGroup>
+ <Crossgen2TasksOverriden>true</Crossgen2TasksOverriden>
+ </PropertyGroup>
+
+ <UsingTask TaskName="PrepareForReadyToRunCompilation" AssemblyFile="$(MSBuildThisFileDirectory)Crossgen2Tasks.dll" />
+ <UsingTask TaskName="ResolveReadyToRunCompilers" AssemblyFile="$(MSBuildThisFileDirectory)Crossgen2Tasks.dll" />
+ <UsingTask TaskName="RunReadyToRunCompiler" AssemblyFile="$(MSBuildThisFileDirectory)Crossgen2Tasks.dll" />
+</Project>
\ No newline at end of file
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using Microsoft.Build.Framework;
+using System;
+using System.IO;
+
+namespace Microsoft.NET.Build.Tasks
+{
+ internal static partial class ItemUtilities
+ {
+ public static bool? GetBooleanMetadata(this ITaskItem item, string metadataName)
+ {
+ bool? result = null;
+
+ string value = item.GetMetadata(metadataName);
+ bool parsedResult;
+ if (bool.TryParse(value, out parsedResult))
+ {
+ result = parsedResult;
+ }
+
+ return result;
+ }
+
+ public static bool HasMetadataValue(this ITaskItem item, string name)
+ {
+ string value = item.GetMetadata(name);
+
+ return !string.IsNullOrEmpty(value);
+ }
+
+ public static bool HasMetadataValue(this ITaskItem item, string name, string expectedValue)
+ {
+ string value = item.GetMetadata(name);
+
+ return string.Equals(value, expectedValue, StringComparison.OrdinalIgnoreCase);
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace Microsoft.NET.Build.Tasks
+{
+ public static class Strings
+ {
+ public static string Crossgen5CannotEmitSymbolsInCompositeMode = "Crossgen5CannotEmitSymbolsInCompositeMode";
+ public static string Crossgen2ToolMissingWhenUseCrossgen2IsSet = "Crossgen2ToolMissingWhenUseCrossgen2IsSet";
+ public static string Crossgen2ToolExecutableNotFound = "Crossgen2ToolExecutableNotFound";
+ public static string DotNetHostExecutableNotFound = "DotNetHostExecutableNotFound";
+ public static string JitLibraryNotFound = "JitLibraryNotFound";
+ public static string Crossgen2MissingRequiredMetadata = "Crossgen2MissingRequiredMetadata";
+ public static string CrossgenToolMissingWhenUseCrossgen2IsNotSet = "CrossgenToolMissingWhenUseCrossgen2IsNotSet";
+ public static string DiaSymReaderLibraryNotFound = "DiaSymReaderLibraryNotFound";
+ public static string MissingOutputPDBImagePath = "MissingOutputPDBImagePath";
+ public static string PDBGeneratorInputExecutableNotFound = "PDBGeneratorInputExecutableNotFound";
+ public static string InputAssemblyNotFound = "InputAssemblyNotFound";
+ public static string MissingOutputR2RImageFileName = "MissingOutputR2RImageFileName";
+ public static string CrossgenToolMissingInPDBCompilationMode = "CrossgenToolMissingInPDBCompilationMode";
+ public static string CrossgenToolExecutableNotFound = "CrossgenToolExecutableNotFound";
+ public static string ReadyToRunNoValidRuntimePackageError = "ReadyToRunNoValidRuntimePackageError";
+ public static string ReadyToRunTargetNotSupportedError = "ReadyToRunTargetNotSupportedError";
+ }
+}
\ No newline at end of file