This fork diverges from upstream defold/defold to add native
OpenHarmony / HarmonyOS Next (arm64-ohos) engine support.
- Base commit at last sync:
17b9565(dev, "New translations en.editor_localization (Chinese Simplified)" — most recent upstream commit before fork divergence). - Strategy: small commits, each kept compile-clean for the existing
control platform (arm64-android) so fork CI stays green between steps.
See
.github/workflows/fork-build.yml. - Companion forks:
birdingman0626/extender— provides thearm64-ohosrecipe inshare/extender/build_input.yml(see that fork'sFORK_NOTES.md§8).D:\Workspace\VNProj\defold_vn\ohos\— the HarmonyOS HAP shell that hosts the eventual engine.so.
Maintainership intent: continuous merge from upstream/dev is expected.
This document is the rebase ledger.
cd D:\DevTools\defold-engine
git fetch upstream
git merge upstream/dev # or: git rebase upstream/dev
# resolve conflicts using this document as a guide
git push origin dev # auto-triggers fork-build CIAfter pushing, watch:
Why: Upstream's .github/workflows/main-ci.yml depends on
Defold-internal secrets that don't exist on this fork (AWS_*,
SLACK_WEBHOOK, NOTARIZATION_*). Every push from this fork to
dev failed CI before this workflow existed.
What it does:
- Triggers on push to
devorohos/**branches, or onworkflow_dispatchwith aplatformchooser (arm64-android/armv7-android/arm64-ohos). - Runs
ci/ci.sh install --platform=...thenci/ci.sh ... --skip-tests --skip-builtins --skip-docs engine. - No
--archive, no S3 upload, no Slack notifications, no notarization.
Validated green builds:
25953358754arm64-android — initial workflow add (8m4s)25953362435arm64-android — manual dispatch sanity (7m19s)25953551557arm64-android — after OHOS platform constants (8m38s)
Conflict resolution: this is a pure addition, no upstream file to conflict with. Safe to keep on any rebase.
These four commits register arm64-ohos as a known target across
the proto / Java / Python layers. They do not add any toolchain
or platform-layer source code — that's stage 3 (still pending).
- Added
OS_ID_OHOS = 11to thePlatformProfile.OSenum. - Upstream conflict risk: if upstream adds new
OS_ID_*values, rebase to the next free integer.
- Added
OHOS(new String[] {"arm64-ohos"}, ...)enum value.
- Added
public static final Platform Arm64Ohosmirroring theArm64Androiddefinition (lib-prefixlib, lib-suffix.so).
- Added
OHOS: str = 'ohos'to theTargetOSContantsNamedTuple.
- Added
{'platform': 'arm64-ohos', 'os': TargetOS.OHOS, 'arch': 'arm64'}to_supported_platforms.
- Added
'arm64-ohos'toBASE_PLATFORMS.
- Added
DM_PLATFORM_OHOSdocstring placeholder. The actual-DDM_PLATFORM_OHOSinjection inbuild_tools/waf_dynamo.pyis in §2.8.
sdk.py: threearm64-ohosbranches added —_get_defold_sdk_folders,check_local_sdk,_get_defold_sdk_info,_get_local_sdk_info. All resolve OHOS toolchain from env varsOHOS_NDK_PATH/OHOS_NDK_BIN_PATH/OHOS_NDK_SYSROOT(no download support — set by caller).check_local_sdkraises a clearSDKExceptionifOHOS_NDK_PATHis missing.waf_dynamo.py:elif TargetOS.OHOS == target_os:branch added. Injects-target aarch64-linux-ohos,--sysroot=$OHOS_NDK_SYSROOT,-D__MUSL__,-DDM_PLATFORM_OHOS, the Android-like flag set (-fpic -ffunction-sections -fno-rtti -static-libstdc++), and pointsCC/CXXat$OHOS_NDK_BIN_PATH/clang*. On Windows usesclang++.exedirectly to bypass the POSIX shell-wrapper shim.
- Minimum-viable
IBundlerforarm64-ohos(auto-registered viaProject.doScan's classpath search). Copies the engine.so, shared libs, archive, and bundle resources into a flat output directory that the project'sohos/ArkTS shell consumes viahvigorw assembleHap. - Does NOT yet produce a
.hapdirectly — that requires templating module.json5 + invokinghvigorw+ signing withhap-sign-tool.jar, which is deferred until an arm64-ohos engine.soactually exists to package (§3 engine port).
- Added
ohos:base block (env, engineLibs, dynamicLibs, file patterns, allowedFlags, libCmd) andarm64-ohos:arch-specific block (NDK_CXX, defines, compileCmd, linkCmd). Mirrors arm64-android structure. - Build output ships in every defoldsdk tarball as
defoldsdk/extender/build.yml; the extender server picks it up automatically. This replaces the previously-uncommitted local prototype that lived inD:\DevTools\extender\sdk\.../defoldsdk/ extender/build.yml. - engineLibs list references
.afiles under{{dynamo_home}}/lib/arm64-ohosthat don't yet exist; link will fail there with "library not found: libengine.a" until §3 produces them. That's the next break-point.
Routes the existing posix / linux / Android-equivalent source
files for OHOS so --platform=arm64-ohos build_engine advances
past the per-subsystem dispatch and only fails when it hits the
missing platform-specific code in §3.
build_tools/cross.py— add'ohos'to the fallback-tag list that maps toposix. Unblocks dlib's socket / sys / mutex / thread / time / condition_variable / file_descriptor.engine/dlib/src/wscript— add'ohos': ['mbedtls']to the sslsocket extra_tags dict (HTTPS works via the same mbedtls build that Android/Linux use).engine/extension/src/wscript— comment-only; noextension_ohos.cppyet (no lifecycle-hook surface yet).engine/sound/src/wscript— route OHOS todevices/device_opensl.cpp. OpenSL ES exists on OHOS.engine/sound/src/devices/device_opensl.cpp— wrapGetSampleRate()JNI body in#if !defined(DM_PLATFORM_OHOS)with a hardcoded44100fallback for OHOS (OpenSL resamples internally if device rate differs).engine/crash/src/wscript— OHOS reuses libunwind +load_addrs_proc_smap.cpp+file_posix.cpp(matches Android).engine/profiler/src/wscript— OHOS reusesprofiler_linux.cppprofiler_proc_utils.cpp(musl exposes /proc the same way).
engine/engine/src/wscript— OHOS added to UNWIND/CPP_RUNTIME list and to no-OPENAL list (it uses OpenSL ES via §2.11 device_opensl).build_tools/waf_dynamo.py apply_apk_test— also convertcprogram→cshlibon OHOS so the engine ships aslibdmengine.sofor ArkTS to load via XComponent.
After this section, every Defold engine subsystem that has a purely-POSIX or Linux-compatible variant is selected for OHOS. The remaining gap is the truly platform-different code in §3.
engine/platform/src/platform_window_ohos.h— declaresdmPlatform::OhosVerifySurface/OhosBeginFrame/GetOhosEGLContext/ GetOhosEGLSurface/GetSafeAreaOhos. Mirrors the Android header but omits the JNI/ANativeWindow-specific functions; OHOS will need NAPI + OHNativeWindow getters when those are implemented.engine/platform/src/platform_window_glfw_ohos.cpp— every function returnsNULL/0/false. Lets theplatformstatic library link cleanly for arm64-ohos; engine produces no output at runtime because there's no surface to draw to. Real XComponent-backed implementation is the multi-day piece in §3.engine/platform/src/wscriptroutes arm64-ohos to the new .cpp and installs the new header.
- Single C placeholder symbol so
bld.stlib(target='dmglfw' ...)builds a non-empty static archive for arm64-ohos.dmengine_headlessdoesn't link dmglfw at all (uses PLATFORM_NULL + GRAPHICS_NULL); fulldmenginewill link it and fail with missing GLFW symbols until §3 lands the real backend.
engine/platform/src/platform_window_ohos.cpp— fulldmPlatform::*implementation that bypasses GLFW. Singleton Window struct, real EGL setup (eglGetDisplay→eglInitialize→eglChooseConfig→eglCreateContext, surface bound from XComponent's OHNativeWindow), trivial stubs for keyboard/mouse/ joystick, touch buffer drained per-frame viaGetTouchData.engine/platform/src/wscriptarm64-ohos branch now builds theplatformlib fromwindow.cpp + platform_window_ohos.cpponly — skipsplatform_window_glfw.cppentirely.- The old
platform_window_glfw_ohos.cppstub is deleted.
engine/engine/src/ohos/napi_init.cpp— exportsattachXComponent / engineStart / engineStopto ArkTS, registersOH_NativeXComponent_RegisterCallbackhandlers that forward intoOhosPlatform_*C hooks (defined in platform_window_ohos.cpp). Spawnsengine_main()on a pthread when ArkTS callsengineStart().engine/engine/src/wscriptincludes ohos/napi_init.cpp in theenginestatic lib on arm64-ohos.build_tools/waf_dynamo.py+share/extender/build_input.ymladdace_napi.z,ace_ndk.z,hilog_ndk.z,native_windowto the OHOS link line.
Lives in defold_vn, not in this fork. Committed at
b99ab79 (defold_vn main branch).
ohos/entry/src/main/ets/pages/Index.ets— now creates anXComponentwith id "defold" and callsdmengine.attachXComponent(context)+engineStart()on load. The previous WebView wrapper is preserved atIndex.webview.ets.bak.EntryAbility.etsdrops thecustomizeSchemes(dvn://)block (no Web component → no CORS workaround needed).scripts/build_ohos.ps1switched to the native pipeline: bob.jar arm64-ohos bundle → copy.soto libs/arm64-v8a → hvigor → sign.
On the local Windows host with the OHOS NDK at
D:\DevTools\command-line-tools\sdk\default\openharmony\native,
python scripts/build.py --platform=arm64-ohos check_sdk now
advances past SDK lookup successfully (only failing on missing
protoc, which install_ext would provide). Confirms the
toolchain wiring + sdk.py + build_constants routing all work
end-to-end for the OHOS path.
After:
install_ext(withPACKAGES_OHOS_64 = []placeholder)check_sdk(sdk_ohos.cmake locates the NDK toolchain file)- scoop-installed LLVM 22.1.5 (
clang++.exefor host gen_java; OHOS NDK's clang 15.0.4 is too old to parse VS Insiders 18 MSVC 14.51 headers) - Git's
sh.exeon PATH (bob-light'sscripts/copy.sh) - VS 2026 Insiders MSVC detection (sdk.py
_get_common_visual_studio_rootsnow scans<base>/Microsoft Visual Studio/<N>/Insiders/)
scripts/build_ohos_engine.ps1 now drives the build through:
✓ host bootstrap (testmain, dlib, texc, modelc, shaderc, bob-light)
✓ arm64-ohos cross-compile of every engine library
✓ bob-light compiling bundled materials (gui.fp/.vp → .spc)
✓ reaches dmengine LINK step
getPlatformShaderLanguages()and the helper near line 266 both addPlatform.Arm64Ohosto the GLES_SM300+GLES_SM100 cluster (Android-equivalent). Without this, bob-light'sShaderProgramBuilder.main()NPEs because the returned set is null, thenaddAll(...)blows up.
Resolved all 7 ld.lld: unable to find library errors:
- bullet-2.77 (BulletDynamics/BulletCollision/LinearMath):
cross-compiled in-tree via
external/bullet3d/wscriptafter filteringEXTERNAL_LIBSto bullet3d+box2d_v2 only for OHOS inscripts/build.py:build_external. 4.6 s build. - box2d_defold-2.2.1: same path via
external/box2d_v2/wscript. 1.9 s build. - tremolo-b0cb4d1: cloned
github.com/defold/defold-tremolo, ran itsbuild.shwithCC=<NDK>/clang.exe -target aarch64-linux-ohos --sysroot=<NDK>/sysroot. Vanilla-DONLY_Cbuild (no ARM asm path needed for aarch64). - dmglfw: empty
libdmglfw.astub (OHOS bypasses GLFW entirely viaplatform_window_ohos.cpp). Packaged asglfw-2.7.1-arm64-ohos.tar.gzto match Defold's pre-existing package naming. - luajit-5.1: gated off —
platform_supports_feature(plat, 'luajit', _)returns False for arm64-ohos inwaf_dynamo.py, which routesSTLIB_LUA='lua'(vanilla Lua, built in-tree fromengine/lua/). Cross-compiling LuaJIT properly from a Windows host requires producing aarch64-pointer-size assembler with a host clang — non-trivial and would block progress.
Also filtered the spurious MinGW -Wl,--enable-auto-import flag
(baked in by waflib's gcc_modifier_win32 when host is Windows)
and reset cprogram_PATTERN/cshlib_PATTERN away from .exe/.dll
to ELF-friendly defaults inside the OHOS branch of
set_cross_compile_args (build_tools/waf_dynamo.py).
Final artifact: tmp/dynamo_home/bin/arm64-ohos/libdmengine.so
(23 MB, ELF64 AArch64 DYN). The ArkTS host shell at
defold_vn/ohos/entry/src/main/ets/pages/Index.ets can now load
this .so via the NAPI module to bring up the visual novel on
the emulator (next step — see §3).
The work in §2 routes everything that can fall back to posix/linux.
What's left is the genuinely platform-different code: ArkTS host shell,
XComponent surface acquisition, EGL+GLESv2 context tied to the
XComponent's OHNativeWindow, NAPI bindings for OS lookups, and the
GLFW backend (external/glfw/lib/ohos/) that mirrors the ~4,450-line
android one.
Estimated remaining work: 40+ engineering hours, much of which is emulator-iteration cycles (build → hdc install → hilog → fix → repeat, typically 10-30 cycles to converge).
Files still to add (mirroring the android variants — see
engine/*/src/platform_* naming convention):
engine/platform/src/platform_window_ohos.cpp— surface backed by OHOS XComponent (instead of GLFW or Android NativeActivity).engine/graphics/src/opengl/graphics_opengl_ohos.cpp— EGL + GLESv2 init. ArkWeb already proved OHOS GLESv2 works; this just wires it to a native EGL context.engine/hid/src/hid_ohos.cpp— touch + sensor input from ArkUI's input bridge.engine/sound/src/sound_ohos.cpp— OpenSL ES is available; can mostly copy the Android backend.engine/sys/src/sys_ohos.cpp— file paths, locale, system info via OHOS NAPI.
After the engine .so exists, extend OhosBundler (§2.9) to template
ArkTS entry + run hvigorw assembleHap + sign.
The extender-side recipe lives in §2.10 above. Any defoldsdk built
from this fork after commit 120c575 ships the OHOS recipe
automatically — no need to keep the local extender SDK cache in sync
by hand.
The work in §1–§2 leaves the build system fully OHOS-aware and the
engine .so should now link as libdmengine.so for arm64-ohos (with
all-NULL platform/graphics). The path from there to actually rendering
the VN sample on the emulator is roughly:
Set OHOS_NDK_PATH + OHOS_NDK_BIN_PATH + OHOS_NDK_SYSROOT, then
./scripts/build.py --platform=arm64-ohos shell and inside that
shell python scripts/build.py --platform=arm64-ohos install_ext install_sdk build_engine. Iterate on whatever compile/link errors
surface (will be many — unfixed symbols inside files that compile but
weren't speculative-tested). When dmengine_headless for arm64-ohos
links to a .so, that's milestone 1.
Mirror external/glfw/lib/android/ into external/glfw/lib/ohos/.
The mapping is well-defined:
| Android concept | OHOS equivalent |
|---|---|
ANativeWindow* |
OHNativeWindow* (from <native_window/external_window.h>) |
android_app* / native_app_glue |
OH_NativeXComponent_Callback (from <ace/xcomponent/native_interface_xcomponent.h>) |
JNI JNIEnv / jobject |
NAPI napi_env / napi_value |
ALooper |
OHOS event loop (uv_async via NAPI bridge) |
AInputEvent |
XComponent touch callbacks (OnTouchEvent_CB) |
ANativeActivity_onCreate() |
XComponent register-callback NAPI export |
Files to write (count + Android-equivalent LOC for sizing):
ohos_init.c(~1300 LOC) — EGL+GLES context init, XComponent surfaceohos_window.c(~900 LOC) — surface lifecycle (create/resize/destroy)ohos_native_app_glue.c(~450 LOC) — XComponent lifecycle ↔ engine main loop bridgeohos_jni.c→ohos_napi.c(~300 LOC) — NAPI bindings for OS lookupsohos_joystick.c,ohos_thread.c,ohos_time.c,ohos_util.{c,h},ohos_enable.c,ohos_glext.c— typically each <200 LOC, mostly direct portsplatform.h— OHOS-specific platform struct (mirrorsandroid/platform.h)
In defold_vn/ohos/entry/src/main/ets/:
- Replace the existing WebView wrapper with an XComponent.
- ArkTS side registers
OH_NativeXComponent_RegisterCallbackfor surface lifecycle + touch events. - Native side (a new tiny
napi_init.cppin the engine) receives those callbacks, packages them into events the GLFW backend's event loop consumes. - ArkTS calls
engineMain(width, height)fromOnSurfaceCreated_CB.
OhosBundler.java (§2.9) currently copies the engine .so to a flat
output dir. Extend it to:
- Template
ohos/entry/src/main/module.json5with the project's bundleName + abilityName. - Run
hvigorw assembleHapfrom the templated project. - Sign with
hap-sign-tool.jarusing project-provided keystore.
Build → hdc install → aa start → hdc shell hilog → fix → repeat.
Common first-cycle failures (in expected order):
libdmengine.sofails to load — missing NAPI export or undefined symbol. Fix innapi_init.cpp/ GLFW backend.- Engine launches but XComponent surface acquisition times out. Fix lifecycle ordering in ArkTS shell.
- EGL context creates but
eglMakeCurrentfails. Usually the EGLConfig isn't matching the XComponent's surface format. - Rendering proceeds but produces a black frame. GLES extension detection (same family of issue as the WebView wrapper had — compressed textures, GLSL version).
- Rendering works but touch input doesn't route. Fix
OH_NativeXComponent_RegisterCallbackwiring.
A real engineer with an OHOS device should expect ~10-30 iteration
cycles to converge. The emulator's software GLES will be more painful
than a real device — the prior WebView attempt black-screened on
emulator GLES specifically (see .agentdocs/build/ohos_webview.md
in defold_vn). A real device is the better target.
scripts/build_ohos.ps1 already drives the flow for the WebView
wrapper. After Step 4, swap it to:
bob.jar --platform=arm64-ohos --architectures=arm64-ohos bundle(uses §2.9OhosBundler).- Optionally
hdc installthe result.
If §2.9 bundles correctly and §1-3 produce a working engine .so, this
script should produce a .hap that boots into the VN title screen on
the emulator.
~6-10 engineering days for a developer who hasn't done OHOS native work before; ~3-4 days for someone with prior OH_NativeXComponent experience. Most of the variance comes from emulator GLES bugs vs. real-device validation.