From b146d14e31e5488978517396406f9afe2664f0b0 Mon Sep 17 00:00:00 2001 From: Thomas Van Lenten Date: Fri, 8 Dec 2023 10:42:19 -0800 Subject: [PATCH] [ObjC] Improve handing of the WKT ObjC Category additions. Include a comment in the generated header about there being helpers in GPBWellKnowTypes.h Generate some extra code for the WKTs that have categories to help ensure the categories get linked so developers don't have to use -ObjC in some cases. PiperOrigin-RevId: 589179237 --- objectivec/GPBAny.pbobjc.h | 3 ++ objectivec/GPBAny.pbobjc.m | 8 +++++ objectivec/GPBDuration.pbobjc.h | 3 ++ objectivec/GPBDuration.pbobjc.m | 8 +++++ objectivec/GPBTimestamp.pbobjc.h | 3 ++ objectivec/GPBTimestamp.pbobjc.m | 8 +++++ .../protobuf/compiler/objectivec/file.cc | 36 +++++++++++++++++++ .../protobuf/compiler/objectivec/helpers.cc | 28 +++++++++++++++ .../protobuf/compiler/objectivec/helpers.h | 5 +++ .../protobuf/compiler/objectivec/message.cc | 12 +++++++ 10 files changed, 114 insertions(+) diff --git a/objectivec/GPBAny.pbobjc.h b/objectivec/GPBAny.pbobjc.h index c0d389f0399cc..a9ae0c53048ab 100644 --- a/objectivec/GPBAny.pbobjc.h +++ b/objectivec/GPBAny.pbobjc.h @@ -168,6 +168,9 @@ GPB_FINAL @interface GPBAny : GPBMessage /** Must be a valid serialized protocol buffer of the above specified type. */ @property(nonatomic, readwrite, copy, null_resettable) NSData *value; +// NOTE: There are some Objective-C specific methods/properties in +// GPBWellKnownTypes.h that will likey be useful. + @end NS_ASSUME_NONNULL_END diff --git a/objectivec/GPBAny.pbobjc.m b/objectivec/GPBAny.pbobjc.m index be6ce4ed5e1f7..65207e8293808 100644 --- a/objectivec/GPBAny.pbobjc.m +++ b/objectivec/GPBAny.pbobjc.m @@ -3,6 +3,7 @@ // source: google/protobuf/any.proto #import "GPBProtocolBuffers_RuntimeSupport.h" +#import "GPBWellKnownTypes.h" #import "GPBAny.pbobjc.h" #if GOOGLE_PROTOBUF_OBJC_VERSION < 30007 @@ -39,6 +40,13 @@ @implementation GPBAnyRoot .syntax = GPBFileSyntaxProto3 }; +// This is to help make sure that the GPBWellKnownTypes.* categories get linked and +// developers do not have to use the `-ObjC` linker flag. More information +// here: https://medium.com/ios-os-x-development/categories-in-static-libraries-78e41f8ddb96 +__attribute__((used)) static NSString* any_importCategories () { + return GPBWellKnownTypesErrorDomain; +} + #pragma mark - GPBAny @implementation GPBAny diff --git a/objectivec/GPBDuration.pbobjc.h b/objectivec/GPBDuration.pbobjc.h index 9e67afc058f89..c5e27e4e5ac8d 100644 --- a/objectivec/GPBDuration.pbobjc.h +++ b/objectivec/GPBDuration.pbobjc.h @@ -123,6 +123,9 @@ GPB_FINAL @interface GPBDuration : GPBMessage **/ @property(nonatomic, readwrite) int32_t nanos; +// NOTE: There are some Objective-C specific methods/properties in +// GPBWellKnownTypes.h that will likey be useful. + @end NS_ASSUME_NONNULL_END diff --git a/objectivec/GPBDuration.pbobjc.m b/objectivec/GPBDuration.pbobjc.m index f53c022d9d91a..124342f5e5cbe 100644 --- a/objectivec/GPBDuration.pbobjc.m +++ b/objectivec/GPBDuration.pbobjc.m @@ -3,6 +3,7 @@ // source: google/protobuf/duration.proto #import "GPBProtocolBuffers_RuntimeSupport.h" +#import "GPBWellKnownTypes.h" #import "GPBDuration.pbobjc.h" #if GOOGLE_PROTOBUF_OBJC_VERSION < 30007 @@ -39,6 +40,13 @@ @implementation GPBDurationRoot .syntax = GPBFileSyntaxProto3 }; +// This is to help make sure that the GPBWellKnownTypes.* categories get linked and +// developers do not have to use the `-ObjC` linker flag. More information +// here: https://medium.com/ios-os-x-development/categories-in-static-libraries-78e41f8ddb96 +__attribute__((used)) static NSString* duration_importCategories () { + return GPBWellKnownTypesErrorDomain; +} + #pragma mark - GPBDuration @implementation GPBDuration diff --git a/objectivec/GPBTimestamp.pbobjc.h b/objectivec/GPBTimestamp.pbobjc.h index 510ecc0316045..bdf8ee418696a 100644 --- a/objectivec/GPBTimestamp.pbobjc.h +++ b/objectivec/GPBTimestamp.pbobjc.h @@ -152,6 +152,9 @@ GPB_FINAL @interface GPBTimestamp : GPBMessage **/ @property(nonatomic, readwrite) int32_t nanos; +// NOTE: There are some Objective-C specific methods/properties in +// GPBWellKnownTypes.h that will likey be useful. + @end NS_ASSUME_NONNULL_END diff --git a/objectivec/GPBTimestamp.pbobjc.m b/objectivec/GPBTimestamp.pbobjc.m index f2b44fdef81da..faf201519be00 100644 --- a/objectivec/GPBTimestamp.pbobjc.m +++ b/objectivec/GPBTimestamp.pbobjc.m @@ -3,6 +3,7 @@ // source: google/protobuf/timestamp.proto #import "GPBProtocolBuffers_RuntimeSupport.h" +#import "GPBWellKnownTypes.h" #import "GPBTimestamp.pbobjc.h" #if GOOGLE_PROTOBUF_OBJC_VERSION < 30007 @@ -39,6 +40,13 @@ @implementation GPBTimestampRoot .syntax = GPBFileSyntaxProto3 }; +// This is to help make sure that the GPBWellKnownTypes.* categories get linked and +// developers do not have to use the `-ObjC` linker flag. More information +// here: https://medium.com/ios-os-x-development/categories-in-static-libraries-78e41f8ddb96 +__attribute__((used)) static NSString* timestamp_importCategories () { + return GPBWellKnownTypesErrorDomain; +} + #pragma mark - GPBTimestamp @implementation GPBTimestamp diff --git a/src/google/protobuf/compiler/objectivec/file.cc b/src/google/protobuf/compiler/objectivec/file.cc index 1e45b375d0e7c..eccec502a7d9a 100644 --- a/src/google/protobuf/compiler/objectivec/file.cc +++ b/src/google/protobuf/compiler/objectivec/file.cc @@ -22,6 +22,8 @@ #include "absl/log/absl_check.h" #include "absl/strings/str_cat.h" #include "absl/strings/str_join.h" +#include "absl/strings/string_view.h" +#include "google/protobuf/compiler/code_generator.h" #include "google/protobuf/compiler/objectivec/enum.h" #include "google/protobuf/compiler/objectivec/extension.h" #include "google/protobuf/compiler/objectivec/helpers.h" @@ -139,6 +141,27 @@ void MakeDescriptors( } } +void EmitLinkWKTs(absl::string_view name, io::Printer* p) { + absl::string_view::size_type last_slash = name.rfind('/'); + std::string basename; + if (last_slash == absl::string_view::npos) { + basename = std::string(name); + } else { + basename = std::string(name.substr(last_slash + 1)); + } + + p->Emit({{"basename", StripProto(basename)}}, + R"objc( + // This is to help make sure that the GPBWellKnownTypes.* categories get linked and + // developers do not have to use the `-ObjC` linker flag. More information + // here: https://medium.com/ios-os-x-development/categories-in-static-libraries-78e41f8ddb96 + __attribute__((used)) static NSString* $basename$_importCategories () { + return GPBWellKnownTypesErrorDomain; + } + )objc"); + p->Emit("\n"); +} + void EmitSourceFwdDecls(const absl::btree_set& fwd_decls, io::Printer* p) { if (fwd_decls.empty()) { @@ -385,6 +408,10 @@ void FileGenerator::GenerateSource(io::Printer* p) const { EmitRootImplementation(p, deps_with_extensions); EmitFileDescription(p); + if (is_bundled_proto_ && HasWKTWithObjCCategory(file_)) { + EmitLinkWKTs(file_->name(), p); + } + for (const auto& generator : enum_generators_) { generator->GenerateSource(p); } @@ -395,6 +422,8 @@ void FileGenerator::GenerateSource(io::Printer* p) const { } void FileGenerator::GenerateGlobalSource(io::Printer* p) const { + ABSL_CHECK(!is_bundled_proto_) + << "Bundled protos aren't expected to use multi source generation."; std::vector deps_with_extensions = common_state_->CollectMinimalFileDepsContainingExtensions(file_); GeneratedFileOptions file_options; @@ -416,6 +445,8 @@ void FileGenerator::GenerateGlobalSource(io::Printer* p) const { } void FileGenerator::GenerateSourceForEnums(io::Printer* p) const { + ABSL_CHECK(!is_bundled_proto_) + << "Bundled protos aren't expected to use multi source generation."; // Enum implementation uses atomic in the generated code. GeneratedFileOptions file_options; file_options.extra_system_headers.push_back("stdatomic.h"); @@ -428,6 +459,8 @@ void FileGenerator::GenerateSourceForEnums(io::Printer* p) const { } void FileGenerator::GenerateSourceForMessage(int idx, io::Printer* p) const { + ABSL_CHECK(!is_bundled_proto_) + << "Bundled protos aren't expected to use multi source generation."; const auto& generator = message_generators_[idx]; absl::btree_set fwd_decls; @@ -485,6 +518,9 @@ void FileGenerator::GenerateFile(io::Printer* p, GeneratedFileType file_type, break; case GeneratedFileType::kSource: import_writer.AddRuntimeImport("GPBProtocolBuffers_RuntimeSupport.h"); + if (is_bundled_proto_ && HasWKTWithObjCCategory(file_)) { + import_writer.AddRuntimeImport("GPBWellKnownTypes.h"); + } import_writer.AddFile(file_, header_extension); if (HeadersUseForwardDeclarations()) { if (generation_options_.generate_minimal_imports) { diff --git a/src/google/protobuf/compiler/objectivec/helpers.cc b/src/google/protobuf/compiler/objectivec/helpers.cc index 0ecb564e90d9b..b1d41803ec8de 100644 --- a/src/google/protobuf/compiler/objectivec/helpers.cc +++ b/src/google/protobuf/compiler/objectivec/helpers.cc @@ -13,6 +13,7 @@ #include #include +#include "absl/log/absl_check.h" #include "absl/log/absl_log.h" #include "absl/strings/ascii.h" #include "absl/strings/escaping.h" @@ -393,6 +394,33 @@ void EmitCommentsString(io::Printer* printer, const SourceLocation& location, )"); } +bool HasWKTWithObjCCategory(const FileDescriptor* file) { + // We don't check the name prefix or proto package because some files + // (descriptor.proto), aren't shipped generated by the library, so this + // seems to be the safest way to only catch the ones shipped. + const std::string name = file->name(); + if (name == "google/protobuf/any.proto" || + name == "google/protobuf/duration.proto" || + name == "google/protobuf/timestamp.proto") { + ABSL_DCHECK(IsProtobufLibraryBundledProtoFile(file)); + return true; + } + return false; +} + +bool IsWKTWithObjCCategory(const Descriptor* descriptor) { + if (!HasWKTWithObjCCategory(descriptor->file())) { + return false; + } + const std::string full_name = descriptor->full_name(); + if (full_name == "google.protobuf.Any" || + full_name == "google.protobuf.Duration" || + full_name == "google.protobuf.Timestamp") { + return true; + } + return false; +} + } // namespace objectivec } // namespace compiler } // namespace protobuf diff --git a/src/google/protobuf/compiler/objectivec/helpers.h b/src/google/protobuf/compiler/objectivec/helpers.h index 086b71faaf2a9..7b6c2b786d4bf 100644 --- a/src/google/protobuf/compiler/objectivec/helpers.h +++ b/src/google/protobuf/compiler/objectivec/helpers.h @@ -148,6 +148,11 @@ std::string GetOptionalDeprecatedAttribute( } } +// Helpers to identify the WellKnownType files/messages that get an Objective-C +// category within the runtime to add helpers. +bool HasWKTWithObjCCategory(const FileDescriptor* file); +bool IsWKTWithObjCCategory(const Descriptor* descriptor); + } // namespace objectivec } // namespace compiler } // namespace protobuf diff --git a/src/google/protobuf/compiler/objectivec/message.cc b/src/google/protobuf/compiler/objectivec/message.cc index 0030cda550731..55fd4d3f2de1d 100644 --- a/src/google/protobuf/compiler/objectivec/message.cc +++ b/src/google/protobuf/compiler/objectivec/message.cc @@ -336,6 +336,17 @@ void MessageGenerator::GenerateMessageHeader(io::Printer* printer) const { } field_generators_.get(field).GeneratePropertyDeclaration(printer); } + }}, + {"wkt_extra", + [&] { + if (!IsWKTWithObjCCategory(descriptor_)) { + return; + } + printer->Emit(R"objc( + // NOTE: There are some Objective-C specific methods/properties in + // GPBWellKnownTypes.h that will likey be useful. + )objc"); + printer->Emit("\n"); }}}, R"objc( #pragma mark - $classname$ @@ -347,6 +358,7 @@ void MessageGenerator::GenerateMessageHeader(io::Printer* printer) const { GPB_FINAL @interface $classname$ : GPBMessage $message_properties$ + $wkt_extra$ @end )objc"); printer->Emit("\n");