Skip to content

Commit aa664c0

Browse files
committed
fix: cpp rules now properly respect the language attribute
Fixes #211 This commit resolves the issue where C/C++ component rules were ignoring the language attribute and always generating C bindings regardless of user configuration. Changes: - Fixed hardcoded language in cpp_component binding generation (line 157) - Fixed hardcoded language in cpp_wit_bindgen binding generation (line 628) - Fixed binding file extensions to match language (.c vs .cpp) - Added C++ source auto-detection for compilation support - Added C++ include paths and compilation flags for C++ bindings - Fixed string-encoding to only apply to C bindings - Added cxx_std and optimization fields to WasmComponentInfo metadata - Fixed all action mnemonics to be language-specific - Updated test framework to handle optional optimization checks - Updated examples to use correct language configurations Test Results: - All 19 C/C++ tests passing - All C/C++ examples building successfully Architecture Patterns: - C++ source with C bindings: language="c" (auto-detects C++ compilation) - C++ source with C++ bindings: language="cpp" (namespace-based API) - C source with C bindings: language="c" (standard C compilation)
1 parent 47a99a5 commit aa664c0

File tree

10 files changed

+122
-59
lines changed

10 files changed

+122
-59
lines changed

cpp/defs.bzl

Lines changed: 67 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,11 @@ def _cpp_component_impl(ctx):
113113
headers = ctx.files.hdrs
114114
wit_file = ctx.file.wit
115115

116+
# Detect if we need C++ compilation support (for source files, not bindings)
117+
# This allows C++ source files to work with both C and C++ bindings
118+
has_cpp_sources = any([src.extension in ["cpp", "cc", "cxx", "C", "CPP"] for src in sources])
119+
needs_cpp_compilation = ctx.attr.language == "cpp" or has_cpp_sources
120+
116121
# Collect dependency headers and libraries using CcInfo provider
117122
dep_headers = []
118123
dep_libraries = []
@@ -154,7 +159,7 @@ def _cpp_component_impl(ctx):
154159

155160
# Generate C/C++ bindings from WIT
156161
wit_args = ctx.actions.args()
157-
wit_args.add("c")
162+
wit_args.add(ctx.attr.language)
158163
wit_args.add("--out-dir", bindings_dir.path)
159164

160165
if ctx.attr.world:
@@ -167,8 +172,8 @@ def _cpp_component_impl(ctx):
167172
arguments = [wit_args],
168173
inputs = [wit_file],
169174
outputs = [bindings_dir],
170-
mnemonic = "WitBindgenCpp",
171-
progress_message = "Generating C/C++ bindings for %s" % ctx.label,
175+
mnemonic = "WitBindgen" + ("C" if ctx.attr.language == "c" else "Cpp"),
176+
progress_message = "Generating %s bindings for %s" % (ctx.attr.language.upper(), ctx.label),
172177
)
173178

174179
# Create working directory for compilation using File Operations Component
@@ -226,8 +231,8 @@ def _cpp_component_impl(ctx):
226231
compile_args.add("-O0")
227232
compile_args.add("-g")
228233

229-
# C++ specific flags
230-
if ctx.attr.language == "cpp":
234+
# C++ specific flags (for source compilation)
235+
if needs_cpp_compilation:
231236
if ctx.attr.enable_exceptions:
232237
# Enable exceptions if specifically requested
233238
compile_args.add("-fexceptions")
@@ -248,7 +253,7 @@ def _cpp_component_impl(ctx):
248253
compile_args.add("-I" + work_dir.path)
249254

250255
# Add C++ standard library paths for wasm32-wasip2 target
251-
if ctx.attr.language == "cpp":
256+
if needs_cpp_compilation:
252257
# WASI SDK stores C++ headers in share/wasi-sysroot, not just sysroot
253258
if "/external/" in sysroot_path:
254259
toolchain_repo = sysroot_path.split("/sysroot")[0]
@@ -298,11 +303,12 @@ def _cpp_component_impl(ctx):
298303
for src in sources:
299304
compile_args.add(work_dir.path + "/" + src.basename)
300305

301-
# Compile generated WIT binding C file separately (without C++ flags)
306+
# Compile generated WIT binding file separately
302307
# wit-bindgen generates filenames by converting hyphens to underscores: http-service-world -> http_service_world.c
303308
world_name = ctx.attr.world or "component" # Default to "component" if no world specified
304309
file_safe_world_name = world_name.replace("-", "_") # Convert hyphens to underscores for filesystem
305-
binding_c_file = bindings_dir.path + "/" + file_safe_world_name + ".c"
310+
binding_ext = ".cpp" if ctx.attr.language == "cpp" else ".c"
311+
binding_c_file = bindings_dir.path + "/" + file_safe_world_name + binding_ext
306312
binding_h_file = bindings_dir.path + "/" + file_safe_world_name + ".h"
307313
binding_o_file = bindings_dir.path + "/" + file_safe_world_name + "_component_type.o"
308314

@@ -336,6 +342,27 @@ def _cpp_component_impl(ctx):
336342
binding_compile_args.add("-fexceptions")
337343
binding_compile_args.add("-fcxx-exceptions")
338344

345+
# C++ compilation flags for binding compilation
346+
if ctx.attr.language == "cpp":
347+
# C++ standard (bindings require C++20 for std::span)
348+
if ctx.attr.cxx_std:
349+
binding_compile_args.add("-std=" + ctx.attr.cxx_std)
350+
else:
351+
binding_compile_args.add("-std=c++20") # Default to C++20 for bindings
352+
353+
# WASI SDK stores C++ headers in share/wasi-sysroot, not just sysroot
354+
if "/external/" in sysroot_path:
355+
toolchain_repo = sysroot_path.split("/sysroot")[0]
356+
wasi_sysroot = toolchain_repo + "/share/wasi-sysroot"
357+
else:
358+
wasi_sysroot = sysroot_path
359+
binding_compile_args.add("-I" + wasi_sysroot + "/include/wasm32-wasip2/c++/v1")
360+
binding_compile_args.add("-I" + wasi_sysroot + "/include/c++/v1")
361+
362+
# Also add clang's builtin headers
363+
if "/external/" in sysroot_path:
364+
binding_compile_args.add("-I" + toolchain_repo + "/lib/clang/20/include")
365+
339366
# Include directories
340367
binding_compile_args.add("-I" + work_dir.path)
341368
binding_compile_args.add("-I" + bindings_dir.path)
@@ -353,8 +380,8 @@ def _cpp_component_impl(ctx):
353380
arguments = [binding_compile_args],
354381
inputs = [work_dir, bindings_dir] + sysroot_files.files.to_list() + dep_headers + external_headers,
355382
outputs = [binding_obj_file],
356-
mnemonic = "CompileCppBindings",
357-
progress_message = "Compiling WIT bindings for %s" % ctx.label,
383+
mnemonic = "Compile" + ("C" if ctx.attr.language == "c" else "Cpp") + "Bindings",
384+
progress_message = "Compiling %s WIT bindings for %s" % (ctx.attr.language.upper(), ctx.label),
358385
)
359386

360387
# Add compiled binding object file and pre-compiled component type object to linking
@@ -370,8 +397,8 @@ def _cpp_component_impl(ctx):
370397
else:
371398
compile_args.add("-l" + lib) # Library name (e.g., "m" -> "-lm")
372399
else:
373-
# Standard library linking for C++ language
374-
if ctx.attr.language == "cpp":
400+
# Standard library linking for C++ source files
401+
if needs_cpp_compilation:
375402
compile_args.add("-lc++")
376403
compile_args.add("-lc++abi")
377404

@@ -394,8 +421,8 @@ def _cpp_component_impl(ctx):
394421
arguments = [compile_args],
395422
inputs = [work_dir, bindings_dir, binding_obj_file] + sysroot_files.files.to_list() + dep_libraries + dep_headers + external_headers,
396423
outputs = [wasm_binary],
397-
mnemonic = "CompileCppWasm",
398-
progress_message = "Compiling C/C++ to WASM for %s" % ctx.label,
424+
mnemonic = "Compile" + ("C" if ctx.attr.language == "c" else "Cpp") + "Wasm",
425+
progress_message = "Compiling %s to WASM for %s" % (ctx.attr.language.upper(), ctx.label),
399426
)
400427

401428
# Embed WIT metadata and create component in one step
@@ -414,8 +441,8 @@ def _cpp_component_impl(ctx):
414441
arguments = [embed_args],
415442
inputs = [wasm_binary, wit_file],
416443
outputs = [component_wasm],
417-
mnemonic = "CreateCppComponent",
418-
progress_message = "Creating WebAssembly component for %s" % ctx.label,
444+
mnemonic = "Create" + ("C" if ctx.attr.language == "c" else "Cpp") + "Component",
445+
progress_message = "Creating %s WebAssembly component for %s" % (ctx.attr.language.upper(), ctx.label),
419446
)
420447

421448
# Optional WIT validation
@@ -469,10 +496,12 @@ def _cpp_component_impl(ctx):
469496
exports = [ctx.attr.world] if ctx.attr.world else [],
470497
metadata = {
471498
"name": ctx.label.name,
472-
"language": "cpp",
499+
"language": ctx.attr.language,
473500
"target": "wasm32-wasip2",
474501
"wasi_sdk": True,
475502
"toolchain": "wasi-sdk",
503+
"cxx_std": ctx.attr.cxx_std if ctx.attr.cxx_std else None,
504+
"optimization": ctx.attr.optimize,
476505
},
477506
profile = ctx.attr.optimization if hasattr(ctx.attr, "optimization") else "release",
478507
profile_variants = {},
@@ -625,7 +654,7 @@ def _cpp_wit_bindgen_impl(ctx):
625654

626655
# Generate C/C++ bindings
627656
args = ctx.actions.args()
628-
args.add("c")
657+
args.add(ctx.attr.language)
629658
args.add("--out-dir", bindings_dir.path)
630659

631660
if ctx.attr.world:
@@ -634,7 +663,8 @@ def _cpp_wit_bindgen_impl(ctx):
634663
if ctx.attr.stubs_only:
635664
args.add("--stubs-only")
636665

637-
if ctx.attr.string_encoding:
666+
# string-encoding is only supported for C bindings
667+
if ctx.attr.string_encoding and ctx.attr.language == "c":
638668
args.add("--string-encoding", ctx.attr.string_encoding)
639669

640670
args.add(wit_file.path)
@@ -644,8 +674,8 @@ def _cpp_wit_bindgen_impl(ctx):
644674
arguments = [args],
645675
inputs = [wit_file],
646676
outputs = [bindings_dir],
647-
mnemonic = "CppWitBindgen",
648-
progress_message = "Generating C/C++ WIT bindings for %s" % ctx.label,
677+
mnemonic = ("C" if ctx.attr.language == "c" else "Cpp") + "WitBindgen",
678+
progress_message = "Generating %s WIT bindings for %s" % (ctx.attr.language.upper(), ctx.label),
649679
)
650680

651681
return [
@@ -675,6 +705,11 @@ cpp_wit_bindgen = rule(
675705
values = ["utf8", "utf16", "compact-utf16"],
676706
doc = "String encoding to use in generated bindings",
677707
),
708+
"language": attr.string(
709+
default = "cpp",
710+
values = ["c", "cpp"],
711+
doc = "Language variant (c or cpp)",
712+
),
678713
},
679714
toolchains = [
680715
"@rules_wasm_component//toolchains:cpp_component_toolchain_type",
@@ -750,6 +785,10 @@ def _cc_component_library_impl(ctx):
750785
sysroot = cpp_toolchain.sysroot
751786
sysroot_files = cpp_toolchain.sysroot_files
752787

788+
# Detect if we need C++ compilation support (for source files)
789+
has_cpp_sources = any([src.extension in ["cpp", "cc", "cxx", "C", "CPP"] for src in ctx.files.srcs])
790+
needs_cpp_compilation = ctx.attr.language == "cpp" or has_cpp_sources
791+
753792
# Output library
754793
library = ctx.actions.declare_file("lib{}.a".format(ctx.attr.name))
755794

@@ -821,8 +860,8 @@ def _cc_component_library_impl(ctx):
821860
compile_args.add("-O0")
822861
compile_args.add("-g")
823862

824-
# C++ specific flags
825-
if ctx.attr.language == "cpp":
863+
# C++ specific flags (for source compilation)
864+
if needs_cpp_compilation:
826865
if ctx.attr.enable_exceptions:
827866
# Enable exceptions if specifically requested
828867
pass
@@ -837,7 +876,7 @@ def _cc_component_library_impl(ctx):
837876
compile_args.add("-I" + work_dir.path) # Workspace with staged headers
838877

839878
# Add C++ standard library paths for wasm32-wasip2 target
840-
if ctx.attr.language == "cpp":
879+
if needs_cpp_compilation:
841880
# WASI SDK stores C++ headers in share/wasi-sysroot, not just sysroot
842881
if "/external/" in sysroot_dir:
843882
toolchain_repo = sysroot_dir.split("/sysroot")[0]
@@ -897,8 +936,8 @@ def _cc_component_library_impl(ctx):
897936
arguments = [compile_args],
898937
inputs = all_inputs,
899938
outputs = [obj_file],
900-
mnemonic = "CompileCppObject",
901-
progress_message = "Compiling {} for component library".format(src.basename),
939+
mnemonic = "Compile" + ("C" if ctx.attr.language == "c" else "Cpp") + "Object",
940+
progress_message = "Compiling {} {} for component library".format(ctx.attr.language.upper(), src.basename),
902941
)
903942

904943
# Create static library
@@ -912,8 +951,8 @@ def _cc_component_library_impl(ctx):
912951
arguments = [ar_args],
913952
inputs = object_files,
914953
outputs = [library],
915-
mnemonic = "CreateCppLibrary",
916-
progress_message = "Creating component library %s" % ctx.label,
954+
mnemonic = "Create" + ("C" if ctx.attr.language == "c" else "Cpp") + "Library",
955+
progress_message = "Creating %s component library %s" % (ctx.attr.language.upper(), ctx.label),
917956
)
918957

919958
# Collect transitive headers and libraries from dependencies

examples/cpp_component/calculator/BUILD.bazel

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ cc_component_library(
2323
optimize = True,
2424
)
2525

26-
# Calculator component (C++)
26+
# Calculator component (C++ source with C bindings)
27+
# This demonstrates using C++ internally but exposing a C ABI through WIT bindings
2728
cpp_component(
2829
name = "calculator_cpp_component",
2930
package_name = "example:calculator@1.0.0",
@@ -35,7 +36,7 @@ cpp_component(
3536
],
3637
cxx_std = "c++20",
3738
enable_exceptions = False, # WASI SDK doesn't support C++ exceptions
38-
language = "cpp",
39+
language = "c", # Use C bindings (C++ source auto-detected)
3940
optimize = True,
4041
validate_wit = True, # Validate WIT compliance
4142
wit = "wit/calculator.wit",

examples/cpp_component/data_structures/BUILD.bazel

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ load("//cpp:defs.bzl", "cc_component_library", "cpp_component", "cpp_wit_bindgen
33
# WIT bindings generation
44
cpp_wit_bindgen(
55
name = "data_structures_bindings",
6+
language = "cpp",
67
wit = "wit/data_structures.wit",
78
world = "data-structures-world",
89
)
@@ -12,6 +13,7 @@ cc_component_library(
1213
name = "memory_pool",
1314
srcs = ["src/memory_pool.cpp"],
1415
hdrs = ["src/memory_pool.h"],
16+
language = "cpp",
1517
target_compatible_with = ["@platforms//cpu:wasm32"],
1618
)
1719

@@ -55,10 +57,12 @@ cc_component_library(
5557
# )
5658

5759
# Main data structures component
60+
# Note: C++ source with C bindings (uses C-style API)
5861
cpp_component(
5962
name = "data_structures_component",
6063
srcs = ["src/data_structures.cpp"],
6164
hdrs = ["src/data_structures.h"],
65+
language = "c", # C bindings (C++ source auto-detected)
6266
target_compatible_with = ["@platforms//cpu:wasm32"],
6367
validate_wit = True, # Enable WIT validation
6468
visibility = ["//visibility:public"],

examples/cpp_component/http_service/BUILD.bazel

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ load("//cpp:defs.bzl", "cc_component_library", "cpp_component", "cpp_wit_bindgen
33
# WIT bindings generation
44
cpp_wit_bindgen(
55
name = "http_service_bindings",
6+
language = "c",
67
visibility = ["//visibility:public"],
78
wit = "wit/http_service.wit",
89
world = "http-service-world",
@@ -21,10 +22,7 @@ cc_component_library(
2122
"src/request_parser.h",
2223
"src/response_builder.h",
2324
],
24-
copts = [
25-
"-x",
26-
"c",
27-
], # Force compilation as C, not C++
25+
language = "c",
2826
target_compatible_with = ["@platforms//cpu:wasm32"],
2927
visibility = ["//visibility:public"],
3028
)
@@ -45,6 +43,7 @@ cpp_component(
4543
"src/response_builder.h",
4644
],
4745
language = "c",
46+
optimize = True, # Enable optimization
4847
target_compatible_with = ["@platforms//cpu:wasm32"],
4948
validate_wit = True, # Validate WIT compliance
5049
visibility = ["//visibility:public"],

examples/cpp_component/image_processing/BUILD.bazel

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ load("//cpp:defs.bzl", "cc_component_library", "cpp_wit_bindgen")
33
# WIT bindings generation
44
cpp_wit_bindgen(
55
name = "image_processing_bindings",
6+
language = "cpp",
67
wit = "wit/image_processing.wit",
78
world = "image-processor-world",
89
)
@@ -16,6 +17,7 @@ cc_component_library(
1617
"-msimd128", # Enable WebAssembly SIMD
1718
"-O3", # Optimize for performance
1819
],
20+
language = "cpp",
1921
target_compatible_with = ["@platforms//cpu:wasm32"],
2022
)
2123

examples/cpp_component/minimal_nostdlib/BUILD.bazel

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ cpp_component(
1010
srcs = ["minimal_math.cpp"],
1111
hdrs = ["minimal_math.h"],
1212
cxx_std = "c++17",
13-
language = "cpp",
13+
language = "c", # Use C bindings (C++ source auto-detected)
1414
libs = ["m"], # Only link math library
1515
nostdlib = True,
1616
optimize = True,
@@ -24,7 +24,7 @@ cpp_component(
2424
name = "minimal_system_component",
2525
srcs = ["minimal_system.cpp"],
2626
cxx_std = "c++17",
27-
language = "cpp",
27+
language = "c", # Use C bindings (C++ source auto-detected)
2828
libs = [
2929
"m", # Math library
3030
"c", # Basic C library
@@ -42,7 +42,7 @@ cpp_component(
4242
name = "standard_component",
4343
srcs = ["standard_math.cpp"],
4444
cxx_std = "c++17",
45-
language = "cpp",
45+
language = "c", # Use C bindings (C++ source auto-detected)
4646
libs = ["m"], # Additional library on top of standard libraries
4747
nostdlib = False,
4848
optimize = True,

examples/cpp_component/multi_component_system/BUILD.bazel

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@ load("//rust:defs.bzl", "rust_wasm_component_bindgen")
44
load("//wit:defs.bzl", "wit_library")
55

66
# Simple C++ component to test build system
7+
# Note: C++ source with C bindings (uses extern "C" and C-style API)
78
cpp_component(
89
name = "auth_service_cpp",
910
srcs = ["test/simple_auth.cpp"],
11+
language = "c", # C bindings (C++ source auto-detected)
1012
target_compatible_with = ["@platforms//cpu:wasm32"],
1113
wit = "wit/auth_service.wit",
1214
world = "auth-service-world",

0 commit comments

Comments
 (0)