File: link_dependencies.py

package info (click to toggle)
chromium 139.0.7258.127-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 6,122,156 kB
  • sloc: cpp: 35,100,771; ansic: 7,163,530; javascript: 4,103,002; python: 1,436,920; asm: 946,517; xml: 746,709; pascal: 187,653; perl: 88,691; sh: 88,436; objc: 79,953; sql: 51,488; cs: 44,583; fortran: 24,137; makefile: 22,147; tcl: 15,277; php: 13,980; yacc: 8,984; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (157 lines) | stat: -rwxr-xr-x 5,108 bytes parent folder | download | duplicates (7)
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
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
#!/usr/bin/env python
#
# Copyright 2014 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

"""Links the deps of a binary into a static library.

Run with a working directory, the name of a binary target, and the name of the
static library that should be produced. For example:

  $ link_dependencies.py out/Release-iphoneos \
                         crnet_consumer.app/crnet_consumer \
                         out/Release-iphoneos/crnet_standalone.a
"""

import argparse
import os
import re
import subprocess
import sys


class SubprocessError(Exception):
  pass


def extract_inputs(query_result, prefix=''):
  """Extracts inputs from ninja query output.

  Given 'ninja -t query' output for a target, extracts all the inputs of that
  target, prefixing them with an optional prefix. Inputs prefixed with '|' are
  implicit, so we discard them as they shouldn't be linked into the resulting
  binary (these are things like the .ninja files themselves, dep lists, and so
  on).

  Example query result:
    arch/crnet_consumer.armv7:
      input: link
        obj/[long path...]/crnet_consumer.crnet_consumer_app_delegate.armv7.o
        obj/[long path...]/crnet_consumer.crnet_consumer_view_controller.armv7.o
        obj/[long path...]/crnet_consumer.main.armv7.o
        libcrnet.a
        libdata_reduction_proxy_code_browser.a
        ... many more inputs ...
        liburl_util.a
        | obj/content/content.actions_depends.stamp
        | gen/components/data_reduction_proxy/common/version.h
        | obj/ui/resources/ui_resources.actions_rules_copies.stamp
        ... more implicit inputs ...
    outputs:
      crnet_consumer.app/crnet_consumer

  Args:
    query_result: output from 'ninja -t query'
    prefix: optional file system path to prefix to returned inputs

  Returns:
    A list of the inputs.
  """
  extracting = False
  inputs = []
  for line in query_result.splitlines():
    if line.startswith('  input:'):
      extracting = True
    elif line.startswith('  outputs:'):
      extracting = False
    elif extracting and '|' not in line:
      inputs.append(os.path.join(prefix, line.strip()))
  return inputs


def query_ninja(target, workdir, prefix=''):
  """Returns the inputs for the named target.

  Queries ninja for the set of inputs of the named target, then returns the list
  of inputs to that target.

  Args:
    target: ninja target name to query for
    workdir: workdir for ninja
    prefix: optional file system path to prefix to returned inputs

  Returns:
    A list of file system paths to the inputs to the named target.
  """
  proc = subprocess.Popen(['ninja', '-C', workdir, '-t', 'query', target],
                          stdout=subprocess.PIPE)
  stdout, _ = proc.communicate()
  return extract_inputs(stdout, prefix)


def is_library(target):
  """Returns whether target is a library file."""
  return os.path.splitext(target)[1] in ('.a', '.o')


def library_deps(targets, workdir, query=query_ninja):
  """Returns the set of library dependencies for the supplied targets.

  The entries in the targets list can be either a static library, an object
  file, or an executable. Static libraries and object files are incorporated
  directly; executables are treated as being thin executable inputs to a fat
  executable link step, and have their own library dependencies added in their
  place.

  Args:
    targets: list of targets to include library dependencies from
    workdir: working directory to run ninja queries in
    query: function taking target, workdir, and prefix and returning an input
           set
  Returns:
    Set of library dependencies.
  """
  deps = set()
  for target in targets:
    if is_library(target):
      deps.add(os.path.join(workdir, target))
    else:
      deps = deps.union(query(target, workdir, workdir))
  return deps


def link(output, inputs):
  """Links output from inputs using libtool.

  Args:
    output: file system path to desired output library
    inputs: list of file system paths to input libraries
  """
  libtool_re = re.compile(r'^.*libtool: (?:for architecture: \S* )?'
                          r'file: .* has no symbols$')
  p = subprocess.Popen(
      ['libtool', '-o', output] + inputs, stderr=subprocess.PIPE)
  _, err = p.communicate()
  for line in err.splitlines():
    if not libtool_re.match(line):
      sys.stderr.write(line)
  if p.returncode != 0:
    message = "subprocess libtool returned {0}".format(p.returncode)
    raise SubprocessError(message)


def main():
  parser = argparse.ArgumentParser(
      description='Link dependencies of a ninja target into a static library')
  parser.add_argument('workdir', nargs=1, help='ninja working directory')
  parser.add_argument('target', nargs=1, help='target to query for deps')
  parser.add_argument('output', nargs=1, help='path to output static library')
  args = parser.parse_args()

  inputs = query_ninja(args.target[0], args.workdir[0])
  link(args.output[0], list(library_deps(inputs, args.workdir[0])))


if __name__ == '__main__':
  main()