624 lines
31 KiB
C#
624 lines
31 KiB
C#
// Copyright (c) Wojciech Figat. All rights reserved.
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using Flax.Build.NativeCpp;
|
|
|
|
namespace Flax.Build.Projects.CMake
|
|
{
|
|
/// <summary>
|
|
/// Project generator for CMake-based IDE facade.
|
|
/// </summary>
|
|
public class CMakeProjectGenerator : ProjectGenerator
|
|
{
|
|
/// <inheritdoc />
|
|
public override string ProjectFileExtension => "cmake";
|
|
|
|
/// <inheritdoc />
|
|
public override string SolutionFileExtension => "cmake";
|
|
|
|
/// <inheritdoc />
|
|
public override TargetType? Type => null;
|
|
|
|
/// <inheritdoc />
|
|
public override void GenerateProject(Project project, string solutionPath, bool isMainProject)
|
|
{
|
|
// Not used, solution contains all CMake project definitions.
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public override void GenerateSolution(Solution solution)
|
|
{
|
|
var projectFolder = GetProjectFolder(solution);
|
|
if (!Directory.Exists(projectFolder))
|
|
Directory.CreateDirectory(projectFolder);
|
|
|
|
var generateCLionFiles = Configuration.ProjectFormatCLion;
|
|
var contents = new StringBuilder();
|
|
var buildPresets = new List<CMakeBuildPreset>();
|
|
var defaultBuildTarget = string.Empty;
|
|
var targetNames = new HashSet<string>();
|
|
var buildToolPath = Path.ChangeExtension(typeof(Builder).Assembly.Location, null) + Utilities.GetPlatformExecutableExt();
|
|
|
|
contents.AppendLine("cmake_minimum_required(VERSION 3.20)");
|
|
contents.AppendLine();
|
|
contents.AppendLine("# Generated by Flax.Build. Do not edit manually.");
|
|
contents.AppendLine("# CMake is used as an IDE facade. Actual builds are delegated to Flax.Build.");
|
|
contents.AppendLine($"project({EscapeIdentifier(solution.Name)} LANGUAGES C CXX)");
|
|
contents.AppendLine();
|
|
contents.AppendLine("set(CMAKE_EXPORT_COMPILE_COMMANDS ON)");
|
|
contents.AppendLine($"set(FLAX_WORKSPACE {QuotePath(solution.WorkspaceRootPath, solution.WorkspaceRootPath)})");
|
|
contents.AppendLine($"set(FLAX_BUILD_TOOL {QuotePath(buildToolPath, solution.WorkspaceRootPath)})");
|
|
if (DotNetSdk.Instance.IsValid)
|
|
contents.AppendLine($"set(FLAX_DOTNET_ROOT {QuotePath(DotNetSdk.Instance.RootPath, solution.WorkspaceRootPath)})");
|
|
contents.AppendLine();
|
|
|
|
foreach (var project in solution.Projects.Where(x => x.Type == TargetType.NativeCpp))
|
|
{
|
|
var visibleFiles = GetProjectFiles(project, generateCLionFiles);
|
|
var hasCodeModelConfiguration = TryGetCodeModelConfiguration(project, out var codeModelConfiguration);
|
|
// Use Flax.Build-selected compile units for the code model. The wider project scan is only for visible files.
|
|
var compileFiles = hasCodeModelConfiguration
|
|
? GetCodeModelCompileFiles(codeModelConfiguration, generateCLionFiles)
|
|
: visibleFiles.Where(x => IsCompileSourceFile(x, generateCLionFiles)).ToArray();
|
|
var files = new HashSet<string>(visibleFiles);
|
|
files.AddRange(compileFiles);
|
|
var allFiles = files.OrderBy(x => x).ToArray();
|
|
var compileFileSet = new HashSet<string>(compileFiles);
|
|
var nonCompileFiles = allFiles.Where(x => !compileFileSet.Contains(x)).ToArray();
|
|
var includePaths = hasCodeModelConfiguration ? GetIncludePaths(project, codeModelConfiguration) : Array.Empty<string>();
|
|
var definitions = hasCodeModelConfiguration ? GetPreprocessorDefinitions(project, codeModelConfiguration) : project.Defines.OrderBy(x => x).ToArray();
|
|
var cppStandard = hasCodeModelConfiguration ? GetCppStandard(codeModelConfiguration) : 14;
|
|
var ideTargetName = GetUniqueTargetName(targetNames, "IDE_" + project.Name);
|
|
|
|
contents.AppendLine($"# {project.Name}");
|
|
if (compileFiles.Length != 0)
|
|
{
|
|
contents.AppendLine($"add_library({ideTargetName} STATIC EXCLUDE_FROM_ALL)");
|
|
contents.AppendLine($"set_target_properties({ideTargetName} PROPERTIES EXCLUDE_FROM_ALL TRUE)");
|
|
AppendTargetSources(contents, ideTargetName, allFiles, solution.WorkspaceRootPath);
|
|
if (nonCompileFiles.Length != 0)
|
|
{
|
|
contents.AppendLine("set_source_files_properties(");
|
|
foreach (var file in nonCompileFiles)
|
|
contents.AppendLine($" {QuotePath(file, solution.WorkspaceRootPath)}");
|
|
contents.AppendLine(" PROPERTIES HEADER_FILE_ONLY TRUE");
|
|
contents.AppendLine(")");
|
|
}
|
|
contents.AppendLine($"set_target_properties({ideTargetName} PROPERTIES CXX_STANDARD {cppStandard} CXX_STANDARD_REQUIRED YES)");
|
|
AppendTargetPaths(contents, ideTargetName, "target_include_directories", "PRIVATE", includePaths, solution.WorkspaceRootPath);
|
|
AppendTargetValues(contents, ideTargetName, "target_compile_definitions", "PRIVATE", definitions);
|
|
}
|
|
else if (allFiles.Length != 0)
|
|
{
|
|
contents.AppendLine($"add_custom_target({ideTargetName} SOURCES");
|
|
foreach (var file in allFiles)
|
|
contents.AppendLine($" {QuotePath(file, solution.WorkspaceRootPath)}");
|
|
contents.AppendLine(")");
|
|
}
|
|
|
|
foreach (var configuration in project.Configurations)
|
|
{
|
|
if (generateCLionFiles && !IsCLionDevelopmentPlatform(configuration.Platform))
|
|
continue;
|
|
|
|
var buildTargetName = GetUniqueTargetName(targetNames, "Build_" + project.Name + "_" + configuration.Target.Name + "_" + configuration.PlatformName + "_" + configuration.ConfigurationName + "_" + configuration.ArchitectureName);
|
|
|
|
AppendBuildTarget(contents, buildTargetName, "-build", configuration);
|
|
var presetSuffix = project.Name + "-" + configuration.Target.Name + "-" + configuration.PlatformName + "-" + configuration.ConfigurationName + "-" + configuration.ArchitectureName;
|
|
if (IsHostConfiguration(configuration))
|
|
AddBuildPreset(buildPresets, "build-" + presetSuffix, "Build " + presetSuffix, buildTargetName);
|
|
|
|
if (defaultBuildTarget.Length == 0 && project == solution.MainProject && configuration.Platform == Platform.BuildPlatform.Target && configuration.Architecture == Platform.BuildTargetArchitecture && configuration.Configuration == TargetConfiguration.Development)
|
|
defaultBuildTarget = buildTargetName;
|
|
|
|
if (generateCLionFiles)
|
|
{
|
|
var runProgram = GetRunProgram(project, configuration, out var runArguments);
|
|
if (IsHostConfiguration(configuration) && !string.IsNullOrEmpty(runProgram))
|
|
{
|
|
var runTargetName = GetUniqueTargetName(targetNames, "App_" + project.Name + "_" + configuration.Target.Name + "_" + configuration.PlatformName + "_" + configuration.ConfigurationName + "_" + configuration.ArchitectureName);
|
|
AppendRunTarget(contents, runTargetName, runProgram, runArguments);
|
|
contents.AppendLine($"add_dependencies({runTargetName} {buildTargetName})");
|
|
contents.AppendLine();
|
|
}
|
|
}
|
|
}
|
|
|
|
contents.AppendLine();
|
|
}
|
|
|
|
if (!string.IsNullOrEmpty(defaultBuildTarget))
|
|
{
|
|
contents.AppendLine($"add_custom_target({EscapeIdentifier(solution.Name)}_Build ALL DEPENDS {defaultBuildTarget})");
|
|
contents.AppendLine();
|
|
}
|
|
|
|
Utilities.WriteFileIfChanged(Path.Combine(projectFolder, "CMakeLists.txt"), contents.ToString());
|
|
GeneratePresets(solution, projectFolder, buildPresets);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the directory that contains the generated CMake facade project.
|
|
/// </summary>
|
|
public static string GetProjectFolder(Solution solution)
|
|
{
|
|
return Path.Combine(solution.WorkspaceRootPath, "Cache", "Projects", "CMake", solution.Name);
|
|
}
|
|
|
|
private struct CMakeBuildPreset
|
|
{
|
|
public string Name;
|
|
public string DisplayName;
|
|
public string Target;
|
|
}
|
|
|
|
private static void AddBuildPreset(List<CMakeBuildPreset> presets, string name, string displayName, string target)
|
|
{
|
|
presets.Add(new CMakeBuildPreset
|
|
{
|
|
Name = EscapePresetName(name),
|
|
DisplayName = displayName,
|
|
Target = target,
|
|
});
|
|
}
|
|
|
|
private static void GeneratePresets(Solution solution, string projectFolder, List<CMakeBuildPreset> buildPresets)
|
|
{
|
|
var contents = new StringBuilder();
|
|
contents.AppendLine("{");
|
|
contents.AppendLine(" \"version\": 3,");
|
|
contents.AppendLine(" \"cmakeMinimumRequired\": {");
|
|
contents.AppendLine(" \"major\": 3,");
|
|
contents.AppendLine(" \"minor\": 20,");
|
|
contents.AppendLine(" \"patch\": 0");
|
|
contents.AppendLine(" },");
|
|
contents.AppendLine(" \"configurePresets\": [");
|
|
contents.AppendLine(" {");
|
|
contents.AppendLine(" \"name\": \"flax\",");
|
|
contents.AppendLine(" \"displayName\": \"Flax\",");
|
|
contents.AppendLine(" \"description\": \"Configure Flax CMake facade for IDE integration.\",");
|
|
contents.AppendLine(" \"generator\": \"Ninja\",");
|
|
contents.AppendLine(" \"binaryDir\": \"${sourceDir}/cmake-build-flax\",");
|
|
contents.AppendLine(" \"cacheVariables\": {");
|
|
contents.AppendLine(" \"CMAKE_BUILD_TYPE\": \"Debug\",");
|
|
contents.AppendLine(" \"CMAKE_EXPORT_COMPILE_COMMANDS\": \"ON\"");
|
|
contents.AppendLine(" }");
|
|
contents.AppendLine(" }");
|
|
contents.AppendLine(" ],");
|
|
contents.AppendLine(" \"buildPresets\": [");
|
|
for (var i = 0; i < buildPresets.Count; i++)
|
|
{
|
|
var preset = buildPresets[i];
|
|
contents.AppendLine(" {");
|
|
contents.AppendLine($" \"name\": {JsonString(preset.Name)},");
|
|
contents.AppendLine($" \"displayName\": {JsonString(preset.DisplayName)},");
|
|
contents.AppendLine(" \"configurePreset\": \"flax\",");
|
|
contents.AppendLine($" \"targets\": [{JsonString(preset.Target)}]");
|
|
contents.Append(" }");
|
|
if (i != buildPresets.Count - 1)
|
|
contents.Append(',');
|
|
contents.AppendLine();
|
|
}
|
|
contents.AppendLine(" ]");
|
|
contents.AppendLine("}");
|
|
|
|
Utilities.WriteFileIfChanged(Path.Combine(projectFolder, "CMakePresets.json"), contents.ToString());
|
|
}
|
|
|
|
private static void AppendBuildTarget(StringBuilder contents, string targetName, string action, Project.ConfigurationData configuration)
|
|
{
|
|
contents.AppendLine($"add_custom_target({targetName}");
|
|
contents.Append(" COMMAND ");
|
|
if (DotNetSdk.Instance.IsValid)
|
|
contents.Append("${CMAKE_COMMAND} -E env \"DOTNET_ROOT=${FLAX_DOTNET_ROOT}\" ");
|
|
contents.AppendLine($"\"${{FLAX_BUILD_TOOL}}\" {Quote(action)} \"-log\" \"-mutex\"");
|
|
contents.AppendLine(" \"-workspace=${FLAX_WORKSPACE}\"");
|
|
contents.AppendLine($" \"-arch={configuration.ArchitectureName}\"");
|
|
contents.AppendLine($" \"-configuration={configuration.ConfigurationName}\"");
|
|
contents.AppendLine($" \"-platform={configuration.PlatformName}\"");
|
|
contents.AppendLine($" \"-buildTargets={configuration.Target.Name}\"");
|
|
if (!string.IsNullOrEmpty(Configuration.Compiler))
|
|
contents.AppendLine($" \"-compiler={EscapeString(Configuration.Compiler)}\"");
|
|
if (!string.IsNullOrEmpty(Configuration.Dotnet))
|
|
contents.AppendLine($" \"-dotnet={EscapeString(Configuration.Dotnet)}\"");
|
|
if (Configuration.Sanitizers != Sanitizer.None)
|
|
contents.AppendLine($" \"-sanitizers={Configuration.Sanitizers}\"");
|
|
contents.AppendLine(" WORKING_DIRECTORY \"${FLAX_WORKSPACE}\"");
|
|
contents.AppendLine(" USES_TERMINAL");
|
|
contents.AppendLine(" VERBATIM");
|
|
contents.AppendLine(")");
|
|
contents.AppendLine();
|
|
}
|
|
|
|
private static void AppendRunTarget(StringBuilder contents, string targetName, string program, string[] arguments)
|
|
{
|
|
var sourcePath = $"${{CMAKE_CURRENT_BINARY_DIR}}/{targetName}.cpp";
|
|
contents.AppendLine($"file(WRITE \"{sourcePath}\" [=[");
|
|
contents.AppendLine("#include <cstdio>");
|
|
contents.AppendLine("#include <vector>");
|
|
contents.AppendLine("#ifdef _WIN32");
|
|
contents.AppendLine("#include <process.h>");
|
|
contents.AppendLine("#else");
|
|
contents.AppendLine("#include <unistd.h>");
|
|
contents.AppendLine("#endif");
|
|
contents.AppendLine("int main(int argc, char** argv) {");
|
|
contents.AppendLine($" const char* program = \"{CppString(program)}\";");
|
|
contents.AppendLine(" std::vector<const char*> args;");
|
|
contents.AppendLine(" args.reserve(argc + 1);");
|
|
contents.AppendLine(" args.push_back(program);");
|
|
foreach (var arg in arguments)
|
|
contents.AppendLine($" args.push_back(\"{CppString(arg)}\");");
|
|
contents.AppendLine(" for (int i = 1; i < argc; i++)");
|
|
contents.AppendLine(" args.push_back(argv[i]);");
|
|
contents.AppendLine(" args.push_back(nullptr);");
|
|
contents.AppendLine("#ifdef _WIN32");
|
|
contents.AppendLine(" _execv(args[0], args.data());");
|
|
contents.AppendLine("#else");
|
|
contents.AppendLine(" execv(args[0], const_cast<char* const*>(args.data()));");
|
|
contents.AppendLine("#endif");
|
|
contents.AppendLine(" perror(args[0]);");
|
|
contents.AppendLine(" return 1;");
|
|
contents.AppendLine("}");
|
|
contents.AppendLine("]=])");
|
|
contents.AppendLine($"add_executable({targetName} EXCLUDE_FROM_ALL \"{sourcePath}\")");
|
|
contents.AppendLine($"set_target_properties({targetName} PROPERTIES");
|
|
contents.AppendLine(" RUNTIME_OUTPUT_DIRECTORY \"${CMAKE_CURRENT_BINARY_DIR}/FlaxLaunchers\"");
|
|
contents.AppendLine(")");
|
|
}
|
|
|
|
private static void AppendTargetSources(StringBuilder contents, string targetName, string[] files, string workspaceRoot)
|
|
{
|
|
contents.AppendLine($"target_sources({targetName} PRIVATE");
|
|
foreach (var file in files)
|
|
contents.AppendLine($" {QuotePath(file, workspaceRoot)}");
|
|
contents.AppendLine(")");
|
|
}
|
|
|
|
private static void AppendTargetPaths(StringBuilder contents, string targetName, string command, string visibility, string[] values, string workspaceRoot)
|
|
{
|
|
if (values.Length == 0)
|
|
return;
|
|
|
|
contents.AppendLine($"{command}({targetName} {visibility}");
|
|
foreach (var value in values)
|
|
contents.AppendLine($" {QuotePath(value, workspaceRoot)}");
|
|
contents.AppendLine(")");
|
|
}
|
|
|
|
private static void AppendTargetValues(StringBuilder contents, string targetName, string command, string visibility, string[] values)
|
|
{
|
|
if (values.Length == 0)
|
|
return;
|
|
|
|
contents.AppendLine($"{command}({targetName} {visibility}");
|
|
foreach (var value in values)
|
|
contents.AppendLine($" {Quote(value)}");
|
|
contents.AppendLine(")");
|
|
}
|
|
|
|
private static string[] GetProjectFiles(Project project, bool generateCLionFiles)
|
|
{
|
|
var files = new HashSet<string>();
|
|
if (project.SourceFiles != null)
|
|
files.AddRange(project.SourceFiles);
|
|
if (project.GeneratedSourceFiles != null)
|
|
files.AddRange(project.GeneratedSourceFiles);
|
|
if (project.SourceDirectories != null)
|
|
{
|
|
foreach (var folder in project.SourceDirectories)
|
|
{
|
|
if (Directory.Exists(folder))
|
|
files.AddRange(Directory.GetFiles(folder, "*", SearchOption.AllDirectories));
|
|
}
|
|
}
|
|
files.RemoveWhere(x =>
|
|
!IsProjectFile(x) ||
|
|
Path.GetFileName(x).Equals(".DS_Store", StringComparison.OrdinalIgnoreCase) ||
|
|
(generateCLionFiles && !IsCLionDevelopmentSourceFile(x)));
|
|
return files.OrderBy(x => x).ToArray();
|
|
}
|
|
|
|
private static string[] GetCodeModelCompileFiles(Project.ConfigurationData configuration, bool generateCLionFiles)
|
|
{
|
|
var files = new HashSet<string>();
|
|
var definitions = new HashSet<string>(configuration.TargetBuildOptions.CompileEnv.PreprocessorDefinitions);
|
|
AddCompileFiles(files, configuration.TargetBuildOptions.SourceFiles, generateCLionFiles, definitions);
|
|
if (configuration.Modules != null)
|
|
{
|
|
foreach (var module in configuration.Modules)
|
|
{
|
|
if (module.Key.BuildNativeCode)
|
|
AddCompileFiles(files, module.Value.SourceFiles, generateCLionFiles, definitions);
|
|
}
|
|
}
|
|
return files.OrderBy(x => x).ToArray();
|
|
}
|
|
|
|
private static void AddCompileFiles(HashSet<string> files, IEnumerable<string> sourceFiles, bool generateCLionFiles, HashSet<string> definitions = null)
|
|
{
|
|
if (sourceFiles == null)
|
|
return;
|
|
|
|
foreach (var file in sourceFiles)
|
|
{
|
|
if (IsCompileSourceFile(file, generateCLionFiles, definitions))
|
|
files.Add(file);
|
|
}
|
|
}
|
|
|
|
private static bool TryGetCodeModelConfiguration(Project project, out Project.ConfigurationData configuration)
|
|
{
|
|
foreach (var e in project.Configurations)
|
|
{
|
|
if (e.TargetBuildOptions != null &&
|
|
e.Platform == Platform.BuildPlatform.Target &&
|
|
e.Architecture == Platform.BuildTargetArchitecture &&
|
|
e.Configuration == TargetConfiguration.Development)
|
|
{
|
|
configuration = e;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
foreach (var e in project.Configurations)
|
|
{
|
|
if (e.TargetBuildOptions != null)
|
|
{
|
|
configuration = e;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
configuration = default;
|
|
return false;
|
|
}
|
|
|
|
private static bool IsHostConfiguration(Project.ConfigurationData configuration)
|
|
{
|
|
return configuration.Platform == Platform.BuildPlatform.Target &&
|
|
configuration.Architecture == Platform.BuildTargetArchitecture;
|
|
}
|
|
|
|
private static bool IsCLionDevelopmentPlatform(TargetPlatform platform)
|
|
{
|
|
switch (platform)
|
|
{
|
|
case TargetPlatform.Windows:
|
|
case TargetPlatform.Linux:
|
|
case TargetPlatform.Mac:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
private static string[] GetIncludePaths(Project project, Project.ConfigurationData configuration)
|
|
{
|
|
var result = new HashSet<string>();
|
|
if (project.SearchPaths != null)
|
|
result.AddRange(project.SearchPaths);
|
|
result.AddRange(configuration.TargetBuildOptions.CompileEnv.IncludePaths);
|
|
return result.OrderBy(x => x).ToArray();
|
|
}
|
|
|
|
private static string[] GetPreprocessorDefinitions(Project project, Project.ConfigurationData configuration)
|
|
{
|
|
var result = new HashSet<string>();
|
|
result.AddRange(project.Defines);
|
|
result.AddRange(configuration.TargetBuildOptions.CompileEnv.PreprocessorDefinitions);
|
|
return result.OrderBy(x => x).ToArray();
|
|
}
|
|
|
|
private static int GetCppStandard(Project.ConfigurationData configuration)
|
|
{
|
|
var result = 14;
|
|
switch (configuration.TargetBuildOptions.CompileEnv.CppVersion)
|
|
{
|
|
case CppVersion.Cpp17:
|
|
result = Math.Max(result, 17);
|
|
break;
|
|
case CppVersion.Cpp20:
|
|
case CppVersion.Latest:
|
|
result = Math.Max(result, 20);
|
|
break;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
private static string GetRunProgram(Project project, Project.ConfigurationData configuration, out string[] arguments)
|
|
{
|
|
var target = configuration.Target;
|
|
var outputType = project.OutputType ?? target.OutputType;
|
|
if (ShouldRunEditorForProject(configuration, outputType))
|
|
{
|
|
var program = Path.Combine(Globals.EngineRoot, Platform.GetEditorBinaryDirectory(), configuration.ConfigurationName, "FlaxEditor" + Utilities.GetPlatformExecutableExt());
|
|
var args = new List<string>();
|
|
if (configuration.Platform == TargetPlatform.Linux || configuration.Platform == TargetPlatform.Mac)
|
|
args.Add("-std");
|
|
args.Add($"-project=\"{project.WorkspaceRootPath}\"");
|
|
args.Add("-skipCompile");
|
|
arguments = args.ToArray();
|
|
return program;
|
|
}
|
|
|
|
arguments = configuration.Platform == TargetPlatform.Linux || configuration.Platform == TargetPlatform.Mac ? new[] { "--std" } : Array.Empty<string>();
|
|
return target.GetOutputFilePath(configuration.TargetBuildOptions, TargetOutputType.Executable);
|
|
}
|
|
|
|
private static bool ShouldRunEditorForProject(Project.ConfigurationData configuration, TargetOutputType outputType)
|
|
{
|
|
return outputType != TargetOutputType.Executable && configuration.Target.IsEditor;
|
|
}
|
|
|
|
private static bool IsCppCompileFile(string path)
|
|
{
|
|
var extension = Path.GetExtension(path);
|
|
return extension.Equals(".c", StringComparison.OrdinalIgnoreCase) ||
|
|
extension.Equals(".cc", StringComparison.OrdinalIgnoreCase) ||
|
|
extension.Equals(".cpp", StringComparison.OrdinalIgnoreCase) ||
|
|
extension.Equals(".cxx", StringComparison.OrdinalIgnoreCase);
|
|
}
|
|
|
|
private static bool IsCompileSourceFile(string path, bool generateCLionFiles, HashSet<string> definitions = null)
|
|
{
|
|
return IsCppCompileFile(path) &&
|
|
(!generateCLionFiles || (IsCLionDevelopmentSourceFile(path) && IsCLionSourceGuardActive(path, definitions)));
|
|
}
|
|
|
|
private static bool IsCLionDevelopmentSourceFile(string path)
|
|
{
|
|
path = Utilities.NormalizePath(path);
|
|
return !ContainsPathSegment(path, "Android") &&
|
|
!ContainsPathSegment(path, "AndroidJNI") &&
|
|
!ContainsPathSegment(path, "GDK") &&
|
|
!ContainsPathSegment(path, "iOS") &&
|
|
!ContainsPathSegment(path, "PS4") &&
|
|
!ContainsPathSegment(path, "PS5") &&
|
|
!ContainsPathSegment(path, "Switch") &&
|
|
!ContainsPathSegment(path, "UWP") &&
|
|
!ContainsPathSegment(path, "Web") &&
|
|
!ContainsPathSegment(path, "XboxOne") &&
|
|
!ContainsPathSegment(path, "XboxScarlett") &&
|
|
!Path.GetFileName(path).Contains(".Web.", StringComparison.OrdinalIgnoreCase);
|
|
}
|
|
|
|
private static bool ContainsPathSegment(string path, string segment)
|
|
{
|
|
return path.IndexOf("/" + segment + "/", StringComparison.OrdinalIgnoreCase) != -1;
|
|
}
|
|
|
|
private static bool IsCLionSourceGuardActive(string path, HashSet<string> definitions)
|
|
{
|
|
if (definitions == null)
|
|
return true;
|
|
|
|
path = Utilities.NormalizePath(path);
|
|
if (ContainsSourcePath(path, "Source/Editor/Cooker/Platform/Windows"))
|
|
return HasDefinition(definitions, "PLATFORM_TOOLS_WINDOWS");
|
|
if (ContainsSourcePath(path, "Source/Editor/Cooker/Platform/Linux"))
|
|
return HasDefinition(definitions, "PLATFORM_TOOLS_LINUX");
|
|
if (ContainsSourcePath(path, "Source/Editor/Cooker/Platform/Mac"))
|
|
return HasDefinition(definitions, "PLATFORM_TOOLS_MAC");
|
|
|
|
if (ContainsSourcePath(path, "Source/Engine/Engine/Windows"))
|
|
return HasDefinition(definitions, "PLATFORM_WINDOWS") && !HasDefinition(definitions, "USE_EDITOR");
|
|
if (ContainsSourcePath(path, "Source/Engine/Engine/Linux"))
|
|
return HasDefinition(definitions, "PLATFORM_LINUX") && !HasDefinition(definitions, "USE_EDITOR");
|
|
if (ContainsSourcePath(path, "Source/Engine/Engine/Mac"))
|
|
return HasDefinition(definitions, "PLATFORM_MAC") && !HasDefinition(definitions, "USE_EDITOR");
|
|
|
|
if (ContainsSourcePath(path, "Source/Engine/GraphicsDevice/Vulkan/Win32"))
|
|
return HasDefinition(definitions, "GRAPHICS_API_VULKAN") && (HasDefinition(definitions, "PLATFORM_WIN32") || HasDefinition(definitions, "PLATFORM_WINDOWS"));
|
|
if (ContainsSourcePath(path, "Source/Engine/GraphicsDevice/Vulkan/Linux"))
|
|
return HasDefinition(definitions, "GRAPHICS_API_VULKAN") && HasDefinition(definitions, "PLATFORM_LINUX");
|
|
if (ContainsSourcePath(path, "Source/Engine/GraphicsDevice/Vulkan/Mac"))
|
|
return HasDefinition(definitions, "GRAPHICS_API_VULKAN") && HasDefinition(definitions, "PLATFORM_MAC");
|
|
|
|
if (ContainsSourcePath(path, "Source/Engine/Platform/Win32"))
|
|
return HasDefinition(definitions, "PLATFORM_WIN32") || HasDefinition(definitions, "PLATFORM_WINDOWS");
|
|
if (ContainsSourcePath(path, "Source/Engine/Platform/Windows"))
|
|
return HasDefinition(definitions, "PLATFORM_WINDOWS");
|
|
if (ContainsSourcePath(path, "Source/Engine/Platform/Linux"))
|
|
return HasDefinition(definitions, "PLATFORM_LINUX");
|
|
if (ContainsSourcePath(path, "Source/Engine/Platform/Mac"))
|
|
return HasDefinition(definitions, "PLATFORM_MAC");
|
|
if (ContainsSourcePath(path, "Source/Engine/Platform/Apple"))
|
|
return HasDefinition(definitions, "PLATFORM_MAC") || HasDefinition(definitions, "PLATFORM_IOS");
|
|
if (ContainsSourcePath(path, "Source/Engine/Platform/Unix"))
|
|
return HasDefinition(definitions, "PLATFORM_UNIX");
|
|
|
|
return true;
|
|
}
|
|
|
|
private static bool ContainsSourcePath(string path, string sourcePath)
|
|
{
|
|
return path.IndexOf("/" + sourcePath + "/", StringComparison.OrdinalIgnoreCase) != -1;
|
|
}
|
|
|
|
private static bool HasDefinition(HashSet<string> definitions, string name)
|
|
{
|
|
return definitions.Contains(name) ||
|
|
definitions.Any(x => x.StartsWith(name + "=", StringComparison.Ordinal));
|
|
}
|
|
|
|
private static bool IsProjectFile(string path)
|
|
{
|
|
var extension = Path.GetExtension(path);
|
|
return IsCppCompileFile(path) ||
|
|
extension.Equals(".h", StringComparison.OrdinalIgnoreCase) ||
|
|
extension.Equals(".hh", StringComparison.OrdinalIgnoreCase) ||
|
|
extension.Equals(".hpp", StringComparison.OrdinalIgnoreCase) ||
|
|
extension.Equals(".hxx", StringComparison.OrdinalIgnoreCase) ||
|
|
extension.Equals(".inl", StringComparison.OrdinalIgnoreCase) ||
|
|
extension.Equals(".rc", StringComparison.OrdinalIgnoreCase) ||
|
|
extension.Equals(".manifest", StringComparison.OrdinalIgnoreCase) ||
|
|
extension.Equals(".natvis", StringComparison.OrdinalIgnoreCase);
|
|
}
|
|
|
|
private static string GetUniqueTargetName(HashSet<string> names, string name)
|
|
{
|
|
var result = EscapeIdentifier(name);
|
|
var baseName = result;
|
|
var index = 2;
|
|
while (!names.Add(result))
|
|
result = baseName + "_" + index++;
|
|
return result;
|
|
}
|
|
|
|
private static string EscapeIdentifier(string value)
|
|
{
|
|
if (string.IsNullOrEmpty(value))
|
|
return "_";
|
|
|
|
var result = new StringBuilder(value.Length);
|
|
foreach (var c in value)
|
|
result.Append(char.IsLetterOrDigit(c) || c == '_' ? c : '_');
|
|
if (char.IsDigit(result[0]))
|
|
result.Insert(0, '_');
|
|
return result.ToString();
|
|
}
|
|
|
|
private static string EscapePresetName(string value)
|
|
{
|
|
var result = new StringBuilder(value.Length);
|
|
foreach (var c in value)
|
|
result.Append(char.IsLetterOrDigit(c) || c == '-' || c == '_' || c == '.' ? char.ToLowerInvariant(c) : '-');
|
|
return result.ToString();
|
|
}
|
|
|
|
private static string QuotePath(string value, string workspaceRoot, bool includeQuotes = true)
|
|
{
|
|
value = Utilities.NormalizePath(value);
|
|
workspaceRoot = Utilities.NormalizePath(workspaceRoot).TrimEnd('/');
|
|
if (value.StartsWith(workspaceRoot + "/", StringComparison.Ordinal))
|
|
value = "${FLAX_WORKSPACE}/" + value.Substring(workspaceRoot.Length + 1);
|
|
return Quote(value, includeQuotes);
|
|
}
|
|
|
|
private static string Quote(string value, bool includeQuotes = true)
|
|
{
|
|
value = EscapeString(value);
|
|
return includeQuotes ? "\"" + value + "\"" : value;
|
|
}
|
|
|
|
private static string EscapeString(string value)
|
|
{
|
|
return value.Replace("\\", "\\\\").Replace(";", "\\;").Replace("\"", "\\\"");
|
|
}
|
|
|
|
private static string JsonString(string value)
|
|
{
|
|
return "\"" + value.Replace("\\", "\\\\").Replace("\"", "\\\"") + "\"";
|
|
}
|
|
|
|
private static string CppString(string value)
|
|
{
|
|
return value.Replace("\\", "\\\\").Replace("\"", "\\\"").Replace("\n", "\\n").Replace("\r", "\\r");
|
|
}
|
|
}
|
|
}
|