aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.travis.yml3
-rw-r--r--cmake/CMakeLists.txt4
-rw-r--r--cmake/examples.cmake3
-rw-r--r--cmake/protobuf-config-version.cmake.in28
-rw-r--r--cmake/protobuf-module.cmake.in33
-rw-r--r--js/gulpfile.js37
-rw-r--r--objectivec/GPBDescriptor.m5
-rw-r--r--objectivec/README.md40
-rwxr-xr-xobjectivec/Tests/CocoaPods/run_tests.sh11
-rw-r--r--src/README.md2
-rw-r--r--src/google/protobuf/compiler/command_line_interface.cc9
-rw-r--r--src/google/protobuf/compiler/command_line_interface_unittest.cc15
-rwxr-xr-xsrc/google/protobuf/compiler/js/js_generator.cc14
-rw-r--r--src/google/protobuf/compiler/objectivec/objectivec_file.cc152
-rw-r--r--src/google/protobuf/compiler/objectivec/objectivec_file.h2
-rw-r--r--src/google/protobuf/compiler/objectivec/objectivec_generator.cc41
-rw-r--r--src/google/protobuf/compiler/objectivec/objectivec_helpers.cc279
-rw-r--r--src/google/protobuf/compiler/objectivec/objectivec_helpers.h19
-rw-r--r--src/google/protobuf/map.h11
-rwxr-xr-xtests.sh12
20 files changed, 537 insertions, 183 deletions
diff --git a/.travis.yml b/.travis.yml
index 86451edd..46abbb9e 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -28,6 +28,7 @@ env:
- CONFIG=objectivec_ios_debug
- CONFIG=objectivec_ios_release
- CONFIG=objectivec_osx
+ - CONFIG=objectivec_cocoapods_integration
- CONFIG=python
- CONFIG=python_cpp
- CONFIG=ruby19
@@ -67,6 +68,8 @@ matrix:
env: CONFIG=objectivec_ios_release
- os: linux
env: CONFIG=objectivec_osx
+ - os: linux
+ env: CONFIG=objectivec_cocoapods_integration
allow_failures:
# These currently do not work on OS X but are being worked on by @haberman.
- os: osx
diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt
index 7e1b385e..07b176d9 100644
--- a/cmake/CMakeLists.txt
+++ b/cmake/CMakeLists.txt
@@ -20,7 +20,9 @@ else (BUILD_SHARED_LIBS)
set(protobuf_BUILD_SHARED_LIBS_DEFAULT OFF)
endif (BUILD_SHARED_LIBS)
option(protobuf_BUILD_SHARED_LIBS "Build Shared Libraries" ${protobuf_BUILD_SHARED_LIBS_DEFAULT})
-option(protobuf_MSVC_STATIC_RUNTIME "Link static runtime libraries" ON)
+include(CMakeDependentOption)
+cmake_dependent_option(protobuf_MSVC_STATIC_RUNTIME "Link static runtime libraries" ON
+ "NOT protobuf_BUILD_SHARED_LIBS" OFF)
if (MSVC)
set(protobuf_WITH_ZLIB_DEFAULT OFF)
else (MSVC)
diff --git a/cmake/examples.cmake b/cmake/examples.cmake
index 0e33cd5a..83d0e988 100644
--- a/cmake/examples.cmake
+++ b/cmake/examples.cmake
@@ -19,8 +19,7 @@ function(add_examples_build NAME)
STAMP_DIR ${NAME}/logs
INSTALL_COMMAND "" #Skip
LOG_CONFIGURE 1
- CMAKE_CACHE_ARGS "-Dprotobuf_MSVC_STATIC_RUNTIME:BOOL=${protobuf_MSVC_STATIC_RUNTIME}"
- "-DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE}"
+ CMAKE_CACHE_ARGS "-DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE}"
"-Dprotobuf_VERBOSE:BOOL=${protobuf_VERBOSE}"
${ARGN}
)
diff --git a/cmake/protobuf-config-version.cmake.in b/cmake/protobuf-config-version.cmake.in
index 2a6feee1..3b8ced2d 100644
--- a/cmake/protobuf-config-version.cmake.in
+++ b/cmake/protobuf-config-version.cmake.in
@@ -34,15 +34,25 @@ elseif(PACKAGE_FIND_VERSION VERSION_EQUAL PACKAGE_VERSION)
endif()
endif()
-# if the installed or the using project don't have CMAKE_SIZEOF_VOID_P set, ignore it:
-if("${CMAKE_SIZEOF_VOID_P}" STREQUAL "" OR "@CMAKE_SIZEOF_VOID_P@" STREQUAL "")
- return()
-endif()
+# Check and save build options used to create this package
+macro(_check_and_save_build_option OPTION VALUE)
+ if(DEFINED ${PACKAGE_FIND_NAME}_${OPTION} AND
+ NOT ${PACKAGE_FIND_NAME}_${OPTION} EQUAL VALUE)
+ set(PACKAGE_VERSION_UNSUITABLE TRUE)
+ endif()
+ set(${PACKAGE_FIND_NAME}_${OPTION} ${VALUE})
+endmacro()
+_check_and_save_build_option(WITH_ZLIB @protobuf_WITH_ZLIB@)
+_check_and_save_build_option(MSVC_STATIC_RUNTIME @protobuf_MSVC_STATIC_RUNTIME@)
+_check_and_save_build_option(BUILD_SHARED_LIBS @protobuf_BUILD_SHARED_LIBS@)
-# check that the installed version has the same 32/64bit-ness as the one which is currently searching:
-if(NOT CMAKE_SIZEOF_VOID_P STREQUAL "@CMAKE_SIZEOF_VOID_P@")
- math(EXPR installedBits "@CMAKE_SIZEOF_VOID_P@ * 8")
- set(PACKAGE_VERSION "${PACKAGE_VERSION} (${installedBits}bit)")
- set(PACKAGE_VERSION_UNSUITABLE TRUE)
+# if the installed or the using project don't have CMAKE_SIZEOF_VOID_P set, ignore it:
+if(NOT "${CMAKE_SIZEOF_VOID_P}" STREQUAL "" AND NOT "@CMAKE_SIZEOF_VOID_P@" STREQUAL "")
+ # check that the installed version has the same 32/64bit-ness as the one which is currently searching:
+ if(NOT CMAKE_SIZEOF_VOID_P STREQUAL "@CMAKE_SIZEOF_VOID_P@")
+ math(EXPR installedBits "@CMAKE_SIZEOF_VOID_P@ * 8")
+ set(PACKAGE_VERSION "${PACKAGE_VERSION} (${installedBits}bit)")
+ set(PACKAGE_VERSION_UNSUITABLE TRUE)
+ endif()
endif()
diff --git a/cmake/protobuf-module.cmake.in b/cmake/protobuf-module.cmake.in
index 7ced934a..6e0bcf90 100644
--- a/cmake/protobuf-module.cmake.in
+++ b/cmake/protobuf-module.cmake.in
@@ -143,15 +143,10 @@ function(_protobuf_find_libraries name filename)
# Honor cache entry used by CMake 3.5 and lower.
set(${name}_LIBRARIES "${${name}_LIBRARY}" PARENT_SCOPE)
else()
- get_target_property(_aliased protobuf::lib${filename} ALIASED_TARGET)
- if(_aliased)
- set(${name}_LIBRARY_RELEASE $<TARGET_FILE:protobuf::lib${filename}>)
- set(${name}_LIBRARY_DEBUG $<TARGET_FILE:protobuf::lib${filename}>)
- else()
- get_target_property(${name}_LIBRARY_RELEASE protobuf::lib${filename}
- LOCATION_RELEASE)
- get_target_property(${name}_LIBRARY_DEBUG protobuf::lib${filename}
- LOCATION_DEBUG)
+ get_target_property(${name}_LIBRARY_RELEASE protobuf::lib${filename}
+ LOCATION_RELEASE)
+ get_target_property(${name}_LIBRARY_DEBUG protobuf::lib${filename}
+ LOCATION_DEBUG)
endif()
select_library_configurations(${name})
@@ -198,21 +193,11 @@ get_target_property(Protobuf_INCLUDE_DIRS protobuf::libprotobuf
INTERFACE_INCLUDE_DIRECTORIES)
# Set the protoc Executable
-get_target_property(_aliased protobuf::protoc ALIASED_TARGET)
-if(_aliased)
- if(POLICY CMP0026)
- set(Protobuf_PROTOC_EXECUTABLE $<TARGET_FILE:protobuf::protoc>)
- else()
- get_target_property(Protobuf_PROTOC_EXECUTABLE protobuf::protoc
- LOCATION)
- endif()
-else()
+get_target_property(Protobuf_PROTOC_EXECUTABLE protobuf::protoc
+ IMPORTED_LOCATION_RELEASE)
+if(NOT EXISTS "${Protobuf_PROTOC_EXECUTABLE}")
get_target_property(Protobuf_PROTOC_EXECUTABLE protobuf::protoc
- IMPORTED_LOCATION_RELEASE)
- if(NOT EXISTS "${Protobuf_PROTOC_EXECUTABLE}")
- get_target_property(Protobuf_PROTOC_EXECUTABLE protobuf::protoc
- IMPORTED_LOCATION_DEBUG)
- endif()
+ IMPORTED_LOCATION_DEBUG)
endif()
# Version info variable
@@ -220,7 +205,7 @@ set(Protobuf_VERSION "@protobuf_VERSION@")
include(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(Protobuf
- REQUIRED_VARS Protobuf_LIBRARIES Protobuf_INCLUDE_DIRS
+ REQUIRED_VARS Protobuf_PROTOC_EXECUTABLE Protobuf_LIBRARIES Protobuf_INCLUDE_DIRS
VERSION_VAR Protobuf_VERSION
)
diff --git a/js/gulpfile.js b/js/gulpfile.js
index c5220153..cca99131 100644
--- a/js/gulpfile.js
+++ b/js/gulpfile.js
@@ -8,6 +8,21 @@ function exec(command, cb) {
var protoc = process.env.PROTOC || '../src/protoc';
+var wellKnownTypes = [
+ '../src/google/protobuf/any.proto',
+ '../src/google/protobuf/api.proto',
+ '../src/google/protobuf/compiler/plugin.proto',
+ '../src/google/protobuf/descriptor.proto',
+ '../src/google/protobuf/duration.proto',
+ '../src/google/protobuf/empty.proto',
+ '../src/google/protobuf/field_mask.proto',
+ '../src/google/protobuf/source_context.proto',
+ '../src/google/protobuf/struct.proto',
+ '../src/google/protobuf/timestamp.proto',
+ '../src/google/protobuf/type.proto',
+ '../src/google/protobuf/wrappers.proto',
+];
+
gulp.task('genproto_closure', function (cb) {
exec(protoc + ' --js_out=library=testproto_libs,binary:. -I ../src -I . *.proto ../src/google/protobuf/descriptor.proto',
function (err, stdout, stderr) {
@@ -26,7 +41,25 @@ gulp.task('genproto_commonjs', function (cb) {
});
});
-gulp.task('dist', function (cb) {
+gulp.task('genproto_commonjs_wellknowntypes', function (cb) {
+ exec('mkdir -p commonjs_out/node_modules/google-protobuf && ' + protoc + ' --js_out=import_style=commonjs,binary:commonjs_out/node_modules/google-protobuf -I ../src ../src/google/protobuf/descriptor.proto',
+ function (err, stdout, stderr) {
+ console.log(stdout);
+ console.log(stderr);
+ cb(err);
+ });
+});
+
+gulp.task('genproto_wellknowntypes', function (cb) {
+ exec(protoc + ' --js_out=import_style=commonjs,binary:. -I ../src ' + wellKnownTypes.join(' '),
+ function (err, stdout, stderr) {
+ console.log(stdout);
+ console.log(stderr);
+ cb(err);
+ });
+});
+
+gulp.task('dist', ['genproto_wellknowntypes'], function (cb) {
// TODO(haberman): minify this more aggressively.
// Will require proper externs/exports.
exec('./node_modules/google-closure-library/closure/bin/calcdeps.py -i message.js -i binary/reader.js -i binary/writer.js -i commonjs/export.js -p . -p node_modules/google-closure-library/closure -o compiled --compiler_jar node_modules/google-closure-compiler/compiler.jar > google-protobuf.js',
@@ -55,7 +88,7 @@ gulp.task('commonjs_testdeps', function (cb) {
});
});
-gulp.task('make_commonjs_out', ['dist', 'genproto_commonjs', 'commonjs_asserts', 'commonjs_testdeps'], function (cb) {
+gulp.task('make_commonjs_out', ['dist', 'genproto_commonjs', 'genproto_commonjs_wellknowntypes', 'commonjs_asserts', 'commonjs_testdeps'], function (cb) {
// TODO(haberman): minify this more aggressively.
// Will require proper externs/exports.
var cmd = "mkdir -p commonjs_out/binary && mkdir -p commonjs_out/test_node_modules && ";
diff --git a/objectivec/GPBDescriptor.m b/objectivec/GPBDescriptor.m
index 898f231d..d27d6892 100644
--- a/objectivec/GPBDescriptor.m
+++ b/objectivec/GPBDescriptor.m
@@ -271,6 +271,11 @@ static NSArray *NewFieldsArrayForHasIndex(int hasIndex,
return self;
}
+- (void)dealloc {
+ [package_ release];
+ [super dealloc];
+}
+
@end
@implementation GPBOneofDescriptor
diff --git a/objectivec/README.md b/objectivec/README.md
index c7313e4f..25355e05 100644
--- a/objectivec/README.md
+++ b/objectivec/README.md
@@ -123,8 +123,8 @@ never included when the message is encoded.
The Objective C classes/enums can be used from Swift code.
-Objective C Generator Options
------------------------------
+Objective C Generator Proto File Options
+----------------------------------------
**objc_class_prefix=\<prefix\>** (no default)
@@ -133,6 +133,42 @@ be collisions. This option provides a prefix that will be added to the Enums
and Objects (for messages) generated from the proto. Convention is to base
the prefix on the package the proto is in.
+Objective C Generator `protoc` Options
+--------------------------------------
+
+When generating Objective C code, `protoc` supports a `--objc_opt` argument; the
+argument is comma-delimited name/value pairs (_key=value,key2=value2_). The
+_keys_ are used to change the behavior during generation. The currently
+supported keys are:
+
+ * `generate_for_framework_named`: The `value` used for this key will be used
+ when generating the `#import` statements in the generated code. Instead
+ of being plain `#import "some/path/file.pbobjc.h"` lines, they will be
+ framework based, i.e. - `#import <VALUE/file.pbobjc.h>`.
+
+ _NOTE:_ If this is used with `named_framework_to_proto_path_mappings_path`,
+ then this is effectively the _default_ to use for everything that wasn't
+ mapped by the other.
+
+ * `named_framework_to_proto_path_mappings_path`: The `value` used for this key
+ is a path to a file containing the listing of framework names and proto
+ files. The generator uses this to decide if another proto file referenced
+ should use a framework style import vs. a user level import
+ (`#import <FRAMEWORK/file.pbobjc.h>` vs `#import "dir/file.pbobjc.h"`).
+
+ The format of the file is:
+ * An entry is a line of `frameworkName: file.proto, dir/file2.proto`.
+ * Comments start with `#`.
+ * A comment can go on a line after an entry.
+ (i.e. - `frameworkName: file.proto # comment`)
+
+ Any number of files can be listed for a framework, just separate them with
+ commas.
+
+ There can be multiple lines listing the same frameworkName incase it has a
+ lot of proto files included in it; and having multiple lines makes things
+ easier to read.
+
Contributing
------------
diff --git a/objectivec/Tests/CocoaPods/run_tests.sh b/objectivec/Tests/CocoaPods/run_tests.sh
index edf0fe81..749f413c 100755
--- a/objectivec/Tests/CocoaPods/run_tests.sh
+++ b/objectivec/Tests/CocoaPods/run_tests.sh
@@ -119,9 +119,18 @@ do_test() {
# Put the right Podfile in place.
cp -f "Podfile-${TEST_MODE}" "Podfile"
+ xcodebuild_args=( "-workspace" "${TEST_NAME}.xcworkspace" "-scheme" "${TEST_NAME}" )
+
+ # For iOS, if the SDK is not provided it tries to use iphoneos, and the test
+ # fail on Travis since those machines don't have a Code Signing identity.
+ if [[ "${TEST_NAME}" == iOS* ]] ; then
+ xcodebuild_args+=( "-sdk" "iphonesimulator" "ONLY_ACTIVE_ARCH=NO" )
+ fi
+
# Do the work!
pod install --verbose
- xcodebuild -workspace "${TEST_NAME}.xcworkspace" -scheme "${TEST_NAME}" build
+
+ xcodebuild "${xcodebuild_args[@]}" build
# Clear the hook and manually run cleanup.
trap - EXIT
diff --git a/src/README.md b/src/README.md
index 63d6e24c..c9362ee2 100644
--- a/src/README.md
+++ b/src/README.md
@@ -22,7 +22,7 @@ To build protobuf from source, the following tools are needed:
On Ubuntu, you can install them with:
- $ sudo apt-get install autoconf automake libtool curl make g++ unzip
+ $ sudo apt-get install autoconf automake libtool curl make g++ unzip
On other platforms, please use the corresponding package managing tool to
install them before proceeding.
diff --git a/src/google/protobuf/compiler/command_line_interface.cc b/src/google/protobuf/compiler/command_line_interface.cc
index fcad6b61..c1a6c15d 100644
--- a/src/google/protobuf/compiler/command_line_interface.cc
+++ b/src/google/protobuf/compiler/command_line_interface.cc
@@ -1136,8 +1136,13 @@ CommandLineInterface::InterpretArgument(const string& name,
// Make sure disk path exists, warn otherwise.
if (access(disk_path.c_str(), F_OK) < 0) {
- std::cerr << disk_path << ": warning: directory does not exist."
- << std::endl;
+ // Try the original path; it may have just happed to have a '=' in it.
+ if (access(parts[i].c_str(), F_OK) < 0) {
+ cerr << disk_path << ": warning: directory does not exist." << endl;
+ } else {
+ virtual_path = "";
+ disk_path = parts[i];
+ }
}
// Don't use make_pair as the old/default standard library on Solaris
diff --git a/src/google/protobuf/compiler/command_line_interface_unittest.cc b/src/google/protobuf/compiler/command_line_interface_unittest.cc
index 9b504d25..0ebf9b6a 100644
--- a/src/google/protobuf/compiler/command_line_interface_unittest.cc
+++ b/src/google/protobuf/compiler/command_line_interface_unittest.cc
@@ -786,6 +786,21 @@ TEST_F(CommandLineInterfaceTest, NonRootMapping) {
ExpectGenerated("test_generator", "", "bar/foo.proto", "Foo");
}
+TEST_F(CommandLineInterfaceTest, PathWithEqualsSign) {
+ // Test setting up a search path which happens to have '=' in it.
+
+ CreateTempDir("with=sign");
+ CreateTempFile("with=sign/foo.proto",
+ "syntax = \"proto2\";\n"
+ "message Foo {}\n");
+
+ Run("protocol_compiler --test_out=$tmpdir "
+ "--proto_path=$tmpdir/with=sign foo.proto");
+
+ ExpectNoErrors();
+ ExpectGenerated("test_generator", "", "foo.proto", "Foo");
+}
+
TEST_F(CommandLineInterfaceTest, MultipleGenerators) {
// Test that we can have multiple generators and use both in one invocation,
// each with a different output directory.
diff --git a/src/google/protobuf/compiler/js/js_generator.cc b/src/google/protobuf/compiler/js/js_generator.cc
index a72cf6a3..8fb24bed 100755
--- a/src/google/protobuf/compiler/js/js_generator.cc
+++ b/src/google/protobuf/compiler/js/js_generator.cc
@@ -159,8 +159,16 @@ string GetJSFilename(const string& filename) {
// Given a filename like foo/bar/baz.proto, returns the root directory
// path ../../
-string GetRootPath(const string& filename) {
- size_t slashes = std::count(filename.begin(), filename.end(), '/');
+string GetRootPath(const string& from_filename, const string& to_filename) {
+ if (to_filename.find("google/protobuf") == 0) {
+ // Well-known types (.proto files in the google/protobuf directory) are
+ // assumed to come from the 'google-protobuf' npm package. We may want to
+ // generalize this exception later by letting others put generated code in
+ // their own npm packages.
+ return "google-protobuf/";
+ }
+
+ size_t slashes = std::count(from_filename.begin(), from_filename.end(), '/');
if (slashes == 0) {
return "./";
}
@@ -2838,7 +2846,7 @@ void Generator::GenerateFile(const GeneratorOptions& options,
printer->Print(
"var $alias$ = require('$file$');\n",
"alias", ModuleAlias(name),
- "file", GetRootPath(file->name()) + GetJSFilename(name));
+ "file", GetRootPath(file->name(), name) + GetJSFilename(name));
}
}
diff --git a/src/google/protobuf/compiler/objectivec/objectivec_file.cc b/src/google/protobuf/compiler/objectivec/objectivec_file.cc
index 7774058e..cf8c34e1 100644
--- a/src/google/protobuf/compiler/objectivec/objectivec_file.cc
+++ b/src/google/protobuf/compiler/objectivec/objectivec_file.cc
@@ -37,8 +37,12 @@
#include <google/protobuf/io/zero_copy_stream_impl.h>
#include <google/protobuf/stubs/stl_util.h>
#include <google/protobuf/stubs/strutil.h>
+#include <iostream>
#include <sstream>
+// NOTE: src/google/protobuf/compiler/plugin.cc makes use of cerr for some
+// error cases, so it seems to be ok to use as a back door for errors.
+
namespace google {
namespace protobuf {
@@ -54,33 +58,78 @@ namespace {
class ImportWriter {
public:
- ImportWriter() {}
+ ImportWriter(const Options& options)
+ : options_(options),
+ need_to_parse_mapping_file_(true) {}
void AddFile(const FileGenerator* file);
void Print(io::Printer *printer) const;
private:
+ class ProtoFrameworkCollector : public LineConsumer {
+ public:
+ ProtoFrameworkCollector(map<string, string>* inout_proto_file_to_framework_name)
+ : map_(inout_proto_file_to_framework_name) {}
+
+ virtual bool ConsumeLine(const StringPiece& line, string* out_error);
+
+ private:
+ map<string, string>* map_;
+ };
+
+ void ParseFrameworkMappings();
+
+ const Options options_;
+ map<string, string> proto_file_to_framework_name_;
+ bool need_to_parse_mapping_file_;
+
vector<string> protobuf_framework_imports_;
vector<string> protobuf_non_framework_imports_;
+ vector<string> other_framework_imports_;
vector<string> other_imports_;
};
void ImportWriter::AddFile(const FileGenerator* file) {
const FileDescriptor* file_descriptor = file->Descriptor();
const string extension(".pbobjc.h");
+
if (IsProtobufLibraryBundledProtoFile(file_descriptor)) {
protobuf_framework_imports_.push_back(
FilePathBasename(file_descriptor) + extension);
protobuf_non_framework_imports_.push_back(file->Path() + extension);
- } else {
- other_imports_.push_back(file->Path() + extension);
+ return;
+ }
+
+ // Lazy parse any mappings.
+ if (need_to_parse_mapping_file_) {
+ ParseFrameworkMappings();
+ }
+
+ map<string, string>::iterator proto_lookup =
+ proto_file_to_framework_name_.find(file_descriptor->name());
+ if (proto_lookup != proto_file_to_framework_name_.end()) {
+ other_framework_imports_.push_back(
+ proto_lookup->second + "/" +
+ FilePathBasename(file_descriptor) + extension);
+ return;
}
+
+ if (!options_.generate_for_named_framework.empty()) {
+ other_framework_imports_.push_back(
+ options_.generate_for_named_framework + "/" +
+ FilePathBasename(file_descriptor) + extension);
+ return;
+ }
+
+ other_imports_.push_back(file->Path() + extension);
}
-void ImportWriter::Print(io::Printer *printer) const {
+void ImportWriter::Print(io::Printer* printer) const {
assert(protobuf_non_framework_imports_.size() ==
protobuf_framework_imports_.size());
+ bool add_blank_line = false;
+
if (protobuf_framework_imports_.size() > 0) {
const string framework_name(ProtobufLibraryFrameworkName);
const string cpp_symbol(ProtobufFrameworkImportSymbol(framework_name));
@@ -106,12 +155,29 @@ void ImportWriter::Print(io::Printer *printer) const {
printer->Print(
"#endif\n");
- if (other_imports_.size() > 0) {
+ add_blank_line = true;
+ }
+
+ if (other_framework_imports_.size() > 0) {
+ if (add_blank_line) {
printer->Print("\n");
}
+
+ for (vector<string>::const_iterator iter = other_framework_imports_.begin();
+ iter != other_framework_imports_.end(); ++iter) {
+ printer->Print(
+ " #import <$header$>\n",
+ "header", *iter);
+ }
+
+ add_blank_line = true;
}
if (other_imports_.size() > 0) {
+ if (add_blank_line) {
+ printer->Print("\n");
+ }
+
for (vector<string>::const_iterator iter = other_imports_.begin();
iter != other_imports_.end(); ++iter) {
printer->Print(
@@ -121,6 +187,69 @@ void ImportWriter::Print(io::Printer *printer) const {
}
}
+void ImportWriter::ParseFrameworkMappings() {
+ need_to_parse_mapping_file_ = false;
+ if (options_.named_framework_to_proto_path_mappings_path.empty()) {
+ return; // Nothing to do.
+ }
+
+ ProtoFrameworkCollector collector(&proto_file_to_framework_name_);
+ string parse_error;
+ if (!ParseSimpleFile(options_.named_framework_to_proto_path_mappings_path,
+ &collector, &parse_error)) {
+ cerr << "error parsing " << options_.named_framework_to_proto_path_mappings_path
+ << " : " << parse_error << endl;
+ cerr.flush();
+ }
+}
+
+bool ImportWriter::ProtoFrameworkCollector::ConsumeLine(
+ const StringPiece& line, string* out_error) {
+ int offset = line.find(':');
+ if (offset == StringPiece::npos) {
+ *out_error =
+ string("Framework/proto file mapping line without colon sign: '") +
+ line.ToString() + "'.";
+ return false;
+ }
+ StringPiece framework_name(line, 0, offset);
+ StringPiece proto_file_list(line, offset + 1, line.length() - offset - 1);
+ StringPieceTrimWhitespace(&framework_name);
+
+ int start = 0;
+ while (start < proto_file_list.length()) {
+ offset = proto_file_list.find(',', start);
+ if (offset == StringPiece::npos) {
+ offset = proto_file_list.length();
+ }
+
+ StringPiece proto_file(proto_file_list, start, offset);
+ StringPieceTrimWhitespace(&proto_file);
+ if (proto_file.size() != 0) {
+ map<string, string>::iterator existing_entry =
+ map_->find(proto_file.ToString());
+ if (existing_entry != map_->end()) {
+ cerr << "warning: duplicate proto file reference, replacing framework entry for '"
+ << proto_file.ToString() << "' with '" << framework_name.ToString()
+ << "' (was '" << existing_entry->second << "')." << endl;
+ cerr.flush();
+ }
+
+ if (proto_file.find(' ') != StringPiece::npos) {
+ cerr << "note: framework mapping file had a proto file with a space in, hopefully that isn't a missing comma: '"
+ << proto_file.ToString() << "'" << endl;
+ cerr.flush();
+ }
+
+ (*map_)[proto_file.ToString()] = framework_name.ToString();
+ }
+
+ start = offset + 1;
+ }
+
+ return true;
+}
+
} // namespace
@@ -156,7 +285,7 @@ FileGenerator::~FileGenerator() {
}
void FileGenerator::GenerateHeader(io::Printer *printer) {
- PrintFilePreamble(printer, "GPBProtocolBuffers.h");
+ PrintFileRuntimePreamble(printer, "GPBProtocolBuffers.h");
// Add some verification that the generated code matches the source the
// code is being compiled with.
@@ -170,7 +299,7 @@ void FileGenerator::GenerateHeader(io::Printer *printer) {
// #import any headers for "public imports" in the proto file.
{
- ImportWriter import_writer;
+ ImportWriter import_writer(options_);
const vector<FileGenerator *> &dependency_generators = DependencyGenerators();
for (vector<FileGenerator *>::const_iterator iter =
dependency_generators.begin();
@@ -273,10 +402,10 @@ void FileGenerator::GenerateHeader(io::Printer *printer) {
void FileGenerator::GenerateSource(io::Printer *printer) {
// #import the runtime support.
- PrintFilePreamble(printer, "GPBProtocolBuffers_RuntimeSupport.h");
+ PrintFileRuntimePreamble(printer, "GPBProtocolBuffers_RuntimeSupport.h");
{
- ImportWriter import_writer;
+ ImportWriter import_writer(options_);
// #import the header for this proto file.
import_writer.AddFile(this);
@@ -471,7 +600,10 @@ const vector<FileGenerator *> &FileGenerator::DependencyGenerators() {
return dependency_generators_;
}
-void FileGenerator::PrintFilePreamble(
+// Helper to print the import of the runtime support at the top of generated
+// files. This currently only supports the runtime coming from a framework
+// as defined by the official CocoaPod.
+void FileGenerator::PrintFileRuntimePreamble(
io::Printer* printer, const string& header_to_import) const {
printer->Print(
"// Generated by the protocol buffer compiler. DO NOT EDIT!\n"
diff --git a/src/google/protobuf/compiler/objectivec/objectivec_file.h b/src/google/protobuf/compiler/objectivec/objectivec_file.h
index 7326c901..8e4388d8 100644
--- a/src/google/protobuf/compiler/objectivec/objectivec_file.h
+++ b/src/google/protobuf/compiler/objectivec/objectivec_file.h
@@ -88,7 +88,7 @@ class FileGenerator {
const Options options_;
const vector<FileGenerator*>& DependencyGenerators();
- void PrintFilePreamble(
+ void PrintFileRuntimePreamble(
io::Printer* printer, const string& header_to_import) const;
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(FileGenerator);
diff --git a/src/google/protobuf/compiler/objectivec/objectivec_generator.cc b/src/google/protobuf/compiler/objectivec/objectivec_generator.cc
index 72e295de..29a8765c 100644
--- a/src/google/protobuf/compiler/objectivec/objectivec_generator.cc
+++ b/src/google/protobuf/compiler/objectivec/objectivec_generator.cc
@@ -58,7 +58,48 @@ bool ObjectiveCGenerator::Generate(const FileDescriptor* file,
ParseGeneratorParameter(parameter, &options);
for (int i = 0; i < options.size(); i++) {
if (options[i].first == "expected_prefixes_path") {
+ // Path to find a file containing the expected prefixes
+ // (objc_class_prefix "PREFIX") for proto packages (package NAME). The
+ // generator will then issue warnings/errors if in the proto files being
+ // generated the option is not listed/wrong/etc in the file.
+ //
+ // The format of the file is:
+ // - An entry is a line of "package=prefix".
+ // - Comments start with "#".
+ // - A comment can go on a line after a expected package/prefix pair.
+ // (i.e. - "package=prefix # comment")
+ //
+ // There is no validation that the prefixes are good prefixes, it is
+ // assume they are when you create the file.
generation_options.expected_prefixes_path = options[i].second;
+ } else if (options[i].first == "generate_for_named_framework") {
+ // The name of the framework that protos are being generated for. This
+ // will cause the #import statements to be framework based using this
+ // name (i.e. - "#import <NAME/proto.pbobjc.h>).
+ //
+ // NOTE: If this option is used with
+ // named_framework_to_proto_path_mappings_path, then this is effectively
+ // the "default" to use for everything that wasn't mapped by the other.
+ generation_options.named_framework_to_proto_path_mappings_path = options[i].second;
+ } else if (options[i].first == "named_framework_to_proto_path_mappings_path") {
+ // Path to find a file containing the listing of framework names and
+ // proto files. The generator uses this to decide if another proto file
+ // referenced should use a framework style import vs. a user level import
+ // (#import <FRAMEWORK/file.pbobjc.h> vs #import "dir/file.pbobjc.h").
+ //
+ // The format of the file is:
+ // - An entry is a line of "frameworkName: file.proto, dir/file2.proto".
+ // - Comments start with "#".
+ // - A comment can go on a line after a expected package/prefix pair.
+ // (i.e. - "frameworkName: file.proto # comment")
+ //
+ // Any number of files can be listed for a framework, just separate them
+ // with commas.
+ //
+ // There can be multiple lines listing the same frameworkName incase it
+ // has a lot of proto files included in it; and having multiple lines
+ // makes things easier to read.
+ generation_options.named_framework_to_proto_path_mappings_path = options[i].second;
} else {
*error = "error: Unknown generator option: " + options[i].first;
return false;
diff --git a/src/google/protobuf/compiler/objectivec/objectivec_helpers.cc b/src/google/protobuf/compiler/objectivec/objectivec_helpers.cc
index 65bf8348..311bf35d 100644
--- a/src/google/protobuf/compiler/objectivec/objectivec_helpers.cc
+++ b/src/google/protobuf/compiler/objectivec/objectivec_helpers.cc
@@ -81,6 +81,10 @@ const char* const kUpperSegmentsList[] = {"url", "http", "https"};
hash_set<string> kUpperSegments =
MakeWordsMap(kUpperSegmentsList, GOOGLE_ARRAYSIZE(kUpperSegmentsList));
+bool ascii_isnewline(char c) {
+ return c == '\n' || c == '\r';
+}
+
// Internal helper for name handing.
// Do not expose this outside of helpers, stick to having functions for specific
// cases (ClassName(), FieldName()), so there is always consistent suffix rules.
@@ -272,6 +276,16 @@ string StripProto(const string& filename) {
}
}
+void StringPieceTrimWhitespace(StringPiece* input) {
+ while (!input->empty() && ascii_isspace(*input->data())) {
+ input->remove_prefix(1);
+ }
+ while (!input->empty() && ascii_isspace((*input)[input->length() - 1])) {
+ input->remove_suffix(1);
+ }
+}
+
+
bool IsRetainedName(const string& name) {
// List of prefixes from
// http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmRules.html
@@ -871,65 +885,6 @@ bool IsProtobufLibraryBundledProtoFile(const FileDescriptor* file) {
return false;
}
-namespace {
-
-// Internal helper class that parses the expected package to prefix mappings
-// file.
-class Parser {
- public:
- Parser(map<string, string>* inout_package_to_prefix_map)
- : prefix_map_(inout_package_to_prefix_map), line_(0) {}
-
- // Parses a check of input, returning success/failure.
- bool ParseChunk(StringPiece chunk);
-
- // Should be called to finish parsing (after all input has been provided via
- // ParseChunk()). Returns success/failure.
- bool Finish();
-
- int last_line() const { return line_; }
- string error_str() const { return error_str_; }
-
- private:
- bool ParseLoop();
-
- map<string, string>* prefix_map_;
- int line_;
- string error_str_;
- StringPiece p_;
- string leftover_;
-};
-
-bool Parser::ParseChunk(StringPiece chunk) {
- if (!leftover_.empty()) {
- chunk.AppendToString(&leftover_);
- p_ = StringPiece(leftover_);
- } else {
- p_ = chunk;
- }
- bool result = ParseLoop();
- if (p_.empty()) {
- leftover_.clear();
- } else {
- leftover_ = p_.ToString();
- }
- return result;
-}
-
-bool Parser::Finish() {
- if (leftover_.empty()) {
- return true;
- }
- // Force a newline onto the end to finish parsing.
- p_ = StringPiece(leftover_ + "\n");
- if (!ParseLoop()) {
- return false;
- }
- return p_.empty(); // Everything used?
-}
-
-static bool ascii_isnewline(char c) { return c == '\n' || c == '\r'; }
-
bool ReadLine(StringPiece* input, StringPiece* line) {
for (int len = 0; len < input->size(); ++len) {
if (ascii_isnewline((*input)[len])) {
@@ -942,15 +897,6 @@ bool ReadLine(StringPiece* input, StringPiece* line) {
return false; // Ran out of input with no newline.
}
-void TrimWhitespace(StringPiece* input) {
- while (!input->empty() && ascii_isspace(*input->data())) {
- input->remove_prefix(1);
- }
- while (!input->empty() && ascii_isspace((*input)[input->length() - 1])) {
- input->remove_suffix(1);
- }
-}
-
void RemoveComment(StringPiece* input) {
int offset = input->find('#');
if (offset != StringPiece::npos) {
@@ -958,29 +904,35 @@ void RemoveComment(StringPiece* input) {
}
}
-bool Parser::ParseLoop() {
- StringPiece line;
- while (ReadLine(&p_, &line)) {
- ++line_;
- RemoveComment(&line);
- TrimWhitespace(&line);
- if (line.size() == 0) {
- continue; // Blank line.
- }
- int offset = line.find('=');
- if (offset == StringPiece::npos) {
- error_str_ =
- string("Line without equal sign: '") + line.ToString() + "'.";
- return false;
- }
- StringPiece package(line, 0, offset);
- StringPiece prefix(line, offset + 1, line.length() - offset - 1);
- TrimWhitespace(&package);
- TrimWhitespace(&prefix);
- // Don't really worry about error checking the package/prefix for
- // being valid. Assume the file is validated when it is created/edited.
- (*prefix_map_)[package.ToString()] = prefix.ToString();
+namespace {
+
+class ExpectedPrefixesCollector : public LineConsumer {
+ public:
+ ExpectedPrefixesCollector(map<string, string>* inout_package_to_prefix_map)
+ : prefix_map_(inout_package_to_prefix_map) {}
+
+ virtual bool ConsumeLine(const StringPiece& line, string* out_error);
+
+ private:
+ map<string, string>* prefix_map_;
+};
+
+bool ExpectedPrefixesCollector::ConsumeLine(
+ const StringPiece& line, string* out_error) {
+ int offset = line.find('=');
+ if (offset == StringPiece::npos) {
+ *out_error =
+ string("Expected prefixes file line without equal sign: '") +
+ line.ToString() + "'.";
+ return false;
}
+ StringPiece package(line, 0, offset);
+ StringPiece prefix(line, offset + 1, line.length() - offset - 1);
+ StringPieceTrimWhitespace(&package);
+ StringPieceTrimWhitespace(&prefix);
+ // Don't really worry about error checking the package/prefix for
+ // being valid. Assume the file is validated when it is created/edited.
+ (*prefix_map_)[package.ToString()] = prefix.ToString();
return true;
}
@@ -991,36 +943,9 @@ bool LoadExpectedPackagePrefixes(const Options &generation_options,
return true;
}
- int fd;
- do {
- fd = open(generation_options.expected_prefixes_path.c_str(), O_RDONLY);
- } while (fd < 0 && errno == EINTR);
- if (fd < 0) {
- *out_error =
- string("error: Unable to open \"") +
- generation_options.expected_prefixes_path +
- "\", " + strerror(errno);
- return false;
- }
- io::FileInputStream file_stream(fd);
- file_stream.SetCloseOnDelete(true);
-
- Parser parser(prefix_map);
- const void* buf;
- int buf_len;
- while (file_stream.Next(&buf, &buf_len)) {
- if (buf_len == 0) {
- continue;
- }
-
- if (!parser.ParseChunk(StringPiece(static_cast<const char*>(buf), buf_len))) {
- *out_error =
- string("error: ") + generation_options.expected_prefixes_path +
- " Line " + SimpleItoa(parser.last_line()) + ", " + parser.error_str();
- return false;
- }
- }
- return parser.Finish();
+ ExpectedPrefixesCollector collector(prefix_map);
+ return ParseSimpleFile(
+ generation_options.expected_prefixes_path, &collector, out_error);
}
} // namespace
@@ -1121,6 +1046,10 @@ bool ValidateObjCClassPrefix(const FileDescriptor* file,
return true;
}
+TextFormatDecodeData::TextFormatDecodeData() { }
+
+TextFormatDecodeData::~TextFormatDecodeData() { }
+
void TextFormatDecodeData::AddString(int32 key,
const string& input_for_decode,
const string& desired_output) {
@@ -1329,6 +1258,116 @@ string TextFormatDecodeData::DecodeDataForString(const string& input_for_decode,
return builder.Finish() + (char)'\0';
}
+namespace {
+
+class Parser {
+ public:
+ Parser(LineConsumer* line_consumer)
+ : line_consumer_(line_consumer), line_(0) {}
+
+ // Parses a check of input, returning success/failure.
+ bool ParseChunk(StringPiece chunk);
+
+ // Should be called to finish parsing (after all input has been provided via
+ // ParseChunk()). Returns success/failure.
+ bool Finish();
+
+ int last_line() const { return line_; }
+ string error_str() const { return error_str_; }
+
+ private:
+ bool ParseLoop();
+
+ LineConsumer* line_consumer_;
+ int line_;
+ string error_str_;
+ StringPiece p_;
+ string leftover_;
+};
+
+bool Parser::ParseChunk(StringPiece chunk) {
+ if (!leftover_.empty()) {
+ chunk.AppendToString(&leftover_);
+ p_ = StringPiece(leftover_);
+ } else {
+ p_ = chunk;
+ }
+ bool result = ParseLoop();
+ if (p_.empty()) {
+ leftover_.clear();
+ } else {
+ leftover_ = p_.ToString();
+ }
+ return result;
+}
+
+bool Parser::Finish() {
+ if (leftover_.empty()) {
+ return true;
+ }
+ // Force a newline onto the end to finish parsing.
+ p_ = StringPiece(leftover_ + "\n");
+ if (!ParseLoop()) {
+ return false;
+ }
+ return p_.empty(); // Everything used?
+}
+
+bool Parser::ParseLoop() {
+ StringPiece line;
+ while (ReadLine(&p_, &line)) {
+ ++line_;
+ RemoveComment(&line);
+ StringPieceTrimWhitespace(&line);
+ if (line.size() == 0) {
+ continue; // Blank line.
+ }
+ if (!line_consumer_->ConsumeLine(line, &error_str_)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+} // namespace
+
+LineConsumer::LineConsumer() {}
+
+LineConsumer::~LineConsumer() {}
+
+bool ParseSimpleFile(
+ const string& path, LineConsumer* line_consumer, string* out_error) {
+ int fd;
+ do {
+ fd = open(path.c_str(), O_RDONLY);
+ } while (fd < 0 && errno == EINTR);
+ if (fd < 0) {
+ *out_error =
+ string("error: Unable to open \"") + path + "\", " + strerror(errno);
+ return false;
+ }
+ io::FileInputStream file_stream(fd);
+ file_stream.SetCloseOnDelete(true);
+
+ Parser parser(line_consumer);
+ const void* buf;
+ int buf_len;
+ while (file_stream.Next(&buf, &buf_len)) {
+ if (buf_len == 0) {
+ continue;
+ }
+
+ if (!parser.ParseChunk(StringPiece(static_cast<const char*>(buf), buf_len))) {
+ *out_error =
+ string("error: ") + path +
+ " Line " + SimpleItoa(parser.last_line()) + ", " + parser.error_str();
+ return false;
+ }
+ }
+ return parser.Finish();
+}
+
+
} // namespace objectivec
} // namespace compiler
} // namespace protobuf
diff --git a/src/google/protobuf/compiler/objectivec/objectivec_helpers.h b/src/google/protobuf/compiler/objectivec/objectivec_helpers.h
index 5898d692..be20beee 100644
--- a/src/google/protobuf/compiler/objectivec/objectivec_helpers.h
+++ b/src/google/protobuf/compiler/objectivec/objectivec_helpers.h
@@ -46,6 +46,8 @@ namespace objectivec {
struct Options {
Options();
string expected_prefixes_path;
+ string generate_for_named_framework;
+ string named_framework_to_proto_path_mappings_path;
};
// Escape C++ trigraphs by escaping question marks to "\?".
@@ -54,6 +56,9 @@ string EscapeTrigraphs(const string& to_escape);
// Strips ".proto" or ".protodevel" from the end of a filename.
string StripProto(const string& filename);
+// Remove white space from either end of a StringPiece.
+void StringPieceTrimWhitespace(StringPiece* input);
+
// Returns true if the name requires a ns_returns_not_retained attribute applied
// to it.
bool IsRetainedName(const string& name);
@@ -189,7 +194,8 @@ bool ValidateObjCClassPrefix(const FileDescriptor* file,
// the input into the expected output.
class LIBPROTOC_EXPORT TextFormatDecodeData {
public:
- TextFormatDecodeData() {}
+ TextFormatDecodeData();
+ ~TextFormatDecodeData();
void AddString(int32 key, const string& input_for_decode,
const string& desired_output);
@@ -206,6 +212,17 @@ class LIBPROTOC_EXPORT TextFormatDecodeData {
vector<DataEntry> entries_;
};
+// Helper for parsing simple files.
+class LIBPROTOC_EXPORT LineConsumer {
+ public:
+ LineConsumer();
+ virtual ~LineConsumer();
+ virtual bool ConsumeLine(const StringPiece& line, string* out_error) = 0;
+};
+
+bool ParseSimpleFile(
+ const string& path, LineConsumer* line_consumer, string* out_error);
+
} // namespace objectivec
} // namespace compiler
} // namespace protobuf
diff --git a/src/google/protobuf/map.h b/src/google/protobuf/map.h
index 6f1a71e4..31593c1a 100644
--- a/src/google/protobuf/map.h
+++ b/src/google/protobuf/map.h
@@ -587,7 +587,7 @@ class Map {
explicit MapAllocator(Arena* arena) : arena_(arena) {}
template <typename X>
MapAllocator(const MapAllocator<X>& allocator)
- : arena_(allocator.arena_) {}
+ : arena_(allocator.arena()) {}
pointer allocate(size_type n, const_pointer hint = 0) {
// If arena is not given, malloc needs to be called which doesn't
@@ -650,12 +650,15 @@ class Map {
return std::numeric_limits<size_type>::max();
}
+ // To support gcc-4.4, which does not properly
+ // support templated friend classes
+ Arena* arena() const {
+ return arena_;
+ }
+
private:
typedef void DestructorSkippable_;
Arena* const arena_;
-
- template <typename X>
- friend class MapAllocator;
};
// InnerMap's key type is Key and its value type is value_type*. We use a
diff --git a/tests.sh b/tests.sh
index cde108fd..72465a7b 100755
--- a/tests.sh
+++ b/tests.sh
@@ -223,6 +223,17 @@ build_objectivec_osx() {
--core-only --skip-xcode-ios
}
+build_objectivec_cocoapods_integration() {
+ # First, load the RVM environment in bash, needed to update ruby.
+ source ~/.rvm/scripts/rvm
+ # Update ruby to 2.2.3 as the default one crashes with segmentation faults
+ # when using pod.
+ rvm use 2.2.3 --install --binary --fuzzy
+ # Update pod to the latest version.
+ gem install cocoapods --no-ri --no-rdoc
+ objectivec/Tests/CocoaPods/run_tests.sh
+}
+
build_python() {
internal_build_cpp
internal_install_python_deps
@@ -304,6 +315,7 @@ Usage: $0 { cpp |
objectivec_ios_debug |
objectivec_ios_release |
objectivec_osx |
+ objectivec_cocoapods_integration |
python |
python_cpp |
ruby19 |