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
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 < 0x105019:
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, LIB_PATH, "private"))
61 mkdir_p(os.path.join(conf.blddir, "modules"))
62 mkdir_p(os.path.join(conf.blddir, 'python/samba/dcerpc'))
63 # this allows all of the bin/shared and bin/python targets
64 # to be expressed in terms of build directory paths
65 mkdir_p(os.path.join(conf.blddir, 'default'))
66 for p in ['python','shared', 'modules']:
67 link_target = os.path.join(conf.blddir, 'default/' + p)
68 if not os.path.lexists(link_target):
69 os.symlink('../' + p, link_target)
71 # get perl to put the blib files in the build directory
72 blib_bld = os.path.join(conf.blddir, 'default/pidl/blib')
73 blib_src = os.path.join(conf.srcdir, 'pidl/blib')
74 mkdir_p(blib_bld + '/man1')
75 mkdir_p(blib_bld + '/man3')
76 if os.path.islink(blib_src):
78 elif os.path.exists(blib_src):
79 shutil.rmtree(blib_src)
82 def ADD_INIT_FUNCTION(bld, subsystem, target, init_function):
83 '''add an init_function to the list for a subsystem'''
84 if init_function is None:
86 bld.ASSERT(subsystem is not None, "You must specify a subsystem for init_function '%s'" % init_function)
87 cache = LOCAL_CACHE(bld, 'INIT_FUNCTIONS')
88 if not subsystem in cache:
90 cache[subsystem].append( { 'TARGET':target, 'INIT_FUNCTION':init_function } )
91 Build.BuildContext.ADD_INIT_FUNCTION = ADD_INIT_FUNCTION
95 #################################################################
96 def SAMBA_LIBRARY(bld, libname, source,
106 external_library=False,
117 target_type='LIBRARY',
118 bundled_extension=True,
124 private_library=False,
125 grouping_library=False,
127 '''define a Samba library'''
130 SET_TARGET_TYPE(bld, libname, 'DISABLED')
133 source = bld.EXPAND_VARIABLES(source, vars=vars)
135 # remember empty libraries, so we can strip the dependencies
136 if ((source == '') or (source == [])) and deps == '' and public_deps == '':
137 SET_TARGET_TYPE(bld, libname, 'EMPTY')
140 if BUILTIN_LIBRARY(bld, libname):
143 obj_target = libname + '.objlist'
145 if group == 'libraries':
146 subsystem_group = 'main'
148 subsystem_group = group
150 # first create a target for building the object files for this library
151 # by separating in this way, we avoid recompiling the C files
152 # separately for the install library and the build library
153 bld.SAMBA_SUBSYSTEM(obj_target,
156 public_deps = public_deps,
158 public_headers = public_headers,
159 header_path = header_path,
161 group = subsystem_group,
162 autoproto = autoproto,
163 depends_on = depends_on,
164 hide_symbols = hide_symbols,
165 pyext = pyext or (target_type == "PYTHON"),
166 local_include = local_include)
168 if BUILTIN_LIBRARY(bld, libname):
171 if not SET_TARGET_TYPE(bld, libname, target_type):
174 # the library itself will depend on that object target
175 deps += ' ' + public_deps
177 deps.append(obj_target)
179 realname = bld.map_shlib_extension(realname, python=(target_type=='PYTHON'))
180 link_name = bld.map_shlib_extension(link_name, python=(target_type=='PYTHON'))
182 # we don't want any public libraries without version numbers
183 if not private_library and vnum is None and soname is None and target_type != 'PYTHON' and not realname:
184 raise Utils.WafError("public library '%s' must have a vnum" % libname)
186 if target_type == 'PYTHON' or realname or not private_library:
187 bundled_name = libname.replace('_', '-')
189 bundled_name = PRIVATE_NAME(bld, libname, bundled_extension, private_library)
193 Logs.error("vnum is invalid for private libraries")
195 vnum = Utils.g_module.VERSION
197 features = 'cc cshlib symlink_lib install_lib'
198 if target_type == 'PYTHON':
201 # this is quite strange. we should add pyext feature for pyext
202 # but that breaks the build. This may be a bug in the waf python tool
203 features += ' pyembed'
205 features += ' abi_check'
208 abi_file = os.path.join(bld.curdir, abi_file)
210 bld.SET_BUILD_GROUP(group)
214 target = bundled_name,
215 depends_on = depends_on,
217 samba_includes = includes,
218 local_include = local_include,
222 samba_inst_path = install_path,
224 samba_realname = realname,
225 samba_install = install,
227 abi_match = abi_match,
228 private_library = private_library,
229 grouping_library=grouping_library
232 if realname and not link_name:
233 link_name = 'shared/%s' % realname
236 t.link_name = link_name
238 if pc_files is not None:
239 bld.PKG_CONFIG_FILES(pc_files, vnum=vnum)
241 if manpages is not None and 'XSLTPROC_MANPAGES' in bld.env and bld.env['XSLTPROC_MANPAGES']:
242 bld.MANPAGES(manpages)
245 Build.BuildContext.SAMBA_LIBRARY = SAMBA_LIBRARY
248 #################################################################
249 def SAMBA_BINARY(bld, binname, source,
259 use_global_deps=True,
270 '''define a Samba binary'''
273 SET_TARGET_TYPE(bld, binname, 'DISABLED')
276 if not SET_TARGET_TYPE(bld, binname, 'BINARY'):
279 features = 'cc cprogram symlink_bin install_bin'
281 features += ' pyembed'
283 obj_target = binname + '.objlist'
285 source = bld.EXPAND_VARIABLES(source, vars=vars)
286 source = unique_list(TO_LIST(source))
288 if group == 'binaries':
289 subsystem_group = 'main'
291 subsystem_group = group
293 # first create a target for building the object files for this binary
294 # by separating in this way, we avoid recompiling the C files
295 # separately for the install binary and the build binary
296 bld.SAMBA_SUBSYSTEM(obj_target,
301 group = subsystem_group,
302 autoproto = autoproto,
303 subsystem_name = subsystem_name,
304 local_include = local_include,
305 use_hostcc = use_hostcc,
307 use_global_deps= use_global_deps)
309 bld.SET_BUILD_GROUP(group)
311 # the binary itself will depend on that object target
313 deps.append(obj_target)
320 samba_includes = includes,
321 local_include = local_include,
322 samba_modules = modules,
324 samba_subsystem= subsystem_name,
326 samba_inst_path= install_path,
327 samba_install = install
330 if manpages is not None and 'XSLTPROC_MANPAGES' in bld.env and bld.env['XSLTPROC_MANPAGES']:
331 bld.MANPAGES(manpages)
333 Build.BuildContext.SAMBA_BINARY = SAMBA_BINARY
336 #################################################################
337 def SAMBA_MODULE(bld, modname, source,
342 module_init_name='samba_init_module',
344 autoproto_extra_source='',
346 internal_module=True,
352 '''define a Samba module.'''
354 source = bld.EXPAND_VARIABLES(source, vars=vars)
356 if internal_module or BUILTIN_LIBRARY(bld, modname):
357 bld.SAMBA_SUBSYSTEM(modname, source,
361 autoproto_extra_source=autoproto_extra_source,
363 local_include=local_include,
366 bld.ADD_INIT_FUNCTION(subsystem, modname, init_function)
370 SET_TARGET_TYPE(bld, modname, 'DISABLED')
373 obj_target = modname + '.objlist'
376 if subsystem is not None:
377 deps += ' ' + subsystem
378 while realname.startswith("lib"+subsystem+"_"):
379 realname = realname[len("lib"+subsystem+"_"):]
380 while realname.startswith(subsystem+"_"):
381 realname = realname[len(subsystem+"_"):]
383 realname = bld.make_libname(realname)
384 while realname.startswith("lib"):
385 realname = realname[len("lib"):]
387 build_link_name = "modules/%s/%s" % (subsystem, realname)
390 cflags += " -D%s=%s" % (init_function, module_init_name)
392 bld.SAMBA_LIBRARY(modname,
397 autoproto = autoproto,
398 local_include=local_include,
400 link_name=build_link_name,
401 install_path="${MODULESDIR}/%s" % subsystem,
406 Build.BuildContext.SAMBA_MODULE = SAMBA_MODULE
409 #################################################################
410 def SAMBA_SUBSYSTEM(bld, modname, source,
419 init_function_sentinal=None,
421 autoproto_extra_source='',
424 local_include_first=True,
428 use_global_deps=True,
432 '''define a Samba subsystem'''
435 SET_TARGET_TYPE(bld, modname, 'DISABLED')
438 # remember empty subsystems, so we can strip the dependencies
439 if ((source == '') or (source == [])) and deps == '' and public_deps == '':
440 SET_TARGET_TYPE(bld, modname, 'EMPTY')
443 if not SET_TARGET_TYPE(bld, modname, 'SUBSYSTEM'):
446 source = bld.EXPAND_VARIABLES(source, vars=vars)
447 source = unique_list(TO_LIST(source))
449 deps += ' ' + public_deps
451 bld.SET_BUILD_GROUP(group)
461 samba_cflags = CURRENT_CFLAGS(bld, modname, cflags, hide_symbols=hide_symbols),
462 depends_on = depends_on,
463 samba_deps = TO_LIST(deps),
464 samba_includes = includes,
465 local_include = local_include,
466 local_include_first = local_include_first,
467 samba_subsystem= subsystem_name,
468 samba_use_hostcc = use_hostcc,
469 samba_use_global_deps = use_global_deps
472 if cflags_end is not None:
473 t.samba_cflags.extend(TO_LIST(cflags_end))
475 if autoproto is not None:
476 bld.SAMBA_AUTOPROTO(autoproto, source + TO_LIST(autoproto_extra_source))
477 if public_headers is not None:
478 bld.PUBLIC_HEADERS(public_headers, header_path=header_path)
482 Build.BuildContext.SAMBA_SUBSYSTEM = SAMBA_SUBSYSTEM
485 def SAMBA_GENERATOR(bld, name, rule, source='', target='',
486 group='generators', enabled=True,
491 '''A generic source generator target'''
493 if not SET_TARGET_TYPE(bld, name, 'GENERATOR'):
499 bld.SET_BUILD_GROUP(group)
502 source=bld.EXPAND_VARIABLES(source, vars=vars),
504 shell=isinstance(rule, str),
513 if public_headers is not None:
514 bld.PUBLIC_HEADERS(public_headers, header_path=header_path)
516 Build.BuildContext.SAMBA_GENERATOR = SAMBA_GENERATOR
521 def SETUP_BUILD_GROUPS(bld):
522 '''setup build groups used to ensure that the different build
523 phases happen consecutively'''
524 bld.p_ln = bld.srcnode # we do want to see all targets!
525 bld.env['USING_BUILD_GROUPS'] = True
526 bld.add_group('setup')
527 bld.add_group('build_compiler_source')
528 bld.add_group('base_libraries')
529 bld.add_group('generators')
530 bld.add_group('compiler_prototypes')
531 bld.add_group('compiler_libraries')
532 bld.add_group('build_compilers')
533 bld.add_group('build_source')
534 bld.add_group('prototypes')
535 bld.add_group('main')
536 bld.add_group('symbolcheck')
537 bld.add_group('libraries')
538 bld.add_group('binaries')
539 bld.add_group('syslibcheck')
540 bld.add_group('final')
541 Build.BuildContext.SETUP_BUILD_GROUPS = SETUP_BUILD_GROUPS
544 def SET_BUILD_GROUP(bld, group):
545 '''set the current build group'''
546 if not 'USING_BUILD_GROUPS' in bld.env:
549 Build.BuildContext.SET_BUILD_GROUP = SET_BUILD_GROUP
554 def ENABLE_TIMESTAMP_DEPENDENCIES(conf):
555 """use timestamps instead of file contents for deps
556 this currently doesn't work"""
557 def h_file(filename):
559 st = os.stat(filename)
560 if stat.S_ISDIR(st[stat.ST_MODE]): raise IOError('not a file')
562 m.update(str(st.st_mtime))
563 m.update(str(st.st_size))
566 Utils.h_file = h_file
570 t = Task.simple_task_type('copy_script', 'rm -f "${LINK_TARGET}" && ln -s "${SRC[0].abspath(env)}" ${LINK_TARGET}',
571 shell=True, color='PINK', ext_in='.bin')
574 @feature('copy_script')
575 @before('apply_link')
576 def copy_script(self):
577 tsk = self.create_task('copy_script', self.allnodes[0])
578 tsk.env.TARGET = self.target
580 def SAMBA_SCRIPT(bld, name, pattern, installdir, installname=None):
581 '''used to copy scripts from the source tree into the build directory
582 for use by selftest'''
584 source = bld.path.ant_glob(pattern)
586 bld.SET_BUILD_GROUP('build_source')
587 for s in TO_LIST(source):
589 if installname != None:
591 target = os.path.join(installdir, iname)
592 tgtdir = os.path.dirname(os.path.join(bld.srcnode.abspath(bld.env), '..', target))
594 t = bld(features='copy_script',
599 t.env.LINK_TARGET = target
601 Build.BuildContext.SAMBA_SCRIPT = SAMBA_SCRIPT
604 def install_file(bld, destdir, file, chmod=MODE_644, flat=False,
605 python_fixup=False, destname=None, base_name=None):
607 destdir = bld.EXPAND_VARIABLES(destdir)
611 destname = os.path.basename(destname)
612 dest = os.path.join(destdir, destname)
614 # fixup the python path it will use to find Samba modules
615 inst_file = file + '.inst'
616 if bld.env["PYTHONDIR"] not in sys.path:
617 regex = "s|\(sys.path.insert.*\)bin/python\(.*\)$|\\1${PYTHONDIR}\\2|g"
619 # Eliminate updating sys.path if the target python dir is already
621 regex = "s|sys.path.insert.*bin/python.*$||g"
622 bld.SAMBA_GENERATOR('python_%s' % destname,
623 rule="sed '%s' < ${SRC} > ${TGT}" % regex,
628 file = os.path.join(base_name, file)
629 bld.install_as(dest, file, chmod=chmod)
632 def INSTALL_FILES(bld, destdir, files, chmod=MODE_644, flat=False,
633 python_fixup=False, destname=None, base_name=None):
634 '''install a set of files'''
635 for f in TO_LIST(files):
636 install_file(bld, destdir, f, chmod=chmod, flat=flat,
637 python_fixup=python_fixup, destname=destname,
639 Build.BuildContext.INSTALL_FILES = INSTALL_FILES
642 def INSTALL_WILDCARD(bld, destdir, pattern, chmod=MODE_644, flat=False,
643 python_fixup=False, exclude=None, trim_path=None):
644 '''install a set of files matching a wildcard pattern'''
645 files=TO_LIST(bld.path.ant_glob(pattern))
649 files2.append(os_path_relpath(f, trim_path))
654 if fnmatch.fnmatch(f, exclude):
656 INSTALL_FILES(bld, destdir, files, chmod=chmod, flat=flat,
657 python_fixup=python_fixup, base_name=trim_path)
658 Build.BuildContext.INSTALL_WILDCARD = INSTALL_WILDCARD
661 def INSTALL_DIRS(bld, destdir, dirs):
662 '''install a set of directories'''
663 destdir = bld.EXPAND_VARIABLES(destdir)
664 dirs = bld.EXPAND_VARIABLES(dirs)
665 for d in TO_LIST(dirs):
666 bld.install_dir(os.path.join(destdir, d))
667 Build.BuildContext.INSTALL_DIRS = INSTALL_DIRS
670 re_header = re.compile('#include[ \t]*"([^"]+)"', re.I | re.M)
671 class header_task(Task.Task):
673 The public headers (the one installed on the system) have both
674 different paths and contents, so the rename is not enough.
676 Intermediate .inst.h files are created because path manipulation
677 may be slow. The substitution is thus performed only once.
682 vars = ['INCLUDEDIR', 'HEADER_DEPS']
685 txt = self.inputs[0].read(self.env)
687 # hard-coded string, but only present in samba4 (I promise, you won't feel a thing)
688 txt = txt.replace('#if _SAMBA_BUILD_ == 4', '#if 1\n')
690 # use a regexp to substitute the #include lines in the files
691 map = self.generator.bld.hnodemap
692 dirnodes = self.generator.bld.hnodedirs
697 # pokemon headers: gotta catch'em all!
699 if s.startswith('bin/default'):
700 node = self.generator.bld.srcnode.find_resource(s.replace('bin/default/', ''))
702 Logs.warn('could not find the public header for %r' % s)
706 Logs.warn('could not find the public header replacement for build header %r' % s)
708 # this part is more difficult since the path may be relative to anything
709 for dirnode in dirnodes:
710 node = dirnode.find_resource(s)
716 Logs.warn('could not find the public header replacement for source header %r %r' % (s, node))
718 Logs.warn('-> could not find the public header for %r' % s)
720 return "#include <%s>" % fin
723 txt = re_header.sub(repl, txt)
725 # and write the output file
728 f = open(self.outputs[0].abspath(self.env), 'w')
734 @TaskGen.feature('pubh')
735 def make_public_headers(self):
737 collect the public headers to process and to install, then
738 create the substitutions (name and contents)
741 if not self.bld.is_install:
742 # install time only (lazy)
746 # hnodedirs: list of folders for searching the headers
747 # hnodemap: node ids and replacement string (node objects are unique)
749 self.bld.hnodedirs.append(self.path)
750 except AttributeError:
751 self.bld.hnodemap = {}
752 self.bld.hnodedirs = [self.bld.srcnode, self.path]
754 for k in 'source4 source4/include lib/talloc lib/tevent/ source4/lib/ldb/include/'.split():
755 node = self.bld.srcnode.find_dir(k)
757 self.bld.hnodedirs.append(node)
759 header_path = getattr(self, 'header_path', None) or ''
761 for x in self.to_list(self.headers):
763 # too complicated, but what was the original idea?
764 if isinstance(header_path, list):
766 for (p1, dir) in header_path:
767 lst = self.to_list(p1)
769 if fnmatch.fnmatch(x, p2):
777 inst_path = header_path
781 if x.find(':') != -1:
786 inn = self.path.find_resource(name)
789 raise ValueError("could not find the public header %r in %r" % (name, self.path))
790 out = inn.change_ext('.inst.h')
791 self.create_task('header', inn, out)
797 inst_path = inst_path + '/'
798 inst_path = inst_path + dest
800 self.bld.install_as('${INCLUDEDIR}/%s' % inst_path, out, self.env)
802 self.bld.hnodemap[inn.id] = inst_path
804 # create a hash (not md5) to make sure the headers are re-created if something changes
806 lst = list(self.bld.hnodemap.keys())
809 val = hash((val, k, self.bld.hnodemap[k]))
810 self.bld.env.HEADER_DEPS = val
812 def PUBLIC_HEADERS(bld, public_headers, header_path=None):
813 '''install some headers
815 header_path may either be a string that is added to the INCLUDEDIR,
816 or it can be a dictionary of wildcard patterns which map to destination
817 directories relative to INCLUDEDIR
819 bld.SET_BUILD_GROUP('final')
820 ret = bld(features=['pubh'], headers=public_headers, header_path=header_path)
822 Build.BuildContext.PUBLIC_HEADERS = PUBLIC_HEADERS
825 def MANPAGES(bld, manpages):
826 '''build and install manual pages'''
827 bld.env.MAN_XSL = 'http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl'
828 for m in manpages.split():
830 bld.SAMBA_GENERATOR(m,
834 rule='${XSLTPROC} -o ${TGT} --nonet ${MAN_XSL} ${SRC}'
836 bld.INSTALL_FILES('${MANDIR}/man%s' % m[-1], m, flat=True)
837 Build.BuildContext.MANPAGES = MANPAGES
840 #############################################################
841 # give a nicer display when building different types of files
842 def progress_display(self, msg, fname):
843 col1 = Logs.colors(self.color)
844 col2 = Logs.colors.NORMAL
845 total = self.position[1]
847 fs = '[%%%dd/%%%dd] %s %%s%%s%%s\n' % (n, n, msg)
848 return fs % (self.position[0], self.position[1], col1, fname, col2)
850 def link_display(self):
851 if Options.options.progress_bar != 0:
852 return Task.Task.old_display(self)
853 fname = self.outputs[0].bldpath(self.env)
854 return progress_display(self, 'Linking', fname)
855 Task.TaskBase.classes['cc_link'].display = link_display
857 def samba_display(self):
858 if Options.options.progress_bar != 0:
859 return Task.Task.old_display(self)
861 targets = LOCAL_CACHE(self, 'TARGET_TYPE')
862 if self.name in targets:
863 target_type = targets[self.name]
864 type_map = { 'GENERATOR' : 'Generating',
865 'PROTOTYPE' : 'Generating'
867 if target_type in type_map:
868 return progress_display(self, type_map[target_type], self.name)
870 if len(self.inputs) == 0:
871 return Task.Task.old_display(self)
873 fname = self.inputs[0].bldpath(self.env)
874 if fname[0:3] == '../':
876 ext_loc = fname.rfind('.')
878 return Task.Task.old_display(self)
879 ext = fname[ext_loc:]
881 ext_map = { '.idl' : 'Compiling IDL',
882 '.et' : 'Compiling ERRTABLE',
883 '.asn1': 'Compiling ASN1',
886 return progress_display(self, ext_map[ext], fname)
887 return Task.Task.old_display(self)
889 Task.TaskBase.classes['Task'].old_display = Task.TaskBase.classes['Task'].display
890 Task.TaskBase.classes['Task'].display = samba_display
895 def apply_bundle_remove_dynamiclib_patch(self):
896 if self.env['MACBUNDLE'] or getattr(self,'mac_bundle',False):
897 if not getattr(self,'vnum',None):
899 self.env['LINKFLAGS'].remove('-dynamiclib')
900 self.env['LINKFLAGS'].remove('-single_module')