Files
firebase-ios-sdk/FirebasePerformance/ProtoSupport/nanopb_objc_generator.py
T
Jeremy JiangandPaul Beusterien ec193d0150 Add Performance Support for Swift Package Manager (#8412)
* Create NanoPb code generation scripts (#8336)

* Create NanoPb code generation scripts

* Fix style and add README

* Fix copyright

* Update proto_generator.py

* Update README.md

* Create nanopb options file. (#8371)

* Create NanoPb code generation scripts

* Fix style and add README

* Fix copyright

* Update perf_metrics.options

* Update options with map

* Add generated proto files.

* Update imports.

* Convert Objective-C Classes to Nanopb (#8387)

* Convert all traces to nanopb

* Refactor for loop.

* Add set has_application_info to true.

* Incorporated comments.

* Fix style

* Create NanoPb code generation scripts (#8336)

* Create NanoPb code generation scripts

* Fix style and add README

* Fix copyright

* Update proto_generator.py

* Update README.md

* Create nanopb options file. (#8371)

* Create NanoPb code generation scripts

* Fix style and add README

* Fix copyright

* Update perf_metrics.options

* Update options with map

* Add generated proto files.

* Update imports.

* Convert Objective-C Classes to Nanopb (#8387)

* Convert all traces to nanopb

* Refactor for loop.

* Add set has_application_info to true.

* Incorporated comments.

* Fix style

* Fix style

* Convert nanopb struct to GDTCOREventDataObject and update the source code to use nanopb struct. (#8439)

* Convert all traces to nanopb

* Refactor for loop.

* WIP: nanopb conversion in FPRClient and FPREvent

* Update FPRClientTest.m

* Update FPRClient.m

* Update FPRClientTest.m

* Add set has_application_info to true.

* Remove proto imports.

* Set has_fields to be true in FPRTestUtils.m

* Completed GDTEvent conversion.

* Fix sampler tests.

* Fix style

* Refactor nanopb struct setter

* Fix style

* Fix pod-lib-lint failures.

* Update FPRClient with decoded string

* Set some "_has" fields to true.

* Add prefix to extern methods.

* Fix style.

* Update FPRClient to use trace.name without FPRDecodeString

* Fix style

* Incorporate comments.

* Integrate Performance with SPM (#8476)

* Convert all traces to nanopb

* Refactor for loop.

* WIP: nanopb conversion in FPRClient and FPREvent

* Update FPRClientTest.m

* Update FPRClient.m

* Update FPRClientTest.m

* Add set has_application_info to true.

* Remove proto imports.

* Set has_fields to be true in FPRTestUtils.m

* Completed GDTEvent conversion.

* Fix sampler tests.

* Fix style

* Refactor nanopb struct setter

* Fix style

* Fix pod-lib-lint failures.

* Update FPRClient with decoded string

* Set some "_has" fields to true.

* Add prefix to extern methods.

* Fix style.

* Update FPRClient to use trace.name without FPRDecodeString

* Fix style

* Update Package.swift

* Update Package.swift

* Update Package.swift

* Made SDK and unit tests buildable.

* Made all unit tests passing.

* Update keyWindow style.

* Add Performance to workflow.

* Update FPRUIViewControllerInstrument.h

* Incorporate comments.

* Add Performance to SwiftPMTests.

* Update style.

* Fix Unit tests and update workflow.

* Add FirebasePerformance wrapper

* Update import statement.

* Update style.

* Update performance.yml

* Create PerformanceUnit.xcscheme

* Update objc-module.m

* Update objc-module.m

* Updae objc-header.m

* Update objc-header.m

* Incorporate feedback.

* Update objc-header.m

* Organize folder structure to fix the spm tests.

* Update public import path

* Delete xcodebuild.log

* Update workflow files

* Fix failing swift-test for Perf SPM (#8507)

* Fix UIKit import

* Update FPRNetworkTraceTest.m

* Exclude TARGET_OS_MACCATALYST from objc header test

* Update main.swift

* Update main.swift

Co-authored-by: Paul Beusterien <paulbeusterien@google.com>

* Remove Protobuf dependency  (#8511)

* Remove protobuf dependency

* Update FirebasePerformance.podspec

* Incorporate comments.

* Update UIKit import

Co-authored-by: Paul Beusterien <paulbeusterien@google.com>
2021-08-10 13:41:18 -04:00

222 lines
6.7 KiB
Python
Executable File

#!/usr/bin/env python
# Copyright 2021 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Generates and massages protocol buffer outputs.
"""
from __future__ import print_function
import sys
import io
import nanopb_generator as nanopb
import os
import os.path
import shlex
from google.protobuf.descriptor_pb2 import FieldDescriptorProto
# The plugin_pb2 package loads descriptors on import, but doesn't defend
# against multiple imports. Reuse the plugin package as loaded by the
# nanopb_generator.
plugin_pb2 = nanopb.plugin_pb2
nanopb_pb2 = nanopb.nanopb_pb2
def main():
# Parse request
data = io.open(sys.stdin.fileno(), 'rb').read()
request = plugin_pb2.CodeGeneratorRequest.FromString(data)
# Preprocess inputs, changing types and nanopb defaults
options = nanopb_parse_options(request)
use_anonymous_oneof(request)
use_bytes_for_strings(request)
# Generate code
parsed_files = nanopb_parse_files(request, options)
results = nanopb_generate(request, options, parsed_files)
response = nanopb_write(results)
# Write to stdout
io.open(sys.stdout.fileno(), 'wb').write(response.SerializeToString())
def use_anonymous_oneof(request):
"""Use anonymous unions for oneofs if they're the only one in a message.
Equivalent to setting this option on messages where it applies:
option (nanopb).anonymous_oneof = true;
Args:
request: A CodeGeneratorRequest from protoc. The descriptors are modified
in place.
"""
for _, message_type in iterate_messages(request):
if len(message_type.oneof_decl) == 1:
ext = message_type.options.Extensions[nanopb_pb2.nanopb_msgopt]
ext.anonymous_oneof = True
def use_bytes_for_strings(request):
"""Always use the bytes type instead of string.
By default, nanopb renders proto strings as having the C type char* and does
not include a separate size field, getting the length of the string via
strlen(). Unfortunately this prevents using strings with embedded nulls,
which is something the wire format supports.
Fortunately, string and bytes proto fields are identical on the wire and
nanopb's bytes representation does have an explicit length, so this function
changes the types of all string fields to bytes. The generated code will now
contain pb_bytes_array_t.
There's no nanopb or proto option to control this behavior. The equivalent
would be to hand edit all the .proto files :-(.
Args:
request: A CodeGeneratorRequest from protoc. The descriptors are modified
in place.
"""
for names, message_type in iterate_messages(request):
for field in message_type.field:
if field.type == FieldDescriptorProto.TYPE_STRING:
field.type = FieldDescriptorProto.TYPE_BYTES
def iterate_messages(request):
"""Iterates over all messages in all files in the request.
Args:
request: A CodeGeneratorRequest passed by protoc.
Yields:
names: a nanopb.Names object giving a qualified name for the message
message_type: a DescriptorProto for the message.
"""
for fdesc in request.proto_file:
for names, message_type in nanopb.iterate_messages(fdesc):
yield names, message_type
def nanopb_parse_options(request):
"""Parses nanopb_generator command-line options from the given request.
Args:
request: A CodeGeneratorRequest passed by protoc.
Returns:
Nanopb's options object, obtained via optparser.
"""
# Parse options the same as nanopb_generator.main_plugin() does.
args = shlex.split(request.parameter)
options, _ = nanopb.optparser.parse_args(args)
# Force certain options
options.extension = '.nanopb'
options.verbose = True
# Replicate options setup from nanopb_generator.main_plugin.
nanopb.Globals.verbose_options = options.verbose
# Google's protoc does not currently indicate the full path of proto files.
# Instead always add the main file path to the search dirs, that works for
# the common case.
options.options_path.append(os.path.dirname(request.file_to_generate[0]))
return options
def nanopb_parse_files(request, options):
"""Parses the files in the given request into nanopb ProtoFile objects.
Args:
request: A CodeGeneratorRequest, as passed by protoc.
options: The command-line options from nanopb_parse_options.
Returns:
A dictionary of filename to nanopb.ProtoFile objects, each one representing
the parsed form of a FileDescriptor in the request.
"""
# Process any include files first, in order to have them
# available as dependencies
parsed_files = {}
for fdesc in request.proto_file:
parsed_files[fdesc.name] = nanopb.parse_file(fdesc.name, fdesc, options)
return parsed_files
def nanopb_generate(request, options, parsed_files):
"""Generates C sources from the given parsed files.
Args:
request: A CodeGeneratorRequest, as passed by protoc.
options: The command-line options from nanopb_parse_options.
parsed_files: A dictionary of filename to nanopb.ProtoFile, as returned by
nanopb_parse_files().
Returns:
A list of nanopb output dictionaries, each one representing the code
generation result for each file to generate. The output dictionaries have
the following form:
{
'headername': Name of header file, ending in .h,
'headerdata': Contents of the header file,
'sourcename': Name of the source code file, ending in .c,
'sourcedata': Contents of the source code file
}
"""
output = []
for filename in request.file_to_generate:
for fdesc in request.proto_file:
if fdesc.name == filename:
results = nanopb.process_file(filename, fdesc, options, parsed_files)
output.append(results)
return output
def nanopb_write(results):
"""Translates nanopb output dictionaries to a CodeGeneratorResponse.
Args:
results: A list of generated source dictionaries, as returned by
nanopb_generate().
Returns:
A CodeGeneratorResponse describing the result of the code generation
process to protoc.
"""
response = plugin_pb2.CodeGeneratorResponse()
for result in results:
f = response.file.add()
f.name = result['headername']
f.content = result['headerdata']
f = response.file.add()
f.name = result['sourcename']
f.content = result['sourcedata']
return response
if __name__ == '__main__':
main()