build: auto-strip empty dependencies
[ira/wip.git] / lib / replace / wafsamba.py
1 # a waf tool to add autoconf-like macros to the configure section
2 # and for SAMBA_ macros for building libraries, binaries etc
3
4 import Build, os, Logs, sys, Configure, Options
5 from Configure import conf
6
7 LIB_PATH="shared"
8
9 ######################################################
10 # this is used as a decorator to make functions only
11 # run once. Based on the idea from
12 # http://stackoverflow.com/questions/815110/is-there-a-decorator-to-simply-cache-function-return-values
13 runonce_ret = {}
14 def runonce(function):
15     def wrapper(*args):
16         if args in runonce_ret:
17             return runonce_ret[args]
18         else:
19             ret = function(*args)
20             runonce_ret[args] = ret
21             return ret
22     return wrapper
23
24
25 ####################################################
26 # some autoconf like helpers, to make the transition
27 # to waf a bit easier for those used to autoconf
28 # m4 files
29 @runonce
30 @conf
31 def DEFUN(conf, d, v):
32     conf.define(d, v, quote=False)
33     conf.env.append_value('CCDEFINES', d + '=' + str(v))
34
35 @runonce
36 def CHECK_HEADER(conf, h):
37     if conf.check(header_name=h):
38         conf.env.hlist.append(h)
39
40 @conf
41 def CHECK_HEADERS(conf, list):
42     for hdr in list.split():
43         CHECK_HEADER(conf, hdr)
44
45 @conf
46 def CHECK_TYPES(conf, list):
47     for t in list.split():
48         conf.check(type_name=t, header_name=conf.env.hlist)
49
50 @conf
51 def CHECK_TYPE_IN(conf, t, hdr):
52     if conf.check(header_name=hdr):
53         conf.check(type_name=t, header_name=hdr)
54
55 @conf
56 def CHECK_TYPE(conf, t, alternate):
57     if not conf.check(type_name=t, header_name=conf.env.hlist):
58         conf.DEFUN(t, alternate)
59
60 @runonce
61 def CHECK_FUNC(conf, f):
62     conf.check(function_name=f, header_name=conf.env.hlist)
63
64
65 @conf
66 def CHECK_FUNCS(conf, list):
67     for f in list.split():
68         CHECK_FUNC(conf, f)
69
70 @conf
71 def CHECK_FUNCS_IN(conf, list, library):
72     if conf.check(lib=library, uselib_store=library):
73         for f in list.split():
74             conf.check(function_name=f, lib=library, header_name=conf.env.hlist)
75         conf.env['LIB_' + library.upper()] = library
76
77 #################################################
78 # write out config.h in the right directory
79 @conf
80 def SAMBA_CONFIG_H(conf, path=None):
81     if os.path.normpath(conf.curdir) != os.path.normpath(os.environ.get('PWD')):
82         return
83     if path is None:
84         conf.write_config_header('config.h', top=True)
85     else:
86         conf.write_config_header(path)
87
88
89 ##############################################################
90 # setup a configurable path
91 @conf
92 def CONFIG_PATH(conf, name, default):
93     if not name in conf.env:
94         conf.env[name] = conf.env['PREFIX'] + default
95     conf.define(name, conf.env[name], quote=True)
96
97 ##############################################################
98 # add some CFLAGS to the command line
99 @conf
100 def ADD_CFLAGS(conf, flags):
101     conf.env.append_value('CCFLAGS', flags.split())
102
103
104 ################################################################
105 # magic rpath handling
106 #
107 # we want a different rpath when installing and when building
108 # Note that this should really check if rpath is available on this platform
109 # and it should also honor an --enable-rpath option
110 def set_rpath(bld):
111     if Options.is_install:
112         if bld.env['RPATH_ON_INSTALL']:
113             bld.env['RPATH'] = ['-Wl,-rpath=%s/lib' % bld.env.PREFIX]
114         else:
115             bld.env['RPATH'] = []
116     else:
117         rpath = os.path.normpath('%s/bin/%s' % (bld.curdir, LIB_PATH))
118         bld.env.append_value('RPATH', '-Wl,-rpath=%s' % rpath)
119 Build.BuildContext.set_rpath = set_rpath
120
121
122 #############################################################
123 # return a named build cache dictionary, used to store
124 # state inside the following functions
125 def BUILD_CACHE(bld, name):
126     if name in bld.env:
127         return bld.env[name]
128     bld.env[name] = {}
129     return bld.env[name]
130
131
132 #############################################################
133 # a build assert call
134 def ASSERT(ctx, expression, msg):
135     if not expression:
136         sys.stderr.write("ERROR: %s\n" % msg)
137         raise AssertionError
138 Build.BuildContext.ASSERT = ASSERT
139
140 ################################################################
141 # create a list of files by pre-pending each with a subdir name
142 def SUBDIR(bld, subdir, list):
143     ret = ''
144     for l in list.split():
145         ret = ret + subdir + '/' + l + ' '
146     return ret
147 Build.BuildContext.SUBDIR = SUBDIR
148
149 #################################################################
150 # create the samba build environment
151 @conf
152 def SAMBA_BUILD_ENV(conf):
153     libpath="%s/%s" % (conf.blddir, LIB_PATH)
154     if not os.path.exists(libpath):
155         os.mkdir(libpath)
156
157 ##############################################
158 # remove .. elements from a path list
159 def NORMPATH(bld, ilist):
160     return " ".join([os.path.normpath(p) for p in ilist.split(" ")])
161 Build.BuildContext.NORMPATH = NORMPATH
162
163 ################################################################
164 # add an init_function to the list for a subsystem
165 def ADD_INIT_FUNCTION(bld, subsystem, init_function):
166     if init_function is None:
167         return
168     bld.ASSERT(subsystem is not None, "You must specify a subsystem for init_function '%s'" % init_function)
169     cache = BUILD_CACHE(bld, 'INIT_FUNCTIONS')
170     if not subsystem in cache:
171         cache[subsystem] = ''
172     cache[subsystem] += '%s,' % init_function
173 Build.BuildContext.ADD_INIT_FUNCTION = ADD_INIT_FUNCTION
174
175 #######################################################
176 # d1 += d2
177 def dict_concat(d1, d2):
178     for t in d2:
179         if t not in d1:
180             d1[t] = d2[t]
181
182 ################################################################
183 # recursively build the dependency list for a target
184 def FULL_DEPENDENCIES(bld, cache, target, chain, path):
185     if not target in cache:
186         return {}
187     deps = cache[target].copy()
188     for t in cache[target]:
189         bld.ASSERT(t not in chain, "Circular dependency for %s: %s->%s" % (t, path, t));
190         c2 = chain.copy()
191         c2[t] = True
192         dict_concat(deps, FULL_DEPENDENCIES(bld, cache, t, c2, "%s->%s" % (path, t)))
193     return deps
194
195 ############################################################
196 # check our build dependencies for circular dependencies
197 def CHECK_TARGET_DEPENDENCY(bld, target):
198     cache = BUILD_CACHE(bld, 'LIB_DEPS')
199     FULL_DEPENDENCIES(bld, cache, target, { target:True }, target)
200
201 ################################################################
202 # add to the dependency list. Return a new dependency list with
203 # any circular dependencies removed
204 # returns a tuple containing (systemdeps, localdeps)
205 def ADD_DEPENDENCIES(bld, name, deps):
206     cache = BUILD_CACHE(bld, 'LIB_DEPS')
207     if not name in cache:
208         cache[name] = {}
209     list = deps.split()
210     list2 = []
211     for d in list:
212         cache[name][d] = True;
213         try:
214             CHECK_TARGET_DEPENDENCY(bld, name)
215             list2.append(d)
216         except AssertionError:
217             print "Removing dependency %s from target %s" % (d, name)
218             del(cache[name][d])
219
220     # extract out the system dependencies
221     sysdeps = []
222     localdeps = []
223     cache = BUILD_CACHE(bld, 'EMPTY_LIBS')
224     for d in list2:
225         # strip out any dependencies on empty libraries
226         if d in cache:
227             continue
228         libname = 'LIB_%s' % d.upper()
229         if libname in bld.env:
230             sysdeps.append(d)
231         else:
232             localdeps.append(d)
233     return (' '.join(sysdeps), ' '.join(localdeps))
234
235
236 #################################################################
237 # return a include list for a set of library dependencies
238 def SAMBA_LIBRARY_INCLUDE_LIST(bld, deps):
239     ret = bld.curdir + ' '
240     cache = BUILD_CACHE(bld, 'INCLUDE_LIST')
241     for l in deps.split():
242         if l in cache:
243             ret = ret + cache[l] + ' '
244     return ret
245 Build.BuildContext.SAMBA_LIBRARY_INCLUDE_LIST = SAMBA_LIBRARY_INCLUDE_LIST
246
247 #################################################################
248 # define a Samba library
249 def SAMBA_LIBRARY(bld, libname, source_list,
250                   deps='',
251                   public_deps='',
252                   include_list='.',
253                   public_headers=None,
254                   vnum=None,
255                   cflags=None,
256                   autoproto=None):
257     # print "Declaring SAMBA_LIBRARY %s" % libname
258     #print "SAMBA_LIBRARY '%s' with deps '%s'" % (libname, deps)
259
260     # remember empty libraries, so we can strip the dependencies
261     if (source_list == '') or (source_list == []):
262         cache = BUILD_CACHE(bld, 'EMPTY_LIBS')
263         cache[libname] = True
264         return
265
266     (sysdeps, deps) = ADD_DEPENDENCIES(bld, libname, deps)
267
268     ilist = bld.SAMBA_LIBRARY_INCLUDE_LIST(deps) + bld.SUBDIR(bld.curdir, include_list)
269     ilist = bld.NORMPATH(ilist)
270     bld(
271         features = 'cc cshlib',
272         source = source_list,
273         target=libname,
274         uselib_local = deps,
275         uselib = sysdeps,
276         includes='. ' + os.environ.get('PWD') + '/bin/default ' + ilist,
277         vnum=vnum)
278
279     # put a link to the library in bin/shared
280     soext=""
281     if vnum is not None:
282         soext = '.' + vnum.split('.')[0]
283     bld(
284         source = 'lib%s.so' % libname,
285         target = '%s.lnk' % libname,
286         rule = 'ln -sf ../${SRC}%s %s/lib%s.so%s && touch ${TGT}' %
287         (soext, LIB_PATH, libname, soext),
288         shell = True
289         )
290     cache = BUILD_CACHE(bld, 'INCLUDE_LIST')
291     cache[libname] = ilist
292
293 Build.BuildContext.SAMBA_LIBRARY = SAMBA_LIBRARY
294
295
296 #################################################################
297 # define a Samba binary
298 def SAMBA_BINARY(bld, binname, source_list,
299                  deps='',
300                  include_list='',
301                  public_headers=None,
302                  modules=None,
303                  installdir=None,
304                  ldflags=None,
305                  cflags=None,
306                  autoproto=None,
307                  use_hostcc=None,
308                  compiler=None,
309                  manpages=None):
310     ilist = '. ' + os.environ.get('PWD') + '/bin/default ' + bld.SAMBA_LIBRARY_INCLUDE_LIST(deps) + ' ' + include_list
311     ilist = bld.NORMPATH(ilist)
312     ccflags = ''
313
314     #print "SAMBA_BINARY '%s' with deps '%s'" % (binname, deps)
315
316     (sysdeps, deps) = ADD_DEPENDENCIES(bld, binname, deps)
317
318     cache = BUILD_CACHE(bld, 'INIT_FUNCTIONS')
319     if modules is not None:
320         for m in modules.split():
321             bld.ASSERT(m in cache,
322                        "No init_function defined for module '%s' in binary '%s'" % (m, binname))
323             ccflags += ' -DSTATIC_%s_MODULES="%s"' % (m, cache[m])
324
325     bld(
326         features = 'cc cprogram',
327         source = source_list,
328         target = binname,
329         uselib_local = deps,
330         uselib = sysdeps,
331         includes = ilist,
332         ccflags = ccflags,
333         top=True)
334     # put a link to the binary in bin/
335     bld(
336         source = binname,
337         rule = 'ln -sf ${SRC} .',
338         )
339 Build.BuildContext.SAMBA_BINARY = SAMBA_BINARY
340
341
342 #################################################################
343 # define a Samba python module
344 def SAMBA_PYTHON(bld, name, source_list,
345                  deps='',
346                  public_deps='',
347                  realname=''):
348
349     #print "SAMBA_PYTHON '%s' with deps '%s'" % (name, deps)
350
351     (sysdeps, deps) = ADD_DEPENDENCIES(bld, name, deps)
352
353     Logs.debug('runner: PYTHON_SAMBA not implemented')
354     return
355 Build.BuildContext.SAMBA_PYTHON = SAMBA_PYTHON
356
357
358 ################################################################
359 # build a C prototype file automatically
360 def AUTOPROTO(bld, header, source_list):
361     if header is not None:
362         bld(
363             source = source_list,
364             target = header,
365             rule = '../script/mkproto.pl --srcdir=.. --builddir=. --public=/dev/null --private=${TGT} ${SRC}'
366             )
367 Build.BuildContext.AUTOPROTO = AUTOPROTO
368
369
370 #################################################################
371 # define a Samba module.
372 def SAMBA_MODULE(bld, modname, source_list,
373                  deps='',
374                  include_list='.',
375                  subsystem=None,
376                  init_function=None,
377                  autoproto=None,
378                  aliases=None,
379                  cflags=None,
380                  output_type=None):
381     #print "SAMBA_MODULE '%s' with deps '%s'" % (modname, deps)
382     bld.ADD_INIT_FUNCTION(subsystem, init_function)
383     bld.AUTOPROTO(autoproto, source_list)
384     bld.SAMBA_LIBRARY(modname, source_list,
385                       deps=deps, include_list=include_list)
386 Build.BuildContext.SAMBA_MODULE = SAMBA_MODULE
387
388 #################################################################
389 # define a Samba subsystem
390 def SAMBA_SUBSYSTEM(bld, modname, source_list,
391                     deps='',
392                     public_deps='',
393                     include_list='.',
394                     public_headers=None,
395                     autoproto=None,
396                     cflags=None,
397                     init_function_sentinal=None):
398     bld.SAMBA_LIBRARY(modname, source_list,
399                       deps=deps, include_list=include_list)
400 Build.BuildContext.SAMBA_SUBSYSTEM = SAMBA_SUBSYSTEM
401
402
403 ###############################################################
404 # add a new set of build rules from a subdirectory
405 # the @runonce decorator ensures we don't end up
406 # with duplicate rules
407 @runonce
408 def BUILD_SUBDIR(bld, dir):
409     bld.add_subdirs(dir)
410 Build.BuildContext.BUILD_SUBDIR = BUILD_SUBDIR
411
412
413 ############################################################
414 # this overrides the 'waf -v' debug output to be in a nice
415 # unix like format instead of a python list.
416 # Thanks to ita on #waf for this
417 def exec_command(self, cmd, **kw):
418     import Utils, Logs
419     _cmd = cmd
420     if isinstance(cmd, list):
421         _cmd = ' '.join(cmd)
422     Logs.debug('runner: %s' % _cmd)
423     if self.log:
424         self.log.write('%s\n' % cmd)
425         kw['log'] = self.log
426     try:
427         if not kw.get('cwd', None):
428             kw['cwd'] = self.cwd
429     except AttributeError:
430         self.cwd = kw['cwd'] = self.bldnode.abspath()
431     return Utils.exec_command(cmd, **kw)
432 Build.BuildContext.exec_command = exec_command
433
434