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