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