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