OLD | NEW |
(Empty) | |
| 1 #!/usr/bin/env python |
| 2 # |
| 3 # Copyright 2014 The Chromium Authors. All rights reserved. |
| 4 # Use of this source code is governed by a BSD-style license that can be |
| 5 # found in the LICENSE file. |
| 6 |
| 7 """Links the deps of a binary into a static library. |
| 8 |
| 9 Run with a working directory, the name of a binary target, and the name of the |
| 10 static library that should be produced. For example: |
| 11 |
| 12 $ link_dependencies.py out/Release-iphoneos \ |
| 13 crnet_consumer.app/crnet_consumer \ |
| 14 out/Release-iphoneos/crnet_standalone.a |
| 15 """ |
| 16 |
| 17 import argparse |
| 18 import os |
| 19 import subprocess |
| 20 |
| 21 |
| 22 class SubprocessError(Exception): |
| 23 pass |
| 24 |
| 25 |
| 26 def extract_inputs(query_result, prefix=''): |
| 27 """Extracts inputs from ninja query output. |
| 28 |
| 29 Given 'ninja -t query' output for a target, extracts all the inputs of that |
| 30 target, prefixing them with an optional prefix. Inputs prefixed with '|' are |
| 31 implicit, so we discard them as they shouldn't be linked into the resulting |
| 32 binary (these are things like the .ninja files themselves, dep lists, and so |
| 33 on). |
| 34 |
| 35 Example query result: |
| 36 arch/crnet_consumer.armv7: |
| 37 input: link |
| 38 obj/[long path...]/crnet_consumer.crnet_consumer_app_delegate.armv7.o |
| 39 obj/[long path...]/crnet_consumer.crnet_consumer_view_controller.armv7.o |
| 40 obj/[long path...]/crnet_consumer.main.armv7.o |
| 41 libcrnet.a |
| 42 libdata_reduction_proxy_code_browser.a |
| 43 ... many more inputs ... |
| 44 liburl_util.a |
| 45 | obj/content/content.actions_depends.stamp |
| 46 | gen/components/data_reduction_proxy/common/version.h |
| 47 | obj/ui/resources/ui_resources.actions_rules_copies.stamp |
| 48 ... more implicit inputs ... |
| 49 outputs: |
| 50 crnet_consumer.app/crnet_consumer |
| 51 |
| 52 Args: |
| 53 query_result: output from 'ninja -t query' |
| 54 prefix: optional file system path to prefix to returned inputs |
| 55 |
| 56 Returns: |
| 57 A list of the inputs. |
| 58 """ |
| 59 extracting = False |
| 60 inputs = [] |
| 61 for line in query_result.splitlines(): |
| 62 if line.startswith(' input:'): |
| 63 extracting = True |
| 64 elif line.startswith(' outputs:'): |
| 65 extracting = False |
| 66 elif extracting and '|' not in line: |
| 67 inputs.append(os.path.join(prefix, line.strip())) |
| 68 return inputs |
| 69 |
| 70 |
| 71 def query_ninja(target, workdir, prefix=''): |
| 72 """Returns the inputs for the named target. |
| 73 |
| 74 Queries ninja for the set of inputs of the named target, then returns the list |
| 75 of inputs to that target. |
| 76 |
| 77 Args: |
| 78 target: ninja target name to query for |
| 79 workdir: workdir for ninja |
| 80 prefix: optional file system path to prefix to returned inputs |
| 81 |
| 82 Returns: |
| 83 A list of file system paths to the inputs to the named target. |
| 84 """ |
| 85 proc = subprocess.Popen(['ninja', '-C', workdir, '-t', 'query', target], |
| 86 stdout=subprocess.PIPE) |
| 87 stdout, _ = proc.communicate() |
| 88 return extract_inputs(stdout, prefix) |
| 89 |
| 90 |
| 91 def is_library(target): |
| 92 """Returns whether target is a library file.""" |
| 93 return os.path.splitext(target)[1] in ('.a', '.o') |
| 94 |
| 95 |
| 96 def library_deps(targets, workdir, query=query_ninja): |
| 97 """Returns the set of library dependencies for the supplied targets. |
| 98 |
| 99 The entries in the targets list can be either a static library, an object |
| 100 file, or an executable. Static libraries and object files are incorporated |
| 101 directly; executables are treated as being thin executable inputs to a fat |
| 102 executable link step, and have their own library dependencies added in their |
| 103 place. |
| 104 |
| 105 Args: |
| 106 targets: list of targets to include library dependencies from |
| 107 workdir: working directory to run ninja queries in |
| 108 query: function taking target, workdir, and prefix and returning an input |
| 109 set |
| 110 Returns: |
| 111 Set of library dependencies. |
| 112 """ |
| 113 deps = set() |
| 114 for target in targets: |
| 115 if is_library(target): |
| 116 deps.add(os.path.join(workdir, target)) |
| 117 else: |
| 118 deps = deps.union(query(target, workdir, workdir)) |
| 119 return deps |
| 120 |
| 121 |
| 122 def link(output, inputs): |
| 123 """Links output from inputs using libtool. |
| 124 |
| 125 Args: |
| 126 output: file system path to desired output library |
| 127 inputs: list of file system paths to input libraries |
| 128 """ |
| 129 p = subprocess.Popen(['libtool', '-o', output] + inputs) |
| 130 p.communicate() |
| 131 if p.returncode != 0: |
| 132 message = "subprocess libtool returned {0}".format(p.returncode) |
| 133 raise SubprocessError(message) |
| 134 |
| 135 |
| 136 def main(): |
| 137 parser = argparse.ArgumentParser( |
| 138 description='Link dependencies of a ninja target into a static library') |
| 139 parser.add_argument('workdir', nargs=1, help='ninja working directory') |
| 140 parser.add_argument('target', nargs=1, help='target to query for deps') |
| 141 parser.add_argument('output', nargs=1, help='path to output static library') |
| 142 args = parser.parse_args() |
| 143 |
| 144 inputs = query_ninja(args.target[0], args.workdir[0]) |
| 145 link(args.output[0], list(library_deps(inputs, args.workdir[0]))) |
| 146 |
| 147 |
| 148 if __name__ == '__main__': |
| 149 main() |
OLD | NEW |