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" % prefix, "exec_prefix=${prefix}\n", "includedir=%s\n" % includeDir, \
44
"libdir=%s\n" % libDir, "compiler=%s\n" % compiler]
45
for i in range(len(lines)):
46
if not lines[i].strip():
50
# Might need to susbstitute things in the body, e.g. dependencies.
52
if replaceDict is not None:
53
for item in replaceDict:
54
# find line(s) in body that match the substitution key:
55
replLines = [ l for l in body if "@"+item+"@" in l ]
56
# replace the key with the replaceDict item, and feed into the body
57
# We support multiple occurences of a string, although that should be
60
body[body.index(l)] = l.replace("@"+item+"@", replaceDict[item])
62
f.writelines(header + body)
67
def _strFunc(target, source, env):
68
return "Building %s from %s" % (target[0], ", ".join([str(s) for s in source]))
71
def generate(env, replace=None):
72
"""Place a template-based generator for pkg-config files in the BUILDERS."""
73
# we might need to pass in things to replace in the pkg-config, we
74
# can use the replace as a dict for that. The keys will be things to
75
# replace in the template and the values will be the replacements.
77
# We need to access the replace-dict inside _pcFunc, hence we must turn the
78
# _pcFunc into a closure.
79
_pcFunc = generate_pcFunc(replace)
80
env["BUILDERS"]["PkgConfigGenerator"] = Builder.Builder(action={".in": Action.Action(_pcFunc, \
81
strfunction=_strFunc)}, suffix="")
84
def get_packgen(package):
85
# Try to import and run the right pkgconfig generator:
87
packgen = __import__("simula_scons.pkgconfiggenerators",globals(),locals())
89
raise PkgconfigGeneratorsMissing()
90
# Generate the pkgconfig file:
91
# There should probably be a second try/except around the import of the
92
# pkgconfig generator for the specific module.
95
exec "from simula_scons.pkgconfiggenerators import %s" % (package.split('-',1)[0]) in ns
97
raise PkgconfigGeneratorMissing(package)
98
packgen = ns.get("%s" % (package.split('-',1)[0]))
102
class PkgConfig(object):
103
"""Handling of pkg-config files.
105
Whenever pkg-config file must be read to handle a dependency, an
106
instance of this class will be created.
108
def __init__(self, package, env):
110
# If the PKG_CONFIG_PATH variable is empty, we are probably on
112
# I'll create a suiteable directory and set as PKG_CONFIG_PATH
115
pkgconfdir = os.path.join(env.Dir("#scons").abspath,"pkgconfig")
116
if not os.environ.has_key("PKG_CONFIG_PATH") or os.environ["PKG_CONFIG_PATH"] == "":
118
if not os.path.isdir(pkgconfdir):
119
os.makedirs(pkgconfdir)
120
os.environ["PKG_CONFIG_PATH"]=pkgconfdir
121
print "\n** Warning: Added %s \n as PKG_CONFIG_PATH **" % (pkgconfdir)
123
os.environ["SCONS_PKG_CONFIG_DIR"] = pkgconfdir
125
self.package = package
128
scons.runCommand("pkg-config", ["--exists", self.package])
131
print "no (pkg-config file not found)"
132
print " Trying to generate pkg-config file for %s..." % (self.package),
134
# Construct pkgconfig-file
135
packgen = get_packgen(package)
137
# # Try to import and run the right pkgconfig generator:
139
# packgen = __import__("simula_scons.pkgconfiggenerators",globals(),locals())
141
# raise PkgconfigGeneratorsMissing()
142
# # Generate the pkgconfig file:
143
# # There should probably be a second try/except around the import of the
144
# # pkgconfig generator for the specific module.
146
# exec "from simula_scons.pkgconfiggenerators import %s" % (package.split('-',1)[0]) in ns
147
# packgen = ns.get("%s" % (package.split('-',1)[0]))
149
packgen.generatePkgConf(sconsEnv=env)
152
def _pkgconfig(self, param):
153
os.environ["PKG_CONFIG_ALLOW_SYSTEM_CFLAGS"] = "1"
154
os.environ["PKG_CONFIG_ALLOW_SYSTEM_LIBS"] = "1"
155
try: out, err = scons.runCommand("pkg-config", [param, self.package])
156
except CommandError, err:
157
raise PkgconfigError(self.package, "Error reported by pkg-config: `%s'" % err.stderr)
161
"""Find out what version the package think it is"""
162
return self._pkgconfig("--modversion")
164
def includeDirs(self):
165
out = self._pkgconfig("--cflags-only-I")
173
out = self._pkgconfig("--libs-only-L")
181
# If the compiler, and maybe the compilertype variable is set, read and
182
# return as a tuple. If I can't figure out the compilertype, I use None
183
# to denote 'unknown'
184
compiler = self._pkgconfig("--variable=compiler")
190
def frameworks(self):
191
out = self._pkgconfig("--libs")
192
fw = re.findall(r"-framework (\S+)",out)
196
"""return a set of libraries for lib.
197
On Darwin (MacOSX) a tuple with libraries and frameworks is returned
199
out = self._pkgconfig("--libs-only-l")
204
if self.env["PLATFORM"] == "darwin":
205
return (libs, self.frameworks())
209
return self._pkgconfig("--cflags")
212
return self._pkgconfig("--libs")