From 35acbef10015720903a336add3727175d8c4947b Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 24 Oct 2024 14:52:38 -0700 Subject: [PATCH] Add SDK support for rooting subsets of inputs in a composite image. (#44436) --- .../PrepareForReadyToRunCompilation.cs | 37 +++++++++++++++---- .../RunReadyToRunCompiler.cs | 9 +++++ .../targets/Microsoft.NET.CrossGen.targets | 7 +++- 3 files changed, 44 insertions(+), 9 deletions(-) diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/PrepareForReadyToRunCompilation.cs b/src/Tasks/Microsoft.NET.Build.Tasks/PrepareForReadyToRunCompilation.cs index 3bfbad774ee2..0f0dcf9edd02 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/PrepareForReadyToRunCompilation.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/PrepareForReadyToRunCompilation.cs @@ -26,6 +26,11 @@ public class PrepareForReadyToRunCompilation : TaskBase public string[] PublishReadyToRunCompositeExclusions { get; set; } + // When specified, only these assemblies will be fully compiled into the composite image. + // All other input (non-reference) assemblies will only have code compiled for methods + // called by a method in a rooted assembly (possibly transitively). + public string[] PublishReadyToRunCompositeRoots { get; set; } + public ITaskItem CrossgenTool { get; set; } public ITaskItem Crossgen2Tool { get; set; } @@ -50,6 +55,9 @@ public class PrepareForReadyToRunCompilation : TaskBase [Output] public ITaskItem[] ReadyToRunCompositeBuildInput => _r2rCompositeInput.ToArray(); + [Output] + public ITaskItem[] ReadyToRunCompositeUnrootedBuildInput => _r2rCompositeUnrootedInput.ToArray(); + private bool _crossgen2IsVersion5; private int _perfmapFormatVersion; @@ -59,6 +67,7 @@ public class PrepareForReadyToRunCompilation : TaskBase private List _r2rReferences = new(); private List _r2rCompositeReferences = new(); private List _r2rCompositeInput = new(); + private List _r2rCompositeUnrootedInput = new(); private bool IsTargetWindows { @@ -108,7 +117,7 @@ protected override void ExecuteCore() !string.IsNullOrEmpty(diaSymReaderPath) && File.Exists(diaSymReaderPath); // Process input lists of files - ProcessInputFileList(Assemblies, _compileList, _symbolsCompileList, _r2rFiles, _r2rReferences, _r2rCompositeReferences, _r2rCompositeInput, hasValidDiaSymReaderLib); + ProcessInputFileList(Assemblies, _compileList, _symbolsCompileList, _r2rFiles, _r2rReferences, _r2rCompositeReferences, _r2rCompositeInput, _r2rCompositeUnrootedInput, hasValidDiaSymReaderLib); } private void ProcessInputFileList( @@ -119,6 +128,7 @@ private void ProcessInputFileList( List r2rReferenceList, List r2rCompositeReferenceList, List r2rCompositeInputList, + List r2rCompositeUnrootedInput, bool hasValidDiaSymReaderLib) { if (inputFiles == null) @@ -128,10 +138,11 @@ private void ProcessInputFileList( var exclusionSet = ExcludeList == null || Crossgen2Composite ? null : new HashSet(ExcludeList, StringComparer.OrdinalIgnoreCase); var compositeExclusionSet = PublishReadyToRunCompositeExclusions == null || !Crossgen2Composite ? null : new HashSet(PublishReadyToRunCompositeExclusions, StringComparer.OrdinalIgnoreCase); + var compositeRootSet = PublishReadyToRunCompositeRoots == null || !Crossgen2Composite ? null : new HashSet(PublishReadyToRunCompositeRoots, StringComparer.OrdinalIgnoreCase); foreach (var file in inputFiles) { - var eligibility = GetInputFileEligibility(file, Crossgen2Composite, exclusionSet, compositeExclusionSet); + var eligibility = GetInputFileEligibility(file, Crossgen2Composite, exclusionSet, compositeExclusionSet, compositeRootSet); if (eligibility.NoEligibility) { @@ -205,6 +216,10 @@ private void ProcessInputFileList( r2rCompilationEntry.RemoveMetadata(MetadataKeys.OriginalItemSpec); imageCompilationList.Add(r2rCompilationEntry); } + else if (eligibility.CompileUnrootedIntoCompositeImage) + { + r2rCompositeUnrootedInput.Add(file); + } else if (eligibility.CompileIntoCompositeImage) { r2rCompositeInputList.Add(file); @@ -334,6 +349,7 @@ private enum EligibilityEnum HideReferenceFromComposite = 2, CompileSeparately = 4, CompileIntoCompositeImage = 8, + CompileUnrootedIntoCompositeImage = 16, } private readonly EligibilityEnum _flags; @@ -344,8 +360,9 @@ private enum EligibilityEnum 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 CompileUnrootedIntoCompositeImage => (_flags & EligibilityEnum.CompileUnrootedIntoCompositeImage) == EligibilityEnum.CompileUnrootedIntoCompositeImage; public bool CompileSeparately => (_flags & EligibilityEnum.CompileSeparately) == EligibilityEnum.CompileSeparately; - public bool Compile => CompileIntoCompositeImage || CompileSeparately; + public bool Compile => CompileIntoCompositeImage || CompileUnrootedIntoCompositeImage || CompileSeparately; private Eligibility(EligibilityEnum flags) { @@ -360,12 +377,14 @@ public static Eligibility CreateReferenceEligibility(bool hideFromCompositeBuild return new Eligibility(EligibilityEnum.Reference); } - public static Eligibility CreateCompileEligibility(bool doNotBuildIntoComposite) + public static Eligibility CreateCompileEligibility(bool doNotBuildIntoComposite, bool rootedInComposite) { if (doNotBuildIntoComposite) return new Eligibility(EligibilityEnum.Reference | EligibilityEnum.HideReferenceFromComposite | EligibilityEnum.CompileSeparately); - else + else if (rootedInComposite) return new Eligibility(EligibilityEnum.Reference | EligibilityEnum.CompileIntoCompositeImage); + else + return new Eligibility(EligibilityEnum.Reference | EligibilityEnum.CompileUnrootedIntoCompositeImage); } }; @@ -388,7 +407,7 @@ private static bool IsNonCompositeReadyToRunImage(PEReader peReader) } } - private static Eligibility GetInputFileEligibility(ITaskItem file, bool compositeCompile, HashSet exclusionSet, HashSet r2rCompositeExclusionSet) + private static Eligibility GetInputFileEligibility(ITaskItem file, bool compositeCompile, HashSet exclusionSet, HashSet r2rCompositeExclusionSet, HashSet r2rCompositeRootSet) { // Check to see if this is a valid ILOnly image that we can compile if (!file.ItemSpec.EndsWith(".dll", StringComparison.OrdinalIgnoreCase) && !file.ItemSpec.EndsWith(".exe", StringComparison.OrdinalIgnoreCase)) @@ -423,6 +442,10 @@ private static Eligibility GetInputFileEligibility(ITaskItem file, bool composit bool excludeFromR2R = (exclusionSet != null && exclusionSet.Contains(Path.GetFileName(file.ItemSpec))); bool excludeFromComposite = (r2rCompositeExclusionSet != null && r2rCompositeExclusionSet.Contains(Path.GetFileName(file.ItemSpec))) || excludeFromR2R; + // Default to rooting all assemblies. + // If a root set is specified, only root if in the set. + bool rootedInComposite = (r2rCompositeRootSet == null || r2rCompositeRootSet.Contains(Path.GetFileName(file.ItemSpec))); + if ((pereader.PEHeaders.CorHeader.Flags & CorFlags.ILOnly) != CorFlags.ILOnly) { // This can happen due to C++/CLI binaries or due to previously R2R compiled binaries. @@ -460,7 +483,7 @@ private static Eligibility GetInputFileEligibility(ITaskItem file, bool composit return Eligibility.CreateReferenceEligibility(excludeFromComposite); } - return Eligibility.CreateCompileEligibility(!compositeCompile || excludeFromComposite); + return Eligibility.CreateCompileEligibility(!compositeCompile || excludeFromComposite, rootedInComposite); } } catch (BadImageFormatException) diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/RunReadyToRunCompiler.cs b/src/Tasks/Microsoft.NET.Build.Tasks/RunReadyToRunCompiler.cs index b9613876d36c..d8b4692f187d 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/RunReadyToRunCompiler.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/RunReadyToRunCompiler.cs @@ -17,6 +17,7 @@ public class RunReadyToRunCompiler : ToolTask public ITaskItem[] ImplementationAssemblyReferences { get; set; } public ITaskItem[] ReadyToRunCompositeBuildReferences { get; set; } public ITaskItem[] ReadyToRunCompositeBuildInput { get; set; } + public ITaskItem[] ReadyToRunCompositeUnrootedBuildInput { get; set; } public bool ShowCompilerWarnings { get; set; } public bool UseCrossgen2 { get; set; } public string Crossgen2ExtraCommandLineArgs { get; set; } @@ -365,6 +366,14 @@ private string GenerateCrossgen2ResponseFile() { result.AppendLine(reference.ItemSpec); } + + if (ReadyToRunCompositeUnrootedBuildInput != null) + { + foreach (var unrooted in ReadyToRunCompositeUnrootedBuildInput) + { + result.AppendLine($"-u:\"{unrooted.ItemSpec}\""); + } + } } else { diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.CrossGen.targets b/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.CrossGen.targets index 99b4af26c808..d3530276ad12 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.CrossGen.targets +++ b/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.CrossGen.targets @@ -421,7 +421,8 @@ Copyright (c) .NET Foundation. All rights reserved. IncludeSymbolsInSingleFile="$(IncludeSymbolsInSingleFile)" ReadyToRunUseCrossgen2="$(PublishReadyToRunUseCrossgen2)" Crossgen2Composite="$(PublishReadyToRunComposite)" - PublishReadyToRunCompositeExclusions="@(PublishReadyToRunCompositeExclusions)"> + PublishReadyToRunCompositeExclusions="@(PublishReadyToRunCompositeExclusions)" + PublishReadyToRunCompositeRoots="@(PublishReadyToRunCompositeRoots)"> @@ -430,6 +431,7 @@ Copyright (c) .NET Foundation. All rights reserved. + @@ -472,7 +474,8 @@ Copyright (c) .NET Foundation. All rights reserved. CompilationEntry="@(_ReadyToRunCompileList)" ContinueOnError="ErrorAndContinue" ReadyToRunCompositeBuildReferences="@(_ReadyToRunCompositeBuildReferences)" - ReadyToRunCompositeBuildInput="@(_ReadyToRunCompositeBuildInput)"> + ReadyToRunCompositeBuildInput="@(_ReadyToRunCompositeBuildInput)" + ReadyToRunCompositeUnrootedBuildInput="@(_ReadyToRunCompositeUnrootedBuildInput)">