4 Commits

Author SHA1 Message Date
8cf6979f90 _some prog with dynamic linking perhaps
Some checks failed
Build Android / Game (Android, Release ARM64) (push) Has been cancelled
Build iOS / Game (iOS, Release ARM64) (push) Has been cancelled
Build Linux / Editor (Linux, Development x64) (push) Has been cancelled
Build Linux / Game (Linux, Release x64) (push) Has been cancelled
Build macOS / Editor (Mac, Development ARM64) (push) Has been cancelled
Build macOS / Game (Mac, Release ARM64) (push) Has been cancelled
Build Windows / Editor (Windows, Development x64) (push) Has been cancelled
Build Windows / Game (Windows, Release x64) (push) Has been cancelled
Cooker / Cook (Mac) (push) Has been cancelled
Tests / Tests (Linux) (push) Has been cancelled
Tests / Tests (Windows) (push) Has been cancelled
2026-03-15 22:45:36 +02:00
1ff4ac0402 _maybe prog, trying static linking 2026-03-14 15:50:15 +02:00
801f0f7432 _some prog, needs aot for corlib 2026-03-14 15:50:15 +02:00
8b605dff89 _baseline emscripten build 2026-03-14 15:49:23 +02:00
28 changed files with 641 additions and 60 deletions

View File

@@ -1,4 +1,4 @@
# Redirect to our own Git LFS server
[lfs]
url="https://gitlab.flaxengine.com/flax/flaxengine.git/info/lfs"
#url="https://gitlab.flaxengine.com/flax/flaxengine.git/info/lfs"
locksverify = false

View File

@@ -49,7 +49,7 @@ ArchitectureType WebPlatformTools::GetArchitecture() const
DotNetAOTModes WebPlatformTools::UseAOT() const
{
return DotNetAOTModes::NoDotnet;
return DotNetAOTModes::MonoAOTDynamic;//MonoAOTStatic;// DotNetAOTModes::None;//DotNetAOTModes::MonoAOTStatic;//DotNetAOTModes::NoDotnet;
}
PixelFormat WebPlatformTools::GetTextureFormat(CookingData& data, TextureBase* texture, PixelFormat format)
@@ -166,8 +166,9 @@ bool WebPlatformTools::OnPostProcess(CookingData& data)
// Move .wasm assemblies into the data files in order for dlopen to work (blocking)
{
Array<String> files;
Array<String> files, files2;
FileSystem::DirectoryGetFiles(files, data.OriginalOutputPath, TEXT("*.wasm"), DirectorySearchOption::AllDirectories);
FileSystem::DirectoryGetFiles(files, data.OriginalOutputPath, TEXT("*.so"), DirectorySearchOption::AllDirectories);
StringView gameWasm = StringUtils::GetFileNameWithoutExtension(gameJs);
for (const String& file : files)
{
@@ -175,6 +176,12 @@ bool WebPlatformTools::OnPostProcess(CookingData& data)
continue; // Skip the main game module
FileSystem::MoveFile(data.DataOutputPath / StringUtils::GetFileName(file), file, true);
}
for (const String& file : files2)
{
if (StringUtils::GetFileNameWithoutExtension(file) == gameWasm)
continue; // Skip the main game module
FileSystem::MoveFile(data.DataOutputPath / StringUtils::GetFileName(file), file, true);
}
}
// Pack data files into a single file using Emscripten's file_packager tool

View File

@@ -37,8 +37,8 @@ void PrecompileAssembliesStep::OnBuildStarted(CookingData& data)
if (cachedData != aotModeCacheValue)
{
LOG(Info, "AOT cache invalidation");
FileSystem::DeleteDirectory(data.ManagedCodeOutputPath); // Remove AOT cache
FileSystem::DeleteDirectory(data.DataOutputPath / TEXT("Dotnet")); // Remove deployed Dotnet libs (be sure to remove any leftovers from previous build)
//FileSystem::DeleteDirectory(data.ManagedCodeOutputPath); // Remove AOT cache
//FileSystem::DeleteDirectory(data.DataOutputPath / TEXT("Dotnet")); // Remove deployed Dotnet libs (be sure to remove any leftovers from previous build)
}
}
if (!FileSystem::DirectoryExists(data.ManagedCodeOutputPath))

View File

@@ -121,7 +121,7 @@ namespace FlaxEditor.GUI
}
// Select the first platform
_selected = platforms[0].PlatformType;
_selected = PlatformType.Web;
((Image)Children[0]).Color = _selectedColor;
((Image)Children[0]).MouseOverColor = _selectedColor;
}

View File

@@ -83,7 +83,7 @@ namespace FlaxEditor.Windows
public bool ShowOutput = true;
[EditorOrder(20), Tooltip("Configuration build mode")]
public BuildConfiguration ConfigurationMode = BuildConfiguration.Development;
public BuildConfiguration ConfigurationMode = BuildConfiguration.Debug;
[EditorOrder(90), Tooltip("The list of custom defines passed to the build tool when compiling project scripts. Can be used in build scripts for configuration (Configuration.CustomDefines).")]
public string[] CustomDefines;

View File

@@ -97,7 +97,8 @@ public class Graphics : EngineModule
Log.WarningOnce(string.Format("Building for {0} without Vulkan rendering backend (Vulkan SDK is missing)", options.Platform.Target), ref _logMissingVulkanSDK);
break;
case TargetPlatform.Web:
options.PrivateDependencies.Add("GraphicsDeviceWebGPU");
options.PrivateDependencies.Add("GraphicsDeviceNull");
//options.PrivateDependencies.Add("GraphicsDeviceWebGPU");
break;
default: throw new InvalidPlatformException(options.Platform.Target);
}

View File

@@ -18,7 +18,7 @@ public class GraphicsDeviceWebGPU : GraphicsDeviceBaseModule
options.CompileEnv.CustomArgs.Add(port);
options.LinkEnv.CustomArgs.Add("-sASYNCIFY");
options.OutputFiles.Add(port);
options.PublicDefinitions.Add("GRAPHICS_API_WEBGPU");
//options.PublicDefinitions.Add("GRAPHICS_API_WEBGPU");
options.PrivateIncludePaths.Add(Path.Combine(EmscriptenSdk.Instance.EmscriptenPath, "emscripten/cache/ports/emdawnwebgpu/emdawnwebgpu_pkg/webgpu/include"));
options.PrivateDependencies.Add("lz4");
}

View File

@@ -6,6 +6,7 @@ namespace FlaxEngine
{
partial class CollisionData
{
#if false
/// <summary>
/// Cooks the mesh collision data and updates the virtual asset. action cannot be performed on a main thread.
/// [Deprecated on 16.06.2022, expires on 16.06.2024]
@@ -68,5 +69,6 @@ namespace FlaxEngine
for (int i = 0; i < tmp.Length; i++)
vertexBuffer[i] = tmp[i];
}
#endif
}
}

View File

@@ -30,4 +30,8 @@
#define GPU_ENABLE_ASYNC_RESOURCES_CREATION 0
#endif
// Use AOT for Mono
#define USE_MONO_AOT 1
#define USE_MONO_AOT_MODE MONO_AOT_MODE_INTERP
#endif

View File

@@ -1175,7 +1175,7 @@ void ShadowsPass::SetupShadows(RenderContext& renderContext, RenderContextBatch&
LOG(Fatal, "Failed to setup shadow map of size {0}x{1} and format {2}", desc.Width, desc.Height, ScriptingEnum::ToString(desc.Format));
return;
}
#if PLATFORM_WEB
#if PLATFORM_WEB && GRAPHICS_API_WEBGPU
// Hack to fix WebGPU limitation that requires to specify different sampler type manually to sample depth texture
void SetWebGPUTextureViewSampler(GPUTextureView * view, uint32 samplerType);
SetWebGPUTextureViewSampler(shadows.ShadowMapAtlas->View(), 0x00000004); // WGPUTextureSampleType_Depth

View File

@@ -51,7 +51,7 @@
#include <mono/metadata/mono-gc.h>
#include <mono/metadata/mono-private-unstable.h>
typedef char char_t;
#define DOTNET_HOST_MONO_DEBUG 0
#define DOTNET_HOST_MONO_DEBUG 1
#ifdef USE_MONO_AOT_MODULE
void* MonoAotModuleHandle = nullptr;
#endif
@@ -270,6 +270,36 @@ struct NativePropertyDefinitions
MMethodAttributes setterAttributes;
};
#if PLATFORM_WEB
extern "C"
{
DLLEXPORT void schedule_background_exec()
{
}
DLLEXPORT int mono_wasm_process_current_pid()
{
return -1;
}
DLLEXPORT void mono_wasm_schedule_timer(int shortestDueTimeMs)
{
}
DLLEXPORT int mono_jiterp_parse_option(const char* option)
{
return -1;
}
DLLEXPORT int mono_wasm_browser_entropy(uint8_t* buffer, int32_t bufferLength)
{
return 0;
}
}
#endif
MDomain* MCore::CreateDomain(const StringAnsi& domainName)
{
return nullptr;
@@ -2031,10 +2061,10 @@ static MonoAssembly* OnMonoAssemblyLoad(const char* aname)
String fileName = name;
if (!name.EndsWith(TEXT(".dll")) && !name.EndsWith(TEXT(".exe")))
fileName += TEXT(".dll");
String path = Globals::ProjectFolder / String(TEXT("/Dotnet/")) / fileName;
String path = Globals::ProjectFolder / String(TEXT("Dotnet/")) / fileName;
if (!FileSystem::FileExists(path))
{
path = Globals::ProjectFolder / String(TEXT("/Dotnet/shared/Microsoft.NETCore.App/")) / fileName;
path = Globals::ProjectFolder / String(TEXT("Dotnet/shared/Microsoft.NETCore.App/")) / fileName;
if (!FileSystem::FileExists(path))
{
path = fileName;
@@ -2045,6 +2075,11 @@ static MonoAssembly* OnMonoAssemblyLoad(const char* aname)
#if DOTNET_HOST_MONO_DEBUG
LOG(Info, "Loading C# assembly from path = {0}, exist = {1}", path, FileSystem::FileExists(path));
#endif
String testPath = path;
testPath += TEXT(".so");
LOG(Info, "{0} exist = {1}", testPath, FileSystem::FileExists(testPath));
//Platform::LoadLibrary(testPath.Get());
MonoAssembly* assembly = nullptr;
if (FileSystem::FileExists(path))
{
@@ -2205,7 +2240,7 @@ bool InitHostfxr()
#if defined(USE_MONO_AOT_MODE)
// Enable AOT mode (per-platform)
mono_jit_set_aot_mode(USE_MONO_AOT_MODE);
mono_jit_set_aot_mode(MONO_AOT_MODE_FULL);
#endif
// Platform-specific setup

View File

@@ -176,16 +176,19 @@ void onEngineUnloading(MAssembly* assembly);
bool ScriptingService::Init()
{
LOG(Info, "ScriptingService::Init");
PROFILE_MEM(Scripting);
Stopwatch stopwatch;
// Initialize managed runtime
LOG(Info, "ScriptingService::Init MCore::LoadEngine");
if (MCore::LoadEngine())
{
LOG(Fatal, "C# runtime initialization failed.");
return true;
}
LOG(Info, "ScriptingService::Init MCore::CreateScriptingAssemblyLoadContext");
MCore::CreateScriptingAssemblyLoadContext();
// Cache root domain

View File

@@ -46,8 +46,9 @@ public class SDL : EngineDepsModule
options.OutputFiles.Add(Path.Combine(depsRoot, "libSDL3.a"));
break;
case TargetPlatform.Web:
options.OutputFiles.Add("--use-port=sdl3");
return;
//options.OutputFiles.Add("--use-port=sdl3");
options.OutputFiles.Add(Path.Combine(depsRoot, "libSDL3.a"));
break;
default: throw new InvalidPlatformException(options.Platform.Target);
}

View File

@@ -104,6 +104,47 @@ public class nethost : ThirdPartyModule
options.PublicDefinitions.Add("USE_MONO_DYNAMIC_LIB");
options.DependencyFiles.Add(Path.Combine(hostRuntime.Path, "libmonosgen-2.0.dylib"));
options.Libraries.Add(Path.Combine(hostRuntime.Path, "libmonosgen-2.0.dylib"));
break;
case TargetPlatform.Web:
//options.PublicDefinitions.Add("USE_MONO_DYNAMIC_LIB");
options.DependencyFiles.Add(Path.Combine(hostRuntime.Path, "libmonosgen-2.0.a"));
options.Libraries.Add(Path.Combine(hostRuntime.Path, "libmonosgen-2.0.a"));
foreach (var lib in new string[] {
//"libz.a",
"libSystem.Native.a",
//"libSystem.Globalization.Native.a",
//"libSystem.IO.Compression.Native.a",
"libmono-component-debugger-stub-static.a",//"libmono-component-debugger-static.a",
"libmono-component-hot_reload-stub-static.a",//"libmono-component-hot_reload-static.a",
"libmono-component-marshal-ilgen-stub-static.a",//"libmono-component-marshal-ilgen-static.a",
"libmono-component-diagnostics_tracing-stub-static.a",
//"libmono-profiler-browser.a",
"libmono-wasm-eh-js.a",
//"libicuuc.a",
//"libmono-icall-table.a",
/*"libmono-wasm-nosimd.a",
"libSystem.Native.a",
"libSystem.IO.Compression.Native.a",
//"libmono-wasm-eh-wasm.a",
//"libmono-ee-interp.a",
"libicuuc.a",
"libicui18n.a",
"libicudata.a",
"libbrotlienc.a",
"libbrotlidec.a",
"libbrotlicommon.a",*/
//"dotnet.native.wasm",
})
{
options.DependencyFiles.Add(Path.Combine(hostRuntime.Path, lib));
options.Libraries.Add(Path.Combine(hostRuntime.Path, lib));
}
//options.DependencyFiles.Add(Path.Combine(hostRuntime.Path, "dotnet.native.wasm"));
//options.LinkEnv.InputFiles.Add(Path.Combine(hostRuntime.Path, "dotnet.native.wasm"));
//options.SourceFiles.Add(Path.Combine(hostRuntime.Path, "dotnet.native.js"));
break;
default: throw new InvalidPlatformException(options.Platform.Target);
}

View File

@@ -2,6 +2,7 @@
using Mono.Cecil;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
@@ -24,6 +25,11 @@ namespace Flax.Build
/// </summary>
ILC,
/// <summary>
///
/// </summary>
WebCIL,
/// <summary>
/// Use Mono AOT to cross-compile all used C# assemblies into native platform shared libraries.
/// </summary>
@@ -318,6 +324,8 @@ namespace Flax.Build
var dotnetLibPath = Utilities.NormalizePath(Path.GetDirectoryName(coreLibPaths[0]));
Log.Info("Class library found in: " + dotnetLibPath);
bool wasm = true;
// Build list of assemblies to process (use game assemblies as root to walk over used references from stdlib)
var assembliesPaths = new List<string>();
if (Configuration.SkipUnusedDotnetLibsPackaging)
@@ -354,6 +362,16 @@ namespace Flax.Build
assembliesPaths.AddRange(stdLibFiles);
assembliesPaths.AddRange(inputFiles);
}
if (!wasm)
{
List<string> assembliesPaths2 = new();
foreach (var assemblyPath in assembliesPaths)
{
if (!assemblyPath.StartsWith(dotnetLibPath))
assembliesPaths2.Add(assemblyPath);
}
assembliesPaths = assembliesPaths2;
}
// Run compilation
bool failed = false, validCache = true, coreLibDirty = false;
@@ -370,6 +388,7 @@ namespace Flax.Build
buildToolchain.CompileCSharp(ref options);
platformToolsPath = options.PlatformToolsPath;
}
//if (!wasm)
{
// Check if core library has been modified
var options = new Toolchain.CSharpOptions
@@ -385,12 +404,39 @@ namespace Flax.Build
};
buildToolchain.CompileCSharp(ref options);
coreLibDirty = File.GetLastWriteTime(options.InputFiles[0]) > File.GetLastWriteTime(options.OutputFiles[0]);
Log.Info("corelibdirty:" + coreLibDirty + ", " + options.OutputFiles[0]);
}
if (!Directory.Exists(platformToolsPath))
throw new Exception("Missing platform tools " + platformToolsPath);
Log.Info("Platform tools found in: " + platformToolsPath);
// Workaround for unknown command "rm" when running on Windows
Utilities.WriteFileIfChanged(Path.Combine(aotAssembliesPath, "rm.bat"),
"""
@echo off
setlocal enabledelayedexpansion
set command=%*
set command=!command:~3!
del /F /Q /S %command%
rmdir /S /Q %command%
"""
);
/*Utilities.WriteFileIfChanged(Path.Combine(Path.GetDirectoryName(inputFiles[0]), "rm.bat"),
"""
@echo off
setlocal enabledelayedexpansion
set command=%*
set command=!command:~3!
del /F /Q /S %command%
rmdir /S /Q %command%
"""
);*/
ConcurrentBag<string> outputStaticLibraryFiles = new ();
var compileAssembly = (string assemblyPath) =>
{
//if (!assemblyPath.Contains("Newtonsoft"))
// return;
// Determinate whether use debug information for that assembly
var useDebug = configuration != TargetConfiguration.Release;
if (!dotnetAotDebug && !inputFiles.Contains(assemblyPath))
@@ -411,6 +457,13 @@ namespace Flax.Build
EnableDebugSymbols = useDebug,
EnableToolDebug = dotnetAotDebug,
};
if (true)
{
//if (Path.GetFileNameWithoutExtension(assemblyPath) == "System.Private.CoreLib")
}
buildToolchain.CompileCSharp(ref options);
// Skip if output is already generated and is newer than a source assembly
@@ -452,8 +505,9 @@ namespace Flax.Build
// Copy AOT build products
foreach (var outputFile in options.OutputFiles)
{
outputStaticLibraryFiles.Add(outputFile);
// Skip if deployed file is already valid
var deployedFilePath = Path.Combine(dotnetOutputPath, Path.GetFileName(outputFile));
/*var deployedFilePath = Path.Combine(dotnetOutputPath, Path.GetFileName(outputFile));
if (!File.Exists(deployedFilePath) || File.GetLastWriteTime(outputFile) > File.GetLastWriteTime(deployedFilePath))
{
// Copy to the destination folder
@@ -461,7 +515,7 @@ namespace Flax.Build
if (useDebug && File.Exists(outputFile + ".pdb"))
Utilities.FileCopy(outputFile + ".pdb", Path.Combine(dotnetOutputPath, Path.GetFileName(outputFile + ".pdb")));
validCache = false;
}
}*/
}
}
else
@@ -470,6 +524,8 @@ namespace Flax.Build
Utilities.FileCopy(assemblyPath, Path.Combine(dotnetOutputPath, assemblyFileName), Utilities.CopyMode.OverrideIfNewer);
}
};
Log.Info("AOT: " + string.Join(", ", assembliesPaths));
Log.Info("");
if (Configuration.MaxConcurrency > 1 && Configuration.ConcurrencyProcessorScale > 0.0f && !dotnetAotDebug)
{
// Multi-threaded
@@ -479,7 +535,14 @@ namespace Flax.Build
{
// Single-threaded
foreach (var assemblyPath in assembliesPaths)
{
Log.Info("");
Log.Warning(" " + assemblyPath);
Log.Info("");
compileAssembly(assemblyPath);
if (failed)
break;
}
}
if (failed)
throw new Exception($"Failed to run AOT. See log ({Configuration.LogFile}).");
@@ -504,6 +567,65 @@ namespace Flax.Build
throw new Exception("Mono AOT failed to link static libraries into a shared module");
}
}
else if (Configuration.AOTMode == DotNetAOTModes.MonoAOTDynamic)
{
var linkAssembly = (string outputFile) =>
{
Log.Info("");
Log.Warning(" " + Path.GetFileNameWithoutExtension(outputFile));
Log.Info("");
// Get output file path for this assembly (platform can use custom extension)
var options = new Toolchain.CSharpOptions
{
Action = Toolchain.CSharpOptions.ActionTypes.MonoLink,
InputFiles = [Path.Combine(Path.GetDirectoryName(outputFile), Path.GetFileNameWithoutExtension(outputFile))],
OutputFiles = [outputFile],
AssembliesPath = aotAssembliesPath,
//ClassLibraryPath = dotnetLibPath,
PlatformToolsPath = Path.GetDirectoryName(outputFile),
//EnableDebugSymbols = useDebug,
//EnableToolDebug = dotnetAotDebug,
};
if (!File.Exists(outputFile) && buildToolchain.CompileCSharp(ref options))
{
Log.Error("Failed to run linker on assembly " + outputFile);
failed = true;
return;
}
// Skip if deployed file is already valid
var deployedFilePath = Path.Combine(dotnetOutputPath, Path.GetFileName(outputFile));
if (!File.Exists(deployedFilePath) || File.GetLastWriteTime(outputFile) > File.GetLastWriteTime(deployedFilePath))
{
// Copy to the destination folder
Utilities.FileCopy(outputFile, deployedFilePath);
if (/*useDebug &&*/ File.Exists(outputFile + ".pdb"))
Utilities.FileCopy(outputFile + ".pdb", Path.Combine(dotnetOutputPath, Path.GetFileName(outputFile + ".pdb")));
validCache = false;
}
};
// Run final linking with single-threaded as this is very memory intensive process
if (Configuration.MaxConcurrency > 1 && Configuration.ConcurrencyProcessorScale > 0.0f && !dotnetAotDebug)
{
// Multi-threaded
var outputFiles = outputStaticLibraryFiles.OrderBy(x => x.Contains("System.Private.CoreLib") || x.Contains("System.Linq") || x.Contains("System.Private.Xml") ? 0 : 1).ToArray(); // Process the file which takes longest time first
System.Threading.Tasks.Parallel.ForEach(outputFiles/*, new System.Threading.Tasks.ParallelOptions() { MaxDegreeOfParallelism = Math.Min(8, Configuration.MaxConcurrency) }*/, linkAssembly);
}
else
{
foreach (var outputFile in outputStaticLibraryFiles)
{
linkAssembly(outputFile);
if (failed)
break;
}
}
if (failed)
throw new Exception($"Failed to run AOT. See log ({Configuration.LogFile}).");
}
}
else
{

View File

@@ -148,6 +148,7 @@ namespace Flax.Build
TargetPlatform.Windows,
TargetPlatform.Linux,
TargetPlatform.Mac,
TargetPlatform.Web,
};
}
}
@@ -356,6 +357,9 @@ namespace Flax.Build
TryAddHostRuntime(TargetPlatform.Android, TargetArchitecture.ARM64, "android-arm64", "Runtime.Mono");
TryAddHostRuntime(TargetPlatform.iOS, TargetArchitecture.ARM, "ios-arm64", "Runtime.Mono");
TryAddHostRuntime(TargetPlatform.iOS, TargetArchitecture.ARM64, "ios-arm64", "Runtime.Mono");
TryAddHostRuntime(TargetPlatform.Web, TargetArchitecture.x86, "browser-wasm", "Runtime.Mono");
//TryAddHostRuntime(TargetPlatform.Web, TargetArchitecture.x86, "browser-wasm", "Runtime.Mono.multithread");
//TryAddHostRuntime(TargetPlatform.Web, TargetArchitecture.x86, "browser-wasm", "Runtime.AOT.win-x64.Cross");
// Found
IsValid = true;
@@ -410,10 +414,16 @@ namespace Flax.Build
case TargetPlatform.iOS:
result = "ios";
break;
case TargetPlatform.Web:
result = "browser";
break;
default: throw new InvalidPlatformException(platform);
}
switch (architecture)
{
case TargetArchitecture.x86 when platform == TargetPlatform.Web:
result += "-wasm";
break;
case TargetArchitecture.x86:
result += "-x86";
break;

View File

@@ -276,6 +276,7 @@ namespace Flax.Build
case TargetPlatform.Linux:
case TargetPlatform.iOS:
case TargetPlatform.Mac:
case TargetPlatform.Web:
// TODO: try to disable this on more platforms! (eg. LocalizationService::OnLocalizationChanged uses it)
options.CompileEnv.EnableExceptions = true;
break;

View File

@@ -296,8 +296,8 @@ namespace Flax.Build
public static bool WithCSharp(NativeCpp.BuildOptions options)
{
if (options.Platform.Target == TargetPlatform.Web)
return false; // TODO: implement .NET for WebAssembly
//if (options.Platform.Target == TargetPlatform.Web)
// return false; // TODO: implement .NET for WebAssembly
return UseCSharp || options.Target.IsEditor;
}

View File

@@ -24,6 +24,7 @@ namespace Flax.Deps.Dependencies
return new[]
{
TargetPlatform.Windows,
TargetPlatform.Web,
};
case TargetPlatform.Linux:
return new[]
@@ -122,6 +123,7 @@ namespace Flax.Deps.Dependencies
}
case TargetPlatform.Linux:
case TargetPlatform.Mac:
case TargetPlatform.Web:
{
var buildDir = Path.Combine(root, "build-" + architecture.ToString());

View File

@@ -114,6 +114,7 @@ namespace Flax.Deps.Dependencies
case TargetPlatform.PS4:
case TargetPlatform.PS5:
case TargetPlatform.Switch:
case TargetPlatform.Web:
runtimeFlavor = "Mono";
setupVersion = true;
buildMonoAotCross = true;
@@ -128,6 +129,9 @@ namespace Flax.Deps.Dependencies
case TargetPlatform.Switch:
os = "switch";
break;
case TargetPlatform.Web:
os = "browser";
break;
default: throw new InvalidPlatformException(targetPlatform);
}
buildArgs = $" /p:RuntimeOS={os} -subset mono+libs -cmakeargs \"-DDISABLE_JIT=1-DENABLE_PERFTRACING=0-DDISABLE_REFLECTION_EMIT=1-DDISABLE_EVENTPIPE=1-DDISABLE_COM=1-DDISABLE_PROFILER=1-DDISABLE_COMPONENTS=1\" /p:FeaturePerfTracing=false /p:FeatureManagedEtw=false /p:FeatureManagedEtwChannels=false /p:FeatureEtw=false /p:ApiCompatValidateAssemblies=false";

View File

@@ -131,6 +131,7 @@ namespace Flax.Deps
TargetArchitecture.x86,
TargetArchitecture.x64,
TargetArchitecture.ARM64,
TargetArchitecture.x86,
};
case TargetPlatform.Linux:
return new[]
@@ -487,6 +488,7 @@ namespace Flax.Deps
public static void RunCmake(string path, TargetPlatform platform, TargetArchitecture architecture, string customArgs = null, Dictionary<string, string> envVars = null)
{
string cmdLine;
string cmakeBinary = "cmake";
switch (platform)
{
case TargetPlatform.Windows:
@@ -567,6 +569,8 @@ namespace Flax.Deps
cmdLine = $"CMakeLists.txt -DCMAKE_TOOLCHAIN_FILE=\"{EmscriptenSdk.Instance.CMakeToolchainPath}\"";
if (BuildPlatform == TargetPlatform.Windows)
cmdLine += " -G \"Ninja\"";
//cmakeBinary = "emcmake.bat";
//cmdLine = "cmake CMakeLists.txt";
break;
}
default: throw new InvalidPlatformException(platform);
@@ -575,7 +579,7 @@ namespace Flax.Deps
if (customArgs != null)
cmdLine += " " + customArgs;
Utilities.Run("cmake", cmdLine, null, path, Utilities.RunOptions.DefaultTool, envVars);
Utilities.Run(cmakeBinary, cmdLine, null, path, Utilities.RunOptions.DefaultTool, envVars);
}
/// <summary>

View File

@@ -1,6 +1,7 @@
// Copyright (c) Wojciech Figat. All rights reserved.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
@@ -43,7 +44,7 @@ namespace Flax.Deps
if (Configuration.BuildArchitectures != null && Configuration.BuildArchitectures.Length != 0)
architectures = Configuration.BuildArchitectures;
architectures = architectures.Where(x => platforms.Any(y => Platform.GetPlatform(y, true)?.CanBuildArchitecture(x) ?? false)).ToArray();
Log.Verbose($"Building deps for platforms {string.Join(',', platforms)}, {string.Join(',', architectures)}:");
Log.Verbose($"Building deps for platforms [{string.Join(", ", platforms)}] with architectures [{string.Join(", ", architectures)}]:");
foreach (var platform in platforms)
{
foreach (var architecture in architectures)
@@ -59,49 +60,63 @@ namespace Flax.Deps
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!");
List<Dependency> failedDependencies = new();
for (var i = 0; i < dependencies.Length; i++)
{
var dependency = dependencies[i];
var name = dependency.GetType().Name;
if (depsToBuild.Length > 0 || !dependency.BuildByDefault)
try
{
if (!depsToBuild.Contains(name.ToLower()))
var name = dependency.GetType().Name;
if (depsToBuild.Length > 0 || !dependency.BuildByDefault)
{
if (!depsToBuild.Contains(name.ToLower()))
{
Log.Info(string.Format("Skipping {0} ({1}/{2})", name, i + 1, dependencies.Length));
Log.Verbose("Not selected for build.");
continue;
}
}
options.Platforms = platforms.Intersect(dependency.Platforms).ToArray();
if (options.Platforms.Length == 0)
{
Log.Info(string.Format("Skipping {0} ({1}/{2})", name, i + 1, dependencies.Length));
Log.Verbose("Not selected for build.");
Log.Verbose("Not used on any of the build platforms.");
continue;
}
}
options.Platforms = platforms.Intersect(dependency.Platforms).ToArray();
if (options.Platforms.Length == 0)
options.Architectures = architectures.Intersect(dependency.Architectures).ToArray();
if (options.Architectures.Length == 0)
{
Log.Info(string.Format("Skipping {0} ({1}/{2})", name, i + 1, dependencies.Length));
Log.Verbose("Architecture not used on any of the build platforms.");
continue;
}
Log.Info(string.Format("Building {0} ({1}/{2})", name, i + 1, dependencies.Length));
options.IntermediateFolder = Path.Combine(Environment.CurrentDirectory, "Cache", "Intermediate", "Deps", name).Replace('\\', '/');
if (!Configuration.ReBuildDeps && Directory.Exists(options.IntermediateFolder))
{
Log.Verbose(string.Format("{0} is up-to-date. Skipping build.", name));
continue;
}
var forceEmpty = false; //Configuration.ReBuildDeps;
Dependency.SetupDirectory(options.IntermediateFolder, forceEmpty);
dependency.Build(options);
}
catch (Exception e)
{
Log.Info(string.Format("Skipping {0} ({1}/{2})", name, i + 1, dependencies.Length));
Log.Verbose("Not used on any of the build platforms.");
continue;
failedDependencies.Add(dependency);
Log.Error(e.ToString());
}
}
options.Architectures = architectures.Intersect(dependency.Architectures).ToArray();
if (options.Architectures.Length == 0)
{
Log.Info(string.Format("Skipping {0} ({1}/{2})", name, i + 1, dependencies.Length));
Log.Verbose("Architecture not used on any of the build platforms.");
continue;
}
Log.Info(string.Format("Building {0} ({1}/{2})", name, i + 1, dependencies.Length));
options.IntermediateFolder = Path.Combine(Environment.CurrentDirectory, "Cache", "Intermediate", "Deps", name).Replace('\\', '/');
if (!Configuration.ReBuildDeps && Directory.Exists(options.IntermediateFolder))
{
Log.Verbose(string.Format("{0} is up-to-date. Skipping build.", name));
continue;
}
var forceEmpty = false; //Configuration.ReBuildDeps;
Dependency.SetupDirectory(options.IntermediateFolder, forceEmpty);
dependency.Build(options);
if (failedDependencies.Count > 0)
{
Log.Error("Failed to build the following dependencies: " + string.Join(", ", failedDependencies.Select(x => x.GetType().Name)));
}
Log.Info("Done!");

View File

@@ -70,7 +70,7 @@ namespace Flax.Build.Platforms
versionStr = versionStr.Substring(1, versionStr.Length - 2);
Version = new Version(versionStr);
var minVersion = new Version(4, 0);
var minVersion = new Version(3, 1);
if (Version < minVersion)
{
Log.Error(string.Format("Unsupported Emscripten SDK version {0}. Minimum supported is {1}.", Version, minVersion));

View File

@@ -32,7 +32,7 @@ namespace Flax.Build.Platforms
public override string ExecutableFileExtension => ".html";
/// <inheritdoc />
public override string SharedLibraryFileExtension => ".wasm";
public override string SharedLibraryFileExtension => ".so";//".wasm";
/// <inheritdoc />
public override string StaticLibraryFileExtension => ".a";

View File

@@ -100,6 +100,8 @@ namespace Flax.Build.Platforms
private void AddSharedArgs(List<string> args, BuildOptions options, bool debugInformation, bool optimization)
{
//args.Add("-pthread");
if (debugInformation)
args.Add("-g2");
else
@@ -153,7 +155,7 @@ namespace Flax.Build.Platforms
if (options.LinkEnv.LinkTimeCodeGeneration)
args.Add("-flto");
if (options.LinkEnv.Output == LinkerOutput.SharedLibrary)
//if (options.LinkEnv.Output == LinkerOutput.SharedLibrary)
args.Add("-fPIC");
var sanitizers = options.CompileEnv.Sanitizers;
@@ -179,8 +181,8 @@ namespace Flax.Build.Platforms
AddSharedArgs(commonArgs, options, options.CompileEnv.DebugInformation, options.CompileEnv.Optimization);
// Hack to pull SDL3 port via emcc
if (options.CompileEnv.PreprocessorDefinitions.Contains("PLATFORM_SDL"))
commonArgs.Add("--use-port=sdl3");
//if (options.CompileEnv.PreprocessorDefinitions.Contains("PLATFORM_SDL"))
// commonArgs.Add("--use-port=sdl3");
}
// Add preprocessor definitions
@@ -260,6 +262,322 @@ namespace Flax.Build.Platforms
return output;
}
public override bool CompileCSharp(ref CSharpOptions options)
{
switch (options.Action)
{
/*case CSharpOptions.ActionTypes.GetOutputFiles:
{
foreach (var inputFile in options.InputFiles)
{
string assemblyPath;
if (Configuration.AOTMode == DotNetAOTModes.MonoAOTDynamic)
assemblyPath = inputFile + Platform.SharedLibraryFileExtension;
else
assemblyPath = Path.Combine(Path.GetDirectoryName(inputFile), Platform.StaticLibraryFilePrefix + Path.GetFileName(inputFile) + Platform.StaticLibraryFileExtension);
if (Path.GetFileNameWithoutExtension(inputFile) == "System.Private.CoreLib")
{
// Use pre-compiled binaries
DotNetSdk.Instance.GetHostRuntime(TargetPlatform.Web, TargetArchitecture.x86, out var hostRuntime);
assemblyPath = Path.Combine(hostRuntime.Path, "libmonosgen-2.0.a");
}
options.OutputFiles.Add(assemblyPath);
}
return false;
}*/
case CSharpOptions.ActionTypes.GetPlatformTools:
{
string arch = Platform.BuildTargetArchitecture switch
{
TargetArchitecture.x64 => "x64",
TargetArchitecture.ARM64 => "arm64",
_ => throw new PlatformNotSupportedException(Platform.BuildTargetArchitecture.ToString()),
};
string os;
switch (Platform.BuildPlatform.Target)
{
case TargetPlatform.Windows:
os = "win";
break;
default:
throw new PlatformNotSupportedException(Platform.BuildPlatform.Target.ToString());
}
options.PlatformToolsPath = Path.Combine(DotNetSdk.SelectVersionFolder(Path.Combine(DotNetSdk.Instance.RootPath, $"packs/Microsoft.NETCore.App.Runtime.AOT.{os}-{arch}.Cross.browser-wasm")), "tools");
return false;
}
case CSharpOptions.ActionTypes.MonoLink:
{
// Setup arguments
var args = new List<string>();
//args.AddRange(options.LinkEnv.CustomArgs);
{
args.Add(string.Format("-o \"{0}\"", options.OutputFiles[0].Replace('\\', '/')));
//AddSharedArgs(args, options, false, false);
// Setup memory
var initialMemory = Configuration.WebInitialMemory;
//if (options.CompileEnv.Sanitizers.HasFlag(Sanitizer.Address))
// initialMemory = Math.Max(initialMemory, 64); // Address Sanitizer needs more memory
args.Add($"-sINITIAL_MEMORY={initialMemory}MB");
args.Add("-sSTACK_SIZE=4MB");
args.Add("-sALLOW_MEMORY_GROWTH=1");
// Setup file access (Game Cooker packs files with file_packager tool)
args.Add("-sFORCE_FILESYSTEM");
//args.Add("-sLZ4");
// https://emscripten.org/docs/compiling/Dynamic-Linking.html#dynamic-linking
// TODO: use -sMAIN_MODULE=2 and -sSIDE_MODULE=2 to strip unused code (mark public APIs with EMSCRIPTEN_KEEPALIVE)
/*if (options.LinkEnv.Output == LinkerOutput.Executable)
{
args.Add("-sMAIN_MODULE");
args.Add("-sEXPORT_ALL");
}
else*/
{
args.Add("-sSIDE_MODULE");
}
}
args.Add("-O0");
args.Add("--verbose");
//args.Add("--trace");
args.Add("-Wl,--allow-multiple-definition"); // Multiple pthread-related definitions in dotnet runtime
args.Add("-Wl,--start-group");
// Input libraries
var libraryPaths = new HashSet<string>();
var dynamicLibExt = Platform.SharedLibraryFileExtension;
var executableExt = Platform.ExecutableFileExtension;
foreach (var library in options.InputFiles)
{
/*var dir = Path.GetDirectoryName(library);
var ext = Path.GetExtension(library);
if (library.StartsWith("--use-port="))
{
// Ports (https://emscripten.org/docs/compiling/Building-Projects.html#emscripten-ports)
args.Add(library);
}
else if (string.IsNullOrEmpty(dir))
{
args.Add(string.Format("\"-l{0}\"", library));
}
else if (ext == executableExt)
{
// Skip executable
}
else if (ext == dynamicLibExt)
{
// Link against dynamic library
//task.PrerequisiteFiles.Add(library);
libraryPaths.Add(dir);
args.Add(string.Format("\"-l{0}\"", GetLibName(library)));
}
else
{
//task.PrerequisiteFiles.Add(library);
args.Add(string.Format("\"-l{0}\"", GetLibName(library)));
libraryPaths.Add(dir); // FIXME
}*/
/*if (library.Contains("net10.0"))
{
if (!library.Contains("System.Private.CoreLib"))
continue;
}*/
args.Add(library.Replace(Path.DirectorySeparatorChar, '/') + Platform.StaticLibraryFileExtension);
}
// Input files (link static libraries last)
/*task.PrerequisiteFiles.AddRange(options.LinkEnv.InputFiles);
foreach (var file in options.LinkEnv.InputFiles.Where(x => !x.EndsWith(".a")).Concat(options.LinkEnv.InputFiles.Where(x => x.EndsWith(".a"))))
{
args.Add(string.Format("\"{0}\"", file.Replace('\\', '/')));
}
// Additional lib paths
libraryPaths.AddRange(options.LinkEnv.LibraryPaths);
foreach (var path in libraryPaths)
{
args.Add(string.Format("-L\"{0}\"", path.Replace('\\', '/')));
}*/
args.Add("-Wl,--end-group");
// Use a response file (it can contain any commands that you would specify on the command line)
bool useResponseFile = true;
string responseFile = null;
if (useResponseFile)
{
responseFile = Path.Combine(options.AssembliesPath, Path.GetFileName(options.OutputFiles[0]) + ".response");
//task.PrerequisiteFiles.Add(responseFile);
Utilities.WriteFileIfChanged(responseFile, string.Join(Environment.NewLine, args));
args.Clear();
args.Add(string.Format("@\"{0}\"", responseFile));
}
//task.WorkingDirectory = options.WorkingDirectory;
//task.CommandPath = _compilerPath;
//task.CommandArguments = string.Join(" ", args);
//int result = Utilities.Run(NativeCompilerPath, $"{string.Join(" ", args)}", null, Path.GetDirectoryName(options.OutputFiles[0]), Utilities.RunOptions.AppMustExist | Utilities.RunOptions.ConsoleLogOutput);
int result = Utilities.Run(NativeCompilerPath, $"{string.Join(" ", args)}", null, options.PlatformToolsPath, Utilities.RunOptions.AppMustExist | Utilities.RunOptions.ConsoleLogOutput);
if (result != 0)
return true;
return false;
}
case CSharpOptions.ActionTypes.MonoCompile:
{
string binaryExtension = Platform.BuildPlatform.Target == TargetPlatform.Windows ? ".exe" : "";
var aotCompilerPath = Path.Combine(options.PlatformToolsPath, "mono-aot-cross") + binaryExtension;
//var clangPath = Path.Combine(ToolchainPath, "usr/bin/clang");
var inputFile = options.InputFiles[0];
var inputFileAsm = inputFile + ".s";
var inputFileObj = inputFile + ".o";
var outputFileDylib = options.OutputFiles[0];
var inputFileFolder = Path.GetDirectoryName(inputFile);
/*if (Path.GetFileNameWithoutExtension(inputFile) == "System.Private.CoreLib")
{
// Pre-compiled, skip
return false;
}*/
string outputFile;
{
/*if (Configuration.AOTMode == DotNetAOTModes.MonoAOTDynamic)
outputFile = inputFile + Platform.SharedLibraryFileExtension;
else*/
outputFile = Path.Combine(Path.GetDirectoryName(inputFile), Platform.StaticLibraryFilePrefix + Path.GetFileName(inputFile) + Platform.StaticLibraryFileExtension);
}
// Setup options
bool debugSymbols = options.EnableDebugSymbols;
bool useLLVM = true;
//if (useLLVM)
debugSymbols = false;
//var llvmPath = "\"C:\\Program Files\\LLVM\\bin\"";//Path.Combine(EmscriptenSdk.Instance.EmscriptenPath, "bin");
var llvmPath = "\"" + Path.Combine(EmscriptenSdk.Instance.EmscriptenPath, "bin") + "\"";
var monoAotMode = "full";
if (useLLVM)
monoAotMode = "no-opt,llvmonly";//"llvmonly";
//if ("mscorlib.dll" == "")
// monoAotMode = "interp";
var monoDebugMode = debugSymbols ? "soft-debug" : "nodebug";
//var aotCompilerArgs = $"{(useLLVM ? "--llvm" : "")} --aot={monoAotMode},asmonly,verbose,stats,print-skipped,{monoDebugMode}{(useLLVM ? $",llvm-path={llvmPath},llvm-outfile=" + Path.GetFileName(outputFile) : "")} -O=float32";
var aotCompilerArgs = $"{(useLLVM ? "--llvm" : "")} --aot={monoAotMode},asmonly,verbose,stats,print-skipped,{monoDebugMode}{(useLLVM ? $",llvm-path={llvmPath},llvm-outfile=" + Path.GetFileName(outputFile) : "")} -O=float32";
//aotCompilerArgs += " --verbose";
//aotCompilerArgs += $" --aot-path=\"{inputFileFolder}\"";
//var aotCompilerArgs = $"{(useLLVM ? " --llvm" : "")} --aot=static,llvmonly,verbose,stats,print-skipped,llvm-path={llvmPath},{monoDebugMode}{(useLLVM ? ",llvm-outfile=" + Path.GetFileName(outputFile) : "")} -O=float32";
//var aotCompilerArgs = $"{(useLLVM ? " --llvm" : "")} --aot=static,llvmonly,verbose,stats,print-skipped,{monoDebugMode}{(useLLVM ? ",llvm-outfile=" + Path.GetFileName(outputFile) : "")} -O=float32";
//if (debugSymbols || options.EnableToolDebug)
// aotCompilerArgs = "--debug " + aotCompilerArgs;
var envVars = new Dictionary<string, string>();
envVars["MONO_PATH"] = options.ClassLibraryPath.Replace('/', Path.DirectorySeparatorChar) + Path.PathSeparator + options.AssembliesPath.Replace('/', Path.DirectorySeparatorChar);
Log.Info("MONO_PATH: " + envVars["MONO_PATH"]);
if (options.EnableToolDebug)
{
envVars["MONO_LOG_LEVEL"] = "debug";
}
//envVars["MONO_DEBUG"] = "gen-seq-points";
// Run cross-compiler compiler (outputs assembly code)
//int result = 0;
int result = Utilities.Run(aotCompilerPath, $"{aotCompilerArgs} \"{inputFile}\"", null, inputFileFolder, Utilities.RunOptions.AppMustExist | Utilities.RunOptions.ConsoleLogOutput, envVars);
if (result != 0)
return true;
/*CSharpOptions soOptions = new CSharpOptions()
{
Action = CSharpOptions.ActionTypes.MonoLink,
InputFiles = [Path.Combine(Path.GetDirectoryName(inputFile), Platform.StaticLibraryFilePrefix + Path.GetFileName(inputFile))],
OutputFiles = [ inputFile + Platform.SharedLibraryFileExtension ],
AssembliesPath = options.AssembliesPath,
PlatformToolsPath = inputFileFolder,
};
return CompileCSharp(ref soOptions);*/
/*result = Utilities.Run(aotCompilerPath, $"{aotCompilerArgs} \"{inputFile}\"", null, inputFileFolder, Utilities.RunOptions.AppMustExist | Utilities.RunOptions.ConsoleLogOutput, envVars);
if (result != 0)
return true;*/
// Get build args for iOS
/*var clangArgs = new List<string>();
AddArgsCommon(null, clangArgs);
var clangArgsText = string.Join(" ", clangArgs);
// Build object file
result = Utilities.Run(clangPath, $"\"{inputFileAsm}\" -c -o \"{inputFileObj}\" " + clangArgsText, null, inputFileFolder, Utilities.RunOptions.AppMustExist | Utilities.RunOptions.ConsoleLogOutput, envVars);
if (result != 0)
return true;
// Build dylib file
result = Utilities.Run(clangPath, $"\"{inputFileObj}\" -dynamiclib -fPIC -o \"{outputFileDylib}\" " + clangArgsText, null, inputFileFolder, Utilities.RunOptions.AppMustExist | Utilities.RunOptions.ConsoleLogOutput, envVars);
if (result != 0)
return true;
// Clean intermediate results
File.Delete(inputFileAsm);
File.Delete(inputFileObj);
// Fix rpath id
result = Utilities.Run("install_name_tool", $"-id \"@rpath/{Path.GetFileName(outputFileDylib)}\" \"{outputFileDylib}\"", null, inputFileFolder, Utilities.RunOptions.ConsoleLogOutput, envVars);
if (result != 0)
return true;*/
return false;
}
}
return base.CompileCSharp(ref options);
}
#if false
/// <inheritdoc />
public override bool CompileCSharp(ref CSharpOptions options)
{
switch (options.Action)
{
case CSharpOptions.ActionTypes.MonoCompile:
{
var aotCompilerPath = Path.Combine(options.PlatformToolsPath, "mono-aot-cross.exe");
// Setup options
var monoAotMode = "full";
var monoDebugMode = options.EnableDebugSymbols ? "soft-debug" : "nodebug";
var aotCompilerArgs = $"--aot={monoAotMode},verbose,stats,print-skipped,{monoDebugMode} -O=all";
if (options.EnableDebugSymbols || options.EnableToolDebug)
aotCompilerArgs = "--debug " + aotCompilerArgs;
var envVars = new Dictionary<string, string>();
envVars["MONO_PATH"] = options.AssembliesPath + ";" + options.ClassLibraryPath;
if (options.EnableToolDebug)
{
envVars["MONO_LOG_LEVEL"] = "debug";
}
// Run cross-compiler compiler
int result = Utilities.Run(aotCompilerPath, $"{aotCompilerArgs} \"{options.InputFiles[0]}\"", null, ""/*options.PlatformToolsPath*/, Utilities.RunOptions.AppMustExist | Utilities.RunOptions.ConsoleLogOutput, envVars);
return result != 0;
}
}
return base.CompileCSharp(ref options);
}
#endif
private Task CreateBinary(TaskGraph graph, BuildOptions options, string outputFilePath)
{
var task = graph.Add<LinkTask>();
@@ -297,6 +615,8 @@ namespace Flax.Build.Platforms
}
}
args.Add("-Wl,--allow-multiple-definition"); // Multiple pthread-related definitions in dotnet runtime
args.Add("-Wl,--start-group");
// Input libraries
@@ -331,6 +651,7 @@ namespace Flax.Build.Platforms
{
task.PrerequisiteFiles.Add(library);
args.Add(string.Format("\"-l{0}\"", GetLibName(library)));
libraryPaths.Add(dir); // FIXME
}
}

View File

@@ -76,8 +76,8 @@ namespace Flax.Build.Platforms
public override bool CanBuildArchitecture(TargetArchitecture targetArchitecture)
{
// Prevent generating configuration data for Windows x86 (deprecated)
if (targetArchitecture == TargetArchitecture.x86)
return false;
//if (targetArchitecture == TargetArchitecture.x86)
// return false;
// Check if we have a compiler for this architecture
var toolsets = GetToolsets();

View File

@@ -500,6 +500,9 @@ namespace Flax.Build.Platforms
// List Include Files
//commonArgs.Add("/showIncludes");
commonArgs.Add("/permissive-");
commonArgs.Add("/Zc:externC-");
// Code Analysis
commonArgs.Add("/analyze-");

View File

@@ -5,6 +5,7 @@ using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Net;
using System.Reflection;
using System.Threading;
using Flax.Deploy;
using Flax.Deps;
@@ -173,6 +174,10 @@ namespace Flax.Build
}
catch (Exception ex)
{
// Skip the exception thrown from failed method invocation
if (ex is TargetInvocationException && ex.InnerException != null)
ex = ex.InnerException;
// Ignore exception logging for build errors
if (!(ex is BuildException))
Log.Exception(ex);