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