Add support for proto_library with more than one srcs

If a proto_library has more than one srcs, we designate the first one as the primary (that file will be passed to rustc as the crate root). All other files will represent (internal) submodules of the crate.

In general, Rust users won't see which .proto file a message came from, they will only see a crate corresponding to the entire proto_library, and in it public submodules for all `package` statements in all .proto files in the proto_library sources. Therefore in this CL we reexport all messages from non primary sources into their corresponding public modules (= packages declared in their owning .proto files).

Besides the common case this CL also handles .proto files without package statement, and a subset of behaviors needed for public import functionality.

PiperOrigin-RevId: 549543321
pull/13363/head
Marcel Hlopko 2 years ago committed by Copybara-Service
parent 4f67e28fca
commit 3ced818640
  1. 57
      rust/test/BUILD
  2. 2
      rust/test/no_package.proto
  3. 35
      rust/test/no_package_import.proto
  4. 35
      rust/test/no_package_other.proto
  5. 37
      rust/test/package.proto
  6. 35
      rust/test/package_import.proto
  7. 35
      rust/test/package_other.proto
  8. 35
      rust/test/package_other_different.proto
  9. 4
      rust/test/shared/BUILD
  10. 24
      rust/test/shared/package_test.rs
  11. 25
      src/google/protobuf/compiler/rust/BUILD.bazel
  12. 198
      src/google/protobuf/compiler/rust/generator.cc
  13. 10
      src/google/protobuf/compiler/rust/naming.cc
  14. 1
      src/google/protobuf/compiler/rust/naming.h
  15. 103
      src/google/protobuf/compiler/rust/relative_path.cc
  16. 77
      src/google/protobuf/compiler/rust/relative_path.h
  17. 67
      src/google/protobuf/compiler/rust/relative_path_test.cc

@ -144,10 +144,21 @@ rust_upb_proto_library(
deps = [":dots_in_package_proto"],
)
proto_library(
name = "no_package_import_proto",
testonly = True,
srcs = ["no_package_import.proto"],
)
proto_library(
name = "no_package_proto",
testonly = True,
srcs = ["no_package.proto"],
srcs = [
"no_package.proto",
"no_package_other.proto",
],
exports = [":no_package_import_proto"],
deps = [":no_package_import_proto"],
)
cc_proto_library(
@ -176,6 +187,50 @@ rust_upb_proto_library(
deps = [":no_package_proto"],
)
proto_library(
name = "package_import_proto",
testonly = True,
srcs = ["package_import.proto"],
)
proto_library(
name = "package_proto",
testonly = True,
srcs = [
"package.proto",
"package_other.proto",
"package_other_different.proto",
],
exports = [":package_import_proto"],
deps = [":package_import_proto"],
)
cc_proto_library(
name = "package_cc_proto",
testonly = True,
deps = [":package_proto"],
)
rust_cc_proto_library(
name = "package_cc_rust_proto",
testonly = True,
visibility = [
"//rust/test/cpp:__subpackages__",
"//rust/test/shared:__subpackages__",
],
deps = [":package_cc_proto"],
)
rust_upb_proto_library(
name = "package_upb_rust_proto",
testonly = True,
visibility = [
"//rust/test/shared:__subpackages__",
"//rust/test/upb:__subpackages__",
],
deps = [":package_proto"],
)
proto_library(
name = "reserved_proto",
testonly = True,

@ -32,4 +32,6 @@ syntax = "proto2";
// package intentionally left unspecified.
import public "google/protobuf/rust/test/no_package_import.proto";
message MsgWithoutPackage {}

@ -0,0 +1,35 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2023 Google LLC. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google LLC. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
syntax = "proto2";
// package intentionally left unspecified.
message ImportedMsgWithoutPackage {}

@ -0,0 +1,35 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2023 Google LLC. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google LLC. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
syntax = "proto2";
// package intentionally left unspecified.
message OtherMsgWithoutPackage {}

@ -0,0 +1,37 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2023 Google LLC. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google LLC. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
syntax = "proto2";
package testing_packages;
import public "google/protobuf/rust/test/package_import.proto";
message MsgWithPackage {}

@ -0,0 +1,35 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2023 Google LLC. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google LLC. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
syntax = "proto2";
package testing_packages;
message ImportedMsgWithPackage {}

@ -0,0 +1,35 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2023 Google LLC. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google LLC. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
syntax = "proto2";
package testing_packages;
message OtherMsgWithPackage {}

@ -0,0 +1,35 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2023 Google LLC. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google LLC. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
syntax = "proto2";
package testing_other_packages;
message OtherMsgInDifferentPackage {}

@ -40,7 +40,7 @@ rust_test(
deps = [
"//rust/test:dots_in_package_cc_rust_proto",
"//rust/test:no_package_cc_rust_proto",
"//rust/test:unittest_cc_rust_proto",
"//rust/test:package_cc_rust_proto",
],
)
@ -50,7 +50,7 @@ rust_test(
deps = [
"//rust/test:dots_in_package_upb_rust_proto",
"//rust/test:no_package_upb_rust_proto",
"//rust/test:unittest_upb_rust_proto",
"//rust/test:package_upb_rust_proto",
],
)

@ -31,16 +31,20 @@
/// Tests covering proto packages.
#[test]
fn test_package_specified() {
let _foo: unittest_proto::proto2_unittest::TestAllTypes;
}
#[test]
fn test_empty_package() {
fn test_packages() {
// empty package, message declared in the first .proto source
let _foo: no_package_proto::MsgWithoutPackage;
}
// empty package, message declared in other .proto source
let _foo: no_package_proto::OtherMsgWithoutPackage;
// empty package, import public of a message
let _foo: no_package_proto::ImportedMsgWithoutPackage;
#[test]
fn test_dots_in_package() {
let _foo: dots_in_package_proto::package::uses::dots::submodule::separator::Msg;
// package, message declared in the first .proto source
let _foo: package_proto::testing_packages::MsgWithPackage;
// package, message declared in the other .proto source with the same package
let _foo: package_proto::testing_packages::OtherMsgWithPackage;
// package, message declared in the other .proto source with a different package
let _foo: package_proto::testing_other_packages::OtherMsgInDifferentPackage;
// package, import public of a message
let _foo: package_proto::testing_packages::ImportedMsgWithPackage;
}

@ -19,6 +19,7 @@ cc_library(
":context",
":message",
":naming",
":relative_path",
"//src/google/protobuf:protobuf_nowkt",
"//src/google/protobuf/compiler:code_generator",
"//src/google/protobuf/compiler/cpp:names",
@ -95,4 +96,26 @@ cc_library(
"@com_google_absl//absl/log:absl_log",
"@com_google_absl//absl/strings",
],
)
)
cc_library(
name = "relative_path",
srcs = ["relative_path.cc"],
hdrs = ["relative_path.h"],
include_prefix = "google/protobuf/compiler/rust",
deps = [
"@com_google_absl//absl/algorithm:container",
"@com_google_absl//absl/log:absl_check",
"@com_google_absl//absl/strings",
],
)
cc_test(
name = "relative_path_test",
srcs = ["relative_path_test.cc"],
deps = [
":relative_path",
"@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)

@ -30,19 +30,25 @@
#include "google/protobuf/compiler/rust/generator.h"
#include <iterator>
#include <memory>
#include <string>
#include <vector>
#include "absl/algorithm/container.h"
#include "absl/container/btree_map.h"
#include "absl/container/flat_hash_set.h"
#include "absl/memory/memory.h"
#include "absl/status/statusor.h"
#include "absl/strings/str_split.h"
#include "absl/strings/string_view.h"
#include "absl/types/span.h"
#include "google/protobuf/compiler/code_generator.h"
#include "google/protobuf/compiler/cpp/names.h"
#include "google/protobuf/compiler/rust/context.h"
#include "google/protobuf/compiler/rust/message.h"
#include "google/protobuf/compiler/rust/naming.h"
#include "google/protobuf/compiler/rust/relative_path.h"
#include "google/protobuf/descriptor.h"
#include "google/protobuf/descriptor.pb.h"
#include "google/protobuf/io/printer.h"
@ -53,9 +59,22 @@ namespace protobuf {
namespace compiler {
namespace rust {
namespace {
void EmitOpeningOfPackageModules(Context<FileDescriptor> file) {
if (file.desc().package().empty()) return;
for (absl::string_view segment : absl::StrSplit(file.desc().package(), '.')) {
// Emits openings for a tree of submodules for a given `pkg`.
//
// For example for `package.uses.dots.submodule.separator` this function
// generates:
// ```
// pub mod package {
// pub mod uses {
// pub mod dots {
// pub mod submodule {
// pub mod separator {
// ```
void EmitOpeningOfPackageModules(absl::string_view pkg,
Context<FileDescriptor> file) {
if (pkg.empty()) return;
for (absl::string_view segment : absl::StrSplit(pkg, '.')) {
file.Emit({{"segment", segment}},
R"rs(
pub mod $segment$ {
@ -63,10 +82,21 @@ void EmitOpeningOfPackageModules(Context<FileDescriptor> file) {
}
}
void EmitClosingOfPackageModules(Context<FileDescriptor> file) {
if (file.desc().package().empty()) return;
std::vector<absl::string_view> segments =
absl::StrSplit(file.desc().package(), '.');
// Emits closing curly brace for a tree of submodules for a given `pkg`.
//
// For example for `package.uses.dots.submodule.separator` this function
// generates:
// ```
// } // mod separator
// } // mod submodule
// } // mod dots
// } // mod uses
// } // mod package
// ```
void EmitClosingOfPackageModules(absl::string_view pkg,
Context<FileDescriptor> file) {
if (pkg.empty()) return;
std::vector<absl::string_view> segments = absl::StrSplit(pkg, '.');
absl::c_reverse(segments);
for (absl::string_view segment : segments) {
@ -75,6 +105,114 @@ void EmitClosingOfPackageModules(Context<FileDescriptor> file) {
)rs");
}
}
// Emits `pub use <internal submodule name>::Msg` for all messages of a
// `non_primary_src` into the `primary_file`.
//
// `non_primary_src` has to be a non-primary src of the current `proto_library`.
void EmitPubUseOfOwnMessages(Context<FileDescriptor>& primary_file,
const Context<FileDescriptor>& non_primary_src) {
for (int i = 0; i < non_primary_src.desc().message_type_count(); ++i) {
auto msg = primary_file.WithDesc(non_primary_src.desc().message_type(i));
auto mod = RustInternalModuleName(non_primary_src);
auto name = msg.desc().name();
primary_file.Emit({{"mod", mod}, {"Msg", name}},
R"rs(
pub use crate::$mod$::$Msg$;
)rs");
}
}
// Emits `pub use <crate_name>::<public package>::Msg` for all messages of a
// `dep` into the `primary_file`.
//
// `dep` is a primary src of a dependency of the current `proto_library`.
// TODO(b/270124215): Add support for public import of non-primary srcs of deps.
void EmitPubUseForImportedMessages(Context<FileDescriptor>& primary_file,
const Context<FileDescriptor>& dep) {
std::string crate_name = GetCrateName(dep);
for (int i = 0; i < dep.desc().message_type_count(); ++i) {
auto msg = primary_file.WithDesc(dep.desc().message_type(i));
auto path = GetCrateRelativeQualifiedPath(msg);
primary_file.Emit({{"crate", crate_name}, {"pkg::Msg", path}},
R"rs(
pub use $crate$::$pkg::Msg$;
)rs");
}
}
// Emits all public imports of the current file
void EmitPublicImports(
Context<FileDescriptor>& primary_file,
const std::vector<const FileDescriptor*>& files_in_current_crate) {
absl::flat_hash_set<const FileDescriptor*> files(
files_in_current_crate.begin(), files_in_current_crate.end());
for (int i = 0; i < primary_file.desc().public_dependency_count(); ++i) {
auto dep_file = primary_file.desc().public_dependency(i);
// If the publicly imported file is a src of the current `proto_library`
// we don't need to emit `pub use` here, we already do it for all srcs in
// RustGenerator::Generate. In other words, all srcs are implicitly publicly
// imported into the primary file for Protobuf Rust.
// TODO(b/270124215): Handle the case where a non-primary src with the same
// declared package as the primary src publicly imports a file that the
// primary doesn't.
if (files.contains(dep_file)) continue;
auto dep = primary_file.WithDesc(dep_file);
EmitPubUseForImportedMessages(primary_file, dep);
}
}
// Emits submodule declarations so `rustc` can find non primary sources from the
// primary file.
void DeclareSubmodulesForNonPrimarySrcs(
Context<FileDescriptor>& primary_file,
absl::Span<const Context<FileDescriptor>> non_primary_srcs) {
std::string primary_file_path = GetRsFile(primary_file);
RelativePath primary_relpath(primary_file_path);
for (const auto& non_primary_src : non_primary_srcs) {
std::string non_primary_file_path = GetRsFile(non_primary_src);
std::string relative_mod_path =
primary_relpath.Relative(RelativePath(non_primary_file_path));
primary_file.Emit({{"file_path", relative_mod_path},
{"foo", primary_file_path},
{"bar", non_primary_file_path},
{"mod_name", RustInternalModuleName(non_primary_src)}},
R"rs(
#[path="$file_path$"]
pub mod $mod_name$;
)rs");
}
}
// Emits `pub use <...>::Msg` for all messages in non primary sources into their
// corresponding packages (each source file can declare a different package).
//
// Returns the non-primary sources that should be reexported from the package of
// the primary file.
std::vector<const Context<FileDescriptor>*> ReexportMessagesFromSubmodules(
Context<FileDescriptor>& primary_file,
absl::Span<const Context<FileDescriptor>> non_primary_srcs) {
absl::btree_map<absl::string_view,
std::vector<const Context<FileDescriptor>*>>
packages;
for (const Context<FileDescriptor>& ctx : non_primary_srcs) {
packages[ctx.desc().package()].push_back(&ctx);
}
for (const auto& pair : packages) {
// We will deal with messages for the package of the primary file later.
auto fds = pair.second;
absl::string_view package = fds[0]->desc().package();
if (package == primary_file.desc().package()) continue;
EmitOpeningOfPackageModules(package, primary_file);
for (const Context<FileDescriptor>* c : fds) {
EmitPubUseOfOwnMessages(primary_file, *c);
}
EmitClosingOfPackageModules(package, primary_file);
}
return packages[primary_file.desc().package()];
}
} // namespace
bool RustGenerator::Generate(const FileDescriptor* file_desc,
@ -107,27 +245,33 @@ bool RustGenerator::Generate(const FileDescriptor* file_desc,
extern crate std as __std;
)rs");
EmitOpeningOfPackageModules(file);
// TODO(b/270124215): Delete the following "placeholder impl" of `import
// public`. Also make sure to figure out how to map FileDescriptor#name to
// Rust crate names (currently Bazel labels).
for (int i = 0; i < file.desc().public_dependency_count(); ++i) {
auto dep = file.WithDesc(file.desc().public_dependency(i));
std::string crate_name = GetCrateName(dep);
for (int j = 0; j < dep.desc().message_type_count(); ++j) {
auto msg = file.WithDesc(dep.desc().message_type(j));
file.Emit(
{
{"crate", crate_name},
{"pkg::Msg", GetCrateRelativeQualifiedPath(msg)},
},
R"rs(
pub use $crate$::$pkg::Msg$;
)rs");
std::vector<const FileDescriptor*> files_in_current_crate;
generator_context->ListParsedFiles(&files_in_current_crate);
std::vector<Context<FileDescriptor>> file_contexts;
for (const FileDescriptor* f : files_in_current_crate) {
file_contexts.push_back(file.WithDesc(*f));
}
// Generating the primary file?
if (file_desc == files_in_current_crate.front()) {
auto non_primary_srcs = absl::MakeConstSpan(file_contexts).subspan(1);
DeclareSubmodulesForNonPrimarySrcs(file, non_primary_srcs);
std::vector<const Context<FileDescriptor>*>
non_primary_srcs_in_primary_package =
ReexportMessagesFromSubmodules(file, non_primary_srcs);
EmitOpeningOfPackageModules(file.desc().package(), file);
for (const Context<FileDescriptor>* non_primary_file :
non_primary_srcs_in_primary_package) {
EmitPubUseOfOwnMessages(file, *non_primary_file);
}
}
EmitPublicImports(file, files_in_current_crate);
std::unique_ptr<io::ZeroCopyOutputStream> thunks_cc;
std::unique_ptr<io::Printer> thunks_printer;
if (file.is_cpp()) {
@ -158,7 +302,9 @@ bool RustGenerator::Generate(const FileDescriptor* file_desc,
thunks_msg.printer().PrintRaw("\n");
}
}
EmitClosingOfPackageModules(file);
if (file_desc == files_in_current_crate.front()) {
EmitClosingOfPackageModules(file.desc().package(), file);
}
return true;
}

@ -36,6 +36,7 @@
#include "absl/strings/str_cat.h"
#include "absl/strings/str_replace.h"
#include "absl/strings/string_view.h"
#include "absl/strings/strip.h"
#include "absl/strings/substitute.h"
#include "google/protobuf/compiler/code_generator.h"
#include "google/protobuf/compiler/rust/context.h"
@ -164,7 +165,16 @@ std::string RustModule(Context<Descriptor> msg) {
return absl::StrCat("", absl::StrReplaceAll(package, {{".", "::"}}));
}
std::string RustInternalModuleName(Context<FileDescriptor> file) {
// TODO(b/291853557): Introduce a more robust mangling here to avoid conflicts
// between `foo/bar/baz.proto` and `foo_bar/baz.proto`.
return absl::StrReplaceAll(StripProto(file.desc().name()), {{"/", "_"}});
}
std::string GetCrateRelativeQualifiedPath(Context<Descriptor> msg) {
if (msg.desc().file()->package().empty()) {
return msg.desc().name();
}
return absl::StrCat(RustModule(msg), "::", msg.desc().name());
}

@ -58,6 +58,7 @@ absl::string_view PrimitiveRsTypeName(Context<FieldDescriptor> field);
std::string FieldInfoComment(Context<FieldDescriptor> field);
std::string RustModule(Context<Descriptor> msg);
std::string RustInternalModuleName(Context<FileDescriptor> file);
std::string GetCrateRelativeQualifiedPath(Context<Descriptor> msg);
} // namespace rust

@ -0,0 +1,103 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2023 Google LLC. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google LLC. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "google/protobuf/compiler/rust/relative_path.h"
#include <iostream>
#include <string>
#include <vector>
#include "absl/algorithm/container.h"
#include "absl/strings/match.h"
#include "absl/strings/str_join.h"
#include "absl/strings/str_split.h"
#include "absl/strings/string_view.h"
namespace google {
namespace protobuf {
namespace compiler {
namespace rust {
std::vector<absl::string_view> RelativePath::Segments() const {
return absl::StrSplit(this->path_, '/', absl::SkipWhitespace());
}
bool RelativePath::IsDirectory() const {
return absl::EndsWith(this->path_, "/");
}
std::string RelativePath::Relative(const RelativePath& dest) const {
ABSL_CHECK(!dest.IsDirectory())
<< "`dest` has to be a file path, but is a directory.";
std::vector<absl::string_view> current_segments = this->Segments();
if (!current_segments.empty() && !this->IsDirectory()) {
// `this` represents a file path, skip the last segment to get its
// directory.
current_segments.pop_back();
}
std::vector<absl::string_view> dest_segments = dest.Segments();
// Find the lowest common ancestor.
absl::c_reverse(current_segments);
absl::c_reverse(dest_segments);
while (true) {
if (current_segments.empty()) break;
if (dest_segments.empty()) break;
if (current_segments.back() != dest_segments.back()) break;
current_segments.pop_back();
dest_segments.pop_back();
}
// Construct the relative path in reverse order.
std::vector<absl::string_view> result;
result.reserve(current_segments.size() + dest_segments.size());
// Push the segments from the `dest` to the common ancestor.
for (const auto& segment : dest_segments) {
result.push_back(segment);
}
// Push `..` from the common ancestor to the current path.
for (int i = 0; i < current_segments.size(); ++i) {
result.push_back("..");
}
absl::c_reverse(result);
if (dest.IsDirectory()) {
// Convince the `StrJoin` below to add a trailing `/`.
result.push_back("");
}
return absl::StrJoin(result, "/");
}
} // namespace rust
} // namespace compiler
} // namespace protobuf
} // namespace google

@ -0,0 +1,77 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2023 Google LLC. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google LLC. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef GOOGLE_PROTOBUF_COMPILER_RUST_RELATIVE_PATH_H__
#define GOOGLE_PROTOBUF_COMPILER_RUST_RELATIVE_PATH_H__
#include <string>
#include <vector>
#include "absl/algorithm/container.h"
#include "absl/log/absl_check.h"
#include "absl/strings/match.h"
#include "absl/strings/string_view.h"
namespace google {
namespace protobuf {
namespace compiler {
namespace rust {
// Relative path using '/' as a separator.
class RelativePath final {
public:
explicit RelativePath(absl::string_view path) : path_(path) {
ABSL_CHECK(!absl::StartsWith(path, "/"))
<< "only relative paths are supported";
// `..` and `.` not supported, since there's no use case for that right now.
for (absl::string_view segment : Segments()) {
ABSL_CHECK(segment != "..") << "`..` segments are not supported";
ABSL_CHECK(segment != ".") << "`.` segments are not supported";
}
}
// Returns a path getting us from the current relative path to the `dest`
// path.
//
// Supports both files and directories.
std::string Relative(const RelativePath& dest) const;
std::vector<absl::string_view> Segments() const;
bool IsDirectory() const;
private:
absl::string_view path_;
};
} // namespace rust
} // namespace compiler
} // namespace protobuf
} // namespace google
#endif // GOOGLE_PROTOBUF_COMPILER_RUST_RELATIVE_PATH_H__

@ -0,0 +1,67 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2023 Google LLC. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google LLC. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "google/protobuf/compiler/rust/relative_path.h"
#include <gmock/gmock.h>
#include <gtest/gtest.h>
namespace google {
namespace protobuf {
namespace compiler {
namespace rust {
namespace {
using testing::Eq;
TEST(RelativePathTest, GetRelativePath) {
auto relative = [](absl::string_view from_path, absl::string_view to_path) {
return RelativePath(from_path).Relative(RelativePath(to_path));
};
EXPECT_EQ(relative("foo/bar/baz.txt", "foo/bar/file.txt"), "file.txt");
EXPECT_EQ(relative("foo/bar/", "foo/bar/file.txt"), "file.txt");
EXPECT_EQ(relative("foo/bar/baz.txt", "foo/file.txt"), "../file.txt");
EXPECT_EQ(relative("foo/bar/", "foo/file.txt"), "../file.txt");
EXPECT_EQ(relative("foo/baz.txt", "foo/bar/baz/file.txt"),
"bar/baz/file.txt");
EXPECT_EQ(relative("foo/", "foo/bar/baz/file.txt"), "bar/baz/file.txt");
EXPECT_EQ(relative("baz.txt", "foo/bar/file.txt"), "foo/bar/file.txt");
EXPECT_EQ(relative("", "foo/bar/file.txt"), "foo/bar/file.txt");
}
} // namespace
} // namespace rust
} // namespace compiler
} // namespace protobuf
} // namespace google
Loading…
Cancel
Save