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