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, sys, 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,
107 external_library=False,
118 target_type='LIBRARY',
119 bundled_extension=True,
125 private_library=False,
126 grouping_library=False,
128 '''define a Samba library'''
131 SET_TARGET_TYPE(bld, libname, 'DISABLED')
134 source = bld.EXPAND_VARIABLES(source, vars=vars)
136 # remember empty libraries, so we can strip the dependencies
137 if ((source == '') or (source == [])) and deps == '' and public_deps == '':
138 SET_TARGET_TYPE(bld, libname, 'EMPTY')
141 if BUILTIN_LIBRARY(bld, libname):
144 obj_target = libname + '.objlist'
146 if group == 'libraries':
147 subsystem_group = 'main'
149 subsystem_group = group
151 # first create a target for building the object files for this library
152 # by separating in this way, we avoid recompiling the C files
153 # separately for the install library and the build library
154 bld.SAMBA_SUBSYSTEM(obj_target,
157 public_deps = public_deps,
159 public_headers = public_headers,
160 header_path = header_path,
162 group = subsystem_group,
163 autoproto = autoproto,
164 depends_on = depends_on,
165 hide_symbols = hide_symbols,
166 pyext = pyext or (target_type == "PYTHON"),
167 local_include = local_include)
169 if BUILTIN_LIBRARY(bld, libname):
172 if not SET_TARGET_TYPE(bld, libname, target_type):
175 # the library itself will depend on that object target
176 deps += ' ' + public_deps
178 deps.append(obj_target)
180 realname = bld.map_shlib_extension(realname, python=(target_type=='PYTHON'))
181 link_name = bld.map_shlib_extension(link_name, python=(target_type=='PYTHON'))
183 # we don't want any public libraries without version numbers
184 if not private_library and vnum is None and soname is None and target_type != 'PYTHON' and not realname:
185 raise Utils.WafError("public library '%s' must have a vnum" % libname)
187 if target_type == 'PYTHON' or realname or not private_library:
188 bundled_name = libname.replace('_', '-')
190 bundled_name = PRIVATE_NAME(bld, libname, bundled_extension, private_library)
192 ldflags = TO_LIST(ldflags)
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'
203 features += ' abi_check'
206 if bld.env.HAVE_LD_VERSION_SCRIPT:
208 version = "%s_%s" % (Utils.g_module.APPNAME, Utils.g_module.VERSION)
210 version = "%s_%s" % (libname, vnum)
214 vscript = "%s.vscript" % libname
215 bld.ABI_VSCRIPT(libname, abi_directory, version, vscript,
217 fullname = bld.env.shlib_PATTERN % bundled_name
218 bld.add_manual_dependency(bld.path.find_or_declare(fullname), bld.path.find_or_declare(vscript))
219 if Options.is_install:
220 # also make the .inst file depend on the vscript
221 instname = bld.env.shlib_PATTERN % (bundled_name + '.inst')
222 bld.add_manual_dependency(bld.path.find_or_declare(instname), bld.path.find_or_declare(vscript))
223 vscript = os.path.join(bld.path.abspath(bld.env), vscript)
225 bld.SET_BUILD_GROUP(group)
229 target = bundled_name,
230 depends_on = depends_on,
231 samba_ldflags = ldflags,
233 samba_includes = includes,
234 version_script = vscript,
235 local_include = local_include,
239 samba_inst_path = install_path,
241 samba_realname = realname,
242 samba_install = install,
243 abi_directory = "%s/%s" % (bld.path.abspath(), abi_directory),
244 abi_match = abi_match,
245 private_library = private_library,
246 grouping_library=grouping_library
249 if realname and not link_name:
250 link_name = 'shared/%s' % realname
253 t.link_name = link_name
255 if pc_files is not None:
256 bld.PKG_CONFIG_FILES(pc_files, vnum=vnum)
258 if manpages is not None and 'XSLTPROC_MANPAGES' in bld.env and bld.env['XSLTPROC_MANPAGES']:
259 bld.MANPAGES(manpages)
262 Build.BuildContext.SAMBA_LIBRARY = SAMBA_LIBRARY
265 #################################################################
266 def SAMBA_BINARY(bld, binname, source,
276 use_global_deps=True,
287 '''define a Samba binary'''
290 SET_TARGET_TYPE(bld, binname, 'DISABLED')
293 if not SET_TARGET_TYPE(bld, binname, 'BINARY'):
296 features = 'cc cprogram symlink_bin install_bin'
298 features += ' pyembed'
300 obj_target = binname + '.objlist'
302 source = bld.EXPAND_VARIABLES(source, vars=vars)
303 source = unique_list(TO_LIST(source))
305 if group == 'binaries':
306 subsystem_group = 'main'
308 subsystem_group = group
310 # first create a target for building the object files for this binary
311 # by separating in this way, we avoid recompiling the C files
312 # separately for the install binary and the build binary
313 bld.SAMBA_SUBSYSTEM(obj_target,
318 group = subsystem_group,
319 autoproto = autoproto,
320 subsystem_name = subsystem_name,
321 local_include = local_include,
322 use_hostcc = use_hostcc,
324 use_global_deps= use_global_deps)
326 bld.SET_BUILD_GROUP(group)
328 # the binary itself will depend on that object target
330 deps.append(obj_target)
337 samba_includes = includes,
338 local_include = local_include,
339 samba_modules = modules,
341 samba_subsystem= subsystem_name,
343 samba_inst_path= install_path,
344 samba_install = install,
345 samba_ldflags = TO_LIST(ldflags)
348 if manpages is not None and 'XSLTPROC_MANPAGES' in bld.env and bld.env['XSLTPROC_MANPAGES']:
349 bld.MANPAGES(manpages)
351 Build.BuildContext.SAMBA_BINARY = SAMBA_BINARY
354 #################################################################
355 def SAMBA_MODULE(bld, modname, source,
360 module_init_name='samba_init_module',
362 autoproto_extra_source='',
364 internal_module=True,
370 '''define a Samba module.'''
372 source = bld.EXPAND_VARIABLES(source, vars=vars)
374 if internal_module or BUILTIN_LIBRARY(bld, modname):
375 bld.SAMBA_SUBSYSTEM(modname, source,
379 autoproto_extra_source=autoproto_extra_source,
381 local_include=local_include,
384 bld.ADD_INIT_FUNCTION(subsystem, modname, init_function)
388 SET_TARGET_TYPE(bld, modname, 'DISABLED')
391 obj_target = modname + '.objlist'
394 if subsystem is not None:
395 deps += ' ' + subsystem
396 while realname.startswith("lib"+subsystem+"_"):
397 realname = realname[len("lib"+subsystem+"_"):]
398 while realname.startswith(subsystem+"_"):
399 realname = realname[len(subsystem+"_"):]
401 realname = bld.make_libname(realname)
402 while realname.startswith("lib"):
403 realname = realname[len("lib"):]
405 build_link_name = "modules/%s/%s" % (subsystem, realname)
408 cflags += " -D%s=%s" % (init_function, module_init_name)
410 bld.SAMBA_LIBRARY(modname,
415 autoproto = autoproto,
416 local_include=local_include,
418 link_name=build_link_name,
419 install_path="${MODULESDIR}/%s" % subsystem,
424 Build.BuildContext.SAMBA_MODULE = SAMBA_MODULE
427 #################################################################
428 def SAMBA_SUBSYSTEM(bld, modname, source,
437 init_function_sentinal=None,
439 autoproto_extra_source='',
442 local_include_first=True,
446 use_global_deps=True,
450 '''define a Samba subsystem'''
453 SET_TARGET_TYPE(bld, modname, 'DISABLED')
456 # remember empty subsystems, so we can strip the dependencies
457 if ((source == '') or (source == [])) and deps == '' and public_deps == '':
458 SET_TARGET_TYPE(bld, modname, 'EMPTY')
461 if not SET_TARGET_TYPE(bld, modname, 'SUBSYSTEM'):
464 source = bld.EXPAND_VARIABLES(source, vars=vars)
465 source = unique_list(TO_LIST(source))
467 deps += ' ' + public_deps
469 bld.SET_BUILD_GROUP(group)
479 samba_cflags = CURRENT_CFLAGS(bld, modname, cflags, hide_symbols=hide_symbols),
480 depends_on = depends_on,
481 samba_deps = TO_LIST(deps),
482 samba_includes = includes,
483 local_include = local_include,
484 local_include_first = local_include_first,
485 samba_subsystem= subsystem_name,
486 samba_use_hostcc = use_hostcc,
487 samba_use_global_deps = use_global_deps
490 if cflags_end is not None:
491 t.samba_cflags.extend(TO_LIST(cflags_end))
493 if autoproto is not None:
494 bld.SAMBA_AUTOPROTO(autoproto, source + TO_LIST(autoproto_extra_source))
495 if public_headers is not None:
496 bld.PUBLIC_HEADERS(public_headers, header_path=header_path)
500 Build.BuildContext.SAMBA_SUBSYSTEM = SAMBA_SUBSYSTEM
503 def SAMBA_GENERATOR(bld, name, rule, source='', target='',
504 group='generators', enabled=True,
509 '''A generic source generator target'''
511 if not SET_TARGET_TYPE(bld, name, 'GENERATOR'):
517 bld.SET_BUILD_GROUP(group)
520 source=bld.EXPAND_VARIABLES(source, vars=vars),
522 shell=isinstance(rule, str),
526 samba_type='GENERATOR',
533 if public_headers is not None:
534 bld.PUBLIC_HEADERS(public_headers, header_path=header_path)
536 Build.BuildContext.SAMBA_GENERATOR = SAMBA_GENERATOR
541 def SETUP_BUILD_GROUPS(bld):
542 '''setup build groups used to ensure that the different build
543 phases happen consecutively'''
544 bld.p_ln = bld.srcnode # we do want to see all targets!
545 bld.env['USING_BUILD_GROUPS'] = True
546 bld.add_group('setup')
547 bld.add_group('build_compiler_source')
548 bld.add_group('vscripts')
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('symbolcheck')
558 bld.add_group('libraries')
559 bld.add_group('binaries')
560 bld.add_group('syslibcheck')
561 bld.add_group('final')
562 Build.BuildContext.SETUP_BUILD_GROUPS = SETUP_BUILD_GROUPS
565 def SET_BUILD_GROUP(bld, group):
566 '''set the current build group'''
567 if not 'USING_BUILD_GROUPS' in bld.env:
570 Build.BuildContext.SET_BUILD_GROUP = SET_BUILD_GROUP
575 def ENABLE_TIMESTAMP_DEPENDENCIES(conf):
576 """use timestamps instead of file contents for deps
577 this currently doesn't work"""
578 def h_file(filename):
580 st = os.stat(filename)
581 if stat.S_ISDIR(st[stat.ST_MODE]): raise IOError('not a file')
583 m.update(str(st.st_mtime))
584 m.update(str(st.st_size))
587 Utils.h_file = h_file
591 t = Task.simple_task_type('copy_script', 'rm -f "${LINK_TARGET}" && ln -s "${SRC[0].abspath(env)}" ${LINK_TARGET}',
592 shell=True, color='PINK', ext_in='.bin')
595 @feature('copy_script')
596 @before('apply_link')
597 def copy_script(self):
598 tsk = self.create_task('copy_script', self.allnodes[0])
599 tsk.env.TARGET = self.target
601 def SAMBA_SCRIPT(bld, name, pattern, installdir, installname=None):
602 '''used to copy scripts from the source tree into the build directory
603 for use by selftest'''
605 source = bld.path.ant_glob(pattern)
607 bld.SET_BUILD_GROUP('build_source')
608 for s in TO_LIST(source):
610 if installname != None:
612 target = os.path.join(installdir, iname)
613 tgtdir = os.path.dirname(os.path.join(bld.srcnode.abspath(bld.env), '..', target))
615 t = bld(features='copy_script',
620 t.env.LINK_TARGET = target
622 Build.BuildContext.SAMBA_SCRIPT = SAMBA_SCRIPT
625 def install_file(bld, destdir, file, chmod=MODE_644, flat=False,
626 python_fixup=False, destname=None, base_name=None):
628 destdir = bld.EXPAND_VARIABLES(destdir)
632 destname = os.path.basename(destname)
633 dest = os.path.join(destdir, destname)
635 # fixup the python path it will use to find Samba modules
636 inst_file = file + '.inst'
637 if bld.env["PYTHONARCHDIR"] not in sys.path:
638 regex = "s|\(sys.path.insert.*\)bin/python\(.*\)$|\\1${PYTHONARCHDIR}\\2|g"
640 # Eliminate updating sys.path if the target python dir is already
642 regex = "s|sys.path.insert.*bin/python.*$||g"
643 bld.SAMBA_GENERATOR('python_%s' % destname,
644 rule="sed '%s' < ${SRC} > ${TGT}" % regex,
649 file = os.path.join(base_name, file)
650 bld.install_as(dest, file, chmod=chmod)
653 def INSTALL_FILES(bld, destdir, files, chmod=MODE_644, flat=False,
654 python_fixup=False, destname=None, base_name=None):
655 '''install a set of files'''
656 for f in TO_LIST(files):
657 install_file(bld, destdir, f, chmod=chmod, flat=flat,
658 python_fixup=python_fixup, destname=destname,
660 Build.BuildContext.INSTALL_FILES = INSTALL_FILES
663 def INSTALL_WILDCARD(bld, destdir, pattern, chmod=MODE_644, flat=False,
664 python_fixup=False, exclude=None, trim_path=None):
665 '''install a set of files matching a wildcard pattern'''
666 files=TO_LIST(bld.path.ant_glob(pattern))
670 files2.append(os_path_relpath(f, trim_path))
675 if fnmatch.fnmatch(f, exclude):
677 INSTALL_FILES(bld, destdir, files, chmod=chmod, flat=flat,
678 python_fixup=python_fixup, base_name=trim_path)
679 Build.BuildContext.INSTALL_WILDCARD = INSTALL_WILDCARD
682 def INSTALL_DIRS(bld, destdir, dirs):
683 '''install a set of directories'''
684 destdir = bld.EXPAND_VARIABLES(destdir)
685 dirs = bld.EXPAND_VARIABLES(dirs)
686 for d in TO_LIST(dirs):
687 bld.install_dir(os.path.join(destdir, d))
688 Build.BuildContext.INSTALL_DIRS = INSTALL_DIRS
691 re_header = re.compile('#include[ \t]*"([^"]+)"', re.I | re.M)
692 class header_task(Task.Task):
694 The public headers (the one installed on the system) have both
695 different paths and contents, so the rename is not enough.
697 Intermediate .inst.h files are created because path manipulation
698 may be slow. The substitution is thus performed only once.
703 vars = ['INCLUDEDIR', 'HEADER_DEPS']
706 txt = self.inputs[0].read(self.env)
708 # hard-coded string, but only present in samba4 (I promise, you won't feel a thing)
709 txt = txt.replace('#if _SAMBA_BUILD_ == 4', '#if 1\n')
711 # use a regexp to substitute the #include lines in the files
712 map = self.generator.bld.hnodemap
713 dirnodes = self.generator.bld.hnodedirs
718 # pokemon headers: gotta catch'em all!
720 if s.startswith('bin/default'):
721 node = self.generator.bld.srcnode.find_resource(s.replace('bin/default/', ''))
723 Logs.warn('could not find the public header for %r' % s)
727 Logs.warn('could not find the public header replacement for build header %r' % s)
729 # this part is more difficult since the path may be relative to anything
730 for dirnode in dirnodes:
731 node = dirnode.find_resource(s)
737 Logs.warn('could not find the public header replacement for source header %r %r' % (s, node))
739 Logs.warn('-> could not find the public header for %r' % s)
741 return "#include <%s>" % fin
744 txt = re_header.sub(repl, txt)
746 # and write the output file
749 f = open(self.outputs[0].abspath(self.env), 'w')
755 @TaskGen.feature('pubh')
756 def make_public_headers(self):
758 collect the public headers to process and to install, then
759 create the substitutions (name and contents)
762 if not self.bld.is_install:
763 # install time only (lazy)
767 # hnodedirs: list of folders for searching the headers
768 # hnodemap: node ids and replacement string (node objects are unique)
770 self.bld.hnodedirs.append(self.path)
771 except AttributeError:
772 self.bld.hnodemap = {}
773 self.bld.hnodedirs = [self.bld.srcnode, self.path]
775 for k in 'source4 source4/include lib/talloc lib/tevent/ source4/lib/ldb/include/'.split():
776 node = self.bld.srcnode.find_dir(k)
778 self.bld.hnodedirs.append(node)
780 header_path = getattr(self, 'header_path', None) or ''
782 for x in self.to_list(self.headers):
784 # too complicated, but what was the original idea?
785 if isinstance(header_path, list):
787 for (p1, dir) in header_path:
788 lst = self.to_list(p1)
790 if fnmatch.fnmatch(x, p2):
798 inst_path = header_path
802 if x.find(':') != -1:
807 inn = self.path.find_resource(name)
810 raise ValueError("could not find the public header %r in %r" % (name, self.path))
811 out = inn.change_ext('.inst.h')
812 self.create_task('header', inn, out)
818 inst_path = inst_path + '/'
819 inst_path = inst_path + dest
821 self.bld.install_as('${INCLUDEDIR}/%s' % inst_path, out, self.env)
823 self.bld.hnodemap[inn.id] = inst_path
825 # create a hash (not md5) to make sure the headers are re-created if something changes
827 lst = list(self.bld.hnodemap.keys())
830 val = hash((val, k, self.bld.hnodemap[k]))
831 self.bld.env.HEADER_DEPS = val
833 def PUBLIC_HEADERS(bld, public_headers, header_path=None):
834 '''install some headers
836 header_path may either be a string that is added to the INCLUDEDIR,
837 or it can be a dictionary of wildcard patterns which map to destination
838 directories relative to INCLUDEDIR
840 bld.SET_BUILD_GROUP('final')
841 ret = bld(features=['pubh'], headers=public_headers, header_path=header_path)
843 Build.BuildContext.PUBLIC_HEADERS = PUBLIC_HEADERS
846 def MANPAGES(bld, manpages):
847 '''build and install manual pages'''
848 bld.env.MAN_XSL = 'http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl'
849 for m in manpages.split():
851 bld.SAMBA_GENERATOR(m,
855 rule='${XSLTPROC} -o ${TGT} --nonet ${MAN_XSL} ${SRC}'
857 bld.INSTALL_FILES('${MANDIR}/man%s' % m[-1], m, flat=True)
858 Build.BuildContext.MANPAGES = MANPAGES
861 #############################################################
862 # give a nicer display when building different types of files
863 def progress_display(self, msg, fname):
864 col1 = Logs.colors(self.color)
865 col2 = Logs.colors.NORMAL
866 total = self.position[1]
868 fs = '[%%%dd/%%%dd] %s %%s%%s%%s\n' % (n, n, msg)
869 return fs % (self.position[0], self.position[1], col1, fname, col2)
871 def link_display(self):
872 if Options.options.progress_bar != 0:
873 return Task.Task.old_display(self)
874 fname = self.outputs[0].bldpath(self.env)
875 return progress_display(self, 'Linking', fname)
876 Task.TaskBase.classes['cc_link'].display = link_display
878 def samba_display(self):
879 if Options.options.progress_bar != 0:
880 return Task.Task.old_display(self)
882 targets = LOCAL_CACHE(self, 'TARGET_TYPE')
883 if self.name in targets:
884 target_type = targets[self.name]
885 type_map = { 'GENERATOR' : 'Generating',
886 'PROTOTYPE' : 'Generating'
888 if target_type in type_map:
889 return progress_display(self, type_map[target_type], self.name)
891 if len(self.inputs) == 0:
892 return Task.Task.old_display(self)
894 fname = self.inputs[0].bldpath(self.env)
895 if fname[0:3] == '../':
897 ext_loc = fname.rfind('.')
899 return Task.Task.old_display(self)
900 ext = fname[ext_loc:]
902 ext_map = { '.idl' : 'Compiling IDL',
903 '.et' : 'Compiling ERRTABLE',
904 '.asn1': 'Compiling ASN1',
907 return progress_display(self, ext_map[ext], fname)
908 return Task.Task.old_display(self)
910 Task.TaskBase.classes['Task'].old_display = Task.TaskBase.classes['Task'].display
911 Task.TaskBase.classes['Task'].display = samba_display
916 def apply_bundle_remove_dynamiclib_patch(self):
917 if self.env['MACBUNDLE'] or getattr(self,'mac_bundle',False):
918 if not getattr(self,'vnum',None):
920 self.env['LINKFLAGS'].remove('-dynamiclib')
921 self.env['LINKFLAGS'].remove('-single_module')