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