build: on the fly dependency checking
[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 #################################################
79 # write out config.h in the right directory
80 @conf
81 def SAMBA_CONFIG_H(conf, path=None):
82     if os.path.normpath(conf.curdir) != os.path.normpath(os.environ.get('PWD')):
83         return
84     if path is None:
85         conf.write_config_header('config.h', top=True)
86     else:
87         conf.write_config_header(path)
88
89
90 ##############################################################
91 # setup a configurable path
92 @conf
93 def CONFIG_PATH(conf, name, default):
94     if not name in conf.env:
95         conf.env[name] = conf.env['PREFIX'] + default
96     conf.define(name, conf.env[name], quote=True)
97
98 ##############################################################
99 # add some CFLAGS to the command line
100 @conf
101 def ADD_CFLAGS(conf, flags):
102     conf.env.append_value('CCFLAGS', flags.split())
103
104
105 ################################################################
106 # magic rpath handling
107 #
108 # we want a different rpath when installing and when building
109 # Note that this should really check if rpath is available on this platform
110 # and it should also honor an --enable-rpath option
111 def set_rpath(bld):
112     if Options.is_install:
113         if bld.env['RPATH_ON_INSTALL']:
114             bld.env['RPATH'] = ['-Wl,-rpath=%s/lib' % bld.env.PREFIX]
115         else:
116             bld.env['RPATH'] = []
117     else:
118         rpath = os.path.normpath('%s/bin/%s' % (bld.curdir, LIB_PATH))
119         bld.env.append_value('RPATH', '-Wl,-rpath=%s' % rpath)
120 Build.BuildContext.set_rpath = set_rpath
121
122
123 #############################################################
124 # return a named build cache dictionary, used to store
125 # state inside the following functions
126 def BUILD_CACHE(bld, name):
127     if name in bld.env:
128         return bld.env[name]
129     bld.env[name] = {}
130     return bld.env[name]
131
132
133 #############################################################
134 # a build assert call
135 def ASSERT(ctx, expression, msg):
136     if not expression:
137         sys.stderr.write("ERROR: %s\n" % msg)
138         raise AssertionError
139 Build.BuildContext.ASSERT = ASSERT
140
141 ################################################################
142 # create a list of files by pre-pending each with a subdir name
143 def SUBDIR(bld, subdir, list):
144     ret = ''
145     for l in list.split():
146         ret = ret + subdir + '/' + l + ' '
147     return ret
148 Build.BuildContext.SUBDIR = SUBDIR
149
150 #################################################################
151 # create the samba build environment
152 @conf
153 def SAMBA_BUILD_ENV(conf):
154     libpath="%s/%s" % (conf.blddir, LIB_PATH)
155     if not os.path.exists(libpath):
156         os.mkdir(libpath)
157
158 ##############################################
159 # remove .. elements from a path list
160 def NORMPATH(bld, ilist):
161     return " ".join([os.path.normpath(p) for p in ilist.split(" ")])
162 Build.BuildContext.NORMPATH = NORMPATH
163
164 ################################################################
165 # add an init_function to the list for a subsystem
166 def ADD_INIT_FUNCTION(bld, subsystem, init_function):
167     if init_function is None:
168         return
169     bld.ASSERT(subsystem is not None, "You must specify a subsystem for init_function '%s'" % init_function)
170     cache = BUILD_CACHE(bld, 'INIT_FUNCTIONS')
171     if not subsystem in cache:
172         cache[subsystem] = ''
173     cache[subsystem] += '%s,' % init_function
174 Build.BuildContext.ADD_INIT_FUNCTION = ADD_INIT_FUNCTION
175
176 #######################################################
177 # d1 += d2
178 def dict_concat(d1, d2):
179     for t in d2:
180         if t not in d1:
181             d1[t] = d2[t]
182
183 ################################################################
184 # recursively build the dependency list for a target
185 def FULL_DEPENDENCIES(bld, cache, target, chain, path):
186     if not target in cache:
187         return {}
188     deps = cache[target].copy()
189     for t in cache[target]:
190         bld.ASSERT(t not in chain, "Circular dependency for %s: %s->%s" % (t, path, t));
191         c2 = chain.copy()
192         c2[t] = True
193         dict_concat(deps, FULL_DEPENDENCIES(bld, cache, t, c2, "%s->%s" % (path, t)))
194     return deps
195
196 ############################################################
197 # check our build dependencies for circular dependencies
198 def CHECK_TARGET_DEPENDENCY(bld, target):
199     cache = BUILD_CACHE(bld, 'LIB_DEPS')
200     FULL_DEPENDENCIES(bld, cache, target, { target:True }, target)
201
202 ################################################################
203 # add to the dependency list. Return a new dependency list with
204 # any circular dependencies removed
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     return ' '.join(list2)
220
221
222 #################################################################
223 # return a include list for a set of library dependencies
224 def SAMBA_LIBRARY_INCLUDE_LIST(bld, deps):
225     ret = bld.curdir + ' '
226     cache = BUILD_CACHE(bld, 'INCLUDE_LIST')
227     for l in deps.split():
228         if l in cache:
229             ret = ret + cache[l] + ' '
230     return ret
231 Build.BuildContext.SAMBA_LIBRARY_INCLUDE_LIST = SAMBA_LIBRARY_INCLUDE_LIST
232
233 #################################################################
234 # define a Samba library
235 def SAMBA_LIBRARY(bld, libname, source_list,
236                   deps='',
237                   public_deps='',
238                   include_list='.',
239                   public_headers=None,
240                   vnum=None,
241                   cflags=None,
242                   autoproto=None):
243     # print "Declaring SAMBA_LIBRARY %s" % libname
244     #print "SAMBA_LIBRARY '%s' with deps '%s'" % (libname, deps)
245
246     deps = ADD_DEPENDENCIES(bld, libname, deps)
247
248     ilist = bld.SAMBA_LIBRARY_INCLUDE_LIST(deps) + bld.SUBDIR(bld.curdir, include_list)
249     ilist = bld.NORMPATH(ilist)
250     bld(
251         features = 'cc cshlib',
252         source = source_list,
253         target=libname,
254         uselib_local = deps,
255         includes='. ' + os.environ.get('PWD') + '/bin/default ' + ilist,
256         vnum=vnum)
257
258     # put a link to the library in bin/shared
259     soext=""
260     if vnum is not None:
261         soext = '.' + vnum.split('.')[0]
262     bld(
263         source = 'lib%s.so' % libname,
264         target = '%s.lnk' % libname,
265         rule = 'ln -sf ../${SRC}%s %s/lib%s.so%s && touch ${TGT}' %
266         (soext, LIB_PATH, libname, soext),
267         shell = True
268         )
269     cache = BUILD_CACHE(bld, 'INCLUDE_LIST')
270     cache[libname] = ilist
271
272 Build.BuildContext.SAMBA_LIBRARY = SAMBA_LIBRARY
273
274
275 #################################################################
276 # define a Samba binary
277 def SAMBA_BINARY(bld, binname, source_list,
278                  deps='',
279                  syslibs='',
280                  include_list='',
281                  public_headers=None,
282                  modules=None,
283                  installdir=None,
284                  ldflags=None,
285                  cflags=None,
286                  autoproto=None,
287                  manpages=None):
288     ilist = '. ' + os.environ.get('PWD') + '/bin/default ' + bld.SAMBA_LIBRARY_INCLUDE_LIST(deps) + ' ' + include_list
289     ilist = bld.NORMPATH(ilist)
290     ccflags = ''
291
292     #print "SAMBA_BINARY '%s' with deps '%s'" % (binname, deps)
293
294     deps = ADD_DEPENDENCIES(bld, binname, deps)
295
296     cache = BUILD_CACHE(bld, 'INIT_FUNCTIONS')
297     if modules is not None:
298         for m in modules.split():
299             bld.ASSERT(m in cache,
300                        "No init_function defined for module '%s' in binary '%s'" % (m, binname))
301             ccflags += ' -DSTATIC_%s_MODULES="%s"' % (m, cache[m])
302
303     bld(
304         features = 'cc cprogram',
305         source = source_list,
306         target = binname,
307         uselib_local = deps,
308         uselib = syslibs,
309         includes = ilist,
310         ccflags = ccflags,
311         top=True)
312     # put a link to the binary in bin/
313     bld(
314         source = binname,
315         rule = 'ln -sf ${SRC} .',
316         )
317 Build.BuildContext.SAMBA_BINARY = SAMBA_BINARY
318
319
320 #################################################################
321 # define a Samba python module
322 def SAMBA_PYTHON(bld, name, source_list,
323                  deps='',
324                  public_deps='',
325                  realname=''):
326
327     #print "SAMBA_PYTHON '%s' with deps '%s'" % (name, deps)
328
329     deps = ADD_DEPENDENCIES(bld, name, deps)
330
331     Logs.debug('runner: PYTHON_SAMBA not implemented')
332     return
333 Build.BuildContext.SAMBA_PYTHON = SAMBA_PYTHON
334
335
336 ################################################################
337 # build a C prototype file automatically
338 def AUTOPROTO(bld, header, source_list):
339     if header is not None:
340         bld(
341             source = source_list,
342             target = header,
343             rule = '../script/mkproto.pl --srcdir=.. --builddir=. --public=/dev/null --private=${TGT} ${SRC}'
344             )
345 Build.BuildContext.AUTOPROTO = AUTOPROTO
346
347
348 #################################################################
349 # define a Samba module.
350 def SAMBA_MODULE(bld, modname, source_list,
351                  deps='',
352                  include_list='.',
353                  subsystem=None,
354                  init_function=None,
355                  autoproto=None,
356                  aliases=None,
357                  cflags=None,
358                  output_type=None):
359     #print "SAMBA_MODULE '%s' with deps '%s'" % (modname, deps)
360     bld.ADD_INIT_FUNCTION(subsystem, init_function)
361     bld.AUTOPROTO(autoproto, source_list)
362     bld.SAMBA_LIBRARY(modname, source_list,
363                       deps=deps, include_list=include_list)
364 Build.BuildContext.SAMBA_MODULE = SAMBA_MODULE
365
366 #################################################################
367 # define a Samba subsystem
368 def SAMBA_SUBSYSTEM(bld, modname, source_list,
369                     deps='',
370                     public_deps='',
371                     include_list='.',
372                     public_headers=None,
373                     autoproto=None,
374                     cflags=None,
375                     init_function_sentinal=None):
376     bld.SAMBA_LIBRARY(modname, source_list,
377                       deps=deps, include_list=include_list)
378 Build.BuildContext.SAMBA_SUBSYSTEM = SAMBA_SUBSYSTEM
379
380
381 ###############################################################
382 # add a new set of build rules from a subdirectory
383 # the @runonce decorator ensures we don't end up
384 # with duplicate rules
385 @runonce
386 def BUILD_SUBDIR(bld, dir):
387     bld.add_subdirs(dir)
388 Build.BuildContext.BUILD_SUBDIR = BUILD_SUBDIR
389
390
391 ############################################################
392 # this overrides the 'waf -v' debug output to be in a nice
393 # unix like format instead of a python list.
394 # Thanks to ita on #waf for this
395 def exec_command(self, cmd, **kw):
396     import Utils, Logs
397     _cmd = cmd
398     if isinstance(cmd, list):
399         _cmd = ' '.join(cmd)
400     Logs.debug('runner: %s' % _cmd)
401     if self.log:
402         self.log.write('%s\n' % cmd)
403         kw['log'] = self.log
404     try:
405         if not kw.get('cwd', None):
406             kw['cwd'] = self.cwd
407     except AttributeError:
408         self.cwd = kw['cwd'] = self.bldnode.abspath()
409     return Utils.exec_command(cmd, **kw)
410 Build.BuildContext.exec_command = exec_command
411
412