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