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