From 8855adf28861f8a4d9b06e30149269531f14e0b2 Mon Sep 17 00:00:00 2001 From: Dan Rosser Date: Wed, 14 Oct 2026 23:49:58 +1300 Subject: [PATCH 1/7] Emscripten WebAssembly 32/64 option: -W/--emscripten-wasm, config.make PROJECT_EMSCRIPTEN_WASM, GUI dropdown --- commandLine/src/main.cpp | 16 ++++++- commandLine/src/projects/baseProject.cpp | 53 ++++++++++++++++++++++++ commandLine/src/projects/baseProject.h | 4 ++ frontend/app.js | 11 ++++- frontend/index.html | 13 ++++++ frontend/index.js | 14 ++++++- frontend/locales/de.json | 6 ++- frontend/locales/en.json | 6 ++- frontend/locales/es.json | 6 ++- frontend/locales/fr.json | 6 ++- frontend/locales/ja.json | 6 ++- frontend/locales/ko.json | 6 ++- frontend/locales/pt.json | 6 ++- frontend/locales/ru.json | 6 ++- frontend/locales/zh-CN.json | 6 ++- 15 files changed, 153 insertions(+), 12 deletions(-) diff --git a/commandLine/src/main.cpp b/commandLine/src/main.cpp index 2a6d9862..917a2ea4 100644 --- a/commandLine/src/main.cpp +++ b/commandLine/src/main.cpp @@ -34,7 +34,8 @@ enum optionIndex { UNKNOWN, CLEANNAME_DISABLE, DEFINES, POSTBUILD, - MSYS2ENV + MSYS2ENV, + EMSCRIPTEN_WASM }; constexpr option::Descriptor usage[] = { @@ -64,6 +65,7 @@ constexpr option::Descriptor usage[] = { { DEFINES, 0, "D", "defines", option::Arg::Optional, " --defines, -D \tpreprocessor defines list (such as OF_USE_ANGLE=1,MY_FLAG)" }, { POSTBUILD, 0, "P", "postbuild", option::Arg::Optional, " --postbuild, -P \tscript to run after the project compiles (Xcode run script / VS post-build event)" }, { MSYS2ENV, 0, "k", "msys2env", option::Arg::Optional, " --msys2env, -k \tMSYS2 environment for the msys2 / VSCode targets: ucrt64 (default) | mingw64 | clang64 | clangarm64" }, + { EMSCRIPTEN_WASM, 0, "W", "emscripten-wasm", option::Arg::Optional, " --emscripten-wasm, -W \temscripten WebAssembly: 32 (default) | 64 (wasm64, MEMORY64), written to config.make PROJECT_EMSCRIPTEN_WASM" }, { 0, 0, 0, 0, 0, 0 } }; @@ -93,6 +95,7 @@ vector targets; vector frameworks; vector defines; string postBuildScript; +string emscriptenWasm; // empty keeps the project's config.make value string ofPathEnv; string templateName; string msys2Env = "ucrt64"; @@ -264,6 +267,7 @@ void updateProject(const fs::path & path, const string & target, bool bConsiderP if (!bDryRun) { auto project = getTargetProject(target); project->setMsys2Environment(msys2Env); + project->setEmscriptenWasm(emscriptenWasm); project->create(path, templateName); if (bConsiderParameterAddons && bAddonsPassedIn) { @@ -615,6 +619,15 @@ int main(int argc, char ** argv) { defines = ofSplitString(options[DEFINES].arg, ",", true, true); } + if (options[EMSCRIPTEN_WASM].count() > 0 && options[EMSCRIPTEN_WASM].arg != NULL) { + std::string bits = ofTrim(options[EMSCRIPTEN_WASM].arg); + if (bits == "32" || bits == "64") { + emscriptenWasm = bits; + } else { + ofLogWarning() << "unknown emscripten wasm " << bits << ", expected 32 or 64 - keeping the project's setting"; + } + } + if (options[POSTBUILD].count() > 0 && options[POSTBUILD].arg != NULL) { postBuildScript = ofTrim(options[POSTBUILD].arg); } @@ -759,6 +772,7 @@ int main(int argc, char ** argv) { ofLogNotice() << "project path is: [" << projectPath << "]"; auto project = getTargetProject(t); project->setMsys2Environment(msys2Env); + project->setEmscriptenWasm(emscriptenWasm); project->create(projectPath, templateName); if(bAddonsPassedIn){ for (auto & addon : addons) { diff --git a/commandLine/src/projects/baseProject.cpp b/commandLine/src/projects/baseProject.cpp index f3a0f701..2a8d9b71 100644 --- a/commandLine/src/projects/baseProject.cpp +++ b/commandLine/src/projects/baseProject.cpp @@ -110,6 +110,23 @@ vector baseProject::listAvailableTemplates(string target) return templates; } +// PROJECT_EMSCRIPTEN_WASM from config.make; the old EMSCRIPTEN_MEMORY64=1 also means 64 +static std::string readEmscriptenWasm(const fs::path & configMake) { + std::string bits; + if (!fs::exists(configMake)) return bits; + for (auto & line : fileToStrings(configMake)) { + auto config = ofTrim(line); + if (config.empty() || config[0] == '#') continue; + auto eq = config.find('='); + if (eq == std::string::npos) continue; + auto var = ofTrim(config.substr(0, eq)); + auto value = ofTrim(config.substr(eq + 1)); + if (var == "PROJECT_EMSCRIPTEN_WASM") bits = value; + else if (var == "EMSCRIPTEN_MEMORY64" && value == "1" && bits.empty()) bits = "64"; + } + return bits; +} + bool baseProject::create(const fs::path & _path, string templateName){ // alert("baseProject::create " + path.string() + " : " + templateName, 35); auto path = _path; // just because it is const @@ -152,6 +169,27 @@ bool baseProject::create(const fs::path & _path, string templateName){ projectPath = normalizePath(projectPath); ofLogNotice() << "projectPath: [" << projectPath << "]"; projectName = projectPath.filename().string(); + + // read before template copies can replace config.make + emscriptenWasm = emscriptenWasmOption.empty() ? readEmscriptenWasm(projectDir / "config.make") : emscriptenWasmOption; + if (!emscriptenWasm.empty()) { + ofLogNotice() << "{ \"emscriptenWasm\": \"" << emscriptenWasm << "\" }"; + } + if (emscriptenWasm == "64") { + bool bHasWasm64Libs = false; + if (fs::exists(getOFRoot() / "libs")) { + for (auto & lib : fs::directory_iterator(getOFRoot() / "libs")) { + if (fs::exists(lib.path() / "lib" / "emscripten" / "WASM64")) { + bHasWasm64Libs = true; + break; + } + } + } + if (!bHasWasm64Libs) { + ofLogWarning() << "wasm64: no libs/*/lib/emscripten/WASM64 found. Run BITS=64 scripts/emscripten/download_libs.sh (replaces the installed lib/emscripten libs)"; + } + } + bool bDoesSrcDirExist = false; fs::path projectSrc { projectDir / "src" }; if (fs::exists(projectSrc) && fs::is_directory(projectSrc)) { @@ -266,6 +304,7 @@ bool baseProject::save(){ vector lines = fileToStrings(projectDir / "config.make"); std::ofstream saveConfig(projectDir / "config.make"); bool bWroteDefines = false; + bool bWroteWasm = false; for (auto & str : lines) { //add the of root path @@ -290,6 +329,16 @@ bool baseProject::save(){ saveConfig << "PROJECT_DEFINES = " << ofJoinString(projectDefines, " ") << std::endl; bWroteDefines = true; } + } + // emscripten wasm32 / wasm64: 32 is the makefile default, so it leaves the line commented + else if( !emscriptenWasm.empty() && (str.rfind("# PROJECT_EMSCRIPTEN_WASM =", 0) == 0 || str.rfind("PROJECT_EMSCRIPTEN_WASM =", 0) == 0) ){ + if (!bWroteWasm) { + saveConfig << (emscriptenWasm == "64" ? "PROJECT_EMSCRIPTEN_WASM = 64" : "# PROJECT_EMSCRIPTEN_WASM = 64") << std::endl; + bWroteWasm = true; + } + } + else if( emscriptenWasm == "32" && str.rfind("EMSCRIPTEN_MEMORY64", 0) == 0 ){ + saveConfig << "# " << str << std::endl; } else { saveConfig << str << std::endl; } @@ -297,6 +346,10 @@ bool baseProject::save(){ if (projectDefines.size() && !bWroteDefines && lines.size()) { saveConfig << "PROJECT_DEFINES = " << ofJoinString(projectDefines, " ") << std::endl; } + if (emscriptenWasm == "64" && !bWroteWasm && lines.size()) { + saveConfig << "# emscripten: 32 (default) or 64 (wasm64, MEMORY64)" << std::endl; + saveConfig << "PROJECT_EMSCRIPTEN_WASM = 64" << std::endl; + } #ifdef OFADDON_OUTPUT_JSON_DEBUG saveAddonsToJson(); #endif diff --git a/commandLine/src/projects/baseProject.h b/commandLine/src/projects/baseProject.h index 310c850a..3a912a8f 100644 --- a/commandLine/src/projects/baseProject.h +++ b/commandLine/src/projects/baseProject.h @@ -110,6 +110,8 @@ class baseProject { // MSYS2 environment (ucrt64 | mingw64 | clang64) - only meaningful for VSCodeProject, // a no-op everywhere else so callers don't need to know which target this applies to virtual void setMsys2Environment(const std::string & env) {} + // emscripten wasm32 / wasm64 (-W/--emscripten-wasm), written to config.make PROJECT_EMSCRIPTEN_WASM; empty keeps the project's value + void setEmscriptenWasm(const std::string & bits) { emscriptenWasmOption = bits; } #ifdef OFADDON_OUTPUT_JSON_DEBUG @@ -181,6 +183,8 @@ class baseProject { std::vector addons; std::vector extSrcPaths; std::vector projectDefines; // also written to config.make PROJECT_DEFINES for make builds + std::string emscriptenWasmOption; // from the CLI, empty = keep + std::string emscriptenWasm; // value for this project: "", "32" or "64" //cached addons - if an addon is requested more than once, avoid loading from disk as it's quite slow std::map> addonsCache; //indexed by [platform][supplied path] diff --git a/frontend/app.js b/frontend/app.js index 709f7578..01143fe2 100644 --- a/frontend/app.js +++ b/frontend/app.js @@ -209,6 +209,12 @@ ipcRenderer.on('setGenerateMode', (event, arg) => { }); //------------------------------------------- +// existing project's PROJECT_EMSCRIPTEN_WASM, so updating keeps it +ipcRenderer.on('setEmscriptenWasm', (event, bits) => { + $('#wasmDropdown').dropdown('set selected', bits === '64' ? '64' : '32'); + $('#wasmNote').toggle(bits === '64'); +}); + ipcRenderer.on('importProjectSettings', (event, settings) => { $("#projectPath").val(settings.projectPath); $("#projectName").val(settings.projectName).trigger('change'); // change triggers addon scanning @@ -992,6 +998,7 @@ function setup() { $('#rendererDropdown').dropdown(); $('#glDropdown, #webglDropdown, #glesDropdown').dropdown(); + $('#wasmDropdown').dropdown({ onChange: (value) => $('#wasmNote').toggle(value === '64') }); $('#rendererDropdown').dropdown('set exactly', 'default'); $('#msys2EnvDropdown').dropdown(); $('#msys2EnvDropdown').dropdown('set exactly', 'ucrt64'); @@ -1227,7 +1234,8 @@ function generate() { verbose: bVerbose, defines: definesArr.join(','), postBuild: $('#postBuildScript').val().trim(), - msys2Env: $('#msys2EnvField').css('display') !== 'none' ? $('#msys2EnvDropdown').dropdown('get value') : '' + msys2Env: $('#msys2EnvField').css('display') !== 'none' ? $('#msys2EnvDropdown').dropdown('get value') : '', + emscriptenWasm: $('#wasmField').css('display') !== 'none' ? $('#wasmDropdown').dropdown('get value') : '' }; // console.log(gen); @@ -1399,6 +1407,7 @@ function updateRendererFieldVisibility() { } const selectedTemplates = ($("#templatesDropdown input").val() || '').trim().split(','); $('#webglField').toggle(!!defaultSettings.advancedMode && selectedTemplates.includes('emscripten')); + $('#wasmField').toggle(!!defaultSettings.advancedMode && selectedTemplates.includes('emscripten')); $('#glesField').toggle(!!defaultSettings.advancedMode && selectedPlatforms.some((p) => ['ios', 'macos'].includes(p))); const glPlatforms = ['osx', 'vs', 'msys2', 'linux', 'linux64', 'linuxarmv6l', 'linuxaarch64', 'vscode']; diff --git a/frontend/index.html b/frontend/index.html index 97545293..bf8e31bc 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -278,6 +278,19 @@ +