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");