Add the namespace hierarchy to BindingsAndMetadata This cl also adds a --namespaces_json flag to Crubit for writing a .json file with the namespace contents. Bazel support for it will be provided in a followup cl. PiperOrigin-RevId: 471791828
diff --git a/rs_bindings_from_cc/BUILD b/rs_bindings_from_cc/BUILD index 5fb2839..8a3110f 100644 --- a/rs_bindings_from_cc/BUILD +++ b/rs_bindings_from_cc/BUILD
@@ -67,6 +67,7 @@ ":bazel_types", ":cc_ir", ":cmdline", + ":collect_namespaces", ":generate_bindings_and_metadata", "//common:file_io", "//common:rust_allocator_shims", @@ -88,6 +89,7 @@ ":cc_collect_instantiations", ":cc_ir", ":cmdline", + ":collect_namespaces", ":ir_from_cc", ":src_code_gen", "//common:status_macros", @@ -101,6 +103,7 @@ deps = [ ":cc_ir", ":cmdline", + ":collect_namespaces", ":generate_bindings_and_metadata", "//common:rust_allocator_shims", "//common:test_utils",
diff --git a/rs_bindings_from_cc/cmdline.cc b/rs_bindings_from_cc/cmdline.cc index 50a6545..707ccac 100644 --- a/rs_bindings_from_cc/cmdline.cc +++ b/rs_bindings_from_cc/cmdline.cc
@@ -57,6 +57,9 @@ "[template instantiation mode only] output path for the JSON file " "with mapping from a template instantiation to a generated Rust " "struct name. This file is used by cc_template! macro expansion."); +ABSL_FLAG(std::string, namespaces_out, "", + "(optional) output path for the JSON file containing the target's" + "namespace hierarchy."); namespace crubit { @@ -78,7 +81,8 @@ 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_crubit_support_path), + absl::GetFlag(FLAGS_ir_out), absl::GetFlag(FLAGS_namespaces_out), + absl::GetFlag(FLAGS_crubit_support_path), absl::GetFlag(FLAGS_rustfmt_exe_path), absl::GetFlag(FLAGS_rustfmt_config_path), absl::GetFlag(FLAGS_do_nothing), absl::GetFlag(FLAGS_public_headers), @@ -89,9 +93,9 @@ absl::StatusOr<Cmdline> Cmdline::CreateFromArgs( std::string cc_out, std::string rs_out, std::string ir_out, - std::string crubit_support_path, std::string rustfmt_exe_path, - std::string rustfmt_config_path, bool do_nothing, - std::vector<std::string> public_headers, + std::string namespaces_out, std::string crubit_support_path, + std::string rustfmt_exe_path, std::string rustfmt_config_path, + bool do_nothing, std::vector<std::string> public_headers, std::string targets_and_headers_str, std::vector<std::string> rust_sources, std::string instantiations_out) { Cmdline cmdline; @@ -108,6 +112,8 @@ cmdline.ir_out_ = std::move(ir_out); + cmdline.namespaces_out_ = std::move(namespaces_out); + if (crubit_support_path.empty()) { return absl::InvalidArgumentError("please specify --crubit_support_path"); }
diff --git a/rs_bindings_from_cc/cmdline.h b/rs_bindings_from_cc/cmdline.h index b7fbd41..44091ca 100644 --- a/rs_bindings_from_cc/cmdline.h +++ b/rs_bindings_from_cc/cmdline.h
@@ -26,17 +26,17 @@ // `rs_out`, and so forth. static absl::StatusOr<Cmdline> CreateForTesting( std::string cc_out, std::string rs_out, std::string ir_out, - std::string crubit_support_path, std::string rustfmt_exe_path, - std::string rustfmt_config_path, bool do_nothing, - std::vector<std::string> public_headers, + std::string namespaces_out, std::string crubit_support_path, + std::string rustfmt_exe_path, std::string rustfmt_config_path, + bool do_nothing, std::vector<std::string> public_headers, std::string targets_and_headers_str, std::vector<std::string> rust_sources, std::string instantiations_out) { return CreateFromArgs( std::move(cc_out), std::move(rs_out), std::move(ir_out), - std::move(crubit_support_path), std::move(rustfmt_exe_path), - std::move(rustfmt_config_path), do_nothing, std::move(public_headers), - std::move(targets_and_headers_str), std::move(rust_sources), - std::move(instantiations_out)); + std::move(namespaces_out), std::move(crubit_support_path), + std::move(rustfmt_exe_path), std::move(rustfmt_config_path), do_nothing, + std::move(public_headers), std::move(targets_and_headers_str), + std::move(rust_sources), std::move(instantiations_out)); } Cmdline(const Cmdline&) = delete; @@ -47,6 +47,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 namespaces_out() const { return namespaces_out_; } absl::string_view crubit_support_path() const { return crubit_support_path_; } absl::string_view rustfmt_exe_path() const { return rustfmt_exe_path_; } absl::string_view rustfmt_config_path() const { return rustfmt_config_path_; } @@ -71,9 +72,9 @@ static absl::StatusOr<Cmdline> CreateFromArgs( std::string cc_out, std::string rs_out, std::string ir_out, - std::string crubit_support_path, std::string rustfmt_exe_path, - std::string rustfmt_config_path, bool do_nothing, - std::vector<std::string> public_headers, + std::string namespaces_out, std::string crubit_support_path, + std::string rustfmt_exe_path, std::string rustfmt_config_path, + bool do_nothing, std::vector<std::string> public_headers, std::string targets_and_headers_str, std::vector<std::string> rust_sources, std::string instantiations_out); @@ -93,6 +94,8 @@ std::string instantiations_out_; std::vector<std::string> rust_sources_; + + std::string namespaces_out_; }; } // namespace crubit
diff --git a/rs_bindings_from_cc/cmdline_test.cc b/rs_bindings_from_cc/cmdline_test.cc index a4f753f..0753cd1 100644 --- a/rs_bindings_from_cc/cmdline_test.cc +++ b/rs_bindings_from_cc/cmdline_test.cc
@@ -26,8 +26,8 @@ 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", "crubit_support_path", "rustfmt_exe_path", - "rustfmt_config_path", + "cc_out", "rs_out", "ir_out", "namespaces_out", "crubit_support_path", + "rustfmt_exe_path", "rustfmt_config_path", /* do_nothing= */ false, std::move(public_headers), std::move(targets_and_headers), /* rust_sources= */ {}, @@ -37,16 +37,18 @@ } // namespace TEST(CmdlineTest, BasicCorrectInput) { - ASSERT_OK_AND_ASSIGN(Cmdline cmdline, - Cmdline::CreateForTesting( - "cc_out", "rs_out", "ir_out", "crubit_support_path", - "rustfmt_exe_path", "rustfmt_config_path", - /* do_nothing= */ false, {"h1"}, - R"([{"t": "t1", "h": ["h1", "h2"]}])", {"lib.rs"}, - "instantiations_out")); + ASSERT_OK_AND_ASSIGN( + Cmdline cmdline, + Cmdline::CreateForTesting("cc_out", "rs_out", "ir_out", "namespaces_out", + "crubit_support_path", "rustfmt_exe_path", + "rustfmt_config_path", + /* do_nothing= */ false, {"h1"}, + R"([{"t": "t1", "h": ["h1", "h2"]}])", + {"lib.rs"}, "instantiations_out")); EXPECT_EQ(cmdline.cc_out(), "cc_out"); EXPECT_EQ(cmdline.rs_out(), "rs_out"); EXPECT_EQ(cmdline.ir_out(), "ir_out"); + EXPECT_EQ(cmdline.namespaces_out(), "namespaces_out"); EXPECT_EQ(cmdline.crubit_support_path(), "crubit_support_path"); EXPECT_EQ(cmdline.rustfmt_exe_path(), "rustfmt_exe_path"); EXPECT_EQ(cmdline.rustfmt_config_path(), "rustfmt_config_path"); @@ -194,7 +196,7 @@ {"t": "target1", "h": ["a.h", "b.h"]} ])"; ASSERT_THAT( - (Cmdline::CreateForTesting("cc_out", "rs_out", "ir_out", + (Cmdline::CreateForTesting("cc_out", "rs_out", "ir_out", "namespaces_out", "crubit_support_path", "rustfmt_exe_path", "rustfmt_config_path", /* do_nothing= */ false, {"a.h"}, @@ -213,7 +215,7 @@ ])"; ASSERT_THAT( Cmdline::CreateForTesting( - "cc_out", "rs_out", "ir_out", "crubit_support_path", + "cc_out", "rs_out", "ir_out", "namespaces_out", "crubit_support_path", "rustfmt_exe_path", "rustfmt_config_path", /* do_nothing= */ false, {"a.h"}, std::string(kTargetsAndHeaders), /* rust_sources= */ {}, "instantiations_out"), @@ -228,14 +230,15 @@ constexpr absl::string_view kTargetsAndHeaders = R"([ {"t": "target1", "h": ["a.h", "b.h"]} ])"; - ASSERT_THAT(Cmdline::CreateForTesting( - /* cc_out= */ "", "rs_out", "ir_out", "crubit_support_path", - "rustfmt_exe_path", "rustfmt_config_path", - /* do_nothing= */ false, {"a.h"}, - std::string(kTargetsAndHeaders), /* rust_sources= */ {}, - /* instantiations_out= */ ""), - StatusIs(absl::StatusCode::kInvalidArgument, - HasSubstr("please specify --cc_out"))); + ASSERT_THAT( + Cmdline::CreateForTesting( + /* cc_out= */ "", "rs_out", "ir_out", "namespaces_out", + "crubit_support_path", "rustfmt_exe_path", "rustfmt_config_path", + /* do_nothing= */ false, {"a.h"}, std::string(kTargetsAndHeaders), + /* rust_sources= */ {}, + /* instantiations_out= */ ""), + StatusIs(absl::StatusCode::kInvalidArgument, + HasSubstr("please specify --cc_out"))); } TEST(CmdlineTest, RsOutEmpty) { @@ -244,8 +247,8 @@ ])"; ASSERT_THAT( Cmdline::CreateForTesting( - "cc_out", /* rs_out= */ "", "ir_out", "crubit_support_path", - "rustfmt_exe_path", "rustfmt_config_path", + "cc_out", /* rs_out= */ "", "namespaces_out", "ir_out", + "crubit_support_path", "rustfmt_exe_path", "rustfmt_config_path", /* do_nothing= */ false, {"a.h"}, std::string(kTargetsAndHeaders), /* rust_sources= */ {}, /* instantiations_out= */ ""), @@ -258,8 +261,8 @@ {"t": "target1", "h": ["a.h", "b.h"]} ])"; ASSERT_OK(Cmdline::CreateForTesting( - "cc_out", "rs_out", /* ir_out= */ "", "crubit_support_path", - "rustfmt_exe_path", "rustfmt_config_path", + "cc_out", "rs_out", /* ir_out= */ "", "namespaces_out", + "crubit_support_path", "rustfmt_exe_path", "rustfmt_config_path", /* do_nothing= */ false, {"a.h"}, std::string(kTargetsAndHeaders), /* rust_sources= */ {}, /* instantiations_out= */ "")); @@ -271,7 +274,7 @@ ])"; ASSERT_THAT( Cmdline::CreateForTesting( - "cc_out", "rs_out", "ir_out", "crubit_support_path", + "cc_out", "rs_out", "ir_out", "namespaces_out", "crubit_support_path", /* rustfmt_exe_path= */ "", "rustfmt_config_path", /* do_nothing= */ false, {"a.h"}, std::string(kTargetsAndHeaders), /* rust_sources= */ {},
diff --git a/rs_bindings_from_cc/collect_namespaces.cc b/rs_bindings_from_cc/collect_namespaces.cc index b921496..8b2bfd0 100644 --- a/rs_bindings_from_cc/collect_namespaces.cc +++ b/rs_bindings_from_cc/collect_namespaces.cc
@@ -79,14 +79,14 @@ } } - // Converts a trie node into the JSON serializable NamespaceForCcImport. - NamespaceForCcImport NodeToNamespaceForCcImport(const Node* node) const { - std::vector<NamespaceForCcImport> namespaces; + // Converts a trie node into the JSON serializable NamespaceNode. + NamespaceNode NodeToNamespaceNode(const Node* node) const { + std::vector<NamespaceNode> namespaces; namespaces.reserve(node->child_name_to_idx.size()); for (const auto& [_, idx] : node->child_name_to_idx) { - namespaces.push_back(NodeToNamespaceForCcImport(&trie_nodes_[idx])); + namespaces.push_back(NodeToNamespaceNode(&trie_nodes_[idx])); } - return NamespaceForCcImport{std::string(node->name), std::move(namespaces)}; + return NamespaceNode{std::string(node->name), std::move(namespaces)}; } public: @@ -118,21 +118,21 @@ } } - // Converts the trie into the JSON serializable AllNamespacesForCcImport. - AllNamespacesForCcImport ToAllNamespacesForCcImport() { - std::vector<NamespaceForCcImport> namespaces; + // Converts the trie into the JSON serializable NamespacesHierarchy. + NamespacesHierarchy ToNamespacesHierarchy() { + std::vector<NamespaceNode> namespaces; namespaces.reserve(this->top_level_name_to_idx_.size()); for (auto& [_, idx] : this->top_level_name_to_idx_) { - namespaces.push_back(NodeToNamespaceForCcImport(&trie_nodes_[idx])); + namespaces.push_back(NodeToNamespaceNode(&trie_nodes_[idx])); } - return AllNamespacesForCcImport{std::move(namespaces)}; + return NamespacesHierarchy{std::move(namespaces)}; } }; } // namespace // Returns the current target's namespace hierarchy in JSON serializable format. -AllNamespacesForCcImport CollectNamespaces(const IR& ir) { +NamespacesHierarchy CollectNamespaces(const IR& ir) { auto all_namespaces = ir.get_items_if<Namespace>(); absl::flat_hash_map<ItemId, const Namespace*> id_to_namespace; for (auto ns : all_namespaces) { @@ -152,10 +152,10 @@ trie.InsertTopLevel(ns); } - return trie.ToAllNamespacesForCcImport(); + return trie.ToNamespacesHierarchy(); } -llvm::json::Value NamespaceForCcImport::ToJson() const { +llvm::json::Value NamespaceNode::ToJson() const { std::vector<llvm::json::Value> json_children; json_children.reserve(children.size()); for (const auto& child : children) { @@ -172,7 +172,7 @@ }; } -llvm::json::Value AllNamespacesForCcImport::ToJson() const { +llvm::json::Value NamespacesHierarchy::ToJson() const { std::vector<llvm::json::Value> json_namespaces; json_namespaces.reserve(namespaces.size()); for (const auto& ns : namespaces) {
diff --git a/rs_bindings_from_cc/collect_namespaces.h b/rs_bindings_from_cc/collect_namespaces.h index 6ef5cc1..d244ed5 100644 --- a/rs_bindings_from_cc/collect_namespaces.h +++ b/rs_bindings_from_cc/collect_namespaces.h
@@ -17,34 +17,33 @@ // stores the names of the namespace children, as it is the only information // that the cc_import! macro needs in order to be able to merge namespaces // across targets. -struct NamespaceForCcImport { +struct NamespaceNode { llvm::json::Value ToJson() const; std::string name; - std::vector<NamespaceForCcImport> children; + std::vector<NamespaceNode> children; }; -inline std::ostream& operator<<(std::ostream& o, - const NamespaceForCcImport& ns) { +inline std::ostream& operator<<(std::ostream& o, const NamespaceNode& ns) { return o << std::string(llvm::formatv("{0:2}", ns.ToJson())); } // Representation of all C++ namespaces within the current target. -struct AllNamespacesForCcImport { +struct NamespacesHierarchy { llvm::json::Value ToJson() const; - std::vector<NamespaceForCcImport> namespaces; + std::vector<NamespaceNode> namespaces; }; inline std::ostream& operator<<(std::ostream& o, - const AllNamespacesForCcImport& all) { + const NamespacesHierarchy& all) { return o << std::string(llvm::formatv("{0:2}", all.ToJson())); } // Returns the current target's namespace hierarchy in JSON serializable format. -AllNamespacesForCcImport CollectNamespaces(const IR& ir); +NamespacesHierarchy CollectNamespaces(const IR& ir); -inline std::string NamespacesAsJson(const AllNamespacesForCcImport& topLevel) { +inline std::string NamespacesAsJson(const NamespacesHierarchy& topLevel) { return llvm::formatv("{0:2}", topLevel.ToJson()); } } // namespace crubit
diff --git a/rs_bindings_from_cc/generate_bindings_and_metadata.cc b/rs_bindings_from_cc/generate_bindings_and_metadata.cc index 90d6036..d43144e 100644 --- a/rs_bindings_from_cc/generate_bindings_and_metadata.cc +++ b/rs_bindings_from_cc/generate_bindings_and_metadata.cc
@@ -6,6 +6,7 @@ #include "common/status_macros.h" #include "rs_bindings_from_cc/collect_instantiations.h" +#include "rs_bindings_from_cc/collect_namespaces.h" #include "rs_bindings_from_cc/ir_from_cc.h" #include "rs_bindings_from_cc/src_code_gen.h" @@ -36,10 +37,13 @@ cmdline.rustfmt_exe_path(), cmdline.rustfmt_config_path())); + auto top_level_namespaces = crubit::CollectNamespaces(ir); + return BindingsAndMetadata{ .ir = ir, .rs_api = bindings.rs_api, .rs_api_impl = bindings.rs_api_impl, + .namespaces = top_level_namespaces, }; }
diff --git a/rs_bindings_from_cc/generate_bindings_and_metadata.h b/rs_bindings_from_cc/generate_bindings_and_metadata.h index 5140c0a..b641d9d 100644 --- a/rs_bindings_from_cc/generate_bindings_and_metadata.h +++ b/rs_bindings_from_cc/generate_bindings_and_metadata.h
@@ -10,6 +10,7 @@ #include "absl/status/statusor.h" #include "rs_bindings_from_cc/cmdline.h" +#include "rs_bindings_from_cc/collect_namespaces.h" #include "rs_bindings_from_cc/ir.h" namespace crubit { @@ -22,6 +23,8 @@ std::string rs_api; // Generated C++ source code. std::string rs_api_impl; + // A hierarchy tree for all C++ namespaces used in the target. + NamespacesHierarchy namespaces; }; // Returns `BindingsAndMetadata` as requested by the user on the command line.
diff --git a/rs_bindings_from_cc/generate_bindings_and_metadata_test.cc b/rs_bindings_from_cc/generate_bindings_and_metadata_test.cc index c89fb8c..193488a 100644 --- a/rs_bindings_from_cc/generate_bindings_and_metadata_test.cc +++ b/rs_bindings_from_cc/generate_bindings_and_metadata_test.cc
@@ -8,6 +8,7 @@ #include "gtest/gtest.h" #include "common/test_utils.h" #include "rs_bindings_from_cc/cmdline.h" +#include "rs_bindings_from_cc/collect_namespaces.h" #include "rs_bindings_from_cc/ir.h" namespace crubit { @@ -26,7 +27,7 @@ ASSERT_OK_AND_ASSIGN( Cmdline cmdline, Cmdline::CreateForTesting( - "cc_out", "rs_out", "ir_out", "crubit_support_path", + "cc_out", "rs_out", "ir_out", "namespaces_out", "crubit_support_path", std::string(kDefaultRustfmtExePath), "nowhere/rustfmt.toml", /* do_nothing= */ false, /* public_headers= */ {"a.h"}, std::string(kTargetsAndHeaders), @@ -54,7 +55,7 @@ ASSERT_OK_AND_ASSIGN( Cmdline cmdline, Cmdline::CreateForTesting( - "cc_out", "rs_out", "ir_out", "crubit_support_path", + "cc_out", "rs_out", "ir_out", "namespaces_out", "crubit_support_path", std::string(kDefaultRustfmtExePath), "nowhere/rustfmt.toml", /* do_nothing= */ false, /* public_headers= */ {"a.h"}, std::string(kTargetsAndHeaders), @@ -79,7 +80,7 @@ ASSERT_OK_AND_ASSIGN( Cmdline cmdline, Cmdline::CreateForTesting( - "cc_out", "rs_out", "ir_out", "crubit_support_path", + "cc_out", "rs_out", "ir_out", "namespaces_out", "crubit_support_path", std::string(kDefaultRustfmtExePath), "nowhere/rustfmt.toml", /* do_nothing= */ false, /* public_headers= */ {"a.h"}, std::string(kTargetsAndHeaders), @@ -96,5 +97,87 @@ ASSERT_THAT(InstantiationsAsJson(result.ir), StrEq("{}")); } +TEST(GenerateBindingsAndMetadataTest, NamespacesJsonGenerated) { + constexpr absl::string_view kTargetsAndHeaders = R"([ + {"t": "target1", "h": ["a.h"]} + ])"; + constexpr absl::string_view kHeaderContent = R"( + namespace top_level_1 { + namespace middle { + namespace inner_1 {} + } + namespace middle { + namespace inner_2 {} + } + } + + namespace top_level_2 { + namespace inner_3 {} + } + + namespace top_level_1 {} + )"; + constexpr absl::string_view kExpected = R"({ + "namespaces": [ + { + "namespace": { + "children": [ + { + "namespace": { + "children": [ + { + "namespace": { + "children": [], + "name": "inner_1" + } + }, + { + "namespace": { + "children": [], + "name": "inner_2" + } + } + ], + "name": "middle" + } + } + ], + "name": "top_level_1" + } + }, + { + "namespace": { + "children": [ + { + "namespace": { + "children": [], + "name": "inner_3" + } + } + ], + "name": "top_level_2" + } + } + ] +})"; + + ASSERT_OK_AND_ASSIGN( + Cmdline cmdline, + Cmdline::CreateForTesting( + "cc_out", "rs_out", "ir_out", "namespaces_json", + "crubit_support_path", std::string(kDefaultRustfmtExePath), + "nowhere/rustfmt.toml", + /* do_nothing= */ false, + /* public_headers= */ {"a.h"}, std::string(kTargetsAndHeaders), + /* rust_sources= */ {}, /* instantiations_out= */ "")); + ASSERT_OK_AND_ASSIGN(BindingsAndMetadata result, + GenerateBindingsAndMetadata( + cmdline, DefaultClangArgs(), + /* virtual_headers_contents= */ + {{HeaderName("a.h"), std::string(kHeaderContent)}})); + + ASSERT_THAT(NamespacesAsJson(result.namespaces), StrEq(kExpected)); +} + } // namespace } // namespace crubit
diff --git a/rs_bindings_from_cc/rs_bindings_from_cc.cc b/rs_bindings_from_cc/rs_bindings_from_cc.cc index 1383fdc..fef6583 100644 --- a/rs_bindings_from_cc/rs_bindings_from_cc.cc +++ b/rs_bindings_from_cc/rs_bindings_from_cc.cc
@@ -17,7 +17,9 @@ #include "common/file_io.h" #include "common/status_macros.h" #include "rs_bindings_from_cc/cmdline.h" +#include "rs_bindings_from_cc/collect_namespaces.h" #include "rs_bindings_from_cc/generate_bindings_and_metadata.h" +#include "rs_bindings_from_cc/ir.h" #include "llvm/Support/raw_ostream.h" namespace crubit { @@ -38,6 +40,9 @@ CRUBIT_RETURN_IF_ERROR( SetFileContents(cmdline.instantiations_out(), "[]")); } + if (!cmdline.namespaces_out().empty()) { + CRUBIT_RETURN_IF_ERROR(SetFileContents(cmdline.namespaces_out(), "[]")); + } return absl::OkStatus(); } @@ -64,6 +69,12 @@ crubit::InstantiationsAsJson(bindings_and_metadata.ir))); } + if (!cmdline.namespaces_out().empty()) { + CRUBIT_RETURN_IF_ERROR(SetFileContents( + cmdline.namespaces_out(), + crubit::NamespacesAsJson(bindings_and_metadata.namespaces))); + } + return absl::OkStatus(); }