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