build: allow always=True/False on SAMBA_GENERATOR()
[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_MANPAGES' in bld.env and bld.env['XSLTPROC_MANPAGES']:
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_MANPAGES' in bld.env and bld.env['XSLTPROC_MANPAGES']:
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                     always=False):
489     '''A generic source generator target'''
490
491     if not SET_TARGET_TYPE(bld, name, 'GENERATOR'):
492         return
493
494     if not enabled:
495         return
496
497     bld.SET_BUILD_GROUP(group)
498     t = bld(
499         rule=rule,
500         source=bld.EXPAND_VARIABLES(source, vars=vars),
501         target=target,
502         shell=isinstance(rule, str),
503         on_results=True,
504         before='cc',
505         ext_out='.c',
506         name=name)
507
508     if always:
509         t.always = True
510
511     if public_headers is not None:
512         bld.PUBLIC_HEADERS(public_headers, header_path=header_path)
513     return t
514 Build.BuildContext.SAMBA_GENERATOR = SAMBA_GENERATOR
515
516
517
518 @runonce
519 def SETUP_BUILD_GROUPS(bld):
520     '''setup build groups used to ensure that the different build
521     phases happen consecutively'''
522     bld.p_ln = bld.srcnode # we do want to see all targets!
523     bld.env['USING_BUILD_GROUPS'] = True
524     bld.add_group('setup')
525     bld.add_group('build_compiler_source')
526     bld.add_group('base_libraries')
527     bld.add_group('generators')
528     bld.add_group('compiler_prototypes')
529     bld.add_group('compiler_libraries')
530     bld.add_group('build_compilers')
531     bld.add_group('build_source')
532     bld.add_group('prototypes')
533     bld.add_group('main')
534     bld.add_group('binaries')
535     bld.add_group('final')
536 Build.BuildContext.SETUP_BUILD_GROUPS = SETUP_BUILD_GROUPS
537
538
539 def SET_BUILD_GROUP(bld, group):
540     '''set the current build group'''
541     if not 'USING_BUILD_GROUPS' in bld.env:
542         return
543     bld.set_group(group)
544 Build.BuildContext.SET_BUILD_GROUP = SET_BUILD_GROUP
545
546
547
548 @conf
549 def ENABLE_TIMESTAMP_DEPENDENCIES(conf):
550     """use timestamps instead of file contents for deps
551     this currently doesn't work"""
552     def h_file(filename):
553         import stat
554         st = os.stat(filename)
555         if stat.S_ISDIR(st[stat.ST_MODE]): raise IOError('not a file')
556         m = Utils.md5()
557         m.update(str(st.st_mtime))
558         m.update(str(st.st_size))
559         m.update(filename)
560         return m.digest()
561     Utils.h_file = h_file
562
563
564
565 t = Task.simple_task_type('copy_script', 'rm -f ${LINK_TARGET} && ln -s ${SRC[0].abspath(env)} ${LINK_TARGET}',
566                           shell=True, color='PINK', ext_in='.bin')
567 t.quiet = True
568
569 @feature('copy_script')
570 @before('apply_link')
571 def copy_script(self):
572     tsk = self.create_task('copy_script', self.allnodes[0])
573     tsk.env.TARGET = self.target
574
575 def SAMBA_SCRIPT(bld, name, pattern, installdir, installname=None):
576     '''used to copy scripts from the source tree into the build directory
577        for use by selftest'''
578
579     source = bld.path.ant_glob(pattern)
580
581     bld.SET_BUILD_GROUP('build_source')
582     for s in TO_LIST(source):
583         iname = s
584         if installname != None:
585             iname = installname
586         target = os.path.join(installdir, iname)
587         tgtdir = os.path.dirname(os.path.join(bld.srcnode.abspath(bld.env), '..', target))
588         mkdir_p(tgtdir)
589         t = bld(features='copy_script',
590                 source       = s,
591                 target       = target,
592                 always       = True,
593                 install_path = None)
594         t.env.LINK_TARGET = target
595
596 Build.BuildContext.SAMBA_SCRIPT = SAMBA_SCRIPT
597
598
599 def install_file(bld, destdir, file, chmod=0644, flat=False,
600                  python_fixup=False, destname=None, base_name=None):
601     '''install a file'''
602     destdir = bld.EXPAND_VARIABLES(destdir)
603     if not destname:
604         destname = file
605         if flat:
606             destname = os.path.basename(destname)
607     dest = os.path.join(destdir, destname)
608     if python_fixup:
609         # fixup the python path it will use to find Samba modules
610         inst_file = file + '.inst'
611         bld.SAMBA_GENERATOR('python_%s' % destname,
612                             rule="sed 's|\(sys.path.insert.*\)bin/python\(.*\)$|\\1${PYTHONDIR}\\2|g' < ${SRC} > ${TGT}",
613                             source=file,
614                             target=inst_file)
615         file = inst_file
616     if base_name:
617         file = os.path.join(base_name, file)
618     bld.install_as(dest, file, chmod=chmod)
619
620
621 def INSTALL_FILES(bld, destdir, files, chmod=0644, flat=False,
622                   python_fixup=False, destname=None, base_name=None):
623     '''install a set of files'''
624     for f in TO_LIST(files):
625         install_file(bld, destdir, f, chmod=chmod, flat=flat,
626                      python_fixup=python_fixup, destname=destname,
627                      base_name=base_name)
628 Build.BuildContext.INSTALL_FILES = INSTALL_FILES
629
630
631 def INSTALL_WILDCARD(bld, destdir, pattern, chmod=0644, flat=False,
632                      python_fixup=False, exclude=None, trim_path=None):
633     '''install a set of files matching a wildcard pattern'''
634     files=TO_LIST(bld.path.ant_glob(pattern))
635     if trim_path:
636         files2 = []
637         for f in files:
638             files2.append(os_path_relpath(f, trim_path))
639         files = files2
640
641     if exclude:
642         for f in files[:]:
643             if fnmatch.fnmatch(f, exclude):
644                 files.remove(f)
645     INSTALL_FILES(bld, destdir, files, chmod=chmod, flat=flat,
646                   python_fixup=python_fixup, base_name=trim_path)
647 Build.BuildContext.INSTALL_WILDCARD = INSTALL_WILDCARD
648
649
650 def INSTALL_DIRS(bld, destdir, dirs):
651     '''install a set of directories'''
652     destdir = bld.EXPAND_VARIABLES(destdir)
653     dirs = bld.EXPAND_VARIABLES(dirs)
654     for d in TO_LIST(dirs):
655         bld.install_dir(os.path.join(destdir, d))
656 Build.BuildContext.INSTALL_DIRS = INSTALL_DIRS
657
658
659 re_header = re.compile('#include[ \t]*"([^"]+)"', re.I | re.M)
660 class header_task(Task.Task):
661     """
662     The public headers (the one installed on the system) have both
663     different paths and contents, so the rename is not enough.
664
665     Intermediate .inst.h files are created because path manipulation
666     may be slow. The substitution is thus performed only once.
667     """
668
669     name = 'header'
670     color = 'PINK'
671     vars = ['INCLUDEDIR', 'HEADER_DEPS']
672
673     def run(self):
674         txt = self.inputs[0].read(self.env)
675
676         # hard-coded string, but only present in samba4 (I promise, you won't feel a thing)
677         txt = txt.replace('#if _SAMBA_BUILD_ == 4', '#if 1\n')
678
679         # use a regexp to substitute the #include lines in the files
680         map = self.generator.bld.hnodemap
681         dirnodes = self.generator.bld.hnodedirs
682         def repl(m):
683             if m.group(1):
684                 s = m.group(1)
685
686                 # pokemon headers: gotta catch'em all!
687                 fin = s
688                 if s.startswith('bin/default'):
689                     node = self.generator.bld.srcnode.find_resource(s.replace('bin/default/', ''))
690                     if not node:
691                         Logs.warn('could not find the public header for %r' % s)
692                     elif node.id in map:
693                         fin = map[node.id]
694                     else:
695                         Logs.warn('could not find the public header replacement for build header %r' % s)
696                 else:
697                     # this part is more difficult since the path may be relative to anything
698                     for dirnode in dirnodes:
699                         node = dirnode.find_resource(s)
700                         if node:
701                              if node.id in map:
702                                  fin = map[node.id]
703                                  break
704                              else:
705                                  Logs.warn('could not find the public header replacement for source header %r %r' % (s, node))
706                     else:
707                         Logs.warn('-> could not find the public header for %r' % s)
708
709                 return "#include <%s>" % fin
710             return ''
711
712         txt = re_header.sub(repl, txt)
713
714         # and write the output file
715         f = None
716         try:
717             f = open(self.outputs[0].abspath(self.env), 'w')
718             f.write(txt)
719         finally:
720             if f:
721                 f.close()
722
723 @TaskGen.feature('pubh')
724 def make_public_headers(self):
725     """
726     collect the public headers to process and to install, then
727     create the substitutions (name and contents)
728     """
729
730     if not self.bld.is_install:
731         # install time only (lazy)
732         return
733
734     # keep two variables
735     #    hnodedirs: list of folders for searching the headers
736     #    hnodemap: node ids and replacement string (node objects are unique)
737     try:
738         self.bld.hnodedirs.append(self.path)
739     except AttributeError:
740         self.bld.hnodemap = {}
741         self.bld.hnodedirs = [self.bld.srcnode, self.path]
742
743         for k in 'source4 source4/include lib/talloc lib/tevent/ source4/lib/ldb/include/'.split():
744             node = self.bld.srcnode.find_dir(k)
745             if node:
746                 self.bld.hnodedirs.append(node)
747
748     header_path = getattr(self, 'header_path', None) or ''
749
750     for x in self.to_list(self.headers):
751
752         # too complicated, but what was the original idea?
753         if isinstance(header_path, list):
754             add_dir = ''
755             for (p1, dir) in header_path:
756                 lst = self.to_list(p1)
757                 for p2 in lst:
758                     if fnmatch.fnmatch(x, p2):
759                         add_dir = dir
760                         break
761                 else:
762                     continue
763                 break
764             inst_path = add_dir
765         else:
766             inst_path = header_path
767
768         dest = ''
769         name = x
770         if x.find(':') != -1:
771             s = x.split(':')
772             name = s[0]
773             dest = s[1]
774
775         inn = self.path.find_resource(name)
776
777         if not inn:
778             raise ValueError("could not find the public header %r in %r" % (name, self.path))
779         out = inn.change_ext('.inst.h')
780         self.create_task('header', inn, out)
781
782         if not dest:
783             dest = inn.name
784
785         if inst_path:
786             inst_path = inst_path + '/'
787         inst_path = inst_path + dest
788
789         self.bld.install_as('${INCLUDEDIR}/%s' % inst_path, out, self.env)
790
791         self.bld.hnodemap[inn.id] = inst_path
792
793     # create a hash (not md5) to make sure the headers are re-created if something changes
794     val = 0
795     lst = list(self.bld.hnodemap.keys())
796     lst.sort()
797     for k in lst:
798         val = hash((val, k, self.bld.hnodemap[k]))
799     self.bld.env.HEADER_DEPS = val
800
801 def PUBLIC_HEADERS(bld, public_headers, header_path=None):
802     '''install some headers
803
804     header_path may either be a string that is added to the INCLUDEDIR,
805     or it can be a dictionary of wildcard patterns which map to destination
806     directories relative to INCLUDEDIR
807     '''
808     bld.SET_BUILD_GROUP('final')
809     ret = bld(features=['pubh'], headers=public_headers, header_path=header_path)
810     return ret
811 Build.BuildContext.PUBLIC_HEADERS = PUBLIC_HEADERS
812
813
814 def subst_at_vars(task):
815     '''substiture @VAR@ style variables in a file'''
816     src = task.inputs[0].srcpath(task.env)
817     tgt = task.outputs[0].bldpath(task.env)
818
819     f = open(src, 'r')
820     s = f.read()
821     f.close()
822     # split on the vars
823     a = re.split('(@\w+@)', s)
824     out = []
825     done_var = {}
826     back_sub = [ ('PREFIX', '${prefix}'), ('EXEC_PREFIX', '${exec_prefix}')]
827     for v in a:
828         if re.match('@\w+@', v):
829             vname = v[1:-1]
830             if not vname in task.env and vname.upper() in task.env:
831                 vname = vname.upper()
832             if not vname in task.env:
833                 Logs.error("Unknown substitution %s in %s" % (v, task.name))
834                 sys.exit(1)
835             v = SUBST_VARS_RECURSIVE(task.env[vname], task.env)
836             # now we back substitute the allowed pc vars
837             for (b, m) in back_sub:
838                 s = task.env[b]
839                 if s == v[0:len(s)]:
840                     if not b in done_var:
841                         # we don't want to substitute the first usage
842                         done_var[b] = True
843                     else:
844                         v = m + v[len(s):]
845                     break
846         out.append(v)
847     contents = ''.join(out)
848     f = open(tgt, 'w')
849     s = f.write(contents)
850     f.close()
851     return 0
852
853
854
855 def PKG_CONFIG_FILES(bld, pc_files, vnum=None):
856     '''install some pkg_config pc files'''
857     dest = '${PKGCONFIGDIR}'
858     dest = bld.EXPAND_VARIABLES(dest)
859     for f in TO_LIST(pc_files):
860         base=os.path.basename(f)
861         t = bld.SAMBA_GENERATOR('PKGCONFIG_%s' % base,
862                                 rule=subst_at_vars,
863                                 source=f+'.in',
864                                 target=f)
865         if vnum:
866             t.env.PACKAGE_VERSION = vnum
867         INSTALL_FILES(bld, dest, f, flat=True, destname=base)
868 Build.BuildContext.PKG_CONFIG_FILES = PKG_CONFIG_FILES
869
870
871 def MANPAGES(bld, manpages):
872     '''build and install manual pages'''
873     bld.env.MAN_XSL = 'http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl'
874     for m in manpages.split():
875         source = m + '.xml'
876         bld.SAMBA_GENERATOR(m,
877                             source=source,
878                             target=m,
879                             group='final',
880                             rule='${XSLTPROC} -o ${TGT} --nonet ${MAN_XSL} ${SRC}'
881                             )
882         bld.INSTALL_FILES('${MANDIR}/man%s' % m[-1], m, flat=True)
883 Build.BuildContext.MANPAGES = MANPAGES
884
885
886 #############################################################
887 # give a nicer display when building different types of files
888 def progress_display(self, msg, fname):
889     col1 = Logs.colors(self.color)
890     col2 = Logs.colors.NORMAL
891     total = self.position[1]
892     n = len(str(total))
893     fs = '[%%%dd/%%%dd] %s %%s%%s%%s\n' % (n, n, msg)
894     return fs % (self.position[0], self.position[1], col1, fname, col2)
895
896 def link_display(self):
897     if Options.options.progress_bar != 0:
898         return Task.Task.old_display(self)
899     fname = self.outputs[0].bldpath(self.env)
900     return progress_display(self, 'Linking', fname)
901 Task.TaskBase.classes['cc_link'].display = link_display
902
903 def samba_display(self):
904     if Options.options.progress_bar != 0:
905         return Task.Task.old_display(self)
906
907     targets    = LOCAL_CACHE(self, 'TARGET_TYPE')
908     if self.name in targets:
909         target_type = targets[self.name]
910         type_map = { 'GENERATOR' : 'Generating',
911                      'PROTOTYPE' : 'Generating'
912                      }
913         if target_type in type_map:
914             return progress_display(self, type_map[target_type], self.name)
915
916     fname = self.inputs[0].bldpath(self.env)
917     if fname[0:3] == '../':
918         fname = fname[3:]
919     ext_loc = fname.rfind('.')
920     if ext_loc == -1:
921         return Task.Task.old_display(self)
922     ext = fname[ext_loc:]
923
924     ext_map = { '.idl' : 'Compiling IDL',
925                 '.et'  : 'Compiling ERRTABLE',
926                 '.asn1': 'Compiling ASN1',
927                 '.c'   : 'Compiling' }
928     if ext in ext_map:
929         return progress_display(self, ext_map[ext], fname)
930     return Task.Task.old_display(self)
931
932 Task.TaskBase.classes['Task'].old_display = Task.TaskBase.classes['Task'].display
933 Task.TaskBase.classes['Task'].display = samba_display