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,
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'
205 if bld.env.HAVE_LD_VERSION_SCRIPT:
206 vscript = "%s.vscript" % libname
208 version = "%s_%s" % (Utils.g_module.APPNAME, Utils.g_module.VERSION)
210 version = "%s_%s" % (libname, vnum)
214 bld.ABI_VSCRIPT(libname, abi_directory, version, vscript)
215 ldflags.append("-Wl,--version-script=%s/%s" % (bld.path.abspath(bld.env), vscript))
216 fullname = bld.env.shlib_PATTERN % bundled_name
217 bld.add_manual_dependency(bld.path.find_or_declare(fullname), bld.path.find_or_declare(vscript))
219 bld.SET_BUILD_GROUP(group)
223 target = bundled_name,
224 depends_on = depends_on,
227 samba_includes = includes,
228 local_include = local_include,
232 samba_inst_path = install_path,
234 samba_realname = realname,
235 samba_install = install,
236 abi_directory = "%s/%s" % (bld.path.abspath(), abi_directory),
237 abi_match = abi_match,
238 private_library = private_library,
239 grouping_library=grouping_library
242 if realname and not link_name:
243 link_name = 'shared/%s' % realname
246 t.link_name = link_name
248 if pc_files is not None:
249 bld.PKG_CONFIG_FILES(pc_files, vnum=vnum)
251 if manpages is not None and 'XSLTPROC_MANPAGES' in bld.env and bld.env['XSLTPROC_MANPAGES']:
252 bld.MANPAGES(manpages)
255 Build.BuildContext.SAMBA_LIBRARY = SAMBA_LIBRARY
258 #################################################################
259 def SAMBA_BINARY(bld, binname, source,
269 use_global_deps=True,
280 '''define a Samba binary'''
283 SET_TARGET_TYPE(bld, binname, 'DISABLED')
286 if not SET_TARGET_TYPE(bld, binname, 'BINARY'):
289 features = 'cc cprogram symlink_bin install_bin'
291 features += ' pyembed'
293 obj_target = binname + '.objlist'
295 source = bld.EXPAND_VARIABLES(source, vars=vars)
296 source = unique_list(TO_LIST(source))
298 if group == 'binaries':
299 subsystem_group = 'main'
301 subsystem_group = group
303 # first create a target for building the object files for this binary
304 # by separating in this way, we avoid recompiling the C files
305 # separately for the install binary and the build binary
306 bld.SAMBA_SUBSYSTEM(obj_target,
311 group = subsystem_group,
312 autoproto = autoproto,
313 subsystem_name = subsystem_name,
314 local_include = local_include,
315 use_hostcc = use_hostcc,
317 use_global_deps= use_global_deps)
319 bld.SET_BUILD_GROUP(group)
321 # the binary itself will depend on that object target
323 deps.append(obj_target)
330 samba_includes = includes,
331 local_include = local_include,
332 samba_modules = modules,
334 samba_subsystem= subsystem_name,
336 samba_inst_path= install_path,
337 samba_install = install
340 if manpages is not None and 'XSLTPROC_MANPAGES' in bld.env and bld.env['XSLTPROC_MANPAGES']:
341 bld.MANPAGES(manpages)
343 Build.BuildContext.SAMBA_BINARY = SAMBA_BINARY
346 #################################################################
347 def SAMBA_MODULE(bld, modname, source,
352 module_init_name='samba_init_module',
354 autoproto_extra_source='',
356 internal_module=True,
362 '''define a Samba module.'''
364 source = bld.EXPAND_VARIABLES(source, vars=vars)
366 if internal_module or BUILTIN_LIBRARY(bld, modname):
367 bld.SAMBA_SUBSYSTEM(modname, source,
371 autoproto_extra_source=autoproto_extra_source,
373 local_include=local_include,
376 bld.ADD_INIT_FUNCTION(subsystem, modname, init_function)
380 SET_TARGET_TYPE(bld, modname, 'DISABLED')
383 obj_target = modname + '.objlist'
386 if subsystem is not None:
387 deps += ' ' + subsystem
388 while realname.startswith("lib"+subsystem+"_"):
389 realname = realname[len("lib"+subsystem+"_"):]
390 while realname.startswith(subsystem+"_"):
391 realname = realname[len(subsystem+"_"):]
393 realname = bld.make_libname(realname)
394 while realname.startswith("lib"):
395 realname = realname[len("lib"):]
397 build_link_name = "modules/%s/%s" % (subsystem, realname)
400 cflags += " -D%s=%s" % (init_function, module_init_name)
402 bld.SAMBA_LIBRARY(modname,
407 autoproto = autoproto,
408 local_include=local_include,
410 link_name=build_link_name,
411 install_path="${MODULESDIR}/%s" % subsystem,
416 Build.BuildContext.SAMBA_MODULE = SAMBA_MODULE
419 #################################################################
420 def SAMBA_SUBSYSTEM(bld, modname, source,
429 init_function_sentinal=None,
431 autoproto_extra_source='',
434 local_include_first=True,
438 use_global_deps=True,
442 '''define a Samba subsystem'''
445 SET_TARGET_TYPE(bld, modname, 'DISABLED')
448 # remember empty subsystems, so we can strip the dependencies
449 if ((source == '') or (source == [])) and deps == '' and public_deps == '':
450 SET_TARGET_TYPE(bld, modname, 'EMPTY')
453 if not SET_TARGET_TYPE(bld, modname, 'SUBSYSTEM'):
456 source = bld.EXPAND_VARIABLES(source, vars=vars)
457 source = unique_list(TO_LIST(source))
459 deps += ' ' + public_deps
461 bld.SET_BUILD_GROUP(group)
471 samba_cflags = CURRENT_CFLAGS(bld, modname, cflags, hide_symbols=hide_symbols),
472 depends_on = depends_on,
473 samba_deps = TO_LIST(deps),
474 samba_includes = includes,
475 local_include = local_include,
476 local_include_first = local_include_first,
477 samba_subsystem= subsystem_name,
478 samba_use_hostcc = use_hostcc,
479 samba_use_global_deps = use_global_deps
482 if cflags_end is not None:
483 t.samba_cflags.extend(TO_LIST(cflags_end))
485 if autoproto is not None:
486 bld.SAMBA_AUTOPROTO(autoproto, source + TO_LIST(autoproto_extra_source))
487 if public_headers is not None:
488 bld.PUBLIC_HEADERS(public_headers, header_path=header_path)
492 Build.BuildContext.SAMBA_SUBSYSTEM = SAMBA_SUBSYSTEM
495 def SAMBA_GENERATOR(bld, name, rule, source='', target='',
496 group='generators', enabled=True,
501 '''A generic source generator target'''
503 if not SET_TARGET_TYPE(bld, name, 'GENERATOR'):
509 bld.SET_BUILD_GROUP(group)
512 source=bld.EXPAND_VARIABLES(source, vars=vars),
514 shell=isinstance(rule, str),
518 samba_type='GENERATOR',
525 if public_headers is not None:
526 bld.PUBLIC_HEADERS(public_headers, header_path=header_path)
528 Build.BuildContext.SAMBA_GENERATOR = SAMBA_GENERATOR
533 def SETUP_BUILD_GROUPS(bld):
534 '''setup build groups used to ensure that the different build
535 phases happen consecutively'''
536 bld.p_ln = bld.srcnode # we do want to see all targets!
537 bld.env['USING_BUILD_GROUPS'] = True
538 bld.add_group('setup')
539 bld.add_group('build_compiler_source')
540 bld.add_group('vscripts')
541 bld.add_group('base_libraries')
542 bld.add_group('generators')
543 bld.add_group('compiler_prototypes')
544 bld.add_group('compiler_libraries')
545 bld.add_group('build_compilers')
546 bld.add_group('build_source')
547 bld.add_group('prototypes')
548 bld.add_group('main')
549 bld.add_group('symbolcheck')
550 bld.add_group('libraries')
551 bld.add_group('binaries')
552 bld.add_group('syslibcheck')
553 bld.add_group('final')
554 Build.BuildContext.SETUP_BUILD_GROUPS = SETUP_BUILD_GROUPS
557 def SET_BUILD_GROUP(bld, group):
558 '''set the current build group'''
559 if not 'USING_BUILD_GROUPS' in bld.env:
562 Build.BuildContext.SET_BUILD_GROUP = SET_BUILD_GROUP
567 def ENABLE_TIMESTAMP_DEPENDENCIES(conf):
568 """use timestamps instead of file contents for deps
569 this currently doesn't work"""
570 def h_file(filename):
572 st = os.stat(filename)
573 if stat.S_ISDIR(st[stat.ST_MODE]): raise IOError('not a file')
575 m.update(str(st.st_mtime))
576 m.update(str(st.st_size))
579 Utils.h_file = h_file
583 t = Task.simple_task_type('copy_script', 'rm -f "${LINK_TARGET}" && ln -s "${SRC[0].abspath(env)}" ${LINK_TARGET}',
584 shell=True, color='PINK', ext_in='.bin')
587 @feature('copy_script')
588 @before('apply_link')
589 def copy_script(self):
590 tsk = self.create_task('copy_script', self.allnodes[0])
591 tsk.env.TARGET = self.target
593 def SAMBA_SCRIPT(bld, name, pattern, installdir, installname=None):
594 '''used to copy scripts from the source tree into the build directory
595 for use by selftest'''
597 source = bld.path.ant_glob(pattern)
599 bld.SET_BUILD_GROUP('build_source')
600 for s in TO_LIST(source):
602 if installname != None:
604 target = os.path.join(installdir, iname)
605 tgtdir = os.path.dirname(os.path.join(bld.srcnode.abspath(bld.env), '..', target))
607 t = bld(features='copy_script',
612 t.env.LINK_TARGET = target
614 Build.BuildContext.SAMBA_SCRIPT = SAMBA_SCRIPT
617 def install_file(bld, destdir, file, chmod=MODE_644, flat=False,
618 python_fixup=False, destname=None, base_name=None):
620 destdir = bld.EXPAND_VARIABLES(destdir)
624 destname = os.path.basename(destname)
625 dest = os.path.join(destdir, destname)
627 # fixup the python path it will use to find Samba modules
628 inst_file = file + '.inst'
629 if bld.env["PYTHONDIR"] not in sys.path:
630 regex = "s|\(sys.path.insert.*\)bin/python\(.*\)$|\\1${PYTHONDIR}\\2|g"
632 # Eliminate updating sys.path if the target python dir is already
634 regex = "s|sys.path.insert.*bin/python.*$||g"
635 bld.SAMBA_GENERATOR('python_%s' % destname,
636 rule="sed '%s' < ${SRC} > ${TGT}" % regex,
641 file = os.path.join(base_name, file)
642 bld.install_as(dest, file, chmod=chmod)
645 def INSTALL_FILES(bld, destdir, files, chmod=MODE_644, flat=False,
646 python_fixup=False, destname=None, base_name=None):
647 '''install a set of files'''
648 for f in TO_LIST(files):
649 install_file(bld, destdir, f, chmod=chmod, flat=flat,
650 python_fixup=python_fixup, destname=destname,
652 Build.BuildContext.INSTALL_FILES = INSTALL_FILES
655 def INSTALL_WILDCARD(bld, destdir, pattern, chmod=MODE_644, flat=False,
656 python_fixup=False, exclude=None, trim_path=None):
657 '''install a set of files matching a wildcard pattern'''
658 files=TO_LIST(bld.path.ant_glob(pattern))
662 files2.append(os_path_relpath(f, trim_path))
667 if fnmatch.fnmatch(f, exclude):
669 INSTALL_FILES(bld, destdir, files, chmod=chmod, flat=flat,
670 python_fixup=python_fixup, base_name=trim_path)
671 Build.BuildContext.INSTALL_WILDCARD = INSTALL_WILDCARD
674 def INSTALL_DIRS(bld, destdir, dirs):
675 '''install a set of directories'''
676 destdir = bld.EXPAND_VARIABLES(destdir)
677 dirs = bld.EXPAND_VARIABLES(dirs)
678 for d in TO_LIST(dirs):
679 bld.install_dir(os.path.join(destdir, d))
680 Build.BuildContext.INSTALL_DIRS = INSTALL_DIRS
683 re_header = re.compile('#include[ \t]*"([^"]+)"', re.I | re.M)
684 class header_task(Task.Task):
686 The public headers (the one installed on the system) have both
687 different paths and contents, so the rename is not enough.
689 Intermediate .inst.h files are created because path manipulation
690 may be slow. The substitution is thus performed only once.
695 vars = ['INCLUDEDIR', 'HEADER_DEPS']
698 txt = self.inputs[0].read(self.env)
700 # hard-coded string, but only present in samba4 (I promise, you won't feel a thing)
701 txt = txt.replace('#if _SAMBA_BUILD_ == 4', '#if 1\n')
703 # use a regexp to substitute the #include lines in the files
704 map = self.generator.bld.hnodemap
705 dirnodes = self.generator.bld.hnodedirs
710 # pokemon headers: gotta catch'em all!
712 if s.startswith('bin/default'):
713 node = self.generator.bld.srcnode.find_resource(s.replace('bin/default/', ''))
715 Logs.warn('could not find the public header for %r' % s)
719 Logs.warn('could not find the public header replacement for build header %r' % s)
721 # this part is more difficult since the path may be relative to anything
722 for dirnode in dirnodes:
723 node = dirnode.find_resource(s)
729 Logs.warn('could not find the public header replacement for source header %r %r' % (s, node))
731 Logs.warn('-> could not find the public header for %r' % s)
733 return "#include <%s>" % fin
736 txt = re_header.sub(repl, txt)
738 # and write the output file
741 f = open(self.outputs[0].abspath(self.env), 'w')
747 @TaskGen.feature('pubh')
748 def make_public_headers(self):
750 collect the public headers to process and to install, then
751 create the substitutions (name and contents)
754 if not self.bld.is_install:
755 # install time only (lazy)
759 # hnodedirs: list of folders for searching the headers
760 # hnodemap: node ids and replacement string (node objects are unique)
762 self.bld.hnodedirs.append(self.path)
763 except AttributeError:
764 self.bld.hnodemap = {}
765 self.bld.hnodedirs = [self.bld.srcnode, self.path]
767 for k in 'source4 source4/include lib/talloc lib/tevent/ source4/lib/ldb/include/'.split():
768 node = self.bld.srcnode.find_dir(k)
770 self.bld.hnodedirs.append(node)
772 header_path = getattr(self, 'header_path', None) or ''
774 for x in self.to_list(self.headers):
776 # too complicated, but what was the original idea?
777 if isinstance(header_path, list):
779 for (p1, dir) in header_path:
780 lst = self.to_list(p1)
782 if fnmatch.fnmatch(x, p2):
790 inst_path = header_path
794 if x.find(':') != -1:
799 inn = self.path.find_resource(name)
802 raise ValueError("could not find the public header %r in %r" % (name, self.path))
803 out = inn.change_ext('.inst.h')
804 self.create_task('header', inn, out)
810 inst_path = inst_path + '/'
811 inst_path = inst_path + dest
813 self.bld.install_as('${INCLUDEDIR}/%s' % inst_path, out, self.env)
815 self.bld.hnodemap[inn.id] = inst_path
817 # create a hash (not md5) to make sure the headers are re-created if something changes
819 lst = list(self.bld.hnodemap.keys())
822 val = hash((val, k, self.bld.hnodemap[k]))
823 self.bld.env.HEADER_DEPS = val
825 def PUBLIC_HEADERS(bld, public_headers, header_path=None):
826 '''install some headers
828 header_path may either be a string that is added to the INCLUDEDIR,
829 or it can be a dictionary of wildcard patterns which map to destination
830 directories relative to INCLUDEDIR
832 bld.SET_BUILD_GROUP('final')
833 ret = bld(features=['pubh'], headers=public_headers, header_path=header_path)
835 Build.BuildContext.PUBLIC_HEADERS = PUBLIC_HEADERS
838 def MANPAGES(bld, manpages):
839 '''build and install manual pages'''
840 bld.env.MAN_XSL = 'http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl'
841 for m in manpages.split():
843 bld.SAMBA_GENERATOR(m,
847 rule='${XSLTPROC} -o ${TGT} --nonet ${MAN_XSL} ${SRC}'
849 bld.INSTALL_FILES('${MANDIR}/man%s' % m[-1], m, flat=True)
850 Build.BuildContext.MANPAGES = MANPAGES
853 #############################################################
854 # give a nicer display when building different types of files
855 def progress_display(self, msg, fname):
856 col1 = Logs.colors(self.color)
857 col2 = Logs.colors.NORMAL
858 total = self.position[1]
860 fs = '[%%%dd/%%%dd] %s %%s%%s%%s\n' % (n, n, msg)
861 return fs % (self.position[0], self.position[1], col1, fname, col2)
863 def link_display(self):
864 if Options.options.progress_bar != 0:
865 return Task.Task.old_display(self)
866 fname = self.outputs[0].bldpath(self.env)
867 return progress_display(self, 'Linking', fname)
868 Task.TaskBase.classes['cc_link'].display = link_display
870 def samba_display(self):
871 if Options.options.progress_bar != 0:
872 return Task.Task.old_display(self)
874 targets = LOCAL_CACHE(self, 'TARGET_TYPE')
875 if self.name in targets:
876 target_type = targets[self.name]
877 type_map = { 'GENERATOR' : 'Generating',
878 'PROTOTYPE' : 'Generating'
880 if target_type in type_map:
881 return progress_display(self, type_map[target_type], self.name)
883 if len(self.inputs) == 0:
884 return Task.Task.old_display(self)
886 fname = self.inputs[0].bldpath(self.env)
887 if fname[0:3] == '../':
889 ext_loc = fname.rfind('.')
891 return Task.Task.old_display(self)
892 ext = fname[ext_loc:]
894 ext_map = { '.idl' : 'Compiling IDL',
895 '.et' : 'Compiling ERRTABLE',
896 '.asn1': 'Compiling ASN1',
899 return progress_display(self, ext_map[ext], fname)
900 return Task.Task.old_display(self)
902 Task.TaskBase.classes['Task'].old_display = Task.TaskBase.classes['Task'].display
903 Task.TaskBase.classes['Task'].display = samba_display
908 def apply_bundle_remove_dynamiclib_patch(self):
909 if self.env['MACBUNDLE'] or getattr(self,'mac_bundle',False):
910 if not getattr(self,'vnum',None):
912 self.env['LINKFLAGS'].remove('-dynamiclib')
913 self.env['LINKFLAGS'].remove('-single_module')