s4-waf: re-use SAMBA_LIBRARY() in building shared modules
[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']:
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     bld.SAMBA_LIBRARY(modname,
367                       source,
368                       deps=deps,
369                       cflags=cflags,
370                       realname = realname,
371                       autoproto = autoproto,
372                       local_include=local_include,
373                       vars=vars,
374                       install_path="${MODULESDIR}/%s" % subsystem
375                       )
376
377 Build.BuildContext.SAMBA_MODULE = SAMBA_MODULE
378
379
380 #################################################################
381 def SAMBA_SUBSYSTEM(bld, modname, source,
382                     deps='',
383                     public_deps='',
384                     includes='',
385                     public_headers=None,
386                     header_path=None,
387                     cflags='',
388                     cflags_end=None,
389                     group='main',
390                     init_function_sentinal=None,
391                     heimdal_autoproto=None,
392                     heimdal_autoproto_options=None,
393                     heimdal_autoproto_private=None,
394                     autoproto=None,
395                     autoproto_extra_source='',
396                     depends_on='',
397                     local_include=True,
398                     local_include_first=True,
399                     subsystem_name=None,
400                     enabled=True,
401                     use_hostcc=False,
402                     use_global_deps=True,
403                     vars=None,
404                     hide_symbols=False,
405                     needs_python=False):
406     '''define a Samba subsystem'''
407
408     if not enabled:
409         SET_TARGET_TYPE(bld, modname, 'DISABLED')
410         return
411
412     # remember empty subsystems, so we can strip the dependencies
413     if ((source == '') or (source == [])) and deps == '' and public_deps == '':
414         SET_TARGET_TYPE(bld, modname, 'EMPTY')
415         return
416
417     if not SET_TARGET_TYPE(bld, modname, 'SUBSYSTEM'):
418         return
419
420     source = bld.EXPAND_VARIABLES(source, vars=vars)
421     source = unique_list(TO_LIST(source))
422
423     deps += ' ' + public_deps
424
425     bld.SET_BUILD_GROUP(group)
426
427     features = 'cc'
428     if needs_python:
429         features += ' pyext'
430
431     t = bld(
432         features       = features,
433         source         = source,
434         target         = modname,
435         samba_cflags   = CURRENT_CFLAGS(bld, modname, cflags, hide_symbols=hide_symbols),
436         depends_on     = depends_on,
437         samba_deps     = TO_LIST(deps),
438         samba_includes = includes,
439         local_include  = local_include,
440         local_include_first  = local_include_first,
441         samba_subsystem= subsystem_name,
442         samba_use_hostcc = use_hostcc,
443         samba_use_global_deps = use_global_deps
444         )
445
446     if cflags_end is not None:
447         t.samba_cflags.extend(TO_LIST(cflags_end))
448
449     if heimdal_autoproto is not None:
450         bld.HEIMDAL_AUTOPROTO(heimdal_autoproto, source, options=heimdal_autoproto_options)
451     if heimdal_autoproto_private is not None:
452         bld.HEIMDAL_AUTOPROTO_PRIVATE(heimdal_autoproto_private, source)
453     if autoproto is not None:
454         bld.SAMBA_AUTOPROTO(autoproto, source + TO_LIST(autoproto_extra_source))
455     if public_headers is not None:
456         bld.PUBLIC_HEADERS(public_headers, header_path=header_path)
457     return t
458
459
460 Build.BuildContext.SAMBA_SUBSYSTEM = SAMBA_SUBSYSTEM
461
462
463 def SAMBA_GENERATOR(bld, name, rule, source='', target='',
464                     group='generators', enabled=True,
465                     public_headers=None,
466                     header_path=None,
467                     vars=None,
468                     always=False):
469     '''A generic source generator target'''
470
471     if not SET_TARGET_TYPE(bld, name, 'GENERATOR'):
472         return
473
474     if not enabled:
475         return
476
477     bld.SET_BUILD_GROUP(group)
478     t = bld(
479         rule=rule,
480         source=bld.EXPAND_VARIABLES(source, vars=vars),
481         target=target,
482         shell=isinstance(rule, str),
483         on_results=True,
484         before='cc',
485         ext_out='.c',
486         name=name)
487
488     if always:
489         t.always = True
490
491     if public_headers is not None:
492         bld.PUBLIC_HEADERS(public_headers, header_path=header_path)
493     return t
494 Build.BuildContext.SAMBA_GENERATOR = SAMBA_GENERATOR
495
496
497
498 @runonce
499 def SETUP_BUILD_GROUPS(bld):
500     '''setup build groups used to ensure that the different build
501     phases happen consecutively'''
502     bld.p_ln = bld.srcnode # we do want to see all targets!
503     bld.env['USING_BUILD_GROUPS'] = True
504     bld.add_group('setup')
505     bld.add_group('build_compiler_source')
506     bld.add_group('base_libraries')
507     bld.add_group('generators')
508     bld.add_group('compiler_prototypes')
509     bld.add_group('compiler_libraries')
510     bld.add_group('build_compilers')
511     bld.add_group('build_source')
512     bld.add_group('prototypes')
513     bld.add_group('main')
514     bld.add_group('binaries')
515     bld.add_group('final')
516 Build.BuildContext.SETUP_BUILD_GROUPS = SETUP_BUILD_GROUPS
517
518
519 def SET_BUILD_GROUP(bld, group):
520     '''set the current build group'''
521     if not 'USING_BUILD_GROUPS' in bld.env:
522         return
523     bld.set_group(group)
524 Build.BuildContext.SET_BUILD_GROUP = SET_BUILD_GROUP
525
526
527
528 @conf
529 def ENABLE_TIMESTAMP_DEPENDENCIES(conf):
530     """use timestamps instead of file contents for deps
531     this currently doesn't work"""
532     def h_file(filename):
533         import stat
534         st = os.stat(filename)
535         if stat.S_ISDIR(st[stat.ST_MODE]): raise IOError('not a file')
536         m = Utils.md5()
537         m.update(str(st.st_mtime))
538         m.update(str(st.st_size))
539         m.update(filename)
540         return m.digest()
541     Utils.h_file = h_file
542
543
544
545 t = Task.simple_task_type('copy_script', 'rm -f ${LINK_TARGET} && ln -s ${SRC[0].abspath(env)} ${LINK_TARGET}',
546                           shell=True, color='PINK', ext_in='.bin')
547 t.quiet = True
548
549 @feature('copy_script')
550 @before('apply_link')
551 def copy_script(self):
552     tsk = self.create_task('copy_script', self.allnodes[0])
553     tsk.env.TARGET = self.target
554
555 def SAMBA_SCRIPT(bld, name, pattern, installdir, installname=None):
556     '''used to copy scripts from the source tree into the build directory
557        for use by selftest'''
558
559     source = bld.path.ant_glob(pattern)
560
561     bld.SET_BUILD_GROUP('build_source')
562     for s in TO_LIST(source):
563         iname = s
564         if installname != None:
565             iname = installname
566         target = os.path.join(installdir, iname)
567         tgtdir = os.path.dirname(os.path.join(bld.srcnode.abspath(bld.env), '..', target))
568         mkdir_p(tgtdir)
569         t = bld(features='copy_script',
570                 source       = s,
571                 target       = target,
572                 always       = True,
573                 install_path = None)
574         t.env.LINK_TARGET = target
575
576 Build.BuildContext.SAMBA_SCRIPT = SAMBA_SCRIPT
577
578
579 def install_file(bld, destdir, file, chmod=0644, flat=False,
580                  python_fixup=False, destname=None, base_name=None):
581     '''install a file'''
582     destdir = bld.EXPAND_VARIABLES(destdir)
583     if not destname:
584         destname = file
585         if flat:
586             destname = os.path.basename(destname)
587     dest = os.path.join(destdir, destname)
588     if python_fixup:
589         # fixup the python path it will use to find Samba modules
590         inst_file = file + '.inst'
591         bld.SAMBA_GENERATOR('python_%s' % destname,
592                             rule="sed 's|\(sys.path.insert.*\)bin/python\(.*\)$|\\1${PYTHONDIR}\\2|g' < ${SRC} > ${TGT}",
593                             source=file,
594                             target=inst_file)
595         file = inst_file
596     if base_name:
597         file = os.path.join(base_name, file)
598     bld.install_as(dest, file, chmod=chmod)
599
600
601 def INSTALL_FILES(bld, destdir, files, chmod=0644, flat=False,
602                   python_fixup=False, destname=None, base_name=None):
603     '''install a set of files'''
604     for f in TO_LIST(files):
605         install_file(bld, destdir, f, chmod=chmod, flat=flat,
606                      python_fixup=python_fixup, destname=destname,
607                      base_name=base_name)
608 Build.BuildContext.INSTALL_FILES = INSTALL_FILES
609
610
611 def INSTALL_WILDCARD(bld, destdir, pattern, chmod=0644, flat=False,
612                      python_fixup=False, exclude=None, trim_path=None):
613     '''install a set of files matching a wildcard pattern'''
614     files=TO_LIST(bld.path.ant_glob(pattern))
615     if trim_path:
616         files2 = []
617         for f in files:
618             files2.append(os_path_relpath(f, trim_path))
619         files = files2
620
621     if exclude:
622         for f in files[:]:
623             if fnmatch.fnmatch(f, exclude):
624                 files.remove(f)
625     INSTALL_FILES(bld, destdir, files, chmod=chmod, flat=flat,
626                   python_fixup=python_fixup, base_name=trim_path)
627 Build.BuildContext.INSTALL_WILDCARD = INSTALL_WILDCARD
628
629
630 def INSTALL_DIRS(bld, destdir, dirs):
631     '''install a set of directories'''
632     destdir = bld.EXPAND_VARIABLES(destdir)
633     dirs = bld.EXPAND_VARIABLES(dirs)
634     for d in TO_LIST(dirs):
635         bld.install_dir(os.path.join(destdir, d))
636 Build.BuildContext.INSTALL_DIRS = INSTALL_DIRS
637
638
639 re_header = re.compile('#include[ \t]*"([^"]+)"', re.I | re.M)
640 class header_task(Task.Task):
641     """
642     The public headers (the one installed on the system) have both
643     different paths and contents, so the rename is not enough.
644
645     Intermediate .inst.h files are created because path manipulation
646     may be slow. The substitution is thus performed only once.
647     """
648
649     name = 'header'
650     color = 'PINK'
651     vars = ['INCLUDEDIR', 'HEADER_DEPS']
652
653     def run(self):
654         txt = self.inputs[0].read(self.env)
655
656         # hard-coded string, but only present in samba4 (I promise, you won't feel a thing)
657         txt = txt.replace('#if _SAMBA_BUILD_ == 4', '#if 1\n')
658
659         # use a regexp to substitute the #include lines in the files
660         map = self.generator.bld.hnodemap
661         dirnodes = self.generator.bld.hnodedirs
662         def repl(m):
663             if m.group(1):
664                 s = m.group(1)
665
666                 # pokemon headers: gotta catch'em all!
667                 fin = s
668                 if s.startswith('bin/default'):
669                     node = self.generator.bld.srcnode.find_resource(s.replace('bin/default/', ''))
670                     if not node:
671                         Logs.warn('could not find the public header for %r' % s)
672                     elif node.id in map:
673                         fin = map[node.id]
674                     else:
675                         Logs.warn('could not find the public header replacement for build header %r' % s)
676                 else:
677                     # this part is more difficult since the path may be relative to anything
678                     for dirnode in dirnodes:
679                         node = dirnode.find_resource(s)
680                         if node:
681                              if node.id in map:
682                                  fin = map[node.id]
683                                  break
684                              else:
685                                  Logs.warn('could not find the public header replacement for source header %r %r' % (s, node))
686                     else:
687                         Logs.warn('-> could not find the public header for %r' % s)
688
689                 return "#include <%s>" % fin
690             return ''
691
692         txt = re_header.sub(repl, txt)
693
694         # and write the output file
695         f = None
696         try:
697             f = open(self.outputs[0].abspath(self.env), 'w')
698             f.write(txt)
699         finally:
700             if f:
701                 f.close()
702
703 @TaskGen.feature('pubh')
704 def make_public_headers(self):
705     """
706     collect the public headers to process and to install, then
707     create the substitutions (name and contents)
708     """
709
710     if not self.bld.is_install:
711         # install time only (lazy)
712         return
713
714     # keep two variables
715     #    hnodedirs: list of folders for searching the headers
716     #    hnodemap: node ids and replacement string (node objects are unique)
717     try:
718         self.bld.hnodedirs.append(self.path)
719     except AttributeError:
720         self.bld.hnodemap = {}
721         self.bld.hnodedirs = [self.bld.srcnode, self.path]
722
723         for k in 'source4 source4/include lib/talloc lib/tevent/ source4/lib/ldb/include/'.split():
724             node = self.bld.srcnode.find_dir(k)
725             if node:
726                 self.bld.hnodedirs.append(node)
727
728     header_path = getattr(self, 'header_path', None) or ''
729
730     for x in self.to_list(self.headers):
731
732         # too complicated, but what was the original idea?
733         if isinstance(header_path, list):
734             add_dir = ''
735             for (p1, dir) in header_path:
736                 lst = self.to_list(p1)
737                 for p2 in lst:
738                     if fnmatch.fnmatch(x, p2):
739                         add_dir = dir
740                         break
741                 else:
742                     continue
743                 break
744             inst_path = add_dir
745         else:
746             inst_path = header_path
747
748         dest = ''
749         name = x
750         if x.find(':') != -1:
751             s = x.split(':')
752             name = s[0]
753             dest = s[1]
754
755         inn = self.path.find_resource(name)
756
757         if not inn:
758             raise ValueError("could not find the public header %r in %r" % (name, self.path))
759         out = inn.change_ext('.inst.h')
760         self.create_task('header', inn, out)
761
762         if not dest:
763             dest = inn.name
764
765         if inst_path:
766             inst_path = inst_path + '/'
767         inst_path = inst_path + dest
768
769         self.bld.install_as('${INCLUDEDIR}/%s' % inst_path, out, self.env)
770
771         self.bld.hnodemap[inn.id] = inst_path
772
773     # create a hash (not md5) to make sure the headers are re-created if something changes
774     val = 0
775     lst = list(self.bld.hnodemap.keys())
776     lst.sort()
777     for k in lst:
778         val = hash((val, k, self.bld.hnodemap[k]))
779     self.bld.env.HEADER_DEPS = val
780
781 def PUBLIC_HEADERS(bld, public_headers, header_path=None):
782     '''install some headers
783
784     header_path may either be a string that is added to the INCLUDEDIR,
785     or it can be a dictionary of wildcard patterns which map to destination
786     directories relative to INCLUDEDIR
787     '''
788     bld.SET_BUILD_GROUP('final')
789     ret = bld(features=['pubh'], headers=public_headers, header_path=header_path)
790     return ret
791 Build.BuildContext.PUBLIC_HEADERS = PUBLIC_HEADERS
792
793
794 def subst_at_vars(task):
795     '''substiture @VAR@ style variables in a file'''
796     src = task.inputs[0].srcpath(task.env)
797     tgt = task.outputs[0].bldpath(task.env)
798
799     f = open(src, 'r')
800     s = f.read()
801     f.close()
802     # split on the vars
803     a = re.split('(@\w+@)', s)
804     out = []
805     done_var = {}
806     back_sub = [ ('PREFIX', '${prefix}'), ('EXEC_PREFIX', '${exec_prefix}')]
807     for v in a:
808         if re.match('@\w+@', v):
809             vname = v[1:-1]
810             if not vname in task.env and vname.upper() in task.env:
811                 vname = vname.upper()
812             if not vname in task.env:
813                 Logs.error("Unknown substitution %s in %s" % (v, task.name))
814                 sys.exit(1)
815             v = SUBST_VARS_RECURSIVE(task.env[vname], task.env)
816             # now we back substitute the allowed pc vars
817             for (b, m) in back_sub:
818                 s = task.env[b]
819                 if s == v[0:len(s)]:
820                     if not b in done_var:
821                         # we don't want to substitute the first usage
822                         done_var[b] = True
823                     else:
824                         v = m + v[len(s):]
825                     break
826         out.append(v)
827     contents = ''.join(out)
828     f = open(tgt, 'w')
829     s = f.write(contents)
830     f.close()
831     return 0
832
833
834
835 def PKG_CONFIG_FILES(bld, pc_files, vnum=None):
836     '''install some pkg_config pc files'''
837     dest = '${PKGCONFIGDIR}'
838     dest = bld.EXPAND_VARIABLES(dest)
839     for f in TO_LIST(pc_files):
840         base=os.path.basename(f)
841         t = bld.SAMBA_GENERATOR('PKGCONFIG_%s' % base,
842                                 rule=subst_at_vars,
843                                 source=f+'.in',
844                                 target=f)
845         if vnum:
846             t.env.PACKAGE_VERSION = vnum
847         INSTALL_FILES(bld, dest, f, flat=True, destname=base)
848 Build.BuildContext.PKG_CONFIG_FILES = PKG_CONFIG_FILES
849
850
851 def MANPAGES(bld, manpages):
852     '''build and install manual pages'''
853     bld.env.MAN_XSL = 'http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl'
854     for m in manpages.split():
855         source = m + '.xml'
856         bld.SAMBA_GENERATOR(m,
857                             source=source,
858                             target=m,
859                             group='final',
860                             rule='${XSLTPROC} -o ${TGT} --nonet ${MAN_XSL} ${SRC}'
861                             )
862         bld.INSTALL_FILES('${MANDIR}/man%s' % m[-1], m, flat=True)
863 Build.BuildContext.MANPAGES = MANPAGES
864
865
866 #############################################################
867 # give a nicer display when building different types of files
868 def progress_display(self, msg, fname):
869     col1 = Logs.colors(self.color)
870     col2 = Logs.colors.NORMAL
871     total = self.position[1]
872     n = len(str(total))
873     fs = '[%%%dd/%%%dd] %s %%s%%s%%s\n' % (n, n, msg)
874     return fs % (self.position[0], self.position[1], col1, fname, col2)
875
876 def link_display(self):
877     if Options.options.progress_bar != 0:
878         return Task.Task.old_display(self)
879     fname = self.outputs[0].bldpath(self.env)
880     return progress_display(self, 'Linking', fname)
881 Task.TaskBase.classes['cc_link'].display = link_display
882
883 def samba_display(self):
884     if Options.options.progress_bar != 0:
885         return Task.Task.old_display(self)
886
887     targets    = LOCAL_CACHE(self, 'TARGET_TYPE')
888     if self.name in targets:
889         target_type = targets[self.name]
890         type_map = { 'GENERATOR' : 'Generating',
891                      'PROTOTYPE' : 'Generating'
892                      }
893         if target_type in type_map:
894             return progress_display(self, type_map[target_type], self.name)
895
896     fname = self.inputs[0].bldpath(self.env)
897     if fname[0:3] == '../':
898         fname = fname[3:]
899     ext_loc = fname.rfind('.')
900     if ext_loc == -1:
901         return Task.Task.old_display(self)
902     ext = fname[ext_loc:]
903
904     ext_map = { '.idl' : 'Compiling IDL',
905                 '.et'  : 'Compiling ERRTABLE',
906                 '.asn1': 'Compiling ASN1',
907                 '.c'   : 'Compiling' }
908     if ext in ext_map:
909         return progress_display(self, ext_map[ext], fname)
910     return Task.Task.old_display(self)
911
912 Task.TaskBase.classes['Task'].old_display = Task.TaskBase.classes['Task'].display
913 Task.TaskBase.classes['Task'].display = samba_display