Skip to content

Commit 11d2477

Browse files
LegNeatoclaude
andcommitted
Fix Rust FFI build and linker issues
Changes: 1. build.rs: Only define SPIRV_RUST_TARGET_ENV when SPIRV_RUST_TARGET_ENV_DEFINE=1 (Bazel). In CMake builds, context_bridge.cc provides implementations directly. 2. context_bridge.cc: Include source/table.h and properly implement dispatch_context_message using context->consumer. This allows the Rust FFI library to provide message dispatch without depending on linker order. 3. text.cpp: Remove duplicate FFI implementations. context_bridge.cc now provides these in all build scenarios. 4. build_rust_ffi.py: Handle Windows library extension (.lib vs .a). This fixes: - Undefined reference errors in CMake builds (linker ordering) - Multiple definition errors when both files provided implementations - Bazel Windows builds failing to find the output library 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent ec2c2be commit 11d2477

4 files changed

Lines changed: 50 additions & 86 deletions

File tree

rust/scripts/build_rust_ffi.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,14 @@ def main() -> int:
6969
workdir = manifest.parent
7070
subprocess.run(command, cwd=workdir, check=True, env=env)
7171

72-
lib_name = f"lib{package.replace('-', '_')}.a"
72+
# Determine library name based on platform
73+
# Windows MSVC: spirv_tools_ffi.lib
74+
# Unix/MinGW: libspirv_tools_ffi.a
75+
lib_base = package.replace("-", "_")
76+
if sys.platform == "win32":
77+
lib_name = f"{lib_base}.lib"
78+
else:
79+
lib_name = f"lib{lib_base}.a"
7380
built_lib = target_dir / profile_dir / lib_name
7481
if not built_lib.exists():
7582
raise FileNotFoundError(f"cargo did not produce {built_lib}")

rust/spirv-tools-ffi/build.rs

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,14 +29,22 @@ fn main() {
2929
.expect("failed to copy context bridge header");
3030

3131
let mut bridge_builder = cxx_build::bridge("src/lib.rs");
32-
// Always define SPIRV_RUST_TARGET_ENV when compiling context_bridge.cc via cargo.
33-
// This ensures:
34-
// 1. We skip C++ includes that need generated headers (reducer.h, libspirv.hpp)
35-
// 2. We delegate to Rust implementations (validate_binary_rust, etc.)
36-
// 3. We don't provide duplicate implementations of dispatch_context_message
37-
// and assemble_text_with_context (text.cpp provides these in CMake builds
38-
// when SPIRV_RUST_TARGET_ENV is defined there too)
39-
bridge_builder.define("SPIRV_RUST_TARGET_ENV", None);
32+
// Define SPIRV_RUST_TARGET_ENV only for standalone Rust/Bazel builds.
33+
// This controls which implementations context_bridge.cc provides:
34+
//
35+
// - CMake builds (SPIRV_RUST_TARGET_ENV NOT defined):
36+
// context_bridge.cc provides full implementations using C++ APIs
37+
// (generated headers are available from CMake build)
38+
//
39+
// - Bazel builds (SPIRV_RUST_TARGET_ENV_DEFINE=1):
40+
// context_bridge.cc provides stub implementations
41+
// (generated headers not available, uses Rust fallbacks)
42+
//
43+
// Note: CMake also sets SPIRV_TOOLS_FFI_SKIP_CPP_LINK=1, but that only
44+
// controls link directives, not the compile-time behavior.
45+
if env::var("SPIRV_RUST_TARGET_ENV_DEFINE").is_ok() {
46+
bridge_builder.define("SPIRV_RUST_TARGET_ENV", None);
47+
}
4048
bridge_builder
4149
.file("src/context_bridge.cc")
4250
.include(repo_root)

rust/spirv-tools-ffi/src/context_bridge.cc

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#ifndef SPIRV_RUST_TARGET_ENV
1414
#include "source/reduce/reducer.h"
1515
#include "source/spirv_reducer_options.h"
16+
#include "source/table.h"
1617
#include "spirv-tools/libspirv.hpp"
1718
#endif
1819

@@ -37,19 +38,34 @@ namespace spvtools::ffi {
3738
// assemble_text_with_context are provided by source/text.cpp using internal APIs.
3839
// In standalone Rust builds (Bazel), we provide them here.
3940
#ifndef SPIRV_RUST_TARGET_ENV
41+
namespace {
42+
spv_position_t ToSpvPosition(MessagePosition position) {
43+
spv_position_t pos = {};
44+
pos.line = position.line;
45+
pos.column = position.column;
46+
pos.index = position.index;
47+
return pos;
48+
}
49+
} // namespace
50+
4051
void dispatch_context_message(std::size_t context_ptr, std::uint32_t level,
4152
bool has_source, rust::Str source,
4253
MessagePosition position, rust::Str message) {
43-
// This function is intentionally a no-op in standalone Rust builds.
44-
// Message dispatch to C++ consumers requires access to spv_context_t internals
45-
// (specifically the `consumer` callback), which requires generated headers
46-
// that are not available when compiling via Rust's build.rs.
47-
(void)context_ptr;
48-
(void)level;
49-
(void)has_source;
50-
(void)source;
51-
(void)position;
52-
(void)message;
54+
auto* context = reinterpret_cast<spv_context>(context_ptr);
55+
if (context == nullptr || !context->consumer) {
56+
return;
57+
}
58+
59+
std::string message_storage(message.data(), message.length());
60+
const char* source_ptr = nullptr;
61+
std::string source_storage;
62+
if (has_source) {
63+
source_storage.assign(source.data(), source.length());
64+
source_ptr = source_storage.c_str();
65+
}
66+
67+
context->consumer(static_cast<spv_message_level_t>(level), source_ptr,
68+
ToSpvPosition(position), message_storage.c_str());
5369
}
5470

5571
AssembleResult assemble_text_with_context(std::size_t context_ptr,

source/text.cpp

Lines changed: 0 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1071,73 +1071,6 @@ spv_result_t spvTextToBinaryWithOptions(const spv_const_context context,
10711071
sanitized_options, pBinary, pDiagnostic);
10721072
}
10731073

1074-
// FFI bridge functions for Rust integration. In CMake builds with SPIRV_RUST_TARGET_ENV,
1075-
// we provide these functions here using internal C++ APIs. In standalone Rust builds (Bazel),
1076-
// context_bridge.cc provides stubs (since generated headers aren't available there).
1077-
#if defined(SPIRV_RUST_TARGET_ENV)
1078-
namespace spvtools::ffi {
1079-
namespace {
1080-
spv_position_t ToSpvPosition(MessagePosition position) {
1081-
spv_position_t pos = {};
1082-
pos.line = position.line;
1083-
pos.column = position.column;
1084-
pos.index = position.index;
1085-
return pos;
1086-
}
1087-
} // namespace
1088-
1089-
void dispatch_context_message(std::size_t context_ptr, std::uint32_t level,
1090-
bool has_source, rust::Str source,
1091-
MessagePosition position, rust::Str message) {
1092-
auto* context = reinterpret_cast<spv_context>(context_ptr);
1093-
if (context == nullptr || !context->consumer) {
1094-
return;
1095-
}
1096-
1097-
std::string message_storage(message.data(), message.length());
1098-
const char* source_ptr = nullptr;
1099-
std::string source_storage;
1100-
if (has_source) {
1101-
source_storage.assign(source.data(), source.length());
1102-
source_ptr = source_storage.c_str();
1103-
}
1104-
1105-
context->consumer(static_cast<spv_message_level_t>(level), source_ptr,
1106-
ToSpvPosition(position), message_storage.c_str());
1107-
}
1108-
1109-
AssembleResult assemble_text_with_context(std::size_t context_ptr,
1110-
rust::Slice<const std::uint8_t> text,
1111-
std::uint32_t options) {
1112-
AssembleResult result{false, ::rust::Vec<std::uint32_t>()};
1113-
auto* context = reinterpret_cast<spv_context>(context_ptr);
1114-
if (context == nullptr) {
1115-
return result;
1116-
}
1117-
1118-
spv_binary binary = nullptr;
1119-
spv_diagnostic diagnostic = nullptr;
1120-
const char* text_ptr = reinterpret_cast<const char*>(text.data());
1121-
const spv_result_t status = AssembleTextWithDiagnostics(
1122-
context, text_ptr, text.size(), options, &binary, &diagnostic);
1123-
if (diagnostic) {
1124-
spvDiagnosticDestroy(diagnostic);
1125-
}
1126-
1127-
if (status == SPV_SUCCESS && binary != nullptr) {
1128-
result.success = true;
1129-
result.binary.reserve(binary->wordCount);
1130-
for (size_t i = 0; i < binary->wordCount; ++i) {
1131-
result.binary.push_back(binary->code[i]);
1132-
}
1133-
spvBinaryDestroy(binary);
1134-
}
1135-
1136-
return result;
1137-
}
1138-
} // namespace spvtools::ffi
1139-
#endif
1140-
11411074
void spvTextDestroy(spv_text text) {
11421075
if (text) {
11431076
if (text->str) delete[] text->str;

0 commit comments

Comments
 (0)