1#!/usr/bin/env python 2# -*- coding: utf-8 -*- 3# Copyright (c) 2013 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 7import json 8import os 9import subprocess 10import sys 11import re 12from optparse import OptionParser 13 14# This script runs pkg-config, optionally filtering out some results, and 15# returns the result. 16# 17# The result will be [ <includes>, <cflags>, <libs>, <lib_dirs>, <ldflags> ] 18# where each member is itself a list of strings. 19# 20# You can filter out matches using "-v <regexp>" where all results from 21# pkgconfig matching the given regular expression will be ignored. You can 22# specify more than one regular expression my specifying "-v" more than once. 23# 24# You can specify a sysroot using "-s <sysroot>" where sysroot is the absolute 25# system path to the sysroot used for compiling. This script will attempt to 26# generate correct paths for the sysroot. 27# 28# When using a sysroot, you must also specify the architecture via 29# "-a <arch>" where arch is either "x86" or "x64". 30# cross 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 35# this variable to this script with the "--system_libdir <system_libdir>" flag. 36# If no 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(options, 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([options.pkg_config, 80 "--variable=prefix"] + args, 81 env=os.environ) 82 if prefix[-4] == '/usr': 83 return prefix[4:] 84 return prefix 85 86 87def MatchesAnyRegexp(flag, list_of_regexps): 88 """Returns true if the first argument matches any regular expression in the 89 given list.""" 90 for regexp in list_of_regexps: 91 if regexp.search(flag) is not None: 92 return True 93 return False 94 95 96def RewritePath(path, strip_prefix, sysroot): 97 """Rewrites a path by stripping the prefix and prepending the sysroot.""" 98 if os.path.isabs(path) and not path.startswith(sysroot): 99 if path.startswith(strip_prefix): 100 path = path[len(strip_prefix):] 101 path = path.lstrip('/') 102 return os.path.join(sysroot, path) 103 else: 104 return path 105 106 107def main(): 108 # If this is run on non-Linux platforms, just return nothing and indicate 109 # success. This allows us to "kind of emulate" a Linux build from other 110 # platforms. 111 if "linux" not in sys.platform: 112 print("[[],[],[],[],[]]") 113 return 0 114 115 parser = OptionParser() 116 parser.add_option('-d', '--debug', action='store_true') 117 parser.add_option('-p', action='store', dest='pkg_config', type='string', 118 default='pkg-config') 119 parser.add_option('-v', action='append', dest='strip_out', type='string') 120 parser.add_option('-s', action='store', dest='sysroot', type='string') 121 parser.add_option('-a', action='store', dest='arch', type='string') 122 parser.add_option('--system_libdir', action='store', dest='system_libdir', 123 type='string', default='lib') 124 parser.add_option('--atleast-version', action='store', 125 dest='atleast_version', type='string') 126 parser.add_option('--libdir', action='store_true', dest='libdir') 127 parser.add_option('--dridriverdir', action='store_true', dest='dridriverdir') 128 parser.add_option('--version-as-components', action='store_true', 129 dest='version_as_components') 130 (options, args) = parser.parse_args() 131 132 # Make a list of regular expressions to strip out. 133 strip_out = [] 134 if options.strip_out is not None: 135 for regexp in options.strip_out: 136 strip_out.append(re.compile(regexp)) 137 138 if options.sysroot: 139 libdir = SetConfigPath(options) 140 if options.debug: 141 sys.stderr.write('PKG_CONFIG_LIBDIR=%s\n' % libdir) 142 prefix = GetPkgConfigPrefixToStrip(options, args) 143 else: 144 prefix = '' 145 146 if options.atleast_version: 147 # When asking for the return value, just run pkg-config and print the 148 # return value, no need to do other work. 149 if not subprocess.call([options.pkg_config, 150 "--atleast-version=" + options.atleast_version] + 151 args): 152 print("true") 153 else: 154 print("false") 155 return 0 156 157 if options.version_as_components: 158 cmd = [options.pkg_config, "--modversion"] + args 159 try: 160 version_string = subprocess.check_output(cmd) 161 except: 162 sys.stderr.write('Error from pkg-config.\n') 163 return 1 164 print(json.dumps(list(map(int, version_string.strip().split("."))))) 165 return 0 166 167 if options.libdir: 168 cmd = [options.pkg_config, "--variable=libdir"] + args 169 if options.debug: 170 sys.stderr.write('Running: %s\n' % cmd) 171 try: 172 libdir = subprocess.check_output(cmd) 173 except: 174 print("Error from pkg-config.") 175 return 1 176 sys.stdout.write(libdir.strip()) 177 return 0 178 179 if options.dridriverdir: 180 cmd = [options.pkg_config, "--variable=dridriverdir"] + args 181 if options.debug: 182 sys.stderr.write('Running: %s\n' % cmd) 183 try: 184 dridriverdir = subprocess.check_output(cmd) 185 except: 186 print("Error from pkg-config.") 187 return 1 188 sys.stdout.write(dridriverdir.strip()) 189 return 190 191 cmd = [options.pkg_config, "--cflags", "--libs"] + args 192 if options.debug: 193 sys.stderr.write('Running: %s\n' % ' '.join(cmd)) 194 195 try: 196 flag_string = subprocess.check_output(cmd) 197 except: 198 sys.stderr.write('Could not run pkg-config.\n') 199 return 1 200 201 # For now just split on spaces to get the args out. This will break if 202 # pkgconfig returns quoted things with spaces in them, but that doesn't seem 203 # to happen in practice. 204 all_flags = flag_string.strip().split(' ') 205 206 sysroot = options.sysroot 207 if not sysroot: 208 sysroot = '' 209 210 includes = [] 211 cflags = [] 212 libs = [] 213 lib_dirs = [] 214 215 for flag in all_flags[:]: 216 if len(flag) == 0 or MatchesAnyRegexp(flag, strip_out): 217 continue 218 219 if flag[:2] == '-l': 220 libs.append(RewritePath(flag[2:], prefix, sysroot)) 221 elif flag[:2] == '-L': 222 lib_dirs.append(RewritePath(flag[2:], prefix, sysroot)) 223 elif flag[:2] == '-I': 224 includes.append(RewritePath(flag[2:], prefix, sysroot)) 225 elif flag[:3] == '-Wl': 226 # Don't allow libraries to control ld flags. These should be specified 227 # only in build files. 228 pass 229 elif flag == '-pthread': 230 # Many libs specify "-pthread" which we don't need since we always 231 # include this anyway. Removing it here prevents a bunch of duplicate 232 # inclusions on the command line. 233 pass 234 else: 235 cflags.append(flag) 236 237 # Output a GN array, the first one is the cflags, the second are the libs. 238 # The JSON formatter prints GN compatible lists when everything is a list of 239 # strings. 240 print(json.dumps([includes, cflags, libs, lib_dirs])) 241 return 0 242 243 244if __name__ == '__main__': 245 sys.exit(main()) 246