1# functions to support bundled libraries
2
3import sys
4from waflib import Build, Options, Logs
5from waflib.Configure import conf
6from wafsamba import samba_utils
7
8def PRIVATE_NAME(bld, name, private_extension, private_library):
9    '''possibly rename a library to include a bundled extension'''
10
11    if not private_library:
12        return name
13
14    # we now use the same private name for libraries as the public name.
15    # see http://git.samba.org/?p=tridge/junkcode.git;a=tree;f=shlib for a
16    # demonstration that this is the right thing to do
17    # also see http://lists.samba.org/archive/samba-technical/2011-January/075816.html
18    if private_extension:
19        return name
20
21    extension = bld.env.PRIVATE_EXTENSION
22
23    if extension and name.startswith('%s' % extension):
24        return name
25
26    if extension and name.endswith('%s' % extension):
27        return name
28
29    return "%s-%s" % (name, extension)
30
31
32def target_in_list(target, lst, default):
33    for l in lst:
34        if target == l:
35            return True
36        if '!' + target == l:
37            return False
38        if l == 'ALL':
39            return True
40        if l == 'NONE':
41            return False
42    return default
43
44
45def BUILTIN_LIBRARY(bld, name):
46    '''return True if a library should be builtin
47       instead of being built as a shared lib'''
48    return target_in_list(name, bld.env.BUILTIN_LIBRARIES, False)
49Build.BuildContext.BUILTIN_LIBRARY = BUILTIN_LIBRARY
50
51
52def BUILTIN_DEFAULT(opt, builtins):
53    '''set a comma separated default list of builtin libraries for this package'''
54    if 'BUILTIN_LIBRARIES_DEFAULT' in Options.options.__dict__:
55        return
56    Options.options.__dict__['BUILTIN_LIBRARIES_DEFAULT'] = builtins
57Options.OptionsContext.BUILTIN_DEFAULT = BUILTIN_DEFAULT
58
59
60def PRIVATE_EXTENSION_DEFAULT(opt, extension, noextension=''):
61    '''set a default private library extension'''
62    if 'PRIVATE_EXTENSION_DEFAULT' in Options.options.__dict__:
63        return
64    Options.options.__dict__['PRIVATE_EXTENSION_DEFAULT'] = extension
65    Options.options.__dict__['PRIVATE_EXTENSION_EXCEPTION'] = noextension
66Options.OptionsContext.PRIVATE_EXTENSION_DEFAULT = PRIVATE_EXTENSION_DEFAULT
67
68
69def minimum_library_version(conf, libname, default):
70    '''allow override of mininum system library version'''
71
72    minlist = Options.options.MINIMUM_LIBRARY_VERSION
73    if not minlist:
74        return default
75
76    for m in minlist.split(','):
77        a = m.split(':')
78        if len(a) != 2:
79            Logs.error("Bad syntax for --minimum-library-version of %s" % m)
80            sys.exit(1)
81        if a[0] == libname:
82            return a[1]
83    return default
84
85
86@conf
87def LIB_MAY_BE_BUNDLED(conf, libname):
88    if libname in conf.env.SYSTEM_LIBS:
89        return False
90    if libname in conf.env.BUNDLED_LIBS:
91        return True
92    if '!%s' % libname in conf.env.BUNDLED_LIBS:
93        return False
94    if 'NONE' in conf.env.BUNDLED_LIBS:
95        return False
96    return True
97
98@conf
99def LIB_MUST_BE_BUNDLED(conf, libname):
100    if libname in conf.env.BUNDLED_LIBS:
101        return True
102    if '!%s' % libname in conf.env.BUNDLED_LIBS:
103        return False
104    if 'ALL' in conf.env.BUNDLED_LIBS:
105        return True
106    return False
107
108@conf
109def LIB_MUST_BE_PRIVATE(conf, libname):
110    return ('ALL' in conf.env.PRIVATE_LIBS or
111            libname in conf.env.PRIVATE_LIBS)
112
113@conf
114def CHECK_BUNDLED_SYSTEM_PKG(conf, libname, minversion='0.0.0',
115        maxversion=None, version_blacklist=[],
116        onlyif=None, implied_deps=None, pkg=None):
117    '''check if a library is available as a system library.
118
119    This only tries using pkg-config
120    '''
121    return conf.CHECK_BUNDLED_SYSTEM(libname,
122                                     minversion=minversion,
123                                     maxversion=maxversion,
124                                     version_blacklist=version_blacklist,
125                                     onlyif=onlyif,
126                                     implied_deps=implied_deps,
127                                     pkg=pkg)
128
129@conf
130def CHECK_BUNDLED_SYSTEM(conf, libname, minversion='0.0.0',
131                         maxversion=None, version_blacklist=[],
132                         checkfunctions=None, headers=None, checkcode=None,
133                         onlyif=None, implied_deps=None,
134                         require_headers=True, pkg=None, set_target=True):
135    '''check if a library is available as a system library.
136    this first tries via pkg-config, then if that fails
137    tries by testing for a specified function in the specified lib
138    '''
139    # We always do a logic validation of 'onlyif' first
140    missing = []
141    if onlyif:
142        for l in samba_utils.TO_LIST(onlyif):
143            f = 'FOUND_SYSTEMLIB_%s' % l
144            if not f in conf.env:
145                Logs.error('ERROR: CHECK_BUNDLED_SYSTEM(%s) - ' % (libname) +
146                           'missing prerequisite check for ' +
147                           'system library %s, onlyif=%r' % (l, onlyif))
148                sys.exit(1)
149            if not conf.env[f]:
150                missing.append(l)
151    found = 'FOUND_SYSTEMLIB_%s' % libname
152    if found in conf.env:
153        return conf.env[found]
154    if conf.LIB_MUST_BE_BUNDLED(libname):
155        conf.env[found] = False
156        return False
157
158    # see if the library should only use a system version if another dependent
159    # system version is found. That prevents possible use of mixed library
160    # versions
161    if missing:
162        if not conf.LIB_MAY_BE_BUNDLED(libname):
163            Logs.error('ERROR: Use of system library %s depends on missing system library/libraries %r' % (libname, missing))
164            sys.exit(1)
165        conf.env[found] = False
166        return False
167
168    def check_functions_headers_code():
169        '''helper function for CHECK_BUNDLED_SYSTEM'''
170        if require_headers and headers and not conf.CHECK_HEADERS(headers, lib=libname):
171            return False
172        if checkfunctions is not None:
173            ok = conf.CHECK_FUNCS_IN(checkfunctions, libname, headers=headers,
174                                     empty_decl=False, set_target=False)
175            if not ok:
176                return False
177        if checkcode is not None:
178            define='CHECK_BUNDLED_SYSTEM_%s' % libname.upper()
179            ok = conf.CHECK_CODE(checkcode, lib=libname,
180                                 headers=headers, local_include=False,
181                                 msg=msg, define=define)
182            conf.CONFIG_RESET(define)
183            if not ok:
184                return False
185        return True
186
187    minversion = minimum_library_version(conf, libname, minversion)
188
189    msg = 'Checking for system %s' % libname
190    msg_ver = []
191    if minversion != '0.0.0':
192        msg_ver.append('>=%s' % minversion)
193    if maxversion is not None:
194        msg_ver.append('<=%s' % maxversion)
195    for v in version_blacklist:
196        msg_ver.append('!=%s' % v)
197    if msg_ver != []:
198        msg += " (%s)" % (" ".join(msg_ver))
199
200    uselib_store=libname.upper()
201    if pkg is None:
202        pkg = libname
203
204    version_checks = '%s >= %s' % (pkg, minversion)
205    if maxversion is not None:
206        version_checks += ' %s <= %s' % (pkg, maxversion)
207
208    version_checks += "".join(' %s != %s' % (pkg, v) for v in version_blacklist)
209
210    # try pkgconfig first
211    if (conf.CHECK_CFG(package=pkg,
212                      args='"%s" --cflags --libs' % (version_checks),
213                      msg=msg, uselib_store=uselib_store) and
214        check_functions_headers_code()):
215        if set_target:
216            conf.SET_TARGET_TYPE(libname, 'SYSLIB')
217        conf.env[found] = True
218        if implied_deps:
219            conf.SET_SYSLIB_DEPS(libname, implied_deps)
220        return True
221    if checkfunctions is not None:
222        if check_functions_headers_code():
223            conf.env[found] = True
224            if implied_deps:
225                conf.SET_SYSLIB_DEPS(libname, implied_deps)
226            if set_target:
227                conf.SET_TARGET_TYPE(libname, 'SYSLIB')
228            return True
229    conf.env[found] = False
230    if not conf.LIB_MAY_BE_BUNDLED(libname):
231        Logs.error('ERROR: System library %s of version %s not found, and bundling disabled' % (libname, minversion))
232        sys.exit(1)
233    return False
234
235
236def tuplize_version(version):
237    return tuple([int(x) for x in version.split(".")])
238
239@conf
240def CHECK_BUNDLED_SYSTEM_PYTHON(conf, libname, modulename, minversion='0.0.0'):
241    '''check if a python module is available on the system and
242    has the specified minimum version.
243    '''
244    if conf.LIB_MUST_BE_BUNDLED(libname):
245        return False
246
247    # see if the library should only use a system version if another dependent
248    # system version is found. That prevents possible use of mixed library
249    # versions
250    minversion = minimum_library_version(conf, libname, minversion)
251
252    try:
253        m = __import__(modulename)
254    except ImportError:
255        found = False
256    else:
257        try:
258            version = m.__version__
259        except AttributeError:
260            found = False
261        else:
262            found = tuplize_version(version) >= tuplize_version(minversion)
263    if not found and not conf.LIB_MAY_BE_BUNDLED(libname):
264        Logs.error('ERROR: Python module %s of version %s not found, and bundling disabled' % (libname, minversion))
265        sys.exit(1)
266    return found
267
268
269def NONSHARED_BINARY(bld, name):
270    '''return True if a binary should be built without non-system shared libs'''
271    return target_in_list(name, bld.env.NONSHARED_BINARIES, False)
272Build.BuildContext.NONSHARED_BINARY = NONSHARED_BINARY
273
274
275