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,
112 target_type='LIBRARY',
113 bundled_extension=True,
119 private_library=False,
120 grouping_library=False,
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 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 hide_symbols = hide_symbols,
155 pyext = pyext or (target_type == "PYTHON"),
156 local_include = local_include)
158 if BUILTIN_LIBRARY(bld, libname):
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 realname = bld.map_shlib_extension(realname, python=(target_type=='PYTHON'))
170 link_name = bld.map_shlib_extension(link_name, python=(target_type=='PYTHON'))
172 # we don't want any public libraries without version numbers
173 if not private_library and vnum is None and target_type != 'PYTHON' and not realname:
174 raise Utils.WafError("public library '%s' must have a vnum" % libname)
176 if target_type == 'PYTHON' or realname or not private_library:
177 bundled_name = libname.replace('_', '-')
179 bundled_name = PRIVATE_NAME(bld, libname, bundled_extension, private_library)
183 Logs.error("vnum is invalid for private libraries")
185 vnum = bld.env.PACKAGE_VERSION
187 features = 'cc cshlib symlink_lib install_lib'
188 if target_type == 'PYTHON':
191 # this is quite strange. we should add pyext feature for pyext
192 # but that breaks the build. This may be a bug in the waf python tool
193 features += ' pyembed'
195 features += ' abi_check'
198 abi_file = os.path.join(bld.curdir, abi_file)
200 bld.SET_BUILD_GROUP(group)
204 target = bundled_name,
205 depends_on = depends_on,
207 samba_includes = includes,
208 local_include = local_include,
211 samba_inst_path = install_path,
213 samba_realname = realname,
214 samba_install = install,
216 abi_match = abi_match,
217 private_library = private_library,
218 grouping_library=grouping_library
221 if realname and not link_name:
222 link_name = 'shared/%s' % realname
225 t.link_name = link_name
227 if pc_files is not None:
228 bld.PKG_CONFIG_FILES(pc_files, vnum=vnum)
230 if manpages is not None and 'XSLTPROC_MANPAGES' in bld.env and bld.env['XSLTPROC_MANPAGES']:
231 bld.MANPAGES(manpages)
234 Build.BuildContext.SAMBA_LIBRARY = SAMBA_LIBRARY
237 #################################################################
238 def SAMBA_BINARY(bld, binname, source,
248 use_global_deps=True,
259 '''define a Samba binary'''
262 SET_TARGET_TYPE(bld, binname, 'DISABLED')
265 if not SET_TARGET_TYPE(bld, binname, 'BINARY'):
268 features = 'cc cprogram symlink_bin install_bin'
270 features += ' pyembed'
272 obj_target = binname + '.objlist'
274 source = bld.EXPAND_VARIABLES(source, vars=vars)
275 source = unique_list(TO_LIST(source))
277 # first create a target for building the object files for this binary
278 # by separating in this way, we avoid recompiling the C files
279 # separately for the install binary and the build binary
280 bld.SAMBA_SUBSYSTEM(obj_target,
286 autoproto = autoproto,
287 subsystem_name = subsystem_name,
288 local_include = local_include,
289 use_hostcc = use_hostcc,
291 use_global_deps= use_global_deps)
293 bld.SET_BUILD_GROUP(group)
295 # the binary itself will depend on that object target
297 deps.append(obj_target)
304 samba_includes = includes,
305 local_include = local_include,
306 samba_modules = modules,
308 samba_subsystem= subsystem_name,
310 samba_inst_path= install_path,
311 samba_install = install
314 # setup the subsystem_name as an alias for the real
315 # binary name, so it can be found when expanding
316 # subsystem dependencies
317 if subsystem_name is not None:
318 bld.TARGET_ALIAS(subsystem_name, binname)
320 if manpages is not None and 'XSLTPROC_MANPAGES' in bld.env and bld.env['XSLTPROC_MANPAGES']:
321 bld.MANPAGES(manpages)
323 Build.BuildContext.SAMBA_BINARY = SAMBA_BINARY
326 #################################################################
327 def SAMBA_MODULE(bld, modname, source,
333 autoproto_extra_source='',
336 internal_module=True,
342 '''define a Samba module.'''
344 source = bld.EXPAND_VARIABLES(source, vars=vars)
346 if internal_module or BUILTIN_LIBRARY(bld, modname):
347 # treat internal modules as subsystems for now
348 if subsystem is not None:
349 deps += ' ' + subsystem
351 bld.SAMBA_SUBSYSTEM(modname, source,
355 autoproto_extra_source=autoproto_extra_source,
357 local_include=local_include,
360 bld.ADD_INIT_FUNCTION(subsystem, modname, init_function)
364 SET_TARGET_TYPE(bld, modname, 'DISABLED')
367 if aliases is not None:
368 # if we have aliases, then create a private base library, and a set
369 # of modules on top of that library
371 cflags += " -D%s=samba_init_module" % init_function
373 basename = modname + '-base'
374 bld.SAMBA_LIBRARY(basename,
378 autoproto = autoproto,
379 local_include=local_include,
385 aliases = TO_LIST(aliases)
386 aliases.append(modname)
388 for alias in aliases:
389 bld.SAMBA_MODULE(alias,
391 internal_module=False,
393 init_function=init_function,
398 obj_target = modname + '.objlist'
401 if subsystem is not None:
402 deps += ' ' + subsystem
403 while realname.startswith("lib"+subsystem+"_"):
404 realname = realname[len("lib"+subsystem+"_"):]
405 while realname.startswith(subsystem+"_"):
406 realname = realname[len(subsystem+"_"):]
408 realname = bld.make_libname(realname)
409 while realname.startswith("lib"):
410 realname = realname[len("lib"):]
412 build_link_name = "modules/%s/%s" % (subsystem, realname)
415 cflags += " -D%s=samba_init_module" % init_function
417 bld.SAMBA_LIBRARY(modname,
422 autoproto = autoproto,
423 local_include=local_include,
425 link_name=build_link_name,
426 install_path="${MODULESDIR}/%s" % subsystem,
431 Build.BuildContext.SAMBA_MODULE = SAMBA_MODULE
434 #################################################################
435 def SAMBA_SUBSYSTEM(bld, modname, source,
444 init_function_sentinal=None,
446 autoproto_extra_source='',
449 local_include_first=True,
453 use_global_deps=True,
457 '''define a Samba subsystem'''
460 SET_TARGET_TYPE(bld, modname, 'DISABLED')
463 # remember empty subsystems, so we can strip the dependencies
464 if ((source == '') or (source == [])) and deps == '' and public_deps == '':
465 SET_TARGET_TYPE(bld, modname, 'EMPTY')
468 if not SET_TARGET_TYPE(bld, modname, 'SUBSYSTEM'):
471 source = bld.EXPAND_VARIABLES(source, vars=vars)
472 source = unique_list(TO_LIST(source))
474 deps += ' ' + public_deps
476 bld.SET_BUILD_GROUP(group)
486 samba_cflags = CURRENT_CFLAGS(bld, modname, cflags, hide_symbols=hide_symbols),
487 depends_on = depends_on,
488 samba_deps = TO_LIST(deps),
489 samba_includes = includes,
490 local_include = local_include,
491 local_include_first = local_include_first,
492 samba_subsystem= subsystem_name,
493 samba_use_hostcc = use_hostcc,
494 samba_use_global_deps = use_global_deps
497 if cflags_end is not None:
498 t.samba_cflags.extend(TO_LIST(cflags_end))
500 if autoproto is not None:
501 bld.SAMBA_AUTOPROTO(autoproto, source + TO_LIST(autoproto_extra_source))
502 if public_headers is not None:
503 bld.PUBLIC_HEADERS(public_headers, header_path=header_path)
507 Build.BuildContext.SAMBA_SUBSYSTEM = SAMBA_SUBSYSTEM
510 def SAMBA_GENERATOR(bld, name, rule, source='', target='',
511 group='generators', enabled=True,
516 '''A generic source generator target'''
518 if not SET_TARGET_TYPE(bld, name, 'GENERATOR'):
524 bld.SET_BUILD_GROUP(group)
527 source=bld.EXPAND_VARIABLES(source, vars=vars),
529 shell=isinstance(rule, str),
538 if public_headers is not None:
539 bld.PUBLIC_HEADERS(public_headers, header_path=header_path)
541 Build.BuildContext.SAMBA_GENERATOR = SAMBA_GENERATOR
546 def SETUP_BUILD_GROUPS(bld):
547 '''setup build groups used to ensure that the different build
548 phases happen consecutively'''
549 bld.p_ln = bld.srcnode # we do want to see all targets!
550 bld.env['USING_BUILD_GROUPS'] = True
551 bld.add_group('setup')
552 bld.add_group('build_compiler_source')
553 bld.add_group('base_libraries')
554 bld.add_group('generators')
555 bld.add_group('compiler_prototypes')
556 bld.add_group('compiler_libraries')
557 bld.add_group('build_compilers')
558 bld.add_group('build_source')
559 bld.add_group('prototypes')
560 bld.add_group('main')
561 bld.add_group('binaries')
562 bld.add_group('final')
563 Build.BuildContext.SETUP_BUILD_GROUPS = SETUP_BUILD_GROUPS
566 def SET_BUILD_GROUP(bld, group):
567 '''set the current build group'''
568 if not 'USING_BUILD_GROUPS' in bld.env:
571 Build.BuildContext.SET_BUILD_GROUP = SET_BUILD_GROUP
576 def ENABLE_TIMESTAMP_DEPENDENCIES(conf):
577 """use timestamps instead of file contents for deps
578 this currently doesn't work"""
579 def h_file(filename):
581 st = os.stat(filename)
582 if stat.S_ISDIR(st[stat.ST_MODE]): raise IOError('not a file')
584 m.update(str(st.st_mtime))
585 m.update(str(st.st_size))
588 Utils.h_file = h_file
592 t = Task.simple_task_type('copy_script', 'rm -f ${LINK_TARGET} && ln -s ${SRC[0].abspath(env)} ${LINK_TARGET}',
593 shell=True, color='PINK', ext_in='.bin')
596 @feature('copy_script')
597 @before('apply_link')
598 def copy_script(self):
599 tsk = self.create_task('copy_script', self.allnodes[0])
600 tsk.env.TARGET = self.target
602 def SAMBA_SCRIPT(bld, name, pattern, installdir, installname=None):
603 '''used to copy scripts from the source tree into the build directory
604 for use by selftest'''
606 source = bld.path.ant_glob(pattern)
608 bld.SET_BUILD_GROUP('build_source')
609 for s in TO_LIST(source):
611 if installname != None:
613 target = os.path.join(installdir, iname)
614 tgtdir = os.path.dirname(os.path.join(bld.srcnode.abspath(bld.env), '..', target))
616 t = bld(features='copy_script',
621 t.env.LINK_TARGET = target
623 Build.BuildContext.SAMBA_SCRIPT = SAMBA_SCRIPT
626 def install_file(bld, destdir, file, chmod=MODE_644, flat=False,
627 python_fixup=False, destname=None, base_name=None):
629 destdir = bld.EXPAND_VARIABLES(destdir)
633 destname = os.path.basename(destname)
634 dest = os.path.join(destdir, destname)
636 # fixup the python path it will use to find Samba modules
637 inst_file = file + '.inst'
638 if bld.env["PYTHONDIR"] not in sys.path:
639 regex = "s|\(sys.path.insert.*\)bin/python\(.*\)$|\\1${PYTHONDIR}\\2|g"
641 # Eliminate updating sys.path if the target python dir is already
643 regex = "s|sys.path.insert.*bin/python.*$||g"
644 bld.SAMBA_GENERATOR('python_%s' % destname,
645 rule="sed '%s' < ${SRC} > ${TGT}" % regex,
650 file = os.path.join(base_name, file)
651 bld.install_as(dest, file, chmod=chmod)
654 def INSTALL_FILES(bld, destdir, files, chmod=MODE_644, flat=False,
655 python_fixup=False, destname=None, base_name=None):
656 '''install a set of files'''
657 for f in TO_LIST(files):
658 install_file(bld, destdir, f, chmod=chmod, flat=flat,
659 python_fixup=python_fixup, destname=destname,
661 Build.BuildContext.INSTALL_FILES = INSTALL_FILES
664 def INSTALL_WILDCARD(bld, destdir, pattern, chmod=MODE_644, flat=False,
665 python_fixup=False, exclude=None, trim_path=None):
666 '''install a set of files matching a wildcard pattern'''
667 files=TO_LIST(bld.path.ant_glob(pattern))
671 files2.append(os_path_relpath(f, trim_path))
676 if fnmatch.fnmatch(f, exclude):
678 INSTALL_FILES(bld, destdir, files, chmod=chmod, flat=flat,
679 python_fixup=python_fixup, base_name=trim_path)
680 Build.BuildContext.INSTALL_WILDCARD = INSTALL_WILDCARD
683 def INSTALL_DIRS(bld, destdir, dirs):
684 '''install a set of directories'''
685 destdir = bld.EXPAND_VARIABLES(destdir)
686 dirs = bld.EXPAND_VARIABLES(dirs)
687 for d in TO_LIST(dirs):
688 bld.install_dir(os.path.join(destdir, d))
689 Build.BuildContext.INSTALL_DIRS = INSTALL_DIRS
692 re_header = re.compile('#include[ \t]*"([^"]+)"', re.I | re.M)
693 class header_task(Task.Task):
695 The public headers (the one installed on the system) have both
696 different paths and contents, so the rename is not enough.
698 Intermediate .inst.h files are created because path manipulation
699 may be slow. The substitution is thus performed only once.
704 vars = ['INCLUDEDIR', 'HEADER_DEPS']
707 txt = self.inputs[0].read(self.env)
709 # hard-coded string, but only present in samba4 (I promise, you won't feel a thing)
710 txt = txt.replace('#if _SAMBA_BUILD_ == 4', '#if 1\n')
712 # use a regexp to substitute the #include lines in the files
713 map = self.generator.bld.hnodemap
714 dirnodes = self.generator.bld.hnodedirs
719 # pokemon headers: gotta catch'em all!
721 if s.startswith('bin/default'):
722 node = self.generator.bld.srcnode.find_resource(s.replace('bin/default/', ''))
724 Logs.warn('could not find the public header for %r' % s)
728 Logs.warn('could not find the public header replacement for build header %r' % s)
730 # this part is more difficult since the path may be relative to anything
731 for dirnode in dirnodes:
732 node = dirnode.find_resource(s)
738 Logs.warn('could not find the public header replacement for source header %r %r' % (s, node))
740 Logs.warn('-> could not find the public header for %r' % s)
742 return "#include <%s>" % fin
745 txt = re_header.sub(repl, txt)
747 # and write the output file
750 f = open(self.outputs[0].abspath(self.env), 'w')
756 @TaskGen.feature('pubh')
757 def make_public_headers(self):
759 collect the public headers to process and to install, then
760 create the substitutions (name and contents)
763 if not self.bld.is_install:
764 # install time only (lazy)
768 # hnodedirs: list of folders for searching the headers
769 # hnodemap: node ids and replacement string (node objects are unique)
771 self.bld.hnodedirs.append(self.path)
772 except AttributeError:
773 self.bld.hnodemap = {}
774 self.bld.hnodedirs = [self.bld.srcnode, self.path]
776 for k in 'source4 source4/include lib/talloc lib/tevent/ source4/lib/ldb/include/'.split():
777 node = self.bld.srcnode.find_dir(k)
779 self.bld.hnodedirs.append(node)
781 header_path = getattr(self, 'header_path', None) or ''
783 for x in self.to_list(self.headers):
785 # too complicated, but what was the original idea?
786 if isinstance(header_path, list):
788 for (p1, dir) in header_path:
789 lst = self.to_list(p1)
791 if fnmatch.fnmatch(x, p2):
799 inst_path = header_path
803 if x.find(':') != -1:
808 inn = self.path.find_resource(name)
811 raise ValueError("could not find the public header %r in %r" % (name, self.path))
812 out = inn.change_ext('.inst.h')
813 self.create_task('header', inn, out)
819 inst_path = inst_path + '/'
820 inst_path = inst_path + dest
822 self.bld.install_as('${INCLUDEDIR}/%s' % inst_path, out, self.env)
824 self.bld.hnodemap[inn.id] = inst_path
826 # create a hash (not md5) to make sure the headers are re-created if something changes
828 lst = list(self.bld.hnodemap.keys())
831 val = hash((val, k, self.bld.hnodemap[k]))
832 self.bld.env.HEADER_DEPS = val
834 def PUBLIC_HEADERS(bld, public_headers, header_path=None):
835 '''install some headers
837 header_path may either be a string that is added to the INCLUDEDIR,
838 or it can be a dictionary of wildcard patterns which map to destination
839 directories relative to INCLUDEDIR
841 bld.SET_BUILD_GROUP('final')
842 ret = bld(features=['pubh'], headers=public_headers, header_path=header_path)
844 Build.BuildContext.PUBLIC_HEADERS = PUBLIC_HEADERS
847 def subst_at_vars(task):
848 '''substiture @VAR@ style variables in a file'''
849 src = task.inputs[0].srcpath(task.env)
850 tgt = task.outputs[0].bldpath(task.env)
856 a = re.split('(@\w+@)', s)
859 back_sub = [ ('PREFIX', '${prefix}'), ('EXEC_PREFIX', '${exec_prefix}')]
861 if re.match('@\w+@', v):
863 if not vname in task.env and vname.upper() in task.env:
864 vname = vname.upper()
865 if not vname in task.env:
866 Logs.error("Unknown substitution %s in %s" % (v, task.name))
868 v = SUBST_VARS_RECURSIVE(task.env[vname], task.env)
869 # now we back substitute the allowed pc vars
870 for (b, m) in back_sub:
873 if not b in done_var:
874 # we don't want to substitute the first usage
880 contents = ''.join(out)
882 s = f.write(contents)
887 def PKG_CONFIG_FILES(bld, pc_files, vnum=None):
888 '''install some pkg_config pc files'''
889 dest = '${PKGCONFIGDIR}'
890 dest = bld.EXPAND_VARIABLES(dest)
891 for f in TO_LIST(pc_files):
892 base=os.path.basename(f)
893 t = bld.SAMBA_GENERATOR('PKGCONFIG_%s' % base,
898 t.env.PACKAGE_VERSION = vnum
899 INSTALL_FILES(bld, dest, f, flat=True, destname=base)
900 Build.BuildContext.PKG_CONFIG_FILES = PKG_CONFIG_FILES
903 def MANPAGES(bld, manpages):
904 '''build and install manual pages'''
905 bld.env.MAN_XSL = 'http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl'
906 for m in manpages.split():
908 bld.SAMBA_GENERATOR(m,
912 rule='${XSLTPROC} -o ${TGT} --nonet ${MAN_XSL} ${SRC}'
914 bld.INSTALL_FILES('${MANDIR}/man%s' % m[-1], m, flat=True)
915 Build.BuildContext.MANPAGES = MANPAGES
918 #############################################################
919 # give a nicer display when building different types of files
920 def progress_display(self, msg, fname):
921 col1 = Logs.colors(self.color)
922 col2 = Logs.colors.NORMAL
923 total = self.position[1]
925 fs = '[%%%dd/%%%dd] %s %%s%%s%%s\n' % (n, n, msg)
926 return fs % (self.position[0], self.position[1], col1, fname, col2)
928 def link_display(self):
929 if Options.options.progress_bar != 0:
930 return Task.Task.old_display(self)
931 fname = self.outputs[0].bldpath(self.env)
932 return progress_display(self, 'Linking', fname)
933 Task.TaskBase.classes['cc_link'].display = link_display
935 def samba_display(self):
936 if Options.options.progress_bar != 0:
937 return Task.Task.old_display(self)
939 targets = LOCAL_CACHE(self, 'TARGET_TYPE')
940 if self.name in targets:
941 target_type = targets[self.name]
942 type_map = { 'GENERATOR' : 'Generating',
943 'PROTOTYPE' : 'Generating'
945 if target_type in type_map:
946 return progress_display(self, type_map[target_type], self.name)
948 fname = self.inputs[0].bldpath(self.env)
949 if fname[0:3] == '../':
951 ext_loc = fname.rfind('.')
953 return Task.Task.old_display(self)
954 ext = fname[ext_loc:]
956 ext_map = { '.idl' : 'Compiling IDL',
957 '.et' : 'Compiling ERRTABLE',
958 '.asn1': 'Compiling ASN1',
961 return progress_display(self, ext_map[ext], fname)
962 return Task.Task.old_display(self)
964 Task.TaskBase.classes['Task'].old_display = Task.TaskBase.classes['Task'].display
965 Task.TaskBase.classes['Task'].display = samba_display