1#!/usr/bin/env python 2# Copyright (c) 2013 The Chromium Authors. All rights reserved. 3# Use of this source code is governed by a BSD-style license that can be 4# found in the LICENSE file. 5 6import json 7import os 8import subprocess 9import sys 10import re 11from optparse import OptionParser 12 13# This script runs pkg-config, optionally filtering out some results, and 14# returns the result. 15# 16# The result will be [ <includes>, <cflags>, <libs>, <lib_dirs>, <ldflags> ] 17# where each member is itself a list of strings. 18# 19# You can filter out matches using "-v <regexp>" where all results from 20# pkgconfig matching the given regular expression will be ignored. You can 21# specify more than one regular expression my specifying "-v" more than once. 22# 23# You can specify a sysroot using "-s <sysroot>" where sysroot is the absolute 24# system path to the sysroot used for compiling. This script will attempt to 25# generate correct paths for the sysroot. 26# 27# When using a sysroot, you must also specify the architecture via 28# "-a <arch>" where arch is either "x86" or "x64". 29# 30# CrOS systemroots place pkgconfig files at <systemroot>/usr/share/pkgconfig 31# and one of <systemroot>/usr/lib/pkgconfig or <systemroot>/usr/lib64/pkgconfig 32# depending on whether the systemroot is for a 32 or 64 bit architecture. They 33# specify the 'lib' or 'lib64' of the pkgconfig path by defining the 34# 'system_libdir' variable in the args.gn file. pkg_config.gni communicates this 35# variable to this script with the "--system_libdir <system_libdir>" flag. If no 36# flag is provided, then pkgconfig files are assumed to come from 37# <systemroot>/usr/lib/pkgconfig. 38# 39# Additionally, you can specify the option --atleast-version. This will skip 40# the normal outputting of a dictionary and instead print true or false, 41# depending on the return value of pkg-config for the given package. 42 43 44def SetConfigPath(options): 45 """Set the PKG_CONFIG_LIBDIR environment variable. 46 47 This takes into account any sysroot and architecture specification from the 48 options on the given command line. 49 """ 50 51 sysroot = options.sysroot 52 assert sysroot 53 54 # Compute the library path name based on the architecture. 55 arch = options.arch 56 if sysroot and not arch: 57 print "You must specify an architecture via -a if using a sysroot." 58 sys.exit(1) 59 60 libdir = sysroot + '/usr/' + options.system_libdir + '/pkgconfig' 61 libdir += ':' + sysroot + '/usr/share/pkgconfig' 62 os.environ['PKG_CONFIG_LIBDIR'] = libdir 63 return libdir 64 65 66def GetPkgConfigPrefixToStrip(args): 67 """Returns the prefix from pkg-config where packages are installed. 68 69 This returned prefix is the one that should be stripped from the beginning of 70 directory names to take into account sysroots. 71 """ 72 # Some sysroots, like the Chromium OS ones, may generate paths that are not 73 # relative to the sysroot. For example, 74 # /path/to/chroot/build/x86-generic/usr/lib/pkgconfig/pkg.pc may have all 75 # paths relative to /path/to/chroot (i.e. prefix=/build/x86-generic/usr) 76 # instead of relative to /path/to/chroot/build/x86-generic (i.e prefix=/usr). 77 # To support this correctly, it's necessary to extract the prefix to strip 78 # from pkg-config's |prefix| variable. 79 prefix = subprocess.check_output(["pkg-config", "--variable=prefix"] + args, 80 env=os.environ) 81 if prefix[-4] == '/usr': 82 return prefix[4:] 83 return prefix 84 85 86def MatchesAnyRegexp(flag, list_of_regexps): 87 """Returns true if the first argument matches any regular expression in the 88 given list.""" 89 for regexp in list_of_regexps: 90 if regexp.search(flag) != None: 91 return True 92 return False 93 94 95def RewritePath(path, strip_prefix, sysroot): 96 """Rewrites a path by stripping the prefix and prepending the sysroot.""" 97 if os.path.isabs(path) and not path.startswith(sysroot): 98 if path.startswith(strip_prefix): 99 path = path[len(strip_prefix):] 100 path = path.lstrip('/') 101 return os.path.join(sysroot, path) 102 else: 103 return path 104 105 106def main(): 107 # If this is run on non-Linux platforms, just return nothing and indicate 108 # success. This allows us to "kind of emulate" a Linux build from other 109 # platforms. 110 if "linux" not in sys.platform: 111 print "[[],[],[],[],[]]" 112 return 0 113 114 parser = OptionParser() 115 parser.add_option('-d', '--debug', action='store_true') 116 parser.add_option('-p', action='store', dest='pkg_config', type='string', 117 default='pkg-config') 118 parser.add_option('-v', action='append', dest='strip_out', type='string') 119 parser.add_option('-s', action='store', dest='sysroot', type='string') 120 parser.add_option('-a', action='store', dest='arch', type='string') 121 parser.add_option('--system_libdir', action='store', dest='system_libdir', 122 type='string', default='lib') 123 parser.add_option('--atleast-version', action='store', 124 dest='atleast_version', type='string') 125 parser.add_option('--libdir', action='store_true', dest='libdir') 126 (options, args) = parser.parse_args() 127 128 # Make a list of regular expressions to strip out. 129 strip_out = [] 130 if options.strip_out != None: 131 for regexp in options.strip_out: 132 strip_out.append(re.compile(regexp)) 133 134 if options.sysroot: 135 libdir = SetConfigPath(options) 136 if options.debug: 137 sys.stderr.write('PKG_CONFIG_LIBDIR=%s\n' % libdir) 138 prefix = GetPkgConfigPrefixToStrip(args) 139 else: 140 prefix = '' 141 142 if options.atleast_version: 143 # When asking for the return value, just run pkg-config and print the return 144 # value, no need to do other work. 145 if not subprocess.call([options.pkg_config, 146 "--atleast-version=" + options.atleast_version] + 147 args): 148 print "true" 149 else: 150 print "false" 151 return 0 152 153 if options.libdir: 154 cmd = [options.pkg_config, "--variable=libdir"] + args 155 if options.debug: 156 sys.stderr.write('Running: %s\n' % cmd) 157 try: 158 libdir = subprocess.check_output(cmd) 159 except: 160 print "Error from pkg-config." 161 return 1 162 sys.stdout.write(libdir.strip()) 163 return 0 164 165 cmd = [options.pkg_config, "--cflags", "--libs"] + args 166 if options.debug: 167 sys.stderr.write('Running: %s\n' % ' '.join(cmd)) 168 169 try: 170 flag_string = subprocess.check_output(cmd) 171 except: 172 sys.stderr.write('Could not run pkg-config.\n') 173 return 1 174 175 # For now just split on spaces to get the args out. This will break if 176 # pkgconfig returns quoted things with spaces in them, but that doesn't seem 177 # to happen in practice. 178 all_flags = flag_string.strip().split(' ') 179 180 181 sysroot = options.sysroot 182 if not sysroot: 183 sysroot = '' 184 185 includes = [] 186 cflags = [] 187 libs = [] 188 lib_dirs = [] 189 ldflags = [] 190 191 for flag in all_flags[:]: 192 if len(flag) == 0 or MatchesAnyRegexp(flag, strip_out): 193 continue; 194 195 if flag[:2] == '-l': 196 libs.append(RewritePath(flag[2:], prefix, sysroot)) 197 elif flag[:2] == '-L': 198 lib_dirs.append(RewritePath(flag[2:], prefix, sysroot)) 199 elif flag[:2] == '-I': 200 includes.append(RewritePath(flag[2:], prefix, sysroot)) 201 elif flag[:3] == '-Wl': 202 ldflags.append(flag) 203 elif flag == '-pthread': 204 # Many libs specify "-pthread" which we don't need since we always include 205 # this anyway. Removing it here prevents a bunch of duplicate inclusions 206 # on the command line. 207 pass 208 else: 209 cflags.append(flag) 210 211 # Output a GN array, the first one is the cflags, the second are the libs. The 212 # JSON formatter prints GN compatible lists when everything is a list of 213 # strings. 214 print json.dumps([includes, cflags, libs, lib_dirs, ldflags]) 215 return 0 216 217 218if __name__ == '__main__': 219 sys.exit(main()) 220