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