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
32 # some systems have broken threading in python
33 if os.environ.get('WAF_NOTHREADS') == '1':
38 os.putenv('PYTHONUNBUFFERED', '1')
41 if Constants.HEXVERSION < 0x105019:
43 Please use the version of waf that comes with Samba, not
44 a system installed version. See http://wiki.samba.org/index.php/Waf
47 Alternatively, please use ./autogen-waf.sh, and then
48 run ./configure and make as usual. That will call the right version of waf.
54 def SAMBA_BUILD_ENV(conf):
55 '''create the samba build environment'''
56 conf.env.BUILD_DIRECTORY = conf.blddir
57 mkdir_p(os.path.join(conf.blddir, LIB_PATH))
58 mkdir_p(os.path.join(conf.blddir, "modules"))
59 mkdir_p(os.path.join(conf.blddir, 'python/samba/dcerpc'))
60 # this allows all of the bin/shared and bin/python targets
61 # to be expressed in terms of build directory paths
62 mkdir_p(os.path.join(conf.blddir, 'default'))
63 for p in ['python','shared', 'modules']:
64 link_target = os.path.join(conf.blddir, 'default/' + p)
65 if not os.path.lexists(link_target):
66 os.symlink('../' + p, link_target)
68 # get perl to put the blib files in the build directory
69 blib_bld = os.path.join(conf.blddir, 'default/pidl/blib')
70 blib_src = os.path.join(conf.srcdir, 'pidl/blib')
71 mkdir_p(blib_bld + '/man1')
72 mkdir_p(blib_bld + '/man3')
73 if os.path.islink(blib_src):
75 elif os.path.exists(blib_src):
76 shutil.rmtree(blib_src)
79 def ADD_INIT_FUNCTION(bld, subsystem, target, init_function):
80 '''add an init_function to the list for a subsystem'''
81 if init_function is None:
83 bld.ASSERT(subsystem is not None, "You must specify a subsystem for init_function '%s'" % init_function)
84 cache = LOCAL_CACHE(bld, 'INIT_FUNCTIONS')
85 if not subsystem in cache:
87 cache[subsystem].append( { 'TARGET':target, 'INIT_FUNCTION':init_function } )
88 Build.BuildContext.ADD_INIT_FUNCTION = ADD_INIT_FUNCTION
92 #################################################################
93 def SAMBA_LIBRARY(bld, libname, source,
103 external_library=False,
114 target_type='LIBRARY',
115 bundled_extension=True,
121 private_library=False,
122 grouping_library=False,
124 '''define a Samba library'''
127 SET_TARGET_TYPE(bld, libname, 'DISABLED')
130 source = bld.EXPAND_VARIABLES(source, vars=vars)
132 # remember empty libraries, so we can strip the dependencies
133 if ((source == '') or (source == [])) and deps == '' and public_deps == '':
134 SET_TARGET_TYPE(bld, libname, 'EMPTY')
137 if BUILTIN_LIBRARY(bld, libname):
140 obj_target = libname + '.objlist'
142 if group == 'libraries':
143 subsystem_group = 'main'
145 subsystem_group = group
147 # first create a target for building the object files for this library
148 # by separating in this way, we avoid recompiling the C files
149 # separately for the install library and the build library
150 bld.SAMBA_SUBSYSTEM(obj_target,
153 public_deps = public_deps,
155 public_headers = public_headers,
156 header_path = header_path,
158 group = subsystem_group,
159 autoproto = autoproto,
160 depends_on = depends_on,
161 hide_symbols = hide_symbols,
162 pyext = pyext or (target_type == "PYTHON"),
163 local_include = local_include)
165 if BUILTIN_LIBRARY(bld, libname):
168 if not SET_TARGET_TYPE(bld, libname, target_type):
171 # the library itself will depend on that object target
172 deps += ' ' + public_deps
174 deps.append(obj_target)
176 realname = bld.map_shlib_extension(realname, python=(target_type=='PYTHON'))
177 link_name = bld.map_shlib_extension(link_name, python=(target_type=='PYTHON'))
179 # we don't want any public libraries without version numbers
180 if not private_library and vnum is None and soname is None and target_type != 'PYTHON' and not realname:
181 raise Utils.WafError("public library '%s' must have a vnum" % libname)
183 if target_type == 'PYTHON' or realname or not private_library:
184 bundled_name = libname.replace('_', '-')
186 bundled_name = PRIVATE_NAME(bld, libname, bundled_extension, private_library)
190 Logs.error("vnum is invalid for private libraries")
192 vnum = Utils.g_module.VERSION
194 features = 'cc cshlib symlink_lib install_lib'
195 if target_type == 'PYTHON':
198 # this is quite strange. we should add pyext feature for pyext
199 # but that breaks the build. This may be a bug in the waf python tool
200 features += ' pyembed'
202 features += ' abi_check'
205 abi_file = os.path.join(bld.curdir, abi_file)
207 bld.SET_BUILD_GROUP(group)
211 target = bundled_name,
212 depends_on = depends_on,
214 samba_includes = includes,
215 local_include = local_include,
219 samba_inst_path = install_path,
221 samba_realname = realname,
222 samba_install = install,
224 abi_match = abi_match,
225 private_library = private_library,
226 grouping_library=grouping_library
229 if realname and not link_name:
230 link_name = 'shared/%s' % realname
233 t.link_name = link_name
235 if pc_files is not None:
236 bld.PKG_CONFIG_FILES(pc_files, vnum=vnum)
238 if manpages is not None and 'XSLTPROC_MANPAGES' in bld.env and bld.env['XSLTPROC_MANPAGES']:
239 bld.MANPAGES(manpages)
242 Build.BuildContext.SAMBA_LIBRARY = SAMBA_LIBRARY
245 #################################################################
246 def SAMBA_BINARY(bld, binname, source,
256 use_global_deps=True,
267 '''define a Samba binary'''
270 SET_TARGET_TYPE(bld, binname, 'DISABLED')
273 if not SET_TARGET_TYPE(bld, binname, 'BINARY'):
276 features = 'cc cprogram symlink_bin install_bin'
278 features += ' pyembed'
280 obj_target = binname + '.objlist'
282 source = bld.EXPAND_VARIABLES(source, vars=vars)
283 source = unique_list(TO_LIST(source))
285 if group == 'binaries':
286 subsystem_group = 'main'
288 subsystem_group = group
290 # first create a target for building the object files for this binary
291 # by separating in this way, we avoid recompiling the C files
292 # separately for the install binary and the build binary
293 bld.SAMBA_SUBSYSTEM(obj_target,
298 group = subsystem_group,
299 autoproto = autoproto,
300 subsystem_name = subsystem_name,
301 local_include = local_include,
302 use_hostcc = use_hostcc,
304 use_global_deps= use_global_deps)
306 bld.SET_BUILD_GROUP(group)
308 # the binary itself will depend on that object target
310 deps.append(obj_target)
317 samba_includes = includes,
318 local_include = local_include,
319 samba_modules = modules,
321 samba_subsystem= subsystem_name,
323 samba_inst_path= install_path,
324 samba_install = install
327 if manpages is not None and 'XSLTPROC_MANPAGES' in bld.env and bld.env['XSLTPROC_MANPAGES']:
328 bld.MANPAGES(manpages)
330 Build.BuildContext.SAMBA_BINARY = SAMBA_BINARY
333 #################################################################
334 def SAMBA_MODULE(bld, modname, source,
339 module_init_name='samba_init_module',
341 autoproto_extra_source='',
343 internal_module=True,
349 '''define a Samba module.'''
351 source = bld.EXPAND_VARIABLES(source, vars=vars)
353 if internal_module or BUILTIN_LIBRARY(bld, modname):
354 bld.SAMBA_SUBSYSTEM(modname, source,
358 autoproto_extra_source=autoproto_extra_source,
360 local_include=local_include,
363 bld.ADD_INIT_FUNCTION(subsystem, modname, init_function)
367 SET_TARGET_TYPE(bld, modname, 'DISABLED')
370 obj_target = modname + '.objlist'
373 if subsystem is not None:
374 deps += ' ' + subsystem
375 while realname.startswith("lib"+subsystem+"_"):
376 realname = realname[len("lib"+subsystem+"_"):]
377 while realname.startswith(subsystem+"_"):
378 realname = realname[len(subsystem+"_"):]
380 realname = bld.make_libname(realname)
381 while realname.startswith("lib"):
382 realname = realname[len("lib"):]
384 build_link_name = "modules/%s/%s" % (subsystem, realname)
387 cflags += " -D%s=%s" % (init_function, module_init_name)
389 bld.SAMBA_LIBRARY(modname,
394 autoproto = autoproto,
395 local_include=local_include,
397 link_name=build_link_name,
398 install_path="${MODULESDIR}/%s" % subsystem,
403 Build.BuildContext.SAMBA_MODULE = SAMBA_MODULE
406 #################################################################
407 def SAMBA_SUBSYSTEM(bld, modname, source,
416 init_function_sentinal=None,
418 autoproto_extra_source='',
421 local_include_first=True,
425 use_global_deps=True,
429 '''define a Samba subsystem'''
432 SET_TARGET_TYPE(bld, modname, 'DISABLED')
435 # remember empty subsystems, so we can strip the dependencies
436 if ((source == '') or (source == [])) and deps == '' and public_deps == '':
437 SET_TARGET_TYPE(bld, modname, 'EMPTY')
440 if not SET_TARGET_TYPE(bld, modname, 'SUBSYSTEM'):
443 source = bld.EXPAND_VARIABLES(source, vars=vars)
444 source = unique_list(TO_LIST(source))
446 deps += ' ' + public_deps
448 bld.SET_BUILD_GROUP(group)
458 samba_cflags = CURRENT_CFLAGS(bld, modname, cflags, hide_symbols=hide_symbols),
459 depends_on = depends_on,
460 samba_deps = TO_LIST(deps),
461 samba_includes = includes,
462 local_include = local_include,
463 local_include_first = local_include_first,
464 samba_subsystem= subsystem_name,
465 samba_use_hostcc = use_hostcc,
466 samba_use_global_deps = use_global_deps
469 if cflags_end is not None:
470 t.samba_cflags.extend(TO_LIST(cflags_end))
472 if autoproto is not None:
473 bld.SAMBA_AUTOPROTO(autoproto, source + TO_LIST(autoproto_extra_source))
474 if public_headers is not None:
475 bld.PUBLIC_HEADERS(public_headers, header_path=header_path)
479 Build.BuildContext.SAMBA_SUBSYSTEM = SAMBA_SUBSYSTEM
482 def SAMBA_GENERATOR(bld, name, rule, source='', target='',
483 group='generators', enabled=True,
488 '''A generic source generator target'''
490 if not SET_TARGET_TYPE(bld, name, 'GENERATOR'):
496 bld.SET_BUILD_GROUP(group)
499 source=bld.EXPAND_VARIABLES(source, vars=vars),
501 shell=isinstance(rule, str),
510 if public_headers is not None:
511 bld.PUBLIC_HEADERS(public_headers, header_path=header_path)
513 Build.BuildContext.SAMBA_GENERATOR = SAMBA_GENERATOR
518 def SETUP_BUILD_GROUPS(bld):
519 '''setup build groups used to ensure that the different build
520 phases happen consecutively'''
521 bld.p_ln = bld.srcnode # we do want to see all targets!
522 bld.env['USING_BUILD_GROUPS'] = True
523 bld.add_group('setup')
524 bld.add_group('build_compiler_source')
525 bld.add_group('base_libraries')
526 bld.add_group('generators')
527 bld.add_group('compiler_prototypes')
528 bld.add_group('compiler_libraries')
529 bld.add_group('build_compilers')
530 bld.add_group('build_source')
531 bld.add_group('prototypes')
532 bld.add_group('main')
533 bld.add_group('symbolcheck')
534 bld.add_group('libraries')
535 bld.add_group('binaries')
536 bld.add_group('syslibcheck')
537 bld.add_group('final')
538 Build.BuildContext.SETUP_BUILD_GROUPS = SETUP_BUILD_GROUPS
541 def SET_BUILD_GROUP(bld, group):
542 '''set the current build group'''
543 if not 'USING_BUILD_GROUPS' in bld.env:
546 Build.BuildContext.SET_BUILD_GROUP = SET_BUILD_GROUP
551 def ENABLE_TIMESTAMP_DEPENDENCIES(conf):
552 """use timestamps instead of file contents for deps
553 this currently doesn't work"""
554 def h_file(filename):
556 st = os.stat(filename)
557 if stat.S_ISDIR(st[stat.ST_MODE]): raise IOError('not a file')
559 m.update(str(st.st_mtime))
560 m.update(str(st.st_size))
563 Utils.h_file = h_file
567 t = Task.simple_task_type('copy_script', 'rm -f ${LINK_TARGET} && ln -s ${SRC[0].abspath(env)} ${LINK_TARGET}',
568 shell=True, color='PINK', ext_in='.bin')
571 @feature('copy_script')
572 @before('apply_link')
573 def copy_script(self):
574 tsk = self.create_task('copy_script', self.allnodes[0])
575 tsk.env.TARGET = self.target
577 def SAMBA_SCRIPT(bld, name, pattern, installdir, installname=None):
578 '''used to copy scripts from the source tree into the build directory
579 for use by selftest'''
581 source = bld.path.ant_glob(pattern)
583 bld.SET_BUILD_GROUP('build_source')
584 for s in TO_LIST(source):
586 if installname != None:
588 target = os.path.join(installdir, iname)
589 tgtdir = os.path.dirname(os.path.join(bld.srcnode.abspath(bld.env), '..', target))
591 t = bld(features='copy_script',
596 t.env.LINK_TARGET = target
598 Build.BuildContext.SAMBA_SCRIPT = SAMBA_SCRIPT
601 def install_file(bld, destdir, file, chmod=MODE_644, flat=False,
602 python_fixup=False, destname=None, base_name=None):
604 destdir = bld.EXPAND_VARIABLES(destdir)
608 destname = os.path.basename(destname)
609 dest = os.path.join(destdir, destname)
611 # fixup the python path it will use to find Samba modules
612 inst_file = file + '.inst'
613 if bld.env["PYTHONDIR"] not in sys.path:
614 regex = "s|\(sys.path.insert.*\)bin/python\(.*\)$|\\1${PYTHONDIR}\\2|g"
616 # Eliminate updating sys.path if the target python dir is already
618 regex = "s|sys.path.insert.*bin/python.*$||g"
619 bld.SAMBA_GENERATOR('python_%s' % destname,
620 rule="sed '%s' < ${SRC} > ${TGT}" % regex,
625 file = os.path.join(base_name, file)
626 bld.install_as(dest, file, chmod=chmod)
629 def INSTALL_FILES(bld, destdir, files, chmod=MODE_644, flat=False,
630 python_fixup=False, destname=None, base_name=None):
631 '''install a set of files'''
632 for f in TO_LIST(files):
633 install_file(bld, destdir, f, chmod=chmod, flat=flat,
634 python_fixup=python_fixup, destname=destname,
636 Build.BuildContext.INSTALL_FILES = INSTALL_FILES
639 def INSTALL_WILDCARD(bld, destdir, pattern, chmod=MODE_644, flat=False,
640 python_fixup=False, exclude=None, trim_path=None):
641 '''install a set of files matching a wildcard pattern'''
642 files=TO_LIST(bld.path.ant_glob(pattern))
646 files2.append(os_path_relpath(f, trim_path))
651 if fnmatch.fnmatch(f, exclude):
653 INSTALL_FILES(bld, destdir, files, chmod=chmod, flat=flat,
654 python_fixup=python_fixup, base_name=trim_path)
655 Build.BuildContext.INSTALL_WILDCARD = INSTALL_WILDCARD
658 def INSTALL_DIRS(bld, destdir, dirs):
659 '''install a set of directories'''
660 destdir = bld.EXPAND_VARIABLES(destdir)
661 dirs = bld.EXPAND_VARIABLES(dirs)
662 for d in TO_LIST(dirs):
663 bld.install_dir(os.path.join(destdir, d))
664 Build.BuildContext.INSTALL_DIRS = INSTALL_DIRS
667 re_header = re.compile('#include[ \t]*"([^"]+)"', re.I | re.M)
668 class header_task(Task.Task):
670 The public headers (the one installed on the system) have both
671 different paths and contents, so the rename is not enough.
673 Intermediate .inst.h files are created because path manipulation
674 may be slow. The substitution is thus performed only once.
679 vars = ['INCLUDEDIR', 'HEADER_DEPS']
682 txt = self.inputs[0].read(self.env)
684 # hard-coded string, but only present in samba4 (I promise, you won't feel a thing)
685 txt = txt.replace('#if _SAMBA_BUILD_ == 4', '#if 1\n')
687 # use a regexp to substitute the #include lines in the files
688 map = self.generator.bld.hnodemap
689 dirnodes = self.generator.bld.hnodedirs
694 # pokemon headers: gotta catch'em all!
696 if s.startswith('bin/default'):
697 node = self.generator.bld.srcnode.find_resource(s.replace('bin/default/', ''))
699 Logs.warn('could not find the public header for %r' % s)
703 Logs.warn('could not find the public header replacement for build header %r' % s)
705 # this part is more difficult since the path may be relative to anything
706 for dirnode in dirnodes:
707 node = dirnode.find_resource(s)
713 Logs.warn('could not find the public header replacement for source header %r %r' % (s, node))
715 Logs.warn('-> could not find the public header for %r' % s)
717 return "#include <%s>" % fin
720 txt = re_header.sub(repl, txt)
722 # and write the output file
725 f = open(self.outputs[0].abspath(self.env), 'w')
731 @TaskGen.feature('pubh')
732 def make_public_headers(self):
734 collect the public headers to process and to install, then
735 create the substitutions (name and contents)
738 if not self.bld.is_install:
739 # install time only (lazy)
743 # hnodedirs: list of folders for searching the headers
744 # hnodemap: node ids and replacement string (node objects are unique)
746 self.bld.hnodedirs.append(self.path)
747 except AttributeError:
748 self.bld.hnodemap = {}
749 self.bld.hnodedirs = [self.bld.srcnode, self.path]
751 for k in 'source4 source4/include lib/talloc lib/tevent/ source4/lib/ldb/include/'.split():
752 node = self.bld.srcnode.find_dir(k)
754 self.bld.hnodedirs.append(node)
756 header_path = getattr(self, 'header_path', None) or ''
758 for x in self.to_list(self.headers):
760 # too complicated, but what was the original idea?
761 if isinstance(header_path, list):
763 for (p1, dir) in header_path:
764 lst = self.to_list(p1)
766 if fnmatch.fnmatch(x, p2):
774 inst_path = header_path
778 if x.find(':') != -1:
783 inn = self.path.find_resource(name)
786 raise ValueError("could not find the public header %r in %r" % (name, self.path))
787 out = inn.change_ext('.inst.h')
788 self.create_task('header', inn, out)
794 inst_path = inst_path + '/'
795 inst_path = inst_path + dest
797 self.bld.install_as('${INCLUDEDIR}/%s' % inst_path, out, self.env)
799 self.bld.hnodemap[inn.id] = inst_path
801 # create a hash (not md5) to make sure the headers are re-created if something changes
803 lst = list(self.bld.hnodemap.keys())
806 val = hash((val, k, self.bld.hnodemap[k]))
807 self.bld.env.HEADER_DEPS = val
809 def PUBLIC_HEADERS(bld, public_headers, header_path=None):
810 '''install some headers
812 header_path may either be a string that is added to the INCLUDEDIR,
813 or it can be a dictionary of wildcard patterns which map to destination
814 directories relative to INCLUDEDIR
816 bld.SET_BUILD_GROUP('final')
817 ret = bld(features=['pubh'], headers=public_headers, header_path=header_path)
819 Build.BuildContext.PUBLIC_HEADERS = PUBLIC_HEADERS
822 def subst_at_vars(task):
823 '''substiture @VAR@ style variables in a file'''
824 src = task.inputs[0].srcpath(task.env)
825 tgt = task.outputs[0].bldpath(task.env)
831 a = re.split('(@\w+@)', s)
834 back_sub = [ ('PREFIX', '${prefix}'), ('EXEC_PREFIX', '${exec_prefix}')]
836 if re.match('@\w+@', v):
838 if not vname in task.env and vname.upper() in task.env:
839 vname = vname.upper()
840 if not vname in task.env:
841 Logs.error("Unknown substitution %s in %s" % (v, task.name))
843 v = SUBST_VARS_RECURSIVE(task.env[vname], task.env)
844 # now we back substitute the allowed pc vars
845 for (b, m) in back_sub:
848 if not b in done_var:
849 # we don't want to substitute the first usage
855 contents = ''.join(out)
857 s = f.write(contents)
862 def PKG_CONFIG_FILES(bld, pc_files, vnum=None):
863 '''install some pkg_config pc files'''
864 dest = '${PKGCONFIGDIR}'
865 dest = bld.EXPAND_VARIABLES(dest)
866 for f in TO_LIST(pc_files):
867 base=os.path.basename(f)
868 t = bld.SAMBA_GENERATOR('PKGCONFIG_%s' % base,
873 t.env.PACKAGE_VERSION = vnum
874 INSTALL_FILES(bld, dest, f, flat=True, destname=base)
875 Build.BuildContext.PKG_CONFIG_FILES = PKG_CONFIG_FILES
878 def MANPAGES(bld, manpages):
879 '''build and install manual pages'''
880 bld.env.MAN_XSL = 'http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl'
881 for m in manpages.split():
883 bld.SAMBA_GENERATOR(m,
887 rule='${XSLTPROC} -o ${TGT} --nonet ${MAN_XSL} ${SRC}'
889 bld.INSTALL_FILES('${MANDIR}/man%s' % m[-1], m, flat=True)
890 Build.BuildContext.MANPAGES = MANPAGES
893 #############################################################
894 # give a nicer display when building different types of files
895 def progress_display(self, msg, fname):
896 col1 = Logs.colors(self.color)
897 col2 = Logs.colors.NORMAL
898 total = self.position[1]
900 fs = '[%%%dd/%%%dd] %s %%s%%s%%s\n' % (n, n, msg)
901 return fs % (self.position[0], self.position[1], col1, fname, col2)
903 def link_display(self):
904 if Options.options.progress_bar != 0:
905 return Task.Task.old_display(self)
906 fname = self.outputs[0].bldpath(self.env)
907 return progress_display(self, 'Linking', fname)
908 Task.TaskBase.classes['cc_link'].display = link_display
910 def samba_display(self):
911 if Options.options.progress_bar != 0:
912 return Task.Task.old_display(self)
914 targets = LOCAL_CACHE(self, 'TARGET_TYPE')
915 if self.name in targets:
916 target_type = targets[self.name]
917 type_map = { 'GENERATOR' : 'Generating',
918 'PROTOTYPE' : 'Generating'
920 if target_type in type_map:
921 return progress_display(self, type_map[target_type], self.name)
923 if len(self.inputs) == 0:
924 return Task.Task.old_display(self)
926 fname = self.inputs[0].bldpath(self.env)
927 if fname[0:3] == '../':
929 ext_loc = fname.rfind('.')
931 return Task.Task.old_display(self)
932 ext = fname[ext_loc:]
934 ext_map = { '.idl' : 'Compiling IDL',
935 '.et' : 'Compiling ERRTABLE',
936 '.asn1': 'Compiling ASN1',
939 return progress_display(self, ext_map[ext], fname)
940 return Task.Task.old_display(self)
942 Task.TaskBase.classes['Task'].old_display = Task.TaskBase.classes['Task'].display
943 Task.TaskBase.classes['Task'].display = samba_display
948 def apply_bundle_remove_dynamiclib_patch(self):
949 if self.env['MACBUNDLE'] or getattr(self,'mac_bundle',False):
950 if not getattr(self,'vnum',None):
952 self.env['LINKFLAGS'].remove('-dynamiclib')
953 self.env['LINKFLAGS'].remove('-single_module')