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,
120 '''define a Samba library'''
123 SET_TARGET_TYPE(bld, libname, 'DISABLED')
126 source = bld.EXPAND_VARIABLES(source, vars=vars)
128 # remember empty libraries, so we can strip the dependencies
129 if ((source == '') or (source == [])) and deps == '' and public_deps == '':
130 SET_TARGET_TYPE(bld, libname, 'EMPTY')
133 if BUILTIN_LIBRARY(bld, libname):
136 obj_target = libname + '.objlist'
138 # first create a target for building the object files for this library
139 # by separating in this way, we avoid recompiling the C files
140 # separately for the install library and the build library
141 bld.SAMBA_SUBSYSTEM(obj_target,
144 public_deps = public_deps,
146 public_headers = public_headers,
147 header_path = header_path,
150 autoproto = autoproto,
151 depends_on = depends_on,
152 hide_symbols = hide_symbols,
153 pyext = (target_type == "PYTHON"),
154 local_include = local_include)
156 if BUILTIN_LIBRARY(bld, libname):
159 if not SET_TARGET_TYPE(bld, libname, target_type):
162 # the library itself will depend on that object target
163 deps += ' ' + public_deps
165 deps.append(obj_target)
167 if target_type == 'PYTHON' or realname or not is_bundled:
168 # Sanitize the library name
169 bundled_name = libname.lower().replace('_', '-')
170 while bundled_name.startswith("lib"):
171 bundled_name = bundled_name[3:]
173 bundled_name = BUNDLED_NAME(bld, libname, bundled_extension)
175 features = 'cc cshlib symlink_lib install_lib'
176 if target_type == 'PYTHON':
179 features += ' pyembed'
181 features += ' abi_check'
184 abi_file = os.path.join(bld.curdir, abi_file)
186 bld.SET_BUILD_GROUP(group)
190 target = bundled_name,
191 samba_cflags = CURRENT_CFLAGS(bld, libname, cflags),
192 depends_on = depends_on,
194 samba_includes = includes,
195 local_include = local_include,
198 samba_inst_path = install_path,
200 samba_realname = realname,
201 samba_install = install,
203 abi_match = abi_match,
204 is_bundled = is_bundled
207 if realname and not link_name:
208 link_name = 'shared/%s' % realname
211 t.link_name = link_name
213 if pc_files is not None:
214 bld.PKG_CONFIG_FILES(pc_files, vnum=vnum)
216 if manpages is not None and 'XSLTPROC_MANPAGES' in bld.env and bld.env['XSLTPROC_MANPAGES']:
217 bld.MANPAGES(manpages)
220 Build.BuildContext.SAMBA_LIBRARY = SAMBA_LIBRARY
223 #################################################################
224 def SAMBA_BINARY(bld, binname, source,
234 use_global_deps=True,
245 '''define a Samba binary'''
248 SET_TARGET_TYPE(bld, binname, 'DISABLED')
251 if not SET_TARGET_TYPE(bld, binname, 'BINARY'):
254 features = 'cc cprogram symlink_bin install_bin'
256 features += ' pyembed'
258 obj_target = binname + '.objlist'
260 source = bld.EXPAND_VARIABLES(source, vars=vars)
261 source = unique_list(TO_LIST(source))
263 # first create a target for building the object files for this binary
264 # by separating in this way, we avoid recompiling the C files
265 # separately for the install binary and the build binary
266 bld.SAMBA_SUBSYSTEM(obj_target,
272 autoproto = autoproto,
273 subsystem_name = subsystem_name,
274 local_include = local_include,
275 use_hostcc = use_hostcc,
277 use_global_deps= use_global_deps)
279 bld.SET_BUILD_GROUP(group)
281 # the binary itself will depend on that object target
283 deps.append(obj_target)
289 samba_cflags = CURRENT_CFLAGS(bld, binname, cflags),
291 samba_includes = includes,
292 local_include = local_include,
293 samba_modules = modules,
295 samba_subsystem= subsystem_name,
297 samba_inst_path= install_path,
298 samba_install = install
301 # setup the subsystem_name as an alias for the real
302 # binary name, so it can be found when expanding
303 # subsystem dependencies
304 if subsystem_name is not None:
305 bld.TARGET_ALIAS(subsystem_name, binname)
307 if manpages is not None and 'XSLTPROC_MANPAGES' in bld.env and bld.env['XSLTPROC_MANPAGES']:
308 bld.MANPAGES(manpages)
310 Build.BuildContext.SAMBA_BINARY = SAMBA_BINARY
313 #################################################################
314 def SAMBA_MODULE(bld, modname, source,
320 autoproto_extra_source='',
323 internal_module=True,
329 '''define a Samba module.'''
331 source = bld.EXPAND_VARIABLES(source, vars=vars)
333 if internal_module or BUILTIN_LIBRARY(bld, modname):
334 # treat internal modules as subsystems for now
335 if subsystem is not None:
336 deps += ' ' + subsystem
338 bld.SAMBA_SUBSYSTEM(modname, source,
342 autoproto_extra_source=autoproto_extra_source,
344 local_include=local_include,
347 bld.ADD_INIT_FUNCTION(subsystem, modname, init_function)
351 SET_TARGET_TYPE(bld, modname, 'DISABLED')
354 modnames = [modname] + TO_LIST(aliases)
355 for modname in modnames:
356 obj_target = modname + '.objlist'
359 if subsystem is not None:
360 deps += ' ' + subsystem
361 while realname.startswith("lib"+subsystem+"_"):
362 realname = realname[len("lib"+subsystem+"_"):]
363 while realname.startswith(subsystem+"_"):
364 realname = realname[len(subsystem+"_"):]
366 realname = bld.env.shlib_PATTERN % realname
367 while realname.startswith("lib"):
368 realname = realname[len("lib"):]
370 build_link_name = "modules/%s/%s" % (subsystem, realname)
373 cflags += " -D%s=samba_init_module" % init_function
375 bld.SAMBA_LIBRARY(modname,
380 autoproto = autoproto,
381 local_include=local_include,
383 link_name=build_link_name,
384 install_path="${MODULESDIR}/%s" % subsystem,
388 Build.BuildContext.SAMBA_MODULE = SAMBA_MODULE
391 #################################################################
392 def SAMBA_SUBSYSTEM(bld, modname, source,
401 init_function_sentinal=None,
403 autoproto_extra_source='',
406 local_include_first=True,
410 use_global_deps=True,
414 '''define a Samba subsystem'''
417 SET_TARGET_TYPE(bld, modname, 'DISABLED')
420 # remember empty subsystems, so we can strip the dependencies
421 if ((source == '') or (source == [])) and deps == '' and public_deps == '':
422 SET_TARGET_TYPE(bld, modname, 'EMPTY')
425 if not SET_TARGET_TYPE(bld, modname, 'SUBSYSTEM'):
428 source = bld.EXPAND_VARIABLES(source, vars=vars)
429 source = unique_list(TO_LIST(source))
431 deps += ' ' + public_deps
433 bld.SET_BUILD_GROUP(group)
443 samba_cflags = CURRENT_CFLAGS(bld, modname, cflags, hide_symbols=hide_symbols),
444 depends_on = depends_on,
445 samba_deps = TO_LIST(deps),
446 samba_includes = includes,
447 local_include = local_include,
448 local_include_first = local_include_first,
449 samba_subsystem= subsystem_name,
450 samba_use_hostcc = use_hostcc,
451 samba_use_global_deps = use_global_deps
454 if cflags_end is not None:
455 t.samba_cflags.extend(TO_LIST(cflags_end))
457 if autoproto is not None:
458 bld.SAMBA_AUTOPROTO(autoproto, source + TO_LIST(autoproto_extra_source))
459 if public_headers is not None:
460 bld.PUBLIC_HEADERS(public_headers, header_path=header_path)
464 Build.BuildContext.SAMBA_SUBSYSTEM = SAMBA_SUBSYSTEM
467 def SAMBA_GENERATOR(bld, name, rule, source='', target='',
468 group='generators', enabled=True,
473 '''A generic source generator target'''
475 if not SET_TARGET_TYPE(bld, name, 'GENERATOR'):
481 bld.SET_BUILD_GROUP(group)
484 source=bld.EXPAND_VARIABLES(source, vars=vars),
486 shell=isinstance(rule, str),
495 if public_headers is not None:
496 bld.PUBLIC_HEADERS(public_headers, header_path=header_path)
498 Build.BuildContext.SAMBA_GENERATOR = SAMBA_GENERATOR
503 def SETUP_BUILD_GROUPS(bld):
504 '''setup build groups used to ensure that the different build
505 phases happen consecutively'''
506 bld.p_ln = bld.srcnode # we do want to see all targets!
507 bld.env['USING_BUILD_GROUPS'] = True
508 bld.add_group('setup')
509 bld.add_group('build_compiler_source')
510 bld.add_group('base_libraries')
511 bld.add_group('generators')
512 bld.add_group('compiler_prototypes')
513 bld.add_group('compiler_libraries')
514 bld.add_group('build_compilers')
515 bld.add_group('build_source')
516 bld.add_group('prototypes')
517 bld.add_group('main')
518 bld.add_group('binaries')
519 bld.add_group('final')
520 Build.BuildContext.SETUP_BUILD_GROUPS = SETUP_BUILD_GROUPS
523 def SET_BUILD_GROUP(bld, group):
524 '''set the current build group'''
525 if not 'USING_BUILD_GROUPS' in bld.env:
528 Build.BuildContext.SET_BUILD_GROUP = SET_BUILD_GROUP
533 def ENABLE_TIMESTAMP_DEPENDENCIES(conf):
534 """use timestamps instead of file contents for deps
535 this currently doesn't work"""
536 def h_file(filename):
538 st = os.stat(filename)
539 if stat.S_ISDIR(st[stat.ST_MODE]): raise IOError('not a file')
541 m.update(str(st.st_mtime))
542 m.update(str(st.st_size))
545 Utils.h_file = h_file
549 t = Task.simple_task_type('copy_script', 'rm -f ${LINK_TARGET} && ln -s ${SRC[0].abspath(env)} ${LINK_TARGET}',
550 shell=True, color='PINK', ext_in='.bin')
553 @feature('copy_script')
554 @before('apply_link')
555 def copy_script(self):
556 tsk = self.create_task('copy_script', self.allnodes[0])
557 tsk.env.TARGET = self.target
559 def SAMBA_SCRIPT(bld, name, pattern, installdir, installname=None):
560 '''used to copy scripts from the source tree into the build directory
561 for use by selftest'''
563 source = bld.path.ant_glob(pattern)
565 bld.SET_BUILD_GROUP('build_source')
566 for s in TO_LIST(source):
568 if installname != None:
570 target = os.path.join(installdir, iname)
571 tgtdir = os.path.dirname(os.path.join(bld.srcnode.abspath(bld.env), '..', target))
573 t = bld(features='copy_script',
578 t.env.LINK_TARGET = target
580 Build.BuildContext.SAMBA_SCRIPT = SAMBA_SCRIPT
583 def install_file(bld, destdir, file, chmod=MODE_644, flat=False,
584 python_fixup=False, destname=None, base_name=None):
586 destdir = bld.EXPAND_VARIABLES(destdir)
590 destname = os.path.basename(destname)
591 dest = os.path.join(destdir, destname)
593 # fixup the python path it will use to find Samba modules
594 inst_file = file + '.inst'
595 if bld.env["PYTHONDIR"] not in sys.path:
596 regex = "s|\(sys.path.insert.*\)bin/python\(.*\)$|\\1${PYTHONDIR}\\2|g"
598 # Eliminate updating sys.path if the target python dir is already
600 regex = "s|sys.path.insert.*bin/python.*$||g"
601 bld.SAMBA_GENERATOR('python_%s' % destname,
602 rule="sed '%s' < ${SRC} > ${TGT}" % regex,
607 file = os.path.join(base_name, file)
608 bld.install_as(dest, file, chmod=chmod)
611 def INSTALL_FILES(bld, destdir, files, chmod=MODE_644, flat=False,
612 python_fixup=False, destname=None, base_name=None):
613 '''install a set of files'''
614 for f in TO_LIST(files):
615 install_file(bld, destdir, f, chmod=chmod, flat=flat,
616 python_fixup=python_fixup, destname=destname,
618 Build.BuildContext.INSTALL_FILES = INSTALL_FILES
621 def INSTALL_WILDCARD(bld, destdir, pattern, chmod=MODE_644, flat=False,
622 python_fixup=False, exclude=None, trim_path=None):
623 '''install a set of files matching a wildcard pattern'''
624 files=TO_LIST(bld.path.ant_glob(pattern))
628 files2.append(os_path_relpath(f, trim_path))
633 if fnmatch.fnmatch(f, exclude):
635 INSTALL_FILES(bld, destdir, files, chmod=chmod, flat=flat,
636 python_fixup=python_fixup, base_name=trim_path)
637 Build.BuildContext.INSTALL_WILDCARD = INSTALL_WILDCARD
640 def INSTALL_DIRS(bld, destdir, dirs):
641 '''install a set of directories'''
642 destdir = bld.EXPAND_VARIABLES(destdir)
643 dirs = bld.EXPAND_VARIABLES(dirs)
644 for d in TO_LIST(dirs):
645 bld.install_dir(os.path.join(destdir, d))
646 Build.BuildContext.INSTALL_DIRS = INSTALL_DIRS
649 re_header = re.compile('#include[ \t]*"([^"]+)"', re.I | re.M)
650 class header_task(Task.Task):
652 The public headers (the one installed on the system) have both
653 different paths and contents, so the rename is not enough.
655 Intermediate .inst.h files are created because path manipulation
656 may be slow. The substitution is thus performed only once.
661 vars = ['INCLUDEDIR', 'HEADER_DEPS']
664 txt = self.inputs[0].read(self.env)
666 # hard-coded string, but only present in samba4 (I promise, you won't feel a thing)
667 txt = txt.replace('#if _SAMBA_BUILD_ == 4', '#if 1\n')
669 # use a regexp to substitute the #include lines in the files
670 map = self.generator.bld.hnodemap
671 dirnodes = self.generator.bld.hnodedirs
676 # pokemon headers: gotta catch'em all!
678 if s.startswith('bin/default'):
679 node = self.generator.bld.srcnode.find_resource(s.replace('bin/default/', ''))
681 Logs.warn('could not find the public header for %r' % s)
685 Logs.warn('could not find the public header replacement for build header %r' % s)
687 # this part is more difficult since the path may be relative to anything
688 for dirnode in dirnodes:
689 node = dirnode.find_resource(s)
695 Logs.warn('could not find the public header replacement for source header %r %r' % (s, node))
697 Logs.warn('-> could not find the public header for %r' % s)
699 return "#include <%s>" % fin
702 txt = re_header.sub(repl, txt)
704 # and write the output file
707 f = open(self.outputs[0].abspath(self.env), 'w')
713 @TaskGen.feature('pubh')
714 def make_public_headers(self):
716 collect the public headers to process and to install, then
717 create the substitutions (name and contents)
720 if not self.bld.is_install:
721 # install time only (lazy)
725 # hnodedirs: list of folders for searching the headers
726 # hnodemap: node ids and replacement string (node objects are unique)
728 self.bld.hnodedirs.append(self.path)
729 except AttributeError:
730 self.bld.hnodemap = {}
731 self.bld.hnodedirs = [self.bld.srcnode, self.path]
733 for k in 'source4 source4/include lib/talloc lib/tevent/ source4/lib/ldb/include/'.split():
734 node = self.bld.srcnode.find_dir(k)
736 self.bld.hnodedirs.append(node)
738 header_path = getattr(self, 'header_path', None) or ''
740 for x in self.to_list(self.headers):
742 # too complicated, but what was the original idea?
743 if isinstance(header_path, list):
745 for (p1, dir) in header_path:
746 lst = self.to_list(p1)
748 if fnmatch.fnmatch(x, p2):
756 inst_path = header_path
760 if x.find(':') != -1:
765 inn = self.path.find_resource(name)
768 raise ValueError("could not find the public header %r in %r" % (name, self.path))
769 out = inn.change_ext('.inst.h')
770 self.create_task('header', inn, out)
776 inst_path = inst_path + '/'
777 inst_path = inst_path + dest
779 self.bld.install_as('${INCLUDEDIR}/%s' % inst_path, out, self.env)
781 self.bld.hnodemap[inn.id] = inst_path
783 # create a hash (not md5) to make sure the headers are re-created if something changes
785 lst = list(self.bld.hnodemap.keys())
788 val = hash((val, k, self.bld.hnodemap[k]))
789 self.bld.env.HEADER_DEPS = val
791 def PUBLIC_HEADERS(bld, public_headers, header_path=None):
792 '''install some headers
794 header_path may either be a string that is added to the INCLUDEDIR,
795 or it can be a dictionary of wildcard patterns which map to destination
796 directories relative to INCLUDEDIR
798 bld.SET_BUILD_GROUP('final')
799 ret = bld(features=['pubh'], headers=public_headers, header_path=header_path)
801 Build.BuildContext.PUBLIC_HEADERS = PUBLIC_HEADERS
804 def subst_at_vars(task):
805 '''substiture @VAR@ style variables in a file'''
806 src = task.inputs[0].srcpath(task.env)
807 tgt = task.outputs[0].bldpath(task.env)
813 a = re.split('(@\w+@)', s)
816 back_sub = [ ('PREFIX', '${prefix}'), ('EXEC_PREFIX', '${exec_prefix}')]
818 if re.match('@\w+@', v):
820 if not vname in task.env and vname.upper() in task.env:
821 vname = vname.upper()
822 if not vname in task.env:
823 Logs.error("Unknown substitution %s in %s" % (v, task.name))
825 v = SUBST_VARS_RECURSIVE(task.env[vname], task.env)
826 # now we back substitute the allowed pc vars
827 for (b, m) in back_sub:
830 if not b in done_var:
831 # we don't want to substitute the first usage
837 contents = ''.join(out)
839 s = f.write(contents)
845 def PKG_CONFIG_FILES(bld, pc_files, vnum=None):
846 '''install some pkg_config pc files'''
847 dest = '${PKGCONFIGDIR}'
848 dest = bld.EXPAND_VARIABLES(dest)
849 for f in TO_LIST(pc_files):
850 base=os.path.basename(f)
851 t = bld.SAMBA_GENERATOR('PKGCONFIG_%s' % base,
856 t.env.PACKAGE_VERSION = vnum
857 INSTALL_FILES(bld, dest, f, flat=True, destname=base)
858 Build.BuildContext.PKG_CONFIG_FILES = PKG_CONFIG_FILES
861 def MANPAGES(bld, manpages):
862 '''build and install manual pages'''
863 bld.env.MAN_XSL = 'http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl'
864 for m in manpages.split():
866 bld.SAMBA_GENERATOR(m,
870 rule='${XSLTPROC} -o ${TGT} --nonet ${MAN_XSL} ${SRC}'
872 bld.INSTALL_FILES('${MANDIR}/man%s' % m[-1], m, flat=True)
873 Build.BuildContext.MANPAGES = MANPAGES
876 #############################################################
877 # give a nicer display when building different types of files
878 def progress_display(self, msg, fname):
879 col1 = Logs.colors(self.color)
880 col2 = Logs.colors.NORMAL
881 total = self.position[1]
883 fs = '[%%%dd/%%%dd] %s %%s%%s%%s\n' % (n, n, msg)
884 return fs % (self.position[0], self.position[1], col1, fname, col2)
886 def link_display(self):
887 if Options.options.progress_bar != 0:
888 return Task.Task.old_display(self)
889 fname = self.outputs[0].bldpath(self.env)
890 return progress_display(self, 'Linking', fname)
891 Task.TaskBase.classes['cc_link'].display = link_display
893 def samba_display(self):
894 if Options.options.progress_bar != 0:
895 return Task.Task.old_display(self)
897 targets = LOCAL_CACHE(self, 'TARGET_TYPE')
898 if self.name in targets:
899 target_type = targets[self.name]
900 type_map = { 'GENERATOR' : 'Generating',
901 'PROTOTYPE' : 'Generating'
903 if target_type in type_map:
904 return progress_display(self, type_map[target_type], self.name)
906 fname = self.inputs[0].bldpath(self.env)
907 if fname[0:3] == '../':
909 ext_loc = fname.rfind('.')
911 return Task.Task.old_display(self)
912 ext = fname[ext_loc:]
914 ext_map = { '.idl' : 'Compiling IDL',
915 '.et' : 'Compiling ERRTABLE',
916 '.asn1': 'Compiling ASN1',
919 return progress_display(self, ext_map[ext], fname)
920 return Task.Task.old_display(self)
922 Task.TaskBase.classes['Task'].old_display = Task.TaskBase.classes['Task'].display
923 Task.TaskBase.classes['Task'].display = samba_display