// Anonymous function that writes project configuration data private void WriteConfiguration(string ProjectName, ProjectConfigAndTargetCombination Combination, StringBuilder VCProjectFileContent, StringBuilder VCUserFileContent) { UnrealTargetPlatform Platform = Combination.Platform; UnrealTargetConfiguration Configuration = Combination.Configuration; UEPlatformProjectGenerator ProjGenerator = UEPlatformProjectGenerator.GetPlatformProjectGenerator(Platform, true); if (((ProjGenerator == null) && (Platform != UnrealTargetPlatform.Unknown))) { return; } string UProjectPath = ""; if (IsForeignProject) { UProjectPath = "\"$(SolutionDir)$(ProjectName).uproject\""; } string ConditionString = "Condition=\"'$(Configuration)|$(Platform)'=='" + Combination.ProjectConfigurationAndPlatformName + "'\""; { VCProjectFileContent.Append( " <ImportGroup " + ConditionString + " Label=\"PropertySheets\">" + ProjectFileGenerator.NewLine + " <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />" + ProjectFileGenerator.NewLine + " </ImportGroup>" + ProjectFileGenerator.NewLine); if (IsStubProject) { string ProjectRelativeUnusedDirectory = NormalizeProjectPath(Path.Combine(ProjectFileGenerator.EngineRelativePath, BuildConfiguration.BaseIntermediateFolder, "Unused")); VCProjectFileContent.Append( " <PropertyGroup " + ConditionString + ">" + ProjectFileGenerator.NewLine + " <OutDir>" + ProjectRelativeUnusedDirectory + Path.DirectorySeparatorChar + "</OutDir>" + ProjectFileGenerator.NewLine + " <IntDir>" + ProjectRelativeUnusedDirectory + Path.DirectorySeparatorChar + "</IntDir>" + ProjectFileGenerator.NewLine + " <NMakeBuildCommandLine>@rem Nothing to do.</NMakeBuildCommandLine>" + ProjectFileGenerator.NewLine + " <NMakeReBuildCommandLine>@rem Nothing to do.</NMakeReBuildCommandLine>" + ProjectFileGenerator.NewLine + " <NMakeCleanCommandLine>@rem Nothing to do.</NMakeCleanCommandLine>" + ProjectFileGenerator.NewLine + " <NMakeOutput/>" + ProjectFileGenerator.NewLine + " </PropertyGroup>" + ProjectFileGenerator.NewLine); } else if(ProjectFileGenerator.bGeneratingRocketProjectFiles && Combination.ProjectTarget != null && Combination.ProjectTarget.TargetRules != null && !Combination.ProjectTarget.TargetRules.SupportsPlatform(Combination.Platform)) { List<UnrealTargetPlatform> SupportedPlatforms = new List<UnrealTargetPlatform>(); Combination.ProjectTarget.TargetRules.GetSupportedPlatforms(ref SupportedPlatforms); string ProjectRelativeUnusedDirectory = NormalizeProjectPath(Path.Combine(ProjectFileGenerator.EngineRelativePath, BuildConfiguration.BaseIntermediateFolder, "Unused")); VCProjectFileContent.AppendFormat( " <PropertyGroup " + ConditionString + ">" + ProjectFileGenerator.NewLine + " <OutDir>" + ProjectRelativeUnusedDirectory + Path.DirectorySeparatorChar + "</OutDir>" + ProjectFileGenerator.NewLine + " <IntDir>" + ProjectRelativeUnusedDirectory + Path.DirectorySeparatorChar + "</IntDir>" + ProjectFileGenerator.NewLine + " <NMakeBuildCommandLine>@echo {0} is not a supported platform for {1}. Valid platforms are {2}.</NMakeBuildCommandLine>" + ProjectFileGenerator.NewLine + " <NMakeReBuildCommandLine>@echo {0} is not a supported platform for {1}. Valid platforms are {2}.</NMakeReBuildCommandLine>" + ProjectFileGenerator.NewLine + " <NMakeCleanCommandLine>@echo {0} is not a supported platform for {1}. Valid platforms are {2}.</NMakeCleanCommandLine>" + ProjectFileGenerator.NewLine + " <NMakeOutput/>" + ProjectFileGenerator.NewLine + " </PropertyGroup>" + ProjectFileGenerator.NewLine, Combination.Platform, Utils.GetFilenameWithoutAnyExtensions(Combination.ProjectTarget.TargetFilePath), String.Join(", ", SupportedPlatforms.Select(x => x.ToString()))); } else { TargetRules TargetRulesObject = Combination.ProjectTarget.TargetRules; string TargetFilePath = Combination.ProjectTarget.TargetFilePath; string TargetName = Utils.GetFilenameWithoutAnyExtensions(TargetFilePath); var UBTPlatformName = IsStubProject ? StubProjectPlatformName : Platform.ToString(); var UBTConfigurationName = IsStubProject ? StubProjectConfigurationName : Configuration.ToString(); // Setup output path var BuildPlatform = UEBuildPlatform.GetBuildPlatform(Platform); // Figure out if this is a monolithic build bool bShouldCompileMonolithic = BuildPlatform.ShouldCompileMonolithicBinary(Platform); bShouldCompileMonolithic |= TargetRulesObject.ShouldCompileMonolithic(Platform, Configuration); // Get the output directory string EngineRootDirectory = Path.GetFullPath(ProjectFileGenerator.EngineRelativePath); string RootDirectory = EngineRootDirectory; if ((TargetRules.IsAGame(TargetRulesObject.Type) || TargetRulesObject.Type == TargetRules.TargetType.Server) && bShouldCompileMonolithic && !TargetRulesObject.bOutputToEngineBinaries) { if (UnrealBuildTool.HasUProjectFile() && Utils.IsFileUnderDirectory(TargetFilePath, UnrealBuildTool.GetUProjectPath())) { RootDirectory = Path.GetFullPath(UnrealBuildTool.GetUProjectPath()); } else { string UnrealProjectPath = UProjectInfo.GetProjectFilePath(ProjectName); if (!String.IsNullOrEmpty(UnrealProjectPath)) { RootDirectory = Path.GetDirectoryName(Path.GetFullPath(UnrealProjectPath)); } } } if(TargetRulesObject.Type == TargetRules.TargetType.Program && !TargetRulesObject.bOutputToEngineBinaries) { string UnrealProjectPath = UProjectInfo.GetProjectForTarget(TargetName); if (!String.IsNullOrEmpty(UnrealProjectPath)) { RootDirectory = Path.GetDirectoryName(Path.GetFullPath(UnrealProjectPath)); } } // Get the output directory string OutputDirectory = Path.Combine(RootDirectory, "Binaries", UBTPlatformName); // Get the executable name (minus any platform or config suffixes) string BaseExeName = TargetName; if (!bShouldCompileMonolithic && TargetRulesObject.Type != TargetRules.TargetType.Program) { // Figure out what the compiled binary will be called so that we can point the IDE to the correct file string TargetConfigurationName = TargetRulesObject.ConfigurationName; if (TargetConfigurationName != TargetRules.TargetType.Game.ToString() && TargetConfigurationName != TargetRules.TargetType.Program.ToString()) { BaseExeName = "UE4" + TargetConfigurationName; } } // Make the output file path string NMakePath = Path.Combine(OutputDirectory, BaseExeName); if (Configuration != UnrealTargetConfiguration.Development && (Configuration != UnrealTargetConfiguration.DebugGame || bShouldCompileMonolithic)) { NMakePath += "-" + UBTPlatformName + "-" + UBTConfigurationName; } NMakePath += BuildPlatform.GetActiveArchitecture(); NMakePath += BuildPlatform.GetBinaryExtension(UEBuildBinaryType.Executable); NMakePath = (BuildPlatform as UEBuildPlatform).ModifyNMakeOutput(NMakePath); VCProjectFileContent.Append( " <PropertyGroup " + ConditionString + ">" + ProjectFileGenerator.NewLine); string PathStrings = (ProjGenerator != null) ? ProjGenerator.GetVisualStudioPathsEntries(Platform, Configuration, TargetRulesObject.Type, TargetFilePath, ProjectFilePath, NMakePath) : ""; if (string.IsNullOrEmpty(PathStrings) || (PathStrings.Contains("<IntDir>") == false)) { string ProjectRelativeUnusedDirectory = "$(ProjectDir)..\\Build\\Unused"; VCProjectFileContent.Append( PathStrings + " <OutDir>" + ProjectRelativeUnusedDirectory + Path.DirectorySeparatorChar + "</OutDir>" + ProjectFileGenerator.NewLine + " <IntDir>" + ProjectRelativeUnusedDirectory + Path.DirectorySeparatorChar + "</IntDir>" + ProjectFileGenerator.NewLine); } else { VCProjectFileContent.Append(PathStrings); } if (TargetRules.IsGameType(TargetRulesObject.Type) && (TargetRules.IsEditorType(TargetRulesObject.Type) == false)) { // Allow platforms to add any special properties they require... like aumid override for Xbox One UEPlatformProjectGenerator.GenerateGamePlatformSpecificProperties(Platform, Configuration, TargetRulesObject.Type, VCProjectFileContent, RootDirectory, TargetFilePath); } // This is the standard UE4 based project NMake build line: // ..\..\Build\BatchFiles\Build.bat <TARGETNAME> <PLATFORM> <CONFIGURATION> // ie ..\..\Build\BatchFiles\Build.bat BlankProgram Win64 Debug string BuildArguments = " " + TargetName + " " + UBTPlatformName + " " + UBTConfigurationName; if(ProjectFileGenerator.bUsePrecompiled) { BuildArguments += " -useprecompiled"; } if (IsForeignProject) { BuildArguments += " " + UProjectPath + (UnrealBuildTool.RunningRocket() ? " -rocket" : ""); } // Always wait for the mutex between UBT invocations, so that building the whole solution doesn't fail. BuildArguments += " -waitmutex"; string BatchFilesDirectoryName = Path.Combine(ProjectFileGenerator.EngineRelativePath, "Build", "BatchFiles"); // @todo UWP: For the MS toolchains, if an override was set for project generation, push that into the build strings to override the build toolchain as well string BuildToolOverride = ""; if (UnrealBuildTool.CommandLineContains("-2012")) { BuildToolOverride = " -2012"; } if (UnrealBuildTool.CommandLineContains("-2013")) { BuildToolOverride = " -2013"; } if (UnrealBuildTool.CommandLineContains("-2015")) { BuildToolOverride = " -2015"; } BuildArguments += BuildToolOverride; // NMake Build command line VCProjectFileContent.Append(" <NMakeBuildCommandLine>"); VCProjectFileContent.Append(EscapePath(NormalizeProjectPath(Path.Combine(BatchFilesDirectoryName, "Build.bat"))) + BuildArguments.ToString()); VCProjectFileContent.Append("</NMakeBuildCommandLine>" + ProjectFileGenerator.NewLine); // NMake ReBuild command line VCProjectFileContent.Append(" <NMakeReBuildCommandLine>"); VCProjectFileContent.Append(EscapePath(NormalizeProjectPath(Path.Combine(BatchFilesDirectoryName, "Rebuild.bat"))) + BuildArguments.ToString()); VCProjectFileContent.Append("</NMakeReBuildCommandLine>" + ProjectFileGenerator.NewLine); // NMake Clean command line VCProjectFileContent.Append(" <NMakeCleanCommandLine>"); VCProjectFileContent.Append(EscapePath(NormalizeProjectPath(Path.Combine(BatchFilesDirectoryName, "Clean.bat"))) + BuildArguments.ToString()); VCProjectFileContent.Append("</NMakeCleanCommandLine>" + ProjectFileGenerator.NewLine); VCProjectFileContent.Append(" <NMakeOutput>"); VCProjectFileContent.Append(NormalizeProjectPath(NMakePath)); VCProjectFileContent.Append("</NMakeOutput>" + ProjectFileGenerator.NewLine); VCProjectFileContent.Append(" </PropertyGroup>" + ProjectFileGenerator.NewLine); string LayoutDirString = (ProjGenerator != null) ? ProjGenerator.GetVisualStudioLayoutDirSection(Platform, Configuration, ConditionString, Combination.ProjectTarget.TargetRules.Type, Combination.ProjectTarget.TargetFilePath, ProjectFilePath, NMakePath) : ""; VCProjectFileContent.Append(LayoutDirString); } if (VCUserFileContent != null && Combination.ProjectTarget != null) { TargetRules TargetRulesObject = Combination.ProjectTarget.TargetRules; if ((Platform == UnrealTargetPlatform.Win32) || (Platform == UnrealTargetPlatform.Win64) || (Platform == UnrealTargetPlatform.UWP)) { VCUserFileContent.Append( " <PropertyGroup " + ConditionString + ">" + ProjectFileGenerator.NewLine); if (TargetRulesObject.Type != TargetRules.TargetType.Game) { string DebugOptions = ""; if(IsForeignProject) { DebugOptions += UProjectPath; } else if(TargetRulesObject.Type == TargetRules.TargetType.Editor && ProjectName != "UE4") { DebugOptions += ProjectName; } if (Configuration == UnrealTargetConfiguration.Debug || Configuration == UnrealTargetConfiguration.DebugGame) { DebugOptions += " -debug"; } else if (Configuration == UnrealTargetConfiguration.Shipping) { DebugOptions += " -shipping"; } VCUserFileContent.Append( " <LocalDebuggerCommandArguments>" + DebugOptions + "</LocalDebuggerCommandArguments>" + ProjectFileGenerator.NewLine ); } VCUserFileContent.Append( " <DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>" + ProjectFileGenerator.NewLine ); VCUserFileContent.Append( " </PropertyGroup>" + ProjectFileGenerator.NewLine ); } string PlatformUserFileStrings = (ProjGenerator != null) ? ProjGenerator.GetVisualStudioUserFileStrings(Platform, Configuration, ConditionString, TargetRulesObject, Combination.ProjectTarget.TargetFilePath, ProjectFilePath) : ""; VCUserFileContent.Append(PlatformUserFileStrings); } } }
// Anonymous function that writes pre-Default.props configuration data private void WritePreDefaultPropsConfiguration(ProjectConfigAndTargetCombination Combination, StringBuilder VCProjectFileContent) { STPlatformProjectGenerator ProjGenerator = STPlatformProjectGenerator.GetPlatformProjectGenerator(Combination.Platform, true); if (((ProjGenerator == null) && (Combination.Platform != STTargetPlatform.Unknown))) { return; } string ConditionString = "Condition=\"'$(Configuration)|$(Platform)'=='" + Combination.ProjectConfigurationAndPlatformName + "'\""; string PlatformToolsetString = (ProjGenerator != null) ? ProjGenerator.GetVisualStudioPlatformToolsetString(Combination.Platform, Combination.Configuration, this) : ""; if (String.IsNullOrEmpty(PlatformToolsetString)) { if (VCProjectFileGenerator.ProjectFileFormat == VCProjectFileGenerator.VCProjectFileFormat.VisualStudio2013) { PlatformToolsetString = " <PlatformToolset>v120</PlatformToolset>" + ProjectFileGenerator.NewLine; } else if (VCProjectFileGenerator.ProjectFileFormat == VCProjectFileGenerator.VCProjectFileFormat.VisualStudio2012) { PlatformToolsetString = " <PlatformToolset>v110</PlatformToolset>" + ProjectFileGenerator.NewLine; } } string PlatformConfigurationType = (ProjGenerator == null) ? "Makefile" : ProjGenerator.GetVisualStudioPlatformConfigurationType(Combination.Platform); VCProjectFileContent.Append( " <PropertyGroup " + ConditionString + " Label=\"Configuration\">" + ProjectFileGenerator.NewLine + " <ConfigurationType>" + PlatformConfigurationType + "</ConfigurationType>" + ProjectFileGenerator.NewLine + PlatformToolsetString + " </PropertyGroup>" + ProjectFileGenerator.NewLine ); }
/// Implements Project interface public override bool WriteProjectFile(List<UnrealTargetPlatform> InPlatforms, List<UnrealTargetConfiguration> InConfigurations) { string ProjectName = Path.GetFileNameWithoutExtension(ProjectFilePath); bool bSuccess = true; // NOTE: We intentionally do not SORT the source file list, as we want the order they're written to disk to be consistent // with how they are stored in memory. This makes for more consistent Unity compiles when alternating between // using "auto" projects and on-disk projects for builds. var ShouldSortSourceFiles = false; if( ShouldSortSourceFiles ) { // Source our list of source files Comparison<SourceFile> SourceFileComparer = ( FileA, FileB ) => { return FileA.FilePath.CompareTo( FileB.FilePath ); }; SourceFiles.Sort( SourceFileComparer ); } // Build up the new include search path string var VCIncludeSearchPaths = new StringBuilder(); { foreach (var CurPath in IntelliSenseIncludeSearchPaths) { VCIncludeSearchPaths.Append(CurPath + ";"); } foreach (var CurPath in IntelliSenseSystemIncludeSearchPaths) { VCIncludeSearchPaths.Append(CurPath + ";"); } if (InPlatforms.Contains(UnrealTargetPlatform.UWP)) { VCIncludeSearchPaths.Append(UWPToolChain.GetVCIncludePaths(CPPTargetPlatform.UWP) + ";"); } else if (InPlatforms.Contains(UnrealTargetPlatform.Win64)) { VCIncludeSearchPaths.Append(VCToolChain.GetVCIncludePaths(CPPTargetPlatform.Win64) + ";"); } else if (InPlatforms.Contains(UnrealTargetPlatform.Win32)) { VCIncludeSearchPaths.Append(VCToolChain.GetVCIncludePaths(CPPTargetPlatform.Win32) + ";"); } } var VCPreprocessorDefinitions = new StringBuilder(); foreach( var CurDef in IntelliSensePreprocessorDefinitions ) { if( VCPreprocessorDefinitions.Length > 0 ) { VCPreprocessorDefinitions.Append( ';' ); } VCPreprocessorDefinitions.Append( CurDef ); } // Setup VC project file content var VCProjectFileContent = new StringBuilder(); var VCFiltersFileContent = new StringBuilder(); var VCUserFileContent = new StringBuilder(); // Visual Studio doesn't require a *.vcxproj.filters file to even exist alongside the project unless // it actually has something of substance in it. We'll avoid saving it out unless we need to. var FiltersFileIsNeeded = false; // Project file header VCProjectFileContent.Append( "<?xml version=\"1.0\" encoding=\"utf-8\"?>" + ProjectFileGenerator.NewLine + ProjectFileGenerator.NewLine + "<Project DefaultTargets=\"Build\" ToolsVersion=\"" + VCProjectFileGenerator.ProjectFileToolVersionString + "\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">" + ProjectFileGenerator.NewLine); bool bGenerateUserFileContent = UEPlatformProjectGenerator.PlatformRequiresVSUserFileGeneration(InPlatforms, InConfigurations); if (bGenerateUserFileContent) { VCUserFileContent.Append( "<?xml version=\"1.0\" encoding=\"utf-8\"?>" + ProjectFileGenerator.NewLine + ProjectFileGenerator.NewLine + "<Project ToolsVersion=\"" + VCProjectFileGenerator.ProjectFileToolVersionString + "\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">" + ProjectFileGenerator.NewLine ); } // Build up a list of platforms and configurations this project will support. In this list, Unknown simply // means that we should use the default "stub" project platform and configuration name. var ProjectConfigAndTargetCombinations = new List< ProjectConfigAndTargetCombination >(); // If this is a "stub" project, then only add a single configuration to the project if( IsStubProject ) { ProjectConfigAndTargetCombination StubCombination = new ProjectConfigAndTargetCombination(UnrealTargetPlatform.Unknown, UnrealTargetConfiguration.Unknown, StubProjectPlatformName, StubProjectConfigurationName, null); ProjectConfigAndTargetCombinations.Add(StubCombination); } else { // Figure out all the desired configurations foreach (var Configuration in InConfigurations) { //@todo.Rocket: Put this in a commonly accessible place? if (UnrealBuildTool.IsValidConfiguration(Configuration) == false) { continue; } foreach (var Platform in InPlatforms) { if (UnrealBuildTool.IsValidPlatform(Platform) == false) { continue; } var BuildPlatform = UEBuildPlatform.GetBuildPlatform(Platform, true); if ((BuildPlatform != null) && (BuildPlatform.HasRequiredSDKsInstalled() == SDKStatus.Valid)) { // Now go through all of the target types for this project if( ProjectTargets.Count == 0 ) { throw new BuildException( "Expecting at least one ProjectTarget to be associated with project '{0}' in the TargetProjects list ", ProjectFilePath ); } foreach( var ProjectTarget in ProjectTargets ) { if(IsValidProjectPlatformAndConfiguration( ProjectTarget, Platform, Configuration )) { string ProjectPlatformName, ProjectConfigurationName; MakeProjectPlatformAndConfigurationNames(Platform, Configuration, ProjectTarget.TargetRules.ConfigurationName, out ProjectPlatformName, out ProjectConfigurationName); ProjectConfigAndTargetCombination Combination = new ProjectConfigAndTargetCombination(Platform, Configuration, ProjectPlatformName, ProjectConfigurationName, ProjectTarget); ProjectConfigAndTargetCombinations.Add( Combination ); } } } } } } VCProjectFileContent.Append( " <ItemGroup Label=\"ProjectConfigurations\">" + ProjectFileGenerator.NewLine); // Make a list of the platforms and configs as project-format names var ProjectPlatforms = new List<UnrealTargetPlatform>(); var ProjectPlatformNameAndPlatforms = new List<Tuple<string, UnrealTargetPlatform>>(); // ProjectPlatformName, Platform var ProjectConfigurationNameAndConfigurations = new List<Tuple<string, UnrealTargetConfiguration>>(); // ProjectConfigurationName, Configuration foreach ( var Combination in ProjectConfigAndTargetCombinations ) { if( !ProjectPlatforms.Contains( Combination.Platform ) ) { ProjectPlatforms.Add( Combination.Platform ); } if( !ProjectPlatformNameAndPlatforms.Any( ProjectPlatformNameAndPlatformTuple => ProjectPlatformNameAndPlatformTuple.Item1 == Combination.ProjectPlatformName ) ) { ProjectPlatformNameAndPlatforms.Add( Tuple.Create( Combination.ProjectPlatformName, Combination.Platform ) ); } if( !ProjectConfigurationNameAndConfigurations.Any( ProjectConfigurationNameAndConfigurationTuple => ProjectConfigurationNameAndConfigurationTuple.Item1 == Combination.ProjectConfigurationName ) ) { ProjectConfigurationNameAndConfigurations.Add( Tuple.Create( Combination.ProjectConfigurationName, Combination.Configuration ) ); } } // Output ALL the project's config-platform permutations (project files MUST do this) foreach( var ConfigurationTuple in ProjectConfigurationNameAndConfigurations ) { var ProjectConfigurationName = ConfigurationTuple.Item1; foreach( var PlatformTuple in ProjectPlatformNameAndPlatforms ) { var ProjectPlatformName = PlatformTuple.Item1; VCProjectFileContent.Append( " <ProjectConfiguration Include=\"" + ProjectConfigurationName + "|" + ProjectPlatformName + "\">" + ProjectFileGenerator.NewLine + " <Configuration>" + ProjectConfigurationName + "</Configuration>" + ProjectFileGenerator.NewLine + " <Platform>" + ProjectPlatformName + "</Platform>" + ProjectFileGenerator.NewLine + " </ProjectConfiguration>" + ProjectFileGenerator.NewLine); } } VCProjectFileContent.Append( " </ItemGroup>" + ProjectFileGenerator.NewLine); VCFiltersFileContent.Append( "<?xml version=\"1.0\" encoding=\"utf-8\"?>" + ProjectFileGenerator.NewLine + ProjectFileGenerator.NewLine + "<Project ToolsVersion=\"" + VCProjectFileGenerator.ProjectFileToolVersionString + "\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">" + ProjectFileGenerator.NewLine); // Platform specific PropertyGroups, etc. StringBuilder AdditionalPropertyGroups = new StringBuilder(); if (!IsStubProject) { foreach (var Platform in ProjectPlatforms) { UEPlatformProjectGenerator ProjGenerator = UEPlatformProjectGenerator.GetPlatformProjectGenerator(Platform, true); if (ProjGenerator != null && ProjGenerator.HasVisualStudioSupport(Platform, UnrealTargetConfiguration.Development)) { AdditionalPropertyGroups.Append(ProjGenerator.GetAdditionalVisualStudioPropertyGroups(Platform)); } } VCProjectFileContent.Append( AdditionalPropertyGroups ); } // Project globals (project GUID, project type, SCC bindings, etc) { VCProjectFileContent.Append( " <PropertyGroup Label=\"Globals\">" + ProjectFileGenerator.NewLine + " <ProjectGuid>" + ProjectGUID.ToString("B").ToUpperInvariant() + "</ProjectGuid>" + ProjectFileGenerator.NewLine + " <Keyword>MakeFileProj</Keyword>" + ProjectFileGenerator.NewLine + " <RootNamespace>" + ProjectName + "</RootNamespace>" + ProjectFileGenerator.NewLine); VCProjectFileContent.Append( " </PropertyGroup>" + ProjectFileGenerator.NewLine); } // Write each project configuration PreDefaultProps section foreach (var ConfigurationTuple in ProjectConfigurationNameAndConfigurations) { var ProjectConfigurationName = ConfigurationTuple.Item1; var TargetConfiguration = ConfigurationTuple.Item2; foreach (var PlatformTuple in ProjectPlatformNameAndPlatforms) { var ProjectPlatformName = PlatformTuple.Item1; var TargetPlatform = PlatformTuple.Item2; WritePreDefaultPropsConfiguration(TargetPlatform, TargetConfiguration, ProjectPlatformName, ProjectConfigurationName, VCProjectFileContent); } } VCProjectFileContent.Append( " <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.Default.props\" />" + ProjectFileGenerator.NewLine); VCProjectFileContent.Append( " <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.props\" />" + ProjectFileGenerator.NewLine + " <ImportGroup Label=\"ExtensionSettings\" />" + ProjectFileGenerator.NewLine + " <PropertyGroup Label=\"UserMacros\" />" + ProjectFileGenerator.NewLine ); // Write each project configuration foreach( var Combination in ProjectConfigAndTargetCombinations ) { WriteConfiguration( ProjectName, Combination, VCProjectFileContent, bGenerateUserFileContent? VCUserFileContent : null ); } // Source folders and files { var LocalAliasedFiles = new List<AliasedFile>(AliasedFiles); foreach (var CurFile in SourceFiles) { if (CurFile.FilePath.Contains(".svn")){ continue; } // We want all source file and directory paths in the project files to be relative to the project file's // location on the disk. Convert the path to be relative to the project file directory var ProjectRelativeSourceFile = Utils.MakePathRelativeTo(CurFile.FilePath, Path.GetDirectoryName(ProjectFilePath)); // By default, files will appear relative to the project file in the solution. This is kind of the normal Visual // Studio way to do things, but because our generated project files are emitted to intermediate folders, if we always // did this it would yield really ugly paths int he solution explorer string FilterRelativeSourceDirectory = Path.GetDirectoryName(ProjectRelativeSourceFile); // Use the specified relative base folder if (CurFile.RelativeBaseFolder != null) // NOTE: We are looking for null strings, not empty strings! { FilterRelativeSourceDirectory = Path.GetDirectoryName(Utils.MakePathRelativeTo(CurFile.FilePath, CurFile.RelativeBaseFolder)); } LocalAliasedFiles.Add(new AliasedFile(ProjectRelativeSourceFile, FilterRelativeSourceDirectory)); } VCFiltersFileContent.Append( " <ItemGroup>" + ProjectFileGenerator.NewLine); VCProjectFileContent.Append( " <ItemGroup>" + ProjectFileGenerator.NewLine); // Add all file directories to the filters file as solution filters var FilterDirectories = new HashSet<string>(); foreach (var AliasedFile in LocalAliasedFiles) { // No need to add the root directory relative to the project (it would just be an empty string!) if (!String.IsNullOrWhiteSpace(AliasedFile.ProjectPath)) { FiltersFileIsNeeded = EnsureFilterPathExists(AliasedFile.ProjectPath, VCFiltersFileContent, FilterDirectories); } var VCFileType = GetVCFileType(AliasedFile.FileSystemPath); VCProjectFileContent.Append( " <" + VCFileType + " Include=\"" + AliasedFile.FileSystemPath + "\" />" + ProjectFileGenerator.NewLine); if (!String.IsNullOrWhiteSpace(AliasedFile.ProjectPath)) { VCFiltersFileContent.Append( " <" + VCFileType + " Include=\"" + AliasedFile.FileSystemPath + "\">" + ProjectFileGenerator.NewLine + " <Filter>" + Utils.CleanDirectorySeparators(AliasedFile.ProjectPath) + "</Filter>" + ProjectFileGenerator.NewLine + " </" + VCFileType + " >" + ProjectFileGenerator.NewLine); FiltersFileIsNeeded = true; } else { // No need to specify the root directory relative to the project (it would just be an empty string!) VCFiltersFileContent.Append( " <" + VCFileType + " Include=\"" + AliasedFile.FileSystemPath + "\" />" + ProjectFileGenerator.NewLine); } } VCProjectFileContent.Append( " </ItemGroup>" + ProjectFileGenerator.NewLine); VCFiltersFileContent.Append( " </ItemGroup>" + ProjectFileGenerator.NewLine); } // For Rocket, include engine source in the source search paths. We never build it locally, so the debugger can't find it. if(UnrealBuildTool.RunningRocket() && !IsStubProject) { VCProjectFileContent.Append(" <PropertyGroup>" + ProjectFileGenerator.NewLine); VCProjectFileContent.Append(" <SourcePath>"); foreach(string DirectoryName in Directory.EnumerateDirectories(Path.GetFullPath(Path.Combine(ProjectFileGenerator.EngineRelativePath, "Source")), "*", SearchOption.AllDirectories)) { if(Directory.EnumerateFiles(DirectoryName, "*.cpp").Any()) { VCProjectFileContent.Append(DirectoryName); VCProjectFileContent.Append(";"); } } VCProjectFileContent.Append("</SourcePath>" + ProjectFileGenerator.NewLine); VCProjectFileContent.Append(" </PropertyGroup>" + ProjectFileGenerator.NewLine); } // Write IntelliSense info { // @todo projectfiles: Currently we are storing defines/include paths for ALL configurations rather than using ConditionString and storing // this data uniquely for each target configuration. IntelliSense may behave better if we did that, but it will result in a LOT more // data being stored into the project file, and might make the IDE perform worse when switching configurations! VCProjectFileContent.Append( " <PropertyGroup>" + ProjectFileGenerator.NewLine + " <NMakePreprocessorDefinitions>$(NMakePreprocessorDefinitions)" + ( VCPreprocessorDefinitions.Length > 0 ? ( ";" + VCPreprocessorDefinitions ) : "" ) + "</NMakePreprocessorDefinitions>" + ProjectFileGenerator.NewLine + " <NMakeIncludeSearchPath>$(NMakeIncludeSearchPath)" + ( VCIncludeSearchPaths.Length > 0 ? ( ";" + VCIncludeSearchPaths ) : "" ) + "</NMakeIncludeSearchPath>" + ProjectFileGenerator.NewLine + " <NMakeForcedIncludes>$(NMakeForcedIncludes)</NMakeForcedIncludes>" + ProjectFileGenerator.NewLine + " <NMakeAssemblySearchPath>$(NMakeAssemblySearchPath)</NMakeAssemblySearchPath>" + ProjectFileGenerator.NewLine + " <NMakeForcedUsingAssemblies>$(NMakeForcedUsingAssemblies)</NMakeForcedUsingAssemblies>" + ProjectFileGenerator.NewLine + " </PropertyGroup>" + ProjectFileGenerator.NewLine ); } // look for additional import lines for all platforms for non stub projects StringBuilder AdditionalImportSettings = new StringBuilder(); if (!IsStubProject) { foreach (var Platform in ProjectPlatforms) { UEPlatformProjectGenerator ProjGenerator = UEPlatformProjectGenerator.GetPlatformProjectGenerator(Platform, true); if (ProjGenerator != null && ProjGenerator.HasVisualStudioSupport(Platform, UnrealTargetConfiguration.Development)) { AdditionalImportSettings.Append(ProjGenerator.GetAdditionalVisualStudioImportSettings(Platform)); } } } string OutputManifestString = ""; if (!IsStubProject) { foreach (var Platform in ProjectPlatforms) { UEPlatformProjectGenerator ProjGenerator = UEPlatformProjectGenerator.GetPlatformProjectGenerator(Platform, true); if (ProjGenerator != null && ProjGenerator.HasVisualStudioSupport(Platform, UnrealTargetConfiguration.Development)) { // @todo projectfiles: Serious hacks here because we are trying to emit one-time platform-specific sections that need information // about a target type, but the project file may contain many types of targets! Some of this logic will need to move into // the per-target configuration writing code. var HackTargetType = TargetRules.TargetType.Game; string HackTargetFilePath = null; foreach( var Combination in ProjectConfigAndTargetCombinations ) { if( Combination.Platform == Platform && Combination.ProjectTarget.TargetRules != null && Combination.ProjectTarget.TargetRules.Type == HackTargetType ) { HackTargetFilePath = Combination.ProjectTarget.TargetFilePath;// ProjectConfigAndTargetCombinations[0].ProjectTarget.TargetFilePath; break; } } if( !String.IsNullOrEmpty( HackTargetFilePath ) ) { OutputManifestString += ProjGenerator.GetVisualStudioOutputManifestSection(Platform, HackTargetType, HackTargetFilePath, ProjectFilePath); } } } } VCProjectFileContent.Append( OutputManifestString + // output manifest must come before the Cpp.targets file. " <ItemDefinitionGroup>" + ProjectFileGenerator.NewLine + " </ItemDefinitionGroup>" + ProjectFileGenerator.NewLine + " <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.targets\" />" + ProjectFileGenerator.NewLine + AdditionalImportSettings.ToString() + " <ImportGroup Label=\"ExtensionTargets\">" + ProjectFileGenerator.NewLine + " </ImportGroup>" + ProjectFileGenerator.NewLine + "</Project>" + ProjectFileGenerator.NewLine ); VCFiltersFileContent.Append( "</Project>" + ProjectFileGenerator.NewLine ); if (bGenerateUserFileContent) { VCUserFileContent.Append( "</Project>" + ProjectFileGenerator.NewLine ); } // Save the project file if( bSuccess ) { bSuccess = ProjectFileGenerator.WriteFileIfChanged( ProjectFilePath, VCProjectFileContent.ToString() ); } // Save the filters file if( bSuccess ) { // Create a path to the project file's filters file var VCFiltersFilePath = ProjectFilePath + ".filters"; if( FiltersFileIsNeeded ) { bSuccess = ProjectFileGenerator.WriteFileIfChanged( VCFiltersFilePath, VCFiltersFileContent.ToString() ); } else { Log.TraceVerbose( "Deleting Visual C++ filters file which is no longer needed: " + VCFiltersFilePath ); // Delete the filters file, if one exists. We no longer need it try { File.Delete( VCFiltersFilePath ); } catch( Exception ) { Log.TraceInformation( "Error deleting filters file (file may not be writable): " + VCFiltersFilePath ); } } } // Save the user file, if required if (VCUserFileContent.Length > 0) { // Create a path to the project file's user file var VCUserFilePath = ProjectFilePath + ".user"; // Never overwrite the existing user path as it will cause them to lose their settings if (File.Exists(VCUserFilePath) == false) { bSuccess = ProjectFileGenerator.WriteFileIfChanged(VCUserFilePath, VCUserFileContent.ToString()); } } return bSuccess; }
/// Implements Project interface public override bool WriteProjectFile(List<UnrealTargetPlatform> InPlatforms, List<UnrealTargetConfiguration> InConfigurations) { string ProjectName = ProjectFilePath.GetFileNameWithoutExtension(); bool bSuccess = true; // Build up the new include search path string StringBuilder VCIncludeSearchPaths = new StringBuilder(); { foreach (string CurPath in IntelliSenseIncludeSearchPaths) { VCIncludeSearchPaths.Append(CurPath + ";"); } foreach (string CurPath in IntelliSenseSystemIncludeSearchPaths) { VCIncludeSearchPaths.Append(CurPath + ";"); } if (InPlatforms.Contains(UnrealTargetPlatform.Win64)) { VCIncludeSearchPaths.Append(VCToolChain.GetVCIncludePaths(CPPTargetPlatform.Win64, false) + ";"); } else if (InPlatforms.Contains(UnrealTargetPlatform.Win32)) { VCIncludeSearchPaths.Append(VCToolChain.GetVCIncludePaths(CPPTargetPlatform.Win32, false) + ";"); } } StringBuilder VCPreprocessorDefinitions = new StringBuilder(); foreach (string CurDef in IntelliSensePreprocessorDefinitions) { if (VCPreprocessorDefinitions.Length > 0) { VCPreprocessorDefinitions.Append(';'); } VCPreprocessorDefinitions.Append(CurDef); } // Setup VC project file content StringBuilder VCProjectFileContent = new StringBuilder(); StringBuilder VCFiltersFileContent = new StringBuilder(); StringBuilder VCUserFileContent = new StringBuilder(); // Visual Studio doesn't require a *.vcxproj.filters file to even exist alongside the project unless // it actually has something of substance in it. We'll avoid saving it out unless we need to. bool FiltersFileIsNeeded = false; // Project file header VCProjectFileContent.Append( "<?xml version=\"1.0\" encoding=\"utf-8\"?>" + ProjectFileGenerator.NewLine + ProjectFileGenerator.NewLine + "<Project DefaultTargets=\"Build\" ToolsVersion=\"" + VCProjectFileGenerator.ProjectFileToolVersionString + "\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">" + ProjectFileGenerator.NewLine); bool bGenerateUserFileContent = UEPlatformProjectGenerator.PlatformRequiresVSUserFileGeneration(InPlatforms, InConfigurations); if (bGenerateUserFileContent) { VCUserFileContent.Append( "<?xml version=\"1.0\" encoding=\"utf-8\"?>" + ProjectFileGenerator.NewLine + ProjectFileGenerator.NewLine + "<Project ToolsVersion=\"" + VCProjectFileGenerator.ProjectFileToolVersionString + "\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">" + ProjectFileGenerator.NewLine ); } // Build up a list of platforms and configurations this project will support. In this list, Unknown simply // means that we should use the default "stub" project platform and configuration name. List<ProjectConfigAndTargetCombination> ProjectConfigAndTargetCombinations = new List<ProjectConfigAndTargetCombination>(); // If this is a "stub" project, then only add a single configuration to the project if (IsStubProject) { ProjectConfigAndTargetCombination StubCombination = new ProjectConfigAndTargetCombination(UnrealTargetPlatform.Unknown, UnrealTargetConfiguration.Unknown, StubProjectPlatformName, StubProjectConfigurationName, null); ProjectConfigAndTargetCombinations.Add(StubCombination); } else { // Figure out all the desired configurations foreach (UnrealTargetConfiguration Configuration in InConfigurations) { //@todo.Rocket: Put this in a commonly accessible place? if (UnrealBuildTool.IsValidConfiguration(Configuration) == false) { continue; } foreach (UnrealTargetPlatform Platform in InPlatforms) { if (UnrealBuildTool.IsValidPlatform(Platform) == false) { continue; } UEBuildPlatform BuildPlatform = UEBuildPlatform.GetBuildPlatform(Platform, true); if ((BuildPlatform != null) && (BuildPlatform.HasRequiredSDKsInstalled() == SDKStatus.Valid)) { // Now go through all of the target types for this project if (ProjectTargets.Count == 0) { throw new BuildException("Expecting at least one ProjectTarget to be associated with project '{0}' in the TargetProjects list ", ProjectFilePath); } foreach (ProjectTarget ProjectTarget in ProjectTargets) { if (IsValidProjectPlatformAndConfiguration(ProjectTarget, Platform, Configuration)) { string ProjectPlatformName, ProjectConfigurationName; MakeProjectPlatformAndConfigurationNames(Platform, Configuration, ProjectTarget.TargetRules.ConfigurationName, out ProjectPlatformName, out ProjectConfigurationName); ProjectConfigAndTargetCombination Combination = new ProjectConfigAndTargetCombination(Platform, Configuration, ProjectPlatformName, ProjectConfigurationName, ProjectTarget); ProjectConfigAndTargetCombinations.Add(Combination); } } } } } } VCProjectFileContent.Append( " <ItemGroup Label=\"ProjectConfigurations\">" + ProjectFileGenerator.NewLine); // Make a list of the platforms and configs as project-format names List<UnrealTargetPlatform> ProjectPlatforms = new List<UnrealTargetPlatform>(); List<Tuple<string, UnrealTargetPlatform>> ProjectPlatformNameAndPlatforms = new List<Tuple<string, UnrealTargetPlatform>>(); // ProjectPlatformName, Platform List<Tuple<string, UnrealTargetConfiguration>> ProjectConfigurationNameAndConfigurations = new List<Tuple<string, UnrealTargetConfiguration>>(); // ProjectConfigurationName, Configuration foreach (ProjectConfigAndTargetCombination Combination in ProjectConfigAndTargetCombinations) { if (!ProjectPlatforms.Contains(Combination.Platform)) { ProjectPlatforms.Add(Combination.Platform); } if (!ProjectPlatformNameAndPlatforms.Any(ProjectPlatformNameAndPlatformTuple => ProjectPlatformNameAndPlatformTuple.Item1 == Combination.ProjectPlatformName)) { ProjectPlatformNameAndPlatforms.Add(Tuple.Create(Combination.ProjectPlatformName, Combination.Platform)); } if (!ProjectConfigurationNameAndConfigurations.Any(ProjectConfigurationNameAndConfigurationTuple => ProjectConfigurationNameAndConfigurationTuple.Item1 == Combination.ProjectConfigurationName)) { ProjectConfigurationNameAndConfigurations.Add(Tuple.Create(Combination.ProjectConfigurationName, Combination.Configuration)); } } // Output ALL the project's config-platform permutations (project files MUST do this) foreach (Tuple<string, UnrealTargetConfiguration> ConfigurationTuple in ProjectConfigurationNameAndConfigurations) { string ProjectConfigurationName = ConfigurationTuple.Item1; foreach (Tuple<string, UnrealTargetPlatform> PlatformTuple in ProjectPlatformNameAndPlatforms) { string ProjectPlatformName = PlatformTuple.Item1; VCProjectFileContent.Append( " <ProjectConfiguration Include=\"" + ProjectConfigurationName + "|" + ProjectPlatformName + "\">" + ProjectFileGenerator.NewLine + " <Configuration>" + ProjectConfigurationName + "</Configuration>" + ProjectFileGenerator.NewLine + " <Platform>" + ProjectPlatformName + "</Platform>" + ProjectFileGenerator.NewLine + " </ProjectConfiguration>" + ProjectFileGenerator.NewLine); } } VCProjectFileContent.Append( " </ItemGroup>" + ProjectFileGenerator.NewLine); VCFiltersFileContent.Append( "<?xml version=\"1.0\" encoding=\"utf-8\"?>" + ProjectFileGenerator.NewLine + ProjectFileGenerator.NewLine + "<Project ToolsVersion=\"" + VCProjectFileGenerator.ProjectFileToolVersionString + "\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">" + ProjectFileGenerator.NewLine); // Platform specific PropertyGroups, etc. StringBuilder AdditionalPropertyGroups = new StringBuilder(); if (!IsStubProject) { foreach (UnrealTargetPlatform Platform in ProjectPlatforms) { UEPlatformProjectGenerator ProjGenerator = UEPlatformProjectGenerator.GetPlatformProjectGenerator(Platform, true); if (ProjGenerator != null && ProjGenerator.HasVisualStudioSupport(Platform, UnrealTargetConfiguration.Development)) { AdditionalPropertyGroups.Append(ProjGenerator.GetAdditionalVisualStudioPropertyGroups(Platform)); } } VCProjectFileContent.Append(AdditionalPropertyGroups); } // Project globals (project GUID, project type, SCC bindings, etc) { VCProjectFileContent.Append( " <PropertyGroup Label=\"Globals\">" + ProjectFileGenerator.NewLine + " <ProjectGuid>" + ProjectGUID.ToString("B").ToUpperInvariant() + "</ProjectGuid>" + ProjectFileGenerator.NewLine + " <Keyword>MakeFileProj</Keyword>" + ProjectFileGenerator.NewLine + " <RootNamespace>" + ProjectName + "</RootNamespace>" + ProjectFileGenerator.NewLine + " <PlatformToolset>" + VCProjectFileGenerator.ProjectFilePlatformToolsetVersionString + "</PlatformToolset>" + ProjectFileGenerator.NewLine + " <MinimumVisualStudioVersion>" + VCProjectFileGenerator.ProjectFileToolVersionString + "</MinimumVisualStudioVersion>" + ProjectFileGenerator.NewLine + " <TargetRuntime>Native</TargetRuntime>" + ProjectFileGenerator.NewLine + " </PropertyGroup>" + ProjectFileGenerator.NewLine); } // Write each project configuration PreDefaultProps section foreach (Tuple<string, UnrealTargetConfiguration> ConfigurationTuple in ProjectConfigurationNameAndConfigurations) { string ProjectConfigurationName = ConfigurationTuple.Item1; UnrealTargetConfiguration TargetConfiguration = ConfigurationTuple.Item2; foreach (Tuple<string, UnrealTargetPlatform> PlatformTuple in ProjectPlatformNameAndPlatforms) { string ProjectPlatformName = PlatformTuple.Item1; UnrealTargetPlatform TargetPlatform = PlatformTuple.Item2; WritePreDefaultPropsConfiguration(TargetPlatform, TargetConfiguration, ProjectPlatformName, ProjectConfigurationName, VCProjectFileContent); } } VCProjectFileContent.Append( " <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.Default.props\" />" + ProjectFileGenerator.NewLine); // Write each project configuration PreDefaultProps section foreach (Tuple<string, UnrealTargetConfiguration> ConfigurationTuple in ProjectConfigurationNameAndConfigurations) { string ProjectConfigurationName = ConfigurationTuple.Item1; UnrealTargetConfiguration TargetConfiguration = ConfigurationTuple.Item2; foreach (Tuple<string, UnrealTargetPlatform> PlatformTuple in ProjectPlatformNameAndPlatforms) { string ProjectPlatformName = PlatformTuple.Item1; UnrealTargetPlatform TargetPlatform = PlatformTuple.Item2; WritePostDefaultPropsConfiguration(TargetPlatform, TargetConfiguration, ProjectPlatformName, ProjectConfigurationName, VCProjectFileContent); } } VCProjectFileContent.Append( " <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.props\" />" + ProjectFileGenerator.NewLine + " <ImportGroup Label=\"ExtensionSettings\" />" + ProjectFileGenerator.NewLine + " <PropertyGroup Label=\"UserMacros\" />" + ProjectFileGenerator.NewLine ); // Write each project configuration foreach (ProjectConfigAndTargetCombination Combination in ProjectConfigAndTargetCombinations) { WriteConfiguration(ProjectName, Combination, VCProjectFileContent, bGenerateUserFileContent ? VCUserFileContent : null); } // Source folders and files { List<AliasedFile> LocalAliasedFiles = new List<AliasedFile>(AliasedFiles); foreach (SourceFile CurFile in SourceFiles) { // We want all source file and directory paths in the project files to be relative to the project file's // location on the disk. Convert the path to be relative to the project file directory string ProjectRelativeSourceFile = CurFile.Reference.MakeRelativeTo(ProjectFilePath.Directory); // By default, files will appear relative to the project file in the solution. This is kind of the normal Visual // Studio way to do things, but because our generated project files are emitted to intermediate folders, if we always // did this it would yield really ugly paths int he solution explorer string FilterRelativeSourceDirectory; if (CurFile.BaseFolder == null) { FilterRelativeSourceDirectory = ProjectRelativeSourceFile; } else { FilterRelativeSourceDirectory = CurFile.Reference.MakeRelativeTo(CurFile.BaseFolder); } // Manually remove the filename for the filter. We run through this code path a lot, so just do it manually. int LastSeparatorIdx = FilterRelativeSourceDirectory.LastIndexOf(Path.DirectorySeparatorChar); if (LastSeparatorIdx == -1) { FilterRelativeSourceDirectory = ""; } else { FilterRelativeSourceDirectory = FilterRelativeSourceDirectory.Substring(0, LastSeparatorIdx); } LocalAliasedFiles.Add(new AliasedFile(ProjectRelativeSourceFile, FilterRelativeSourceDirectory)); } VCFiltersFileContent.Append( " <ItemGroup>" + ProjectFileGenerator.NewLine); VCProjectFileContent.Append( " <ItemGroup>" + ProjectFileGenerator.NewLine); // Add all file directories to the filters file as solution filters HashSet<string> FilterDirectories = new HashSet<string>(); UEBuildPlatform BuildPlatform = UEBuildPlatform.GetBuildPlatform(BuildHostPlatform.Current.Platform); bool bWritePerFilePCHInfo = false; if (BuildConfiguration.bUsePerFileIntellisense && VCProjectFileGenerator.ProjectFileFormat >= VCProjectFileGenerator.VCProjectFileFormat.VisualStudio2015) { string UpdateRegistryLoc = string.Format(@"HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\DevDiv\vs\Servicing\{0}\devenv", VCProjectFileGenerator.ProjectFileToolVersionString); object Result = Microsoft.Win32.Registry.GetValue(UpdateRegistryLoc, "UpdateVersion", null); if (Result != null) { int UpdateVersion = 0; if (Int32.TryParse(Result.ToString().Split('.').Last(), out UpdateVersion) && UpdateVersion >= 25420) { bWritePerFilePCHInfo = true; } } } foreach (AliasedFile AliasedFile in LocalAliasedFiles) { // No need to add the root directory relative to the project (it would just be an empty string!) if (!String.IsNullOrWhiteSpace(AliasedFile.ProjectPath)) { FiltersFileIsNeeded = EnsureFilterPathExists(AliasedFile.ProjectPath, VCFiltersFileContent, FilterDirectories); } string VCFileType = GetVCFileType(AliasedFile.FileSystemPath); string PCHFileName = null; if (bWritePerFilePCHInfo && VCFileType == "ClCompile") { FileReference TruePath = FileReference.Combine(ProjectFilePath.Directory, AliasedFile.FileSystemPath); FileItem SourceFile = FileItem.GetItemByFileReference(TruePath); List <DependencyInclude> DirectlyIncludedFilenames = CPPEnvironment.GetUncachedDirectIncludeDependencies(SourceFile, BuildPlatform); if (DirectlyIncludedFilenames.Count > 0) { PCHFileName = DirectlyIncludedFilenames[0].IncludeName; } } if (!string.IsNullOrEmpty(PCHFileName)) { VCProjectFileContent.Append( " <" + VCFileType + " Include=\"" + EscapeFileName(AliasedFile.FileSystemPath) + "\">" + ProjectFileGenerator.NewLine + " <AdditionalOptions>$(AdditionalOptions) /Yu" + PCHFileName + "</AdditionalOptions>" + ProjectFileGenerator.NewLine + " </" + VCFileType + " >" + ProjectFileGenerator.NewLine); } else { VCProjectFileContent.Append( " <" + VCFileType + " Include=\"" + EscapeFileName(AliasedFile.FileSystemPath) + "\" />" + ProjectFileGenerator.NewLine); } if (!String.IsNullOrWhiteSpace(AliasedFile.ProjectPath)) { VCFiltersFileContent.Append( " <" + VCFileType + " Include=\"" + EscapeFileName(AliasedFile.FileSystemPath) + "\">" + ProjectFileGenerator.NewLine + " <Filter>" + Utils.CleanDirectorySeparators(AliasedFile.ProjectPath) + "</Filter>" + ProjectFileGenerator.NewLine + " </" + VCFileType + " >" + ProjectFileGenerator.NewLine); FiltersFileIsNeeded = true; } else { // No need to specify the root directory relative to the project (it would just be an empty string!) VCFiltersFileContent.Append( " <" + VCFileType + " Include=\"" + EscapeFileName(AliasedFile.FileSystemPath) + "\" />" + ProjectFileGenerator.NewLine); } } VCProjectFileContent.Append( " </ItemGroup>" + ProjectFileGenerator.NewLine); VCFiltersFileContent.Append( " </ItemGroup>" + ProjectFileGenerator.NewLine); } // For Installed engine builds, include engine source in the source search paths if it exists. We never build it locally, so the debugger can't find it. if (UnrealBuildTool.IsEngineInstalled() && !IsStubProject) { VCProjectFileContent.Append(" <PropertyGroup>" + ProjectFileGenerator.NewLine); VCProjectFileContent.Append(" <SourcePath>"); foreach (string DirectoryName in Directory.EnumerateDirectories(Path.GetFullPath(Path.Combine(ProjectFileGenerator.EngineRelativePath, "Source")), "*", SearchOption.AllDirectories)) { if (Directory.EnumerateFiles(DirectoryName, "*.cpp").Any()) { VCProjectFileContent.Append(DirectoryName); VCProjectFileContent.Append(";"); } } VCProjectFileContent.Append("</SourcePath>" + ProjectFileGenerator.NewLine); VCProjectFileContent.Append(" </PropertyGroup>" + ProjectFileGenerator.NewLine); } // Write IntelliSense info { // @todo projectfiles: Currently we are storing defines/include paths for ALL configurations rather than using ConditionString and storing // this data uniquely for each target configuration. IntelliSense may behave better if we did that, but it will result in a LOT more // data being stored into the project file, and might make the IDE perform worse when switching configurations! VCProjectFileContent.Append( " <PropertyGroup>" + ProjectFileGenerator.NewLine + " <NMakePreprocessorDefinitions>$(NMakePreprocessorDefinitions)" + (VCPreprocessorDefinitions.Length > 0 ? (";" + VCPreprocessorDefinitions) : "") + "</NMakePreprocessorDefinitions>" + ProjectFileGenerator.NewLine + " <NMakeIncludeSearchPath>$(NMakeIncludeSearchPath)" + (VCIncludeSearchPaths.Length > 0 ? (";" + VCIncludeSearchPaths) : "") + "</NMakeIncludeSearchPath>" + ProjectFileGenerator.NewLine + " <NMakeForcedIncludes>$(NMakeForcedIncludes)</NMakeForcedIncludes>" + ProjectFileGenerator.NewLine + " <NMakeAssemblySearchPath>$(NMakeAssemblySearchPath)</NMakeAssemblySearchPath>" + ProjectFileGenerator.NewLine + " <NMakeForcedUsingAssemblies>$(NMakeForcedUsingAssemblies)</NMakeForcedUsingAssemblies>" + ProjectFileGenerator.NewLine + " </PropertyGroup>" + ProjectFileGenerator.NewLine); } // look for additional import lines for all platforms for non stub projects StringBuilder AdditionalImportSettings = new StringBuilder(); if (!IsStubProject) { foreach (UnrealTargetPlatform Platform in ProjectPlatforms) { UEPlatformProjectGenerator ProjGenerator = UEPlatformProjectGenerator.GetPlatformProjectGenerator(Platform, true); if (ProjGenerator != null && ProjGenerator.HasVisualStudioSupport(Platform, UnrealTargetConfiguration.Development)) { AdditionalImportSettings.Append(ProjGenerator.GetAdditionalVisualStudioImportSettings(Platform)); } } } string OutputManifestString = ""; if (!IsStubProject) { foreach (UnrealTargetPlatform Platform in ProjectPlatforms) { UEPlatformProjectGenerator ProjGenerator = UEPlatformProjectGenerator.GetPlatformProjectGenerator(Platform, true); if (ProjGenerator != null && ProjGenerator.HasVisualStudioSupport(Platform, UnrealTargetConfiguration.Development)) { // @todo projectfiles: Serious hacks here because we are trying to emit one-time platform-specific sections that need information // about a target type, but the project file may contain many types of targets! Some of this logic will need to move into // the per-target configuration writing code. TargetRules.TargetType HackTargetType = TargetRules.TargetType.Game; FileReference HackTargetFilePath = null; foreach (ProjectConfigAndTargetCombination Combination in ProjectConfigAndTargetCombinations) { if (Combination.Platform == Platform && Combination.ProjectTarget.TargetRules != null && Combination.ProjectTarget.TargetRules.Type == HackTargetType) { HackTargetFilePath = Combination.ProjectTarget.TargetFilePath;// ProjectConfigAndTargetCombinations[0].ProjectTarget.TargetFilePath; break; } } if (HackTargetFilePath != null) { OutputManifestString += ProjGenerator.GetVisualStudioOutputManifestSection(Platform, HackTargetType, HackTargetFilePath, ProjectFilePath); } } } } VCProjectFileContent.Append( OutputManifestString + // output manifest must come before the Cpp.targets file. " <ItemDefinitionGroup>" + ProjectFileGenerator.NewLine + " </ItemDefinitionGroup>" + ProjectFileGenerator.NewLine + " <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.targets\" />" + ProjectFileGenerator.NewLine + AdditionalImportSettings.ToString() + " <ImportGroup Label=\"ExtensionTargets\">" + ProjectFileGenerator.NewLine + " </ImportGroup>" + ProjectFileGenerator.NewLine + "</Project>" + ProjectFileGenerator.NewLine); VCFiltersFileContent.Append( "</Project>" + ProjectFileGenerator.NewLine); if (bGenerateUserFileContent) { VCUserFileContent.Append( "</Project>" + ProjectFileGenerator.NewLine ); } // Save the project file if (bSuccess) { bSuccess = ProjectFileGenerator.WriteFileIfChanged(ProjectFilePath.FullName, VCProjectFileContent.ToString()); } // Save the filters file if (bSuccess) { // Create a path to the project file's filters file string VCFiltersFilePath = ProjectFilePath.FullName + ".filters"; if (FiltersFileIsNeeded) { bSuccess = ProjectFileGenerator.WriteFileIfChanged(VCFiltersFilePath, VCFiltersFileContent.ToString()); } else { Log.TraceVerbose("Deleting Visual C++ filters file which is no longer needed: " + VCFiltersFilePath); // Delete the filters file, if one exists. We no longer need it try { File.Delete(VCFiltersFilePath); } catch (Exception) { Log.TraceInformation("Error deleting filters file (file may not be writable): " + VCFiltersFilePath); } } } // Save the user file, if required if (VCUserFileContent.Length > 0) { // Create a path to the project file's user file string VCUserFilePath = ProjectFilePath.FullName + ".user"; // Never overwrite the existing user path as it will cause them to lose their settings if (File.Exists(VCUserFilePath) == false) { bSuccess = ProjectFileGenerator.WriteFileIfChanged(VCUserFilePath, VCUserFileContent.ToString()); } } return bSuccess; }