// Copyright 1998-2017 Epic Games, Inc. All Rights Reserved. using System; using System.Collections.Generic; using System.Text; using System.IO; using System.Linq; namespace UnrealBuildTool { /// /// Represents a folder within the master project (e.g. Visual Studio solution) /// class QMakefileFolder : MasterProjectFolder { /// /// Constructor /// public QMakefileFolder(ProjectFileGenerator InitOwnerProjectFileGenerator, string InitFolderName) : base(InitOwnerProjectFileGenerator, InitFolderName) { } } class QMakefileProjectFile : ProjectFile { public QMakefileProjectFile(FileReference InitFilePath) : base(InitFilePath) { } } /// /// QMakefile project file generator implementation /// class QMakefileGenerator : ProjectFileGenerator { /// Default constructor public QMakefileGenerator(FileReference InOnlyGameProject) : base(InOnlyGameProject) { } /// File extension for project files we'll be generating (e.g. ".vcxproj") override public string ProjectFileExtension { get { return ".pro"; } } protected override bool WriteMasterProjectFile(ProjectFile UBTProject) { bool bSuccess = true; return bSuccess; } /// /// Splits the definition text into macro name and value (if any). /// /// Definition text /// Out: The definition name /// Out: The definition value or null if it has none /// Pair representing macro name and value. private void SplitDefinitionAndValue(string Definition, out String Key, out String Value) { int EqualsIndex = Definition.IndexOf('='); if (EqualsIndex >= 0) { Key = Definition.Substring(0, EqualsIndex); Value = Definition.Substring(EqualsIndex + 1); } else { Key = Definition; Value = ""; } } /// Adds the include directory to the list, after converting it to relative to UE4 root private void AddIncludeDirectory(ref List IncludeDirectories, string IncludeDir, string ProjectDir) { string FullProjectPath = ProjectFileGenerator.MasterProjectPath.FullName; string FullPath = ""; if (IncludeDir.StartsWith("/") && !IncludeDir.StartsWith(FullProjectPath)) { // Full path to a fulder outside of project FullPath = IncludeDir; } else { FullPath = Path.GetFullPath(Path.Combine(ProjectDir, IncludeDir)); FullPath = Utils.MakePathRelativeTo(FullPath, FullProjectPath); FullPath = FullPath.TrimEnd('/'); } if (!FullPath.Contains("FortniteGame/") && !FullPath.Contains("ThirdParty/")) // @todo: skipping Fortnite header paths to shorten clang command line for building UE4XcodeHelper { IncludeDirectories.Add(FullPath); } } // For later when moving all this back to MakefileGenerator.cs private void AddDefines(ref List DefinesContent, string define, string value) { } private bool WriteQMakePro() { // Some more stuff borrowed from Mac side of things. List IncludeDirectories = new List(); List SystemIncludeDirectories = new List(); List DefinesAndValues = new List(); // DefineList.Add (""); var QMakeIncludesFileName = MasterProjectName + "Includes.pri"; var QMakeIncludesPriFileContent = new StringBuilder(); var QMakeDefinesFileName = MasterProjectName + "Defines.pri"; var QMakeDefinesPriFileContent = new StringBuilder(); string GameProjectPath = ""; string GameProjectFile = ""; string GameProjectRootPath = ""; string BuildCommand = ""; string QMakeGameProjectFile = ""; foreach (var CurProject in GeneratedProjectFiles) { QMakefileProjectFile QMakeProject = CurProject as QMakefileProjectFile; if (QMakeProject == null) { System.Console.WriteLine("QMakeProject == null"); continue; } foreach (var CurPath in QMakeProject.IntelliSenseIncludeSearchPaths) { AddIncludeDirectory(ref IncludeDirectories, CurPath, Path.GetDirectoryName(QMakeProject.ProjectFilePath.FullName)); // System.Console.WriteLine ("Not empty now? CurPath == ", CurPath); } foreach (var CurPath in QMakeProject.IntelliSenseSystemIncludeSearchPaths) { AddIncludeDirectory(ref SystemIncludeDirectories, CurPath, Path.GetDirectoryName(QMakeProject.ProjectFilePath.FullName)); } } // Remove duplicate paths from include dir and system include dir list IncludeDirectories = IncludeDirectories.Distinct().ToList(); SystemIncludeDirectories = SystemIncludeDirectories.Distinct().ToList(); // Iterate through all the defines for the projects that are generated by // UnrealBuildTool.exe // !RAKE: move to seperate function QMakeDefinesPriFileContent.Append("DEFINES += \\\n"); foreach (var CurProject in GeneratedProjectFiles) { QMakefileProjectFile QMakeProject = CurProject as QMakefileProjectFile; if (QMakeProject == null) { System.Console.WriteLine("QMakeProject == null"); continue; } foreach (var CurDefine in QMakeProject.IntelliSensePreprocessorDefinitions) { String define = ""; String value = ""; SplitDefinitionAndValue(CurDefine, out define, out value); if (!DefinesAndValues.Contains(define)) { // System.Console.WriteLine (CurDefine); if (string.IsNullOrEmpty(value)) { DefinesAndValues.Add("\t"); DefinesAndValues.Add(String.Format("{0}=", define)); DefinesAndValues.Add(" \\\n"); } else { DefinesAndValues.Add("\t"); DefinesAndValues.Add(define); DefinesAndValues.Add("="); DefinesAndValues.Add(value); DefinesAndValues.Add(" \\\n"); } } } } foreach (var Def in DefinesAndValues) { QMakeDefinesPriFileContent.Append(Def); } // Iterate through all the include paths that // UnrealBuildTool.exe generates // !RAKE: Move to seperate function QMakeIncludesPriFileContent.Append("INCLUDEPATH += \\\n"); foreach (var CurPath in IncludeDirectories) { QMakeIncludesPriFileContent.Append("\t"); QMakeIncludesPriFileContent.Append(CurPath); QMakeIncludesPriFileContent.Append(" \\\n"); } foreach (var CurPath in SystemIncludeDirectories) { QMakeIncludesPriFileContent.Append("\t"); QMakeIncludesPriFileContent.Append(CurPath); QMakeIncludesPriFileContent.Append(" \\\n"); } QMakeIncludesPriFileContent.Append("\n"); if (!String.IsNullOrEmpty(GameProjectName)) { GameProjectPath = OnlyGameProject.Directory.FullName; GameProjectFile = OnlyGameProject.FullName; QMakeGameProjectFile = "gameProjectFile=" + GameProjectFile + "\n"; BuildCommand = "build=mono $$unrealRootPath/Engine/Binaries/DotNET/UnrealBuildTool.exe\n\n"; } else { BuildCommand = "build=bash $$unrealRootPath/Engine/Build/BatchFiles/Linux/Build.sh\n"; } var UnrealRootPath = UnrealBuildTool.RootDirectory.FullName; var FileName = MasterProjectName + ".pro"; var QMakeSourcePriFileName = MasterProjectName + "Source.pri"; var QMakeHeaderPriFileName = MasterProjectName + "Header.pri"; var QMakeConfigPriFileName = MasterProjectName + "Config.pri"; var QMakeFileContent = new StringBuilder(); var QMakeSourcePriFileContent = new StringBuilder(); var QMakeHeaderPriFileContent = new StringBuilder(); var QMakeConfigPriFileContent = new StringBuilder(); var QMakeSectionEnd = " \n\n"; var QMakeSourceFilesList = "SOURCES += \\ \n"; var QMakeHeaderFilesList = "HEADERS += \\ \n"; var QMakeConfigFilesList = "OTHER_FILES += \\ \n"; var QMakeTargetList = "QMAKE_EXTRA_TARGETS += \\ \n"; if (!String.IsNullOrEmpty(GameProjectName)) { GameProjectRootPath = GameProjectName + "RootPath=" + GameProjectPath + "\n\n"; } QMakeFileContent.Append( "# UnrealEngine.pro generated by QMakefileGenerator.cs\n" + "# *DO NOT EDIT*\n\n" + "TEMPLATE = aux\n" + "CONFIG += c++14\n" + "CONFIG -= console\n" + "CONFIG -= app_bundle\n" + "CONFIG -= qt\n\n" + "TARGET = UE4 \n\n" + "unrealRootPath=" + UnrealRootPath + "\n" + GameProjectRootPath + QMakeGameProjectFile + BuildCommand + "args=$(ARGS)\n\n" + "include(" + QMakeSourcePriFileName + ")\n" + "include(" + QMakeHeaderPriFileName + ")\n" + "include(" + QMakeConfigPriFileName + ")\n" + "include(" + QMakeIncludesFileName + ")\n" + "include(" + QMakeDefinesFileName + ")\n\n" ); // Create SourceFiles, HeaderFiles, and ConfigFiles sections. var AllModuleFiles = DiscoverModules(FindGameProjects()); foreach (FileReference CurModuleFile in AllModuleFiles) { var FoundFiles = SourceFileSearch.FindModuleSourceFiles(CurModuleFile); foreach (FileReference CurSourceFile in FoundFiles) { string SourceFileRelativeToRoot = CurSourceFile.MakeRelativeTo(UnrealBuildTool.EngineDirectory); // Exclude some directories that we don't compile (note that we still want Windows/Mac etc for code navigation) if (!SourceFileRelativeToRoot.Contains("Source/ThirdParty/")) { if (SourceFileRelativeToRoot.EndsWith(".cpp")) { if (!SourceFileRelativeToRoot.StartsWith("..") && !Path.IsPathRooted(SourceFileRelativeToRoot)) { QMakeSourceFilesList += ("\t\"" + "$$unrealRootPath/Engine/" + SourceFileRelativeToRoot + "\" \\\n"); } else { if (String.IsNullOrEmpty(GameProjectName)) { QMakeSourceFilesList += ("\t\"" + SourceFileRelativeToRoot.Substring(3) + "\" \\\n"); } else { QMakeSourceFilesList += ("\t\"$$" + GameProjectName + "RootPath/" + Utils.MakePathRelativeTo(CurSourceFile.FullName, GameProjectPath) + "\" \\\n"); } } } if (SourceFileRelativeToRoot.EndsWith(".h")) { if (!SourceFileRelativeToRoot.StartsWith("..") && !Path.IsPathRooted(SourceFileRelativeToRoot)) { // SourceFileRelativeToRoot = "Engine/" + SourceFileRelativeToRoot; QMakeHeaderFilesList += ("\t\"" + "$$unrealRootPath/Engine/" + SourceFileRelativeToRoot + "\" \\\n"); } else { if (String.IsNullOrEmpty(GameProjectName)) { // SourceFileRelativeToRoot = SourceFileRelativeToRoot.Substring (3); QMakeHeaderFilesList += ("\t\"" + SourceFileRelativeToRoot.Substring(3) + "\" \\\n"); } else { QMakeHeaderFilesList += ("\t\"$$" + GameProjectName + "RootPath/" + Utils.MakePathRelativeTo(CurSourceFile.FullName, GameProjectPath) + "\" \\\n"); } } } if (SourceFileRelativeToRoot.EndsWith(".cs")) { if (!SourceFileRelativeToRoot.StartsWith("..") && !Path.IsPathRooted(SourceFileRelativeToRoot)) { // SourceFileRelativeToRoot = "Engine/" + SourceFileRelativeToRoot; QMakeConfigFilesList += ("\t\"" + "$$unrealRootPath/Engine/" + SourceFileRelativeToRoot + "\" \\\n"); } else { if (String.IsNullOrEmpty(GameProjectName)) { // SourceFileRelativeToRoot = SourceFileRelativeToRoot.Substring (3); QMakeConfigFilesList += ("\t\"" + SourceFileRelativeToRoot.Substring(3) + "\" \\\n"); } else { QMakeConfigFilesList += ("\t\"$$" + GameProjectName + "RootPath/" + Utils.MakePathRelativeTo(CurSourceFile.FullName, GameProjectPath) + "\" \\\n"); } } } } } } // Add section end to section strings; QMakeSourceFilesList += QMakeSectionEnd; QMakeHeaderFilesList += QMakeSectionEnd; QMakeConfigFilesList += QMakeSectionEnd; // Append sections to the QMakeLists.txt file QMakeSourcePriFileContent.Append(QMakeSourceFilesList); QMakeHeaderPriFileContent.Append(QMakeHeaderFilesList); QMakeConfigPriFileContent.Append(QMakeConfigFilesList); string QMakeProjectCmdArg = ""; foreach (var Project in GeneratedProjectFiles) { foreach (var TargetFile in Project.ProjectTargets) { if (TargetFile.TargetFilePath == null) { continue; } var TargetName = TargetFile.TargetFilePath.GetFileNameWithoutAnyExtensions(); // Remove both ".cs" and ". foreach (UnrealTargetConfiguration CurConfiguration in Enum.GetValues(typeof(UnrealTargetConfiguration))) { if (CurConfiguration != UnrealTargetConfiguration.Unknown && CurConfiguration != UnrealTargetConfiguration.Development) { if (UnrealBuildTool.IsValidConfiguration(CurConfiguration)) { if (TargetName == GameProjectName || TargetName == (GameProjectName + "Editor")) { QMakeProjectCmdArg = " -project=\"\\\"$$gameProjectFile\\\"\""; } var ConfName = Enum.GetName(typeof(UnrealTargetConfiguration), CurConfiguration); QMakeFileContent.Append(String.Format("{0}-Linux-{1}.commands = $$build {0} Linux {1} {2} $$args\n", TargetName, ConfName, QMakeProjectCmdArg)); QMakeTargetList += "\t" + TargetName + "-Linux-" + ConfName + " \\\n"; // , TargetName, ConfName); } } } if (TargetName == GameProjectName || TargetName == (GameProjectName + "Editor")) { QMakeProjectCmdArg = " -project=\"\\\"$$gameProjectFile\\\"\""; } QMakeFileContent.Append(String.Format("{0}.commands = $$build {0} Linux Development {1} $$args\n\n", TargetName, QMakeProjectCmdArg)); QMakeTargetList += "\t" + TargetName + " \\\n"; } } QMakeFileContent.Append(QMakeTargetList.TrimEnd('\\')); var FullFileName = Path.Combine(MasterProjectPath.FullName, FileName); var FullQMakeDefinesFileName = Path.Combine(MasterProjectPath.FullName, QMakeDefinesFileName); var FullQMakeIncludesFileName = Path.Combine(MasterProjectPath.FullName, QMakeIncludesFileName); var FullQMakeSourcePriFileName = Path.Combine(MasterProjectPath.FullName, QMakeSourcePriFileName); var FullQMakeHeaderPriFileName = Path.Combine(MasterProjectPath.FullName, QMakeHeaderPriFileName); var FullQMakeConfigPriFileName = Path.Combine(MasterProjectPath.FullName, QMakeConfigPriFileName); WriteFileIfChanged(FullQMakeDefinesFileName, QMakeDefinesPriFileContent.ToString()); WriteFileIfChanged(FullQMakeIncludesFileName, QMakeIncludesPriFileContent.ToString()); WriteFileIfChanged(FullQMakeSourcePriFileName, QMakeSourcePriFileContent.ToString()); WriteFileIfChanged(FullQMakeHeaderPriFileName, QMakeHeaderPriFileContent.ToString()); WriteFileIfChanged(FullQMakeConfigPriFileName, QMakeConfigPriFileContent.ToString()); return WriteFileIfChanged(FullFileName, QMakeFileContent.ToString()); } /// ProjectFileGenerator interface //protected override bool WriteMasterProjectFile( ProjectFile UBTProject ) protected override bool WriteProjectFiles() { return WriteQMakePro(); } /// ProjectFileGenerator interface public override MasterProjectFolder AllocateMasterProjectFolder(ProjectFileGenerator InitOwnerProjectFileGenerator, string InitFolderName) { return new QMakefileFolder(InitOwnerProjectFileGenerator, InitFolderName); } /// ProjectFileGenerator interface /// /// Allocates a generator-specific project file object /// /// Path to the project file /// The newly allocated project file object protected override ProjectFile AllocateProjectFile(FileReference InitFilePath) { return new QMakefileProjectFile(InitFilePath); } /// ProjectFileGenerator interface public override void CleanProjectFiles(DirectoryReference InMasterProjectDirectory, string InMasterProjectName, DirectoryReference InIntermediateProjectFilesDirectory) { } } }