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