2
# Various functionality for handling pkg-config in a scons based build.
4
# The "public interface" here is the 'generate' method and the 'PkgConfig'
5
# class. The 'generate' method can be used to generate a pkg-config file
6
# based on a template. Within pycc this is used to generate a suiteable
7
# pkg-config file for the version of pycc currently build.
8
# The 'PkgConfig' class is used to read pkg-config files using the pkg-config
9
# command, and output the result suitable for inclusion in scons build
12
import os, os.path, re, sys
14
# imports from the global 'SCons'
15
from SCons import Builder, Action
17
# import the local 'scons'
18
import simula_scons as scons
19
from simula_scons.Errors import CommandError, PkgconfigError, PkgconfigMissing, PkgconfigGeneratorsMissing, PkgconfigGeneratorMissing
21
def generate_pcFunc(replaceDict):
22
def _pcFunc(target, source, env):
23
""" Fill in .pc template. """
24
f = file(str(source[0]))
25
try: lines = f.readlines()
28
includeDir = env["includeDir"]
29
libDir = env["libDir"]
30
prefix = env["prefix"]
31
f = file(str(target[0]), "w")
34
# if env["CXX"] is set (are we sure we always use C++ - probably not...)
35
if env.has_key("CXX") and env["CXX"] != None:
41
includeDir = includeDir.replace("$prefix", "${prefix}")
42
libDir = libDir.replace("$prefix", "${exec_prefix}")
43
header = ["prefix=%s\n" % repr(prefix)[1:-1],
44
"exec_prefix=${prefix}\n",
45
"includedir=%s\n" % repr(includeDir)[1:-1], \
46
"libdir=%s\n" % repr(libDir)[1:-1],
47
"compiler=%s\n" % compiler]
48
for i in range(len(lines)):
49
if not lines[i].strip():
53
# Might need to susbstitute things in the body, e.g. dependencies.
55
if replaceDict is not None:
56
for item in replaceDict:
57
# find line(s) in body that match the substitution key:
58
replLines = [ l for l in body if "@"+item+"@" in l ]
59
# replace the key with the replaceDict item, and feed into the body
60
# We support multiple occurences of a string, although that should be
63
body[body.index(l)] = l.replace("@"+item+"@", replaceDict[item])
65
f.writelines(header + body)
70
def _strFunc(target, source, env):
71
return "Building %s from %s" % (target[0], ", ".join([str(s) for s in source]))
74
def generate(env, replace=None):
75
"""Place a template-based generator for pkg-config files in the BUILDERS."""
76
# we might need to pass in things to replace in the pkg-config, we
77
# can use the replace as a dict for that. The keys will be things to
78
# replace in the template and the values will be the replacements.
80
# We need to access the replace-dict inside _pcFunc, hence we must turn the
81
# _pcFunc into a closure.
82
_pcFunc = generate_pcFunc(replace)
83
env["BUILDERS"]["PkgConfigGenerator"] = Builder.Builder(action={".in": Action.Action(_pcFunc, \
84
strfunction=_strFunc)}, suffix="")
87
def get_packgen(package):
88
# Try to import and run the right pkgconfig generator:
90
packgen = __import__("simula_scons.pkgconfiggenerators",globals(),locals())
92
raise PkgconfigGeneratorsMissing()
93
# Generate the pkgconfig file:
94
# There should probably be a second try/except around the import of the
95
# pkgconfig generator for the specific module.
98
exec "from simula_scons.pkgconfiggenerators import %s" % (package.split('-',1)[0]) in ns
100
raise PkgconfigGeneratorMissing(package)
101
packgen = ns.get("%s" % (package.split('-',1)[0]))
105
class PkgConfig(object):
106
"""Handling of pkg-config files.
108
Whenever pkg-config file must be read to handle a dependency, an
109
instance of this class will be created.
111
def __init__(self, package, env):
113
# If the PKG_CONFIG_PATH variable is empty, we are probably on
115
# I'll create a suiteable directory and set as PKG_CONFIG_PATH
118
# Set up a temporary place for pkgconfig files.
119
pkgconfdir = os.path.join(env.Dir("#scons").abspath,"pkgconfig")
120
# set this is SCONS_PKG_CONFIG_DIR, which should be the place the
121
# pkgconfiggenerators put pc-files during build
122
os.environ["SCONS_PKG_CONFIG_DIR"] = pkgconfdir
123
# Make sure that the directory exist:
124
if not os.path.isdir(pkgconfdir):
125
os.makedirs(pkgconfdir)
127
if not os.environ.has_key("PKG_CONFIG_PATH") or os.environ["PKG_CONFIG_PATH"] == "":
128
os.environ["PKG_CONFIG_PATH"]=pkgconfdir
129
#print "\n** Warning: Added %s \n as PKG_CONFIG_PATH **" % (pkgconfdir)
130
elif os.environ.has_key("PKG_CONFIG_PATH") and pkgconfdir not in os.environ["PKG_CONFIG_PATH"]:
131
pkgConfPath = os.environ["PKG_CONFIG_PATH"].split(os.path.pathsep)
132
pkgConfPath.append(pkgconfdir)
133
os.environ["PKG_CONFIG_PATH"] = os.path.pathsep.join(pkgConfPath)
134
#print "\n** Warning: Added %s \n in PKG_CONFIG_PATH **" % (pkgconfdir)
136
self.package = package
139
scons.runCommand("pkg-config", ["--exists", self.package])
142
print "no (pkg-config file not found)"
143
print " Trying to generate pkg-config file for %s..." % (self.package),
145
# Construct pkgconfig-file
146
packgen = get_packgen(package)
148
# # Try to import and run the right pkgconfig generator:
150
# packgen = __import__("simula_scons.pkgconfiggenerators",globals(),locals())
152
# raise PkgconfigGeneratorsMissing()
153
# # Generate the pkgconfig file:
154
# # There should probably be a second try/except around the import of the
155
# # pkgconfig generator for the specific module.
157
# exec "from simula_scons.pkgconfiggenerators import %s" % (package.split('-',1)[0]) in ns
158
# packgen = ns.get("%s" % (package.split('-',1)[0]))
160
packgen.generatePkgConf(sconsEnv=env)
163
def _pkgconfig(self, param):
164
os.environ["PKG_CONFIG_ALLOW_SYSTEM_CFLAGS"] = "1"
165
#os.environ["PKG_CONFIG_ALLOW_SYSTEM_LIBS"] = "1"
166
try: out, err = scons.runCommand("pkg-config", [param, self.package])
167
except CommandError, err:
168
raise PkgconfigError(self.package, "Error reported by pkg-config: `%s'" % err.stderr)
172
"""Find out what version the package think it is"""
173
return self._pkgconfig("--modversion")
175
def includeDirs(self):
176
out = self._pkgconfig("--cflags-only-I")
177
# Note that pkgconfig will not have spaces between -I and its argument
178
return [i[2:] for i in out.split()]
181
out = self._pkgconfig("--libs-only-L")
189
return self.__extract_opts("--libs-only-other")
191
def compileOpts(self, filter=["-D"]):
192
opts = self.__extract_opts("--cflags-only-other")
197
filtered += [o for o in opts if o.startswith(f)]
200
def __extract_opts(self, name):
201
return [o.strip() for o in self._pkgconfig(name).split()]
204
# If the compiler, and maybe the compilertype variable is set, read and
205
# return as a tuple. If I can't figure out the compilertype, I use None
206
# to denote 'unknown'
207
compiler = self._pkgconfig("--variable=compiler")
213
def frameworks(self):
214
out = self._pkgconfig("--libs")
215
fw = re.findall(r"-framework (\S+)",out)
219
"""return a set of libraries for lib.
220
On Darwin (MacOSX) a tuple with libraries and frameworks is returned
222
out = self._pkgconfig("--libs-only-l")
227
if self.env["PLATFORM"] == "darwin":
228
return (libs, self.frameworks())
232
return self._pkgconfig("--cflags")
235
return self._pkgconfig("--libs")