1 # a waf tool to add autoconf-like macros to the configure section
2 # and for SAMBA_ macros for building libraries, binaries etc
4 import Build, os, Options, Task, Utils, cc, TaskGen, fnmatch, re, shutil, Logs, Constants
5 from Configure import conf
7 from samba_utils import SUBST_VARS_RECURSIVE
8 TaskGen.task_gen.apply_verif = Utils.nada
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_errtable import *
18 from samba_asn1 import *
19 from samba_autoproto import *
20 from samba_python import *
21 from samba_deps import *
22 from samba_bundled import *
24 import samba_conftests
34 # some systems have broken threading in python
35 if os.environ.get('WAF_NOTHREADS') == '1':
40 os.putenv('PYTHONUNBUFFERED', '1')
43 if Constants.HEXVERSION < 0x105016:
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
49 Alternatively, please use ./autogen-waf.sh, and then
50 run ./configure and make as usual. That will call the right version of waf.
56 def SAMBA_BUILD_ENV(conf):
57 '''create the samba build environment'''
58 conf.env.BUILD_DIRECTORY = conf.blddir
59 mkdir_p(os.path.join(conf.blddir, LIB_PATH))
60 mkdir_p(os.path.join(conf.blddir, 'python/samba/dcerpc'))
61 # this allows all of the bin/shared and bin/python targets
62 # to be expressed in terms of build directory paths
63 mkdir_p(os.path.join(conf.blddir, 'default'))
64 for p in ['python','shared']:
65 link_target = os.path.join(conf.blddir, 'default/' + p)
66 if not os.path.lexists(link_target):
67 os.symlink('../' + p, link_target)
69 # get perl to put the blib files in the build directory
70 blib_bld = os.path.join(conf.blddir, 'default/pidl/blib')
71 blib_src = os.path.join(conf.srcdir, 'pidl/blib')
72 mkdir_p(blib_bld + '/man1')
73 mkdir_p(blib_bld + '/man3')
74 if os.path.islink(blib_src):
76 elif os.path.exists(blib_src):
77 shutil.rmtree(blib_src)
80 def ADD_INIT_FUNCTION(bld, subsystem, target, init_function):
81 '''add an init_function to the list for a subsystem'''
82 if init_function is None:
84 bld.ASSERT(subsystem is not None, "You must specify a subsystem for init_function '%s'" % init_function)
85 cache = LOCAL_CACHE(bld, 'INIT_FUNCTIONS')
86 if not subsystem in cache:
88 cache[subsystem].append( { 'TARGET':target, 'INIT_FUNCTION':init_function } )
89 Build.BuildContext.ADD_INIT_FUNCTION = ADD_INIT_FUNCTION
93 #################################################################
94 def SAMBA_LIBRARY(bld, libname, source,
103 external_library=False,
113 target_type='LIBRARY',
114 bundled_extension=True,
122 '''define a Samba library'''
125 SET_TARGET_TYPE(bld, libname, 'DISABLED')
128 source = bld.EXPAND_VARIABLES(source, vars=vars)
130 # remember empty libraries, so we can strip the dependencies
131 if ((source == '') or (source == [])) and deps == '' and public_deps == '':
132 SET_TARGET_TYPE(bld, libname, 'EMPTY')
135 if target_type != 'PYTHON' and BUILTIN_LIBRARY(bld, libname):
138 obj_target = libname + '.objlist'
140 # first create a target for building the object files for this library
141 # by separating in this way, we avoid recompiling the C files
142 # separately for the install library and the build library
143 bld.SAMBA_SUBSYSTEM(obj_target,
146 public_deps = public_deps,
148 public_headers = public_headers,
149 header_path = header_path,
152 autoproto = autoproto,
153 depends_on = depends_on,
154 needs_python = needs_python,
155 hide_symbols = hide_symbols,
156 local_include = local_include)
158 if libname == obj_target:
161 if not SET_TARGET_TYPE(bld, libname, target_type):
164 # the library itself will depend on that object target
165 deps += ' ' + public_deps
167 deps.append(obj_target)
169 if target_type == 'PYTHON' or realname or not is_bundled:
170 # Sanitize the library name
171 bundled_name = libname.lower().replace('_', '-')
172 while bundled_name.startswith("lib"):
173 bundled_name = bundled_name[3:]
175 bundled_name = BUNDLED_NAME(bld, libname, bundled_extension)
177 features = 'cc cshlib symlink_lib install_lib'
178 if target_type == 'PYTHON':
181 features += ' pyembed'
183 features += ' abi_check'
186 abi_file = os.path.join(bld.curdir, abi_file)
188 bld.SET_BUILD_GROUP(group)
192 target = bundled_name,
193 samba_cflags = CURRENT_CFLAGS(bld, libname, cflags),
194 depends_on = depends_on,
196 samba_includes = includes,
197 local_include = local_include,
200 samba_inst_path = install_path,
202 samba_realname = realname,
203 samba_install = install,
205 abi_match = abi_match,
206 is_bundled = is_bundled
209 if realname and not link_name:
210 link_name = 'shared/%s' % realname
213 t.link_name = link_name
215 if pc_files is not None:
216 bld.PKG_CONFIG_FILES(pc_files, vnum=vnum)
218 if manpages is not None and 'XSLTPROC' in bld.env and bld.env['XSLTPROC']:
219 bld.MANPAGES(manpages)
222 Build.BuildContext.SAMBA_LIBRARY = SAMBA_LIBRARY
225 #################################################################
226 def SAMBA_BINARY(bld, binname, source,
236 use_global_deps=True,
247 '''define a Samba binary'''
250 SET_TARGET_TYPE(bld, binname, 'DISABLED')
253 if not SET_TARGET_TYPE(bld, binname, 'BINARY'):
256 features = 'cc cprogram symlink_bin install_bin'
258 features += ' pyembed'
260 obj_target = binname + '.objlist'
262 source = bld.EXPAND_VARIABLES(source, vars=vars)
263 source = unique_list(TO_LIST(source))
265 # first create a target for building the object files for this binary
266 # by separating in this way, we avoid recompiling the C files
267 # separately for the install binary and the build binary
268 bld.SAMBA_SUBSYSTEM(obj_target,
274 autoproto = autoproto,
275 subsystem_name = subsystem_name,
276 needs_python = needs_python,
277 local_include = local_include,
278 use_hostcc = use_hostcc,
279 use_global_deps= use_global_deps)
281 bld.SET_BUILD_GROUP(group)
283 # the binary itself will depend on that object target
285 deps.append(obj_target)
291 samba_cflags = CURRENT_CFLAGS(bld, binname, cflags),
293 samba_includes = includes,
294 local_include = local_include,
295 samba_modules = modules,
297 samba_subsystem= subsystem_name,
299 samba_inst_path= install_path,
300 samba_install = install
303 # setup the subsystem_name as an alias for the real
304 # binary name, so it can be found when expanding
305 # subsystem dependencies
306 if subsystem_name is not None:
307 bld.TARGET_ALIAS(subsystem_name, binname)
309 if manpages is not None and 'XSLTPROC' in bld.env and bld.env['XSLTPROC']:
310 bld.MANPAGES(manpages)
312 Build.BuildContext.SAMBA_BINARY = SAMBA_BINARY
315 #################################################################
316 def SAMBA_MODULE(bld, modname, source,
322 autoproto_extra_source='',
325 internal_module=True,
329 '''define a Samba module.'''
331 source = bld.EXPAND_VARIABLES(source, vars=vars)
333 obj_target = modname + '.objlist'
335 bld.SAMBA_SUBSYSTEM(obj_target, source,
339 autoproto_extra_source=autoproto_extra_source,
341 local_include=local_include,
344 if internal_module or BUILTIN_LIBRARY(bld, modname):
345 # treat internal modules as subsystems for now
346 bld.SAMBA_SUBSYSTEM(modname, deps=obj_target, source=[],
348 bld.ADD_INIT_FUNCTION(subsystem, modname, init_function)
352 SET_TARGET_TYPE(bld, modname, 'DISABLED')
355 if not SET_TARGET_TYPE(bld, modname, 'MODULE'):
359 deps.append(obj_target)
360 if subsystem is not None:
361 deps.append(subsystem)
363 bld.SET_BUILD_GROUP('main')
365 features = 'cc cshlib install_lib',
368 samba_cflags = CURRENT_CFLAGS(bld, modname, cflags),
369 samba_includes = includes,
370 local_include = local_include,
373 samba_inst_path= "${MODULESDIR}/%s" % subsystem,
374 samba_realname = None,
376 samba_install = True,
380 Build.BuildContext.SAMBA_MODULE = SAMBA_MODULE
383 #################################################################
384 def SAMBA_SUBSYSTEM(bld, modname, source,
393 init_function_sentinal=None,
394 heimdal_autoproto=None,
395 heimdal_autoproto_options=None,
396 heimdal_autoproto_private=None,
398 autoproto_extra_source='',
401 local_include_first=True,
405 use_global_deps=True,
409 '''define a Samba subsystem'''
412 SET_TARGET_TYPE(bld, modname, 'DISABLED')
415 # remember empty subsystems, so we can strip the dependencies
416 if ((source == '') or (source == [])) and deps == '' and public_deps == '':
417 SET_TARGET_TYPE(bld, modname, 'EMPTY')
420 if not SET_TARGET_TYPE(bld, modname, 'SUBSYSTEM'):
423 source = bld.EXPAND_VARIABLES(source, vars=vars)
424 source = unique_list(TO_LIST(source))
426 deps += ' ' + public_deps
428 bld.SET_BUILD_GROUP(group)
438 samba_cflags = CURRENT_CFLAGS(bld, modname, cflags, hide_symbols=hide_symbols),
439 depends_on = depends_on,
440 samba_deps = TO_LIST(deps),
441 samba_includes = includes,
442 local_include = local_include,
443 local_include_first = local_include_first,
444 samba_subsystem= subsystem_name,
445 samba_use_hostcc = use_hostcc,
446 samba_use_global_deps = use_global_deps
449 if cflags_end is not None:
450 t.samba_cflags.extend(TO_LIST(cflags_end))
452 if heimdal_autoproto is not None:
453 bld.HEIMDAL_AUTOPROTO(heimdal_autoproto, source, options=heimdal_autoproto_options)
454 if heimdal_autoproto_private is not None:
455 bld.HEIMDAL_AUTOPROTO_PRIVATE(heimdal_autoproto_private, source)
456 if autoproto is not None:
457 bld.SAMBA_AUTOPROTO(autoproto, source + TO_LIST(autoproto_extra_source))
458 if public_headers is not None:
459 bld.PUBLIC_HEADERS(public_headers, header_path=header_path)
463 Build.BuildContext.SAMBA_SUBSYSTEM = SAMBA_SUBSYSTEM
466 def SAMBA_GENERATOR(bld, name, rule, source='', target='',
467 group='generators', enabled=True,
471 '''A generic source generator target'''
473 if not SET_TARGET_TYPE(bld, name, 'GENERATOR'):
479 bld.SET_BUILD_GROUP(group)
482 source=bld.EXPAND_VARIABLES(source, vars=vars),
484 shell=isinstance(rule, str),
490 if public_headers is not None:
491 bld.PUBLIC_HEADERS(public_headers, header_path=header_path)
493 Build.BuildContext.SAMBA_GENERATOR = SAMBA_GENERATOR
498 def SETUP_BUILD_GROUPS(bld):
499 '''setup build groups used to ensure that the different build
500 phases happen consecutively'''
501 bld.p_ln = bld.srcnode # we do want to see all targets!
502 bld.env['USING_BUILD_GROUPS'] = True
503 bld.add_group('setup')
504 bld.add_group('build_compiler_source')
505 bld.add_group('base_libraries')
506 bld.add_group('generators')
507 bld.add_group('compiler_prototypes')
508 bld.add_group('compiler_libraries')
509 bld.add_group('build_compilers')
510 bld.add_group('build_source')
511 bld.add_group('prototypes')
512 bld.add_group('main')
513 bld.add_group('binaries')
514 bld.add_group('final')
515 Build.BuildContext.SETUP_BUILD_GROUPS = SETUP_BUILD_GROUPS
518 def SET_BUILD_GROUP(bld, group):
519 '''set the current build group'''
520 if not 'USING_BUILD_GROUPS' in bld.env:
523 Build.BuildContext.SET_BUILD_GROUP = SET_BUILD_GROUP
528 def ENABLE_TIMESTAMP_DEPENDENCIES(conf):
529 """use timestamps instead of file contents for deps
530 this currently doesn't work"""
531 def h_file(filename):
533 st = os.stat(filename)
534 if stat.S_ISDIR(st[stat.ST_MODE]): raise IOError('not a file')
536 m.update(str(st.st_mtime))
537 m.update(str(st.st_size))
540 Utils.h_file = h_file
544 t = Task.simple_task_type('copy_script', 'rm -f ${LINK_TARGET} && ln -s ${SRC[0].abspath(env)} ${LINK_TARGET}',
545 shell=True, color='PINK', ext_in='.bin')
548 @feature('copy_script')
549 @before('apply_link')
550 def copy_script(self):
551 tsk = self.create_task('copy_script', self.allnodes[0])
552 tsk.env.TARGET = self.target
554 def SAMBA_SCRIPT(bld, name, pattern, installdir, installname=None):
555 '''used to copy scripts from the source tree into the build directory
556 for use by selftest'''
558 source = bld.path.ant_glob(pattern)
560 bld.SET_BUILD_GROUP('build_source')
561 for s in TO_LIST(source):
563 if installname != None:
565 target = os.path.join(installdir, iname)
566 tgtdir = os.path.dirname(os.path.join(bld.srcnode.abspath(bld.env), '..', target))
568 t = bld(features='copy_script',
573 t.env.LINK_TARGET = target
575 Build.BuildContext.SAMBA_SCRIPT = SAMBA_SCRIPT
578 def install_file(bld, destdir, file, chmod=O644, flat=False,
579 python_fixup=False, destname=None, base_name=None):
581 destdir = bld.EXPAND_VARIABLES(destdir)
585 destname = os.path.basename(destname)
586 dest = os.path.join(destdir, destname)
588 # fixup the python path it will use to find Samba modules
589 inst_file = file + '.inst'
590 bld.SAMBA_GENERATOR('python_%s' % destname,
591 rule="sed 's|\(sys.path.insert.*\)bin/python\(.*\)$|\\1${PYTHONDIR}\\2|g' < ${SRC} > ${TGT}",
596 file = os.path.join(base_name, file)
597 bld.install_as(dest, file, chmod=chmod)
600 def INSTALL_FILES(bld, destdir, files, chmod=O644, flat=False,
601 python_fixup=False, destname=None, base_name=None):
602 '''install a set of files'''
603 for f in TO_LIST(files):
604 install_file(bld, destdir, f, chmod=chmod, flat=flat,
605 python_fixup=python_fixup, destname=destname,
607 Build.BuildContext.INSTALL_FILES = INSTALL_FILES
610 def INSTALL_WILDCARD(bld, destdir, pattern, chmod=O644, flat=False,
611 python_fixup=False, exclude=None, trim_path=None):
612 '''install a set of files matching a wildcard pattern'''
613 files=TO_LIST(bld.path.ant_glob(pattern))
617 files2.append(os_path_relpath(f, trim_path))
622 if fnmatch.fnmatch(f, exclude):
624 INSTALL_FILES(bld, destdir, files, chmod=chmod, flat=flat,
625 python_fixup=python_fixup, base_name=trim_path)
626 Build.BuildContext.INSTALL_WILDCARD = INSTALL_WILDCARD
629 def INSTALL_DIRS(bld, destdir, dirs):
630 '''install a set of directories'''
631 destdir = bld.EXPAND_VARIABLES(destdir)
632 dirs = bld.EXPAND_VARIABLES(dirs)
633 for d in TO_LIST(dirs):
634 bld.install_dir(os.path.join(destdir, d))
635 Build.BuildContext.INSTALL_DIRS = INSTALL_DIRS
638 re_header = re.compile('#include[ \t]*"([^"]+)"', re.I | re.M)
639 class header_task(Task.Task):
641 The public headers (the one installed on the system) have both
642 different paths and contents, so the rename is not enough.
644 Intermediate .inst.h files are created because path manipulation
645 may be slow. The substitution is thus performed only once.
650 vars = ['INCLUDEDIR', 'HEADER_DEPS']
653 txt = self.inputs[0].read(self.env)
655 # hard-coded string, but only present in samba4 (I promise, you won't feel a thing)
656 txt = txt.replace('#if _SAMBA_BUILD_ == 4', '#if 1\n')
658 # use a regexp to substitute the #include lines in the files
659 map = self.generator.bld.hnodemap
660 dirnodes = self.generator.bld.hnodedirs
665 # pokemon headers: gotta catch'em all!
667 if s.startswith('bin/default'):
668 node = self.generator.bld.srcnode.find_resource(s.replace('bin/default/', ''))
670 Logs.warn('could not find the public header for %r' % s)
674 Logs.warn('could not find the public header replacement for build header %r' % s)
676 # this part is more difficult since the path may be relative to anything
677 for dirnode in dirnodes:
678 node = dirnode.find_resource(s)
684 Logs.warn('could not find the public header replacement for source header %r %r' % (s, node))
686 Logs.warn('-> could not find the public header for %r' % s)
688 return "#include <%s>" % fin
691 txt = re_header.sub(repl, txt)
693 # and write the output file
696 f = open(self.outputs[0].abspath(self.env), 'w')
702 @TaskGen.feature('pubh')
703 def make_public_headers(self):
705 collect the public headers to process and to install, then
706 create the substitutions (name and contents)
709 if not self.bld.is_install:
710 # install time only (lazy)
714 # hnodedirs: list of folders for searching the headers
715 # hnodemap: node ids and replacement string (node objects are unique)
717 self.bld.hnodedirs.append(self.path)
718 except AttributeError:
719 self.bld.hnodemap = {}
720 self.bld.hnodedirs = [self.bld.srcnode, self.path]
722 for k in 'source4 source4/include lib/talloc lib/tevent/ source4/lib/ldb/include/'.split():
723 node = self.bld.srcnode.find_dir(k)
725 self.bld.hnodedirs.append(node)
727 header_path = getattr(self, 'header_path', None) or ''
729 for x in self.to_list(self.headers):
731 # too complicated, but what was the original idea?
732 if isinstance(header_path, list):
734 for (p1, dir) in header_path:
735 lst = self.to_list(p1)
737 if fnmatch.fnmatch(x, p2):
745 inst_path = header_path
749 if x.find(':') != -1:
754 inn = self.path.find_resource(name)
757 raise ValueError("could not find the public header %r in %r" % (name, self.path))
758 out = inn.change_ext('.inst.h')
759 self.create_task('header', inn, out)
765 inst_path = inst_path + '/'
766 inst_path = inst_path + dest
768 self.bld.install_as('${INCLUDEDIR}/%s' % inst_path, out, self.env)
770 self.bld.hnodemap[inn.id] = inst_path
772 # create a hash (not md5) to make sure the headers are re-created if something changes
774 lst = list(self.bld.hnodemap.keys())
777 val = hash((val, k, self.bld.hnodemap[k]))
778 self.bld.env.HEADER_DEPS = val
780 def PUBLIC_HEADERS(bld, public_headers, header_path=None):
781 '''install some headers
783 header_path may either be a string that is added to the INCLUDEDIR,
784 or it can be a dictionary of wildcard patterns which map to destination
785 directories relative to INCLUDEDIR
787 bld.SET_BUILD_GROUP('final')
788 ret = bld(features=['pubh'], headers=public_headers, header_path=header_path)
790 Build.BuildContext.PUBLIC_HEADERS = PUBLIC_HEADERS
793 def subst_at_vars(task):
794 '''substiture @VAR@ style variables in a file'''
795 src = task.inputs[0].srcpath(task.env)
796 tgt = task.outputs[0].bldpath(task.env)
802 a = re.split('(@\w+@)', s)
805 back_sub = [ ('PREFIX', '${prefix}'), ('EXEC_PREFIX', '${exec_prefix}')]
807 if re.match('@\w+@', v):
809 if not vname in task.env and vname.upper() in task.env:
810 vname = vname.upper()
811 if not vname in task.env:
812 Logs.error("Unknown substitution %s in %s" % (v, task.name))
814 v = SUBST_VARS_RECURSIVE(task.env[vname], task.env)
815 # now we back substitute the allowed pc vars
816 for (b, m) in back_sub:
819 if not b in done_var:
820 # we don't want to substitute the first usage
826 contents = ''.join(out)
828 s = f.write(contents)
834 def PKG_CONFIG_FILES(bld, pc_files, vnum=None):
835 '''install some pkg_config pc files'''
836 dest = '${PKGCONFIGDIR}'
837 dest = bld.EXPAND_VARIABLES(dest)
838 for f in TO_LIST(pc_files):
839 base=os.path.basename(f)
840 t = bld.SAMBA_GENERATOR('PKGCONFIG_%s' % base,
845 t.env.PACKAGE_VERSION = vnum
846 INSTALL_FILES(bld, dest, f, flat=True, destname=base)
847 Build.BuildContext.PKG_CONFIG_FILES = PKG_CONFIG_FILES
850 def MANPAGES(bld, manpages):
851 '''build and install manual pages'''
852 bld.env.MAN_XSL = 'http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl'
853 for m in manpages.split():
855 bld.SAMBA_GENERATOR(m,
859 rule='${XSLTPROC} -o ${TGT} ${MAN_XSL} ${SRC}'
861 bld.INSTALL_FILES('${MANDIR}/man%s' % m[-1], m, flat=True)
862 Build.BuildContext.MANPAGES = MANPAGES
865 #############################################################
866 # give a nicer display when building different types of files
867 def progress_display(self, msg, fname):
868 col1 = Logs.colors(self.color)
869 col2 = Logs.colors.NORMAL
870 total = self.position[1]
872 fs = '[%%%dd/%%%dd] %s %%s%%s%%s\n' % (n, n, msg)
873 return fs % (self.position[0], self.position[1], col1, fname, col2)
875 def link_display(self):
876 if Options.options.progress_bar != 0:
877 return Task.Task.old_display(self)
878 fname = self.outputs[0].bldpath(self.env)
879 return progress_display(self, 'Linking', fname)
880 Task.TaskBase.classes['cc_link'].display = link_display
882 def samba_display(self):
883 if Options.options.progress_bar != 0:
884 return Task.Task.old_display(self)
886 targets = LOCAL_CACHE(self, 'TARGET_TYPE')
887 if self.name in targets:
888 target_type = targets[self.name]
889 type_map = { 'GENERATOR' : 'Generating',
890 'PROTOTYPE' : 'Generating'
892 if target_type in type_map:
893 return progress_display(self, type_map[target_type], self.name)
895 fname = self.inputs[0].bldpath(self.env)
896 if fname[0:3] == '../':
898 ext_loc = fname.rfind('.')
900 return Task.Task.old_display(self)
901 ext = fname[ext_loc:]
903 ext_map = { '.idl' : 'Compiling IDL',
904 '.et' : 'Compiling ERRTABLE',
905 '.asn1': 'Compiling ASN1',
908 return progress_display(self, ext_map[ext], fname)
909 return Task.Task.old_display(self)
911 Task.TaskBase.classes['Task'].old_display = Task.TaskBase.classes['Task'].display
912 Task.TaskBase.classes['Task'].display = samba_display