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