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