blob: bd25c0c424481192b0f4f020ae46ded0c1ad8ca6 [file] [log] [blame]
#!/usr/bin/env python
# Copyright 2014 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.
"""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:
$ out/Release-iphoneos \ \
import argparse
import os
import re
import subprocess
import sys
class SubprocessError(Exception):
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
Example query result:
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
... many more inputs ...
| 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 ...
query_result: output from 'ninja -t query'
prefix: optional file system path to prefix to returned inputs
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.
target: ninja target name to query for
workdir: workdir for ninja
prefix: optional file system path to prefix to returned inputs
A list of file system paths to the inputs to the named target.
proc = subprocess.Popen(['ninja', '-C', workdir, '-t', 'query', target],
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
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 of library dependencies.
deps = set()
for target in targets:
if is_library(target):
deps.add(os.path.join(workdir, target))
deps = deps.union(query(target, workdir, workdir))
return deps
def link(output, inputs):
"""Links output from inputs using libtool.
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):
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([0], args.workdir[0])
link(args.output[0], list(library_deps(inputs, args.workdir[0])))
if __name__ == '__main__':