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