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