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