buildtools: prefix the private symbol namespaces with PRIVATE_
[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 = "PRIVATE_%s_%s" % (Utils.g_module.APPNAME, Utils.g_module.VERSION)
209             vscript_abi_directory = None
210         elif vnum:
211             version = "%s_%s" % (libname, vnum)
212             vscript_abi_directory = abi_directory
213         else:
214             version = None
215             vscript_abi_directory = None
216         if version:
217             bld.ABI_VSCRIPT(libname, vscript_abi_directory, version, vscript)
218             ldflags.append("-Wl,--version-script=%s/%s" % (bld.path.abspath(bld.env), vscript))
219             fullname = bld.env.shlib_PATTERN % bundled_name
220             bld.add_manual_dependency(bld.path.find_or_declare(fullname), bld.path.find_or_declare(vscript))
221
222     bld.SET_BUILD_GROUP(group)
223     t = bld(
224         features        = features,
225         source          = [],
226         target          = bundled_name,
227         depends_on      = depends_on,
228         ldflags         = ldflags,
229         samba_deps      = deps,
230         samba_includes  = includes,
231         local_include   = local_include,
232         vnum            = vnum,
233         soname          = soname,
234         install_path    = None,
235         samba_inst_path = install_path,
236         name            = libname,
237         samba_realname  = realname,
238         samba_install   = install,
239         abi_directory   = "%s/%s" % (bld.path.abspath(), abi_directory),
240         abi_match       = abi_match,
241         private_library = private_library,
242         grouping_library=grouping_library
243         )
244
245     if realname and not link_name:
246         link_name = 'shared/%s' % realname
247
248     if link_name:
249         t.link_name = link_name
250
251     if pc_files is not None:
252         bld.PKG_CONFIG_FILES(pc_files, vnum=vnum)
253
254     if manpages is not None and 'XSLTPROC_MANPAGES' in bld.env and bld.env['XSLTPROC_MANPAGES']:
255         bld.MANPAGES(manpages)
256
257
258 Build.BuildContext.SAMBA_LIBRARY = SAMBA_LIBRARY
259
260
261 #################################################################
262 def SAMBA_BINARY(bld, binname, source,
263                  deps='',
264                  includes='',
265                  public_headers=None,
266                  header_path=None,
267                  modules=None,
268                  ldflags=None,
269                  cflags='',
270                  autoproto=None,
271                  use_hostcc=False,
272                  use_global_deps=True,
273                  compiler=None,
274                  group='binaries',
275                  manpages=None,
276                  local_include=True,
277                  subsystem_name=None,
278                  pyembed=False,
279                  vars=None,
280                  install=True,
281                  install_path=None,
282                  enabled=True):
283     '''define a Samba binary'''
284
285     if not enabled:
286         SET_TARGET_TYPE(bld, binname, 'DISABLED')
287         return
288
289     if not SET_TARGET_TYPE(bld, binname, 'BINARY'):
290         return
291
292     features = 'cc cprogram symlink_bin install_bin'
293     if pyembed:
294         features += ' pyembed'
295
296     obj_target = binname + '.objlist'
297
298     source = bld.EXPAND_VARIABLES(source, vars=vars)
299     source = unique_list(TO_LIST(source))
300
301     if group == 'binaries':
302         subsystem_group = 'main'
303     else:
304         subsystem_group = group
305
306     # first create a target for building the object files for this binary
307     # by separating in this way, we avoid recompiling the C files
308     # separately for the install binary and the build binary
309     bld.SAMBA_SUBSYSTEM(obj_target,
310                         source         = source,
311                         deps           = deps,
312                         includes       = includes,
313                         cflags         = cflags,
314                         group          = subsystem_group,
315                         autoproto      = autoproto,
316                         subsystem_name = subsystem_name,
317                         local_include  = local_include,
318                         use_hostcc     = use_hostcc,
319                         pyext          = pyembed,
320                         use_global_deps= use_global_deps)
321
322     bld.SET_BUILD_GROUP(group)
323
324     # the binary itself will depend on that object target
325     deps = TO_LIST(deps)
326     deps.append(obj_target)
327
328     t = bld(
329         features       = features,
330         source         = [],
331         target         = binname,
332         samba_deps     = deps,
333         samba_includes = includes,
334         local_include  = local_include,
335         samba_modules  = modules,
336         top            = True,
337         samba_subsystem= subsystem_name,
338         install_path   = None,
339         samba_inst_path= install_path,
340         samba_install  = install
341         )
342
343     if manpages is not None and 'XSLTPROC_MANPAGES' in bld.env and bld.env['XSLTPROC_MANPAGES']:
344         bld.MANPAGES(manpages)
345
346 Build.BuildContext.SAMBA_BINARY = SAMBA_BINARY
347
348
349 #################################################################
350 def SAMBA_MODULE(bld, modname, source,
351                  deps='',
352                  includes='',
353                  subsystem=None,
354                  init_function=None,
355                  module_init_name='samba_init_module',
356                  autoproto=None,
357                  autoproto_extra_source='',
358                  cflags='',
359                  internal_module=True,
360                  local_include=True,
361                  vars=None,
362                  enabled=True,
363                  pyembed=True,
364                  ):
365     '''define a Samba module.'''
366
367     source = bld.EXPAND_VARIABLES(source, vars=vars)
368
369     if internal_module or BUILTIN_LIBRARY(bld, modname):
370         bld.SAMBA_SUBSYSTEM(modname, source,
371                     deps=deps,
372                     includes=includes,
373                     autoproto=autoproto,
374                     autoproto_extra_source=autoproto_extra_source,
375                     cflags=cflags,
376                     local_include=local_include,
377                     enabled=enabled)
378
379         bld.ADD_INIT_FUNCTION(subsystem, modname, init_function)
380         return
381
382     if not enabled:
383         SET_TARGET_TYPE(bld, modname, 'DISABLED')
384         return
385
386     obj_target = modname + '.objlist'
387
388     realname = modname
389     if subsystem is not None:
390         deps += ' ' + subsystem
391         while realname.startswith("lib"+subsystem+"_"):
392             realname = realname[len("lib"+subsystem+"_"):]
393         while realname.startswith(subsystem+"_"):
394             realname = realname[len(subsystem+"_"):]
395
396     realname = bld.make_libname(realname)
397     while realname.startswith("lib"):
398         realname = realname[len("lib"):]
399
400     build_link_name = "modules/%s/%s" % (subsystem, realname)
401
402     if init_function:
403         cflags += " -D%s=%s" % (init_function, module_init_name)
404
405     bld.SAMBA_LIBRARY(modname,
406                       source,
407                       deps=deps,
408                       cflags=cflags,
409                       realname = realname,
410                       autoproto = autoproto,
411                       local_include=local_include,
412                       vars=vars,
413                       link_name=build_link_name,
414                       install_path="${MODULESDIR}/%s" % subsystem,
415                       pyembed=pyembed,
416                       )
417
418
419 Build.BuildContext.SAMBA_MODULE = SAMBA_MODULE
420
421
422 #################################################################
423 def SAMBA_SUBSYSTEM(bld, modname, source,
424                     deps='',
425                     public_deps='',
426                     includes='',
427                     public_headers=None,
428                     header_path=None,
429                     cflags='',
430                     cflags_end=None,
431                     group='main',
432                     init_function_sentinal=None,
433                     autoproto=None,
434                     autoproto_extra_source='',
435                     depends_on='',
436                     local_include=True,
437                     local_include_first=True,
438                     subsystem_name=None,
439                     enabled=True,
440                     use_hostcc=False,
441                     use_global_deps=True,
442                     vars=None,
443                     hide_symbols=False,
444                     pyext=False):
445     '''define a Samba subsystem'''
446
447     if not enabled:
448         SET_TARGET_TYPE(bld, modname, 'DISABLED')
449         return
450
451     # remember empty subsystems, so we can strip the dependencies
452     if ((source == '') or (source == [])) and deps == '' and public_deps == '':
453         SET_TARGET_TYPE(bld, modname, 'EMPTY')
454         return
455
456     if not SET_TARGET_TYPE(bld, modname, 'SUBSYSTEM'):
457         return
458
459     source = bld.EXPAND_VARIABLES(source, vars=vars)
460     source = unique_list(TO_LIST(source))
461
462     deps += ' ' + public_deps
463
464     bld.SET_BUILD_GROUP(group)
465
466     features = 'cc'
467     if pyext:
468         features += ' pyext'
469
470     t = bld(
471         features       = features,
472         source         = source,
473         target         = modname,
474         samba_cflags   = CURRENT_CFLAGS(bld, modname, cflags, hide_symbols=hide_symbols),
475         depends_on     = depends_on,
476         samba_deps     = TO_LIST(deps),
477         samba_includes = includes,
478         local_include  = local_include,
479         local_include_first  = local_include_first,
480         samba_subsystem= subsystem_name,
481         samba_use_hostcc = use_hostcc,
482         samba_use_global_deps = use_global_deps
483         )
484
485     if cflags_end is not None:
486         t.samba_cflags.extend(TO_LIST(cflags_end))
487
488     if autoproto is not None:
489         bld.SAMBA_AUTOPROTO(autoproto, source + TO_LIST(autoproto_extra_source))
490     if public_headers is not None:
491         bld.PUBLIC_HEADERS(public_headers, header_path=header_path)
492     return t
493
494
495 Build.BuildContext.SAMBA_SUBSYSTEM = SAMBA_SUBSYSTEM
496
497
498 def SAMBA_GENERATOR(bld, name, rule, source='', target='',
499                     group='generators', enabled=True,
500                     public_headers=None,
501                     header_path=None,
502                     vars=None,
503                     always=False):
504     '''A generic source generator target'''
505
506     if not SET_TARGET_TYPE(bld, name, 'GENERATOR'):
507         return
508
509     if not enabled:
510         return
511
512     bld.SET_BUILD_GROUP(group)
513     t = bld(
514         rule=rule,
515         source=bld.EXPAND_VARIABLES(source, vars=vars),
516         target=target,
517         shell=isinstance(rule, str),
518         on_results=True,
519         before='cc',
520         ext_out='.c',
521         samba_type='GENERATOR',
522         vars = [rule],
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