Fix for x86_64 build fail
[platform/upstream/connectedhomeip.git] / build / config / linux / pkg-config.py
1 #!/usr/bin/env python
2 # Copyright (c) 2013 The Chromium Authors. All rights reserved.
3 # Copyright (c) 2020 Project CHIP Authors
4 #
5 # Redistribution and use in source and binary forms, with or without
6 # modification, are permitted provided that the following conditions are
7 # met:
8 #
9 #    * Redistributions of source code must retain the above copyright
10 # notice, this list of conditions and the following disclaimer.
11 #    * Redistributions in binary form must reproduce the above
12 # copyright notice, this list of conditions and the following disclaimer
13 # in the documentation and/or other materials provided with the
14 # distribution.
15 #    * Neither the name of Google Inc. nor the names of its
16 # contributors may be used to endorse or promote products derived from
17 # this software without specific prior written permission.
18 #
19 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
31 from __future__ import print_function
32
33 import json
34 import os
35 import subprocess
36 import sys
37 import re
38 from optparse import OptionParser
39
40 # This script runs pkg-config, optionally filtering out some results, and
41 # returns the result.
42 #
43 # The result will be [ <includes>, <cflags>, <libs>, <lib_dirs>, <ldflags> ]
44 # where each member is itself a list of strings.
45 #
46 # You can filter out matches using "-v <regexp>" where all results from
47 # pkgconfig matching the given regular expression will be ignored. You can
48 # specify more than one regular expression my specifying "-v" more than once.
49 #
50 # You can specify a sysroot using "-s <sysroot>" where sysroot is the absolute
51 # system path to the sysroot used for compiling. This script will attempt to
52 # generate correct paths for the sysroot.
53 #
54 # When using a sysroot, you must also specify the architecture via
55 # "-a <arch>" where arch is either "x86" or "x64".
56 #
57 # CrOS systemroots place pkgconfig files at <systemroot>/usr/share/pkgconfig
58 # and one of <systemroot>/usr/lib/pkgconfig or <systemroot>/usr/lib64/pkgconfig
59 # depending on whether the systemroot is for a 32 or 64 bit architecture. They
60 # specify the 'lib' or 'lib64' of the pkgconfig path by defining the
61 # 'system_libdir' variable in the args.gn file. pkg_config.gni communicates this
62 # variable to this script with the "--system_libdir <system_libdir>" flag. If no
63 # flag is provided, then pkgconfig files are assumed to come from
64 # <systemroot>/usr/lib/pkgconfig.
65 #
66 # Additionally, you can specify the option --atleast-version. This will skip
67 # the normal outputting of a dictionary and instead print true or false,
68 # depending on the return value of pkg-config for the given package.
69
70
71 def SetConfigPath(options):
72   """Set the PKG_CONFIG_LIBDIR environment variable.
73
74   This takes into account any sysroot and architecture specification from the
75   options on the given command line.
76   """
77
78   sysroot = options.sysroot
79   assert sysroot
80
81   # Compute the library path name based on the architecture.
82   arch = options.arch
83   if sysroot and not arch:
84     print("You must specify an architecture via -a if using a sysroot.")
85     sys.exit(1)
86
87   libdir = sysroot + '/usr/' + options.system_libdir + '/pkgconfig'
88   libdir += ':' + sysroot + '/usr/share/pkgconfig'
89   os.environ['PKG_CONFIG_LIBDIR'] = libdir
90   return libdir
91
92
93 def GetPkgConfigPrefixToStrip(options, args):
94   """Returns the prefix from pkg-config where packages are installed.
95
96   This returned prefix is the one that should be stripped from the beginning of
97   directory names to take into account sysroots.
98   """
99   # Some sysroots, like the Chromium OS ones, may generate paths that are not
100   # relative to the sysroot. For example,
101   # /path/to/chroot/build/x86-generic/usr/lib/pkgconfig/pkg.pc may have all
102   # paths relative to /path/to/chroot (i.e. prefix=/build/x86-generic/usr)
103   # instead of relative to /path/to/chroot/build/x86-generic (i.e prefix=/usr).
104   # To support this correctly, it's necessary to extract the prefix to strip
105   # from pkg-config's |prefix| variable.
106   prefix = subprocess.check_output([options.pkg_config,
107       "--variable=prefix"] + args, env=os.environ).decode('utf-8')
108   if prefix[-4] == '/usr':
109     return prefix[4:]
110   return prefix
111
112
113 def MatchesAnyRegexp(flag, list_of_regexps):
114   """Returns true if the first argument matches any regular expression in the
115   given list."""
116   for regexp in list_of_regexps:
117     if regexp.search(flag) != None:
118       return True
119   return False
120
121
122 def RewritePath(path, strip_prefix, sysroot):
123   """Rewrites a path by stripping the prefix and prepending the sysroot."""
124   if os.path.isabs(path) and not path.startswith(sysroot):
125     if path.startswith(strip_prefix):
126       path = path[len(strip_prefix):]
127     path = path.lstrip('/')
128     return os.path.join(sysroot, path)
129   else:
130     return path
131
132
133 def main():
134   parser = OptionParser()
135   parser.add_option('-d', '--debug', action='store_true')
136   parser.add_option('-p', action='store', dest='pkg_config', type='string',
137                     default='pkg-config')
138   parser.add_option('-v', action='append', dest='strip_out', type='string')
139   parser.add_option('-s', action='store', dest='sysroot', type='string')
140   parser.add_option('-a', action='store', dest='arch', type='string')
141   parser.add_option('--system_libdir', action='store', dest='system_libdir',
142                     type='string', default='lib')
143   parser.add_option('--atleast-version', action='store',
144                     dest='atleast_version', type='string')
145   parser.add_option('--libdir', action='store_true', dest='libdir')
146   parser.add_option('--dridriverdir', action='store_true', dest='dridriverdir')
147   parser.add_option('--version-as-components', action='store_true',
148                     dest='version_as_components')
149   (options, args) = parser.parse_args()
150
151   # Make a list of regular expressions to strip out.
152   strip_out = []
153   if options.strip_out != None:
154     for regexp in options.strip_out:
155       strip_out.append(re.compile(regexp))
156
157   if options.sysroot:
158     libdir = SetConfigPath(options)
159     if options.debug:
160       sys.stderr.write('PKG_CONFIG_LIBDIR=%s\n' % libdir)
161     prefix = GetPkgConfigPrefixToStrip(options, args)
162   else:
163     prefix = ''
164
165   if options.atleast_version:
166     # When asking for the return value, just run pkg-config and print the return
167     # value, no need to do other work.
168     if not subprocess.call([options.pkg_config,
169                             "--atleast-version=" + options.atleast_version] +
170                             args):
171       print("true")
172     else:
173       print("false")
174     return 0
175
176   if options.version_as_components:
177     cmd = [options.pkg_config, "--modversion"] + args
178     try:
179       version_string = subprocess.check_output(cmd).decode('utf-8')
180     except Exception:
181       sys.stderr.write('Error from pkg-config.\n')
182       return 1
183     print(json.dumps(list(map(int, version_string.strip().split(".")))))
184     return 0
185
186
187   if options.libdir:
188     cmd = [options.pkg_config, "--variable=libdir"] + args
189     if options.debug:
190       sys.stderr.write('Running: %s\n' % cmd)
191     try:
192       libdir = subprocess.check_output(cmd).decode('utf-8')
193     except Exception:
194       print("Error from pkg-config.")
195       return 1
196     sys.stdout.write(libdir.strip())
197     return 0
198
199   if options.dridriverdir:
200     cmd = [options.pkg_config, "--variable=dridriverdir"] + args
201     if options.debug:
202       sys.stderr.write('Running: %s\n' % cmd)
203     try:
204       dridriverdir = subprocess.check_output(cmd).decode('utf-8')
205     except Exception:
206       print("Error from pkg-config.")
207       return 1
208     sys.stdout.write(dridriverdir.strip())
209     return
210
211   cmd = [options.pkg_config, "--cflags", "--libs"] + args
212   if options.debug:
213     sys.stderr.write('Running: %s\n' % ' '.join(cmd))
214
215   try:
216     flag_string = subprocess.check_output(cmd).decode('utf-8')
217   except Exception:
218     sys.stderr.write('Could not run pkg-config.\n')
219     return 1
220
221   # For now just split on spaces to get the args out. This will break if
222   # pkgconfig returns quoted things with spaces in them, but that doesn't seem
223   # to happen in practice.
224   all_flags = flag_string.strip().split(' ')
225
226
227   sysroot = options.sysroot
228   if not sysroot:
229     sysroot = ''
230
231   includes = []
232   cflags = []
233   libs = []
234   lib_dirs = []
235
236   for flag in all_flags[:]:
237     if len(flag) == 0 or MatchesAnyRegexp(flag, strip_out):
238       continue;
239
240     if flag[:2] == '-l':
241       libs.append(RewritePath(flag[2:], prefix, sysroot))
242     elif flag[:2] == '-L':
243       lib_dirs.append(RewritePath(flag[2:], prefix, sysroot))
244     elif flag[:2] == '-I':
245       includes.append(RewritePath(flag[2:], prefix, sysroot))
246     elif flag[:3] == '-Wl':
247       # Don't allow libraries to control ld flags.  These should be specified
248       # only in build files.
249       pass
250     elif flag == '-pthread':
251       # Many libs specify "-pthread" which we don't need since we always include
252       # this anyway. Removing it here prevents a bunch of duplicate inclusions
253       # on the command line.
254       pass
255     else:
256       cflags.append(flag)
257
258   # Output a GN array, the first one is the cflags, the second are the libs. The
259   # JSON formatter prints GN compatible lists when everything is a list of
260   # strings.
261   print(json.dumps([includes, cflags, libs, lib_dirs]))
262   return 0
263
264
265 if __name__ == '__main__':
266   sys.exit(main())