waf: re-work the module alias code
[abartlet/samba.git/.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                   target_type='LIBRARY',
112                   bundled_extension=True,
113                   link_name=None,
114                   abi_file=None,
115                   abi_match=None,
116                   hide_symbols=False,
117                   is_bundled=False,
118                   manpages=None,
119                   private_library=False,
120                   enabled=True):
121     '''define a Samba library'''
122
123     if not enabled:
124         SET_TARGET_TYPE(bld, libname, 'DISABLED')
125         return
126
127     source = bld.EXPAND_VARIABLES(source, vars=vars)
128
129     # remember empty libraries, so we can strip the dependencies
130     if ((source == '') or (source == [])) and deps == '' and public_deps == '':
131         SET_TARGET_TYPE(bld, libname, 'EMPTY')
132         return
133
134     if BUILTIN_LIBRARY(bld, libname):
135         obj_target = libname
136     else:
137         obj_target = libname + '.objlist'
138
139     # first create a target for building the object files for this library
140     # by separating in this way, we avoid recompiling the C files
141     # separately for the install library and the build library
142     bld.SAMBA_SUBSYSTEM(obj_target,
143                         source         = source,
144                         deps           = deps,
145                         public_deps    = public_deps,
146                         includes       = includes,
147                         public_headers = public_headers,
148                         header_path    = header_path,
149                         cflags         = cflags,
150                         group          = group,
151                         autoproto      = autoproto,
152                         depends_on     = depends_on,
153                         hide_symbols   = hide_symbols,
154                         pyext          = (target_type == "PYTHON"),
155                         local_include  = local_include)
156
157     if BUILTIN_LIBRARY(bld, libname):
158         return
159
160     if not SET_TARGET_TYPE(bld, libname, target_type):
161         return
162
163     # the library itself will depend on that object target
164     deps += ' ' + public_deps
165     deps = TO_LIST(deps)
166     deps.append(obj_target)
167
168     realname = bld.map_shlib_extension(realname, python=(target_type=='PYTHON'))
169     link_name = bld.map_shlib_extension(link_name, python=(target_type=='PYTHON'))
170
171     if private_library:
172         # private libraries always get the 'bundling' treatment with respect
173         # to the library name suffix
174         is_bundled = True
175
176     # we don't want any public libraries without version numbers
177     if not private_library and vnum is None and target_type != 'PYTHON' and not realname:
178         raise Utils.WafError("public library '%s' must have a vnum" % libname)
179
180     if target_type == 'PYTHON' or realname or not is_bundled:
181         # Sanitize the library name
182         bundled_name = libname.lower().replace('_', '-')
183         while bundled_name.startswith("lib"):
184             bundled_name = bundled_name[3:]
185     else:
186         bundled_name = BUNDLED_NAME(bld, libname, bundled_extension, private_library)
187
188     features = 'cc cshlib symlink_lib install_lib'
189     if target_type == 'PYTHON':
190         features += ' pyext'
191     if pyembed:
192         features += ' pyembed'
193     if abi_file:
194         features += ' abi_check'
195
196     if abi_file:
197         abi_file = os.path.join(bld.curdir, abi_file)
198
199     bld.SET_BUILD_GROUP(group)
200     t = bld(
201         features        = features,
202         source          = [],
203         target          = bundled_name,
204         samba_cflags    = CURRENT_LDFLAGS(bld, libname, cflags),
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         is_bundled      = is_bundled
218         )
219
220     if realname and not link_name:
221         link_name = 'shared/%s' % realname
222
223     if link_name:
224         t.link_name = link_name
225
226     if pc_files is not None:
227         bld.PKG_CONFIG_FILES(pc_files, vnum=vnum)
228
229     if manpages is not None and 'XSLTPROC_MANPAGES' in bld.env and bld.env['XSLTPROC_MANPAGES']:
230         bld.MANPAGES(manpages)
231
232
233 Build.BuildContext.SAMBA_LIBRARY = SAMBA_LIBRARY
234
235
236 #################################################################
237 def SAMBA_BINARY(bld, binname, source,
238                  deps='',
239                  includes='',
240                  public_headers=None,
241                  header_path=None,
242                  modules=None,
243                  ldflags=None,
244                  cflags='',
245                  autoproto=None,
246                  use_hostcc=False,
247                  use_global_deps=True,
248                  compiler=None,
249                  group='binaries',
250                  manpages=None,
251                  local_include=True,
252                  subsystem_name=None,
253                  pyembed=False,
254                  vars=None,
255                  install=True,
256                  install_path=None,
257                  enabled=True):
258     '''define a Samba binary'''
259
260     if not enabled:
261         SET_TARGET_TYPE(bld, binname, 'DISABLED')
262         return
263
264     if not SET_TARGET_TYPE(bld, binname, 'BINARY'):
265         return
266
267     features = 'cc cprogram symlink_bin install_bin'
268     if pyembed:
269         features += ' pyembed'
270
271     obj_target = binname + '.objlist'
272
273     source = bld.EXPAND_VARIABLES(source, vars=vars)
274     source = unique_list(TO_LIST(source))
275
276     # first create a target for building the object files for this binary
277     # by separating in this way, we avoid recompiling the C files
278     # separately for the install binary and the build binary
279     bld.SAMBA_SUBSYSTEM(obj_target,
280                         source         = source,
281                         deps           = deps,
282                         includes       = includes,
283                         cflags         = cflags,
284                         group          = group,
285                         autoproto      = autoproto,
286                         subsystem_name = subsystem_name,
287                         local_include  = local_include,
288                         use_hostcc     = use_hostcc,
289                         pyext          = pyembed,
290                         use_global_deps= use_global_deps)
291
292     bld.SET_BUILD_GROUP(group)
293
294     # the binary itself will depend on that object target
295     deps = TO_LIST(deps)
296     deps.append(obj_target)
297
298     t = bld(
299         features       = features,
300         source         = [],
301         target         = binname,
302         samba_cflags   = CURRENT_LDFLAGS(bld, binname, cflags),
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
888 def PKG_CONFIG_FILES(bld, pc_files, vnum=None):
889     '''install some pkg_config pc files'''
890     dest = '${PKGCONFIGDIR}'
891     dest = bld.EXPAND_VARIABLES(dest)
892     for f in TO_LIST(pc_files):
893         base=os.path.basename(f)
894         t = bld.SAMBA_GENERATOR('PKGCONFIG_%s' % base,
895                                 rule=subst_at_vars,
896                                 source=f+'.in',
897                                 target=f)
898         if vnum:
899             t.env.PACKAGE_VERSION = vnum
900         INSTALL_FILES(bld, dest, f, flat=True, destname=base)
901 Build.BuildContext.PKG_CONFIG_FILES = PKG_CONFIG_FILES
902
903
904 def MANPAGES(bld, manpages):
905     '''build and install manual pages'''
906     bld.env.MAN_XSL = 'http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl'
907     for m in manpages.split():
908         source = m + '.xml'
909         bld.SAMBA_GENERATOR(m,
910                             source=source,
911                             target=m,
912                             group='final',
913                             rule='${XSLTPROC} -o ${TGT} --nonet ${MAN_XSL} ${SRC}'
914                             )
915         bld.INSTALL_FILES('${MANDIR}/man%s' % m[-1], m, flat=True)
916 Build.BuildContext.MANPAGES = MANPAGES
917
918
919 #############################################################
920 # give a nicer display when building different types of files
921 def progress_display(self, msg, fname):
922     col1 = Logs.colors(self.color)
923     col2 = Logs.colors.NORMAL
924     total = self.position[1]
925     n = len(str(total))
926     fs = '[%%%dd/%%%dd] %s %%s%%s%%s\n' % (n, n, msg)
927     return fs % (self.position[0], self.position[1], col1, fname, col2)
928
929 def link_display(self):
930     if Options.options.progress_bar != 0:
931         return Task.Task.old_display(self)
932     fname = self.outputs[0].bldpath(self.env)
933     return progress_display(self, 'Linking', fname)
934 Task.TaskBase.classes['cc_link'].display = link_display
935
936 def samba_display(self):
937     if Options.options.progress_bar != 0:
938         return Task.Task.old_display(self)
939
940     targets    = LOCAL_CACHE(self, 'TARGET_TYPE')
941     if self.name in targets:
942         target_type = targets[self.name]
943         type_map = { 'GENERATOR' : 'Generating',
944                      'PROTOTYPE' : 'Generating'
945                      }
946         if target_type in type_map:
947             return progress_display(self, type_map[target_type], self.name)
948
949     fname = self.inputs[0].bldpath(self.env)
950     if fname[0:3] == '../':
951         fname = fname[3:]
952     ext_loc = fname.rfind('.')
953     if ext_loc == -1:
954         return Task.Task.old_display(self)
955     ext = fname[ext_loc:]
956
957     ext_map = { '.idl' : 'Compiling IDL',
958                 '.et'  : 'Compiling ERRTABLE',
959                 '.asn1': 'Compiling ASN1',
960                 '.c'   : 'Compiling' }
961     if ext in ext_map:
962         return progress_display(self, ext_map[ext], fname)
963     return Task.Task.old_display(self)
964
965 Task.TaskBase.classes['Task'].old_display = Task.TaskBase.classes['Task'].display
966 Task.TaskBase.classes['Task'].display = samba_display