Support custom path to crubit/rs_bindings_from_cc/support directory.
The generated thunks and other code in ..._rs_api_impl.cc might depend
on Crubit's support headers like so:
#include "third_party/crubit/rs_bindings_from_cc/support/..."
Before this CL, `src_code_gen.rs` would hardcode the path to the support
directory. After this CL, this is replaced with an argument of a new
cmdline parameter: --crubit_support_path.
PiperOrigin-RevId: 447003312
diff --git a/rs_bindings_from_cc/bazel_support/rust_bindings_from_cc_utils.bzl b/rs_bindings_from_cc/bazel_support/rust_bindings_from_cc_utils.bzl
index 37e0d2c..81b49af 100644
--- a/rs_bindings_from_cc/bazel_support/rust_bindings_from_cc_utils.bzl
+++ b/rs_bindings_from_cc/bazel_support/rust_bindings_from_cc_utils.bzl
@@ -219,6 +219,8 @@
rs_output.path,
"--cc_out",
cc_output.path,
+ "--crubit_support_path",
+ "rs_bindings_from_cc/support",
"--rustfmt_config_path",
"external/rustfmt/rustfmt.toml",
] + _get_hdrs_command_line(public_hdrs),
diff --git a/rs_bindings_from_cc/cmdline.cc b/rs_bindings_from_cc/cmdline.cc
index c3c3d23..a9d695e 100644
--- a/rs_bindings_from_cc/cmdline.cc
+++ b/rs_bindings_from_cc/cmdline.cc
@@ -26,6 +26,10 @@
ABSL_FLAG(std::string, ir_out, "",
"(optional) output path for the JSON IR. If not present, the JSON IR "
"will not be dumped.");
+ABSL_FLAG(std::string, crubit_support_path, "",
+ "path to a the crubit/rs_bindings/support directory in a format that "
+ "should be used in the #include directives inside the generated .cc "
+ "files.");
ABSL_FLAG(std::string, rustfmt_config_path, "",
"(optional) path to a rustfmt.toml file that should replace the "
"default formatting of the .rs files generated by the tool.");
@@ -64,15 +68,16 @@
absl::StatusOr<Cmdline> Cmdline::Create() {
return CreateFromArgs(
absl::GetFlag(FLAGS_cc_out), absl::GetFlag(FLAGS_rs_out),
- absl::GetFlag(FLAGS_ir_out), absl::GetFlag(FLAGS_rustfmt_config_path),
- absl::GetFlag(FLAGS_do_nothing), absl::GetFlag(FLAGS_public_headers),
+ absl::GetFlag(FLAGS_ir_out), absl::GetFlag(FLAGS_crubit_support_path),
+ absl::GetFlag(FLAGS_rustfmt_config_path), absl::GetFlag(FLAGS_do_nothing),
+ absl::GetFlag(FLAGS_public_headers),
absl::GetFlag(FLAGS_targets_and_headers));
}
absl::StatusOr<Cmdline> Cmdline::CreateFromArgs(
std::string cc_out, std::string rs_out, std::string ir_out,
- std::string rustfmt_config_path, bool do_nothing,
- std::vector<std::string> public_headers,
+ std::string crubit_support_path, std::string rustfmt_config_path,
+ bool do_nothing, std::vector<std::string> public_headers,
std::string targets_and_headers_str) {
Cmdline cmdline;
@@ -87,6 +92,12 @@
cmdline.cc_out_ = std::move(cc_out);
cmdline.ir_out_ = std::move(ir_out);
+
+ if (crubit_support_path.empty()) {
+ return absl::InvalidArgumentError("please specify --crubit_support_path");
+ }
+ cmdline.crubit_support_path_ = std::move(crubit_support_path);
+
cmdline.rustfmt_config_path_ = std::move(rustfmt_config_path);
cmdline.do_nothing_ = do_nothing;
diff --git a/rs_bindings_from_cc/cmdline.h b/rs_bindings_from_cc/cmdline.h
index c24c178..6b5dd4b 100644
--- a/rs_bindings_from_cc/cmdline.h
+++ b/rs_bindings_from_cc/cmdline.h
@@ -26,11 +26,12 @@
// `rs_out`, and so forth.
static absl::StatusOr<Cmdline> CreateForTesting(
std::string cc_out, std::string rs_out, std::string ir_out,
- std::string rustfmt_config_path, bool do_nothing,
- std::vector<std::string> public_headers,
+ std::string crubit_support_path, std::string rustfmt_config_path,
+ bool do_nothing, std::vector<std::string> public_headers,
std::string targets_and_headers_str) {
- return CreateFromArgs(cc_out, rs_out, ir_out, rustfmt_config_path,
- do_nothing, public_headers, targets_and_headers_str);
+ return CreateFromArgs(cc_out, rs_out, ir_out, crubit_support_path,
+ rustfmt_config_path, do_nothing, public_headers,
+ targets_and_headers_str);
}
Cmdline(const Cmdline&) = delete;
@@ -41,6 +42,7 @@
absl::string_view cc_out() const { return cc_out_; }
absl::string_view rs_out() const { return rs_out_; }
absl::string_view ir_out() const { return ir_out_; }
+ absl::string_view crubit_support_path() const { return crubit_support_path_; }
absl::string_view rustfmt_config_path() const { return rustfmt_config_path_; }
bool do_nothing() const { return do_nothing_; }
@@ -60,8 +62,8 @@
static absl::StatusOr<Cmdline> CreateFromArgs(
std::string cc_out, std::string rs_out, std::string ir_out,
- std::string rustfmt_config_path, bool do_nothing,
- std::vector<std::string> public_headers,
+ std::string crubit_support_path, std::string rustfmt_config_path,
+ bool do_nothing, std::vector<std::string> public_headers,
std::string targets_and_headers_str);
absl::StatusOr<BazelLabel> FindHeader(const HeaderName& header) const;
@@ -69,6 +71,7 @@
std::string cc_out_;
std::string rs_out_;
std::string ir_out_;
+ std::string crubit_support_path_;
std::string rustfmt_config_path_;
bool do_nothing_ = true;
diff --git a/rs_bindings_from_cc/cmdline_test.cc b/rs_bindings_from_cc/cmdline_test.cc
index a25c1b3..e49d93a 100644
--- a/rs_bindings_from_cc/cmdline_test.cc
+++ b/rs_bindings_from_cc/cmdline_test.cc
@@ -25,22 +25,25 @@
absl::StatusOr<Cmdline> TestCmdline(std::vector<std::string> public_headers,
const std::string& targets_and_headers) {
- return Cmdline::CreateForTesting(
- "cc_out", "rs_out", "ir_out", "rustfmt_config_path",
- /* do_nothing= */ false, public_headers, targets_and_headers);
+ return Cmdline::CreateForTesting("cc_out", "rs_out", "ir_out",
+ "crubit_support_path", "rustfmt_config_path",
+ /* do_nothing= */ false, public_headers,
+ targets_and_headers);
}
} // namespace
TEST(CmdlineTest, BasicCorrectInput) {
- ASSERT_OK_AND_ASSIGN(Cmdline cmdline,
- Cmdline::CreateForTesting(
- "cc_out", "rs_out", "ir_out", "rustfmt_config_path",
- /* do_nothing= */ false, {"h1"},
- R"([{"t": "t1", "h": ["h1", "h2"]}])"));
+ ASSERT_OK_AND_ASSIGN(
+ Cmdline cmdline,
+ Cmdline::CreateForTesting("cc_out", "rs_out", "ir_out",
+ "crubit_support_path", "rustfmt_config_path",
+ /* do_nothing= */ false, {"h1"},
+ R"([{"t": "t1", "h": ["h1", "h2"]}])"));
EXPECT_EQ(cmdline.cc_out(), "cc_out");
EXPECT_EQ(cmdline.rs_out(), "rs_out");
EXPECT_EQ(cmdline.ir_out(), "ir_out");
+ EXPECT_EQ(cmdline.crubit_support_path(), "crubit_support_path");
EXPECT_EQ(cmdline.rustfmt_config_path(), "rustfmt_config_path");
EXPECT_EQ(cmdline.do_nothing(), false);
EXPECT_EQ(cmdline.current_target().value(), "t1");
@@ -183,7 +186,8 @@
{"t": "target1", "h": ["a.h", "b.h"]}
])";
ASSERT_THAT(Cmdline::CreateForTesting(
- /* cc_out= */ "", "rs_out", "ir_out", "rustfmt_config_path",
+ /* cc_out= */ "", "rs_out", "ir_out", "crubit_support_path",
+ "rustfmt_config_path",
/* do_nothing= */ false, {"a.h"}, kTargetsAndHeaders),
StatusIs(absl::StatusCode::kInvalidArgument,
HasSubstr("please specify --cc_out")));
@@ -194,7 +198,8 @@
{"t": "target1", "h": ["a.h", "b.h"]}
])";
ASSERT_THAT(Cmdline::CreateForTesting(
- "cc_out", /* rs_out= */ "", "ir_out", "rustfmt_config_path",
+ "cc_out", /* rs_out= */ "", "ir_out", "crubit_support_path",
+ "rustfmt_config_path",
/* do_nothing= */ false, {"a.h"}, kTargetsAndHeaders),
StatusIs(absl::StatusCode::kInvalidArgument,
HasSubstr("please specify --rs_out")));
@@ -205,16 +210,30 @@
{"t": "target1", "h": ["a.h", "b.h"]}
])";
ASSERT_OK(Cmdline::CreateForTesting(
- "cc_out", "rs_out", /* ir_out= */ "", "rustfmt_config_path",
+ "cc_out", "rs_out", /* ir_out= */ "", "crubit_support_path",
+ "rustfmt_config_path",
/* do_nothing= */ false, {"a.h"}, kTargetsAndHeaders));
}
+TEST(CmdlineTest, CrubitSupportPathEmpty) {
+ constexpr char kTargetsAndHeaders[] = R"([
+ {"t": "target1", "h": ["a.h", "b.h"]}
+ ])";
+ ASSERT_THAT(Cmdline::CreateForTesting(
+ "cc_out", "rs_out", "ir_out", /* crubit_support_path= */ "",
+ "rustfmt_config_path",
+ /* do_nothing= */ false, {"a.h"}, kTargetsAndHeaders),
+ StatusIs(absl::StatusCode::kInvalidArgument,
+ HasSubstr("please specify --crubit_support_path")));
+}
+
TEST(CmdlineTest, RustfmtTomlPathEmpty) {
constexpr char kTargetsAndHeaders[] = R"([
{"t": "target1", "h": ["a.h", "b.h"]}
])";
ASSERT_OK(Cmdline::CreateForTesting(
- "cc_out", "rs_out", "ir_out", /* rustfmt_config_path= */ "",
+ "cc_out", "rs_out", "ir_out", "crubit_support_path",
+ /* rustfmt_config_path= */ "",
/* do_nothing= */ false, {"a.h"}, kTargetsAndHeaders));
}
diff --git a/rs_bindings_from_cc/rs_bindings_from_cc.cc b/rs_bindings_from_cc/rs_bindings_from_cc.cc
index 81361b1..d540548 100644
--- a/rs_bindings_from_cc/rs_bindings_from_cc.cc
+++ b/rs_bindings_from_cc/rs_bindings_from_cc.cc
@@ -59,8 +59,8 @@
cmdline.ir_out(), std::string(llvm::formatv("{0:2}", ir.ToJson()))));
}
- crubit::Bindings bindings =
- crubit::GenerateBindings(ir, cmdline.rustfmt_config_path());
+ crubit::Bindings bindings = crubit::GenerateBindings(
+ ir, cmdline.crubit_support_path(), cmdline.rustfmt_config_path());
CRUBIT_RETURN_IF_ERROR(SetFileContents(cmdline.rs_out(), bindings.rs_api));
CRUBIT_RETURN_IF_ERROR(
SetFileContents(cmdline.cc_out(), bindings.rs_api_impl));
diff --git a/rs_bindings_from_cc/src_code_gen.cc b/rs_bindings_from_cc/src_code_gen.cc
index 590dad2..40b3078 100644
--- a/rs_bindings_from_cc/src_code_gen.cc
+++ b/rs_bindings_from_cc/src_code_gen.cc
@@ -22,6 +22,7 @@
// This function is implemented in Rust.
extern "C" FfiBindings GenerateBindingsImpl(FfiU8Slice json,
+ FfiU8Slice crubit_support_path,
FfiU8Slice rustfmt_config_path);
// Creates `Bindings` instance from copied data from `ffi_bindings`.
@@ -49,11 +50,13 @@
FreeFfiU8SliceBox(ffi_bindings.rs_api_impl);
}
-Bindings GenerateBindings(const IR& ir, absl::string_view rustfmt_config_path) {
+Bindings GenerateBindings(const IR& ir, absl::string_view crubit_support_path,
+ absl::string_view rustfmt_config_path) {
std::string json = llvm::formatv("{0}", ir.ToJson());
FfiBindings ffi_bindings = GenerateBindingsImpl(
- MakeFfiU8Slice(json), MakeFfiU8Slice(rustfmt_config_path));
+ MakeFfiU8Slice(json), MakeFfiU8Slice(crubit_support_path),
+ MakeFfiU8Slice(rustfmt_config_path));
Bindings bindings = MakeBindingsFromFfiBindings(ffi_bindings);
FreeFfiBindings(ffi_bindings);
return bindings;
diff --git a/rs_bindings_from_cc/src_code_gen.h b/rs_bindings_from_cc/src_code_gen.h
index 84ea625..fc942c7 100644
--- a/rs_bindings_from_cc/src_code_gen.h
+++ b/rs_bindings_from_cc/src_code_gen.h
@@ -21,7 +21,8 @@
};
// Generates bindings from the given `IR`.
-Bindings GenerateBindings(const IR& ir, absl::string_view rustfmt_config_path);
+Bindings GenerateBindings(const IR& ir, absl::string_view crubit_support_path,
+ absl::string_view rustfmt_config_path);
} // namespace crubit
diff --git a/rs_bindings_from_cc/src_code_gen.rs b/rs_bindings_from_cc/src_code_gen.rs
index 1a002b0..1a7b3d4 100644
--- a/rs_bindings_from_cc/src_code_gen.rs
+++ b/rs_bindings_from_cc/src_code_gen.rs
@@ -31,26 +31,31 @@
/// Expectations:
/// * `json` should be a FfiU8Slice for a valid array of bytes with the given
/// size.
+/// * `crubit_support_path` should be a FfiU8Slice for a valid array of bytes
+/// representing an UTF8-encoded string
/// * `rustfmt_config_path` should be a FfiU8Slice for a valid array of bytes
/// representing an UTF8-encoded string (without the UTF-8 requirement, it
/// seems that Rust doesn't offer a way to convert to OsString on Windows)
-/// * `json` and `rustfmt_config_path` shouldn't change during the call.
+/// * `json`, `crubit_support_path`, and `rustfmt_config_path` shouldn't
+/// change during the call.
///
/// Ownership:
/// * function doesn't take ownership of (in other words it borrows) the
-/// input params: `json`, and `rustfmt_config_path`
+/// input params: `json`, `crubit_support_path`, and `rustfmt_config_path`
/// * function passes ownership of the returned value to the caller
#[no_mangle]
pub unsafe extern "C" fn GenerateBindingsImpl(
json: FfiU8Slice,
+ crubit_support_path: FfiU8Slice,
rustfmt_config_path: FfiU8Slice,
) -> FfiBindings {
let json: &[u8] = json.as_slice();
+ let crubit_support_path: &str = std::str::from_utf8(crubit_support_path.as_slice()).unwrap();
let rustfmt_config_path: OsString =
std::str::from_utf8(rustfmt_config_path.as_slice()).unwrap().into();
catch_unwind(|| {
// It is ok to abort here.
- let Bindings { rs_api, rs_api_impl } = generate_bindings(json, &rustfmt_config_path).unwrap();
+ let Bindings { rs_api, rs_api_impl } = generate_bindings(json, crubit_support_path, &rustfmt_config_path).unwrap();
FfiBindings {
rs_api: FfiU8SliceBox::from_boxed_slice(rs_api.into_bytes().into_boxed_slice()),
rs_api_impl: FfiU8SliceBox::from_boxed_slice(
@@ -77,10 +82,15 @@
rs_api_impl: TokenStream,
}
-fn generate_bindings(json: &[u8], rustfmt_config_path: &OsStr) -> Result<Bindings> {
+fn generate_bindings(
+ json: &[u8],
+ crubit_support_path: &str,
+ rustfmt_config_path: &OsStr,
+) -> Result<Bindings> {
let ir = deserialize_ir(json)?;
- let BindingsTokens { rs_api, rs_api_impl } = generate_bindings_tokens(&ir)?;
+ let BindingsTokens { rs_api, rs_api_impl } =
+ generate_bindings_tokens(&ir, crubit_support_path)?;
let rs_api = {
let rustfmt_config = if rustfmt_config_path.is_empty() {
RustfmtConfig::default()
@@ -1404,10 +1414,10 @@
// Returns the Rust code implementing bindings, plus any auxiliary C++ code
// needed to support it.
-fn generate_bindings_tokens(ir: &IR) -> Result<BindingsTokens> {
+fn generate_bindings_tokens(ir: &IR, crubit_support_path: &str) -> Result<BindingsTokens> {
let mut items = vec![];
let mut thunks = vec![];
- let mut thunk_impls = vec![generate_rs_api_impl(ir)?];
+ let mut thunk_impls = vec![generate_rs_api_impl(ir, crubit_support_path)?];
let mut assertions = vec![];
// We import nullable pointers as an Option<&T> and assume that at the ABI
@@ -2209,7 +2219,7 @@
format_ident!("__rust_thunk__{}", func.mangled_name)
}
-fn generate_rs_api_impl(ir: &IR) -> Result<TokenStream> {
+fn generate_rs_api_impl(ir: &IR, crubit_support_path: &str) -> Result<TokenStream> {
// This function uses quote! to generate C++ source code out of convenience.
// This is a bold idea so we have to continously evaluate if it still makes
// sense or the cost of working around differences in Rust and C++ tokens is
@@ -2317,14 +2327,14 @@
standard_headers.insert(format_ident!("cstddef"));
};
- let mut includes = vec![
- "rs_bindings_from_cc/support/cxx20_backports.h",
- "rs_bindings_from_cc/support/offsetof.h",
- ];
+ let mut includes = vec!["cxx20_backports.h", "offsetof.h"]
+ .into_iter()
+ .map(|hdr| format!("{}/{}", crubit_support_path, hdr))
+ .collect_vec();
// In order to generate C++ thunk in all the cases Clang needs to be able to
// access declarations from public headers of the C++ library.
- includes.extend(ir.used_headers().map(|i| &i.name as &str));
+ includes.extend(ir.used_headers().map(|hdr| hdr.name.clone()));
Ok(quote! {
#( __HASH_TOKEN__ include <#standard_headers> __NEWLINE__)*
@@ -2360,6 +2370,10 @@
};
use token_stream_printer::{rs_tokens_to_formatted_string_for_tests, tokens_to_string};
+ fn generate_bindings_tokens(ir: &IR) -> Result<BindingsTokens> {
+ super::generate_bindings_tokens(ir, "crubit/rs_bindings_support")
+ }
+
#[test]
fn test_disable_thread_safety_warnings() -> Result<()> {
let ir = ir_from_cc("inline void foo() {}")?;
diff --git a/rs_bindings_from_cc/test/rs_bindings_from_cc_test.sh b/rs_bindings_from_cc/test/rs_bindings_from_cc_test.sh
index c4725b0..a725993 100755
--- a/rs_bindings_from_cc/test/rs_bindings_from_cc_test.sh
+++ b/rs_bindings_from_cc/test/rs_bindings_from_cc_test.sh
@@ -31,6 +31,14 @@
"\"${RS_BINDINGS_FROM_CC}\" \
--rs_out=\"${rs_out}\" \
--cc_out=\"${cc_out}\" 2>&1 | \
+ grep 'please specify --crubit_support_path' > /dev/null" \
+ "generator should show help message for --crubit_support_path"
+
+ EXPECT_SUCCEED \
+ "\"${RS_BINDINGS_FROM_CC}\" \
+ --rs_out=\"${rs_out}\" \
+ --cc_out=\"${cc_out}\" 2>&1 \
+ --crubit_support_path=test/crubit/support/path | \
grep 'please specify --public_headers' > /dev/null" \
"generator should show help message for --public_headers"
@@ -41,6 +49,7 @@
"\"${RS_BINDINGS_FROM_CC}\" \
--rs_out=\"${rs_out}\" \
--cc_out=\"${cc_out}\" \
+ --crubit_support_path=test/crubit/support/path \
--public_headers=\"${hdr}\" 2>&1 | \
grep 'please specify --targets_and_headers' > /dev/null" \
"generator should show help message for --targets_and_headers"
@@ -55,6 +64,7 @@
"\"${RS_BINDINGS_FROM_CC}\" \
--rs_out=\"${rs_out}\" \
--cc_out=\"${cc_out}\" \
+ --crubit_support_path=test/crubit/support/path \
--public_headers=\"${hdr}\" \
--targets_and_headers=\"$(echo "${json}" | quote_escape)\""
@@ -76,6 +86,7 @@
"\"${RS_BINDINGS_FROM_CC}\" \
--rs_out=\"${rs_out}\" \
--cc_out=\"${cc_out}\" \
+ --crubit_support_path=test/crubit/support/path \
--public_headers=\"${hdr}\" \
--targets_and_headers=\"$(echo "${json}" | quote_escape)\" \
--do_nothing"
@@ -105,6 +116,7 @@
"\"${RS_BINDINGS_FROM_CC}\" \
--rs_out=\"${rs_out}\" \
--cc_out=\"${cc_out}\" \
+ --crubit_support_path=test/crubit/support/path \
--public_headers=\"${hdr}\" \
--targets_and_headers=\"$(echo "${json}" | quote_escape)\" 2>&1"
@@ -132,6 +144,7 @@
"\"${RS_BINDINGS_FROM_CC}\" \
--rs_out=\"${rs_out}\" \
--cc_out=\"${cc_out}\" \
+ --crubit_support_path=test/crubit/support/path \
--public_headers=\"${header_1},${header_2}\" \
--targets_and_headers=\"$(echo "${json}" | quote_escape)\" 2>&1"
@@ -158,6 +171,7 @@
"\"${RS_BINDINGS_FROM_CC}\" \
--rs_out=\"${rs_out}\" \
--cc_out=\"${cc_out}\" \
+ --crubit_support_path=test/crubit/support/path \
--public_headers=\"${hdr}\" \
--targets_and_headers=\"$(echo "${json}" | quote_escape)\""
@@ -184,6 +198,7 @@
"\"${RS_BINDINGS_FROM_CC}\" \
--rs_out=\"${rs_out}\" \
--cc_out=\"${cc_out}\" \
+ --crubit_support_path=test/crubit/support/path \
--rustfmt_config_path=\"${rustfmt_config_path}\" \
--public_headers=\"${hdr}\" \
--targets_and_headers=\"$(echo "${json}" | quote_escape)\""
@@ -203,4 +218,31 @@
"Verify function args are *not on single line when using default rustfmt config (2)"
}
+function test::crubit_support_path() {
+ local rs_out="${TEST_TMPDIR}/rs_api.rs"
+ local cc_out="${TEST_TMPDIR}/rs_api_impl.cc"
+
+ local hdr="${TEST_TMPDIR}/hello_world.h"
+ echo "int MyFunction();" > "${hdr}"
+
+ local json
+ json="$(cat <<-EOT
+ [{"t": "//foo/bar:baz", "h": ["${hdr}"]}]
+EOT
+)"
+
+ EXPECT_SUCCEED \
+ "\"${RS_BINDINGS_FROM_CC}\" \
+ --rs_out=\"${rs_out}\" \
+ --cc_out=\"${cc_out}\" \
+ --crubit_support_path=test/specific/crubit/support/path \
+ --public_headers=\"${hdr}\" \
+ --targets_and_headers=\"$(echo "${json}" | quote_escape)\""
+
+ EXPECT_FILE_NOT_EMPTY "${cc_out}"
+ EXPECT_SUCCEED \
+ "grep \"#include.*test/specific/crubit/support/path/[a-z]\" \"${cc_out}\"" \
+ "Verify #include paths are based on the argument of --crubit_support_path"
+}
+
gbash::unit::main "$@"