Optimize build tool

This commit is contained in:
Wojtek Figat
2021-02-15 13:23:09 +01:00
parent 788907f3b1
commit 29f0834cc4
6 changed files with 99 additions and 65 deletions

View File

@@ -3,7 +3,6 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Flax.Build.NativeCpp;
using BuildData = Flax.Build.Builder.BuildData;
@@ -58,11 +57,8 @@ namespace Flax.Build.Bindings
private static ModuleInfo ParseModuleInner(BuildData buildData, Module module, BuildOptions moduleOptions = null)
{
if (buildData.ModulesInfo.TryGetValue(module, out var moduleInfo))
return moduleInfo;
// Setup bindings module info descriptor
moduleInfo = new ModuleInfo
var moduleInfo = new ModuleInfo
{
Module = module,
Name = module.BinaryModuleName,
@@ -81,26 +77,35 @@ namespace Flax.Build.Bindings
throw new Exception($"Cannot parse module {module.Name} without options.");
// Collect all header files that can have public symbols for API
var headerFiles = moduleOptions.SourceFiles.Where(x => x.EndsWith(".h")).ToList();
// Skip if no header files to process
var headerFiles = new List<string>(moduleOptions.SourceFiles.Count / 2);
for (int i = 0; i < moduleOptions.SourceFiles.Count; i++)
{
if (moduleOptions.SourceFiles[i].EndsWith(".h", StringComparison.OrdinalIgnoreCase))
headerFiles.Add(moduleOptions.SourceFiles[i]);
}
if (headerFiles.Count == 0)
return moduleInfo;
if (module.Name == "Core")
{
// Special case for Core module to ignore API tags defines
for (int i = 0; i < headerFiles.Count; i++)
{
if (headerFiles[i].EndsWith("Config.h", StringComparison.Ordinal))
{
headerFiles.RemoveAt(i);
break;
}
}
}
// TODO: use aynsc tasks or thread pool to load and parse multiple header files at once using multi-threading
// TODO: use async tasks or thread pool to load and parse multiple header files at once using multi-threading
// Find and load files with API tags
string[] headerFilesContents = null;
//using (new ProfileEventScope("LoadHeaderFiles"))
using (new ProfileEventScope("LoadHeaderFiles"))
{
var anyApi = false;
for (int i = 0; i < headerFiles.Count; i++)
{
// Skip scripting tags definitions file
if (headerFiles[i].Replace('\\', '/').EndsWith("Engine/Core/Config.h", StringComparison.Ordinal))
continue;
// Check if file contains any valid API tag
var contents = File.ReadAllText(headerFiles[i]);
for (int j = 0; j < ApiTokens.SearchTags.Length; j++)
{
@@ -109,15 +114,13 @@ namespace Flax.Build.Bindings
if (headerFilesContents == null)
headerFilesContents = new string[headerFiles.Count];
headerFilesContents[i] = contents;
anyApi = true;
break;
}
}
}
if (!anyApi)
return moduleInfo;
}
if (headerFilesContents == null)
return moduleInfo;
// Skip if none of the headers was modified after last time generated C++ file was edited
// TODO: skip parsing if module has API cache file -> then load it from disk

View File

@@ -12,12 +12,31 @@ namespace Flax.Build
partial class Builder
{
private static RulesAssembly _rules;
private static Type[] _buildTypes;
/// <summary>
/// The build configuration files postfix.
/// </summary>
public static string BuildFilesPostfix = ".Build.cs";
/// <summary>
/// The cached list of types from Flax.Build assembly. Reused by other build tool utilities to improve performance.
/// </summary>
internal static Type[] BuildTypes
{
get
{
if (_buildTypes == null)
{
using (new ProfileEventScope("CacheBuildTypes"))
{
_buildTypes = typeof(Program).Assembly.GetTypes();
}
}
return _buildTypes;
}
}
/// <summary>
/// The rules assembly data.
/// </summary>
@@ -128,7 +147,7 @@ namespace Flax.Build
using (new ProfileEventScope("InitInBuildPlugins"))
{
foreach (var type in typeof(Program).Assembly.GetTypes().Where(x => !x.IsAbstract && x.IsSubclassOf(typeof(Plugin))))
foreach (var type in BuildTypes.Where(x => x.IsClass && !x.IsAbstract && x.IsSubclassOf(typeof(Plugin))))
{
var plugin = (Plugin)Activator.CreateInstance(type);
plugin.Init();
@@ -176,67 +195,74 @@ namespace Flax.Build
// Prepare targets and modules objects
Type[] types;
Target[] targetObjects;
Module[] moduleObjects;
Plugin[] pluginObjects;
var targetObjects = new List<Target>(16);
var moduleObjects = new List<Module>(256);
var pluginObjects = new List<Plugin>();
using (new ProfileEventScope("GetTypes"))
{
types = assembly.GetTypes();
targetObjects = types.Where(x => !x.IsAbstract && x.IsSubclassOf(typeof(Target))).Select(type =>
for (var i = 0; i < types.Length; i++)
{
var target = (Target)Activator.CreateInstance(type);
var targetFilename = target.Name + BuildFilesPostfix;
target.FilePath = files.FirstOrDefault(path => string.Equals(Path.GetFileName(path), targetFilename, StringComparison.OrdinalIgnoreCase));
if (target.FilePath == null)
var type = types[i];
if (!type.IsClass || type.IsAbstract)
continue;
if (type.IsSubclassOf(typeof(Target)))
{
targetFilename = target.Name + "Target" + BuildFilesPostfix;
var target = (Target)Activator.CreateInstance(type);
var targetFilename = target.Name + BuildFilesPostfix;
target.FilePath = files.FirstOrDefault(path => string.Equals(Path.GetFileName(path), targetFilename, StringComparison.OrdinalIgnoreCase));
if (target.FilePath == null)
{
if (target.Name.EndsWith("Target"))
{
targetFilename = target.Name.Substring(0, target.Name.Length - "Target".Length) + BuildFilesPostfix;
target.FilePath = files.FirstOrDefault(path => string.Equals(Path.GetFileName(path), targetFilename, StringComparison.OrdinalIgnoreCase));
}
targetFilename = target.Name + "Target" + BuildFilesPostfix;
target.FilePath = files.FirstOrDefault(path => string.Equals(Path.GetFileName(path), targetFilename, StringComparison.OrdinalIgnoreCase));
if (target.FilePath == null)
{
throw new Exception(string.Format("Failed to find source file path for {0}", target));
if (target.Name.EndsWith("Target"))
{
targetFilename = target.Name.Substring(0, target.Name.Length - "Target".Length) + BuildFilesPostfix;
target.FilePath = files.FirstOrDefault(path => string.Equals(Path.GetFileName(path), targetFilename, StringComparison.OrdinalIgnoreCase));
}
if (target.FilePath == null)
{
throw new Exception(string.Format("Failed to find source file path for {0}", target));
}
}
}
target.FolderPath = Path.GetDirectoryName(target.FilePath);
target.Init();
targetObjects.Add(target);
}
target.FolderPath = Path.GetDirectoryName(target.FilePath);
target.Init();
return target;
}).ToArray();
moduleObjects = types.Where(x => !x.IsAbstract && x.IsSubclassOf(typeof(Module))).Select(type =>
{
var module = (Module)Activator.CreateInstance(type);
var moduleFilename = module.Name + BuildFilesPostfix;
module.FilePath = files.FirstOrDefault(path => string.Equals(Path.GetFileName(path), moduleFilename, StringComparison.OrdinalIgnoreCase));
if (module.FilePath == null)
else if (type.IsSubclassOf(typeof(Module)))
{
moduleFilename = module.Name + "Module" + BuildFilesPostfix;
var module = (Module)Activator.CreateInstance(type);
var moduleFilename = module.Name + BuildFilesPostfix;
module.FilePath = files.FirstOrDefault(path => string.Equals(Path.GetFileName(path), moduleFilename, StringComparison.OrdinalIgnoreCase));
if (module.FilePath == null)
{
throw new Exception(string.Format("Failed to find source file path for {0}", module));
moduleFilename = module.Name + "Module" + BuildFilesPostfix;
module.FilePath = files.FirstOrDefault(path => string.Equals(Path.GetFileName(path), moduleFilename, StringComparison.OrdinalIgnoreCase));
if (module.FilePath == null)
{
throw new Exception(string.Format("Failed to find source file path for {0}", module));
}
}
module.FolderPath = Path.GetDirectoryName(module.FilePath);
module.Init();
moduleObjects.Add(module);
}
module.FolderPath = Path.GetDirectoryName(module.FilePath);
module.Init();
return module;
}).ToArray();
pluginObjects = types.Where(x => !x.IsAbstract && x.IsSubclassOf(typeof(Plugin))).Select(type =>
{
var plugin = (Plugin)Activator.CreateInstance(type);
plugin.Init();
return plugin;
}).ToArray();
else if (type.IsSubclassOf(typeof(Plugin)))
{
var plugin = (Plugin)Activator.CreateInstance(type);
plugin.Init();
pluginObjects.Add(plugin);
}
}
}
_rules = new RulesAssembly(assembly, targetObjects, moduleObjects, pluginObjects);
_rules = new RulesAssembly(assembly, targetObjects.ToArray(), moduleObjects.ToArray(), pluginObjects.ToArray());
}
return _rules;

View File

@@ -330,7 +330,12 @@ namespace Flax.Build
}
// Collect all files to compile
var cppFiles = moduleOptions.SourceFiles.Where(x => x.EndsWith(".cpp")).ToList();
var cppFiles = new List<string>(moduleOptions.SourceFiles.Count / 2);
for (int i = 0; i < moduleOptions.SourceFiles.Count; i++)
{
if (moduleOptions.SourceFiles[i].EndsWith(".cpp", StringComparison.OrdinalIgnoreCase))
cppFiles.Add(moduleOptions.SourceFiles[i]);
}
if (!string.IsNullOrEmpty(module.BinaryModuleName))
{

View File

@@ -157,7 +157,7 @@ namespace Flax.Build
if (_platforms == null)
{
using (new ProfileEventScope("GetPlatforms"))
_platforms = typeof(Platform).Assembly.GetTypes().Where(x => !x.IsAbstract && x.IsSubclassOf(typeof(Platform))).Select(Activator.CreateInstance).Cast<Platform>().ToArray();
_platforms = Builder.BuildTypes.Where(x => x.IsClass && !x.IsAbstract && x.IsSubclassOf(typeof(Platform))).Select(Activator.CreateInstance).Cast<Platform>().ToArray();
}
foreach (var platform in _platforms)

View File

@@ -68,7 +68,7 @@ namespace Flax.Build
using (new ProfileEventScope("GetSdks"))
{
_sdks = new Dictionary<string, Sdk>();
var types = typeof(Sdk).Assembly.GetTypes().Where(x => !x.IsAbstract && x.IsSubclassOf(typeof(Sdk)));
var types = Builder.BuildTypes.Where(x => !x.IsAbstract && x.IsSubclassOf(typeof(Sdk)));
foreach (var type in types)
{
object instance = null;

View File

@@ -51,7 +51,7 @@ namespace Flax.Deps
}
// Get all deps
var dependencies = typeof(DepsBuilder).Assembly.GetTypes().Where(x => x.IsSubclassOf(typeof(Dependency))).Select(Activator.CreateInstance).Cast<Dependency>().ToArray();
var dependencies = Builder.BuildTypes.Where(x => x.IsSubclassOf(typeof(Dependency))).Select(Activator.CreateInstance).Cast<Dependency>().ToArray();
if (dependencies.Length == 0)
Log.Warning("No dependencies found!");
for (var i = 0; i < dependencies.Length; i++)