waf: removed the module aliases code
[kai/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, Constants
5 from Configure import conf
6 from Logs import debug
7 from samba_utils import SUBST_VARS_RECURSIVE
8 TaskGen.task_gen.apply_verif = Utils.nada
9
10 # bring in the other samba modules
11 from samba_optimisation import *
12 from samba_utils import *
13 from samba_version import *
14 from samba_autoconf import *
15 from samba_patterns import *
16 from samba_pidl 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 samba_abi
24 import tru64cc
25 import irixcc
26 import generic_cc
27 import samba_dist
28 import samba_wildcard
29 import stale_files
30 import symbols
31
32 # some systems have broken threading in python
33 if os.environ.get('WAF_NOTHREADS') == '1':
34     import nothreads
35
36 LIB_PATH="shared"
37
38 os.putenv('PYTHONUNBUFFERED', '1')
39
40
41 if Constants.HEXVERSION < 0x105019:
42     Logs.error('''
43 Please use the version of waf that comes with Samba, not
44 a system installed version. See http://wiki.samba.org/index.php/Waf
45 for details.
46
47 Alternatively, please use ./autogen-waf.sh, and then
48 run ./configure and make as usual. That will call the right version of waf.
49 ''')
50     sys.exit(1)
51
52
53 @conf
54 def SAMBA_BUILD_ENV(conf):
55     '''create the samba build environment'''
56     conf.env.BUILD_DIRECTORY = conf.blddir
57     mkdir_p(os.path.join(conf.blddir, LIB_PATH))
58     mkdir_p(os.path.join(conf.blddir, "modules"))
59     mkdir_p(os.path.join(conf.blddir, 'python/samba/dcerpc'))
60     # this allows all of the bin/shared and bin/python targets
61     # to be expressed in terms of build directory paths
62     mkdir_p(os.path.join(conf.blddir, 'default'))
63     for p in ['python','shared', 'modules']:
64         link_target = os.path.join(conf.blddir, 'default/' + p)
65         if not os.path.lexists(link_target):
66             os.symlink('../' + p, link_target)
67
68     # get perl to put the blib files in the build directory
69     blib_bld = os.path.join(conf.blddir, 'default/pidl/blib')
70     blib_src = os.path.join(conf.srcdir, 'pidl/blib')
71     mkdir_p(blib_bld + '/man1')
72     mkdir_p(blib_bld + '/man3')
73     if os.path.islink(blib_src):
74         os.unlink(blib_src)
75     elif os.path.exists(blib_src):
76         shutil.rmtree(blib_src)
77
78
79 def ADD_INIT_FUNCTION(bld, subsystem, target, init_function):
80     '''add an init_function to the list for a subsystem'''
81     if init_function is None:
82         return
83     bld.ASSERT(subsystem is not None, "You must specify a subsystem for init_function '%s'" % init_function)
84     cache = LOCAL_CACHE(bld, 'INIT_FUNCTIONS')
85     if not subsystem in cache:
86         cache[subsystem] = []
87     cache[subsystem].append( { 'TARGET':target, 'INIT_FUNCTION':init_function } )
88 Build.BuildContext.ADD_INIT_FUNCTION = ADD_INIT_FUNCTION
89
90
91
92 #################################################################
93 def SAMBA_LIBRARY(bld, libname, source,
94                   deps='',
95                   public_deps='',
96                   includes='',
97                   public_headers=None,
98                   header_path=None,
99                   pc_files=None,
100                   vnum=None,
101                   soname=None,
102                   cflags='',
103                   external_library=False,
104                   realname=None,
105                   autoproto=None,
106                   group='libraries',
107                   depends_on='',
108                   local_include=True,
109                   vars=None,
110                   install_path=None,
111                   install=True,
112                   pyembed=False,
113                   pyext=False,
114                   target_type='LIBRARY',
115                   bundled_extension=True,
116                   link_name=None,
117                   abi_file=None,
118                   abi_match=None,
119                   hide_symbols=False,
120                   manpages=None,
121                   private_library=False,
122                   grouping_library=False,
123                   enabled=True):
124     '''define a Samba library'''
125
126     if not enabled:
127         SET_TARGET_TYPE(bld, libname, 'DISABLED')
128         return
129
130     source = bld.EXPAND_VARIABLES(source, vars=vars)
131
132     # remember empty libraries, so we can strip the dependencies
133     if ((source == '') or (source == [])) and deps == '' and public_deps == '':
134         SET_TARGET_TYPE(bld, libname, 'EMPTY')
135         return
136
137     if BUILTIN_LIBRARY(bld, libname):
138         obj_target = libname
139     else:
140         obj_target = libname + '.objlist'
141
142     if group == 'libraries':
143         subsystem_group = 'main'
144     else:
145         subsystem_group = group
146
147     # first create a target for building the object files for this library
148     # by separating in this way, we avoid recompiling the C files
149     # separately for the install library and the build library
150     bld.SAMBA_SUBSYSTEM(obj_target,
151                         source         = source,
152                         deps           = deps,
153                         public_deps    = public_deps,
154                         includes       = includes,
155                         public_headers = public_headers,
156                         header_path    = header_path,
157                         cflags         = cflags,
158                         group          = subsystem_group,
159                         autoproto      = autoproto,
160                         depends_on     = depends_on,
161                         hide_symbols   = hide_symbols,
162                         pyext          = pyext or (target_type == "PYTHON"),
163                         local_include  = local_include)
164
165     if BUILTIN_LIBRARY(bld, libname):
166         return
167
168     if not SET_TARGET_TYPE(bld, libname, target_type):
169         return
170
171     # the library itself will depend on that object target
172     deps += ' ' + public_deps
173     deps = TO_LIST(deps)
174     deps.append(obj_target)
175
176     realname = bld.map_shlib_extension(realname, python=(target_type=='PYTHON'))
177     link_name = bld.map_shlib_extension(link_name, python=(target_type=='PYTHON'))
178
179     # we don't want any public libraries without version numbers
180     if not private_library and vnum is None and soname is None and target_type != 'PYTHON' and not realname:
181         raise Utils.WafError("public library '%s' must have a vnum" % libname)
182
183     if target_type == 'PYTHON' or realname or not private_library:
184         bundled_name = libname.replace('_', '-')
185     else:
186         bundled_name = PRIVATE_NAME(bld, libname, bundled_extension, private_library)
187
188     if private_library:
189         if vnum:
190             Logs.error("vnum is invalid for private libraries")
191             sys.exit(1)
192         vnum = Utils.g_module.VERSION
193
194     features = 'cc cshlib symlink_lib install_lib'
195     if target_type == 'PYTHON':
196         features += ' pyext'
197     if pyext or pyembed:
198         # this is quite strange. we should add pyext feature for pyext
199         # but that breaks the build. This may be a bug in the waf python tool
200         features += ' pyembed'
201     if abi_file:
202         features += ' abi_check'
203
204     if abi_file:
205         abi_file = os.path.join(bld.curdir, abi_file)
206
207     bld.SET_BUILD_GROUP(group)
208     t = bld(
209         features        = features,
210         source          = [],
211         target          = bundled_name,
212         depends_on      = depends_on,
213         samba_deps      = deps,
214         samba_includes  = includes,
215         local_include   = local_include,
216         vnum            = vnum,
217         soname          = soname,
218         install_path    = None,
219         samba_inst_path = install_path,
220         name            = libname,
221         samba_realname  = realname,
222         samba_install   = install,
223         abi_file        = abi_file,
224         abi_match       = abi_match,
225         private_library = private_library,
226         grouping_library=grouping_library
227         )
228
229     if realname and not link_name:
230         link_name = 'shared/%s' % realname
231
232     if link_name:
233         t.link_name = link_name
234
235     if pc_files is not None:
236         bld.PKG_CONFIG_FILES(pc_files, vnum=vnum)
237
238     if manpages is not None and 'XSLTPROC_MANPAGES' in bld.env and bld.env['XSLTPROC_MANPAGES']:
239         bld.MANPAGES(manpages)
240
241
242 Build.BuildContext.SAMBA_LIBRARY = SAMBA_LIBRARY
243
244
245 #################################################################
246 def SAMBA_BINARY(bld, binname, source,
247                  deps='',
248                  includes='',
249                  public_headers=None,
250                  header_path=None,
251                  modules=None,
252                  ldflags=None,
253                  cflags='',
254                  autoproto=None,
255                  use_hostcc=False,
256                  use_global_deps=True,
257                  compiler=None,
258                  group='binaries',
259                  manpages=None,
260                  local_include=True,
261                  subsystem_name=None,
262                  pyembed=False,
263                  vars=None,
264                  install=True,
265                  install_path=None,
266                  enabled=True):
267     '''define a Samba binary'''
268
269     if not enabled:
270         SET_TARGET_TYPE(bld, binname, 'DISABLED')
271         return
272
273     if not SET_TARGET_TYPE(bld, binname, 'BINARY'):
274         return
275
276     features = 'cc cprogram symlink_bin install_bin'
277     if pyembed:
278         features += ' pyembed'
279
280     obj_target = binname + '.objlist'
281
282     source = bld.EXPAND_VARIABLES(source, vars=vars)
283     source = unique_list(TO_LIST(source))
284
285     if group == 'binaries':
286         subsystem_group = 'main'
287     else:
288         subsystem_group = group
289
290     # first create a target for building the object files for this binary
291     # by separating in this way, we avoid recompiling the C files
292     # separately for the install binary and the build binary
293     bld.SAMBA_SUBSYSTEM(obj_target,
294                         source         = source,
295                         deps           = deps,
296                         includes       = includes,
297                         cflags         = cflags,
298                         group          = subsystem_group,
299                         autoproto      = autoproto,
300                         subsystem_name = subsystem_name,
301                         local_include  = local_include,
302                         use_hostcc     = use_hostcc,
303                         pyext          = pyembed,
304                         use_global_deps= use_global_deps)
305
306     bld.SET_BUILD_GROUP(group)
307
308     # the binary itself will depend on that object target
309     deps = TO_LIST(deps)
310     deps.append(obj_target)
311
312     t = bld(
313         features       = features,
314         source         = [],
315         target         = binname,
316         samba_deps     = deps,
317         samba_includes = includes,
318         local_include  = local_include,
319         samba_modules  = modules,
320         top            = True,
321         samba_subsystem= subsystem_name,
322         install_path   = None,
323         samba_inst_path= install_path,
324         samba_install  = install
325         )
326
327     if manpages is not None and 'XSLTPROC_MANPAGES' in bld.env and bld.env['XSLTPROC_MANPAGES']:
328         bld.MANPAGES(manpages)
329
330 Build.BuildContext.SAMBA_BINARY = SAMBA_BINARY
331
332
333 #################################################################
334 def SAMBA_MODULE(bld, modname, source,
335                  deps='',
336                  includes='',
337                  subsystem=None,
338                  init_function=None,
339                  module_init_name='samba_init_module',
340                  autoproto=None,
341                  autoproto_extra_source='',
342                  cflags='',
343                  internal_module=True,
344                  local_include=True,
345                  vars=None,
346                  enabled=True,
347                  pyembed=True,
348                  ):
349     '''define a Samba module.'''
350
351     source = bld.EXPAND_VARIABLES(source, vars=vars)
352
353     if internal_module or BUILTIN_LIBRARY(bld, modname):
354         bld.SAMBA_SUBSYSTEM(modname, source,
355                     deps=deps,
356                     includes=includes,
357                     autoproto=autoproto,
358                     autoproto_extra_source=autoproto_extra_source,
359                     cflags=cflags,
360                     local_include=local_include,
361                     enabled=enabled)
362
363         bld.ADD_INIT_FUNCTION(subsystem, modname, init_function)
364         return
365
366     if not enabled:
367         SET_TARGET_TYPE(bld, modname, 'DISABLED')
368         return
369
370     obj_target = modname + '.objlist'
371
372     realname = modname
373     if subsystem is not None:
374         deps += ' ' + subsystem
375         while realname.startswith("lib"+subsystem+"_"):
376             realname = realname[len("lib"+subsystem+"_"):]
377         while realname.startswith(subsystem+"_"):
378             realname = realname[len(subsystem+"_"):]
379
380     realname = bld.make_libname(realname)
381     while realname.startswith("lib"):
382         realname = realname[len("lib"):]
383
384     build_link_name = "modules/%s/%s" % (subsystem, realname)
385
386     if init_function:
387         cflags += " -D%s=%s" % (init_function, module_init_name)
388
389     bld.SAMBA_LIBRARY(modname,
390                       source,
391                       deps=deps,
392                       cflags=cflags,
393                       realname = realname,
394                       autoproto = autoproto,
395                       local_include=local_include,
396                       vars=vars,
397                       link_name=build_link_name,
398                       install_path="${MODULESDIR}/%s" % subsystem,
399                       pyembed=pyembed,
400                       )
401
402
403 Build.BuildContext.SAMBA_MODULE = SAMBA_MODULE
404
405
406 #################################################################
407 def SAMBA_SUBSYSTEM(bld, modname, source,
408                     deps='',
409                     public_deps='',
410                     includes='',
411                     public_headers=None,
412                     header_path=None,
413                     cflags='',
414                     cflags_end=None,
415                     group='main',
416                     init_function_sentinal=None,
417                     autoproto=None,
418                     autoproto_extra_source='',
419                     depends_on='',
420                     local_include=True,
421                     local_include_first=True,
422                     subsystem_name=None,
423                     enabled=True,
424                     use_hostcc=False,
425                     use_global_deps=True,
426                     vars=None,
427                     hide_symbols=False,
428                     pyext=False):
429     '''define a Samba subsystem'''
430
431     if not enabled:
432         SET_TARGET_TYPE(bld, modname, 'DISABLED')
433         return
434
435     # remember empty subsystems, so we can strip the dependencies
436     if ((source == '') or (source == [])) and deps == '' and public_deps == '':
437         SET_TARGET_TYPE(bld, modname, 'EMPTY')
438         return
439
440     if not SET_TARGET_TYPE(bld, modname, 'SUBSYSTEM'):
441         return
442
443     source = bld.EXPAND_VARIABLES(source, vars=vars)
444     source = unique_list(TO_LIST(source))
445
446     deps += ' ' + public_deps
447
448     bld.SET_BUILD_GROUP(group)
449
450     features = 'cc'
451     if pyext:
452         features += ' pyext'
453
454     t = bld(
455         features       = features,
456         source         = source,
457         target         = modname,
458         samba_cflags   = CURRENT_CFLAGS(bld, modname, cflags, hide_symbols=hide_symbols),
459         depends_on     = depends_on,
460         samba_deps     = TO_LIST(deps),
461         samba_includes = includes,
462         local_include  = local_include,
463         local_include_first  = local_include_first,
464         samba_subsystem= subsystem_name,
465         samba_use_hostcc = use_hostcc,
466         samba_use_global_deps = use_global_deps
467         )
468
469     if cflags_end is not None:
470         t.samba_cflags.extend(TO_LIST(cflags_end))
471
472     if autoproto is not None:
473         bld.SAMBA_AUTOPROTO(autoproto, source + TO_LIST(autoproto_extra_source))
474     if public_headers is not None:
475         bld.PUBLIC_HEADERS(public_headers, header_path=header_path)
476     return t
477
478
479 Build.BuildContext.SAMBA_SUBSYSTEM = SAMBA_SUBSYSTEM
480
481
482 def SAMBA_GENERATOR(bld, name, rule, source='', target='',
483                     group='generators', enabled=True,
484                     public_headers=None,
485                     header_path=None,
486                     vars=None,
487                     always=False):
488     '''A generic source generator target'''
489
490     if not SET_TARGET_TYPE(bld, name, 'GENERATOR'):
491         return
492
493     if not enabled:
494         return
495
496     bld.SET_BUILD_GROUP(group)
497     t = bld(
498         rule=rule,
499         source=bld.EXPAND_VARIABLES(source, vars=vars),
500         target=target,
501         shell=isinstance(rule, str),
502         on_results=True,
503         before='cc',
504         ext_out='.c',
505         name=name)
506
507     if always:
508         t.always = True
509
510     if public_headers is not None:
511         bld.PUBLIC_HEADERS(public_headers, header_path=header_path)
512     return t
513 Build.BuildContext.SAMBA_GENERATOR = SAMBA_GENERATOR
514
515
516
517 @runonce
518 def SETUP_BUILD_GROUPS(bld):
519     '''setup build groups used to ensure that the different build
520     phases happen consecutively'''
521     bld.p_ln = bld.srcnode # we do want to see all targets!
522     bld.env['USING_BUILD_GROUPS'] = True
523     bld.add_group('setup')
524     bld.add_group('build_compiler_source')
525     bld.add_group('base_libraries')
526     bld.add_group('generators')
527     bld.add_group('compiler_prototypes')
528     bld.add_group('compiler_libraries')
529     bld.add_group('build_compilers')
530     bld.add_group('build_source')
531     bld.add_group('prototypes')
532     bld.add_group('main')
533     bld.add_group('symbolcheck')
534     bld.add_group('libraries')
535     bld.add_group('binaries')
536     bld.add_group('syslibcheck')
537     bld.add_group('final')
538 Build.BuildContext.SETUP_BUILD_GROUPS = SETUP_BUILD_GROUPS
539
540
541 def SET_BUILD_GROUP(bld, group):
542     '''set the current build group'''
543     if not 'USING_BUILD_GROUPS' in bld.env:
544         return
545     bld.set_group(group)
546 Build.BuildContext.SET_BUILD_GROUP = SET_BUILD_GROUP
547
548
549
550 @conf
551 def ENABLE_TIMESTAMP_DEPENDENCIES(conf):
552     """use timestamps instead of file contents for deps
553     this currently doesn't work"""
554     def h_file(filename):
555         import stat
556         st = os.stat(filename)
557         if stat.S_ISDIR(st[stat.ST_MODE]): raise IOError('not a file')
558         m = Utils.md5()
559         m.update(str(st.st_mtime))
560         m.update(str(st.st_size))
561         m.update(filename)
562         return m.digest()
563     Utils.h_file = h_file
564
565
566
567 t = Task.simple_task_type('copy_script', 'rm -f ${LINK_TARGET} && ln -s ${SRC[0].abspath(env)} ${LINK_TARGET}',
568                           shell=True, color='PINK', ext_in='.bin')
569 t.quiet = True
570
571 @feature('copy_script')
572 @before('apply_link')
573 def copy_script(self):
574     tsk = self.create_task('copy_script', self.allnodes[0])
575     tsk.env.TARGET = self.target
576
577 def SAMBA_SCRIPT(bld, name, pattern, installdir, installname=None):
578     '''used to copy scripts from the source tree into the build directory
579        for use by selftest'''
580
581     source = bld.path.ant_glob(pattern)
582
583     bld.SET_BUILD_GROUP('build_source')
584     for s in TO_LIST(source):
585         iname = s
586         if installname != None:
587             iname = installname
588         target = os.path.join(installdir, iname)
589         tgtdir = os.path.dirname(os.path.join(bld.srcnode.abspath(bld.env), '..', target))
590         mkdir_p(tgtdir)
591         t = bld(features='copy_script',
592                 source       = s,
593                 target       = target,
594                 always       = True,
595                 install_path = None)
596         t.env.LINK_TARGET = target
597
598 Build.BuildContext.SAMBA_SCRIPT = SAMBA_SCRIPT
599
600
601 def install_file(bld, destdir, file, chmod=MODE_644, flat=False,
602                  python_fixup=False, destname=None, base_name=None):
603     '''install a file'''
604     destdir = bld.EXPAND_VARIABLES(destdir)
605     if not destname:
606         destname = file
607         if flat:
608             destname = os.path.basename(destname)
609     dest = os.path.join(destdir, destname)
610     if python_fixup:
611         # fixup the python path it will use to find Samba modules
612         inst_file = file + '.inst'
613         if bld.env["PYTHONDIR"] not in sys.path:
614             regex = "s|\(sys.path.insert.*\)bin/python\(.*\)$|\\1${PYTHONDIR}\\2|g"
615         else:
616             # Eliminate updating sys.path if the target python dir is already
617             # in python path.
618             regex = "s|sys.path.insert.*bin/python.*$||g"
619         bld.SAMBA_GENERATOR('python_%s' % destname,
620                             rule="sed '%s' < ${SRC} > ${TGT}" % regex,
621                             source=file,
622                             target=inst_file)
623         file = inst_file
624     if base_name:
625         file = os.path.join(base_name, file)
626     bld.install_as(dest, file, chmod=chmod)
627
628
629 def INSTALL_FILES(bld, destdir, files, chmod=MODE_644, flat=False,
630                   python_fixup=False, destname=None, base_name=None):
631     '''install a set of files'''
632     for f in TO_LIST(files):
633         install_file(bld, destdir, f, chmod=chmod, flat=flat,
634                      python_fixup=python_fixup, destname=destname,
635                      base_name=base_name)
636 Build.BuildContext.INSTALL_FILES = INSTALL_FILES
637
638
639 def INSTALL_WILDCARD(bld, destdir, pattern, chmod=MODE_644, flat=False,
640                      python_fixup=False, exclude=None, trim_path=None):
641     '''install a set of files matching a wildcard pattern'''
642     files=TO_LIST(bld.path.ant_glob(pattern))
643     if trim_path:
644         files2 = []
645         for f in files:
646             files2.append(os_path_relpath(f, trim_path))
647         files = files2
648
649     if exclude:
650         for f in files[:]:
651             if fnmatch.fnmatch(f, exclude):
652                 files.remove(f)
653     INSTALL_FILES(bld, destdir, files, chmod=chmod, flat=flat,
654                   python_fixup=python_fixup, base_name=trim_path)
655 Build.BuildContext.INSTALL_WILDCARD = INSTALL_WILDCARD
656
657
658 def INSTALL_DIRS(bld, destdir, dirs):
659     '''install a set of directories'''
660     destdir = bld.EXPAND_VARIABLES(destdir)
661     dirs = bld.EXPAND_VARIABLES(dirs)
662     for d in TO_LIST(dirs):
663         bld.install_dir(os.path.join(destdir, d))
664 Build.BuildContext.INSTALL_DIRS = INSTALL_DIRS
665
666
667 re_header = re.compile('#include[ \t]*"([^"]+)"', re.I | re.M)
668 class header_task(Task.Task):
669     """
670     The public headers (the one installed on the system) have both
671     different paths and contents, so the rename is not enough.
672
673     Intermediate .inst.h files are created because path manipulation
674     may be slow. The substitution is thus performed only once.
675     """
676
677     name = 'header'
678     color = 'PINK'
679     vars = ['INCLUDEDIR', 'HEADER_DEPS']
680
681     def run(self):
682         txt = self.inputs[0].read(self.env)
683
684         # hard-coded string, but only present in samba4 (I promise, you won't feel a thing)
685         txt = txt.replace('#if _SAMBA_BUILD_ == 4', '#if 1\n')
686
687         # use a regexp to substitute the #include lines in the files
688         map = self.generator.bld.hnodemap
689         dirnodes = self.generator.bld.hnodedirs
690         def repl(m):
691             if m.group(1):
692                 s = m.group(1)
693
694                 # pokemon headers: gotta catch'em all!
695                 fin = s
696                 if s.startswith('bin/default'):
697                     node = self.generator.bld.srcnode.find_resource(s.replace('bin/default/', ''))
698                     if not node:
699                         Logs.warn('could not find the public header for %r' % s)
700                     elif node.id in map:
701                         fin = map[node.id]
702                     else:
703                         Logs.warn('could not find the public header replacement for build header %r' % s)
704                 else:
705                     # this part is more difficult since the path may be relative to anything
706                     for dirnode in dirnodes:
707                         node = dirnode.find_resource(s)
708                         if node:
709                              if node.id in map:
710                                  fin = map[node.id]
711                                  break
712                              else:
713                                  Logs.warn('could not find the public header replacement for source header %r %r' % (s, node))
714                     else:
715                         Logs.warn('-> could not find the public header for %r' % s)
716
717                 return "#include <%s>" % fin
718             return ''
719
720         txt = re_header.sub(repl, txt)
721
722         # and write the output file
723         f = None
724         try:
725             f = open(self.outputs[0].abspath(self.env), 'w')
726             f.write(txt)
727         finally:
728             if f:
729                 f.close()
730
731 @TaskGen.feature('pubh')
732 def make_public_headers(self):
733     """
734     collect the public headers to process and to install, then
735     create the substitutions (name and contents)
736     """
737
738     if not self.bld.is_install:
739         # install time only (lazy)
740         return
741
742     # keep two variables
743     #    hnodedirs: list of folders for searching the headers
744     #    hnodemap: node ids and replacement string (node objects are unique)
745     try:
746         self.bld.hnodedirs.append(self.path)
747     except AttributeError:
748         self.bld.hnodemap = {}
749         self.bld.hnodedirs = [self.bld.srcnode, self.path]
750
751         for k in 'source4 source4/include lib/talloc lib/tevent/ source4/lib/ldb/include/'.split():
752             node = self.bld.srcnode.find_dir(k)
753             if node:
754                 self.bld.hnodedirs.append(node)
755
756     header_path = getattr(self, 'header_path', None) or ''
757
758     for x in self.to_list(self.headers):
759
760         # too complicated, but what was the original idea?
761         if isinstance(header_path, list):
762             add_dir = ''
763             for (p1, dir) in header_path:
764                 lst = self.to_list(p1)
765                 for p2 in lst:
766                     if fnmatch.fnmatch(x, p2):
767                         add_dir = dir
768                         break
769                 else:
770                     continue
771                 break
772             inst_path = add_dir
773         else:
774             inst_path = header_path
775
776         dest = ''
777         name = x
778         if x.find(':') != -1:
779             s = x.split(':')
780             name = s[0]
781             dest = s[1]
782
783         inn = self.path.find_resource(name)
784
785         if not inn:
786             raise ValueError("could not find the public header %r in %r" % (name, self.path))
787         out = inn.change_ext('.inst.h')
788         self.create_task('header', inn, out)
789
790         if not dest:
791             dest = inn.name
792
793         if inst_path:
794             inst_path = inst_path + '/'
795         inst_path = inst_path + dest
796
797         self.bld.install_as('${INCLUDEDIR}/%s' % inst_path, out, self.env)
798
799         self.bld.hnodemap[inn.id] = inst_path
800
801     # create a hash (not md5) to make sure the headers are re-created if something changes
802     val = 0
803     lst = list(self.bld.hnodemap.keys())
804     lst.sort()
805     for k in lst:
806         val = hash((val, k, self.bld.hnodemap[k]))
807     self.bld.env.HEADER_DEPS = val
808
809 def PUBLIC_HEADERS(bld, public_headers, header_path=None):
810     '''install some headers
811
812     header_path may either be a string that is added to the INCLUDEDIR,
813     or it can be a dictionary of wildcard patterns which map to destination
814     directories relative to INCLUDEDIR
815     '''
816     bld.SET_BUILD_GROUP('final')
817     ret = bld(features=['pubh'], headers=public_headers, header_path=header_path)
818     return ret
819 Build.BuildContext.PUBLIC_HEADERS = PUBLIC_HEADERS
820
821
822 def subst_at_vars(task):
823     '''substiture @VAR@ style variables in a file'''
824     src = task.inputs[0].srcpath(task.env)
825     tgt = task.outputs[0].bldpath(task.env)
826
827     f = open(src, 'r')
828     s = f.read()
829     f.close()
830     # split on the vars
831     a = re.split('(@\w+@)', s)
832     out = []
833     done_var = {}
834     back_sub = [ ('PREFIX', '${prefix}'), ('EXEC_PREFIX', '${exec_prefix}')]
835     for v in a:
836         if re.match('@\w+@', v):
837             vname = v[1:-1]
838             if not vname in task.env and vname.upper() in task.env:
839                 vname = vname.upper()
840             if not vname in task.env:
841                 Logs.error("Unknown substitution %s in %s" % (v, task.name))
842                 sys.exit(1)
843             v = SUBST_VARS_RECURSIVE(task.env[vname], task.env)
844             # now we back substitute the allowed pc vars
845             for (b, m) in back_sub:
846                 s = task.env[b]
847                 if s == v[0:len(s)]:
848                     if not b in done_var:
849                         # we don't want to substitute the first usage
850                         done_var[b] = True
851                     else:
852                         v = m + v[len(s):]
853                     break
854         out.append(v)
855     contents = ''.join(out)
856     f = open(tgt, 'w')
857     s = f.write(contents)
858     f.close()
859     return 0
860
861
862 def PKG_CONFIG_FILES(bld, pc_files, vnum=None):
863     '''install some pkg_config pc files'''
864     dest = '${PKGCONFIGDIR}'
865     dest = bld.EXPAND_VARIABLES(dest)
866     for f in TO_LIST(pc_files):
867         base=os.path.basename(f)
868         t = bld.SAMBA_GENERATOR('PKGCONFIG_%s' % base,
869                                 rule=subst_at_vars,
870                                 source=f+'.in',
871                                 target=f)
872         if vnum:
873             t.env.PACKAGE_VERSION = vnum
874         INSTALL_FILES(bld, dest, f, flat=True, destname=base)
875 Build.BuildContext.PKG_CONFIG_FILES = PKG_CONFIG_FILES
876
877
878 def MANPAGES(bld, manpages):
879     '''build and install manual pages'''
880     bld.env.MAN_XSL = 'http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl'
881     for m in manpages.split():
882         source = m + '.xml'
883         bld.SAMBA_GENERATOR(m,
884                             source=source,
885                             target=m,
886                             group='final',
887                             rule='${XSLTPROC} -o ${TGT} --nonet ${MAN_XSL} ${SRC}'
888                             )
889         bld.INSTALL_FILES('${MANDIR}/man%s' % m[-1], m, flat=True)
890 Build.BuildContext.MANPAGES = MANPAGES
891
892
893 #############################################################
894 # give a nicer display when building different types of files
895 def progress_display(self, msg, fname):
896     col1 = Logs.colors(self.color)
897     col2 = Logs.colors.NORMAL
898     total = self.position[1]
899     n = len(str(total))
900     fs = '[%%%dd/%%%dd] %s %%s%%s%%s\n' % (n, n, msg)
901     return fs % (self.position[0], self.position[1], col1, fname, col2)
902
903 def link_display(self):
904     if Options.options.progress_bar != 0:
905         return Task.Task.old_display(self)
906     fname = self.outputs[0].bldpath(self.env)
907     return progress_display(self, 'Linking', fname)
908 Task.TaskBase.classes['cc_link'].display = link_display
909
910 def samba_display(self):
911     if Options.options.progress_bar != 0:
912         return Task.Task.old_display(self)
913
914     targets    = LOCAL_CACHE(self, 'TARGET_TYPE')
915     if self.name in targets:
916         target_type = targets[self.name]
917         type_map = { 'GENERATOR' : 'Generating',
918                      'PROTOTYPE' : 'Generating'
919                      }
920         if target_type in type_map:
921             return progress_display(self, type_map[target_type], self.name)
922
923     if len(self.inputs) == 0:
924         return Task.Task.old_display(self)
925
926     fname = self.inputs[0].bldpath(self.env)
927     if fname[0:3] == '../':
928         fname = fname[3:]
929     ext_loc = fname.rfind('.')
930     if ext_loc == -1:
931         return Task.Task.old_display(self)
932     ext = fname[ext_loc:]
933
934     ext_map = { '.idl' : 'Compiling IDL',
935                 '.et'  : 'Compiling ERRTABLE',
936                 '.asn1': 'Compiling ASN1',
937                 '.c'   : 'Compiling' }
938     if ext in ext_map:
939         return progress_display(self, ext_map[ext], fname)
940     return Task.Task.old_display(self)
941
942 Task.TaskBase.classes['Task'].old_display = Task.TaskBase.classes['Task'].display
943 Task.TaskBase.classes['Task'].display = samba_display
944
945
946 @after('apply_link')
947 @feature('cshlib')
948 def apply_bundle_remove_dynamiclib_patch(self):
949     if self.env['MACBUNDLE'] or getattr(self,'mac_bundle',False):
950         if not getattr(self,'vnum',None):
951             try:
952                 self.env['LINKFLAGS'].remove('-dynamiclib')
953                 self.env['LINKFLAGS'].remove('-single_module')
954             except ValueError:
955                 pass