• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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