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