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