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