Skip to content

Commit 348f8ea

Browse files
committed
chore(ci): optimize partial linking for faster WASM builds
1 parent 344e623 commit 348f8ea

File tree

2 files changed

+77
-5
lines changed

2 files changed

+77
-5
lines changed

ci/wasm_build_library.py

Lines changed: 72 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,9 @@ def get_source_files() -> list[Path]:
211211
src_dir = PROJECT_ROOT / "src"
212212
sources = []
213213
for cpp_file in src_dir.rglob("*.cpp"):
214+
# Exclude entry_point.cpp (it's compiled separately in wasm_compile_native.py)
215+
if cpp_file.name == "entry_point.cpp":
216+
continue
214217
sources.append(cpp_file.resolve())
215218

216219
return sorted(sources) # Sort for deterministic ordering
@@ -757,12 +760,78 @@ def build_library(
757760

758761
print(f"Found {len(all_objects)} object files")
759762

760-
# Step 9: Create thin archive
761-
archive_result = create_thin_archive(emar, all_objects, LIBRARY_OUTPUT, verbose)
763+
# Step 9: Partial linking optimization (combine all objects into one)
764+
# This dramatically speeds up final linking by reducing file I/O
765+
partial_object = BUILD_DIR / "libfastled_partial.o"
766+
needs_partial_relink = force
767+
768+
# Check if partial object needs to be regenerated
769+
if not needs_partial_relink and partial_object.exists():
770+
partial_mtime = partial_object.stat().st_mtime
771+
# Check if any input object is newer
772+
for obj in all_objects:
773+
if obj.stat().st_mtime > partial_mtime:
774+
needs_partial_relink = True
775+
break
776+
else:
777+
needs_partial_relink = True
778+
779+
if needs_partial_relink:
780+
print(f"Partial linking {len(all_objects)} objects into single object...")
781+
782+
# Use response file for wasm-ld (Windows command line is too short)
783+
response_file = BUILD_DIR / "partial_link_objects.rsp"
784+
with open(response_file, "w") as f:
785+
for obj_path in all_objects:
786+
f.write(f'"{obj_path}"\n')
787+
788+
# Find wasm-ld (it's in the same directory as em++)
789+
emcc_dir = emcc.parent
790+
wasm_ld = (
791+
emcc_dir / "wasm-ld.exe"
792+
if emcc_dir.name == "emscripten"
793+
else emcc_dir / "wasm-ld"
794+
)
795+
if not wasm_ld.exists():
796+
# Try in bin subdirectory
797+
wasm_ld = (
798+
emcc_dir.parent
799+
/ "bin"
800+
/ ("wasm-ld.exe" if sys.platform == "win32" else "wasm-ld")
801+
)
802+
803+
# Partial link command using wasm-ld directly
804+
partial_link_cmd = [
805+
str(wasm_ld),
806+
"-r", # Relocatable output
807+
"--threads=8", # Use threading for faster partial linking
808+
f"@{response_file}",
809+
"-o",
810+
str(partial_object),
811+
]
812+
813+
if verbose:
814+
print(
815+
f"Partial link command: wasm-ld -r @{response_file.name} -o {partial_object}"
816+
)
817+
818+
result = subprocess.run(partial_link_cmd, cwd=PROJECT_ROOT)
819+
if result.returncode != 0:
820+
print(f"✗ Partial linking failed with return code {result.returncode}")
821+
return result.returncode
822+
823+
print(f"✓ Partial object created: {partial_object}")
824+
else:
825+
print(f"✓ Partial object is up-to-date")
826+
827+
# Step 10: Create thin archive from partial object (single file, much faster)
828+
archive_result = create_thin_archive(
829+
emar, [partial_object], LIBRARY_OUTPUT, verbose
830+
)
762831
if archive_result != 0:
763832
return archive_result
764833

765-
# Step 10: Save metadata for future builds
834+
# Step 11: Save metadata for future builds
766835
metadata = {
767836
"flags_hash": flags_hash,
768837
"build_mode": build_mode,

src/platforms/wasm/compiler/build_flags.toml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,9 @@ flags = [
262262
"-sWASM=1", # Generate WebAssembly output (not asm.js)
263263

264264
# Linker Performance Optimization
265-
"-Wl,--threads=6", # Enable parallel linking with 6 threads (balanced for performance)
265+
# Note: With partial linking reducing input to single object file, threading overhead isn't worth it
266+
# Testing showed 1 thread is as fast or faster than 8/16 threads for this small workload
267+
"-fuse-ld=wasm-ld", # Explicitly use wasm-ld (default, but explicit for clarity)
266268

267269
# Threading Support
268270
"-pthread", # Enable POSIX threads support
@@ -305,7 +307,8 @@ flags = [
305307
# Re-enable for release builds: "-Wl,--gc-sections"
306308
"-Wl,--strip-debug", # Strip debug info from quick builds (faster linking)
307309
"-Wl,--no-demangle", # Disable symbol demangling (faster linking)
308-
"--source-map-base=http://localhost:8000/", # Base URL for source map files
310+
"-Wl,--no-export-dynamic", # Don't export all symbols (faster symbol processing)
311+
# Note: --source-map-base removed in quick mode for faster linking
309312
]
310313

311314
[linking.library]

0 commit comments

Comments
 (0)