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