xref: /aosp_15_r20/tools/treble/split/manifest_split.py (revision 105f628577ac4ba0e277a494fbb614ed8c12a994)
1*105f6285SAndroid Build Coastguard Worker# Copyright (C) 2020 The Android Open Source Project
2*105f6285SAndroid Build Coastguard Worker#
3*105f6285SAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License");
4*105f6285SAndroid Build Coastguard Worker# you may not use this file except in compliance with the License.
5*105f6285SAndroid Build Coastguard Worker# You may obtain a copy of the License at
6*105f6285SAndroid Build Coastguard Worker#
7*105f6285SAndroid Build Coastguard Worker#      http://www.apache.org/licenses/LICENSE-2.0
8*105f6285SAndroid Build Coastguard Worker#
9*105f6285SAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software
10*105f6285SAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS,
11*105f6285SAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*105f6285SAndroid Build Coastguard Worker# See the License for the specific language governing permissions and
13*105f6285SAndroid Build Coastguard Worker# limitations under the License.
14*105f6285SAndroid Build Coastguard Worker"""Splits a manifest to the minimum set of projects needed to build the targets.
15*105f6285SAndroid Build Coastguard Worker
16*105f6285SAndroid Build Coastguard WorkerUsage: manifest_split [options] targets
17*105f6285SAndroid Build Coastguard Worker
18*105f6285SAndroid Build Coastguard Workertargets: Space-separated list of targets that should be buildable
19*105f6285SAndroid Build Coastguard Worker         using the split manifest.
20*105f6285SAndroid Build Coastguard Worker
21*105f6285SAndroid Build Coastguard Workeroptions:
22*105f6285SAndroid Build Coastguard Worker  --manifest <path>
23*105f6285SAndroid Build Coastguard Worker      Path to the repo manifest to split. [Required]
24*105f6285SAndroid Build Coastguard Worker  --split-manifest <path>
25*105f6285SAndroid Build Coastguard Worker      Path to write the resulting split manifest. [Required]
26*105f6285SAndroid Build Coastguard Worker  --config <path>
27*105f6285SAndroid Build Coastguard Worker      Optional path(s) to a config XML file containing projects to add or
28*105f6285SAndroid Build Coastguard Worker      remove. See default_config.xml for an example. This flag can be passed
29*105f6285SAndroid Build Coastguard Worker      more than once to use multiple config files.
30*105f6285SAndroid Build Coastguard Worker        Sample file my_config.xml:
31*105f6285SAndroid Build Coastguard Worker          <config>
32*105f6285SAndroid Build Coastguard Worker            <add_project name="vendor/my/needed/project" />
33*105f6285SAndroid Build Coastguard Worker            <remove_project name="vendor/my/unused/project" />
34*105f6285SAndroid Build Coastguard Worker          </config>
35*105f6285SAndroid Build Coastguard Worker  --ignore-default-config
36*105f6285SAndroid Build Coastguard Worker      If provided, don't include default_config.xml.
37*105f6285SAndroid Build Coastguard Worker  --installed-prebuilt
38*105f6285SAndroid Build Coastguard Worker      Specify the directory containing an installed prebuilt Android.bp file.
39*105f6285SAndroid Build Coastguard Worker      Supply this option zero or more times, once for each installed prebuilt
40*105f6285SAndroid Build Coastguard Worker      directory.
41*105f6285SAndroid Build Coastguard Worker  --repo-list <path>
42*105f6285SAndroid Build Coastguard Worker      Optional path to the output of the 'repo list' command. Used if the
43*105f6285SAndroid Build Coastguard Worker      output of 'repo list' needs pre-processing before being used by
44*105f6285SAndroid Build Coastguard Worker      this tool.
45*105f6285SAndroid Build Coastguard Worker  --ninja-build <path>
46*105f6285SAndroid Build Coastguard Worker      Optional path to the combined-<target>.ninja file found in an out dir.
47*105f6285SAndroid Build Coastguard Worker      If not provided, the default file is used based on the lunch environment.
48*105f6285SAndroid Build Coastguard Worker  --ninja-binary <path>
49*105f6285SAndroid Build Coastguard Worker      Optional path to the ninja binary. Uses the standard binary by default.
50*105f6285SAndroid Build Coastguard Worker  --module-info <path>
51*105f6285SAndroid Build Coastguard Worker      Optional path to the module-info.json file found in an out dir.
52*105f6285SAndroid Build Coastguard Worker      If not provided, the default file is used based on the lunch environment.
53*105f6285SAndroid Build Coastguard Worker  --skip-module-info
54*105f6285SAndroid Build Coastguard Worker      If provided, skip parsing module-info.json for direct and adjacent
55*105f6285SAndroid Build Coastguard Worker      dependencies. Overrides --module-info option.
56*105f6285SAndroid Build Coastguard Worker  --kati-stamp <path>
57*105f6285SAndroid Build Coastguard Worker      Optional path to the .kati_stamp file found in an out dir.
58*105f6285SAndroid Build Coastguard Worker      If not provided, the default file is used based on the lunch environment.
59*105f6285SAndroid Build Coastguard Worker  --skip-kati
60*105f6285SAndroid Build Coastguard Worker      If provided, skip Kati makefiles projects. Overrides --kati-stamp option.
61*105f6285SAndroid Build Coastguard Worker  --overlay <path>
62*105f6285SAndroid Build Coastguard Worker      Optional path(s) to treat as overlays when parsing the kati stamp file
63*105f6285SAndroid Build Coastguard Worker      and scanning for makefiles. See the tools/treble/build/sandbox directory
64*105f6285SAndroid Build Coastguard Worker      for more info about overlays. This flag can be passed more than once.
65*105f6285SAndroid Build Coastguard Worker  --debug-file <path>
66*105f6285SAndroid Build Coastguard Worker      If provided, debug info will be written to a JSON file at this path.
67*105f6285SAndroid Build Coastguard Worker  -h  (--help)
68*105f6285SAndroid Build Coastguard Worker      Display this usage message and exit.
69*105f6285SAndroid Build Coastguard Worker"""
70*105f6285SAndroid Build Coastguard Worker
71*105f6285SAndroid Build Coastguard Workerfrom __future__ import print_function
72*105f6285SAndroid Build Coastguard Worker
73*105f6285SAndroid Build Coastguard Workerimport getopt
74*105f6285SAndroid Build Coastguard Workerimport json
75*105f6285SAndroid Build Coastguard Workerimport logging
76*105f6285SAndroid Build Coastguard Workerimport os
77*105f6285SAndroid Build Coastguard Workerimport pkgutil
78*105f6285SAndroid Build Coastguard Workerimport re
79*105f6285SAndroid Build Coastguard Workerimport subprocess
80*105f6285SAndroid Build Coastguard Workerimport sys
81*105f6285SAndroid Build Coastguard Workerimport tempfile
82*105f6285SAndroid Build Coastguard Workerfrom typing import Dict, List, Pattern, Set, Tuple
83*105f6285SAndroid Build Coastguard Workerimport xml.etree.ElementTree as ET
84*105f6285SAndroid Build Coastguard Worker
85*105f6285SAndroid Build Coastguard Workerimport dataclasses
86*105f6285SAndroid Build Coastguard Worker
87*105f6285SAndroid Build Coastguard Worker
88*105f6285SAndroid Build Coastguard Workerlogging.basicConfig(
89*105f6285SAndroid Build Coastguard Worker    stream=sys.stdout,
90*105f6285SAndroid Build Coastguard Worker    level=logging.INFO,
91*105f6285SAndroid Build Coastguard Worker    format="%(asctime)s - %(filename)s - %(levelname)-8s: %(message)s",
92*105f6285SAndroid Build Coastguard Worker    datefmt="%Y-%m-%d %H:%M:%S")
93*105f6285SAndroid Build Coastguard Workerlogger = logging.getLogger(os.path.basename(__file__))
94*105f6285SAndroid Build Coastguard Worker
95*105f6285SAndroid Build Coastguard Worker# Projects determined to be needed despite the dependency not being visible
96*105f6285SAndroid Build Coastguard Worker# to ninja.
97*105f6285SAndroid Build Coastguard WorkerDEFAULT_CONFIG_XML = "default_config.xml"
98*105f6285SAndroid Build Coastguard Worker
99*105f6285SAndroid Build Coastguard Worker# Pattern that matches a java dependency.
100*105f6285SAndroid Build Coastguard Worker_JAVA_LIB_PATTERN = re.compile(
101*105f6285SAndroid Build Coastguard Worker    # pylint: disable=line-too-long
102*105f6285SAndroid Build Coastguard Worker    '^out/target/common/obj/JAVA_LIBRARIES/(.+)_intermediates/classes-header.jar$'
103*105f6285SAndroid Build Coastguard Worker)
104*105f6285SAndroid Build Coastguard Worker
105*105f6285SAndroid Build Coastguard Worker
106*105f6285SAndroid Build Coastguard Worker@dataclasses.dataclass
107*105f6285SAndroid Build Coastguard Workerclass PathMappingConfig:
108*105f6285SAndroid Build Coastguard Worker  pattern: Pattern[str]
109*105f6285SAndroid Build Coastguard Worker  sub: str
110*105f6285SAndroid Build Coastguard Worker
111*105f6285SAndroid Build Coastguard Worker
112*105f6285SAndroid Build Coastguard Worker@dataclasses.dataclass
113*105f6285SAndroid Build Coastguard Workerclass ManifestSplitConfig:
114*105f6285SAndroid Build Coastguard Worker  """Holds the configuration for the split manifest tool.
115*105f6285SAndroid Build Coastguard Worker
116*105f6285SAndroid Build Coastguard Worker  Attributes:
117*105f6285SAndroid Build Coastguard Worker    remove_projects: A Dict of project name to the config file that specified
118*105f6285SAndroid Build Coastguard Worker      this project, for projects that should be removed from the resulting
119*105f6285SAndroid Build Coastguard Worker      manifest.
120*105f6285SAndroid Build Coastguard Worker    add_projects: A Dict of project name to the config file that specified
121*105f6285SAndroid Build Coastguard Worker      this project, for projects that should be added to the resulting manifest.
122*105f6285SAndroid Build Coastguard Worker    path_mappings: A list of PathMappingConfigs to modify a path in the build
123*105f6285SAndroid Build Coastguard Worker      sandbox to the path in the manifest.
124*105f6285SAndroid Build Coastguard Worker    ignore_paths: Set of paths to ignore when parsing module_info_file
125*105f6285SAndroid Build Coastguard Worker  """
126*105f6285SAndroid Build Coastguard Worker  remove_projects: Dict[str, str]
127*105f6285SAndroid Build Coastguard Worker  add_projects: Dict[str, str]
128*105f6285SAndroid Build Coastguard Worker  path_mappings: List[PathMappingConfig]
129*105f6285SAndroid Build Coastguard Worker  ignore_paths: Set[str]
130*105f6285SAndroid Build Coastguard Worker
131*105f6285SAndroid Build Coastguard Worker  @classmethod
132*105f6285SAndroid Build Coastguard Worker  def from_config_files(cls, config_files: List[str]):
133*105f6285SAndroid Build Coastguard Worker    """Reads from a list of config XML files.
134*105f6285SAndroid Build Coastguard Worker
135*105f6285SAndroid Build Coastguard Worker    Args:
136*105f6285SAndroid Build Coastguard Worker      config_files: A list of config XML filenames.
137*105f6285SAndroid Build Coastguard Worker
138*105f6285SAndroid Build Coastguard Worker    Returns:
139*105f6285SAndroid Build Coastguard Worker      A ManifestSplitConfig from the files.
140*105f6285SAndroid Build Coastguard Worker    """
141*105f6285SAndroid Build Coastguard Worker    remove_projects: Dict[str, str] = {}
142*105f6285SAndroid Build Coastguard Worker    add_projects: Dict[str, str] = {}
143*105f6285SAndroid Build Coastguard Worker    path_mappings = []
144*105f6285SAndroid Build Coastguard Worker    """ Always ignore paths in out/ directory. """
145*105f6285SAndroid Build Coastguard Worker    ignore_paths = set(["out/"])
146*105f6285SAndroid Build Coastguard Worker    for config_file in config_files:
147*105f6285SAndroid Build Coastguard Worker      root = ET.parse(config_file).getroot()
148*105f6285SAndroid Build Coastguard Worker
149*105f6285SAndroid Build Coastguard Worker      remove_projects.update({
150*105f6285SAndroid Build Coastguard Worker          c.attrib["name"]: config_file for c in root.findall("remove_project")
151*105f6285SAndroid Build Coastguard Worker      })
152*105f6285SAndroid Build Coastguard Worker
153*105f6285SAndroid Build Coastguard Worker      add_projects.update(
154*105f6285SAndroid Build Coastguard Worker          {c.attrib["name"]: config_file for c in root.findall("add_project")})
155*105f6285SAndroid Build Coastguard Worker
156*105f6285SAndroid Build Coastguard Worker      path_mappings.extend([
157*105f6285SAndroid Build Coastguard Worker          PathMappingConfig(
158*105f6285SAndroid Build Coastguard Worker              re.compile(child.attrib["pattern"]), child.attrib["sub"])
159*105f6285SAndroid Build Coastguard Worker          for child in root.findall("path_mapping")
160*105f6285SAndroid Build Coastguard Worker      ])
161*105f6285SAndroid Build Coastguard Worker
162*105f6285SAndroid Build Coastguard Worker      ignore_paths.update(
163*105f6285SAndroid Build Coastguard Worker          {c.attrib["name"]: config_file for c in root.findall("ignore_path")})
164*105f6285SAndroid Build Coastguard Worker
165*105f6285SAndroid Build Coastguard Worker    return cls(remove_projects, add_projects, path_mappings, ignore_paths)
166*105f6285SAndroid Build Coastguard Worker
167*105f6285SAndroid Build Coastguard Worker
168*105f6285SAndroid Build Coastguard Workerdef get_repo_projects(repo_list_file, manifest, path_mappings):
169*105f6285SAndroid Build Coastguard Worker  """Returns a dict of { project path : project name } using the manifest.
170*105f6285SAndroid Build Coastguard Worker
171*105f6285SAndroid Build Coastguard Worker  The path_mappings stop on the first match mapping.  If the mapping results in
172*105f6285SAndroid Build Coastguard Worker  an empty string, that entry is removed.
173*105f6285SAndroid Build Coastguard Worker
174*105f6285SAndroid Build Coastguard Worker  Args:
175*105f6285SAndroid Build Coastguard Worker    repo_list_file: An optional filename to read instead of parsing the manifest.
176*105f6285SAndroid Build Coastguard Worker    manifest: The manifest object to scan for projects.
177*105f6285SAndroid Build Coastguard Worker    path_mappings: A list of PathMappingConfigs to modify a path in the build
178*105f6285SAndroid Build Coastguard Worker      sandbox to the path in the manifest.
179*105f6285SAndroid Build Coastguard Worker  """
180*105f6285SAndroid Build Coastguard Worker  repo_list = []
181*105f6285SAndroid Build Coastguard Worker
182*105f6285SAndroid Build Coastguard Worker  if repo_list_file:
183*105f6285SAndroid Build Coastguard Worker    with open(repo_list_file) as repo_list_lines:
184*105f6285SAndroid Build Coastguard Worker      repo_list = [line.strip().split(" : ") for line in repo_list_lines if line.strip()]
185*105f6285SAndroid Build Coastguard Worker  else:
186*105f6285SAndroid Build Coastguard Worker    root = manifest.getroot()
187*105f6285SAndroid Build Coastguard Worker    repo_list = [(p.get("path", p.get("name")), p.get("name")) for p in root.findall("project")]
188*105f6285SAndroid Build Coastguard Worker
189*105f6285SAndroid Build Coastguard Worker  repo_dict = {}
190*105f6285SAndroid Build Coastguard Worker  for entry in repo_list:
191*105f6285SAndroid Build Coastguard Worker    path, project = entry
192*105f6285SAndroid Build Coastguard Worker    for mapping in path_mappings:
193*105f6285SAndroid Build Coastguard Worker      if mapping.pattern.fullmatch(path):
194*105f6285SAndroid Build Coastguard Worker        path = mapping.pattern.sub(mapping.sub, path)
195*105f6285SAndroid Build Coastguard Worker        break
196*105f6285SAndroid Build Coastguard Worker    # If the resulting path mapping is empty, then don't add entry
197*105f6285SAndroid Build Coastguard Worker    if path:
198*105f6285SAndroid Build Coastguard Worker      repo_dict[path] = project
199*105f6285SAndroid Build Coastguard Worker  return repo_dict
200*105f6285SAndroid Build Coastguard Worker
201*105f6285SAndroid Build Coastguard Worker
202*105f6285SAndroid Build Coastguard Workerclass ModuleInfo:
203*105f6285SAndroid Build Coastguard Worker  """Contains various mappings to/from module/project"""
204*105f6285SAndroid Build Coastguard Worker
205*105f6285SAndroid Build Coastguard Worker  def __init__(self, module_info_file, repo_projects, ignore_paths):
206*105f6285SAndroid Build Coastguard Worker    """Initialize a module info instance.
207*105f6285SAndroid Build Coastguard Worker
208*105f6285SAndroid Build Coastguard Worker    Builds various maps related to platform build system modules and how they
209*105f6285SAndroid Build Coastguard Worker    relate to each other and projects.
210*105f6285SAndroid Build Coastguard Worker
211*105f6285SAndroid Build Coastguard Worker    Args:
212*105f6285SAndroid Build Coastguard Worker      module_info_file: The path to a module-info.json file from a build.
213*105f6285SAndroid Build Coastguard Worker      repo_projects: The output of the get_repo_projects function.
214*105f6285SAndroid Build Coastguard Worker      ignore_paths: Set of paths to ignore from module_info_file data
215*105f6285SAndroid Build Coastguard Worker
216*105f6285SAndroid Build Coastguard Worker    Raises:
217*105f6285SAndroid Build Coastguard Worker      ValueError: A module from module-info.json belongs to a path not
218*105f6285SAndroid Build Coastguard Worker        known by the repo projects output.
219*105f6285SAndroid Build Coastguard Worker    """
220*105f6285SAndroid Build Coastguard Worker    # Maps a project to the set of modules it contains.
221*105f6285SAndroid Build Coastguard Worker    self.project_modules = {}
222*105f6285SAndroid Build Coastguard Worker    # Maps a module to the project that contains it.
223*105f6285SAndroid Build Coastguard Worker    self.module_project = {}
224*105f6285SAndroid Build Coastguard Worker    # Maps a module to its class.
225*105f6285SAndroid Build Coastguard Worker    self.module_class = {}
226*105f6285SAndroid Build Coastguard Worker    # Maps a module to modules it depends on.
227*105f6285SAndroid Build Coastguard Worker    self.module_deps = {}
228*105f6285SAndroid Build Coastguard Worker
229*105f6285SAndroid Build Coastguard Worker    with open(module_info_file) as module_info_file:
230*105f6285SAndroid Build Coastguard Worker      module_info = json.load(module_info_file)
231*105f6285SAndroid Build Coastguard Worker
232*105f6285SAndroid Build Coastguard Worker    # Check that module contains a path and the path is not in set of
233*105f6285SAndroid Build Coastguard Worker    # ignore paths
234*105f6285SAndroid Build Coastguard Worker    def module_has_valid_path(module):
235*105f6285SAndroid Build Coastguard Worker      paths = module.get("path")
236*105f6285SAndroid Build Coastguard Worker      if not paths:
237*105f6285SAndroid Build Coastguard Worker        return False
238*105f6285SAndroid Build Coastguard Worker      return all(not paths[0].startswith(p) for p in ignore_paths)
239*105f6285SAndroid Build Coastguard Worker
240*105f6285SAndroid Build Coastguard Worker    module_paths = {
241*105f6285SAndroid Build Coastguard Worker        module: module_info[module]["path"][0]
242*105f6285SAndroid Build Coastguard Worker        for module in module_info
243*105f6285SAndroid Build Coastguard Worker        if module_has_valid_path(module_info[module])
244*105f6285SAndroid Build Coastguard Worker    }
245*105f6285SAndroid Build Coastguard Worker    module_project_paths = {
246*105f6285SAndroid Build Coastguard Worker        module: scan_repo_projects(repo_projects, module_paths[module])
247*105f6285SAndroid Build Coastguard Worker        for module in module_paths
248*105f6285SAndroid Build Coastguard Worker    }
249*105f6285SAndroid Build Coastguard Worker
250*105f6285SAndroid Build Coastguard Worker    for module, project_path in module_project_paths.items():
251*105f6285SAndroid Build Coastguard Worker      if not project_path:
252*105f6285SAndroid Build Coastguard Worker        raise ValueError("Unknown module path for module %s: %s" %
253*105f6285SAndroid Build Coastguard Worker                         (module, module_info[module]))
254*105f6285SAndroid Build Coastguard Worker      repo_project = repo_projects[project_path]
255*105f6285SAndroid Build Coastguard Worker      self.project_modules.setdefault(repo_project, set()).add(module)
256*105f6285SAndroid Build Coastguard Worker      self.module_project[module] = repo_project
257*105f6285SAndroid Build Coastguard Worker
258*105f6285SAndroid Build Coastguard Worker    def dep_from_raw_dep(raw_dep):
259*105f6285SAndroid Build Coastguard Worker      match = re.search(_JAVA_LIB_PATTERN, raw_dep)
260*105f6285SAndroid Build Coastguard Worker      return match.group(1) if match else raw_dep
261*105f6285SAndroid Build Coastguard Worker
262*105f6285SAndroid Build Coastguard Worker    def deps_from_raw_deps(raw_deps):
263*105f6285SAndroid Build Coastguard Worker      return [dep_from_raw_dep(raw_dep) for raw_dep in raw_deps]
264*105f6285SAndroid Build Coastguard Worker
265*105f6285SAndroid Build Coastguard Worker    self.module_class = {
266*105f6285SAndroid Build Coastguard Worker        module: module_info[module]["class"][0]
267*105f6285SAndroid Build Coastguard Worker        for module in module_info
268*105f6285SAndroid Build Coastguard Worker    }
269*105f6285SAndroid Build Coastguard Worker    self.module_deps = {
270*105f6285SAndroid Build Coastguard Worker        module: deps_from_raw_deps(module_info[module]["dependencies"])
271*105f6285SAndroid Build Coastguard Worker        for module in module_info
272*105f6285SAndroid Build Coastguard Worker    }
273*105f6285SAndroid Build Coastguard Worker
274*105f6285SAndroid Build Coastguard Worker
275*105f6285SAndroid Build Coastguard Workerdef get_ninja_inputs(ninja_binary, ninja_build_file, modules):
276*105f6285SAndroid Build Coastguard Worker  """Returns the set of input file path strings for the given modules.
277*105f6285SAndroid Build Coastguard Worker
278*105f6285SAndroid Build Coastguard Worker  Uses the `ninja -t inputs` tool.
279*105f6285SAndroid Build Coastguard Worker
280*105f6285SAndroid Build Coastguard Worker  Args:
281*105f6285SAndroid Build Coastguard Worker    ninja_binary: The path to a ninja binary.
282*105f6285SAndroid Build Coastguard Worker    ninja_build_file: The path to a .ninja file from a build.
283*105f6285SAndroid Build Coastguard Worker    modules: The list of modules to scan for inputs.
284*105f6285SAndroid Build Coastguard Worker  """
285*105f6285SAndroid Build Coastguard Worker  inputs = set()
286*105f6285SAndroid Build Coastguard Worker  NINJA_SHARD_LIMIT = 20000
287*105f6285SAndroid Build Coastguard Worker  for i in range(0, len(modules), NINJA_SHARD_LIMIT):
288*105f6285SAndroid Build Coastguard Worker    modules_shard = modules[i:i + NINJA_SHARD_LIMIT]
289*105f6285SAndroid Build Coastguard Worker    inputs = inputs.union(set(
290*105f6285SAndroid Build Coastguard Worker        subprocess.check_output([
291*105f6285SAndroid Build Coastguard Worker            ninja_binary,
292*105f6285SAndroid Build Coastguard Worker            "-f",
293*105f6285SAndroid Build Coastguard Worker            ninja_build_file,
294*105f6285SAndroid Build Coastguard Worker            "-t",
295*105f6285SAndroid Build Coastguard Worker            "inputs",
296*105f6285SAndroid Build Coastguard Worker            "-d",
297*105f6285SAndroid Build Coastguard Worker        ] + list(modules_shard)).decode().strip("\n").split("\n")))
298*105f6285SAndroid Build Coastguard Worker
299*105f6285SAndroid Build Coastguard Worker  def input_allowed(path):
300*105f6285SAndroid Build Coastguard Worker    path = path.strip()
301*105f6285SAndroid Build Coastguard Worker    if path.endswith("TEST_MAPPING") and "test_mapping" not in modules:
302*105f6285SAndroid Build Coastguard Worker      # Exclude projects that are only needed for TEST_MAPPING files, unless the
303*105f6285SAndroid Build Coastguard Worker      # user is asking to build 'test_mapping'.
304*105f6285SAndroid Build Coastguard Worker      return False
305*105f6285SAndroid Build Coastguard Worker    if path.endswith("MODULE_LICENSE_GPL"):
306*105f6285SAndroid Build Coastguard Worker      # Exclude projects that are included only due to having a
307*105f6285SAndroid Build Coastguard Worker      # MODULE_LICENSE_GPL file, if no other inputs from that project are used.
308*105f6285SAndroid Build Coastguard Worker      return False
309*105f6285SAndroid Build Coastguard Worker    return path
310*105f6285SAndroid Build Coastguard Worker
311*105f6285SAndroid Build Coastguard Worker  return {path.strip() for path in inputs if input_allowed(path)}
312*105f6285SAndroid Build Coastguard Worker
313*105f6285SAndroid Build Coastguard Worker
314*105f6285SAndroid Build Coastguard Workerdef get_kati_makefiles(kati_stamp_file, overlays):
315*105f6285SAndroid Build Coastguard Worker  """Returns the set of makefile paths from the kati stamp file.
316*105f6285SAndroid Build Coastguard Worker
317*105f6285SAndroid Build Coastguard Worker  Uses the ckati prebuilt binary.
318*105f6285SAndroid Build Coastguard Worker  Also includes symlink sources in the resulting set for any
319*105f6285SAndroid Build Coastguard Worker  makefiles that are symlinks.
320*105f6285SAndroid Build Coastguard Worker
321*105f6285SAndroid Build Coastguard Worker  Args:
322*105f6285SAndroid Build Coastguard Worker    kati_stamp_file: The path to a .kati_stamp file from a build.
323*105f6285SAndroid Build Coastguard Worker    overlays: A list of paths to treat as overlays when parsing the kati stamp
324*105f6285SAndroid Build Coastguard Worker      file.
325*105f6285SAndroid Build Coastguard Worker  """
326*105f6285SAndroid Build Coastguard Worker  # Get a set of all makefiles that were parsed by Kati during the build.
327*105f6285SAndroid Build Coastguard Worker  makefiles = set(
328*105f6285SAndroid Build Coastguard Worker      subprocess.check_output([
329*105f6285SAndroid Build Coastguard Worker          "prebuilts/build-tools/linux-x86/bin/ckati",
330*105f6285SAndroid Build Coastguard Worker          "--dump_stamp_tool",
331*105f6285SAndroid Build Coastguard Worker          "--files",
332*105f6285SAndroid Build Coastguard Worker          kati_stamp_file,
333*105f6285SAndroid Build Coastguard Worker      ]).decode().strip("\n").split("\n"))
334*105f6285SAndroid Build Coastguard Worker
335*105f6285SAndroid Build Coastguard Worker  def is_product_makefile(makefile):
336*105f6285SAndroid Build Coastguard Worker    """Returns True if the makefile path meets certain criteria."""
337*105f6285SAndroid Build Coastguard Worker    banned_prefixes = [
338*105f6285SAndroid Build Coastguard Worker        "out/",
339*105f6285SAndroid Build Coastguard Worker        # Ignore product makefiles for sample AOSP boards.
340*105f6285SAndroid Build Coastguard Worker        "device/amlogic",
341*105f6285SAndroid Build Coastguard Worker        "device/generic",
342*105f6285SAndroid Build Coastguard Worker        "device/google",
343*105f6285SAndroid Build Coastguard Worker        "device/linaro",
344*105f6285SAndroid Build Coastguard Worker        "device/sample",
345*105f6285SAndroid Build Coastguard Worker    ]
346*105f6285SAndroid Build Coastguard Worker    banned_suffixes = [
347*105f6285SAndroid Build Coastguard Worker        # All Android.mk files in the source are always parsed by Kati,
348*105f6285SAndroid Build Coastguard Worker        # so including them here would bring in lots of unnecessary projects.
349*105f6285SAndroid Build Coastguard Worker        "Android.mk",
350*105f6285SAndroid Build Coastguard Worker        # The ckati stamp file always includes a line for the ckati bin at
351*105f6285SAndroid Build Coastguard Worker        # the beginnning.
352*105f6285SAndroid Build Coastguard Worker        "bin/ckati",
353*105f6285SAndroid Build Coastguard Worker    ]
354*105f6285SAndroid Build Coastguard Worker    return (all([not makefile.startswith(p) for p in banned_prefixes]) and
355*105f6285SAndroid Build Coastguard Worker            all([not makefile.endswith(s) for s in banned_suffixes]))
356*105f6285SAndroid Build Coastguard Worker
357*105f6285SAndroid Build Coastguard Worker  # Limit the makefiles to only product makefiles.
358*105f6285SAndroid Build Coastguard Worker  product_makefiles = {
359*105f6285SAndroid Build Coastguard Worker      os.path.normpath(path) for path in makefiles if is_product_makefile(path)
360*105f6285SAndroid Build Coastguard Worker  }
361*105f6285SAndroid Build Coastguard Worker
362*105f6285SAndroid Build Coastguard Worker  def strip_overlay(makefile):
363*105f6285SAndroid Build Coastguard Worker    """Remove any overlays from a makefile path."""
364*105f6285SAndroid Build Coastguard Worker    for overlay in overlays:
365*105f6285SAndroid Build Coastguard Worker      if makefile.startswith(overlay):
366*105f6285SAndroid Build Coastguard Worker        return makefile[len(overlay):]
367*105f6285SAndroid Build Coastguard Worker    return makefile
368*105f6285SAndroid Build Coastguard Worker
369*105f6285SAndroid Build Coastguard Worker  makefiles_and_symlinks = set()
370*105f6285SAndroid Build Coastguard Worker  for makefile in product_makefiles:
371*105f6285SAndroid Build Coastguard Worker    # Search for the makefile, possibly scanning overlays as well.
372*105f6285SAndroid Build Coastguard Worker    for overlay in [""] + overlays:
373*105f6285SAndroid Build Coastguard Worker      makefile_with_overlay = os.path.join(overlay, makefile)
374*105f6285SAndroid Build Coastguard Worker      if os.path.exists(makefile_with_overlay):
375*105f6285SAndroid Build Coastguard Worker        makefile = makefile_with_overlay
376*105f6285SAndroid Build Coastguard Worker        break
377*105f6285SAndroid Build Coastguard Worker
378*105f6285SAndroid Build Coastguard Worker    if not os.path.exists(makefile):
379*105f6285SAndroid Build Coastguard Worker      logger.warning("Unknown kati makefile: %s" % makefile)
380*105f6285SAndroid Build Coastguard Worker      continue
381*105f6285SAndroid Build Coastguard Worker
382*105f6285SAndroid Build Coastguard Worker    # Ensure the project that contains the makefile is included, as well as
383*105f6285SAndroid Build Coastguard Worker    # the project that any makefile symlinks point to.
384*105f6285SAndroid Build Coastguard Worker    makefiles_and_symlinks.add(strip_overlay(makefile))
385*105f6285SAndroid Build Coastguard Worker    if os.path.islink(makefile):
386*105f6285SAndroid Build Coastguard Worker      makefiles_and_symlinks.add(
387*105f6285SAndroid Build Coastguard Worker          strip_overlay(os.path.relpath(os.path.realpath(makefile))))
388*105f6285SAndroid Build Coastguard Worker
389*105f6285SAndroid Build Coastguard Worker  return makefiles_and_symlinks
390*105f6285SAndroid Build Coastguard Worker
391*105f6285SAndroid Build Coastguard Worker
392*105f6285SAndroid Build Coastguard Workerdef scan_repo_projects(repo_projects, input_path):
393*105f6285SAndroid Build Coastguard Worker  """Returns the project path of the given input path if it exists.
394*105f6285SAndroid Build Coastguard Worker
395*105f6285SAndroid Build Coastguard Worker  Args:
396*105f6285SAndroid Build Coastguard Worker    repo_projects: The output of the get_repo_projects function.
397*105f6285SAndroid Build Coastguard Worker    input_path: The path of an input file used in the build, as given by the
398*105f6285SAndroid Build Coastguard Worker      ninja inputs tool.
399*105f6285SAndroid Build Coastguard Worker
400*105f6285SAndroid Build Coastguard Worker  Returns:
401*105f6285SAndroid Build Coastguard Worker    The path string, or None if not found.
402*105f6285SAndroid Build Coastguard Worker  """
403*105f6285SAndroid Build Coastguard Worker  parts = input_path.split("/")
404*105f6285SAndroid Build Coastguard Worker
405*105f6285SAndroid Build Coastguard Worker  for index in reversed(range(0, len(parts))):
406*105f6285SAndroid Build Coastguard Worker    project_path = os.path.join(*parts[:index + 1])
407*105f6285SAndroid Build Coastguard Worker    if project_path in repo_projects:
408*105f6285SAndroid Build Coastguard Worker      return project_path
409*105f6285SAndroid Build Coastguard Worker
410*105f6285SAndroid Build Coastguard Worker  return None
411*105f6285SAndroid Build Coastguard Worker
412*105f6285SAndroid Build Coastguard Worker
413*105f6285SAndroid Build Coastguard Workerdef get_input_projects(repo_projects, inputs):
414*105f6285SAndroid Build Coastguard Worker  """Returns the collection of project names that contain the given input paths.
415*105f6285SAndroid Build Coastguard Worker
416*105f6285SAndroid Build Coastguard Worker  Args:
417*105f6285SAndroid Build Coastguard Worker    repo_projects: The output of the get_repo_projects function.
418*105f6285SAndroid Build Coastguard Worker    inputs: The paths of input files used in the build, as given by the ninja
419*105f6285SAndroid Build Coastguard Worker      inputs tool.
420*105f6285SAndroid Build Coastguard Worker  """
421*105f6285SAndroid Build Coastguard Worker  input_project_paths = {}
422*105f6285SAndroid Build Coastguard Worker  for input_path in inputs:
423*105f6285SAndroid Build Coastguard Worker    if not input_path.startswith("out/") and not input_path.startswith("/"):
424*105f6285SAndroid Build Coastguard Worker      input_project_paths.setdefault(
425*105f6285SAndroid Build Coastguard Worker          scan_repo_projects(repo_projects, input_path), []).append(input_path)
426*105f6285SAndroid Build Coastguard Worker
427*105f6285SAndroid Build Coastguard Worker  return {
428*105f6285SAndroid Build Coastguard Worker      repo_projects[project_path]: inputs
429*105f6285SAndroid Build Coastguard Worker      for project_path, inputs in input_project_paths.items()
430*105f6285SAndroid Build Coastguard Worker      if project_path is not None
431*105f6285SAndroid Build Coastguard Worker  }
432*105f6285SAndroid Build Coastguard Worker
433*105f6285SAndroid Build Coastguard Worker
434*105f6285SAndroid Build Coastguard Workerdef update_manifest(manifest, input_projects, remove_projects):
435*105f6285SAndroid Build Coastguard Worker  """Modifies and returns a manifest ElementTree by modifying its projects.
436*105f6285SAndroid Build Coastguard Worker
437*105f6285SAndroid Build Coastguard Worker  Args:
438*105f6285SAndroid Build Coastguard Worker    manifest: The manifest object to modify.
439*105f6285SAndroid Build Coastguard Worker    input_projects: A set of projects that should stay in the manifest.
440*105f6285SAndroid Build Coastguard Worker    remove_projects: A set of projects that should be removed from the manifest.
441*105f6285SAndroid Build Coastguard Worker      Projects in this set override input_projects.
442*105f6285SAndroid Build Coastguard Worker
443*105f6285SAndroid Build Coastguard Worker  Returns:
444*105f6285SAndroid Build Coastguard Worker    The modified manifest object.
445*105f6285SAndroid Build Coastguard Worker  """
446*105f6285SAndroid Build Coastguard Worker  projects_to_keep = input_projects.difference(remove_projects)
447*105f6285SAndroid Build Coastguard Worker  root = manifest.getroot()
448*105f6285SAndroid Build Coastguard Worker  for child in root.findall("project"):
449*105f6285SAndroid Build Coastguard Worker    if child.attrib["name"] not in projects_to_keep:
450*105f6285SAndroid Build Coastguard Worker      root.remove(child)
451*105f6285SAndroid Build Coastguard Worker  return manifest
452*105f6285SAndroid Build Coastguard Worker
453*105f6285SAndroid Build Coastguard Worker
454*105f6285SAndroid Build Coastguard Worker@dataclasses.dataclass
455*105f6285SAndroid Build Coastguard Workerclass DebugInfo:
456*105f6285SAndroid Build Coastguard Worker  """Simple class to store structured debug info for a project."""
457*105f6285SAndroid Build Coastguard Worker  direct_input: bool = False
458*105f6285SAndroid Build Coastguard Worker  adjacent_input: bool = False
459*105f6285SAndroid Build Coastguard Worker  deps_input: bool = False
460*105f6285SAndroid Build Coastguard Worker  kati_makefiles: List[str] = dataclasses.field(default_factory=list)
461*105f6285SAndroid Build Coastguard Worker  manual_add_config: str = ""
462*105f6285SAndroid Build Coastguard Worker  manual_remove_config: str = ""
463*105f6285SAndroid Build Coastguard Worker
464*105f6285SAndroid Build Coastguard Worker
465*105f6285SAndroid Build Coastguard Workerdef create_split_manifest(targets, manifest_file, split_manifest_file,
466*105f6285SAndroid Build Coastguard Worker                          config_files, repo_list_file, ninja_build_file,
467*105f6285SAndroid Build Coastguard Worker                          ninja_binary, module_info_file, kati_stamp_file,
468*105f6285SAndroid Build Coastguard Worker                          overlays, installed_prebuilts, debug_file):
469*105f6285SAndroid Build Coastguard Worker  """Creates and writes a split manifest by inspecting build inputs.
470*105f6285SAndroid Build Coastguard Worker
471*105f6285SAndroid Build Coastguard Worker  Args:
472*105f6285SAndroid Build Coastguard Worker    targets: List of targets that should be buildable using the split manifest.
473*105f6285SAndroid Build Coastguard Worker    manifest_file: Path to the repo manifest to split.
474*105f6285SAndroid Build Coastguard Worker    split_manifest_file: Path to write the resulting split manifest.
475*105f6285SAndroid Build Coastguard Worker    config_files: Paths to a config XML file containing projects to add or
476*105f6285SAndroid Build Coastguard Worker      remove. See default_config.xml for an example. This flag can be passed
477*105f6285SAndroid Build Coastguard Worker      more than once to use multiple config files.
478*105f6285SAndroid Build Coastguard Worker    repo_list_file: Path to the output of the 'repo list' command.
479*105f6285SAndroid Build Coastguard Worker    ninja_build_file: Path to the combined-<target>.ninja file found in an out
480*105f6285SAndroid Build Coastguard Worker      dir.
481*105f6285SAndroid Build Coastguard Worker    ninja_binary: Path to the ninja binary.
482*105f6285SAndroid Build Coastguard Worker    module_info_file: Path to the module-info.json file found in an out dir.
483*105f6285SAndroid Build Coastguard Worker    kati_stamp_file: The path to a .kati_stamp file from a build.
484*105f6285SAndroid Build Coastguard Worker    overlays: A list of paths to treat as overlays when parsing the kati stamp
485*105f6285SAndroid Build Coastguard Worker      file.
486*105f6285SAndroid Build Coastguard Worker    installed_prebuilts: A list of paths for which to create "fake" repo
487*105f6285SAndroid Build Coastguard Worker      entries. These entries allow the tool to recognize modules that installed
488*105f6285SAndroid Build Coastguard Worker      rather than being sync'd via a manifest.
489*105f6285SAndroid Build Coastguard Worker    debug_file: If not None, the path to write JSON debug info.
490*105f6285SAndroid Build Coastguard Worker  """
491*105f6285SAndroid Build Coastguard Worker  debug_info = {}
492*105f6285SAndroid Build Coastguard Worker
493*105f6285SAndroid Build Coastguard Worker  config = ManifestSplitConfig.from_config_files(config_files)
494*105f6285SAndroid Build Coastguard Worker  original_manifest = ET.parse(manifest_file)
495*105f6285SAndroid Build Coastguard Worker
496*105f6285SAndroid Build Coastguard Worker
497*105f6285SAndroid Build Coastguard Worker  repo_projects = get_repo_projects(repo_list_file, original_manifest,
498*105f6285SAndroid Build Coastguard Worker                                    config.path_mappings)
499*105f6285SAndroid Build Coastguard Worker  repo_projects.update({ip: ip for ip in installed_prebuilts})
500*105f6285SAndroid Build Coastguard Worker
501*105f6285SAndroid Build Coastguard Worker  inputs = get_ninja_inputs(ninja_binary, ninja_build_file, targets)
502*105f6285SAndroid Build Coastguard Worker  input_projects = set(get_input_projects(repo_projects, inputs).keys())
503*105f6285SAndroid Build Coastguard Worker  for project in input_projects:
504*105f6285SAndroid Build Coastguard Worker    debug_info.setdefault(project, DebugInfo()).direct_input = True
505*105f6285SAndroid Build Coastguard Worker  logger.info(
506*105f6285SAndroid Build Coastguard Worker      "%s projects needed for Ninja-graph direct dependencies of targets \"%s\"",
507*105f6285SAndroid Build Coastguard Worker      len(input_projects), " ".join(targets))
508*105f6285SAndroid Build Coastguard Worker
509*105f6285SAndroid Build Coastguard Worker  if kati_stamp_file:
510*105f6285SAndroid Build Coastguard Worker    kati_makefiles = get_kati_makefiles(kati_stamp_file, overlays)
511*105f6285SAndroid Build Coastguard Worker    kati_makefiles_projects = get_input_projects(repo_projects, kati_makefiles)
512*105f6285SAndroid Build Coastguard Worker    for project, makefiles in kati_makefiles_projects.items():
513*105f6285SAndroid Build Coastguard Worker      debug_info.setdefault(project, DebugInfo()).kati_makefiles = makefiles
514*105f6285SAndroid Build Coastguard Worker    input_projects = input_projects.union(kati_makefiles_projects.keys())
515*105f6285SAndroid Build Coastguard Worker    logger.info("%s projects after including Kati makefiles projects.",
516*105f6285SAndroid Build Coastguard Worker                len(input_projects))
517*105f6285SAndroid Build Coastguard Worker  else:
518*105f6285SAndroid Build Coastguard Worker    logger.info("Kati makefiles projects skipped.")
519*105f6285SAndroid Build Coastguard Worker
520*105f6285SAndroid Build Coastguard Worker  for project, cfile in config.add_projects.items():
521*105f6285SAndroid Build Coastguard Worker    debug_info.setdefault(project, DebugInfo()).manual_add_config = cfile
522*105f6285SAndroid Build Coastguard Worker  for project, cfile in config.remove_projects.items():
523*105f6285SAndroid Build Coastguard Worker    debug_info.setdefault(project, DebugInfo()).manual_remove_config = cfile
524*105f6285SAndroid Build Coastguard Worker  input_projects = input_projects.union(config.add_projects.keys())
525*105f6285SAndroid Build Coastguard Worker  logger.info("%s projects after including manual additions.",
526*105f6285SAndroid Build Coastguard Worker              len(input_projects))
527*105f6285SAndroid Build Coastguard Worker
528*105f6285SAndroid Build Coastguard Worker  # Remove projects from our set of input projects before adding adjacent
529*105f6285SAndroid Build Coastguard Worker  # modules, so that no project is added only because of an adjacent
530*105f6285SAndroid Build Coastguard Worker  # dependency in a to-be-removed project.
531*105f6285SAndroid Build Coastguard Worker  input_projects = input_projects.difference(config.remove_projects.keys())
532*105f6285SAndroid Build Coastguard Worker
533*105f6285SAndroid Build Coastguard Worker  # While we still have projects whose modules we haven't checked yet,
534*105f6285SAndroid Build Coastguard Worker  if module_info_file:
535*105f6285SAndroid Build Coastguard Worker    module_info = ModuleInfo(module_info_file, repo_projects,
536*105f6285SAndroid Build Coastguard Worker                             config.ignore_paths)
537*105f6285SAndroid Build Coastguard Worker    checked_projects = set()
538*105f6285SAndroid Build Coastguard Worker    projects_to_check = input_projects.difference(checked_projects)
539*105f6285SAndroid Build Coastguard Worker    logger.info("Checking module-info dependencies for direct and adjacent modules...")
540*105f6285SAndroid Build Coastguard Worker  else:
541*105f6285SAndroid Build Coastguard Worker    logging.info("Direct and adjacent modules skipped.")
542*105f6285SAndroid Build Coastguard Worker    projects_to_check = None
543*105f6285SAndroid Build Coastguard Worker
544*105f6285SAndroid Build Coastguard Worker  iteration = 0
545*105f6285SAndroid Build Coastguard Worker
546*105f6285SAndroid Build Coastguard Worker  while projects_to_check:
547*105f6285SAndroid Build Coastguard Worker    iteration += 1
548*105f6285SAndroid Build Coastguard Worker    # check all modules in each project,
549*105f6285SAndroid Build Coastguard Worker    modules = []
550*105f6285SAndroid Build Coastguard Worker    deps_additions = set()
551*105f6285SAndroid Build Coastguard Worker
552*105f6285SAndroid Build Coastguard Worker    def process_deps(module):
553*105f6285SAndroid Build Coastguard Worker      for d in module_info.module_deps[module]:
554*105f6285SAndroid Build Coastguard Worker        if d in module_info.module_class:
555*105f6285SAndroid Build Coastguard Worker          if module_info.module_class[d] == "HEADER_LIBRARIES":
556*105f6285SAndroid Build Coastguard Worker            hla = module_info.module_project[d]
557*105f6285SAndroid Build Coastguard Worker            if hla not in input_projects:
558*105f6285SAndroid Build Coastguard Worker              deps_additions.add(hla)
559*105f6285SAndroid Build Coastguard Worker
560*105f6285SAndroid Build Coastguard Worker    for project in projects_to_check:
561*105f6285SAndroid Build Coastguard Worker      checked_projects.add(project)
562*105f6285SAndroid Build Coastguard Worker      if project not in module_info.project_modules:
563*105f6285SAndroid Build Coastguard Worker        continue
564*105f6285SAndroid Build Coastguard Worker      for module in module_info.project_modules[project]:
565*105f6285SAndroid Build Coastguard Worker        modules.append(module)
566*105f6285SAndroid Build Coastguard Worker        process_deps(module)
567*105f6285SAndroid Build Coastguard Worker
568*105f6285SAndroid Build Coastguard Worker    for project in deps_additions:
569*105f6285SAndroid Build Coastguard Worker      debug_info.setdefault(project, DebugInfo()).deps_input = True
570*105f6285SAndroid Build Coastguard Worker    input_projects = input_projects.union(deps_additions)
571*105f6285SAndroid Build Coastguard Worker    logger.info(
572*105f6285SAndroid Build Coastguard Worker        "pass %d - %d projects after including HEADER_LIBRARIES dependencies",
573*105f6285SAndroid Build Coastguard Worker        iteration, len(input_projects))
574*105f6285SAndroid Build Coastguard Worker
575*105f6285SAndroid Build Coastguard Worker    # adding those modules' input projects to our list of projects.
576*105f6285SAndroid Build Coastguard Worker    inputs = get_ninja_inputs(ninja_binary, ninja_build_file, modules)
577*105f6285SAndroid Build Coastguard Worker    adjacent_module_additions = set(
578*105f6285SAndroid Build Coastguard Worker        get_input_projects(repo_projects, inputs).keys())
579*105f6285SAndroid Build Coastguard Worker    for project in adjacent_module_additions:
580*105f6285SAndroid Build Coastguard Worker      debug_info.setdefault(project, DebugInfo()).adjacent_input = True
581*105f6285SAndroid Build Coastguard Worker    input_projects = input_projects.union(adjacent_module_additions)
582*105f6285SAndroid Build Coastguard Worker    logger.info(
583*105f6285SAndroid Build Coastguard Worker        "pass %d - %d projects after including adjacent-module Ninja-graph dependencies",
584*105f6285SAndroid Build Coastguard Worker        iteration, len(input_projects))
585*105f6285SAndroid Build Coastguard Worker
586*105f6285SAndroid Build Coastguard Worker    projects_to_check = input_projects.difference(checked_projects)
587*105f6285SAndroid Build Coastguard Worker
588*105f6285SAndroid Build Coastguard Worker  logger.info("%s projects - complete", len(input_projects))
589*105f6285SAndroid Build Coastguard Worker
590*105f6285SAndroid Build Coastguard Worker  split_manifest = update_manifest(original_manifest, input_projects,
591*105f6285SAndroid Build Coastguard Worker                                   config.remove_projects.keys())
592*105f6285SAndroid Build Coastguard Worker  split_manifest.write(split_manifest_file)
593*105f6285SAndroid Build Coastguard Worker
594*105f6285SAndroid Build Coastguard Worker  if debug_file:
595*105f6285SAndroid Build Coastguard Worker    with open(debug_file, "w") as debug_fp:
596*105f6285SAndroid Build Coastguard Worker      logger.info("Writing debug info to %s", debug_file)
597*105f6285SAndroid Build Coastguard Worker      json.dump(
598*105f6285SAndroid Build Coastguard Worker          debug_info,
599*105f6285SAndroid Build Coastguard Worker          fp=debug_fp,
600*105f6285SAndroid Build Coastguard Worker          sort_keys=True,
601*105f6285SAndroid Build Coastguard Worker          indent=2,
602*105f6285SAndroid Build Coastguard Worker          default=lambda info: info.__dict__)
603*105f6285SAndroid Build Coastguard Worker
604*105f6285SAndroid Build Coastguard Worker
605*105f6285SAndroid Build Coastguard Workerdef main(argv):
606*105f6285SAndroid Build Coastguard Worker  try:
607*105f6285SAndroid Build Coastguard Worker    opts, args = getopt.getopt(argv, "h", [
608*105f6285SAndroid Build Coastguard Worker        "help",
609*105f6285SAndroid Build Coastguard Worker        "debug-file=",
610*105f6285SAndroid Build Coastguard Worker        "manifest=",
611*105f6285SAndroid Build Coastguard Worker        "split-manifest=",
612*105f6285SAndroid Build Coastguard Worker        "config=",
613*105f6285SAndroid Build Coastguard Worker        "ignore-default-config",
614*105f6285SAndroid Build Coastguard Worker        "repo-list=",
615*105f6285SAndroid Build Coastguard Worker        "ninja-build=",
616*105f6285SAndroid Build Coastguard Worker        "ninja-binary=",
617*105f6285SAndroid Build Coastguard Worker        "module-info=",
618*105f6285SAndroid Build Coastguard Worker        "skip-module-info",
619*105f6285SAndroid Build Coastguard Worker        "kati-stamp=",
620*105f6285SAndroid Build Coastguard Worker        "skip-kati",
621*105f6285SAndroid Build Coastguard Worker        "overlay=",
622*105f6285SAndroid Build Coastguard Worker        "installed-prebuilt=",
623*105f6285SAndroid Build Coastguard Worker    ])
624*105f6285SAndroid Build Coastguard Worker  except getopt.GetoptError as err:
625*105f6285SAndroid Build Coastguard Worker    print(__doc__, file=sys.stderr)
626*105f6285SAndroid Build Coastguard Worker    print("**%s**" % str(err), file=sys.stderr)
627*105f6285SAndroid Build Coastguard Worker    sys.exit(2)
628*105f6285SAndroid Build Coastguard Worker
629*105f6285SAndroid Build Coastguard Worker  debug_file = None
630*105f6285SAndroid Build Coastguard Worker  manifest_file = None
631*105f6285SAndroid Build Coastguard Worker  split_manifest_file = None
632*105f6285SAndroid Build Coastguard Worker  config_files = []
633*105f6285SAndroid Build Coastguard Worker  repo_list_file = None
634*105f6285SAndroid Build Coastguard Worker  ninja_build_file = None
635*105f6285SAndroid Build Coastguard Worker  module_info_file = None
636*105f6285SAndroid Build Coastguard Worker  ninja_binary = "prebuilts/build-tools/linux-x86/bin/ninja"
637*105f6285SAndroid Build Coastguard Worker  kati_stamp_file = None
638*105f6285SAndroid Build Coastguard Worker  overlays = []
639*105f6285SAndroid Build Coastguard Worker  installed_prebuilts = []
640*105f6285SAndroid Build Coastguard Worker  ignore_default_config = False
641*105f6285SAndroid Build Coastguard Worker  skip_kati = False
642*105f6285SAndroid Build Coastguard Worker  skip_module_info = False
643*105f6285SAndroid Build Coastguard Worker
644*105f6285SAndroid Build Coastguard Worker  for o, a in opts:
645*105f6285SAndroid Build Coastguard Worker    if o in ("-h", "--help"):
646*105f6285SAndroid Build Coastguard Worker      print(__doc__, file=sys.stderr)
647*105f6285SAndroid Build Coastguard Worker      sys.exit()
648*105f6285SAndroid Build Coastguard Worker    elif o in ("--debug-file"):
649*105f6285SAndroid Build Coastguard Worker      debug_file = a
650*105f6285SAndroid Build Coastguard Worker    elif o in ("--manifest"):
651*105f6285SAndroid Build Coastguard Worker      manifest_file = a
652*105f6285SAndroid Build Coastguard Worker    elif o in ("--split-manifest"):
653*105f6285SAndroid Build Coastguard Worker      split_manifest_file = a
654*105f6285SAndroid Build Coastguard Worker    elif o in ("--config"):
655*105f6285SAndroid Build Coastguard Worker      config_files.append(a)
656*105f6285SAndroid Build Coastguard Worker    elif o == "--ignore-default-config":
657*105f6285SAndroid Build Coastguard Worker      ignore_default_config = True
658*105f6285SAndroid Build Coastguard Worker    elif o in ("--repo-list"):
659*105f6285SAndroid Build Coastguard Worker      repo_list_file = a
660*105f6285SAndroid Build Coastguard Worker    elif o in ("--ninja-build"):
661*105f6285SAndroid Build Coastguard Worker      ninja_build_file = a
662*105f6285SAndroid Build Coastguard Worker    elif o in ("--ninja-binary"):
663*105f6285SAndroid Build Coastguard Worker      ninja_binary = a
664*105f6285SAndroid Build Coastguard Worker    elif o in ("--module-info"):
665*105f6285SAndroid Build Coastguard Worker      module_info_file = a
666*105f6285SAndroid Build Coastguard Worker    elif o == "--skip-module-info":
667*105f6285SAndroid Build Coastguard Worker      skip_module_info = True
668*105f6285SAndroid Build Coastguard Worker    elif o in ("--kati-stamp"):
669*105f6285SAndroid Build Coastguard Worker      kati_stamp_file = a
670*105f6285SAndroid Build Coastguard Worker    elif o == "--skip-kati":
671*105f6285SAndroid Build Coastguard Worker      skip_kati = True
672*105f6285SAndroid Build Coastguard Worker    elif o in ("--overlay"):
673*105f6285SAndroid Build Coastguard Worker      overlays.append(a)
674*105f6285SAndroid Build Coastguard Worker    elif o in ("--installed-prebuilt"):
675*105f6285SAndroid Build Coastguard Worker      installed_prebuilts.append(a)
676*105f6285SAndroid Build Coastguard Worker    else:
677*105f6285SAndroid Build Coastguard Worker      assert False, "unknown option \"%s\"" % o
678*105f6285SAndroid Build Coastguard Worker
679*105f6285SAndroid Build Coastguard Worker  if not args:
680*105f6285SAndroid Build Coastguard Worker    print(__doc__, file=sys.stderr)
681*105f6285SAndroid Build Coastguard Worker    print("**Missing targets**", file=sys.stderr)
682*105f6285SAndroid Build Coastguard Worker    sys.exit(2)
683*105f6285SAndroid Build Coastguard Worker  if not manifest_file:
684*105f6285SAndroid Build Coastguard Worker    print(__doc__, file=sys.stderr)
685*105f6285SAndroid Build Coastguard Worker    print("**Missing required flag --manifest**", file=sys.stderr)
686*105f6285SAndroid Build Coastguard Worker    sys.exit(2)
687*105f6285SAndroid Build Coastguard Worker  if not split_manifest_file:
688*105f6285SAndroid Build Coastguard Worker    print(__doc__, file=sys.stderr)
689*105f6285SAndroid Build Coastguard Worker    print("**Missing required flag --split-manifest**", file=sys.stderr)
690*105f6285SAndroid Build Coastguard Worker    sys.exit(2)
691*105f6285SAndroid Build Coastguard Worker
692*105f6285SAndroid Build Coastguard Worker  if skip_module_info:
693*105f6285SAndroid Build Coastguard Worker    if module_info_file:
694*105f6285SAndroid Build Coastguard Worker      logging.warning("User provided both --skip-module-info and --module-info args.  Arg --module-info ignored.")
695*105f6285SAndroid Build Coastguard Worker    module_info_file = None
696*105f6285SAndroid Build Coastguard Worker  elif not module_info_file:
697*105f6285SAndroid Build Coastguard Worker    module_info_file = os.path.join(os.environ["ANDROID_PRODUCT_OUT"],
698*105f6285SAndroid Build Coastguard Worker                                    "module-info.json")
699*105f6285SAndroid Build Coastguard Worker  if skip_kati:
700*105f6285SAndroid Build Coastguard Worker    if kati_stamp_file:
701*105f6285SAndroid Build Coastguard Worker      logging.warning("User provided both --skip-kati and --kati-stamp args.  Arg --kati-stamp ignored.")
702*105f6285SAndroid Build Coastguard Worker    kati_stamp_file = None
703*105f6285SAndroid Build Coastguard Worker  elif not kati_stamp_file:
704*105f6285SAndroid Build Coastguard Worker    kati_stamp_file = os.path.join(
705*105f6285SAndroid Build Coastguard Worker        os.environ["ANDROID_BUILD_TOP"], "out",
706*105f6285SAndroid Build Coastguard Worker        ".kati_stamp-%s" % os.environ["TARGET_PRODUCT"])
707*105f6285SAndroid Build Coastguard Worker
708*105f6285SAndroid Build Coastguard Worker  if not ninja_build_file:
709*105f6285SAndroid Build Coastguard Worker    ninja_build_file = os.path.join(
710*105f6285SAndroid Build Coastguard Worker        os.environ["ANDROID_BUILD_TOP"], "out",
711*105f6285SAndroid Build Coastguard Worker        "combined-%s.ninja" % os.environ["TARGET_PRODUCT"])
712*105f6285SAndroid Build Coastguard Worker
713*105f6285SAndroid Build Coastguard Worker  with tempfile.NamedTemporaryFile() as default_config_file:
714*105f6285SAndroid Build Coastguard Worker    if not ignore_default_config:
715*105f6285SAndroid Build Coastguard Worker      default_config_file.write(pkgutil.get_data(__name__, DEFAULT_CONFIG_XML))
716*105f6285SAndroid Build Coastguard Worker      default_config_file.flush()
717*105f6285SAndroid Build Coastguard Worker      config_files.insert(0, default_config_file.name)
718*105f6285SAndroid Build Coastguard Worker
719*105f6285SAndroid Build Coastguard Worker    create_split_manifest(
720*105f6285SAndroid Build Coastguard Worker        targets=args,
721*105f6285SAndroid Build Coastguard Worker        manifest_file=manifest_file,
722*105f6285SAndroid Build Coastguard Worker        split_manifest_file=split_manifest_file,
723*105f6285SAndroid Build Coastguard Worker        config_files=config_files,
724*105f6285SAndroid Build Coastguard Worker        repo_list_file=repo_list_file,
725*105f6285SAndroid Build Coastguard Worker        ninja_build_file=ninja_build_file,
726*105f6285SAndroid Build Coastguard Worker        ninja_binary=ninja_binary,
727*105f6285SAndroid Build Coastguard Worker        module_info_file=module_info_file,
728*105f6285SAndroid Build Coastguard Worker        kati_stamp_file=kati_stamp_file,
729*105f6285SAndroid Build Coastguard Worker        overlays=overlays,
730*105f6285SAndroid Build Coastguard Worker        installed_prebuilts=installed_prebuilts,
731*105f6285SAndroid Build Coastguard Worker        debug_file=debug_file)
732*105f6285SAndroid Build Coastguard Worker
733*105f6285SAndroid Build Coastguard Worker
734*105f6285SAndroid Build Coastguard Workerif __name__ == "__main__":
735*105f6285SAndroid Build Coastguard Worker  main(sys.argv[1:])
736