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