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