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