2022-07-12 20:57:59 -04:00
// Copyright Epic Games, Inc. All Rights Reserved.
using System ;
using System.Collections.Generic ;
using System.IO ;
using System.Linq ;
using System.Text ;
using EpicGames.Core ;
using Microsoft.Extensions.Logging ;
2023-05-30 18:38:07 -04:00
using UnrealBuildBase ;
2022-07-12 20:57:59 -04:00
namespace UnrealBuildTool
{
internal class TargetEntry
{
public TargetEntry ( FileReference OutputFile , UEBuildTarget BuildTarget , bool bBuildByDefault )
{
this . OutputFile = OutputFile ;
this . BuildTarget = BuildTarget ;
this . bBuildByDefault = bBuildByDefault ;
}
2023-05-30 18:38:07 -04:00
2022-07-12 20:57:59 -04:00
public readonly FileReference OutputFile ;
public readonly UEBuildTarget BuildTarget ;
public readonly bool bBuildByDefault ;
}
internal class RiderProjectFile : ProjectFile
{
2022-09-16 15:53:01 -04:00
private readonly DirectoryReference RootPath ;
private readonly HashSet < TargetType > TargetTypes ;
private readonly CommandLineArguments Arguments ;
2022-07-12 20:57:59 -04:00
private ToolchainInfo RootToolchainInfo = new ToolchainInfo ( ) ;
private UEBuildTarget ? CurrentTarget ;
2023-05-30 18:38:07 -04:00
public RiderProjectFile ( FileReference InProjectFilePath , DirectoryReference BaseDir ,
2022-07-12 20:57:59 -04:00
DirectoryReference RootPath , HashSet < TargetType > TargetTypes , CommandLineArguments Arguments )
: base ( InProjectFilePath , BaseDir )
{
this . RootPath = RootPath ;
this . TargetTypes = TargetTypes ;
this . Arguments = Arguments ;
}
/// <summary>
/// Write project file info in JSON file.
/// For every combination of <c>UnrealTargetPlatform</c>, <c>UnrealTargetConfiguration</c> and <c>TargetType</c>
/// will be generated separate JSON file.
/// Project file will be stored:
/// For UE: {UnrealRoot}/Engine/Intermediate/ProjectFiles/.Rider/{Platform}/{Configuration}/{TargetType}/{ProjectName}.json
/// For game: {GameRoot}/Intermediate/ProjectFiles/.Rider/{Platform}/{Configuration}/{TargetType}/{ProjectName}.json
/// </summary>
/// <remarks>
/// * <c>TargetType.Editor</c> will be generated for current platform only and will ignore <c>UnrealTargetConfiguration.Test</c> and <c>UnrealTargetConfiguration.Shipping</c> configurations
/// * <c>TargetType.Program</c> will be generated for current platform only and <c>UnrealTargetConfiguration.Development</c> configuration only
/// </remarks>
/// <param name="InPlatforms"></param>
/// <param name="InConfigurations"></param>
/// <param name="PlatformProjectGenerators"></param>
/// <param name="Minimize"></param>
/// <param name="Logger"></param>
/// <returns></returns>
public bool WriteProjectFile ( List < UnrealTargetPlatform > InPlatforms ,
List < UnrealTargetConfiguration > InConfigurations ,
PlatformProjectGeneratorCollection PlatformProjectGenerators , JsonWriterStyle Minimize , ILogger Logger )
{
string ProjectName = ProjectFilePath . GetFileNameWithoutAnyExtensions ( ) ;
DirectoryReference ProjectRootFolder = RootPath ;
List < TargetEntry > FileToTarget = new List < TargetEntry > ( ) ;
2024-02-20 18:54:13 -05:00
HashSet < UnrealTargetPlatform > ServerPlatforms = Utils . GetPlatformsInClass ( UnrealPlatformClass . Server ) . ToHashSet ( ) ;
2022-07-12 20:57:59 -04:00
foreach ( UnrealTargetPlatform Platform in InPlatforms )
2023-05-30 18:38:07 -04:00
{
2022-07-12 20:57:59 -04:00
foreach ( UnrealTargetConfiguration Configuration in InConfigurations )
{
foreach ( ProjectTarget ProjectTarget in ProjectTargets . OfType < ProjectTarget > ( ) )
{
2023-05-30 18:59:32 -04:00
if ( TargetTypes . Any ( ) & & ! TargetTypes . Contains ( ProjectTarget . TargetRules ! . Type ) )
{
continue ;
}
2022-07-12 20:57:59 -04:00
// Skip Programs for all configs except for current platform + Development & Debug configurations
if ( ProjectTarget . TargetRules ! . Type = = TargetType . Program & &
2023-05-30 18:38:07 -04:00
( BuildHostPlatform . Current . Platform ! = Platform | |
! ( Configuration = = UnrealTargetConfiguration . Development | | Configuration = = UnrealTargetConfiguration . Debug ) ) )
2022-07-12 20:57:59 -04:00
{
continue ;
}
// Skip Editor for all platforms except for current platform
if ( ProjectTarget . TargetRules . Type = = TargetType . Editor & & ( BuildHostPlatform . Current . Platform ! = Platform | | ( Configuration = = UnrealTargetConfiguration . Test | | Configuration = = UnrealTargetConfiguration . Shipping ) ) )
{
continue ;
}
2023-05-30 18:38:07 -04:00
2024-02-20 18:54:13 -05:00
// Skip Server for all invalid platforms
if ( ProjectTarget . TargetRules . Type = = TargetType . Server & & ! ServerPlatforms . Contains ( Platform ) )
{
continue ;
}
2022-07-12 20:57:59 -04:00
bool bBuildByDefault = ShouldBuildByDefaultForSolutionTargets & & ProjectTarget . SupportedPlatforms . Contains ( Platform ) ;
2023-05-30 18:38:07 -04:00
2022-07-12 20:57:59 -04:00
DirectoryReference ConfigurationFolder = DirectoryReference . Combine ( ProjectRootFolder , Platform . ToString ( ) , Configuration . ToString ( ) ) ;
DirectoryReference TargetFolder =
DirectoryReference . Combine ( ConfigurationFolder , ProjectTarget . TargetRules . Type . ToString ( ) ) ;
UnrealArch/UnrealArchitectures changes
- Creates the UnrealArchitectures class, which wraps a list of UnrealArch objects
- UnrealArch is a single architecture, expandable enum-like struct
- There is no more concept of "no/default architecture", there is always a valid active architecture when building
- Most uses of "string Architecture" are replaced with one of the two above, depending if multiple architectures are supported or not
- UnrealArch has some platform-extensions for platform-specific naming (like Linux adds in LinuxName that turns, for instance, Arm64 -> aarch64-unknown-linux-gnueabi, which is used in folder names, etc)
- UnrealArch has bIsX64 which can be used determine intel instruction set (as opposed to arm)
- TargetRules class has an "Architecture" accessor that will return a single architecture if the active architectures is a single architecture, or throw an exception if multiple. This is useful in a majority of the cases where a paltform can only have a single architecture active in TargetRules (microsoft platforms, for instance, will create separate targets when compiling multiple architectures at once)
- Added UnrealArchitectureConfig class, which contains all the architecture information for a platform (what architectures are supported, what ones are currently active for given project, etc)
#preflight 63c81fb5b065224750a1759e
#rb mike.fricker,roman.dzieciol,joe.kirchoff,dmytro.vovk,brandon.schaefer [various parts]
#p4v-preflight-copy 23562471
[CL 23829977 by josh adams in ue5-main branch]
2023-01-24 09:30:28 -05:00
UnrealArchitectures ProjectArchitectures = UEBuildPlatform
2022-07-12 20:57:59 -04:00
. GetBuildPlatform ( Platform )
UnrealArch/UnrealArchitectures changes
- Creates the UnrealArchitectures class, which wraps a list of UnrealArch objects
- UnrealArch is a single architecture, expandable enum-like struct
- There is no more concept of "no/default architecture", there is always a valid active architecture when building
- Most uses of "string Architecture" are replaced with one of the two above, depending if multiple architectures are supported or not
- UnrealArch has some platform-extensions for platform-specific naming (like Linux adds in LinuxName that turns, for instance, Arm64 -> aarch64-unknown-linux-gnueabi, which is used in folder names, etc)
- UnrealArch has bIsX64 which can be used determine intel instruction set (as opposed to arm)
- TargetRules class has an "Architecture" accessor that will return a single architecture if the active architectures is a single architecture, or throw an exception if multiple. This is useful in a majority of the cases where a paltform can only have a single architecture active in TargetRules (microsoft platforms, for instance, will create separate targets when compiling multiple architectures at once)
- Added UnrealArchitectureConfig class, which contains all the architecture information for a platform (what architectures are supported, what ones are currently active for given project, etc)
#preflight 63c81fb5b065224750a1759e
#rb mike.fricker,roman.dzieciol,joe.kirchoff,dmytro.vovk,brandon.schaefer [various parts]
#p4v-preflight-copy 23562471
[CL 23829977 by josh adams in ue5-main branch]
2023-01-24 09:30:28 -05:00
. ArchitectureConfig . ActiveArchitectures ( ProjectTarget . UnrealProjectFilePath , ProjectTarget . Name ) ;
2022-07-12 20:57:59 -04:00
TargetDescriptor TargetDesc = new TargetDescriptor ( ProjectTarget . UnrealProjectFilePath , ProjectTarget . Name ,
UnrealArch/UnrealArchitectures changes
- Creates the UnrealArchitectures class, which wraps a list of UnrealArch objects
- UnrealArch is a single architecture, expandable enum-like struct
- There is no more concept of "no/default architecture", there is always a valid active architecture when building
- Most uses of "string Architecture" are replaced with one of the two above, depending if multiple architectures are supported or not
- UnrealArch has some platform-extensions for platform-specific naming (like Linux adds in LinuxName that turns, for instance, Arm64 -> aarch64-unknown-linux-gnueabi, which is used in folder names, etc)
- UnrealArch has bIsX64 which can be used determine intel instruction set (as opposed to arm)
- TargetRules class has an "Architecture" accessor that will return a single architecture if the active architectures is a single architecture, or throw an exception if multiple. This is useful in a majority of the cases where a paltform can only have a single architecture active in TargetRules (microsoft platforms, for instance, will create separate targets when compiling multiple architectures at once)
- Added UnrealArchitectureConfig class, which contains all the architecture information for a platform (what architectures are supported, what ones are currently active for given project, etc)
#preflight 63c81fb5b065224750a1759e
#rb mike.fricker,roman.dzieciol,joe.kirchoff,dmytro.vovk,brandon.schaefer [various parts]
#p4v-preflight-copy 23562471
[CL 23829977 by josh adams in ue5-main branch]
2023-01-24 09:30:28 -05:00
Platform , Configuration , ProjectArchitectures , Arguments ) ;
2022-07-12 20:57:59 -04:00
try
{
2024-01-23 14:25:03 -05:00
UEBuildTarget BuildTarget = UEBuildTarget . Create ( TargetDesc , false , false , false , UnrealIntermediateEnvironment . GenerateProjectFiles , Logger ) ;
2023-05-30 18:38:07 -04:00
2022-07-12 20:57:59 -04:00
FileReference OutputFile = FileReference . Combine ( TargetFolder , $"{ProjectName}.json" ) ;
FileToTarget . Add ( new TargetEntry ( OutputFile , BuildTarget , bBuildByDefault ) ) ;
}
2023-05-30 18:38:07 -04:00
catch ( Exception Ex )
2022-07-12 20:57:59 -04:00
{
Logger . LogWarning ( "Exception while generating include data for Target:{Target}, Platform: {Platform}, Configuration: {Configuration}" , TargetDesc . Name , Platform . ToString ( ) , Configuration . ToString ( ) ) ;
Logger . LogWarning ( "{Ex}" , Ex . ToString ( ) ) ;
}
}
}
}
foreach ( TargetEntry TargetEntry in FileToTarget )
{
try
{
CurrentTarget = TargetEntry . BuildTarget ;
CurrentTarget . PreBuildSetup ( Logger ) ;
SerializeTarget ( TargetEntry . OutputFile , CurrentTarget , PlatformProjectGenerators , Minimize , TargetEntry . bBuildByDefault , Logger ) ;
}
catch ( Exception Ex )
{
Logger . LogWarning ( "Exception while generating include data for Target:{Target}, Platform: {Platform}, Configuration: {Configuration}" ,
TargetEntry . BuildTarget . AppName , TargetEntry . BuildTarget . Platform . ToString ( ) , TargetEntry . BuildTarget . Configuration . ToString ( ) ) ;
Logger . LogWarning ( "{Ex}" , Ex . ToString ( ) ) ;
}
}
2023-05-30 18:38:07 -04:00
2022-07-12 20:57:59 -04:00
return true ;
}
private bool IsPlatformInHostGroup ( UnrealTargetPlatform Platform )
{
2023-05-31 13:37:21 -04:00
IEnumerable < UnrealPlatformGroup > Groups = UEBuildPlatform . GetPlatformGroups ( BuildHostPlatform . Current . Platform ) ;
2023-05-30 18:38:07 -04:00
foreach ( UnrealPlatformGroup Group in Groups )
2022-07-12 20:57:59 -04:00
{
// Desktop includes Linux, Mac and Windows.
2023-05-30 18:38:07 -04:00
if ( UEBuildPlatform . IsPlatformInGroup ( Platform , Group ) & & Group ! = UnrealPlatformGroup . Desktop )
2022-07-12 20:57:59 -04:00
{
return true ;
}
}
2023-05-30 18:38:07 -04:00
2022-07-12 20:57:59 -04:00
return false ;
}
private void SerializeTarget ( FileReference OutputFile , UEBuildTarget BuildTarget , PlatformProjectGeneratorCollection PlatformProjectGenerators , JsonWriterStyle Minimize , bool bBuildByDefault , ILogger Logger )
{
DirectoryReference . CreateDirectory ( OutputFile . Directory ) ;
using ( JsonWriter Writer = new JsonWriter ( OutputFile , Minimize ) )
{
ExportTarget ( BuildTarget , Writer , PlatformProjectGenerators , bBuildByDefault , Logger ) ;
}
}
/// <summary>
/// Write a Target to a JSON writer. Is array is empty, don't write anything
/// </summary>
/// <param name="Target"></param>
/// <param name="Writer">Writer for the array data</param>
/// <param name="PlatformProjectGenerators"></param>
/// <param name="bBuildByDefault"></param>
/// <param name="Logger">Logger for output</param>
private void ExportTarget ( UEBuildTarget Target , JsonWriter Writer , PlatformProjectGeneratorCollection PlatformProjectGenerators , bool bBuildByDefault , ILogger Logger )
{
Writer . WriteObjectStart ( ) ;
Writer . WriteValue ( "Name" , Target . TargetName ) ;
Writer . WriteValue ( "Configuration" , Target . Configuration . ToString ( ) ) ;
Writer . WriteValue ( "Platform" , Target . Platform . ToString ( ) ) ;
Writer . WriteValue ( "TargetFile" , Target . TargetRulesFile . FullName ) ;
if ( Target . ProjectFile ! = null )
{
Writer . WriteValue ( "ProjectFile" , Target . ProjectFile . FullName ) ;
}
2023-05-30 18:38:07 -04:00
2022-07-12 20:57:59 -04:00
ExportEnvironmentToJson ( Target , Writer , PlatformProjectGenerators , bBuildByDefault , Logger ) ;
2023-05-30 18:38:07 -04:00
if ( Target . Binaries . Any ( ) )
2022-07-12 20:57:59 -04:00
{
Writer . WriteArrayStart ( "Binaries" ) ;
foreach ( UEBuildBinary Binary in Target . Binaries )
{
Writer . WriteObjectStart ( ) ;
ExportBinary ( Binary , Writer ) ;
Writer . WriteObjectEnd ( ) ;
}
Writer . WriteArrayEnd ( ) ;
}
2023-05-30 18:38:07 -04:00
2022-07-12 20:57:59 -04:00
CppCompileEnvironment GlobalCompileEnvironment = Target . CreateCompileEnvironmentForProjectFiles ( Logger ) ;
HashSet < string > ModuleNames = new HashSet < string > ( ) ;
Writer . WriteObjectStart ( "Modules" ) ;
foreach ( UEBuildBinary Binary in Target . Binaries )
{
CppCompileEnvironment BinaryCompileEnvironment = Binary . CreateBinaryCompileEnvironment ( GlobalCompileEnvironment ) ;
foreach ( UEBuildModule Module in Binary . Modules )
{
2023-05-30 18:38:07 -04:00
if ( ModuleNames . Add ( Module . Name ) )
2022-07-12 20:57:59 -04:00
{
Writer . WriteObjectStart ( Module . Name ) ;
UEBuildModuleCPP ? ModuleCpp = Module as UEBuildModuleCPP ;
if ( ModuleCpp ! = null )
{
CppCompileEnvironment ModuleCompileEnvironment = ModuleCpp . CreateCompileEnvironmentForIntellisense ( Target . Rules , BinaryCompileEnvironment , Logger ) ;
ExportModuleCpp ( ModuleCpp , ModuleCompileEnvironment , Writer , Logger ) ;
Module . PrivateIncludePaths . UnionWith ( ModuleCompileEnvironment . UserIncludePaths ) ;
}
ExportModule ( Module , Binary . OutputDir , Target . GetExecutableDir ( ) , Writer , Logger ) ;
Writer . WriteObjectEnd ( ) ;
}
}
}
Writer . WriteObjectEnd ( ) ;
2023-05-30 18:38:07 -04:00
2022-07-12 20:57:59 -04:00
ExportPluginsFromTarget ( Target , Writer , Logger ) ;
2023-05-30 18:38:07 -04:00
2022-07-12 20:57:59 -04:00
Writer . WriteObjectEnd ( ) ;
}
private void ExportModuleCpp ( UEBuildModuleCPP ModuleCPP , CppCompileEnvironment ModuleCompileEnvironment , JsonWriter Writer , ILogger Logger )
{
2023-05-30 18:59:32 -04:00
Writer . WriteValue ( "GeneratedCodeDirectory" , ModuleCPP . GeneratedCodeDirectory ! = null ? ModuleCPP . GeneratedCodeDirectory . FullName : String . Empty ) ;
2023-05-30 18:38:07 -04:00
2022-07-12 20:57:59 -04:00
ToolchainInfo ModuleToolchainInfo = GenerateToolchainInfo ( ModuleCompileEnvironment ) ;
if ( ! ModuleToolchainInfo . Equals ( RootToolchainInfo ) )
{
Writer . WriteObjectStart ( "ToolchainInfo" ) ;
2023-05-30 18:38:07 -04:00
foreach ( Tuple < string , object? > Field in ModuleToolchainInfo . GetDiff ( RootToolchainInfo ) )
2022-07-12 20:57:59 -04:00
{
WriteField ( ModuleCPP . Name , Writer , Field , Logger ) ;
}
Writer . WriteObjectEnd ( ) ;
}
2023-05-30 18:38:07 -04:00
2022-07-12 20:57:59 -04:00
if ( ModuleCompileEnvironment . PrecompiledHeaderIncludeFilename ! = null )
{
string CorrectFilePathPch ;
2023-05-30 18:38:07 -04:00
if ( ExtractWrappedIncludeFile ( ModuleCompileEnvironment . PrecompiledHeaderIncludeFilename , Logger , out CorrectFilePathPch ) )
2023-05-30 18:59:32 -04:00
{
2022-07-12 20:57:59 -04:00
Writer . WriteValue ( "SharedPCHFilePath" , CorrectFilePathPch ) ;
2023-05-30 18:59:32 -04:00
}
2022-07-12 20:57:59 -04:00
}
}
private static bool ExtractWrappedIncludeFile ( FileSystemReference FileRef , ILogger Logger , out string CorrectFilePathPch )
{
CorrectFilePathPch = "" ;
try
{
using ( StreamReader Reader = new StreamReader ( FileRef . FullName ) )
{
string? Line = Reader . ReadLine ( ) ;
if ( Line ! = null )
{
CorrectFilePathPch = Line . Substring ( "// PCH for " . Length ) . Trim ( ) ;
return true ;
}
}
}
finally
{
Logger . LogDebug ( "Couldn't extract path to PCH from {FileRef}" , FileRef ) ;
}
return false ;
}
/// <summary>
/// Write a Module to a JSON writer. If array is empty, don't write anything
/// </summary>
/// <param name="BinaryOutputDir"></param>
/// <param name="TargetOutputDir"></param>
/// <param name="Writer">Writer for the array data</param>
/// <param name="Module"></param>
/// <param name="Logger"></param>
private static void ExportModule ( UEBuildModule Module , DirectoryReference BinaryOutputDir , DirectoryReference TargetOutputDir , JsonWriter Writer , ILogger Logger )
{
Writer . WriteValue ( "Name" , Module . Name ) ;
2023-05-30 18:38:07 -04:00
Writer . WriteValue ( "Directory" , Module . ModuleDirectory . FullName ) ;
Writer . WriteValue ( "Rules" , Module . RulesFile . FullName ) ;
2024-01-30 15:18:09 -05:00
ExportJsonStringArray ( Writer , "SubRules" , Module . Rules . SubclassRules ) ;
2022-07-12 20:57:59 -04:00
Writer . WriteValue ( "PCHUsage" , Module . Rules . PCHUsage . ToString ( ) ) ;
if ( Module . Rules . PrivatePCHHeaderFile ! = null )
{
Writer . WriteValue ( "PrivatePCH" , FileReference . Combine ( Module . ModuleDirectory , Module . Rules . PrivatePCHHeaderFile ) . FullName ) ;
}
if ( Module . Rules . SharedPCHHeaderFile ! = null )
{
Writer . WriteValue ( "SharedPCH" , FileReference . Combine ( Module . ModuleDirectory , Module . Rules . SharedPCHHeaderFile ) . FullName ) ;
}
ExportJsonModuleArray ( Writer , "PublicDependencyModules" , Module . PublicDependencyModules ) ;
ExportJsonModuleArray ( Writer , "PublicIncludePathModules" , Module . PublicIncludePathModules ) ;
ExportJsonModuleArray ( Writer , "PrivateDependencyModules" , Module . PrivateDependencyModules ) ;
ExportJsonModuleArray ( Writer , "PrivateIncludePathModules" , Module . PrivateIncludePathModules ) ;
ExportJsonModuleArray ( Writer , "DynamicallyLoadedModules" , Module . DynamicallyLoadedModules ) ;
2023-05-30 18:38:07 -04:00
ExportJsonStringArray ( Writer , "PublicSystemIncludePaths" , Module . PublicSystemIncludePaths . Select ( x = > x . FullName ) ) ;
ExportJsonStringArray ( Writer , "PublicIncludePaths" , Module . PublicIncludePaths . Select ( x = > x . FullName ) ) ;
2022-07-12 20:57:59 -04:00
ExportJsonStringArray ( Writer , "InternalIncludePaths" , Module . InternalIncludePaths . Select ( x = > x . FullName ) ) ;
2023-05-30 18:38:07 -04:00
ExportJsonStringArray ( Writer , "LegacyPublicIncludePaths" , Module . LegacyPublicIncludePaths . Select ( x = > x . FullName ) ) ;
2022-10-26 19:13:07 -04:00
ExportJsonStringArray ( Writer , "LegacyParentIncludePaths" , Module . LegacyParentIncludePaths . Select ( x = > x . FullName ) ) ;
2022-07-12 20:57:59 -04:00
ExportJsonStringArray ( Writer , "PrivateIncludePaths" , Module . PrivateIncludePaths . Select ( x = > x . FullName ) ) ;
ExportJsonStringArray ( Writer , "PublicLibraryPaths" , Module . PublicSystemLibraryPaths . Select ( x = > x . FullName ) ) ;
ExportJsonStringArray ( Writer , "PublicAdditionalLibraries" , Module . PublicSystemLibraries . Concat ( Module . PublicLibraries . Select ( x = > x . FullName ) ) ) ;
ExportJsonStringArray ( Writer , "PublicFrameworks" , Module . PublicFrameworks ) ;
ExportJsonStringArray ( Writer , "PublicWeakFrameworks" , Module . PublicWeakFrameworks ) ;
ExportJsonStringArray ( Writer , "PublicDelayLoadDLLs" , Module . PublicDelayLoadDLLs ) ;
ExportJsonStringArray ( Writer , "PublicDefinitions" , Module . PublicDefinitions ) ;
2024-01-30 15:20:35 -05:00
ExportJsonStringArray ( Writer , "PrivateDefinitions" , Module . Rules . PrivateDefinitions . Concat ( EngineIncludeOrderHelper . GetDeprecationDefines ( Module . Rules . IncludeOrderVersion ) ) ) ;
2024-07-23 19:39:24 -04:00
ExportJsonStringArray ( Writer , "ProjectDefinitions" , /* TODO: Add method ShouldAddProjectDefinitions */ ! Module . Rules . bTreatAsEngineModule ? Module . Rules . Target . ProjectDefinitions : Array . Empty < string > ( ) ) ;
2022-07-12 20:57:59 -04:00
ExportJsonStringArray ( Writer , "ApiDefinitions" , Module . GetEmptyApiMacros ( ) ) ;
Writer . WriteValue ( "ShouldAddLegacyPublicIncludePaths" , Module . Rules . bLegacyPublicIncludePaths ) ;
2022-10-26 19:13:07 -04:00
Writer . WriteValue ( "ShouldAddLegacyParentIncludePaths" , Module . Rules . bLegacyParentIncludePaths ) ;
2022-07-12 20:57:59 -04:00
2022-10-26 19:13:07 -04:00
if ( Module . Rules . CircularlyReferencedDependentModules . Any ( ) )
2022-07-12 20:57:59 -04:00
{
Writer . WriteArrayStart ( "CircularlyReferencedModules" ) ;
foreach ( string ModuleName in Module . Rules . CircularlyReferencedDependentModules )
{
Writer . WriteValue ( ModuleName ) ;
}
Writer . WriteArrayEnd ( ) ;
}
2023-05-30 18:38:07 -04:00
if ( Module . Rules . RuntimeDependencies . Inner . Any ( ) )
2022-07-12 20:57:59 -04:00
{
// We don't use info from RuntimeDependencies for code analyzes (at the moment)
// So we're OK with skipping some values if they are not presented
Writer . WriteArrayStart ( "RuntimeDependencies" ) ;
foreach ( ModuleRules . RuntimeDependency RuntimeDependency in Module . Rules . RuntimeDependencies . Inner )
{
Writer . WriteObjectStart ( ) ;
try
{
Writer . WriteValue ( "Path" ,
Module . ExpandPathVariables ( RuntimeDependency . Path , BinaryOutputDir , TargetOutputDir ) ) ;
}
2023-05-30 18:38:07 -04:00
catch ( BuildException buildException )
2022-07-12 20:57:59 -04:00
{
2023-05-30 18:38:07 -04:00
Logger . LogDebug ( "Value {Value} for module {ModuleName} will not be stored. Reason: {Ex}" , "Path" , Module . Name , buildException ) ;
2022-07-12 20:57:59 -04:00
}
2023-05-30 18:38:07 -04:00
2022-07-12 20:57:59 -04:00
if ( RuntimeDependency . SourcePath ! = null )
{
try
{
Writer . WriteValue ( "SourcePath" ,
Module . ExpandPathVariables ( RuntimeDependency . SourcePath , BinaryOutputDir ,
TargetOutputDir ) ) ;
}
2023-05-30 18:38:07 -04:00
catch ( BuildException buildException )
2022-07-12 20:57:59 -04:00
{
2023-05-30 18:38:07 -04:00
Logger . LogDebug ( "Value {Value} for module {ModuleName} will not be stored. Reason: {Ex}" , "SourcePath" , Module . Name , buildException ) ;
2022-07-12 20:57:59 -04:00
}
}
Writer . WriteValue ( "Type" , RuntimeDependency . Type . ToString ( ) ) ;
2023-05-30 18:38:07 -04:00
2022-07-12 20:57:59 -04:00
Writer . WriteObjectEnd ( ) ;
}
Writer . WriteArrayEnd ( ) ;
}
}
2023-05-30 18:38:07 -04:00
2022-07-12 20:57:59 -04:00
/// <summary>
/// Write an array of Modules to a JSON writer. If array is empty, don't write anything
/// </summary>
/// <param name="Writer">Writer for the array data</param>
/// <param name="ArrayName">Name of the array property</param>
/// <param name="Modules">Sequence of Modules to write. May be null.</param>
private static void ExportJsonModuleArray ( JsonWriter Writer , string ArrayName , IEnumerable < UEBuildModule > ? Modules )
{
2023-05-30 18:59:32 -04:00
if ( Modules = = null | | ! Modules . Any ( ) )
{
return ;
}
2023-05-30 18:38:07 -04:00
2022-07-12 20:57:59 -04:00
Writer . WriteArrayStart ( ArrayName ) ;
foreach ( UEBuildModule Module in Modules )
{
Writer . WriteValue ( Module . Name ) ;
}
Writer . WriteArrayEnd ( ) ;
}
2023-05-30 18:38:07 -04:00
2022-07-12 20:57:59 -04:00
/// <summary>
/// Write an array of strings to a JSON writer. Ifl array is empty, don't write anything
/// </summary>
/// <param name="Writer">Writer for the array data</param>
/// <param name="ArrayName">Name of the array property</param>
/// <param name="Strings">Sequence of strings to write. May be null.</param>
2024-01-30 15:18:09 -05:00
private static void ExportJsonStringArray ( JsonWriter Writer , string ArrayName , IEnumerable < string > ? Strings )
2022-07-12 20:57:59 -04:00
{
2023-05-30 18:59:32 -04:00
if ( Strings = = null | | ! Strings . Any ( ) )
{
return ;
}
2023-05-30 18:38:07 -04:00
2022-07-12 20:57:59 -04:00
Writer . WriteArrayStart ( ArrayName ) ;
foreach ( string String in Strings )
{
Writer . WriteValue ( String ) ;
}
Writer . WriteArrayEnd ( ) ;
}
2023-05-30 18:38:07 -04:00
2022-07-12 20:57:59 -04:00
/// <summary>
/// Write uplugin content to a JSON writer
/// </summary>
/// <param name="Plugin">Uplugin description</param>
/// <param name="Writer">JSON writer</param>
private static void ExportPlugin ( UEBuildPlugin Plugin , JsonWriter Writer )
{
Writer . WriteObjectStart ( Plugin . Name ) ;
2023-05-30 18:38:07 -04:00
Writer . WriteValue ( "File" , Plugin . File . FullName ) ;
2022-07-12 20:57:59 -04:00
Writer . WriteValue ( "Type" , Plugin . Type . ToString ( ) ) ;
2023-05-30 18:38:07 -04:00
if ( Plugin . Dependencies ! = null & & Plugin . Dependencies . Any ( ) )
2022-07-12 20:57:59 -04:00
{
Writer . WriteStringArrayField ( "Dependencies" , Plugin . Dependencies . Select ( it = > it . Name ) ) ;
}
2023-05-30 18:38:07 -04:00
if ( Plugin . Modules . Any ( ) )
2022-07-12 20:57:59 -04:00
{
Writer . WriteStringArrayField ( "Modules" , Plugin . Modules . Select ( it = > it . Name ) ) ;
}
2023-05-30 18:38:07 -04:00
2022-07-12 20:57:59 -04:00
Writer . WriteObjectEnd ( ) ;
}
2023-05-30 18:38:07 -04:00
2022-07-12 20:57:59 -04:00
/// <summary>
/// Setup plugins for Target and write plugins to JSON writer. Don't write anything if there are no plugins
/// </summary>
/// <param name="Target"></param>
/// <param name="Writer"></param>
/// <param name="Logger"></param>
private static void ExportPluginsFromTarget ( UEBuildTarget Target , JsonWriter Writer , ILogger Logger )
{
Target . SetupPlugins ( Logger ) ;
2023-05-30 18:59:32 -04:00
if ( Target . BuildPlugins = = null | | ! Target . BuildPlugins . Any ( ) )
{
return ;
}
2023-05-30 18:38:07 -04:00
2022-07-12 20:57:59 -04:00
Writer . WriteObjectStart ( "Plugins" ) ;
foreach ( UEBuildPlugin plugin in Target . BuildPlugins ! )
{
ExportPlugin ( plugin , Writer ) ;
}
Writer . WriteObjectEnd ( ) ;
}
/// <summary>
/// Write information about this binary to a JSON file
/// </summary>
/// <param name="Binary"></param>
/// <param name="Writer">Writer for this binary's data</param>
private static void ExportBinary ( UEBuildBinary Binary , JsonWriter Writer )
{
2023-05-30 18:38:07 -04:00
Writer . WriteValue ( "File" , Binary . OutputFilePath . FullName ) ;
2022-07-12 20:57:59 -04:00
Writer . WriteValue ( "Type" , Binary . Type . ToString ( ) ) ;
Writer . WriteArrayStart ( "Modules" ) ;
2023-05-30 18:38:07 -04:00
foreach ( UEBuildModule Module in Binary . Modules )
2022-07-12 20:57:59 -04:00
{
Writer . WriteValue ( Module . Name ) ;
}
Writer . WriteArrayEnd ( ) ;
}
2023-05-30 18:38:07 -04:00
2022-07-12 20:57:59 -04:00
/// <summary>
/// Write C++ toolchain information to JSON writer
/// </summary>
/// <param name="Target"></param>
/// <param name="Writer"></param>
/// <param name="PlatformProjectGenerators"></param>
/// <param name="bBuildByDefault"></param>
/// <param name="Logger"></param>
private void ExportEnvironmentToJson ( UEBuildTarget Target , JsonWriter Writer , PlatformProjectGeneratorCollection PlatformProjectGenerators , bool bBuildByDefault , ILogger Logger )
{
CppCompileEnvironment GlobalCompileEnvironment = Target . CreateCompileEnvironmentForProjectFiles ( Logger ) ;
2023-05-30 18:38:07 -04:00
2022-07-12 20:57:59 -04:00
RootToolchainInfo = GenerateToolchainInfo ( GlobalCompileEnvironment ) ;
2023-05-30 18:38:07 -04:00
2022-07-12 20:57:59 -04:00
Writer . WriteObjectStart ( "ToolchainInfo" ) ;
foreach ( Tuple < string , object? > Field in RootToolchainInfo . GetFields ( ) )
{
WriteField ( Target . TargetName , Writer , Field , Logger ) ;
}
Writer . WriteObjectEnd ( ) ;
2023-05-30 18:38:07 -04:00
2024-03-12 20:35:34 -04:00
ExportBuildInfo ( Writer , Target , PlatformProjectGenerators , bBuildByDefault , Logger ) ;
2023-05-30 18:38:07 -04:00
2022-07-12 20:57:59 -04:00
Writer . WriteArrayStart ( "EnvironmentIncludePaths" ) ;
foreach ( DirectoryReference Path in GlobalCompileEnvironment . UserIncludePaths )
{
2023-05-30 18:38:07 -04:00
Writer . WriteValue ( Path . FullName ) ;
2022-07-12 20:57:59 -04:00
}
foreach ( DirectoryReference Path in GlobalCompileEnvironment . SystemIncludePaths )
{
2023-05-30 18:38:07 -04:00
Writer . WriteValue ( Path . FullName ) ;
2022-07-12 20:57:59 -04:00
}
2024-04-03 12:22:43 -04:00
2023-12-14 01:45:26 -05:00
PlatformProjectGenerator ? ProjGenerator = PlatformProjectGenerators . GetPlatformProjectGenerator ( Target . Platform , true ) ;
if ( ProjGenerator ! = null )
{
foreach ( string Path in ProjGenerator . GetSystemIncludePaths ( Target ) )
{
Writer . WriteValue ( Path ) ;
}
}
2023-05-30 18:38:07 -04:00
2022-07-12 20:57:59 -04:00
Writer . WriteArrayEnd ( ) ;
2023-05-30 18:38:07 -04:00
2022-07-12 20:57:59 -04:00
Writer . WriteArrayStart ( "EnvironmentDefinitions" ) ;
foreach ( string Definition in GlobalCompileEnvironment . Definitions )
{
Writer . WriteValue ( Definition ) ;
}
Writer . WriteArrayEnd ( ) ;
}
2024-03-12 20:35:34 -04:00
private void ExportBuildInfo ( JsonWriter Writer , UEBuildTarget Target , PlatformProjectGeneratorCollection PlatformProjectGenerators ,
bool bBuildByDefault , ILogger Logger )
2022-07-12 20:57:59 -04:00
{
2023-05-30 18:59:32 -04:00
if ( IsStubProject )
{
return ;
}
2023-05-30 18:38:07 -04:00
2024-03-12 20:35:34 -04:00
try
2022-07-12 20:57:59 -04:00
{
2024-03-12 20:35:34 -04:00
string BuildScript ;
string RebuildScript ;
string CleanScript ;
string BuildArguments ;
string RebuildArguments ;
string CleanArguments ;
string Output = Target . Binaries [ 0 ] . OutputFilePath . FullName ;
2023-05-30 18:38:07 -04:00
2024-03-12 20:35:34 -04:00
ProjectTarget ProjectTarget = ProjectTargets . OfType < ProjectTarget > ( ) . Single ( It = > Target . TargetRulesFile = = It . TargetFilePath ) ;
string UProjectPath = IsForeignProject ? String . Format ( "\"{0}\"" , ProjectTarget . UnrealProjectFilePath ! . FullName ) : "" ;
UnrealTargetPlatform HostPlatform = BuildHostPlatform . Current . Platform ;
if ( HostPlatform . IsInGroup ( UnrealPlatformGroup . Windows ) )
2022-07-12 20:57:59 -04:00
{
2024-03-12 20:35:34 -04:00
PlatformProjectGenerator ? ProjGenerator = PlatformProjectGenerators . GetPlatformProjectGenerator ( Target . Platform , true ) ;
VCProjectFile . BuildCommandBuilder BuildCommandBuilder =
new VCProjectFile . BuildCommandBuilder (
new PlatformProjectGenerator . VSSettings ( Target . Platform , Target . Configuration , VCProjectFileFormat . Default , null ) ,
ProjectTarget , UProjectPath )
{
ProjectGenerator = ProjGenerator ,
bIsForeignProject = IsForeignProject
} ;
2023-05-30 18:38:07 -04:00
2024-03-12 20:35:34 -04:00
BuildArguments = RebuildArguments = CleanArguments = BuildCommandBuilder . GetBuildArguments ( ) ;
BuildScript = EscapePath ( BuildCommandBuilder . BuildScript . FullName ) ;
RebuildScript = EscapePath ( BuildCommandBuilder . RebuildScript . FullName ) ;
CleanScript = EscapePath ( BuildCommandBuilder . CleanScript . FullName ) ;
}
else
{
BuildScript = CleanScript = GetBuildScript ( HostPlatform ) ;
BuildArguments = GetBuildArguments ( HostPlatform , ProjectTarget , Target , UProjectPath , false ) ;
CleanArguments = GetBuildArguments ( HostPlatform , ProjectTarget , Target , UProjectPath , true ) ;
RebuildScript = RebuildArguments = "" ;
}
Writer . WriteObjectStart ( "BuildInfo" ) ;
Writer . WriteValue ( "bBuildByDefault" , bBuildByDefault ) ;
2022-07-12 20:57:59 -04:00
WriteCommand ( Writer , "BuildCmd" , BuildScript , BuildArguments ) ;
2024-03-12 20:35:34 -04:00
WriteCommand ( Writer , "RebuildCmd" , RebuildScript , RebuildArguments ) ;
WriteCommand ( Writer , "CleanCmd" , CleanScript , CleanArguments ) ;
Writer . WriteValue ( "Output" , Output ) ;
Writer . WriteObjectEnd ( ) ;
}
catch ( Exception Ex )
{
Logger . LogWarning ( Ex ,
"Exception while generating build info for Target: {Target}, Platform: {Platform}, Configuration: {Configuration}" ,
Target . TargetName , Target . Platform . ToString ( ) , Target . Configuration . ToString ( ) ) ;
2022-07-12 20:57:59 -04:00
}
}
private string GetBuildScript ( UnrealTargetPlatform HostPlatform )
{
DirectoryReference BatchFilesDirectory = DirectoryReference . Combine ( Unreal . EngineDirectory , "Build" , "BatchFiles" ) ;
string ScriptExtension = HostPlatform . IsInGroup ( UnrealPlatformGroup . Windows ) ? ".bat" : ".sh" ;
if ( HostPlatform . IsInGroup ( UnrealPlatformGroup . Linux ) )
{
BatchFilesDirectory = DirectoryReference . Combine ( BatchFilesDirectory , "Linux" ) ;
}
else if ( HostPlatform = = UnrealTargetPlatform . Mac )
{
BatchFilesDirectory = DirectoryReference . Combine ( BatchFilesDirectory , "Mac" ) ;
}
2023-05-30 18:38:07 -04:00
2022-07-12 20:57:59 -04:00
return EscapePath ( FileReference . Combine ( BatchFilesDirectory , "Build" + ScriptExtension ) . FullName ) ;
}
private string GetBuildArguments ( UnrealTargetPlatform HostPlatform , ProjectTarget ProjectTarget , UEBuildTarget Target , string UProjectPath , bool bIsClean )
{
UnrealTargetConfiguration Configuration = Target . Configuration ;
UnrealTargetPlatform Platform = Target . Platform ;
string TargetName = ProjectTarget . TargetFilePath . GetFileNameWithoutAnyExtensions ( ) ;
StringBuilder BuildArguments = new StringBuilder ( ) ;
BuildArguments . AppendFormat ( "{0} {1} {2}" , TargetName , Platform . ToString ( ) , Configuration . ToString ( ) ) ;
if ( IsForeignProject )
{
BuildArguments . AppendFormat ( " -Project={0}" , UProjectPath ) ;
}
if ( Target . TargetType = = TargetType . Editor )
{
BuildArguments . Append ( " -buildscw" ) ;
}
if ( bIsClean )
{
BuildArguments . Append ( " -clean" ) ;
}
else if ( HostPlatform . IsInGroup ( UnrealPlatformGroup . Apple ) & &
2023-05-30 18:38:07 -04:00
( Platform = = UnrealTargetPlatform . TVOS | | Platform = = UnrealTargetPlatform . IOS ) )
2022-07-12 20:57:59 -04:00
{
BuildArguments . Append ( " -deploy" ) ;
}
return BuildArguments . ToString ( ) ;
}
private static void WriteCommand ( JsonWriter Writer , string CommandName , string Command , string Arguments )
{
Writer . WriteObjectStart ( CommandName ) ;
Writer . WriteValue ( "Command" , Command ) ;
Writer . WriteValue ( "Args" , Arguments ) ;
Writer . WriteObjectEnd ( ) ;
}
private static void WriteField ( string ModuleOrTargetName , JsonWriter Writer , Tuple < string , object? > Field , ILogger Logger )
{
2023-05-30 18:59:32 -04:00
if ( Field . Item2 = = null )
{
return ;
}
2022-07-12 20:57:59 -04:00
string Name = Field . Item1 ;
2023-05-31 13:37:21 -04:00
if ( Field . Item2 is bool vbool )
2022-07-12 20:57:59 -04:00
{
2023-05-31 13:37:21 -04:00
Writer . WriteValue ( Name , vbool ) ;
2022-07-12 20:57:59 -04:00
}
2023-05-31 13:37:21 -04:00
else if ( Field . Item2 is string vstring )
2022-07-12 20:57:59 -04:00
{
2023-05-31 13:37:21 -04:00
if ( ! String . IsNullOrEmpty ( vstring ) )
2023-05-30 18:59:32 -04:00
{
2023-05-31 13:37:21 -04:00
Writer . WriteValue ( Name , vstring ) ;
2023-05-30 18:59:32 -04:00
}
2022-07-12 20:57:59 -04:00
}
2023-05-31 13:37:21 -04:00
else if ( Field . Item2 is int vint )
2022-07-12 20:57:59 -04:00
{
2023-05-31 13:37:21 -04:00
Writer . WriteValue ( Name , vint ) ;
2022-07-12 20:57:59 -04:00
}
2023-05-31 13:37:21 -04:00
else if ( Field . Item2 is double vdouble )
2022-07-12 20:57:59 -04:00
{
2023-05-31 13:37:21 -04:00
Writer . WriteValue ( Name , vdouble ) ;
2022-07-12 20:57:59 -04:00
}
else if ( Field . Item2 is CppStandardVersion version )
{
// Do not use version.ToString(). See: https://youtrack.jetbrains.com/issue/RIDER-68030
switch ( version )
{
case CppStandardVersion . Cpp14 :
Writer . WriteValue ( Name , "Cpp14" ) ;
break ;
case CppStandardVersion . Cpp17 :
Writer . WriteValue ( Name , "Cpp17" ) ;
break ;
case CppStandardVersion . Cpp20 :
Writer . WriteValue ( Name , "Cpp20" ) ;
break ;
case CppStandardVersion . Latest :
Writer . WriteValue ( Name , "Latest" ) ;
break ;
default :
Logger . LogError ( "Unsupported C++ standard type: {Type}" , version ) ;
break ;
}
}
else if ( Field . Item2 is Enum )
{
Writer . WriteValue ( Name , Field . Item2 . ToString ( ) ) ;
}
2023-05-31 13:37:21 -04:00
else if ( Field . Item2 is IEnumerable < string > FieldValue )
2022-07-12 20:57:59 -04:00
{
2023-05-30 18:38:07 -04:00
if ( FieldValue . Any ( ) )
2023-05-30 18:59:32 -04:00
{
2022-07-12 20:57:59 -04:00
Writer . WriteStringArrayField ( Name , FieldValue ) ;
2023-05-30 18:59:32 -04:00
}
2022-07-12 20:57:59 -04:00
}
else
{
Logger . LogWarning ( "Dumping incompatible ToolchainInfo field: {Name} with type: {Field} for: {ModuleOrTarget}" ,
Name , Field . Item2 , ModuleOrTargetName ) ;
}
}
private ToolchainInfo GenerateToolchainInfo ( CppCompileEnvironment CompileEnvironment )
{
ToolchainInfo ToolchainInfo = new ToolchainInfo
{
CppStandard = CompileEnvironment . CppStandard ,
Configuration = CompileEnvironment . Configuration . ToString ( ) ,
bEnableExceptions = CompileEnvironment . bEnableExceptions ,
bOptimizeCode = CompileEnvironment . bOptimizeCode ,
bUseInlining = CompileEnvironment . bUseInlining ,
bUseUnity = CompileEnvironment . bUseUnity ,
bCreateDebugInfo = CompileEnvironment . bCreateDebugInfo ,
bIsBuildingLibrary = CompileEnvironment . bIsBuildingLibrary ,
2023-03-30 17:52:50 -04:00
MinCpuArchX64 = CompileEnvironment . MinCpuArchX64 ,
2022-07-12 20:57:59 -04:00
bIsBuildingDLL = CompileEnvironment . bIsBuildingDLL ,
bUseDebugCRT = CompileEnvironment . bUseDebugCRT ,
bUseRTTI = CompileEnvironment . bUseRTTI ,
bUseStaticCRT = CompileEnvironment . bUseStaticCRT ,
PrecompiledHeaderAction = CompileEnvironment . PrecompiledHeaderAction . ToString ( ) ,
PrecompiledHeaderFile = CompileEnvironment . PrecompiledHeaderFile ? . ToString ( ) ,
2023-01-03 13:35:58 -05:00
ForceIncludeFiles = CompileEnvironment . ForceIncludeFiles . Select ( Item = > Item . ToString ( ) ) . ToList ( ) ,
bEnableCoroutines = CompileEnvironment . bEnableCoroutines
2022-07-12 20:57:59 -04:00
} ;
2024-01-30 15:13:12 -05:00
if ( CompileEnvironment . Architectures . Architectures . Count > = 1 )
{
ToolchainInfo . Architecture = CompileEnvironment . Architectures . Architectures [ 0 ] . ToString ( ) ;
}
2022-07-12 20:57:59 -04:00
if ( CurrentTarget ! . Platform . IsInGroup ( UnrealPlatformGroup . Windows ) )
{
2024-01-30 15:43:41 -05:00
ToolchainInfo . bEnableAddressSanitizer = CurrentTarget . Rules . WindowsPlatform . bEnableAddressSanitizer ;
2024-03-12 20:35:34 -04:00
ToolchainInfo . bUpdatedCPPMacro = CurrentTarget . Rules . WindowsPlatform . bUpdatedCPPMacro ;
2022-07-12 20:57:59 -04:00
WindowsCompiler WindowsPlatformCompiler = CurrentTarget . Rules . WindowsPlatform . Compiler ;
ToolchainInfo . bStrictConformanceMode = WindowsPlatformCompiler . IsMSVC ( ) & & CurrentTarget . Rules . WindowsPlatform . bStrictConformanceMode ;
2024-01-30 15:43:41 -05:00
ToolchainInfo . bStrictPreprocessorConformanceMode =
WindowsPlatformCompiler . IsMSVC ( ) & & CurrentTarget . Rules . WindowsPlatform . bStrictPreprocessorConformance ;
2022-07-12 20:57:59 -04:00
ToolchainInfo . Compiler = WindowsPlatformCompiler . ToString ( ) ;
}
else
{
string PlatformName = $"{CurrentTarget.Platform}Platform" ;
object? Value = typeof ( ReadOnlyTargetRules ) . GetProperty ( PlatformName ) ? . GetValue ( CurrentTarget . Rules ) ;
object? CompilerField = Value ? . GetType ( ) . GetProperty ( "Compiler" ) ? . GetValue ( Value ) ;
if ( CompilerField ! = null )
2023-05-30 18:59:32 -04:00
{
2022-07-12 20:57:59 -04:00
ToolchainInfo . Compiler = CompilerField . ToString ( ) ! ;
2023-05-30 18:59:32 -04:00
}
2022-07-12 20:57:59 -04:00
}
2023-05-30 18:38:07 -04:00
return ToolchainInfo ;
2022-07-12 20:57:59 -04:00
}
}
}