tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

extract_metadata.py (4253B)


      1 # Copyright 2024 The Chromium Authors
      2 # Use of this source code is governed by a BSD-style license that can be
      3 # found in the LICENSE file.
      4 
      5 import argparse
      6 import json
      7 import os
      8 import subprocess
      9 import sys
     10 import shutil
     11 import tempfile
     12 
     13 TARGET_CPU_MAPPING = {
     14    'x64': 'x86_64',
     15    'arm64': 'arm64',
     16 }
     17 
     18 METADATA_FILES = ('extract.actionsdata', 'version.json')
     19 
     20 
     21 def read_json(path):
     22  """Reads JSON file at `path`."""
     23  with open(path, encoding='utf8') as stream:
     24    return json.load(stream)
     25 
     26 
     27 def add_argument(parser, name, help, required=True):
     28  """Add argument --{name} to `parser` with description `help`."""
     29  parser.add_argument(f'--{name}', required=required, help=help)
     30 
     31 
     32 def extract_metadata(parsed, module_name, swift_files, const_files):
     33  """
     34  Extracts metadata for `module_name` according to `parsed`.
     35 
     36  If the extraction fails or no metadata is generated, terminate the script
     37  with an error (after printing the command stdout/stderr to stderr).
     38  """
     39 
     40  metadata_dir = os.path.join(parsed.output, 'Metadata.appintents')
     41  if os.path.exists(metadata_dir):
     42    shutil.rmtree(metadata_dir)
     43 
     44  target_cpu = TARGET_CPU_MAPPING[parsed.target_cpu]
     45  target_triple = f'{target_cpu}-apple-ios{parsed.deployment_target}'
     46  if parsed.target_environment == 'simulator':
     47    target_triple += '-simulator'
     48 
     49  command = [
     50      os.path.join(parsed.toolchain_dir, 'usr/bin/appintentsmetadataprocessor'),
     51      '--toolchain-dir',
     52      parsed.toolchain_dir,
     53      '--sdk-root',
     54      parsed.sdk_root,
     55      '--deployment-target',
     56      parsed.deployment_target,
     57      '--target-triple',
     58      target_triple,
     59      '--module-name',
     60      module_name,
     61      '--output',
     62      parsed.output,
     63      '--binary-file',
     64      parsed.binary_file,
     65      '--compile-time-extraction',
     66  ]
     67 
     68  inputs = set()
     69  inputs.add(parsed.binary_file)
     70 
     71  for swift_file in swift_files:
     72    inputs.add(swift_file)
     73    command.extend(('--source-files', swift_file))
     74 
     75  for const_file in const_files:
     76    inputs.add(const_file)
     77    command.extend(('--swift-const-vals', const_file))
     78 
     79  if parsed.xcode_version is not None:
     80    command.extend(('--xcode-version', parsed.xcode_version))
     81 
     82  process = subprocess.Popen(command,
     83                             stdout=subprocess.PIPE,
     84                             stderr=subprocess.PIPE)
     85  (stdout, stderr) = process.communicate()
     86 
     87  if process.returncode:
     88    sys.stderr.write(stdout.decode('utf8'))
     89    sys.stderr.write(stderr.decode('utf8'))
     90    return process.returncode
     91 
     92  # Force failure if the tool extracted no data. This is because gn does
     93  # not support optional outputs and thus it would consider the build as
     94  # dirty if the output is missing.
     95  if not os.path.exists(metadata_dir):
     96    sys.stderr.write(f'error: no metadata generated for {module_name}\n')
     97    sys.stderr.write(stdout.decode('utf8'))
     98    sys.stderr.write(stderr.decode('utf8'))
     99    return 1  # failure
    100 
    101  output_files = METADATA_FILES
    102  with open(parsed.depfile, 'w', encoding='utf8') as depfile:
    103    for output in output_files:
    104      depfile.write(f'{metadata_dir}/{output}:')
    105      for item in sorted(inputs):
    106        depfile.write(f' {item}')
    107      depfile.write('\n')
    108 
    109  return 0  # success
    110 
    111 
    112 def main(args):
    113  parser = argparse.ArgumentParser()
    114 
    115  add_argument(parser, 'output', 'path to the output directory')
    116  add_argument(parser, 'depfile', 'path to the output depfile')
    117  add_argument(parser, 'toolchain-dir', 'path to the toolchain directory')
    118  add_argument(parser, 'sdk-root', 'path to the SDK root directory')
    119  add_argument(parser, 'target-cpu', 'target cpu architecture')
    120  add_argument(parser, 'target-environment', 'target environment')
    121  add_argument(parser, 'deployment-target', 'deployment target version')
    122  add_argument(parser, 'binary-file', 'path to the binary to process')
    123  add_argument(parser, 'module-info-path', 'path to the module info JSON file')
    124  add_argument(parser, 'xcode-version', 'version of Xcode', required=False)
    125 
    126  parsed = parser.parse_args(args)
    127 
    128  module_info = read_json(parsed.module_info_path)
    129  return extract_metadata(
    130      parsed,  #
    131      module_info['module_name'],
    132      module_info['swift_files'],
    133      module_info['const_files'])
    134 
    135 
    136 if __name__ == '__main__':
    137  sys.exit(main(sys.argv[1:]))