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