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