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