xref: /aosp_15_r20/external/perfetto/tools/gen_amalgamated (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
1*6dbdd20aSAndroid Build Coastguard Worker#!/usr/bin/env python3
2*6dbdd20aSAndroid Build Coastguard Worker# Copyright (C) 2019 The Android Open Source Project
3*6dbdd20aSAndroid Build Coastguard Worker#
4*6dbdd20aSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License");
5*6dbdd20aSAndroid Build Coastguard Worker# you may not use this file except in compliance with the License.
6*6dbdd20aSAndroid Build Coastguard Worker# You may obtain a copy of the License at
7*6dbdd20aSAndroid Build Coastguard Worker#
8*6dbdd20aSAndroid Build Coastguard Worker#      http://www.apache.org/licenses/LICENSE-2.0
9*6dbdd20aSAndroid Build Coastguard Worker#
10*6dbdd20aSAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software
11*6dbdd20aSAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS,
12*6dbdd20aSAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*6dbdd20aSAndroid Build Coastguard Worker# See the License for the specific language governing permissions and
14*6dbdd20aSAndroid Build Coastguard Worker# limitations under the License.
15*6dbdd20aSAndroid Build Coastguard Worker
16*6dbdd20aSAndroid Build Coastguard Worker# This tool uses a collection of BUILD.gn files and build targets to generate
17*6dbdd20aSAndroid Build Coastguard Worker# an "amalgamated" C++ header and source file pair which compiles to an
18*6dbdd20aSAndroid Build Coastguard Worker# equivalent program. The tool also outputs the necessary compiler and linker
19*6dbdd20aSAndroid Build Coastguard Worker# flags needed to compile the resulting source code.
20*6dbdd20aSAndroid Build Coastguard Worker
21*6dbdd20aSAndroid Build Coastguard Workerfrom __future__ import print_function
22*6dbdd20aSAndroid Build Coastguard Workerimport argparse
23*6dbdd20aSAndroid Build Coastguard Workerimport os
24*6dbdd20aSAndroid Build Coastguard Workerimport re
25*6dbdd20aSAndroid Build Coastguard Workerimport shutil
26*6dbdd20aSAndroid Build Coastguard Workerimport subprocess
27*6dbdd20aSAndroid Build Coastguard Workerimport sys
28*6dbdd20aSAndroid Build Coastguard Workerimport tempfile
29*6dbdd20aSAndroid Build Coastguard Worker
30*6dbdd20aSAndroid Build Coastguard Workerimport gn_utils
31*6dbdd20aSAndroid Build Coastguard Worker
32*6dbdd20aSAndroid Build Coastguard Worker# Default targets to include in the result.
33*6dbdd20aSAndroid Build Coastguard Worker# TODO(primiano): change this script to recurse into target deps when generating
34*6dbdd20aSAndroid Build Coastguard Worker# headers, but only for proto targets. .pbzero.h files don't include each other
35*6dbdd20aSAndroid Build Coastguard Worker# and we need to list targets here individually, which is unmaintainable.
36*6dbdd20aSAndroid Build Coastguard Workerdefault_targets = [
37*6dbdd20aSAndroid Build Coastguard Worker    '//:libperfetto_client_experimental',
38*6dbdd20aSAndroid Build Coastguard Worker    '//include/perfetto/protozero:protozero',
39*6dbdd20aSAndroid Build Coastguard Worker    '//protos/perfetto/config:zero',
40*6dbdd20aSAndroid Build Coastguard Worker    '//protos/perfetto/trace:zero',
41*6dbdd20aSAndroid Build Coastguard Worker]
42*6dbdd20aSAndroid Build Coastguard Worker
43*6dbdd20aSAndroid Build Coastguard Worker# Arguments for the GN output directory (unless overridden from the command
44*6dbdd20aSAndroid Build Coastguard Worker# line).
45*6dbdd20aSAndroid Build Coastguard Workergn_args = ' '.join([
46*6dbdd20aSAndroid Build Coastguard Worker    'enable_perfetto_ipc=true',
47*6dbdd20aSAndroid Build Coastguard Worker    'enable_perfetto_zlib=false',
48*6dbdd20aSAndroid Build Coastguard Worker    'is_debug=false',
49*6dbdd20aSAndroid Build Coastguard Worker    'is_perfetto_build_generator=true',
50*6dbdd20aSAndroid Build Coastguard Worker    'is_perfetto_embedder=true',
51*6dbdd20aSAndroid Build Coastguard Worker    'perfetto_enable_git_rev_version_header=true',
52*6dbdd20aSAndroid Build Coastguard Worker    'use_custom_libcxx=false',
53*6dbdd20aSAndroid Build Coastguard Worker])
54*6dbdd20aSAndroid Build Coastguard Worker
55*6dbdd20aSAndroid Build Coastguard Worker# By default, the amalgamated .h only recurses in #includes but not in the
56*6dbdd20aSAndroid Build Coastguard Worker# target deps. In the case of protos we want to follow deps even in lieu of
57*6dbdd20aSAndroid Build Coastguard Worker# direct #includes. This is because, by design, protozero headers don't
58*6dbdd20aSAndroid Build Coastguard Worker# include each other but rely on forward declarations. The alternative would
59*6dbdd20aSAndroid Build Coastguard Worker# be adding each proto sub-target individually (e.g. //proto/trace/gpu:zero),
60*6dbdd20aSAndroid Build Coastguard Worker# but doing that is unmaintainable. We also do this for cpp bindings since some
61*6dbdd20aSAndroid Build Coastguard Worker# tracing SDK functions depend on them (and the system tracing IPC mechanism
62*6dbdd20aSAndroid Build Coastguard Worker# does so too).
63*6dbdd20aSAndroid Build Coastguard Workerrecurse_in_header_deps = '^//protos/.*(cpp|zero)$'
64*6dbdd20aSAndroid Build Coastguard Worker
65*6dbdd20aSAndroid Build Coastguard Worker# Compiler flags which aren't filtered out.
66*6dbdd20aSAndroid Build Coastguard Workercflag_allowlist = r'^-(W.*|fno-exceptions|fPIC|std.*|fvisibility.*)$'
67*6dbdd20aSAndroid Build Coastguard Worker
68*6dbdd20aSAndroid Build Coastguard Worker# Linker flags which aren't filtered out.
69*6dbdd20aSAndroid Build Coastguard Workerldflag_allowlist = r'^-()$'
70*6dbdd20aSAndroid Build Coastguard Worker
71*6dbdd20aSAndroid Build Coastguard Worker# Libraries which are filtered out.
72*6dbdd20aSAndroid Build Coastguard Workerlib_denylist = r'^(c|gcc_eh)$'
73*6dbdd20aSAndroid Build Coastguard Worker
74*6dbdd20aSAndroid Build Coastguard Worker# Macros which aren't filtered out.
75*6dbdd20aSAndroid Build Coastguard Workerdefine_allowlist = r'^(PERFETTO.*|GOOGLE_PROTOBUF.*)$'
76*6dbdd20aSAndroid Build Coastguard Worker
77*6dbdd20aSAndroid Build Coastguard Worker# Includes which will be removed from the generated source.
78*6dbdd20aSAndroid Build Coastguard Workerincludes_to_remove = r'^(gtest).*$'
79*6dbdd20aSAndroid Build Coastguard Worker
80*6dbdd20aSAndroid Build Coastguard Worker# From //gn:default_config (since "gn desc" doesn't describe configs).
81*6dbdd20aSAndroid Build Coastguard Workerdefault_includes = [
82*6dbdd20aSAndroid Build Coastguard Worker  'include',
83*6dbdd20aSAndroid Build Coastguard Worker]
84*6dbdd20aSAndroid Build Coastguard Worker
85*6dbdd20aSAndroid Build Coastguard Workerdefault_cflags = [
86*6dbdd20aSAndroid Build Coastguard Worker    # Since we're expanding header files into the generated source file, some
87*6dbdd20aSAndroid Build Coastguard Worker    # constant may remain unused.
88*6dbdd20aSAndroid Build Coastguard Worker    '-Wno-unused-const-variable'
89*6dbdd20aSAndroid Build Coastguard Worker]
90*6dbdd20aSAndroid Build Coastguard Worker
91*6dbdd20aSAndroid Build Coastguard Worker# Build flags to satisfy a protobuf (lite or full) dependency.
92*6dbdd20aSAndroid Build Coastguard Workerprotobuf_cflags = [
93*6dbdd20aSAndroid Build Coastguard Worker    # Note that these point to the local copy of protobuf in buildtools. In
94*6dbdd20aSAndroid Build Coastguard Worker    # reality the user of the amalgamated result will have to provide a path to
95*6dbdd20aSAndroid Build Coastguard Worker    # an installed copy of the exact same version of protobuf which was used to
96*6dbdd20aSAndroid Build Coastguard Worker    # generate the amalgamated build.
97*6dbdd20aSAndroid Build Coastguard Worker    '-isystembuildtools/protobuf/src',
98*6dbdd20aSAndroid Build Coastguard Worker    '-Lbuildtools/protobuf/src/.libs',
99*6dbdd20aSAndroid Build Coastguard Worker    # We also need to disable some warnings for protobuf.
100*6dbdd20aSAndroid Build Coastguard Worker    '-Wno-missing-prototypes',
101*6dbdd20aSAndroid Build Coastguard Worker    '-Wno-missing-variable-declarations',
102*6dbdd20aSAndroid Build Coastguard Worker    '-Wno-sign-conversion',
103*6dbdd20aSAndroid Build Coastguard Worker    '-Wno-unknown-pragmas',
104*6dbdd20aSAndroid Build Coastguard Worker    '-Wno-unused-macros',
105*6dbdd20aSAndroid Build Coastguard Worker]
106*6dbdd20aSAndroid Build Coastguard Worker
107*6dbdd20aSAndroid Build Coastguard Worker# A mapping of dependencies to system libraries. Libraries in this map will not
108*6dbdd20aSAndroid Build Coastguard Worker# be built statically but instead added as dependencies of the amalgamated
109*6dbdd20aSAndroid Build Coastguard Worker# project.
110*6dbdd20aSAndroid Build Coastguard Workersystem_library_map = {
111*6dbdd20aSAndroid Build Coastguard Worker    '//buildtools:protobuf_full': {
112*6dbdd20aSAndroid Build Coastguard Worker        'libs': ['protobuf'],
113*6dbdd20aSAndroid Build Coastguard Worker        'cflags': protobuf_cflags,
114*6dbdd20aSAndroid Build Coastguard Worker    },
115*6dbdd20aSAndroid Build Coastguard Worker    '//buildtools:protobuf_lite': {
116*6dbdd20aSAndroid Build Coastguard Worker        'libs': ['protobuf-lite'],
117*6dbdd20aSAndroid Build Coastguard Worker        'cflags': protobuf_cflags,
118*6dbdd20aSAndroid Build Coastguard Worker    },
119*6dbdd20aSAndroid Build Coastguard Worker    '//buildtools:protoc_lib': {
120*6dbdd20aSAndroid Build Coastguard Worker        'libs': ['protoc']
121*6dbdd20aSAndroid Build Coastguard Worker    },
122*6dbdd20aSAndroid Build Coastguard Worker}
123*6dbdd20aSAndroid Build Coastguard Worker
124*6dbdd20aSAndroid Build Coastguard Worker# ----------------------------------------------------------------------------
125*6dbdd20aSAndroid Build Coastguard Worker# End of configuration.
126*6dbdd20aSAndroid Build Coastguard Worker# ----------------------------------------------------------------------------
127*6dbdd20aSAndroid Build Coastguard Worker
128*6dbdd20aSAndroid Build Coastguard Workertool_name = os.path.basename(__file__)
129*6dbdd20aSAndroid Build Coastguard Workerproject_root = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
130*6dbdd20aSAndroid Build Coastguard Workerpreamble = """// Copyright (C) 2019 The Android Open Source Project
131*6dbdd20aSAndroid Build Coastguard Worker//
132*6dbdd20aSAndroid Build Coastguard Worker// Licensed under the Apache License, Version 2.0 (the "License");
133*6dbdd20aSAndroid Build Coastguard Worker// you may not use this file except in compliance with the License.
134*6dbdd20aSAndroid Build Coastguard Worker// You may obtain a copy of the License at
135*6dbdd20aSAndroid Build Coastguard Worker//
136*6dbdd20aSAndroid Build Coastguard Worker//      http://www.apache.org/licenses/LICENSE-2.0
137*6dbdd20aSAndroid Build Coastguard Worker//
138*6dbdd20aSAndroid Build Coastguard Worker// Unless required by applicable law or agreed to in writing, software
139*6dbdd20aSAndroid Build Coastguard Worker// distributed under the License is distributed on an "AS IS" BASIS,
140*6dbdd20aSAndroid Build Coastguard Worker// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
141*6dbdd20aSAndroid Build Coastguard Worker// See the License for the specific language governing permissions and
142*6dbdd20aSAndroid Build Coastguard Worker// limitations under the License.
143*6dbdd20aSAndroid Build Coastguard Worker//
144*6dbdd20aSAndroid Build Coastguard Worker// This file is automatically generated by %s. Do not edit.
145*6dbdd20aSAndroid Build Coastguard Worker""" % tool_name
146*6dbdd20aSAndroid Build Coastguard Worker
147*6dbdd20aSAndroid Build Coastguard Worker
148*6dbdd20aSAndroid Build Coastguard Workerdef apply_denylist(denylist, items):
149*6dbdd20aSAndroid Build Coastguard Worker  return [item for item in items if not re.match(denylist, item)]
150*6dbdd20aSAndroid Build Coastguard Worker
151*6dbdd20aSAndroid Build Coastguard Worker
152*6dbdd20aSAndroid Build Coastguard Workerdef apply_allowlist(allowlist, items):
153*6dbdd20aSAndroid Build Coastguard Worker  return [item for item in items if re.match(allowlist, item)]
154*6dbdd20aSAndroid Build Coastguard Worker
155*6dbdd20aSAndroid Build Coastguard Worker
156*6dbdd20aSAndroid Build Coastguard Workerdef normalize_path(path):
157*6dbdd20aSAndroid Build Coastguard Worker  path = os.path.relpath(path, project_root)
158*6dbdd20aSAndroid Build Coastguard Worker  path = re.sub(r'^out/[^/]+/', '', path)
159*6dbdd20aSAndroid Build Coastguard Worker  return path
160*6dbdd20aSAndroid Build Coastguard Worker
161*6dbdd20aSAndroid Build Coastguard Worker
162*6dbdd20aSAndroid Build Coastguard Workerclass Error(Exception):
163*6dbdd20aSAndroid Build Coastguard Worker  pass
164*6dbdd20aSAndroid Build Coastguard Worker
165*6dbdd20aSAndroid Build Coastguard Worker
166*6dbdd20aSAndroid Build Coastguard Workerclass DependencyNode(object):
167*6dbdd20aSAndroid Build Coastguard Worker  """A target in a GN build description along with its dependencies."""
168*6dbdd20aSAndroid Build Coastguard Worker
169*6dbdd20aSAndroid Build Coastguard Worker  def __init__(self, target_name):
170*6dbdd20aSAndroid Build Coastguard Worker    self.target_name = target_name
171*6dbdd20aSAndroid Build Coastguard Worker    self.dependencies = set()
172*6dbdd20aSAndroid Build Coastguard Worker
173*6dbdd20aSAndroid Build Coastguard Worker  def add_dependency(self, target_node):
174*6dbdd20aSAndroid Build Coastguard Worker    if target_node in self.dependencies:
175*6dbdd20aSAndroid Build Coastguard Worker      return
176*6dbdd20aSAndroid Build Coastguard Worker    self.dependencies.add(target_node)
177*6dbdd20aSAndroid Build Coastguard Worker
178*6dbdd20aSAndroid Build Coastguard Worker  def iterate_depth_first(self):
179*6dbdd20aSAndroid Build Coastguard Worker    for node in sorted(self.dependencies, key=lambda n: n.target_name):
180*6dbdd20aSAndroid Build Coastguard Worker      for node in node.iterate_depth_first():
181*6dbdd20aSAndroid Build Coastguard Worker        yield node
182*6dbdd20aSAndroid Build Coastguard Worker    if self.target_name:
183*6dbdd20aSAndroid Build Coastguard Worker      yield self
184*6dbdd20aSAndroid Build Coastguard Worker
185*6dbdd20aSAndroid Build Coastguard Worker
186*6dbdd20aSAndroid Build Coastguard Workerclass DependencyTree(object):
187*6dbdd20aSAndroid Build Coastguard Worker  """A tree of GN build target dependencies."""
188*6dbdd20aSAndroid Build Coastguard Worker
189*6dbdd20aSAndroid Build Coastguard Worker  def __init__(self):
190*6dbdd20aSAndroid Build Coastguard Worker    self.target_to_node_map = {}
191*6dbdd20aSAndroid Build Coastguard Worker    self.root = self._get_or_create_node(None)
192*6dbdd20aSAndroid Build Coastguard Worker
193*6dbdd20aSAndroid Build Coastguard Worker  def _get_or_create_node(self, target_name):
194*6dbdd20aSAndroid Build Coastguard Worker    if target_name in self.target_to_node_map:
195*6dbdd20aSAndroid Build Coastguard Worker      return self.target_to_node_map[target_name]
196*6dbdd20aSAndroid Build Coastguard Worker    node = DependencyNode(target_name)
197*6dbdd20aSAndroid Build Coastguard Worker    self.target_to_node_map[target_name] = node
198*6dbdd20aSAndroid Build Coastguard Worker    return node
199*6dbdd20aSAndroid Build Coastguard Worker
200*6dbdd20aSAndroid Build Coastguard Worker  def add_dependency(self, from_target, to_target):
201*6dbdd20aSAndroid Build Coastguard Worker    from_node = self._get_or_create_node(from_target)
202*6dbdd20aSAndroid Build Coastguard Worker    to_node = self._get_or_create_node(to_target)
203*6dbdd20aSAndroid Build Coastguard Worker    assert from_node is not to_node
204*6dbdd20aSAndroid Build Coastguard Worker    from_node.add_dependency(to_node)
205*6dbdd20aSAndroid Build Coastguard Worker
206*6dbdd20aSAndroid Build Coastguard Worker  def iterate_depth_first(self):
207*6dbdd20aSAndroid Build Coastguard Worker    for node in self.root.iterate_depth_first():
208*6dbdd20aSAndroid Build Coastguard Worker      yield node
209*6dbdd20aSAndroid Build Coastguard Worker
210*6dbdd20aSAndroid Build Coastguard Worker
211*6dbdd20aSAndroid Build Coastguard Workerclass AmalgamatedProject(object):
212*6dbdd20aSAndroid Build Coastguard Worker  """In-memory representation of an amalgamated source/header pair."""
213*6dbdd20aSAndroid Build Coastguard Worker
214*6dbdd20aSAndroid Build Coastguard Worker  def __init__(self, desc, source_deps, compute_deps_only=False):
215*6dbdd20aSAndroid Build Coastguard Worker    """Constructor.
216*6dbdd20aSAndroid Build Coastguard Worker
217*6dbdd20aSAndroid Build Coastguard Worker        Args:
218*6dbdd20aSAndroid Build Coastguard Worker            desc: JSON build description.
219*6dbdd20aSAndroid Build Coastguard Worker            source_deps: A map of (source file, [dependency header]) which is
220*6dbdd20aSAndroid Build Coastguard Worker                to detect which header files are included by each source file.
221*6dbdd20aSAndroid Build Coastguard Worker            compute_deps_only: If True, the project will only be used to compute
222*6dbdd20aSAndroid Build Coastguard Worker                dependency information. Use |get_source_files()| to retrieve
223*6dbdd20aSAndroid Build Coastguard Worker                the result.
224*6dbdd20aSAndroid Build Coastguard Worker        """
225*6dbdd20aSAndroid Build Coastguard Worker    self.desc = desc
226*6dbdd20aSAndroid Build Coastguard Worker    self.source_deps = source_deps
227*6dbdd20aSAndroid Build Coastguard Worker    self.header = []
228*6dbdd20aSAndroid Build Coastguard Worker    self.source = []
229*6dbdd20aSAndroid Build Coastguard Worker    self.source_defines = []
230*6dbdd20aSAndroid Build Coastguard Worker    # Note that we don't support multi-arg flags.
231*6dbdd20aSAndroid Build Coastguard Worker    self.cflags = set(default_cflags)
232*6dbdd20aSAndroid Build Coastguard Worker    self.ldflags = set()
233*6dbdd20aSAndroid Build Coastguard Worker    self.defines = set()
234*6dbdd20aSAndroid Build Coastguard Worker    self.libs = set()
235*6dbdd20aSAndroid Build Coastguard Worker    self._dependency_tree = DependencyTree()
236*6dbdd20aSAndroid Build Coastguard Worker    self._processed_sources = set()
237*6dbdd20aSAndroid Build Coastguard Worker    self._processed_headers = set()
238*6dbdd20aSAndroid Build Coastguard Worker    self._processed_header_deps = set()
239*6dbdd20aSAndroid Build Coastguard Worker    self._processed_source_headers = set()  # Header files included from .cc
240*6dbdd20aSAndroid Build Coastguard Worker    self._include_re = re.compile(r'#include "(.*)"')
241*6dbdd20aSAndroid Build Coastguard Worker    self._compute_deps_only = compute_deps_only
242*6dbdd20aSAndroid Build Coastguard Worker
243*6dbdd20aSAndroid Build Coastguard Worker  def add_target(self, target_name):
244*6dbdd20aSAndroid Build Coastguard Worker    """Include |target_name| in the amalgamated result."""
245*6dbdd20aSAndroid Build Coastguard Worker    self._dependency_tree.add_dependency(None, target_name)
246*6dbdd20aSAndroid Build Coastguard Worker    self._add_target_dependencies(target_name)
247*6dbdd20aSAndroid Build Coastguard Worker    self._add_target_flags(target_name)
248*6dbdd20aSAndroid Build Coastguard Worker    self._add_target_headers(target_name)
249*6dbdd20aSAndroid Build Coastguard Worker
250*6dbdd20aSAndroid Build Coastguard Worker    # Recurse into target deps, but only for protos. This generates headers
251*6dbdd20aSAndroid Build Coastguard Worker    # for all the .{pbzero,gen}.h files, even if they don't #include each other.
252*6dbdd20aSAndroid Build Coastguard Worker    for _, dep in self._iterate_dep_edges(target_name):
253*6dbdd20aSAndroid Build Coastguard Worker      if (dep not in self._processed_header_deps and
254*6dbdd20aSAndroid Build Coastguard Worker          re.match(recurse_in_header_deps, dep)):
255*6dbdd20aSAndroid Build Coastguard Worker        self._processed_header_deps.add(dep)
256*6dbdd20aSAndroid Build Coastguard Worker        self.add_target(dep)
257*6dbdd20aSAndroid Build Coastguard Worker
258*6dbdd20aSAndroid Build Coastguard Worker  def _iterate_dep_edges(self, target_name):
259*6dbdd20aSAndroid Build Coastguard Worker    target = self.desc[target_name]
260*6dbdd20aSAndroid Build Coastguard Worker    for dep in target.get('deps', []):
261*6dbdd20aSAndroid Build Coastguard Worker      # Ignore system libraries since they will be added as build-time
262*6dbdd20aSAndroid Build Coastguard Worker      # dependencies.
263*6dbdd20aSAndroid Build Coastguard Worker      if dep in system_library_map:
264*6dbdd20aSAndroid Build Coastguard Worker        continue
265*6dbdd20aSAndroid Build Coastguard Worker      # Don't descend into build action dependencies.
266*6dbdd20aSAndroid Build Coastguard Worker      if self.desc[dep]['type'] == 'action':
267*6dbdd20aSAndroid Build Coastguard Worker        continue
268*6dbdd20aSAndroid Build Coastguard Worker      for sub_target, sub_dep in self._iterate_dep_edges(dep):
269*6dbdd20aSAndroid Build Coastguard Worker        yield sub_target, sub_dep
270*6dbdd20aSAndroid Build Coastguard Worker      yield target_name, dep
271*6dbdd20aSAndroid Build Coastguard Worker
272*6dbdd20aSAndroid Build Coastguard Worker  def _iterate_target_and_deps(self, target_name):
273*6dbdd20aSAndroid Build Coastguard Worker    yield target_name
274*6dbdd20aSAndroid Build Coastguard Worker    for _, dep in self._iterate_dep_edges(target_name):
275*6dbdd20aSAndroid Build Coastguard Worker      yield dep
276*6dbdd20aSAndroid Build Coastguard Worker
277*6dbdd20aSAndroid Build Coastguard Worker  def _add_target_dependencies(self, target_name):
278*6dbdd20aSAndroid Build Coastguard Worker    for target, dep in self._iterate_dep_edges(target_name):
279*6dbdd20aSAndroid Build Coastguard Worker      self._dependency_tree.add_dependency(target, dep)
280*6dbdd20aSAndroid Build Coastguard Worker
281*6dbdd20aSAndroid Build Coastguard Worker    def process_dep(dep):
282*6dbdd20aSAndroid Build Coastguard Worker      if dep in system_library_map:
283*6dbdd20aSAndroid Build Coastguard Worker        self.libs.update(system_library_map[dep].get('libs', []))
284*6dbdd20aSAndroid Build Coastguard Worker        self.cflags.update(system_library_map[dep].get('cflags', []))
285*6dbdd20aSAndroid Build Coastguard Worker        self.defines.update(system_library_map[dep].get('defines', []))
286*6dbdd20aSAndroid Build Coastguard Worker        return True
287*6dbdd20aSAndroid Build Coastguard Worker
288*6dbdd20aSAndroid Build Coastguard Worker    def walk_all_deps(target_name):
289*6dbdd20aSAndroid Build Coastguard Worker      target = self.desc[target_name]
290*6dbdd20aSAndroid Build Coastguard Worker      for dep in target.get('deps', []):
291*6dbdd20aSAndroid Build Coastguard Worker        if process_dep(dep):
292*6dbdd20aSAndroid Build Coastguard Worker          return
293*6dbdd20aSAndroid Build Coastguard Worker        walk_all_deps(dep)
294*6dbdd20aSAndroid Build Coastguard Worker
295*6dbdd20aSAndroid Build Coastguard Worker    walk_all_deps(target_name)
296*6dbdd20aSAndroid Build Coastguard Worker
297*6dbdd20aSAndroid Build Coastguard Worker  def _filter_cflags(self, cflags):
298*6dbdd20aSAndroid Build Coastguard Worker    # Since we want to deduplicate flags, combine two-part switches (e.g.,
299*6dbdd20aSAndroid Build Coastguard Worker    # "-foo bar") into one value ("-foobar") so we can store the result as
300*6dbdd20aSAndroid Build Coastguard Worker    # a set.
301*6dbdd20aSAndroid Build Coastguard Worker    result = []
302*6dbdd20aSAndroid Build Coastguard Worker    for flag in cflags:
303*6dbdd20aSAndroid Build Coastguard Worker      if flag.startswith('-'):
304*6dbdd20aSAndroid Build Coastguard Worker        result.append(flag)
305*6dbdd20aSAndroid Build Coastguard Worker      else:
306*6dbdd20aSAndroid Build Coastguard Worker        result[-1] += flag
307*6dbdd20aSAndroid Build Coastguard Worker    return apply_allowlist(cflag_allowlist, result)
308*6dbdd20aSAndroid Build Coastguard Worker
309*6dbdd20aSAndroid Build Coastguard Worker  def _add_target_flags(self, target_name):
310*6dbdd20aSAndroid Build Coastguard Worker    for target_name in self._iterate_target_and_deps(target_name):
311*6dbdd20aSAndroid Build Coastguard Worker      target = self.desc[target_name]
312*6dbdd20aSAndroid Build Coastguard Worker      self.cflags.update(self._filter_cflags(target.get('cflags', [])))
313*6dbdd20aSAndroid Build Coastguard Worker      self.cflags.update(self._filter_cflags(target.get('cflags_cc', [])))
314*6dbdd20aSAndroid Build Coastguard Worker      self.ldflags.update(
315*6dbdd20aSAndroid Build Coastguard Worker          apply_allowlist(ldflag_allowlist, target.get('ldflags', [])))
316*6dbdd20aSAndroid Build Coastguard Worker      self.libs.update(apply_denylist(lib_denylist, target.get('libs', [])))
317*6dbdd20aSAndroid Build Coastguard Worker      self.defines.update(
318*6dbdd20aSAndroid Build Coastguard Worker          apply_allowlist(define_allowlist, target.get('defines', [])))
319*6dbdd20aSAndroid Build Coastguard Worker
320*6dbdd20aSAndroid Build Coastguard Worker  def _add_target_headers(self, target_name):
321*6dbdd20aSAndroid Build Coastguard Worker    target = self.desc[target_name]
322*6dbdd20aSAndroid Build Coastguard Worker    if not 'sources' in target:
323*6dbdd20aSAndroid Build Coastguard Worker      return
324*6dbdd20aSAndroid Build Coastguard Worker    headers = [
325*6dbdd20aSAndroid Build Coastguard Worker        gn_utils.label_to_path(s) for s in target['sources'] if s.endswith('.h')
326*6dbdd20aSAndroid Build Coastguard Worker    ]
327*6dbdd20aSAndroid Build Coastguard Worker    for header in headers:
328*6dbdd20aSAndroid Build Coastguard Worker      self._add_header(target_name, header)
329*6dbdd20aSAndroid Build Coastguard Worker
330*6dbdd20aSAndroid Build Coastguard Worker  def _get_include_dirs(self, target_name):
331*6dbdd20aSAndroid Build Coastguard Worker    include_dirs = set(default_includes)
332*6dbdd20aSAndroid Build Coastguard Worker    for target_name in self._iterate_target_and_deps(target_name):
333*6dbdd20aSAndroid Build Coastguard Worker      target = self.desc[target_name]
334*6dbdd20aSAndroid Build Coastguard Worker      if 'include_dirs' in target:
335*6dbdd20aSAndroid Build Coastguard Worker        include_dirs.update(
336*6dbdd20aSAndroid Build Coastguard Worker            [gn_utils.label_to_path(d) for d in target['include_dirs']])
337*6dbdd20aSAndroid Build Coastguard Worker    return include_dirs
338*6dbdd20aSAndroid Build Coastguard Worker
339*6dbdd20aSAndroid Build Coastguard Worker  def _add_source_included_header(self, include_dirs, allowed_files,
340*6dbdd20aSAndroid Build Coastguard Worker                                  header_name):
341*6dbdd20aSAndroid Build Coastguard Worker    for include_dir in include_dirs:
342*6dbdd20aSAndroid Build Coastguard Worker      rel_path = os.path.join(include_dir, header_name)
343*6dbdd20aSAndroid Build Coastguard Worker      full_path = os.path.join(gn_utils.repo_root(), rel_path)
344*6dbdd20aSAndroid Build Coastguard Worker      if os.path.exists(full_path):
345*6dbdd20aSAndroid Build Coastguard Worker        if not rel_path in allowed_files:
346*6dbdd20aSAndroid Build Coastguard Worker          return
347*6dbdd20aSAndroid Build Coastguard Worker        if full_path in self._processed_headers:
348*6dbdd20aSAndroid Build Coastguard Worker          return
349*6dbdd20aSAndroid Build Coastguard Worker        if full_path in self._processed_source_headers:
350*6dbdd20aSAndroid Build Coastguard Worker          return
351*6dbdd20aSAndroid Build Coastguard Worker        self._processed_source_headers.add(full_path)
352*6dbdd20aSAndroid Build Coastguard Worker        with open(full_path) as f:
353*6dbdd20aSAndroid Build Coastguard Worker          self.source.append('// %s begin header: %s' %
354*6dbdd20aSAndroid Build Coastguard Worker                             (tool_name, normalize_path(full_path)))
355*6dbdd20aSAndroid Build Coastguard Worker          self.source.extend(
356*6dbdd20aSAndroid Build Coastguard Worker              self._process_source_includes(include_dirs, allowed_files, f))
357*6dbdd20aSAndroid Build Coastguard Worker        return
358*6dbdd20aSAndroid Build Coastguard Worker    if self._compute_deps_only:
359*6dbdd20aSAndroid Build Coastguard Worker      return
360*6dbdd20aSAndroid Build Coastguard Worker    msg = 'Looked in %s' % ', '.join('"%s"' % d for d in include_dirs)
361*6dbdd20aSAndroid Build Coastguard Worker    raise Error('Header file %s not found. %s' % (header_name, msg))
362*6dbdd20aSAndroid Build Coastguard Worker
363*6dbdd20aSAndroid Build Coastguard Worker  def _add_source(self, target_name, source_name):
364*6dbdd20aSAndroid Build Coastguard Worker    if source_name in self._processed_sources:
365*6dbdd20aSAndroid Build Coastguard Worker      return
366*6dbdd20aSAndroid Build Coastguard Worker    self._processed_sources.add(source_name)
367*6dbdd20aSAndroid Build Coastguard Worker    include_dirs = self._get_include_dirs(target_name)
368*6dbdd20aSAndroid Build Coastguard Worker    deps = self.source_deps[source_name]
369*6dbdd20aSAndroid Build Coastguard Worker    full_path = os.path.join(gn_utils.repo_root(), source_name)
370*6dbdd20aSAndroid Build Coastguard Worker    if not os.path.exists(full_path):
371*6dbdd20aSAndroid Build Coastguard Worker      raise Error('Source file %s not found' % source_name)
372*6dbdd20aSAndroid Build Coastguard Worker    with open(full_path) as f:
373*6dbdd20aSAndroid Build Coastguard Worker      self.source.append('// %s begin source: %s' %
374*6dbdd20aSAndroid Build Coastguard Worker                         (tool_name, normalize_path(full_path)))
375*6dbdd20aSAndroid Build Coastguard Worker      try:
376*6dbdd20aSAndroid Build Coastguard Worker        self.source.extend(
377*6dbdd20aSAndroid Build Coastguard Worker            self._patch_source(
378*6dbdd20aSAndroid Build Coastguard Worker                source_name,
379*6dbdd20aSAndroid Build Coastguard Worker                self._process_source_includes(include_dirs, deps, f)))
380*6dbdd20aSAndroid Build Coastguard Worker      except Error as e:
381*6dbdd20aSAndroid Build Coastguard Worker        raise Error('Failed adding source %s: %s' % (source_name, e))
382*6dbdd20aSAndroid Build Coastguard Worker
383*6dbdd20aSAndroid Build Coastguard Worker  def _add_header_included_header(self, include_dirs, header_name):
384*6dbdd20aSAndroid Build Coastguard Worker    for include_dir in include_dirs:
385*6dbdd20aSAndroid Build Coastguard Worker      full_path = os.path.join(gn_utils.repo_root(), include_dir, header_name)
386*6dbdd20aSAndroid Build Coastguard Worker      if os.path.exists(full_path):
387*6dbdd20aSAndroid Build Coastguard Worker        if full_path in self._processed_headers:
388*6dbdd20aSAndroid Build Coastguard Worker          return
389*6dbdd20aSAndroid Build Coastguard Worker        self._processed_headers.add(full_path)
390*6dbdd20aSAndroid Build Coastguard Worker        with open(full_path) as f:
391*6dbdd20aSAndroid Build Coastguard Worker          self.header.append('// %s begin header: %s' %
392*6dbdd20aSAndroid Build Coastguard Worker                             (tool_name, normalize_path(full_path)))
393*6dbdd20aSAndroid Build Coastguard Worker          self.header.extend(self._process_header_includes(include_dirs, f))
394*6dbdd20aSAndroid Build Coastguard Worker        return
395*6dbdd20aSAndroid Build Coastguard Worker    if self._compute_deps_only:
396*6dbdd20aSAndroid Build Coastguard Worker      return
397*6dbdd20aSAndroid Build Coastguard Worker    msg = 'Looked in %s' % ', '.join('"%s"' % d for d in include_dirs)
398*6dbdd20aSAndroid Build Coastguard Worker    raise Error('Header file %s not found. %s' % (header_name, msg))
399*6dbdd20aSAndroid Build Coastguard Worker
400*6dbdd20aSAndroid Build Coastguard Worker  def _add_header(self, target_name, header_name):
401*6dbdd20aSAndroid Build Coastguard Worker    include_dirs = self._get_include_dirs(target_name)
402*6dbdd20aSAndroid Build Coastguard Worker    full_path = os.path.join(gn_utils.repo_root(), header_name)
403*6dbdd20aSAndroid Build Coastguard Worker    if full_path in self._processed_headers:
404*6dbdd20aSAndroid Build Coastguard Worker      return
405*6dbdd20aSAndroid Build Coastguard Worker    self._processed_headers.add(full_path)
406*6dbdd20aSAndroid Build Coastguard Worker    if not os.path.exists(full_path):
407*6dbdd20aSAndroid Build Coastguard Worker      if self._compute_deps_only:
408*6dbdd20aSAndroid Build Coastguard Worker        return
409*6dbdd20aSAndroid Build Coastguard Worker      raise Error('Header file %s not found' % header_name)
410*6dbdd20aSAndroid Build Coastguard Worker    with open(full_path) as f:
411*6dbdd20aSAndroid Build Coastguard Worker      self.header.append('// %s begin header: %s' %
412*6dbdd20aSAndroid Build Coastguard Worker                         (tool_name, normalize_path(full_path)))
413*6dbdd20aSAndroid Build Coastguard Worker      try:
414*6dbdd20aSAndroid Build Coastguard Worker        self.header.extend(self._process_header_includes(include_dirs, f))
415*6dbdd20aSAndroid Build Coastguard Worker      except Error as e:
416*6dbdd20aSAndroid Build Coastguard Worker        raise Error('Failed adding header %s: %s' % (header_name, e))
417*6dbdd20aSAndroid Build Coastguard Worker
418*6dbdd20aSAndroid Build Coastguard Worker  def _patch_source(self, source_name, lines):
419*6dbdd20aSAndroid Build Coastguard Worker    result = []
420*6dbdd20aSAndroid Build Coastguard Worker    namespace = re.sub(r'[^a-z]', '_',
421*6dbdd20aSAndroid Build Coastguard Worker                       os.path.splitext(os.path.basename(source_name))[0])
422*6dbdd20aSAndroid Build Coastguard Worker    for line in lines:
423*6dbdd20aSAndroid Build Coastguard Worker      # Protobuf generates an identical anonymous function into each
424*6dbdd20aSAndroid Build Coastguard Worker      # message description. Rename all but the first occurrence to avoid
425*6dbdd20aSAndroid Build Coastguard Worker      # duplicate symbol definitions.
426*6dbdd20aSAndroid Build Coastguard Worker      line = line.replace('MergeFromFail', '%s_MergeFromFail' % namespace)
427*6dbdd20aSAndroid Build Coastguard Worker      result.append(line)
428*6dbdd20aSAndroid Build Coastguard Worker    return result
429*6dbdd20aSAndroid Build Coastguard Worker
430*6dbdd20aSAndroid Build Coastguard Worker  def _process_source_includes(self, include_dirs, allowed_files, file):
431*6dbdd20aSAndroid Build Coastguard Worker    result = []
432*6dbdd20aSAndroid Build Coastguard Worker    for line in file:
433*6dbdd20aSAndroid Build Coastguard Worker      line = line.rstrip('\n')
434*6dbdd20aSAndroid Build Coastguard Worker      m = self._include_re.match(line)
435*6dbdd20aSAndroid Build Coastguard Worker      if not m:
436*6dbdd20aSAndroid Build Coastguard Worker        result.append(line)
437*6dbdd20aSAndroid Build Coastguard Worker        continue
438*6dbdd20aSAndroid Build Coastguard Worker      elif re.match(includes_to_remove, m.group(1)):
439*6dbdd20aSAndroid Build Coastguard Worker        result.append('// %s removed: %s' % (tool_name, line))
440*6dbdd20aSAndroid Build Coastguard Worker      else:
441*6dbdd20aSAndroid Build Coastguard Worker        result.append('// %s expanded: %s' % (tool_name, line))
442*6dbdd20aSAndroid Build Coastguard Worker        self._add_source_included_header(include_dirs, allowed_files,
443*6dbdd20aSAndroid Build Coastguard Worker                                         m.group(1))
444*6dbdd20aSAndroid Build Coastguard Worker    return result
445*6dbdd20aSAndroid Build Coastguard Worker
446*6dbdd20aSAndroid Build Coastguard Worker  def _process_header_includes(self, include_dirs, file):
447*6dbdd20aSAndroid Build Coastguard Worker    result = []
448*6dbdd20aSAndroid Build Coastguard Worker    for line in file:
449*6dbdd20aSAndroid Build Coastguard Worker      line = line.rstrip('\n')
450*6dbdd20aSAndroid Build Coastguard Worker      m = self._include_re.match(line)
451*6dbdd20aSAndroid Build Coastguard Worker      if not m:
452*6dbdd20aSAndroid Build Coastguard Worker        result.append(line)
453*6dbdd20aSAndroid Build Coastguard Worker        continue
454*6dbdd20aSAndroid Build Coastguard Worker      elif re.match(includes_to_remove, m.group(1)):
455*6dbdd20aSAndroid Build Coastguard Worker        result.append('// %s removed: %s' % (tool_name, line))
456*6dbdd20aSAndroid Build Coastguard Worker      else:
457*6dbdd20aSAndroid Build Coastguard Worker        result.append('// %s expanded: %s' % (tool_name, line))
458*6dbdd20aSAndroid Build Coastguard Worker        self._add_header_included_header(include_dirs, m.group(1))
459*6dbdd20aSAndroid Build Coastguard Worker    return result
460*6dbdd20aSAndroid Build Coastguard Worker
461*6dbdd20aSAndroid Build Coastguard Worker  def generate(self):
462*6dbdd20aSAndroid Build Coastguard Worker    """Prepares the output for this amalgamated project.
463*6dbdd20aSAndroid Build Coastguard Worker
464*6dbdd20aSAndroid Build Coastguard Worker        Call save() to persist the result.
465*6dbdd20aSAndroid Build Coastguard Worker        """
466*6dbdd20aSAndroid Build Coastguard Worker    assert not self._compute_deps_only
467*6dbdd20aSAndroid Build Coastguard Worker    self.source_defines.append('// %s: predefined macros' % tool_name)
468*6dbdd20aSAndroid Build Coastguard Worker
469*6dbdd20aSAndroid Build Coastguard Worker    def add_define(name):
470*6dbdd20aSAndroid Build Coastguard Worker      # Valued macros aren't supported for now.
471*6dbdd20aSAndroid Build Coastguard Worker      assert '=' not in name
472*6dbdd20aSAndroid Build Coastguard Worker      self.source_defines.append('#if !defined(%s)' % name)
473*6dbdd20aSAndroid Build Coastguard Worker      self.source_defines.append('#define %s' % name)
474*6dbdd20aSAndroid Build Coastguard Worker      self.source_defines.append('#endif')
475*6dbdd20aSAndroid Build Coastguard Worker
476*6dbdd20aSAndroid Build Coastguard Worker    for name in self.defines:
477*6dbdd20aSAndroid Build Coastguard Worker      add_define(name)
478*6dbdd20aSAndroid Build Coastguard Worker    for target_name, source_name in self.get_source_files():
479*6dbdd20aSAndroid Build Coastguard Worker      self._add_source(target_name, source_name)
480*6dbdd20aSAndroid Build Coastguard Worker
481*6dbdd20aSAndroid Build Coastguard Worker  def get_source_files(self):
482*6dbdd20aSAndroid Build Coastguard Worker    """Return a list of (target, [source file]) that describes the source
483*6dbdd20aSAndroid Build Coastguard Worker           files pulled in by each target which is a dependency of this project.
484*6dbdd20aSAndroid Build Coastguard Worker        """
485*6dbdd20aSAndroid Build Coastguard Worker    source_files = []
486*6dbdd20aSAndroid Build Coastguard Worker    for node in self._dependency_tree.iterate_depth_first():
487*6dbdd20aSAndroid Build Coastguard Worker      target = self.desc[node.target_name]
488*6dbdd20aSAndroid Build Coastguard Worker      if not 'sources' in target:
489*6dbdd20aSAndroid Build Coastguard Worker        continue
490*6dbdd20aSAndroid Build Coastguard Worker      sources = [(node.target_name, gn_utils.label_to_path(s))
491*6dbdd20aSAndroid Build Coastguard Worker                 for s in target['sources']
492*6dbdd20aSAndroid Build Coastguard Worker                 if s.endswith('.cc')]
493*6dbdd20aSAndroid Build Coastguard Worker      source_files.extend(sources)
494*6dbdd20aSAndroid Build Coastguard Worker    return source_files
495*6dbdd20aSAndroid Build Coastguard Worker
496*6dbdd20aSAndroid Build Coastguard Worker  def _get_nice_path(self, prefix, format):
497*6dbdd20aSAndroid Build Coastguard Worker    basename = os.path.basename(prefix)
498*6dbdd20aSAndroid Build Coastguard Worker    return os.path.join(
499*6dbdd20aSAndroid Build Coastguard Worker        os.path.relpath(os.path.dirname(prefix)), format % basename)
500*6dbdd20aSAndroid Build Coastguard Worker
501*6dbdd20aSAndroid Build Coastguard Worker  def _make_directories(self, directory):
502*6dbdd20aSAndroid Build Coastguard Worker    if not os.path.isdir(directory):
503*6dbdd20aSAndroid Build Coastguard Worker      os.makedirs(directory)
504*6dbdd20aSAndroid Build Coastguard Worker
505*6dbdd20aSAndroid Build Coastguard Worker  def save(self, output_prefix, system_buildtools=False):
506*6dbdd20aSAndroid Build Coastguard Worker    """Save the generated header and source file pair.
507*6dbdd20aSAndroid Build Coastguard Worker
508*6dbdd20aSAndroid Build Coastguard Worker        Returns a message describing the output with build instructions.
509*6dbdd20aSAndroid Build Coastguard Worker        """
510*6dbdd20aSAndroid Build Coastguard Worker    header_file = self._get_nice_path(output_prefix, '%s.h')
511*6dbdd20aSAndroid Build Coastguard Worker    source_file = self._get_nice_path(output_prefix, '%s.cc')
512*6dbdd20aSAndroid Build Coastguard Worker    self._make_directories(os.path.dirname(header_file))
513*6dbdd20aSAndroid Build Coastguard Worker    self._make_directories(os.path.dirname(source_file))
514*6dbdd20aSAndroid Build Coastguard Worker    with open(header_file, 'w') as f:
515*6dbdd20aSAndroid Build Coastguard Worker      f.write('\n'.join([preamble] + self.header + ['\n']))
516*6dbdd20aSAndroid Build Coastguard Worker    with open(source_file, 'w') as f:
517*6dbdd20aSAndroid Build Coastguard Worker      include_stmt = '#include "%s"' % os.path.basename(header_file)
518*6dbdd20aSAndroid Build Coastguard Worker      f.write('\n'.join([preamble] + self.source_defines + [include_stmt] +
519*6dbdd20aSAndroid Build Coastguard Worker                        self.source + ['\n']))
520*6dbdd20aSAndroid Build Coastguard Worker    build_cmd = self.get_build_command(output_prefix, system_buildtools)
521*6dbdd20aSAndroid Build Coastguard Worker    return """Amalgamated project written to %s and %s.
522*6dbdd20aSAndroid Build Coastguard Worker
523*6dbdd20aSAndroid Build Coastguard WorkerBuild settings:
524*6dbdd20aSAndroid Build Coastguard Worker - cflags:    %s
525*6dbdd20aSAndroid Build Coastguard Worker - ldflags:   %s
526*6dbdd20aSAndroid Build Coastguard Worker - libs:      %s
527*6dbdd20aSAndroid Build Coastguard Worker
528*6dbdd20aSAndroid Build Coastguard WorkerExample build command:
529*6dbdd20aSAndroid Build Coastguard Worker
530*6dbdd20aSAndroid Build Coastguard Worker%s
531*6dbdd20aSAndroid Build Coastguard Worker""" % (header_file, source_file, ' '.join(self.cflags), ' '.join(
532*6dbdd20aSAndroid Build Coastguard Worker        self.ldflags), ' '.join(self.libs), ' '.join(build_cmd))
533*6dbdd20aSAndroid Build Coastguard Worker
534*6dbdd20aSAndroid Build Coastguard Worker  def get_build_command(self, output_prefix, system_buildtools=False):
535*6dbdd20aSAndroid Build Coastguard Worker    """Returns an example command line for building the output source."""
536*6dbdd20aSAndroid Build Coastguard Worker    source = self._get_nice_path(output_prefix, '%s.cc')
537*6dbdd20aSAndroid Build Coastguard Worker    library = self._get_nice_path(output_prefix, 'lib%s.so')
538*6dbdd20aSAndroid Build Coastguard Worker
539*6dbdd20aSAndroid Build Coastguard Worker    if sys.platform.startswith('linux') and not system_buildtools:
540*6dbdd20aSAndroid Build Coastguard Worker      llvm_script = os.path.join(gn_utils.repo_root(), 'gn', 'standalone',
541*6dbdd20aSAndroid Build Coastguard Worker                                 'toolchain', 'linux_find_llvm.py')
542*6dbdd20aSAndroid Build Coastguard Worker      cxx = subprocess.check_output([llvm_script]).splitlines()[2].decode()
543*6dbdd20aSAndroid Build Coastguard Worker    else:
544*6dbdd20aSAndroid Build Coastguard Worker      cxx = 'clang++'
545*6dbdd20aSAndroid Build Coastguard Worker
546*6dbdd20aSAndroid Build Coastguard Worker    build_cmd = [cxx, source, '-o', library, '-shared'] + \
547*6dbdd20aSAndroid Build Coastguard Worker        sorted(self.cflags) + sorted(self.ldflags)
548*6dbdd20aSAndroid Build Coastguard Worker    for lib in sorted(self.libs):
549*6dbdd20aSAndroid Build Coastguard Worker      build_cmd.append('-l%s' % lib)
550*6dbdd20aSAndroid Build Coastguard Worker    return build_cmd
551*6dbdd20aSAndroid Build Coastguard Worker
552*6dbdd20aSAndroid Build Coastguard Worker
553*6dbdd20aSAndroid Build Coastguard Workerdef main():
554*6dbdd20aSAndroid Build Coastguard Worker  parser = argparse.ArgumentParser(
555*6dbdd20aSAndroid Build Coastguard Worker      description='Generate an amalgamated header/source pair from a GN '
556*6dbdd20aSAndroid Build Coastguard Worker      'build description.')
557*6dbdd20aSAndroid Build Coastguard Worker  parser.add_argument(
558*6dbdd20aSAndroid Build Coastguard Worker      '--out',
559*6dbdd20aSAndroid Build Coastguard Worker      help='The name of the temporary build folder in \'out\'',
560*6dbdd20aSAndroid Build Coastguard Worker      default='tmp.gen_amalgamated.%u' % os.getpid())
561*6dbdd20aSAndroid Build Coastguard Worker  parser.add_argument(
562*6dbdd20aSAndroid Build Coastguard Worker      '--output',
563*6dbdd20aSAndroid Build Coastguard Worker      help='Base name of files to create. A .cc/.h extension will be added',
564*6dbdd20aSAndroid Build Coastguard Worker      default=os.path.join(gn_utils.repo_root(), 'out/amalgamated/perfetto'))
565*6dbdd20aSAndroid Build Coastguard Worker  parser.add_argument(
566*6dbdd20aSAndroid Build Coastguard Worker      '--gn_args',
567*6dbdd20aSAndroid Build Coastguard Worker      help='GN arguments used to prepare the output directory',
568*6dbdd20aSAndroid Build Coastguard Worker      default=gn_args)
569*6dbdd20aSAndroid Build Coastguard Worker  parser.add_argument(
570*6dbdd20aSAndroid Build Coastguard Worker      '--keep',
571*6dbdd20aSAndroid Build Coastguard Worker      help='Don\'t delete the GN output directory at exit',
572*6dbdd20aSAndroid Build Coastguard Worker      action='store_true')
573*6dbdd20aSAndroid Build Coastguard Worker  parser.add_argument(
574*6dbdd20aSAndroid Build Coastguard Worker      '--build', help='Also compile the generated files', action='store_true')
575*6dbdd20aSAndroid Build Coastguard Worker  parser.add_argument(
576*6dbdd20aSAndroid Build Coastguard Worker      '--check', help='Don\'t keep the generated files', action='store_true')
577*6dbdd20aSAndroid Build Coastguard Worker  parser.add_argument('--quiet', help='Only report errors', action='store_true')
578*6dbdd20aSAndroid Build Coastguard Worker  parser.add_argument(
579*6dbdd20aSAndroid Build Coastguard Worker      '--dump-deps',
580*6dbdd20aSAndroid Build Coastguard Worker      help='List all source files that the amalgamated output depends on',
581*6dbdd20aSAndroid Build Coastguard Worker      action='store_true')
582*6dbdd20aSAndroid Build Coastguard Worker  parser.add_argument(
583*6dbdd20aSAndroid Build Coastguard Worker      '--system_buildtools',
584*6dbdd20aSAndroid Build Coastguard Worker      help='Use the buildtools (e.g. gn) preinstalled in the system instead '
585*6dbdd20aSAndroid Build Coastguard Worker      'of the hermetic ones',
586*6dbdd20aSAndroid Build Coastguard Worker      action='store_true')
587*6dbdd20aSAndroid Build Coastguard Worker  parser.add_argument(
588*6dbdd20aSAndroid Build Coastguard Worker      'targets',
589*6dbdd20aSAndroid Build Coastguard Worker      nargs=argparse.REMAINDER,
590*6dbdd20aSAndroid Build Coastguard Worker      help='Targets to include in the output (e.g., "//:libperfetto")')
591*6dbdd20aSAndroid Build Coastguard Worker  args = parser.parse_args()
592*6dbdd20aSAndroid Build Coastguard Worker  targets = args.targets or default_targets
593*6dbdd20aSAndroid Build Coastguard Worker
594*6dbdd20aSAndroid Build Coastguard Worker  # The CHANGELOG mtime triggers the perfetto_version.gen.h genrule. This is
595*6dbdd20aSAndroid Build Coastguard Worker  # to avoid emitting a stale version information in the remote case of somebody
596*6dbdd20aSAndroid Build Coastguard Worker  # running gen_amalgamated incrementally after having moved to another commit.
597*6dbdd20aSAndroid Build Coastguard Worker  changelog_path = os.path.join(project_root, 'CHANGELOG')
598*6dbdd20aSAndroid Build Coastguard Worker  assert (os.path.exists(changelog_path))
599*6dbdd20aSAndroid Build Coastguard Worker  subprocess.check_call(['touch', '-c', changelog_path])
600*6dbdd20aSAndroid Build Coastguard Worker
601*6dbdd20aSAndroid Build Coastguard Worker  output = args.output
602*6dbdd20aSAndroid Build Coastguard Worker  if args.check:
603*6dbdd20aSAndroid Build Coastguard Worker    output = os.path.join(tempfile.mkdtemp(), 'perfetto_amalgamated')
604*6dbdd20aSAndroid Build Coastguard Worker
605*6dbdd20aSAndroid Build Coastguard Worker  out = gn_utils.prepare_out_directory(args.gn_args,
606*6dbdd20aSAndroid Build Coastguard Worker                                       args.out,
607*6dbdd20aSAndroid Build Coastguard Worker                                       system_buildtools=args.system_buildtools)
608*6dbdd20aSAndroid Build Coastguard Worker  if not args.quiet:
609*6dbdd20aSAndroid Build Coastguard Worker    print('Building project...')
610*6dbdd20aSAndroid Build Coastguard Worker  try:
611*6dbdd20aSAndroid Build Coastguard Worker    desc = gn_utils.load_build_description(out, args.system_buildtools)
612*6dbdd20aSAndroid Build Coastguard Worker
613*6dbdd20aSAndroid Build Coastguard Worker    # We need to build everything first so that the necessary header
614*6dbdd20aSAndroid Build Coastguard Worker    # dependencies get generated. However if we are just dumping dependency
615*6dbdd20aSAndroid Build Coastguard Worker    # information this can be skipped, allowing cross-platform operation.
616*6dbdd20aSAndroid Build Coastguard Worker    if not args.dump_deps:
617*6dbdd20aSAndroid Build Coastguard Worker      gn_utils.build_targets(out, targets,
618*6dbdd20aSAndroid Build Coastguard Worker                             system_buildtools=args.system_buildtools)
619*6dbdd20aSAndroid Build Coastguard Worker    source_deps = gn_utils.compute_source_dependencies(out,
620*6dbdd20aSAndroid Build Coastguard Worker                                                       args.system_buildtools)
621*6dbdd20aSAndroid Build Coastguard Worker    project = AmalgamatedProject(
622*6dbdd20aSAndroid Build Coastguard Worker        desc, source_deps, compute_deps_only=args.dump_deps)
623*6dbdd20aSAndroid Build Coastguard Worker
624*6dbdd20aSAndroid Build Coastguard Worker    for target in targets:
625*6dbdd20aSAndroid Build Coastguard Worker      project.add_target(target)
626*6dbdd20aSAndroid Build Coastguard Worker
627*6dbdd20aSAndroid Build Coastguard Worker    if args.dump_deps:
628*6dbdd20aSAndroid Build Coastguard Worker      source_files = [
629*6dbdd20aSAndroid Build Coastguard Worker          source_file for _, source_file in project.get_source_files()
630*6dbdd20aSAndroid Build Coastguard Worker      ]
631*6dbdd20aSAndroid Build Coastguard Worker      print('\n'.join(sorted(set(source_files))))
632*6dbdd20aSAndroid Build Coastguard Worker      return
633*6dbdd20aSAndroid Build Coastguard Worker
634*6dbdd20aSAndroid Build Coastguard Worker    project.generate()
635*6dbdd20aSAndroid Build Coastguard Worker    result = project.save(output, args.system_buildtools)
636*6dbdd20aSAndroid Build Coastguard Worker    if not args.quiet:
637*6dbdd20aSAndroid Build Coastguard Worker      print(result)
638*6dbdd20aSAndroid Build Coastguard Worker    if args.build:
639*6dbdd20aSAndroid Build Coastguard Worker      if not args.quiet:
640*6dbdd20aSAndroid Build Coastguard Worker        sys.stdout.write('Building amalgamated project...')
641*6dbdd20aSAndroid Build Coastguard Worker        sys.stdout.flush()
642*6dbdd20aSAndroid Build Coastguard Worker      subprocess.check_call(project.get_build_command(output,
643*6dbdd20aSAndroid Build Coastguard Worker                                                      args.system_buildtools))
644*6dbdd20aSAndroid Build Coastguard Worker      if not args.quiet:
645*6dbdd20aSAndroid Build Coastguard Worker        print('done')
646*6dbdd20aSAndroid Build Coastguard Worker  finally:
647*6dbdd20aSAndroid Build Coastguard Worker    if not args.keep:
648*6dbdd20aSAndroid Build Coastguard Worker      shutil.rmtree(out)
649*6dbdd20aSAndroid Build Coastguard Worker    if args.check:
650*6dbdd20aSAndroid Build Coastguard Worker      shutil.rmtree(os.path.dirname(output))
651*6dbdd20aSAndroid Build Coastguard Worker
652*6dbdd20aSAndroid Build Coastguard Worker
653*6dbdd20aSAndroid Build Coastguard Workerif __name__ == '__main__':
654*6dbdd20aSAndroid Build Coastguard Worker  sys.exit(main())
655