663 lines
29 KiB
C#
663 lines
29 KiB
C#
// Copyright (c) Wojciech Figat. All rights reserved.
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using Flax.Build.Graph;
|
|
using Flax.Build.NativeCpp;
|
|
|
|
namespace Flax.Build
|
|
{
|
|
partial class Configuration
|
|
{
|
|
/// <summary>
|
|
/// Specifies the initial memory size (in MB) to use by Web app.
|
|
/// </summary>
|
|
[CommandLine("webInitialMemory", "<size_mb>", "Specifies the initial memory size (in MB) to use by Web app.")]
|
|
public static int WebInitialMemory = 32;
|
|
}
|
|
}
|
|
|
|
namespace Flax.Build.Platforms
|
|
{
|
|
/// <summary>
|
|
/// The build toolchain for Web with Emscripten.
|
|
/// </summary>
|
|
/// <seealso cref="Toolchain" />
|
|
public sealed class WebToolchain : Toolchain
|
|
{
|
|
private string _sysrootPath;
|
|
private string _compilerPath;
|
|
private Version _compilerVersion;
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="WebToolchain"/> class.
|
|
/// </summary>
|
|
/// <param name="platform">The platform.</param>
|
|
/// <param name="architecture">The target architecture.</param>
|
|
public WebToolchain(WebPlatform platform, TargetArchitecture architecture)
|
|
: base(platform, architecture)
|
|
{
|
|
var sdkPath = EmscriptenSdk.Instance.EmscriptenPath;
|
|
|
|
// Setup tools
|
|
_compilerPath = Path.Combine(sdkPath, "emscripten", "emcc");
|
|
var clangPath = Path.Combine(sdkPath, "bin", "clang++");
|
|
if (Platform.BuildTargetPlatform == TargetPlatform.Windows)
|
|
{
|
|
_compilerPath += ".bat";
|
|
clangPath += ".exe";
|
|
}
|
|
|
|
// Determinate compiler version
|
|
_compilerVersion = UnixToolchain.GetClangVersion(platform.Target, clangPath);
|
|
|
|
// Setup system paths
|
|
SystemIncludePaths.Add(Path.Combine(sdkPath, "lib", "clang", _compilerVersion.Major.ToString(), "include"));
|
|
SystemIncludePaths.Add(Path.Combine(sdkPath, "emscripten", "system", "include"));
|
|
SystemIncludePaths.Add(Path.Combine(sdkPath, "emscripten", "system", "lib"));
|
|
_sysrootPath = Path.Combine(sdkPath, "emscripten/cache/sysroot/include");
|
|
}
|
|
|
|
public static string GetLibName(string path)
|
|
{
|
|
var libName = Path.GetFileNameWithoutExtension(path);
|
|
if (libName.StartsWith("lib"))
|
|
libName = libName.Substring(3);
|
|
return libName;
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public override string DllExport => "__attribute__((__visibility__(\\\"default\\\")))";
|
|
|
|
/// <inheritdoc />
|
|
public override string DllImport => "";
|
|
|
|
/// <inheritdoc />
|
|
public override TargetCompiler Compiler => TargetCompiler.Clang;
|
|
|
|
/// <inheritdoc />
|
|
public override string NativeCompilerPath => _compilerPath;
|
|
|
|
/// <inheritdoc />
|
|
public override void LogInfo()
|
|
{
|
|
Log.Info("Clang version: " + _compilerVersion);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public override void SetupEnvironment(BuildOptions options)
|
|
{
|
|
base.SetupEnvironment(options);
|
|
|
|
options.CompileEnv.PreprocessorDefinitions.Add("PLATFORM_WEB");
|
|
options.CompileEnv.PreprocessorDefinitions.Add("PLATFORM_UNIX");
|
|
options.CompileEnv.PreprocessorDefinitions.Add("__EMSCRIPTEN__");
|
|
options.CompileEnv.EnableExceptions = false;
|
|
options.CompileEnv.CpuArchitecture = CpuArchitecture.None; // TODO: try SIMD support in Emscripten
|
|
}
|
|
|
|
private void AddSharedArgs(List<string> args, BuildOptions options, bool debugInformation, bool optimization)
|
|
{
|
|
//args.Add("-pthread");
|
|
|
|
if (debugInformation)
|
|
args.Add("-g2");
|
|
else
|
|
args.Add("-g0");
|
|
|
|
if (options.CompileEnv.FavorSizeOrSpeed == FavorSizeOrSpeed.SmallCode)
|
|
args.Add("-Os");
|
|
if (options.CompileEnv.FavorSizeOrSpeed == FavorSizeOrSpeed.FastCode)
|
|
args.Add("-O3");
|
|
else if (optimization && options.Configuration == TargetConfiguration.Release)
|
|
args.Add("-O3");
|
|
else if (optimization)
|
|
args.Add("-O2");
|
|
else
|
|
args.Add("-O0");
|
|
|
|
if (options.CompileEnv.RuntimeTypeInfo)
|
|
args.Add("-frtti");
|
|
else
|
|
args.Add("-fno-rtti");
|
|
|
|
if (options.CompileEnv.TreatWarningsAsErrors)
|
|
args.Add("-Wall -Werror");
|
|
|
|
if (options.CompileEnv.EnableExceptions)
|
|
args.Add("-fexceptions");
|
|
else
|
|
args.Add("-fno-exceptions");
|
|
|
|
if (options.LinkEnv.LinkTimeCodeGeneration)
|
|
args.Add("-flto");
|
|
|
|
//if (options.LinkEnv.Output == LinkerOutput.SharedLibrary)
|
|
args.Add("-fPIC");
|
|
|
|
var sanitizers = options.CompileEnv.Sanitizers;
|
|
if (sanitizers.HasFlag(Sanitizer.Address))
|
|
args.Add("-fsanitize=address");
|
|
if (sanitizers.HasFlag(Sanitizer.Undefined))
|
|
args.Add("-fsanitize=undefined");
|
|
if (sanitizers == Sanitizer.None)
|
|
args.Add("-fsanitize=null -fsanitize-minimal-runtime"); // Minimal Runtime
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public override CompileOutput CompileCppFiles(TaskGraph graph, BuildOptions options, List<string> sourceFiles, string outputPath)
|
|
{
|
|
var output = new CompileOutput();
|
|
|
|
// Setup arguments shared by all source files
|
|
var commonArgs = new List<string>();
|
|
commonArgs.AddRange(options.CompileEnv.CustomArgs);
|
|
{
|
|
commonArgs.Add("-c");
|
|
|
|
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");
|
|
}
|
|
|
|
// Add preprocessor definitions
|
|
foreach (var definition in options.CompileEnv.PreprocessorDefinitions)
|
|
{
|
|
commonArgs.Add(string.Format("-D \"{0}\"", definition));
|
|
}
|
|
|
|
// Add include paths
|
|
foreach (var includePath in options.CompileEnv.IncludePaths)
|
|
{
|
|
if (SystemIncludePaths.Contains(includePath)) // TODO: fix SystemIncludePaths so this chack can be removed
|
|
continue; // Skip system includes as those break compilation (need to fix sys root linking for emscripten)
|
|
commonArgs.Add(string.Format("-I\"{0}\"", includePath.Replace('\\', '/')));
|
|
}
|
|
|
|
// Hack for sysroot includes
|
|
commonArgs.Add(string.Format("-I\"{0}\"", _sysrootPath.Replace('\\', '/')));
|
|
|
|
// Compile all C/C++ files
|
|
var args = new List<string>();
|
|
foreach (var sourceFile in sourceFiles)
|
|
{
|
|
var sourceFilename = Path.GetFileNameWithoutExtension(sourceFile);
|
|
var task = graph.Add<CompileCppTask>();
|
|
|
|
// Use shared arguments
|
|
args.Clear();
|
|
args.AddRange(commonArgs);
|
|
|
|
// Language for the file
|
|
args.Add("-x");
|
|
if (sourceFile.EndsWith(".c", StringComparison.OrdinalIgnoreCase))
|
|
args.Add("c");
|
|
else
|
|
{
|
|
args.Add("c++");
|
|
|
|
// C++ version
|
|
switch (options.CompileEnv.CppVersion)
|
|
{
|
|
case CppVersion.Cpp14:
|
|
args.Add("-std=c++14");
|
|
break;
|
|
case CppVersion.Cpp17:
|
|
case CppVersion.Latest:
|
|
args.Add("-std=c++17");
|
|
break;
|
|
case CppVersion.Cpp20:
|
|
args.Add("-std=c++20");
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Object File Name
|
|
var objFile = Path.Combine(outputPath, sourceFilename + ".o");
|
|
args.Add(string.Format("-o \"{0}\"", objFile.Replace('\\', '/')));
|
|
output.ObjectFiles.Add(objFile);
|
|
task.ProducedFiles.Add(objFile);
|
|
|
|
// Source File Name
|
|
args.Add("\"" + sourceFile.Replace('\\', '/') + "\"");
|
|
|
|
// Request included files to exist
|
|
var includes = IncludesCache.FindAllIncludedFiles(sourceFile);
|
|
task.PrerequisiteFiles.AddRange(includes);
|
|
|
|
// Compile
|
|
task.WorkingDirectory = options.WorkingDirectory;
|
|
task.CommandPath = _compilerPath;
|
|
task.CommandArguments = string.Join(" ", args);
|
|
task.PrerequisiteFiles.Add(sourceFile);
|
|
task.InfoMessage = Path.GetFileName(sourceFile);
|
|
task.Cost = task.PrerequisiteFiles.Count; // TODO: include source file size estimation to improve tasks sorting
|
|
}
|
|
|
|
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("-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
|
|
}*/
|
|
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);
|
|
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 monoAotMode = "full";
|
|
if (useLLVM)
|
|
monoAotMode = "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";
|
|
|
|
//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 = 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>();
|
|
|
|
// Setup arguments
|
|
var args = new List<string>();
|
|
args.AddRange(options.LinkEnv.CustomArgs);
|
|
{
|
|
args.Add(string.Format("-o \"{0}\"", outputFilePath.Replace('\\', '/')));
|
|
|
|
AddSharedArgs(args, options, options.LinkEnv.DebugInformation, options.LinkEnv.Optimization);
|
|
|
|
// 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("-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.LinkEnv.InputLibraries.Concat(options.Libraries))
|
|
{
|
|
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
|
|
}
|
|
}
|
|
|
|
// 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.IntermediateFolder, Path.GetFileName(outputFilePath) + ".response");
|
|
task.PrerequisiteFiles.Add(responseFile);
|
|
Utilities.WriteFileIfChanged(responseFile, string.Join(Environment.NewLine, args));
|
|
}
|
|
|
|
// Link
|
|
task.WorkingDirectory = options.WorkingDirectory;
|
|
task.CommandPath = _compilerPath;
|
|
task.CommandArguments = useResponseFile ? string.Format("@\"{0}\"", responseFile) : string.Join(" ", args);
|
|
task.InfoMessage = "Linking " + outputFilePath;
|
|
task.Cost = task.PrerequisiteFiles.Count;
|
|
task.ProducedFiles.Add(outputFilePath);
|
|
|
|
return task;
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public override void LinkFiles(TaskGraph graph, BuildOptions options, string outputFilePath)
|
|
{
|
|
outputFilePath = Utilities.NormalizePath(outputFilePath);
|
|
|
|
Task linkTask;
|
|
switch (options.LinkEnv.Output)
|
|
{
|
|
case LinkerOutput.Executable:
|
|
case LinkerOutput.SharedLibrary:
|
|
linkTask = CreateBinary(graph, options, outputFilePath);
|
|
break;
|
|
case LinkerOutput.StaticLibrary:
|
|
case LinkerOutput.ImportLibrary:
|
|
default:
|
|
throw new ArgumentOutOfRangeException();
|
|
}
|
|
}
|
|
}
|
|
}
|