/// <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 UE4: {UE4Root}/Engine/Intermediate/ProjectFiles/.Rider/{Platform}/{Configuration}/{TargetType}/{ProjectName}.json /// For game: {GameRoot}/Intermediate/ProjectFiles/.Rider/{Platform}/{Configuration}/{TargetType}/{ProjectName}.json /// </summary> /// <remarks> /// * <c>UnrealTargetPlatform.Win32</c> will be always ignored. /// * <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> /// <returns></returns> public override bool WriteProjectFile(List <UnrealTargetPlatform> InPlatforms, List <UnrealTargetConfiguration> InConfigurations, PlatformProjectGeneratorCollection PlatformProjectGenerators) { string ProjectName = ProjectFilePath.GetFileNameWithoutAnyExtensions(); DirectoryReference projectRootFolder = DirectoryReference.Combine(RootPath, ".Rider"); List <Tuple <FileReference, UEBuildTarget> > fileToTarget = new List <Tuple <FileReference, UEBuildTarget> >(); foreach (UnrealTargetPlatform Platform in InPlatforms.Where(it => it != UnrealTargetPlatform.Win32)) { foreach (UnrealTargetConfiguration Configuration in InConfigurations) { foreach (ProjectTarget ProjectTarget in ProjectTargets) { if (TargetTypes.Any() && !TargetTypes.Contains(ProjectTarget.TargetRules.Type)) { continue; } // Skip Programs for all configs except for current platform + Development configuration if (ProjectTarget.TargetRules.Type == TargetType.Program && (BuildHostPlatform.Current.Platform != Platform || Configuration != UnrealTargetConfiguration.Development)) { 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; } DirectoryReference ConfigurationFolder = DirectoryReference.Combine(projectRootFolder, Platform.ToString(), Configuration.ToString()); DirectoryReference TargetFolder = DirectoryReference.Combine(ConfigurationFolder, ProjectTarget.TargetRules.Type.ToString()); string DefaultArchitecture = UEBuildPlatform .GetBuildPlatform(BuildHostPlatform.Current.Platform) .GetDefaultArchitecture(ProjectTarget.UnrealProjectFilePath); TargetDescriptor TargetDesc = new TargetDescriptor(ProjectTarget.UnrealProjectFilePath, ProjectTarget.Name, BuildHostPlatform.Current.Platform, UnrealTargetConfiguration.Development, DefaultArchitecture, Arguments); UEBuildTarget BuildTarget = UEBuildTarget.Create(TargetDesc, false, false); FileReference OutputFile = FileReference.Combine(TargetFolder, $"{ProjectName}.json"); fileToTarget.Add(Tuple.Create(OutputFile, BuildTarget)); } } } foreach (Tuple <FileReference, UEBuildTarget> tuple in fileToTarget) { SerializeTarget(tuple.Item1, tuple.Item2); } return(true); }
/// <summary> /// Execute this command /// </summary> /// <param name="Arguments">Command line arguments</param> /// <returns>Exit code (always zero)</returns> public override int Execute(CommandLineArguments Arguments) { Arguments.ApplyTo(this); List <TargetDescriptor> TargetDescriptors = TargetDescriptor.ParseCommandLine(Arguments, false, false); foreach (TargetDescriptor TargetDescriptor in TargetDescriptors) { // Create the target UEBuildTarget Target = UEBuildTarget.Create(TargetDescriptor, false, false); // Get the output file FileReference OutputFile = TargetDescriptor.AdditionalArguments.GetFileReferenceOrDefault("-OutputFile=", null); if (OutputFile == null) { OutputFile = Target.ReceiptFileName.ChangeExtension(".json"); } // Execute code generation actions if (bExecCodeGenActions) { using (ISourceFileWorkingSet WorkingSet = new EmptySourceFileWorkingSet()) { // Create the build configuration object, and read the settings BuildConfiguration BuildConfiguration = new BuildConfiguration(); XmlConfig.ApplyTo(BuildConfiguration); Arguments.ApplyTo(BuildConfiguration); // Create the makefile const bool bIsAssemblingBuild = true; TargetMakefile Makefile = Target.Build(BuildConfiguration, WorkingSet, bIsAssemblingBuild, TargetDescriptor.SingleFileToCompile); ActionGraph.Link(Makefile.Actions); // Filter all the actions to execute HashSet <FileItem> PrerequisiteItems = new HashSet <FileItem>(Makefile.Actions.SelectMany(x => x.ProducedItems).Where(x => x.HasExtension(".h") || x.HasExtension(".cpp"))); List <Action> PrerequisiteActions = ActionGraph.GatherPrerequisiteActions(Makefile.Actions, PrerequisiteItems); // Execute these actions if (PrerequisiteActions.Count > 0) { Log.TraceInformation("Exeucting actions that produce source files..."); ActionGraph.ExecuteActions(BuildConfiguration, PrerequisiteActions); } } } // Write the output file Log.TraceInformation("Writing {0}...", OutputFile); Target.ExportJson(OutputFile); } return(0); }
/// <summary> /// Execute this command /// </summary> /// <param name="Arguments">Command line arguments</param> /// <returns>Exit code (always zero)</returns> public override int Execute(CommandLineArguments Arguments) { List <TargetDescriptor> TargetDescriptors = TargetDescriptor.ParseCommandLine(Arguments, false, false); foreach (TargetDescriptor TargetDescriptor in TargetDescriptors) { // Create the target UEBuildTarget Target = UEBuildTarget.Create(TargetDescriptor, false, false); // Get the output file FileReference OutputFile = TargetDescriptor.AdditionalArguments.GetFileReferenceOrDefault("-OutputFile=", null); if (OutputFile == null) { OutputFile = Target.ReceiptFileName.ChangeExtension(".json"); } // Write the output file Log.TraceInformation("Writing {0}...", OutputFile); Target.ExportJson(OutputFile); } return(0); }
/// <summary> /// Creates the makefile for a target. If an existing, valid makefile already exists on disk, loads that instead. /// </summary> /// <param name="BuildConfiguration">The build configuration</param> /// <param name="TargetDescriptor">Target being built</param> /// <param name="WorkingSet">Set of source files which are part of the working set</param> /// <returns>Makefile for the given target</returns> static TargetMakefile CreateMakefile(BuildConfiguration BuildConfiguration, TargetDescriptor TargetDescriptor, ISourceFileWorkingSet WorkingSet) { // Get the path to the makefile for this target FileReference MakefileLocation = null; if (BuildConfiguration.bUseUBTMakefiles && TargetDescriptor.SpecificFilesToCompile.Count == 0) { MakefileLocation = TargetMakefile.GetLocation(TargetDescriptor.ProjectFile, TargetDescriptor.Name, TargetDescriptor.Platform, TargetDescriptor.Architecture, TargetDescriptor.Configuration); } // Try to load an existing makefile TargetMakefile Makefile = null; if (MakefileLocation != null) { using (Timeline.ScopeEvent("TargetMakefile.Load()")) { string ReasonNotLoaded; Makefile = TargetMakefile.Load(MakefileLocation, TargetDescriptor.ProjectFile, TargetDescriptor.Platform, TargetDescriptor.AdditionalArguments.GetRawArray(), out ReasonNotLoaded); if (Makefile == null) { Log.TraceInformation("Creating makefile for {0} ({1})", TargetDescriptor.Name, ReasonNotLoaded); } } } // If we have a makefile, execute the pre-build steps and check it's still valid bool bHasRunPreBuildScripts = false; if (Makefile != null) { // Execute the scripts. We have to invalidate all cached file info after doing so, because we don't know what may have changed. if (Makefile.PreBuildScripts.Length > 0) { Utils.ExecuteCustomBuildSteps(Makefile.PreBuildScripts); DirectoryItem.ResetAllCachedInfo_SLOW(); } // Don't run the pre-build steps again, even if we invalidate the makefile. bHasRunPreBuildScripts = true; // Check that the makefile is still valid string Reason; if (!TargetMakefile.IsValidForSourceFiles(Makefile, TargetDescriptor.ProjectFile, TargetDescriptor.Platform, WorkingSet, out Reason)) { Log.TraceInformation("Invalidating makefile for {0} ({1})", TargetDescriptor.Name, Reason); Makefile = null; } } // If we couldn't load a makefile, create a new one if (Makefile == null) { // Create the target UEBuildTarget Target; using (Timeline.ScopeEvent("UEBuildTarget.Create()")) { Target = UEBuildTarget.Create(TargetDescriptor, BuildConfiguration.bSkipRulesCompile, BuildConfiguration.bUsePrecompiled); } // Create the pre-build scripts FileReference[] PreBuildScripts = Target.CreatePreBuildScripts(); // Execute the pre-build scripts if (!bHasRunPreBuildScripts) { Utils.ExecuteCustomBuildSteps(PreBuildScripts); bHasRunPreBuildScripts = true; } // Build the target using (Timeline.ScopeEvent("UEBuildTarget.Build()")) { const bool bIsAssemblingBuild = true; Makefile = Target.Build(BuildConfiguration, WorkingSet, bIsAssemblingBuild, TargetDescriptor.SpecificFilesToCompile); } // Save the pre-build scripts onto the makefile Makefile.PreBuildScripts = PreBuildScripts; // Save the additional command line arguments Makefile.AdditionalArguments = TargetDescriptor.AdditionalArguments.GetRawArray(); // Save the environment variables foreach (System.Collections.DictionaryEntry EnvironmentVariable in Environment.GetEnvironmentVariables()) { Makefile.EnvironmentVariables.Add(Tuple.Create((string)EnvironmentVariable.Key, (string)EnvironmentVariable.Value)); } // Save the makefile for next time if (MakefileLocation != null) { using (Timeline.ScopeEvent("TargetMakefile.Save()")) { Makefile.Save(MakefileLocation); } } } else { // Restore the environment variables foreach (Tuple <string, string> EnvironmentVariable in Makefile.EnvironmentVariables) { Environment.SetEnvironmentVariable(EnvironmentVariable.Item1, EnvironmentVariable.Item2); } // If the target needs UHT to be run, we'll go ahead and do that now if (Makefile.UObjectModules.Count > 0) { const bool bIsGatheringBuild = false; const bool bIsAssemblingBuild = true; FileReference ModuleInfoFileName = FileReference.Combine(Makefile.ProjectIntermediateDirectory, TargetDescriptor.Name + ".uhtmanifest"); ExternalExecution.ExecuteHeaderToolIfNecessary(BuildConfiguration, TargetDescriptor.ProjectFile, TargetDescriptor.Name, Makefile.TargetType, Makefile.bHasProjectScriptPlugin, UObjectModules: Makefile.UObjectModules, ModuleInfoFileName: ModuleInfoFileName, bIsGatheringBuild: bIsGatheringBuild, bIsAssemblingBuild: bIsAssemblingBuild, WorkingSet: WorkingSet); } } return(Makefile); }
/// <summary> /// Execute the command /// </summary> /// <param name="Arguments">Command line arguments</param> /// <returns>Exit code</returns> public override int Execute(CommandLineArguments Arguments) { Arguments.ApplyTo(this); // Create the build configuration object, and read the settings BuildConfiguration BuildConfiguration = new BuildConfiguration(); XmlConfig.ApplyTo(BuildConfiguration); Arguments.ApplyTo(BuildConfiguration); // Parse the filter argument FileFilter FileFilter = null; if (FilterRules.Count > 0) { FileFilter = new FileFilter(FileFilterType.Exclude); foreach (string FilterRule in FilterRules) { FileFilter.AddRules(FilterRule.Split(';')); } } // Parse all the target descriptors List <TargetDescriptor> TargetDescriptors = TargetDescriptor.ParseCommandLine(Arguments, BuildConfiguration.bUsePrecompiled, BuildConfiguration.bSkipRulesCompile); // Generate the compile DB for each target using (ISourceFileWorkingSet WorkingSet = new EmptySourceFileWorkingSet()) { // Find the compile commands for each file in the target Dictionary <FileReference, string> FileToCommand = new Dictionary <FileReference, string>(); foreach (TargetDescriptor TargetDescriptor in TargetDescriptors) { // Disable PCHs and unity builds for the target TargetDescriptor.AdditionalArguments = TargetDescriptor.AdditionalArguments.Append(new string[] { "-NoPCH", "-DisableUnity" }); // Create a makefile for the target UEBuildTarget Target = UEBuildTarget.Create(TargetDescriptor, BuildConfiguration.bSkipRulesCompile, BuildConfiguration.bUsePrecompiled); // Find the location of the compiler VCEnvironment Environment = VCEnvironment.Create(WindowsCompiler.Clang, Target.Platform, Target.Rules.WindowsPlatform.Architecture, null, Target.Rules.WindowsPlatform.WindowsSdkVersion, null); FileReference ClangPath = FileReference.Combine(Environment.CompilerDir, "bin", "clang++.exe"); // Convince each module to output its generated code include path foreach (UEBuildBinary Binary in Target.Binaries) { foreach (UEBuildModuleCPP Module in Binary.Modules.OfType <UEBuildModuleCPP>()) { Module.bAddGeneratedCodeIncludePath = true; } } // Create all the binaries and modules CppCompileEnvironment GlobalCompileEnvironment = Target.CreateCompileEnvironmentForProjectFiles(); foreach (UEBuildBinary Binary in Target.Binaries) { CppCompileEnvironment BinaryCompileEnvironment = Binary.CreateBinaryCompileEnvironment(GlobalCompileEnvironment); foreach (UEBuildModuleCPP Module in Binary.Modules.OfType <UEBuildModuleCPP>()) { if (!Module.Rules.bUsePrecompiled) { UEBuildModuleCPP.InputFileCollection InputFileCollection = Module.FindInputFiles(Target.Platform, new Dictionary <DirectoryItem, FileItem[]>()); List <FileItem> InputFiles = new List <FileItem>(); InputFiles.AddRange(InputFileCollection.CPPFiles); InputFiles.AddRange(InputFileCollection.CCFiles); CppCompileEnvironment ModuleCompileEnvironment = Module.CreateModuleCompileEnvironment(Target.Rules, BinaryCompileEnvironment); StringBuilder CommandBuilder = new StringBuilder(); CommandBuilder.AppendFormat("\"{0}\"", ClangPath.FullName); foreach (FileItem ForceIncludeFile in ModuleCompileEnvironment.ForceIncludeFiles) { CommandBuilder.AppendFormat(" -include \"{0}\"", ForceIncludeFile.FullName); } foreach (string Definition in ModuleCompileEnvironment.Definitions) { CommandBuilder.AppendFormat(" -D\"{0}\"", Definition); } foreach (DirectoryReference IncludePath in ModuleCompileEnvironment.UserIncludePaths) { CommandBuilder.AppendFormat(" -I\"{0}\"", IncludePath); } foreach (DirectoryReference IncludePath in ModuleCompileEnvironment.SystemIncludePaths) { CommandBuilder.AppendFormat(" -I\"{0}\"", IncludePath); } foreach (FileItem InputFile in InputFiles) { if (FileFilter == null || FileFilter.Matches(InputFile.Location.MakeRelativeTo(UnrealBuildTool.RootDirectory))) { FileToCommand[InputFile.Location] = String.Format("{0} \"{1}\"", CommandBuilder, InputFile.FullName); } } } } } } // Write the compile database FileReference DatabaseFile = FileReference.Combine(UnrealBuildTool.RootDirectory, "compile_commands.json"); using (JsonWriter Writer = new JsonWriter(DatabaseFile)) { Writer.WriteArrayStart(); foreach (KeyValuePair <FileReference, string> FileCommandPair in FileToCommand.OrderBy(x => x.Key.FullName)) { Writer.WriteObjectStart(); Writer.WriteValue("file", FileCommandPair.Key.FullName); Writer.WriteValue("command", FileCommandPair.Value); Writer.WriteValue("directory", UnrealBuildTool.EngineSourceDirectory.ToString()); Writer.WriteObjectEnd(); } Writer.WriteArrayEnd(); } } return(0); }
/// <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 UE4: {UE4Root}/Engine/Intermediate/ProjectFiles/.Rider/{Platform}/{Configuration}/{TargetType}/{ProjectName}.json /// For game: {GameRoot}/Intermediate/ProjectFiles/.Rider/{Platform}/{Configuration}/{TargetType}/{ProjectName}.json /// </summary> /// <remarks> /// * <c>UnrealTargetPlatform.Win32</c> will be always ignored. /// * <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> /// <returns></returns> public bool WriteProjectFile(List <UnrealTargetPlatform> InPlatforms, List <UnrealTargetConfiguration> InConfigurations, PlatformProjectGeneratorCollection PlatformProjectGenerators, JsonWriterStyle Minimize) { string ProjectName = ProjectFilePath.GetFileNameWithoutAnyExtensions(); DirectoryReference ProjectRootFolder = RootPath; List <Tuple <FileReference, UEBuildTarget> > FileToTarget = new List <Tuple <FileReference, UEBuildTarget> >(); foreach (UnrealTargetPlatform Platform in InPlatforms) { foreach (UnrealTargetConfiguration Configuration in InConfigurations) { foreach (ProjectTarget ProjectTarget in ProjectTargets) { if (TargetTypes.Any() && !TargetTypes.Contains(ProjectTarget.TargetRules.Type)) { continue; } // Skip Programs for all configs except for current platform + Development & Debug configurations if (ProjectTarget.TargetRules.Type == TargetType.Program && (BuildHostPlatform.Current.Platform != Platform || !(Configuration == UnrealTargetConfiguration.Development || Configuration == UnrealTargetConfiguration.Debug))) { 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; } DirectoryReference ConfigurationFolder = DirectoryReference.Combine(ProjectRootFolder, Platform.ToString(), Configuration.ToString()); DirectoryReference TargetFolder = DirectoryReference.Combine(ConfigurationFolder, ProjectTarget.TargetRules.Type.ToString()); string DefaultArchitecture = UEBuildPlatform .GetBuildPlatform(Platform) .GetDefaultArchitecture(ProjectTarget.UnrealProjectFilePath); TargetDescriptor TargetDesc = new TargetDescriptor(ProjectTarget.UnrealProjectFilePath, ProjectTarget.Name, Platform, Configuration, DefaultArchitecture, Arguments); try { UEBuildTarget BuildTarget = UEBuildTarget.Create(TargetDesc, false, false); FileReference OutputFile = FileReference.Combine(TargetFolder, $"{ProjectName}.json"); FileToTarget.Add(Tuple.Create(OutputFile, BuildTarget)); } catch (Exception Ex) { Log.TraceWarning("Exception while generating include data for Target:{0}, Platform: {1}, Configuration: {2}", TargetDesc.Name, Platform.ToString(), Configuration.ToString()); Log.TraceWarning(Ex.ToString()); } } } } foreach (Tuple <FileReference, UEBuildTarget> tuple in FileToTarget) { try { CurrentTarget = tuple.Item2; CurrentTarget.PreBuildSetup(); SerializeTarget(tuple.Item1, CurrentTarget, Minimize); } catch (Exception Ex) { Log.TraceWarning("Exception while generating include data for Target:{0}, Platform: {1}, Configuration: {2}", tuple.Item2.AppName, tuple.Item2.Platform.ToString(), tuple.Item2.Configuration.ToString()); Log.TraceWarning(Ex.ToString()); } } return(true); }