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