// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. using System; using System.Collections.Generic; using System.Text; using System.IO; namespace UnrealBuildTool { /// /// Represents a folder within the master project (e.g. Visual Studio solution) /// public class KDevelopFolder : MasterProjectFolder { /// /// Constructor /// public KDevelopFolder( ProjectFileGenerator InitOwnerProjectFileGenerator, string InitFolderName ) : base(InitOwnerProjectFileGenerator, InitFolderName) { } } public class KDevelopProjectFile : ProjectFile { public KDevelopProjectFile( string InitFilePath ) : base(InitFilePath) { } } /// /// KDevelop project file generator implementation /// public class KDevelopGenerator : ProjectFileGenerator { /// File extension for project files we'll be generating (e.g. ".vcxproj") override public string ProjectFileExtension { get { return ".kdev4"; } } protected override bool WriteMasterProjectFile( ProjectFile UBTProject ) { bool bSuccess = true; return bSuccess; } /// /// Write the primare $ProjectName.kdev4 project file, the one that should be opened when loading the project /// into KDevelop /// /// File content. /// Name. private void WriteKDevMasterProjectSection( ref StringBuilder FileContent, string Name) { FileContent.Append ("\n"); FileContent.Append ("[Project] \n"); FileContent.Append ("Manager=KDevCustomBuildSystem \n"); FileContent.Append ("Name="); FileContent.Append (Name); FileContent.Append ("\n"); } /// /// Write the Command Sub section for .kdev4/$ProjectName.kdev4 file /// /// File content. /// Target name. /// Conf name. /// Build config index. /// Type. private void WriteCommandSubSection(ref StringBuilder FileContent, string TargetName, string ConfName, int BuildConfigIndex, int Type) { string ToolType = ""; string Executable = ""; string ProjectCmdArg = ""; string BuildCommand = ""; if (TargetName == GameProjectName) { ProjectCmdArg = " -project=\"" + UnrealBuildTool.GetUProjectFile () + "\""; Executable = "mono"; BuildCommand = "Engine/Binaries/DotNET/UnrealBuildTool.exe"; if (Type == 1) { ProjectCmdArg = " -makefile -kdevelopfile " + ProjectCmdArg + " -game -engine "; } } else if (TargetName == (GameProjectName + "Editor")) { ProjectCmdArg = " -editorrecompile -project=\"" + UnrealBuildTool.GetUProjectFile () + "\""; Executable = "mono"; BuildCommand = "Engine/Binaries/DotNET/UnrealBuildTool.exe"; if (Type == 1) { ProjectCmdArg = " -makefile -kdevelopfile " + ProjectCmdArg + " -game -engine "; } } else { Executable = "bash"; BuildCommand = "Engine/Build/BatchFiles/Linux/Build.sh"; if (Type == 1) { // Override BuildCommand and ProjectCmdArg BuildCommand = "./GenerateProjectFiles.sh"; // ProjectCmdArg = ""; } } if (Type == 0) { ToolType = "Build"; } else if (Type == 1) { ToolType = "Configure"; } else if (Type == 3) { ToolType = "Clean"; ConfName = ConfName + " -clean"; } FileContent.Append (String.Format("[CustomBuildSystem][BuildConfig{0}][Tool{1}]\n", BuildConfigIndex, ToolType)); FileContent.Append (String.Format ("Arguments={0} {1} {2} Linux {3}\n", BuildCommand, ProjectCmdArg, TargetName, ConfName)); FileContent.Append ("Enabled=true\n"); FileContent.Append ("Environment=\n"); FileContent.Append (String.Format("Executable={0}\n", Executable)); FileContent.Append (String.Format("Type={0}\n\n",Type)); } /// /// Write the Command section for a .kdev4/$ProjectName.kdev4 file. /// /// File content. private void WriteCommandSection( ref StringBuilder FileContent) { int BuildConfigIndex = 1; var UnrealRootPath = Path.GetFullPath(ProjectFileGenerator.RootRelativePath); FileContent.Append ("[CustomBuildSystem]\n"); FileContent.Append("CurrentConfiguration=BuildConfig0\n\n"); // // The Basics to get up and running with the editor, utilizing the Makefile. FileContent.Append (String.Format("[CustomBuildSystem][BuildConfig0]\nBuildDir=file://{0}\n", UnrealRootPath )); FileContent.Append ("Title=BuildMeFirst\n\n"); FileContent.Append ("[CustomBuildSystem][BuildConfig0][ToolBuild]\n"); FileContent.Append ("Arguments=-f Makefile UE4Editor UE4Game ShaderCompileWorker UnrealLightmass UnrealPak\n"); FileContent.Append ("Enabled=true\n"); FileContent.Append ("Environment=\n"); FileContent.Append ("Executable=make\n"); FileContent.Append ("Type=0\n\n"); FileContent.Append ("[CustomBuildSystem][BuildConfig0][ToolClean]\n"); FileContent.Append ("Arguments=-f Makefile UE4Editor UE4Game ShaderCompileWorker UnrealLightmass UnrealPak -clean\n"); FileContent.Append ("Enabled=true\n"); FileContent.Append ("Environment=\n"); FileContent.Append ("Executable=make\n"); FileContent.Append ("Type=3\n\n"); FileContent.Append ("[CustomBuildSystem][BuildConfig0][ToolConfigure]\n"); FileContent.Append ("Arguments=./GenerateProjectFiles.sh\n"); FileContent.Append ("Enabled=true\n"); FileContent.Append ("Environment=\n"); FileContent.Append ("Executable=bash\n"); FileContent.Append ("Type=1\n\n"); foreach (string TargetFilePath in DiscoverTargets()) { var TargetName = Utils.GetFilenameWithoutAnyExtensions(TargetFilePath); // Remove both ".cs" and ". foreach (UnrealTargetConfiguration CurConfiguration in Enum.GetValues(typeof(UnrealTargetConfiguration))) { if (CurConfiguration != UnrealTargetConfiguration.Unknown && CurConfiguration != UnrealTargetConfiguration.Development) { if (UnrealBuildTool.IsValidConfiguration (CurConfiguration)) { var ConfName = Enum.GetName(typeof(UnrealTargetConfiguration), CurConfiguration); FileContent.Append (String.Format("[CustomBuildSystem][BuildConfig{0}]\nBuildDir=file://{1}\n", BuildConfigIndex, UnrealRootPath )); if (TargetName == GameProjectName) { FileContent.Append (String.Format ("Title={0}-Linux-{1}\n\n", TargetName, ConfName)); WriteCommandSubSection (ref FileContent, TargetName, ConfName, BuildConfigIndex, 0); WriteCommandSubSection (ref FileContent, TargetName, ConfName, BuildConfigIndex, 1); WriteCommandSubSection (ref FileContent, TargetName, ConfName, BuildConfigIndex, 3); } else if (TargetName == (GameProjectName + "Editor")) { FileContent.Append (String.Format ("Title={0}-Linux-{1}\n\n", TargetName, ConfName)); WriteCommandSubSection (ref FileContent, TargetName, ConfName, BuildConfigIndex, 0); WriteCommandSubSection (ref FileContent, TargetName, ConfName, BuildConfigIndex, 1); WriteCommandSubSection (ref FileContent, TargetName, ConfName, BuildConfigIndex, 3); } else { FileContent.Append (String.Format ("Title={0}-Linux-{1}\n\n", TargetName, ConfName)); WriteCommandSubSection (ref FileContent, TargetName, ConfName, BuildConfigIndex, 0); WriteCommandSubSection (ref FileContent, TargetName, ConfName, BuildConfigIndex, 1); WriteCommandSubSection (ref FileContent, TargetName, ConfName, BuildConfigIndex, 3); } BuildConfigIndex++; } } } FileContent.Append (String.Format("[CustomBuildSystem][BuildConfig{0}]\nBuildDir=file://{1}\n", BuildConfigIndex, UnrealRootPath )); if (TargetName == GameProjectName) { FileContent.Append (String.Format ("Title={0}\n\n", TargetName)); WriteCommandSubSection (ref FileContent, TargetName, "Development" , BuildConfigIndex, 0); WriteCommandSubSection (ref FileContent, TargetName, "Development" , BuildConfigIndex, 1); WriteCommandSubSection (ref FileContent, TargetName, "Development" , BuildConfigIndex, 3); } else if (TargetName == (GameProjectName + "Editor")) { FileContent.Append (String.Format ("Title={0}\n\n", TargetName)); WriteCommandSubSection (ref FileContent, TargetName, "Development" , BuildConfigIndex, 0); WriteCommandSubSection (ref FileContent, TargetName, "Development" , BuildConfigIndex, 1); WriteCommandSubSection (ref FileContent, TargetName, "Development" , BuildConfigIndex, 3); } else { FileContent.Append (String.Format ("Title={0}\n\n", TargetName)); WriteCommandSubSection (ref FileContent, TargetName, "Development" , BuildConfigIndex, 0); WriteCommandSubSection (ref FileContent, TargetName, "Development" , BuildConfigIndex, 1); WriteCommandSubSection (ref FileContent, TargetName, "Development" , BuildConfigIndex, 3); } BuildConfigIndex++; } } /// /// Adds the include directory to the list, after converting it to an absolute path to UE4 root directory. /// /// File content. private void WriteIncludeSection(ref StringBuilder FileContent) { List IncludeDirectories = new List (); List SystemIncludeDirectories = new List (); var UnrealEngineRootPath = Path.GetFullPath(ProjectFileGenerator.RootRelativePath); int IncludeIndex = 1; // Iterate through all the include paths that // UnrealBuildTool.exe generates foreach (var CurProject in GeneratedProjectFiles) { KDevelopProjectFile KDevelopProject = CurProject as KDevelopProjectFile; if (KDevelopProject == null) { System.Console.WriteLine ("KDevelopProject == null"); continue; } foreach (var CurPath in KDevelopProject.IntelliSenseIncludeSearchPaths) { string FullProjectPath = Path.GetFullPath(ProjectFileGenerator.MasterProjectRelativePath); string FullPath = ""; // need to test to see if this in the project souce tree if (CurPath.StartsWith("/") && !CurPath.StartsWith(FullProjectPath)) { // Full path to a folder outside of project FullPath = CurPath; System.Console.WriteLine("FullPath starting with /: " + FullPath); } else { FullPath = Path.GetFullPath(Path.Combine(Path.GetDirectoryName(KDevelopProject.ProjectFilePath), CurPath)); FullPath = Utils.MakePathRelativeTo(FullPath, FullProjectPath); FullPath = FullPath.TrimEnd('/'); FullPath = Path.Combine(UnrealEngineRootPath, FullPath); } if (!IncludeDirectories.Contains (FullPath) && !FullPath.Contains ("FortniteGame/") && !FullPath.Contains ("Intermediate/") && Directory.Exists (FullPath)) { SystemIncludeDirectories.Add (String.Format ("{0}", FullPath)); IncludeIndex++; } } foreach (var CurPath in KDevelopProject.IntelliSenseSystemIncludeSearchPaths) { string FullProjectPath = Path.GetFullPath(ProjectFileGenerator.MasterProjectRelativePath); string FullPath = ""; if (CurPath.StartsWith("/") && !CurPath.StartsWith(FullProjectPath)) { // Full path to a folder outside of project FullPath = CurPath; } else { FullPath = Path.GetFullPath(Path.Combine(Path.GetDirectoryName(KDevelopProject.ProjectFilePath), CurPath)); FullPath = Utils.MakePathRelativeTo(FullPath, FullProjectPath); FullPath = FullPath.TrimEnd('/'); FullPath = Path.Combine(UnrealEngineRootPath, FullPath); } if (!SystemIncludeDirectories.Contains(FullPath) && !FullPath.Contains("FortniteGame/") && !FullPath.Contains("Intermediate/") && Directory.Exists(FullPath)) // @todo: skipping Fortnite header paths to shorten clang command line for building UE4XcodeHelper { SystemIncludeDirectories.Add(String.Format("{0}",FullPath)); IncludeIndex++; } } } foreach (var CurPath in IncludeDirectories) { FileContent.Append (CurPath); FileContent.Append (" \n"); } foreach (var CurPath in SystemIncludeDirectories) { FileContent.Append (CurPath); FileContent.Append (" \n"); } FileContent.Append("\n"); } /// /// Write the defines section to the .kdev4/$ProjectName.kdev4 project file. /// /// File content. private void WriteDefineSection( ref StringBuilder FileContent) { List DefineHolder = new List(); foreach (var CurProject in GeneratedProjectFiles) { KDevelopProjectFile KDevelopProject = CurProject as KDevelopProjectFile; if (KDevelopProject == null) { System.Console.WriteLine ("KDevelopProject == null"); continue; } foreach (var CurDefine in KDevelopProject.IntelliSensePreprocessorDefinitions) { if (!DefineHolder.Contains (CurDefine)) { DefineHolder.Add (CurDefine); } } } foreach (var Def in DefineHolder) { FileContent.Append (Def + "\n"); } FileContent.Append ("\n\n"); } /// /// Excludes list in one big ugly list. /// /// File content. private void WriteExcludeSection( ref StringBuilder FileContent) { // Default excludes list with Engine/Intermediate, *PCH.h, and *.gch added to the exclude list FileContent.Append ("[Filters]\nsize=30\n\n[Filters][0]\ninclusive=0\npattern=.*\ntargets=3\n\n" + "[Filters][1]\ninclusive=0\npattern=.git\ntargets=2\n\n[Filters][10]\ninclusive=0\npattern=*.o\ntargets=1\n\n" + "[Filters][11]\ninclusive=0\npattern=*.a\ntargets=1\n\n[Filters][12]\ninclusive=0\npattern=*.so\ntargets=1\n\n" + "[Filters][13]\ninclusive=0\npattern=*.so.*\ntargets=1\n\n[Filters][14]\ninclusive=0\npattern=moc_*.cpp\ntargets=1\n\n" + "[Filters][15]\ninclusive=0\npattern=*.moc\ntargets=1\n\n[Filters][16]\ninclusive=0\npattern=ui_*.h\ntargets=1\n\n" + "[Filters][17]\ninclusive=0\npattern=qrc_*.cpp\ntargets=1\n\n[Filters][18]\ninclusive=0\npattern=*~\ntargets=1\n\n" + "[Filters][19]\ninclusive=0\npattern=*.orig\ntargets=1\n\n[Filters][2]\ninclusive=0\npattern=CVS\ntargets=2\n\n" + "[Filters][20]\ninclusive=0\npattern=.*.kate-swp\ntargets=1\n\n[Filters][21]\ninclusive=0\npattern=.*.swp\ntargets=1\n\n" + "[Filters][22]\ninclusive=0\npattern=*.pyc\ntargets=1\n\n[Filters][23]\ninclusive=0\npattern=*.pyo\ntargets=1\n\n" + "[Filters][24]\ninclusive=0\npattern=Engine/Intermediate\ntargets=2\n\n[Filters][25]\ninclusive=0\npattern=*PCH.h\ntargets=1\n\n" + "[Filters][26]\ninclusive=0\npattern=*.gch\ntargets=1\n\n[Filters][27]\ninclusive=0\npattern=*.generated.h\ntargets=1\n\n" + "[Filters][28]\ninclusive=0\npattern=Intermediate\ntargets=2\n\n[Filters][3]\ninclusive=0\npattern=.svn\ntargets=2\n\n" + "[Filters][4]\ninclusive=0\npattern=_svn\ntargets=2\n\n[Filters][5]\ninclusive=0\npattern=SCCS\ntargets=2\n\n" + "[Filters][6]\ninclusive=0\npattern=_darcs\ntargets=2\n\n[Filters][7]\ninclusive=0\npattern=.hg\ntargets=2\n\n" + "[Filters][8]\ninclusive=0\npattern=.bzr\ntargets=2\n\n[Filters][9]\ninclusive=0\npattern=__pycache__\ntargets=2\n\n" + "[Filters][29]\ninclusive=0\npattern=Engine/Source/ThirdParty\ntargets=2\n\n"); } /// Simple Place to call all the Write*Section functions. private bool WriteKDevelopPro() { /// RAKE! Take one KDevelopProjectFileContent and pass /// it through each function that writes out the sections. var KDevelopFileContent = new StringBuilder(); var KDevelopMasterFileContent = new StringBuilder (); // These are temp files until we can write them to the // *.kdev4 filename directly var DefinesFileContent = new StringBuilder (); var IncludesFileContent = new StringBuilder (); var FileName = MasterProjectName + ".kdev4"; var DefinesFileName = "Defines.txt"; // RAKE! TEMP! var IncludesFileName = "Includes.txt"; // RAKE! TEMP! WriteKDevMasterProjectSection (ref KDevelopMasterFileContent, MasterProjectName); WriteCommandSection (ref KDevelopFileContent); WriteIncludeSection (ref IncludesFileContent); WriteDefineSection (ref DefinesFileContent); WriteExcludeSection (ref KDevelopFileContent); // Write the master kdev file. var FullMasterProjectPath = Path.Combine (MasterProjectRelativePath,".kdev4/"); if(!Directory.Exists(FullMasterProjectPath)) { Directory.CreateDirectory (FullMasterProjectPath); } var FullKDevelopMasterFileName = Path.Combine(MasterProjectRelativePath, FileName); var FullKDevelopFileName = Path.Combine (FullMasterProjectPath, FileName); var FullDefinesFileName = Path.Combine (FullMasterProjectPath, DefinesFileName); var FullIncludesFileName = Path.Combine (FullMasterProjectPath, IncludesFileName); WriteFileIfChanged (FullDefinesFileName, DefinesFileContent.ToString()); WriteFileIfChanged (FullIncludesFileName, IncludesFileContent.ToString()); return WriteFileIfChanged (FullKDevelopMasterFileName, KDevelopMasterFileContent.ToString ()) && WriteFileIfChanged (FullKDevelopFileName, KDevelopFileContent.ToString ()); } /// ProjectFileGenerator interface //protected override bool WriteMasterProjectFile( ProjectFile UBTProject ) protected override bool WriteProjectFiles () { return WriteKDevelopPro(); } /// ProjectFileGenerator interface public override MasterProjectFolder AllocateMasterProjectFolder( ProjectFileGenerator InitOwnerProjectFileGenerator, string InitFolderName ) { return new KDevelopFolder( 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( string InitFilePath ) { return new KDevelopProjectFile( InitFilePath ); } /// ProjectFileGenerator interface public override void CleanProjectFiles(string InMasterProjectRelativePath, string InMasterProjectName, string InIntermediateProjectFilesPath) { } } }