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