Skip to content

Commit a9b2088

Browse files
committed
feat: add nostdlib option to C++ components for minimal WASM validation
- Add nostdlib attribute to cpp_component rule for creating minimal components - Components with nostdlib=true exclude standard library imports to match WIT specs exactly - Useful for validation scenarios where precise WIT compliance is required - Standard library linking is automatically disabled when nostdlib is enabled
1 parent 9488f04 commit a9b2088

File tree

1 file changed

+91
-10
lines changed

1 file changed

+91
-10
lines changed

cpp/defs.bzl

Lines changed: 91 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ def _cpp_component_impl(ctx):
9797
dep_includes.extend(cc_info.compilation_context.includes.to_list())
9898
dep_includes.extend(cc_info.compilation_context.system_includes.to_list())
9999
dep_includes.extend(cc_info.compilation_context.quote_includes.to_list())
100-
100+
101101
# CRITICAL FIX: Stage local CcInfo headers for cross-package dependencies
102102
# External libraries (path contains "external/") don't need staging - use original paths
103103
# Local libraries (same workspace) need staging for relative includes to work
@@ -107,6 +107,7 @@ def _cpp_component_impl(ctx):
107107
if "external/" not in hdr.path:
108108
# Local cross-package header - stage it for relative includes to work
109109
dep_headers.append(hdr)
110+
110111
# External headers are handled via include paths only (no staging needed)
111112

112113
# Generate bindings directory
@@ -171,6 +172,12 @@ def _cpp_component_impl(ctx):
171172
compile_args.add("-D_WASI_EMULATED_MMAN")
172173
compile_args.add("-DCOMPONENT_MODEL_PREVIEW2")
173174

175+
# Standard library control
176+
if ctx.attr.nostdlib:
177+
compile_args.add("-nostdlib")
178+
# When using nostdlib, we need to be more selective about what we link
179+
compile_args.add("-Wl,--no-entry") # Don't expect a main function
180+
174181
# Optimization and language settings
175182
if ctx.attr.optimize:
176183
compile_args.add("-O3")
@@ -239,25 +246,88 @@ def _cpp_component_impl(ctx):
239246
# Output
240247
compile_args.add("-o", wasm_binary.path)
241248

249+
# Add external dependency headers to inputs (from CcInfo)
250+
external_headers = []
251+
for dep in ctx.attr.deps:
252+
if CcInfo in dep:
253+
cc_info = dep[CcInfo]
254+
external_headers.extend(cc_info.compilation_context.headers.to_list())
255+
242256
# Add source files from work directory
243257
for src in sources:
244258
compile_args.add(work_dir.path + "/" + src.basename)
245259

260+
# Compile generated WIT binding C file separately (without C++ flags)
261+
# wit-bindgen generates filenames by converting hyphens to underscores: http-service-world -> http_service_world.c
262+
world_name = ctx.attr.world or "component" # Default to "component" if no world specified
263+
file_safe_world_name = world_name.replace("-", "_") # Convert hyphens to underscores for filesystem
264+
binding_c_file = bindings_dir.path + "/" + file_safe_world_name + ".c"
265+
binding_h_file = bindings_dir.path + "/" + file_safe_world_name + ".h"
266+
binding_o_file = bindings_dir.path + "/" + file_safe_world_name + "_component_type.o"
267+
268+
# Add bindings header directory to include path
269+
compile_args.add("-I" + bindings_dir.path)
270+
271+
# Compile the generated C binding file separately to avoid C++ flags
272+
binding_obj_file = ctx.actions.declare_file(ctx.attr.name + "_bindings.o")
273+
274+
binding_compile_args = ctx.actions.args()
275+
binding_compile_args.add("--target=wasm32-wasip2")
276+
binding_compile_args.add("--sysroot=" + sysroot_path)
277+
binding_compile_args.add("-c") # Compile only, don't link
278+
279+
# Component model definitions (same as main compilation)
280+
binding_compile_args.add("-D_WASI_EMULATED_PROCESS_CLOCKS")
281+
binding_compile_args.add("-D_WASI_EMULATED_SIGNAL")
282+
binding_compile_args.add("-D_WASI_EMULATED_MMAN")
283+
binding_compile_args.add("-DCOMPONENT_MODEL_PREVIEW2")
284+
285+
# Optimization settings
286+
if ctx.attr.optimize:
287+
binding_compile_args.add("-O3")
288+
binding_compile_args.add("-flto")
289+
else:
290+
binding_compile_args.add("-O0")
291+
binding_compile_args.add("-g")
292+
293+
# Include directories
294+
binding_compile_args.add("-I" + work_dir.path)
295+
binding_compile_args.add("-I" + bindings_dir.path)
296+
297+
# Add dependency include directories
298+
for include_dir in dep_includes:
299+
binding_compile_args.add("-I" + include_dir)
300+
301+
# Output and input
302+
binding_compile_args.add("-o", binding_obj_file.path)
303+
binding_compile_args.add(binding_c_file)
304+
305+
ctx.actions.run(
306+
executable = clang,
307+
arguments = [binding_compile_args],
308+
inputs = [work_dir, bindings_dir] + sysroot_files.files.to_list() + dep_headers + external_headers,
309+
outputs = [binding_obj_file],
310+
mnemonic = "CompileCppBindings",
311+
progress_message = "Compiling WIT bindings for %s" % ctx.label,
312+
)
313+
314+
# Add compiled binding object file and pre-compiled component type object to linking
315+
compile_args.add(binding_obj_file.path)
316+
compile_args.add(binding_o_file)
317+
318+
# Add C++ standard library linking for C++ language (unless nostdlib is used)
319+
if ctx.attr.language == "cpp" and not ctx.attr.nostdlib:
320+
compile_args.add("-lc++")
321+
compile_args.add("-lc++abi")
322+
246323
# Add dependency libraries for linking
247324
for lib in dep_libraries:
248325
compile_args.add(lib.path)
249326

250-
# Add external dependency headers to inputs (from CcInfo)
251-
external_headers = []
252-
for dep in ctx.attr.deps:
253-
if CcInfo in dep:
254-
cc_info = dep[CcInfo]
255-
external_headers.extend(cc_info.compilation_context.headers.to_list())
256-
257327
ctx.actions.run(
258328
executable = clang,
259329
arguments = [compile_args],
260-
inputs = [work_dir] + sysroot_files.files.to_list() + dep_libraries + dep_headers + external_headers,
330+
inputs = [work_dir, bindings_dir, binding_obj_file] + sysroot_files.files.to_list() + dep_libraries + dep_headers + external_headers,
261331
outputs = [wasm_binary],
262332
mnemonic = "CompileCppWasm",
263333
progress_message = "Compiling C/C++ to WASM for %s" % ctx.label,
@@ -369,6 +439,10 @@ cpp_component = rule(
369439
default = False,
370440
doc = "Enable C++ exceptions (increases binary size)",
371441
),
442+
"nostdlib": attr.bool(
443+
default = False,
444+
doc = "Disable standard library linking to create minimal components that match WIT specifications exactly",
445+
),
372446
},
373447
toolchains = [
374448
"@rules_wasm_component//toolchains:cpp_component_toolchain_type",
@@ -485,7 +559,9 @@ def _cc_component_library_impl(ctx):
485559

486560
# Get C/C++ toolchain
487561
cpp_toolchain = ctx.toolchains["@rules_wasm_component//toolchains:cpp_component_toolchain_type"]
488-
clang = cpp_toolchain.clang if ctx.attr.language == "c" else cpp_toolchain.clang_cpp
562+
563+
# Use clang for both C and C++ compilation to avoid clang_cpp preprocessor issues
564+
clang = cpp_toolchain.clang
489565
llvm_ar = cpp_toolchain.llvm_ar
490566
sysroot = cpp_toolchain.sysroot
491567
sysroot_files = cpp_toolchain.sysroot_files
@@ -513,6 +589,7 @@ def _cc_component_library_impl(ctx):
513589
if "external/" not in hdr.path:
514590
# Local cross-package header - stage it for relative includes to work
515591
dep_headers.append(hdr)
592+
516593
# External headers are handled via include paths only (no staging needed)
517594

518595
# Set up workspace with proper header staging (CRITICAL FIX for issue #38)
@@ -753,6 +830,10 @@ cc_component_library = rule(
753830
default = False,
754831
doc = "Enable C++ exceptions (increases binary size)",
755832
),
833+
"nostdlib": attr.bool(
834+
default = False,
835+
doc = "Disable standard library linking to create minimal components that match WIT specifications exactly",
836+
),
756837
},
757838
toolchains = [
758839
"@rules_wasm_component//toolchains:cpp_component_toolchain_type",

0 commit comments

Comments
 (0)