08d3d20137737af143936c974d078c918cbf1a24
[samba.git] / buildtools / wafsamba / 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, Options, Task, Utils, cc, TaskGen, fnmatch, re, shutil, Logs
5 from Configure import conf
6 from Logs import debug
7 from samba_utils import SUBST_VARS_RECURSIVE
8
9 # bring in the other samba modules
10 from samba_optimisation import *
11 from samba_utils import *
12 from samba_autoconf import *
13 from samba_patterns import *
14 from samba_pidl import *
15 from samba_errtable import *
16 from samba_asn1 import *
17 from samba_autoproto import *
18 from samba_python import *
19 from samba_deps import *
20 from samba_bundled import *
21 import samba_install
22 import samba_conftests
23 import tru64cc
24 import irixcc
25 import generic_cc
26 import samba_dist
27
28 O644 = 420
29
30 # some systems have broken threading in python
31 if os.environ.get('WAF_NOTHREADS') == '1':
32     import nothreads
33
34 LIB_PATH="shared"
35
36 os.putenv('PYTHONUNBUFFERED', '1')
37
38 @conf
39 def SAMBA_BUILD_ENV(conf):
40     '''create the samba build environment'''
41     conf.env.BUILD_DIRECTORY = conf.blddir
42     mkdir_p(os.path.join(conf.blddir, LIB_PATH))
43     mkdir_p(os.path.join(conf.blddir, 'python/samba/dcerpc'))
44     # this allows all of the bin/shared and bin/python targets
45     # to be expressed in terms of build directory paths
46     for p in ['python','shared']:
47         link_target = os.path.join(conf.blddir, 'default/' + p)
48         if not os.path.lexists(link_target):
49             os.symlink('../' + p, link_target)
50
51     # get perl to put the blib files in the build directory
52     blib_bld = os.path.join(conf.blddir, 'default/pidl/blib')
53     blib_src = os.path.join(conf.srcdir, 'pidl/blib')
54     mkdir_p(blib_bld + '/man1')
55     mkdir_p(blib_bld + '/man3')
56     if os.path.islink(blib_src):
57         os.unlink(blib_src)
58     elif os.path.exists(blib_src):
59         shutil.rmtree(blib_src)
60
61
62 def ADD_INIT_FUNCTION(bld, subsystem, target, init_function):
63     '''add an init_function to the list for a subsystem'''
64     if init_function is None:
65         return
66     bld.ASSERT(subsystem is not None, "You must specify a subsystem for init_function '%s'" % init_function)
67     cache = LOCAL_CACHE(bld, 'INIT_FUNCTIONS')
68     if not subsystem in cache:
69         cache[subsystem] = []
70     cache[subsystem].append( { 'TARGET':target, 'INIT_FUNCTION':init_function } )
71 Build.BuildContext.ADD_INIT_FUNCTION = ADD_INIT_FUNCTION
72
73
74
75 #################################################################
76 def SAMBA_LIBRARY(bld, libname, source,
77                   deps='',
78                   public_deps='',
79                   includes='',
80                   public_headers=None,
81                   header_path=None,
82                   pc_files=None,
83                   vnum=None,
84                   cflags='',
85                   external_library=False,
86                   realname=None,
87                   autoproto=None,
88                   group='main',
89                   depends_on='',
90                   local_include=True,
91                   vars=None,
92                   install_path=None,
93                   install=True,
94                   needs_python=False,
95                   target_type='LIBRARY',
96                   bundled_extension=True,
97                   link_name=None,
98                   enabled=True):
99     '''define a Samba library'''
100
101     if not enabled:
102         SET_TARGET_TYPE(bld, libname, 'DISABLED')
103         return
104
105     source = bld.EXPAND_VARIABLES(source, vars=vars)
106
107     # remember empty libraries, so we can strip the dependencies
108     if (source == '') or (source == []):
109         SET_TARGET_TYPE(bld, libname, 'EMPTY')
110         return
111
112     if target_type != 'PYTHON' and BUILTIN_LIBRARY(bld, libname):
113         obj_target = libname
114     else:
115         obj_target = libname + '.objlist'
116
117     # first create a target for building the object files for this library
118     # by separating in this way, we avoid recompiling the C files
119     # separately for the install library and the build library
120     bld.SAMBA_SUBSYSTEM(obj_target,
121                         source         = source,
122                         deps           = deps,
123                         public_deps    = public_deps,
124                         includes       = includes,
125                         public_headers = public_headers,
126                         header_path    = header_path,
127                         cflags         = cflags,
128                         group          = group,
129                         autoproto      = autoproto,
130                         depends_on     = depends_on,
131                         needs_python   = needs_python,
132                         local_include  = local_include)
133
134     if libname == obj_target:
135         return
136
137     if not SET_TARGET_TYPE(bld, libname, target_type):
138         return
139
140     # the library itself will depend on that object target
141     deps += ' ' + public_deps
142     deps = TO_LIST(deps)
143     deps.append(obj_target)
144
145     if target_type == 'PYTHON':
146         bundled_name = libname
147     else:
148         bundled_name = BUNDLED_NAME(bld, libname, bundled_extension)
149
150     features = 'cc cshlib symlink_lib install_lib'
151     if target_type == 'PYTHON':
152         features += ' pyext'
153     elif needs_python:
154         features += ' pyembed'
155
156     bld.SET_BUILD_GROUP(group)
157     t = bld(
158         features        = features,
159         source          = [],
160         target          = bundled_name,
161         samba_cflags    = CURRENT_CFLAGS(bld, libname, cflags),
162         depends_on      = depends_on,
163         samba_deps      = deps,
164         samba_includes  = includes,
165         local_include   = local_include,
166         vnum            = vnum,
167         install_path    = None,
168         samba_inst_path = install_path,
169         name            = libname,
170         samba_realname  = realname,
171         samba_install   = install
172         )
173
174     if link_name:
175         t.link_name = link_name
176
177     if autoproto is not None:
178         bld.SAMBA_AUTOPROTO(autoproto, source)
179
180     if public_headers is not None:
181         bld.PUBLIC_HEADERS(public_headers, header_path=header_path)
182
183     if pc_files is not None:
184         bld.PKG_CONFIG_FILES(pc_files, vnum=vnum)
185
186 Build.BuildContext.SAMBA_LIBRARY = SAMBA_LIBRARY
187
188
189 #################################################################
190 def SAMBA_BINARY(bld, binname, source,
191                  deps='',
192                  includes='',
193                  public_headers=None,
194                  header_path=None,
195                  modules=None,
196                  installdir=None,
197                  ldflags=None,
198                  cflags='',
199                  autoproto=None,
200                  use_hostcc=None,
201                  compiler=None,
202                  group='binaries',
203                  manpages=None,
204                  local_include=True,
205                  subsystem_name=None,
206                  needs_python=False,
207                  vars=None,
208                  install=True,
209                  install_path=None):
210     '''define a Samba binary'''
211
212     if not SET_TARGET_TYPE(bld, binname, 'BINARY'):
213         return
214
215     features = 'cc cprogram symlink_bin install_bin'
216     if needs_python:
217         features += ' pyembed'
218
219     obj_target = binname + '.objlist'
220
221     source = bld.EXPAND_VARIABLES(source, vars=vars)
222
223     # first create a target for building the object files for this binary
224     # by separating in this way, we avoid recompiling the C files
225     # separately for the install binary and the build binary
226     bld.SAMBA_SUBSYSTEM(obj_target,
227                         source         = source,
228                         deps           = deps,
229                         includes       = includes,
230                         cflags         = cflags,
231                         group          = group,
232                         autoproto      = autoproto,
233                         subsystem_name = subsystem_name,
234                         needs_python   = needs_python,
235                         local_include  = local_include)
236
237     bld.SET_BUILD_GROUP(group)
238
239     # the binary itself will depend on that object target
240     deps = TO_LIST(deps)
241     deps.append(obj_target)
242
243     t = bld(
244         features       = features,
245         source         = [],
246         target         = binname,
247         samba_cflags   = CURRENT_CFLAGS(bld, binname, cflags),
248         samba_deps     = deps,
249         samba_includes = includes,
250         local_include  = local_include,
251         samba_modules  = modules,
252         top            = True,
253         samba_subsystem= subsystem_name,
254         install_path   = None,
255         samba_inst_path= install_path,
256         samba_install  = install
257         )
258
259     # setup the subsystem_name as an alias for the real
260     # binary name, so it can be found when expanding
261     # subsystem dependencies
262     if subsystem_name is not None:
263         bld.TARGET_ALIAS(subsystem_name, binname)
264
265     if autoproto is not None:
266         bld.SAMBA_AUTOPROTO(autoproto, source)
267     if public_headers is not None:
268         bld.PUBLIC_HEADERS(public_headers, header_path=header_path)
269 Build.BuildContext.SAMBA_BINARY = SAMBA_BINARY
270
271
272 #################################################################
273 def SAMBA_MODULE(bld, modname, source,
274                  deps='',
275                  includes='',
276                  subsystem=None,
277                  init_function=None,
278                  autoproto=None,
279                  autoproto_extra_source='',
280                  aliases=None,
281                  cflags='',
282                  internal_module=True,
283                  local_include=True,
284                  vars=None,
285                  enabled=True):
286     '''define a Samba module.'''
287
288     # we add the init function regardless of whether the module
289     # is enabled or not, as we need to generate a null list if
290     # all disabled
291     bld.ADD_INIT_FUNCTION(subsystem, modname, init_function)
292
293     if internal_module or BUILTIN_LIBRARY(bld, modname):
294         # treat internal modules as subsystems for now
295         SAMBA_SUBSYSTEM(bld, modname, source,
296                         deps=deps,
297                         includes=includes,
298                         autoproto=autoproto,
299                         autoproto_extra_source=autoproto_extra_source,
300                         cflags=cflags,
301                         local_include=local_include,
302                         enabled=enabled)
303         return
304
305     if not enabled:
306         SET_TARGET_TYPE(bld, modname, 'DISABLED')
307         return
308
309     source = bld.EXPAND_VARIABLES(source, vars=vars)
310
311     # remember empty modules, so we can strip the dependencies
312     if (source == '') or (source == []):
313         SET_TARGET_TYPE(bld, modname, 'EMPTY')
314         return
315
316     if not SET_TARGET_TYPE(bld, modname, 'MODULE'):
317         return
318
319     if subsystem is not None:
320         deps += ' ' + subsystem
321
322     bld.SET_BUILD_GROUP('main')
323     bld(
324         features       = 'cc',
325         source         = source,
326         target         = modname,
327         samba_cflags   = CURRENT_CFLAGS(bld, modname, cflags),
328         samba_includes = includes,
329         local_include  = local_include,
330         samba_deps     = TO_LIST(deps)
331         )
332
333     if autoproto is not None:
334         bld.SAMBA_AUTOPROTO(autoproto, source + ' ' + autoproto_extra_source)
335
336 Build.BuildContext.SAMBA_MODULE = SAMBA_MODULE
337
338
339 #################################################################
340 def SAMBA_SUBSYSTEM(bld, modname, source,
341                     deps='',
342                     public_deps='',
343                     includes='',
344                     public_headers=None,
345                     header_path=None,
346                     cflags='',
347                     cflags_end=None,
348                     group='main',
349                     init_function_sentinal=None,
350                     heimdal_autoproto=None,
351                     heimdal_autoproto_options=None,
352                     heimdal_autoproto_private=None,
353                     autoproto=None,
354                     autoproto_extra_source='',
355                     depends_on='',
356                     local_include=True,
357                     local_include_first=True,
358                     subsystem_name=None,
359                     enabled=True,
360                     vars=None,
361                     needs_python=False):
362     '''define a Samba subsystem'''
363
364     if not enabled:
365         SET_TARGET_TYPE(bld, modname, 'DISABLED')
366         return
367
368     # remember empty subsystems, so we can strip the dependencies
369     if (source == '') or (source == []):
370         SET_TARGET_TYPE(bld, modname, 'EMPTY')
371         return
372
373     if not SET_TARGET_TYPE(bld, modname, 'SUBSYSTEM'):
374         return
375
376     source = bld.EXPAND_VARIABLES(source, vars=vars)
377
378     deps += ' ' + public_deps
379
380     bld.SET_BUILD_GROUP(group)
381
382     features = 'cc'
383     if needs_python:
384         features += ' pyext'
385
386     t = bld(
387         features       = features,
388         source         = source,
389         target         = modname,
390         samba_cflags   = CURRENT_CFLAGS(bld, modname, cflags),
391         depends_on     = depends_on,
392         samba_deps     = TO_LIST(deps),
393         samba_includes = includes,
394         local_include  = local_include,
395         local_include_first  = local_include_first,
396         samba_subsystem= subsystem_name
397         )
398
399     if cflags_end is not None:
400         t.samba_cflags.extend(TO_LIST(cflags_end))
401
402     if heimdal_autoproto is not None:
403         bld.HEIMDAL_AUTOPROTO(heimdal_autoproto, source, options=heimdal_autoproto_options)
404     if heimdal_autoproto_private is not None:
405         bld.HEIMDAL_AUTOPROTO_PRIVATE(heimdal_autoproto_private, source)
406     if autoproto is not None:
407         bld.SAMBA_AUTOPROTO(autoproto, source + ' ' + autoproto_extra_source)
408     if public_headers is not None:
409         bld.PUBLIC_HEADERS(public_headers, header_path=header_path)
410     return t
411
412
413 Build.BuildContext.SAMBA_SUBSYSTEM = SAMBA_SUBSYSTEM
414
415
416 def SAMBA_GENERATOR(bld, name, rule, source='', target='',
417                     group='generators', enabled=True,
418                     public_headers=None,
419                     header_path=None,
420                     vars=None):
421     '''A generic source generator target'''
422
423     if not SET_TARGET_TYPE(bld, name, 'GENERATOR'):
424         return
425
426     if not enabled:
427         return
428
429     bld.SET_BUILD_GROUP(group)
430     t = bld(
431         rule=rule,
432         source=bld.EXPAND_VARIABLES(source, vars=vars),
433         target=target,
434         shell=isinstance(rule, str),
435         on_results=True,
436         before='cc',
437         ext_out='.c',
438         name=name)
439
440     if public_headers is not None:
441         bld.PUBLIC_HEADERS(public_headers, header_path=header_path)
442     return t
443 Build.BuildContext.SAMBA_GENERATOR = SAMBA_GENERATOR
444
445
446
447 @runonce
448 def SETUP_BUILD_GROUPS(bld):
449     '''setup build groups used to ensure that the different build
450     phases happen consecutively'''
451     bld.p_ln = bld.srcnode # we do want to see all targets!
452     bld.env['USING_BUILD_GROUPS'] = True
453     bld.add_group('setup')
454     bld.add_group('build_compiler_source')
455     bld.add_group('base_libraries')
456     bld.add_group('generators')
457     bld.add_group('compiler_prototypes')
458     bld.add_group('compiler_libraries')
459     bld.add_group('build_compilers')
460     bld.add_group('build_source')
461     bld.add_group('prototypes')
462     bld.add_group('main')
463     bld.add_group('binaries')
464     bld.add_group('final')
465 Build.BuildContext.SETUP_BUILD_GROUPS = SETUP_BUILD_GROUPS
466
467
468 def SET_BUILD_GROUP(bld, group):
469     '''set the current build group'''
470     if not 'USING_BUILD_GROUPS' in bld.env:
471         return
472     bld.set_group(group)
473 Build.BuildContext.SET_BUILD_GROUP = SET_BUILD_GROUP
474
475
476
477 @conf
478 def ENABLE_TIMESTAMP_DEPENDENCIES(conf):
479     """use timestamps instead of file contents for deps
480     this currently doesn't work"""
481     def h_file(filename):
482         import stat
483         st = os.stat(filename)
484         if stat.S_ISDIR(st[stat.ST_MODE]): raise IOError('not a file')
485         m = Utils.md5()
486         m.update(str(st.st_mtime))
487         m.update(str(st.st_size))
488         m.update(filename)
489         return m.digest()
490     Utils.h_file = h_file
491
492
493
494 t = Task.simple_task_type('copy_script', 'rm -f ${LINK_TARGET} && ln -s ${SRC[0].abspath(env)} ${LINK_TARGET}',
495                           shell=True, color='PINK', ext_in='.bin')
496 t.quiet = True
497
498 @feature('copy_script')
499 @before('apply_link')
500 def copy_script(self):
501     tsk = self.create_task('copy_script', self.allnodes[0])
502     tsk.env.TARGET = self.target
503
504 def SAMBA_SCRIPT(bld, name, pattern, installdir, installname=None):
505     '''used to copy scripts from the source tree into the build directory
506        for use by selftest'''
507
508     source = bld.path.ant_glob(pattern)
509
510     bld.SET_BUILD_GROUP('build_source')
511     for s in TO_LIST(source):
512         iname = s
513         if installname != None:
514             iname = installname
515         target = os.path.join(installdir, iname)
516         tgtdir = os.path.dirname(os.path.join(bld.srcnode.abspath(bld.env), '..', target))
517         mkdir_p(tgtdir)
518         t = bld(features='copy_script',
519                 source       = s,
520                 target       = target,
521                 always       = True,
522                 install_path = None)
523         t.env.LINK_TARGET = target
524
525 Build.BuildContext.SAMBA_SCRIPT = SAMBA_SCRIPT
526
527
528 def install_file(bld, destdir, file, chmod=O644, flat=False,
529                  python_fixup=False, destname=None, base_name=None):
530     '''install a file'''
531     destdir = bld.EXPAND_VARIABLES(destdir)
532     if not destname:
533         destname = file
534         if flat:
535             destname = os.path.basename(destname)
536     dest = os.path.join(destdir, destname)
537     if python_fixup:
538         # fixup the python path it will use to find Samba modules
539         inst_file = file + '.inst'
540         bld.SAMBA_GENERATOR('python_%s' % destname,
541                             rule="sed 's|\(sys.path.insert.*\)bin/python\(.*\)$|\\1${PYTHONDIR}\\2|g' < ${SRC} > ${TGT}",
542                             source=file,
543                             target=inst_file)
544         file = inst_file
545     if base_name:
546         file = os.path.join(base_name, file)
547     bld.install_as(dest, file, chmod=chmod)
548
549
550 def INSTALL_FILES(bld, destdir, files, chmod=O644, flat=False,
551                   python_fixup=False, destname=None, base_name=None):
552     '''install a set of files'''
553     for f in TO_LIST(files):
554         install_file(bld, destdir, f, chmod=chmod, flat=flat,
555                      python_fixup=python_fixup, destname=destname,
556                      base_name=base_name)
557 Build.BuildContext.INSTALL_FILES = INSTALL_FILES
558
559
560 def INSTALL_WILDCARD(bld, destdir, pattern, chmod=O644, flat=False,
561                      python_fixup=False, exclude=None, trim_path=None):
562     '''install a set of files matching a wildcard pattern'''
563     files=TO_LIST(bld.path.ant_glob(pattern))
564     if trim_path:
565         files2 = []
566         for f in files:
567             files2.append(os_path_relpath(f, trim_path))
568         files = files2
569
570     if exclude:
571         for f in files[:]:
572             if fnmatch.fnmatch(f, exclude):
573                 files.remove(f)
574     INSTALL_FILES(bld, destdir, files, chmod=chmod, flat=flat,
575                   python_fixup=python_fixup, base_name=trim_path)
576 Build.BuildContext.INSTALL_WILDCARD = INSTALL_WILDCARD
577
578
579 def PUBLIC_HEADERS(bld, public_headers, header_path=None):
580     '''install some headers
581
582     header_path may either be a string that is added to the INCLUDEDIR,
583     or it can be a dictionary of wildcard patterns which map to destination
584     directories relative to INCLUDEDIR
585     '''
586     dest = '${INCLUDEDIR}'
587     if isinstance(header_path, str):
588         dest += '/' + header_path
589     for h in TO_LIST(public_headers):
590         hdest = dest
591         if isinstance(header_path, list):
592             for (p1, dir) in header_path:
593                 found_match=False
594                 lst = TO_LIST(p1)
595                 for p2 in lst:
596                     if fnmatch.fnmatch(h, p2):
597                         if dir:
598                             hdest = os.path.join(hdest, dir)
599                         found_match=True
600                         break
601                 if found_match: break
602         if h.find(':') != -1:
603             hs=h.split(':')
604             INSTALL_FILES(bld, hdest, hs[0], flat=True, destname=hs[1])
605         else:
606             INSTALL_FILES(bld, hdest, h, flat=True)
607 Build.BuildContext.PUBLIC_HEADERS = PUBLIC_HEADERS
608
609
610 def subst_at_vars(task):
611     '''substiture @VAR@ style variables in a file'''
612     src = task.inputs[0].srcpath(task.env)
613     tgt = task.outputs[0].bldpath(task.env)
614
615     f = open(src, 'r')
616     s = f.read()
617     f.close()
618     # split on the vars
619     a = re.split('(@\w+@)', s)
620     out = []
621     back_sub = [ ('PREFIX', '${prefix}'), ('EXEC_PREFIX', '${exec_prefix}')]
622     for v in a:
623         if re.match('@\w+@', v):
624             vname = v[1:-1]
625             if not vname in task.env and vname.upper() in task.env:
626                 vname = vname.upper()
627             if not vname in task.env:
628                 print("Unknown substitution %s in %s" % (v, task.name))
629                 raise
630             v = SUBST_VARS_RECURSIVE(task.env[vname], task.env)
631             # now we back substitute the allowed pc vars
632             for (b, m) in back_sub:
633                 s = task.env[b]
634                 if s == v[0:len(s)]:
635                     v = m + v[len(s):]
636         out.append(v)
637     contents = ''.join(out)
638     f = open(tgt, 'w')
639     s = f.write(contents)
640     f.close()
641     return 0
642
643
644
645 def PKG_CONFIG_FILES(bld, pc_files, vnum=None):
646     '''install some pkg_config pc files'''
647     dest = '${PKGCONFIGDIR}'
648     dest = bld.EXPAND_VARIABLES(dest)
649     for f in TO_LIST(pc_files):
650         base=os.path.basename(f)
651         t = bld.SAMBA_GENERATOR('PKGCONFIG_%s' % base,
652                                 rule=subst_at_vars,
653                                 source=f+'.in',
654                                 target=f)
655         if vnum:
656             t.env.PACKAGE_VERSION = vnum
657         INSTALL_FILES(bld, dest, f, flat=True, destname=base)
658 Build.BuildContext.PKG_CONFIG_FILES = PKG_CONFIG_FILES
659
660
661
662 #############################################################
663 # give a nicer display when building different types of files
664 def progress_display(self, msg, fname):
665     col1 = Logs.colors(self.color)
666     col2 = Logs.colors.NORMAL
667     total = self.position[1]
668     n = len(str(total))
669     fs = '[%%%dd/%%%dd] %s %%s%%s%%s\n' % (n, n, msg)
670     return fs % (self.position[0], self.position[1], col1, fname, col2)
671
672 def link_display(self):
673     if Options.options.progress_bar != 0:
674         return Task.Task.old_display(self)
675     fname = self.outputs[0].bldpath(self.env)
676     return progress_display(self, 'Linking', fname)
677 Task.TaskBase.classes['cc_link'].display = link_display
678
679 def samba_display(self):
680     if Options.options.progress_bar != 0:
681         return Task.Task.old_display(self)
682
683     targets    = LOCAL_CACHE(self, 'TARGET_TYPE')
684     if self.name in targets:
685         target_type = targets[self.name]
686         type_map = { 'GENERATOR' : 'Generating',
687                      'PROTOTYPE' : 'Generating'
688                      }
689         if target_type in type_map:
690             return progress_display(self, type_map[target_type], self.name)
691
692     fname = self.inputs[0].bldpath(self.env)
693     if fname[0:3] == '../':
694         fname = fname[3:]
695     ext_loc = fname.rfind('.')
696     if ext_loc == -1:
697         return Task.Task.old_display(self)
698     ext = fname[ext_loc:]
699
700     ext_map = { '.idl' : 'Compiling IDL',
701                 '.et'  : 'Compiling ERRTABLE',
702                 '.asn1': 'Compiling ASN1',
703                 '.c'   : 'Compiling' }
704     if ext in ext_map:
705         return progress_display(self, ext_map[ext], fname)
706     return Task.Task.old_display(self)
707
708 Task.TaskBase.classes['Task'].old_display = Task.TaskBase.classes['Task'].display
709 Task.TaskBase.classes['Task'].display = samba_display