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