// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. using System; using System.Collections.Generic; using System.Linq; using Tools.DotNETCommon; namespace UnrealBuildTool { internal class RiderProjectFile : ProjectFile { public DirectoryReference RootPath; public HashSet TargetTypes; public CommandLineArguments Arguments; public RiderProjectFile(FileReference InProjectFilePath) : base(InProjectFilePath) { } /// /// Write project file info in JSON file. /// For every combination of UnrealTargetPlatform, UnrealTargetConfiguration and TargetType /// 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 /// /// /// * UnrealTargetPlatform.Win32 will be always ignored. /// * TargetType.Editor will be generated for current platform only and will ignore UnrealTargetConfiguration.Test and UnrealTargetConfiguration.Shipping configurations /// * TargetType.Program will be generated for current platform only and UnrealTargetConfiguration.Development configuration only /// /// /// /// /// public override bool WriteProjectFile(List InPlatforms, List InConfigurations, PlatformProjectGeneratorCollection PlatformProjectGenerators) { string ProjectName = ProjectFilePath.GetFileNameWithoutAnyExtensions(); DirectoryReference projectRootFolder = DirectoryReference.Combine(RootPath, ".Rider"); List> fileToTarget = new List>(); 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 tuple in fileToTarget) { SerializeTarget(tuple.Item1, tuple.Item2); } return true; } private static void SerializeTarget(FileReference OutputFile, UEBuildTarget BuildTarget) { DirectoryReference.CreateDirectory(OutputFile.Directory); using (JsonWriter Writer = new JsonWriter(OutputFile)) { ExportTarget(BuildTarget, Writer); } } /// /// Write a Target to a JSON writer. Is array is empty, don't write anything /// /// /// Writer for the array data private static void ExportTarget(UEBuildTarget Target, JsonWriter Writer) { 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); } ExportEnvironmentToJson(Target, Writer); if(Target.Binaries.Any()) { Writer.WriteArrayStart("Binaries"); foreach (UEBuildBinary Binary in Target.Binaries) { Writer.WriteObjectStart(); ExportBinary(Binary, Writer); Writer.WriteObjectEnd(); } Writer.WriteArrayEnd(); } HashSet moduleNames = new HashSet(); Writer.WriteObjectStart("Modules"); foreach (UEBuildBinary Binary in Target.Binaries) { foreach (UEBuildModule Module in Binary.Modules) { if(moduleNames.Add(Module.Name)) { Writer.WriteObjectStart(Module.Name); ExportModule(Module, Binary.OutputDir, Target.GetExecutableDir(), Writer); Writer.WriteObjectEnd(); } } } Writer.WriteObjectEnd(); ExportPluginsFromTarget(Target, Writer); Writer.WriteObjectEnd(); } /// /// Write a Module to a JSON writer. If array is empty, don't write anything /// /// /// /// Writer for the array data /// private static void ExportModule(UEBuildModule Module, DirectoryReference BinaryOutputDir, DirectoryReference TargetOutputDir, JsonWriter Writer) { Writer.WriteValue("Name", Module.Name); Writer.WriteValue("Directory", Module.ModuleDirectory.FullName); Writer.WriteValue("Rules", Module.RulesFile.FullName); Writer.WriteValue("PCHUsage", Module.Rules.PCHUsage.ToString()); UEBuildModuleCPP ModuleCPP = Module as UEBuildModuleCPP; if (ModuleCPP != null) { Writer.WriteValue("GeneratedCodeDirectory", ModuleCPP.GeneratedCodeDirectory != null ? ModuleCPP.GeneratedCodeDirectory.FullName : string.Empty); } 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); ExportJsonStringArray(Writer, "PublicSystemIncludePaths", Module.PublicSystemIncludePaths.Select(x => x.FullName)); ExportJsonStringArray(Writer, "PublicIncludePaths", Module.PublicIncludePaths.Select(x => x.FullName)); ExportJsonStringArray(Writer, "LegacyPublicIncludePaths", Module.LegacyPublicIncludePaths.Select(x => x.FullName)); ExportJsonStringArray(Writer, "PrivateIncludePaths", Module.PrivateIncludePaths.Select(x => x.FullName)); ExportJsonStringArray(Writer, "PublicLibraryPaths", Module.PublicSystemLibraryPaths.Select(x => x.FullName)); ExportJsonStringArray(Writer, "PublicAdditionalLibraries", Module.PublicAdditionalLibraries); ExportJsonStringArray(Writer, "PublicFrameworks", Module.PublicFrameworks); ExportJsonStringArray(Writer, "PublicWeakFrameworks", Module.PublicWeakFrameworks); ExportJsonStringArray(Writer, "PublicDelayLoadDLLs", Module.PublicDelayLoadDLLs); ExportJsonStringArray(Writer, "PublicDefinitions", Module.PublicDefinitions); ExportJsonStringArray(Writer, "PrivateDefinitions", Module.Rules.PrivateDefinitions); ExportJsonStringArray(Writer, "ProjectDefinitions", /* TODO: Add method ShouldAddProjectDefinitions */ !Module.Rules.bTreatAsEngineModule ? Module.Rules.Target.ProjectDefinitions : new string[0]); ExportJsonStringArray(Writer, "ApiDefinitions", Module.GetEmptyApiMacros()); Writer.WriteValue("ShouldAddLegacyPublicIncludePaths", Module.Rules.bLegacyPublicIncludePaths); if(Module.Rules.CircularlyReferencedDependentModules.Any()) { Writer.WriteArrayStart("CircularlyReferencedModules"); foreach (string ModuleName in Module.Rules.CircularlyReferencedDependentModules) { Writer.WriteValue(ModuleName); } Writer.WriteArrayEnd(); } if(Module.Rules.RuntimeDependencies.Inner.Any()) { // 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)); } catch(BuildException buildException) { Log.TraceVerbose("Value {0} for module {1} will not be stored. Reason: {2}", "Path", Module.Name, buildException); } if (RuntimeDependency.SourcePath != null) { try { Writer.WriteValue("SourcePath", Module.ExpandPathVariables(RuntimeDependency.SourcePath, BinaryOutputDir, TargetOutputDir)); } catch(BuildException buildException) { Log.TraceVerbose("Value {0} for module {1} will not be stored. Reason: {2}", "SourcePath", Module.Name, buildException); } } Writer.WriteValue("Type", RuntimeDependency.Type.ToString()); Writer.WriteObjectEnd(); } Writer.WriteArrayEnd(); } } /// /// Write an array of Modules to a JSON writer. If array is empty, don't write anything /// /// Writer for the array data /// Name of the array property /// Sequence of Modules to write. May be null. private static void ExportJsonModuleArray(JsonWriter Writer, string ArrayName, IEnumerable Modules) { if (Modules == null || !Modules.Any()) return; Writer.WriteArrayStart(ArrayName); foreach (UEBuildModule Module in Modules) { Writer.WriteValue(Module.Name); } Writer.WriteArrayEnd(); } /// /// Write an array of strings to a JSON writer. Ifl array is empty, don't write anything /// /// Writer for the array data /// Name of the array property /// Sequence of strings to write. May be null. static void ExportJsonStringArray(JsonWriter Writer, string ArrayName, IEnumerable Strings) { if (Strings == null || !Strings.Any()) return; Writer.WriteArrayStart(ArrayName); foreach (string String in Strings) { Writer.WriteValue(String); } Writer.WriteArrayEnd(); } /// /// Write uplugin content to a JSON writer /// /// Uplugin description /// JSON writer private static void ExportPlugin(UEBuildPlugin Plugin, JsonWriter Writer) { Writer.WriteObjectStart(Plugin.Name); Writer.WriteValue("File", Plugin.File.FullName); Writer.WriteValue("Type", Plugin.Type.ToString()); if(Plugin.Dependencies.Any()) { Writer.WriteStringArrayField("Dependencies", Plugin.Dependencies.Select(it => it.Name)); } if(Plugin.Modules.Any()) { Writer.WriteStringArrayField("Modules", Plugin.Modules.Select(it => it.Name)); } Writer.WriteObjectEnd(); } /// /// Setup plugins for Target and write plugins to JSON writer. Don't write anything if there are no plugins /// /// /// private static void ExportPluginsFromTarget(UEBuildTarget Target, JsonWriter Writer) { Target.SetupPlugins(); if (!Target.BuildPlugins.Any()) return; Writer.WriteObjectStart("Plugins"); foreach (UEBuildPlugin plugin in Target.BuildPlugins) { ExportPlugin(plugin, Writer); } Writer.WriteObjectEnd(); } /// /// Write information about this binary to a JSON file /// /// /// Writer for this binary's data private static void ExportBinary(UEBuildBinary Binary, JsonWriter Writer) { Writer.WriteValue("File", Binary.OutputFilePath.FullName); Writer.WriteValue("Type", Binary.Type.ToString()); Writer.WriteArrayStart("Modules"); foreach(UEBuildModule Module in Binary.Modules) { Writer.WriteValue(Module.Name); } Writer.WriteArrayEnd(); } /// /// Write C++ toolchain information to JSON writer /// /// /// private static void ExportEnvironmentToJson(UEBuildTarget Target, JsonWriter Writer) { CppCompileEnvironment GlobalCompileEnvironment = Target.CreateCompileEnvironmentForProjectFiles(); Writer.WriteArrayStart("EnvironmentIncludePaths"); foreach (DirectoryReference Path in GlobalCompileEnvironment.UserIncludePaths) { Writer.WriteValue(Path.FullName); } foreach (DirectoryReference Path in GlobalCompileEnvironment.SystemIncludePaths) { Writer.WriteValue(Path.FullName); } // TODO: get corresponding includes for specific platforms if (UEBuildPlatform.IsPlatformInGroup(Target.Platform, UnrealPlatformGroup.Windows)) { foreach (DirectoryReference Path in Target.Rules.WindowsPlatform.Environment.IncludePaths) { Writer.WriteValue(Path.FullName); } } Writer.WriteArrayEnd(); Writer.WriteArrayStart("EnvironmentDefinitions"); foreach (string Definition in GlobalCompileEnvironment.Definitions) { Writer.WriteValue(Definition); } Writer.WriteArrayEnd(); } } }