Home | History | Annotate | Download | only in linux
      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 
      6 import json
      7 import os
      8 import subprocess
      9 import sys
     10 import re
     11 from 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 
     44 def 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 
     66 def 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 
     86 def 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 
     95 def 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 
    106 def 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 
    218 if __name__ == '__main__':
    219   sys.exit(main())
    220