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_autoproto import *
18 from samba_python import *
19 from samba_deps import *
20 from samba_bundled import *
22 import samba_conftests
31 # some systems have broken threading in python
32 if os.environ.get('WAF_NOTHREADS') == '1':
37 os.putenv('PYTHONUNBUFFERED', '1')
40 if Constants.HEXVERSION < 0x105019:
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
46 Alternatively, please use ./autogen-waf.sh, and then
47 run ./configure and make as usual. That will call the right version of waf.
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)
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):
74 elif os.path.exists(blib_src):
75 shutil.rmtree(blib_src)
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:
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:
86 cache[subsystem].append( { 'TARGET':target, 'INIT_FUNCTION':init_function } )
87 Build.BuildContext.ADD_INIT_FUNCTION = ADD_INIT_FUNCTION
91 #################################################################
92 def SAMBA_LIBRARY(bld, libname, source,
101 external_library=False,
111 target_type='LIBRARY',
112 bundled_extension=True,
118 private_library=False,
119 grouping_library=False,
121 '''define a Samba library'''
124 SET_TARGET_TYPE(bld, libname, 'DISABLED')
127 source = bld.EXPAND_VARIABLES(source, vars=vars)
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')
134 if BUILTIN_LIBRARY(bld, libname):
137 obj_target = libname + '.objlist'
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,
145 public_deps = public_deps,
147 public_headers = public_headers,
148 header_path = header_path,
151 autoproto = autoproto,
152 depends_on = depends_on,
153 hide_symbols = hide_symbols,
154 pyext = (target_type == "PYTHON"),
155 local_include = local_include)
157 if BUILTIN_LIBRARY(bld, libname):
160 if not SET_TARGET_TYPE(bld, libname, target_type):
163 # the library itself will depend on that object target
164 deps += ' ' + public_deps
166 deps.append(obj_target)
168 realname = bld.map_shlib_extension(realname, python=(target_type=='PYTHON'))
169 link_name = bld.map_shlib_extension(link_name, python=(target_type=='PYTHON'))
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)
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:]
181 bundled_name = BUNDLED_NAME(bld, libname, bundled_extension, private_library)
183 features = 'cc cshlib symlink_lib install_lib'
184 if target_type == 'PYTHON':
187 features += ' pyembed'
189 features += ' abi_check'
192 abi_file = os.path.join(bld.curdir, abi_file)
194 bld.SET_BUILD_GROUP(group)
198 target = bundled_name,
199 samba_cflags = CURRENT_LDFLAGS(bld, libname, cflags),
200 depends_on = depends_on,
202 samba_includes = includes,
203 local_include = local_include,
206 samba_inst_path = install_path,
208 samba_realname = realname,
209 samba_install = install,
211 abi_match = abi_match,
212 private_library = private_library,
213 grouping_library=grouping_library
216 if realname and not link_name:
217 link_name = 'shared/%s' % realname
220 t.link_name = link_name
222 if pc_files is not None:
223 bld.PKG_CONFIG_FILES(pc_files, vnum=vnum)
225 if manpages is not None and 'XSLTPROC_MANPAGES' in bld.env and bld.env['XSLTPROC_MANPAGES']:
226 bld.MANPAGES(manpages)
229 Build.BuildContext.SAMBA_LIBRARY = SAMBA_LIBRARY
232 #################################################################
233 def SAMBA_BINARY(bld, binname, source,
243 use_global_deps=True,
254 '''define a Samba binary'''
257 SET_TARGET_TYPE(bld, binname, 'DISABLED')
260 if not SET_TARGET_TYPE(bld, binname, 'BINARY'):
263 features = 'cc cprogram symlink_bin install_bin'
265 features += ' pyembed'
267 obj_target = binname + '.objlist'
269 source = bld.EXPAND_VARIABLES(source, vars=vars)
270 source = unique_list(TO_LIST(source))
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,
281 autoproto = autoproto,
282 subsystem_name = subsystem_name,
283 local_include = local_include,
284 use_hostcc = use_hostcc,
286 use_global_deps= use_global_deps)
288 bld.SET_BUILD_GROUP(group)
290 # the binary itself will depend on that object target
292 deps.append(obj_target)
298 samba_cflags = CURRENT_LDFLAGS(bld, binname, cflags),
300 samba_includes = includes,
301 local_include = local_include,
302 samba_modules = modules,
304 samba_subsystem= subsystem_name,
306 samba_inst_path= install_path,
307 samba_install = install
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)
316 if manpages is not None and 'XSLTPROC_MANPAGES' in bld.env and bld.env['XSLTPROC_MANPAGES']:
317 bld.MANPAGES(manpages)
319 Build.BuildContext.SAMBA_BINARY = SAMBA_BINARY
322 #################################################################
323 def SAMBA_MODULE(bld, modname, source,
329 autoproto_extra_source='',
332 internal_module=True,
338 '''define a Samba module.'''
340 source = bld.EXPAND_VARIABLES(source, vars=vars)
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
347 bld.SAMBA_SUBSYSTEM(modname, source,
351 autoproto_extra_source=autoproto_extra_source,
353 local_include=local_include,
356 bld.ADD_INIT_FUNCTION(subsystem, modname, init_function)
360 SET_TARGET_TYPE(bld, modname, 'DISABLED')
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
367 cflags += " -D%s=samba_init_module" % init_function
369 basename = modname + '-base'
370 bld.SAMBA_LIBRARY(basename,
374 autoproto = autoproto,
375 local_include=local_include,
381 aliases = TO_LIST(aliases)
382 aliases.append(modname)
384 for alias in aliases:
385 bld.SAMBA_MODULE(alias,
387 internal_module=False,
389 init_function=init_function,
394 obj_target = modname + '.objlist'
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+"_"):]
404 realname = bld.make_libname(realname)
405 while realname.startswith("lib"):
406 realname = realname[len("lib"):]
408 build_link_name = "modules/%s/%s" % (subsystem, realname)
411 cflags += " -D%s=samba_init_module" % init_function
413 bld.SAMBA_LIBRARY(modname,
418 autoproto = autoproto,
419 local_include=local_include,
421 link_name=build_link_name,
422 install_path="${MODULESDIR}/%s" % subsystem,
427 Build.BuildContext.SAMBA_MODULE = SAMBA_MODULE
430 #################################################################
431 def SAMBA_SUBSYSTEM(bld, modname, source,
440 init_function_sentinal=None,
442 autoproto_extra_source='',
445 local_include_first=True,
449 use_global_deps=True,
453 '''define a Samba subsystem'''
456 SET_TARGET_TYPE(bld, modname, 'DISABLED')
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')
464 if not SET_TARGET_TYPE(bld, modname, 'SUBSYSTEM'):
467 source = bld.EXPAND_VARIABLES(source, vars=vars)
468 source = unique_list(TO_LIST(source))
470 deps += ' ' + public_deps
472 bld.SET_BUILD_GROUP(group)
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
493 if cflags_end is not None:
494 t.samba_cflags.extend(TO_LIST(cflags_end))
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)
503 Build.BuildContext.SAMBA_SUBSYSTEM = SAMBA_SUBSYSTEM
506 def SAMBA_GENERATOR(bld, name, rule, source='', target='',
507 group='generators', enabled=True,
512 '''A generic source generator target'''
514 if not SET_TARGET_TYPE(bld, name, 'GENERATOR'):
520 bld.SET_BUILD_GROUP(group)
523 source=bld.EXPAND_VARIABLES(source, vars=vars),
525 shell=isinstance(rule, str),
534 if public_headers is not None:
535 bld.PUBLIC_HEADERS(public_headers, header_path=header_path)
537 Build.BuildContext.SAMBA_GENERATOR = SAMBA_GENERATOR
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
562 def SET_BUILD_GROUP(bld, group):
563 '''set the current build group'''
564 if not 'USING_BUILD_GROUPS' in bld.env:
567 Build.BuildContext.SET_BUILD_GROUP = SET_BUILD_GROUP
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):
577 st = os.stat(filename)
578 if stat.S_ISDIR(st[stat.ST_MODE]): raise IOError('not a file')
580 m.update(str(st.st_mtime))
581 m.update(str(st.st_size))
584 Utils.h_file = h_file
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')
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
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'''
602 source = bld.path.ant_glob(pattern)
604 bld.SET_BUILD_GROUP('build_source')
605 for s in TO_LIST(source):
607 if installname != None:
609 target = os.path.join(installdir, iname)
610 tgtdir = os.path.dirname(os.path.join(bld.srcnode.abspath(bld.env), '..', target))
612 t = bld(features='copy_script',
617 t.env.LINK_TARGET = target
619 Build.BuildContext.SAMBA_SCRIPT = SAMBA_SCRIPT
622 def install_file(bld, destdir, file, chmod=MODE_644, flat=False,
623 python_fixup=False, destname=None, base_name=None):
625 destdir = bld.EXPAND_VARIABLES(destdir)
629 destname = os.path.basename(destname)
630 dest = os.path.join(destdir, destname)
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"
637 # Eliminate updating sys.path if the target python dir is already
639 regex = "s|sys.path.insert.*bin/python.*$||g"
640 bld.SAMBA_GENERATOR('python_%s' % destname,
641 rule="sed '%s' < ${SRC} > ${TGT}" % regex,
646 file = os.path.join(base_name, file)
647 bld.install_as(dest, file, chmod=chmod)
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,
657 Build.BuildContext.INSTALL_FILES = INSTALL_FILES
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))
667 files2.append(os_path_relpath(f, trim_path))
672 if fnmatch.fnmatch(f, exclude):
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
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
688 re_header = re.compile('#include[ \t]*"([^"]+)"', re.I | re.M)
689 class header_task(Task.Task):
691 The public headers (the one installed on the system) have both
692 different paths and contents, so the rename is not enough.
694 Intermediate .inst.h files are created because path manipulation
695 may be slow. The substitution is thus performed only once.
700 vars = ['INCLUDEDIR', 'HEADER_DEPS']
703 txt = self.inputs[0].read(self.env)
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')
708 # use a regexp to substitute the #include lines in the files
709 map = self.generator.bld.hnodemap
710 dirnodes = self.generator.bld.hnodedirs
715 # pokemon headers: gotta catch'em all!
717 if s.startswith('bin/default'):
718 node = self.generator.bld.srcnode.find_resource(s.replace('bin/default/', ''))
720 Logs.warn('could not find the public header for %r' % s)
724 Logs.warn('could not find the public header replacement for build header %r' % s)
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)
734 Logs.warn('could not find the public header replacement for source header %r %r' % (s, node))
736 Logs.warn('-> could not find the public header for %r' % s)
738 return "#include <%s>" % fin
741 txt = re_header.sub(repl, txt)
743 # and write the output file
746 f = open(self.outputs[0].abspath(self.env), 'w')
752 @TaskGen.feature('pubh')
753 def make_public_headers(self):
755 collect the public headers to process and to install, then
756 create the substitutions (name and contents)
759 if not self.bld.is_install:
760 # install time only (lazy)
764 # hnodedirs: list of folders for searching the headers
765 # hnodemap: node ids and replacement string (node objects are unique)
767 self.bld.hnodedirs.append(self.path)
768 except AttributeError:
769 self.bld.hnodemap = {}
770 self.bld.hnodedirs = [self.bld.srcnode, self.path]
772 for k in 'source4 source4/include lib/talloc lib/tevent/ source4/lib/ldb/include/'.split():
773 node = self.bld.srcnode.find_dir(k)
775 self.bld.hnodedirs.append(node)
777 header_path = getattr(self, 'header_path', None) or ''
779 for x in self.to_list(self.headers):
781 # too complicated, but what was the original idea?
782 if isinstance(header_path, list):
784 for (p1, dir) in header_path:
785 lst = self.to_list(p1)
787 if fnmatch.fnmatch(x, p2):
795 inst_path = header_path
799 if x.find(':') != -1:
804 inn = self.path.find_resource(name)
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)
815 inst_path = inst_path + '/'
816 inst_path = inst_path + dest
818 self.bld.install_as('${INCLUDEDIR}/%s' % inst_path, out, self.env)
820 self.bld.hnodemap[inn.id] = inst_path
822 # create a hash (not md5) to make sure the headers are re-created if something changes
824 lst = list(self.bld.hnodemap.keys())
827 val = hash((val, k, self.bld.hnodemap[k]))
828 self.bld.env.HEADER_DEPS = val
830 def PUBLIC_HEADERS(bld, public_headers, header_path=None):
831 '''install some headers
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
837 bld.SET_BUILD_GROUP('final')
838 ret = bld(features=['pubh'], headers=public_headers, header_path=header_path)
840 Build.BuildContext.PUBLIC_HEADERS = PUBLIC_HEADERS
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)
852 a = re.split('(@\w+@)', s)
855 back_sub = [ ('PREFIX', '${prefix}'), ('EXEC_PREFIX', '${exec_prefix}')]
857 if re.match('@\w+@', v):
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))
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:
869 if not b in done_var:
870 # we don't want to substitute the first usage
876 contents = ''.join(out)
878 s = f.write(contents)
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,
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
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():
905 bld.SAMBA_GENERATOR(m,
909 rule='${XSLTPROC} -o ${TGT} --nonet ${MAN_XSL} ${SRC}'
911 bld.INSTALL_FILES('${MANDIR}/man%s' % m[-1], m, flat=True)
912 Build.BuildContext.MANPAGES = MANPAGES
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]
922 fs = '[%%%dd/%%%dd] %s %%s%%s%%s\n' % (n, n, msg)
923 return fs % (self.position[0], self.position[1], col1, fname, col2)
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
932 def samba_display(self):
933 if Options.options.progress_bar != 0:
934 return Task.Task.old_display(self)
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'
942 if target_type in type_map:
943 return progress_display(self, type_map[target_type], self.name)
945 fname = self.inputs[0].bldpath(self.env)
946 if fname[0:3] == '../':
948 ext_loc = fname.rfind('.')
950 return Task.Task.old_display(self)
951 ext = fname[ext_loc:]
953 ext_map = { '.idl' : 'Compiling IDL',
954 '.et' : 'Compiling ERRTABLE',
955 '.asn1': 'Compiling ASN1',
958 return progress_display(self, ext_map[ext], fname)
959 return Task.Task.old_display(self)
961 Task.TaskBase.classes['Task'].old_display = Task.TaskBase.classes['Task'].display
962 Task.TaskBase.classes['Task'].display = samba_display