Skip to content

Commit

Permalink
Add a Protoc C++ tool.
Browse files Browse the repository at this point in the history
Example:

    prog = cxx.Program('prog')
    protos = builder.tools.Protoc(sources = ['myproto.proto'])
    prog.custom += [protos]

To make this work, protoc is now always executed in the top level of the
build directory. This is kind of gross as we have to make sure all paths
are relative to the root, rather than cwd. The alternative is running
protoc once for each language, which would be fine, but the current
thing is working.
  • Loading branch information
dvander committed Nov 9, 2023
1 parent 7bf999b commit 2d4620d
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 13 deletions.
1 change: 1 addition & 0 deletions ambuild2/frontend/v2_2/tools/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@

from ambuild2.frontend.v2_2.tools.fxc import FxcJob as FXC
from ambuild2.frontend.v2_2.tools.protoc import DetectProtoc as DetectProtoc
from ambuild2.frontend.v2_2.tools.protoc import ProtocJob as Protoc
63 changes: 50 additions & 13 deletions ambuild2/frontend/v2_2/tools/protoc.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@
import subprocess
from ambuild2 import util
from ambuild2.frontend.version import Version
from ambuild2.frontend import paths

# Helper for building AddCommand() invocations for protoc.
class ProtocRunner(object):
def __init__(self, protoc, builder, includes):
self.protoc = protoc
Expand All @@ -46,8 +48,12 @@ def AddOutput(self, language, folder):
}
self.gen_map[language] = {}

out_build_path = os.path.relpath(folder.path, self.builder.buildFolder)
self.argv += ['--{}_out={}'.format(language, out_build_path)]
if folder is None:
folder_path = '.'
else:
folder_path = folder.path

self.argv += ['--{}_out={}'.format(language, folder_path)]

def AddSource(self, source_path):
source_name = os.path.basename(source_path)
Expand All @@ -59,9 +65,11 @@ def AddSource(self, source_path):
gen_file_list = []
gen_file_map = {}
for language in self.languages:
folder = self.languages[language]['folder']

gen_info = gen_file_map.setdefault(language, {
'sources': [],
'headers': [],
'sources': 0,
'headers': 0,
})
gen_source_names = []
gen_header_names = []
Expand All @@ -79,13 +87,13 @@ def AddSource(self, source_path):
else:
raise Exception('Language not supported yet: {}'.format(language))

gen_file_list += gen_source_names
gen_file_list += gen_header_names
for file in gen_source_names + gen_header_names:
gen_file_list += [paths.Join(folder, file)]

gen_info['sources'] += gen_source_names
gen_info['headers'] += gen_header_names
gen_info['sources'] += len(gen_source_names)
gen_info['headers'] += len(gen_header_names)

dep_file = '{}.d'.format(source_name)
dep_file = paths.Join(folder, '{}.d'.format(source_name))
argv = self.argv + [
'--dependency_out={}'.format(dep_file),
source_path,
Expand All @@ -96,16 +104,17 @@ def AddSource(self, source_path):
outputs = gen_file_list,
dep_type = 'md',
dep_file = dep_file,
shared_outputs = [dep_file])
shared_outputs = [dep_file],
folder = None)

# Translate the list of generated output entries.
cursor = 0
for language in self.languages:
gen_info = gen_file_map[language]
gen_sources = gen_entries[cursor:cursor + len(gen_info['sources'])]
gen_sources = gen_entries[cursor:cursor + gen_info['sources']]
cursor += len(gen_sources)

gen_headers = gen_entries[cursor:cursor + len(gen_info['headers'])]
gen_headers = gen_entries[cursor:cursor + gen_info['headers']]
cursor += len(gen_headers)

self.gen_map[language].setdefault('sources', []).extend(gen_sources)
Expand All @@ -115,11 +124,13 @@ def AddSource(self, source_path):
# Should be one entry remaining, for the .d file.
assert (cursor == len(gen_entries))

# Named tuple for protoc.StaticLibrary() results.
class ProtocCppNode(object):
def __init__(self, lib, headers):
self.lib = lib
self.headers = headers

# Like cpp.Compiler, but for protobufs.
class Protoc(object):
def __init__(self, path, name, version):
super(Protoc, self).__init__()
Expand Down Expand Up @@ -168,6 +179,8 @@ def StaticLibrary(self, name, builder, cxx, sources, includes = []):
out = builder.Add(binary)
return ProtocCppNode(out, gen_map['cpp']['headers'])

FoundProtocMap = set()

def DetectProtoc(**kwargs):
path = kwargs.pop('path', None)
if len(kwargs):
Expand All @@ -188,6 +201,30 @@ def DetectProtoc(**kwargs):
name = parts[0]
version = Version(parts[1])

util.con_out(util.ConsoleHeader, 'found protoc {}-{}'.format(name, version))
if path not in FoundProtocMap:
util.con_out(util.ConsoleHeader, 'found protoc {}-{}'.format(name, version))
FoundProtocMap.add(path)

return Protoc(path, name, version)

class ProtocTool(object):
def __init__(self):
pass

def evaluate(self, cmd):
gen_map = cmd.data.protoc.Generate(builder = cmd.context,
sources = cmd.data.sources,
outputs = [('cpp', cmd.localFolderNode)])
cmd.sourcedeps += gen_map['cpp']['sources']
cmd.sourcedeps += gen_map['cpp']['headers']
for source in gen_map['cpp']['sources']:
cmd.sources += [os.path.join(cmd.context.buildPath, source.path)]

class ProtocJob(object):
def __init__(self, protoc = None, sources = []):
if protoc is None:
self.protoc = DetectProtoc()
else:
self.protoc = protoc.clone()
self.sources = sources[:]
self.tool = ProtocTool()

0 comments on commit 2d4620d

Please sign in to comment.