summaryrefslogtreecommitdiff
path: root/chromium/build/config/fuchsia/build_manifest.py
blob: 15f9cc1c47dc8724485ab299f730740826543c42 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
# Copyright 2018 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

"""Creates a archive manifest used for Fuchsia package generation.

Arguments:
  root_dir: The absolute path to the Chromium source tree root.

  out_dir: The absolute path to the Chromium build directory.

  app_name: The filename of the package's executable target.

  runtime_deps: The path to the GN runtime deps file.

  output_path: The path of the manifest file which will be written.
"""

import json
import os
import sys
import tempfile


# Path to file describing the services to be made available to the process.
SANDBOX_POLICY_PATH = 'build/config/fuchsia/sandbox_policy'


def MakePackagePath(file_path, roots):
  """Computes a path for |file_path| that is relative to one of the directory
  paths in |roots|.

  file_path: The absolute file path to relativize.
  roots: A list of absolute directory paths which may serve as a relative root
         for |file_path|. At least one path must contain |file_path|.
         Overlapping roots are permitted; the deepest matching root will be
         chosen.

  Examples:

  >>> MakePackagePath('/foo/bar.txt', ['/foo/'])
  'bar.txt'

  >>> MakePackagePath('/foo/dir/bar.txt', ['/foo/'])
  'dir/bar.txt'

  >>> MakePackagePath('/foo/out/Debug/bar.exe', ['/foo/', '/foo/out/Debug/'])
  'bar.exe'
  """

  # Prevents greedily matching against a shallow path when a deeper, better
  # matching path exists.
  roots.sort(key=len, reverse=True)

  for next_root in roots:
    if not next_root.endswith(os.sep):
      next_root += os.sep

    if file_path.startswith(next_root):
      relative_path = file_path[len(next_root):]

      # TODO(fuchsia): The requirements for finding/loading .so are in flux, so
      # this ought to be reconsidered at some point.
      # See https://crbug.com/732897.
      if file_path.endswith('.so'):
        relative_path = 'lib/' + os.path.basename(relative_path)

      return relative_path

  raise Exception('Error: no matching root paths found for \'%s\'.' % file_path)


def _GetStrippedPath(bin_path):
  """Finds the stripped version of the binary |bin_path| in the build
  output directory."""

  if not '.unstripped' in bin_path:
    raise Exception('File "%s" is not in an .unstripped directory.' % bin_path)

  return os.path.normpath(os.path.join(bin_path,
                                       os.path.pardir,
                                       os.path.pardir,
                                       os.path.basename(bin_path)))


def _IsBinary(path):
  """Checks if the file at |path| is an ELF executable by inspecting its FourCC
  header."""

  with open(path, 'rb') as f:
    file_tag = f.read(4)
  return file_tag == '\x7fELF'


def BuildManifest(root_dir, out_dir, app_name, runtime_deps_file, output_path):
  with open(output_path, 'w') as output:
    # Process the runtime deps file for file paths, recursively walking
    # directories as needed.
    # runtime_deps may contain duplicate paths, so use a set for
    # de-duplication.
    expanded_files = set()
    for next_path in open(runtime_deps_file, 'r'):
      next_path = next_path.strip()
      if os.path.isdir(next_path):
        for root, _, files in os.walk(next_path):
          for next_file in files:
            if next_file.startswith('.'):
              continue
            expanded_files.add(os.path.abspath(os.path.join(root, next_file)))
      else:
        expanded_files.add(os.path.abspath(next_path))

    # Format and write out the manifest contents.
    app_found = False
    for next_file in expanded_files:
      if _IsBinary(next_file):
        next_file = _GetStrippedPath(next_file)

      in_package_path = MakePackagePath(os.path.join(out_dir, next_file),
                                        [root_dir, out_dir])
      if in_package_path == app_name:
        in_package_path = 'bin/app'
        app_found = True

      # The source path is relativized so that it can be used on multiple
      # environments with differing parent directory structures,
      # e.g. builder bots and swarming clients.
      output.write('%s=%s\n' % (in_package_path,
                                os.path.relpath(next_file, out_dir)))
    if not app_found:
      raise Exception('Could not locate executable inside runtime_deps.')
    output.write('meta/sandbox=' +
                 os.path.relpath(os.path.join(root_dir, SANDBOX_POLICY_PATH),
                                 out_dir))

  return 0


if __name__ == '__main__':
  sys.exit(BuildManifest(sys.argv[1], sys.argv[2], sys.argv[3], sys.argv[4],
                         sys.argv[5]))