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