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 run ./configure and make as usual. That will
50 call the right version of waf.''')
55 def SAMBA_BUILD_ENV(conf):
56 '''create the samba build environment'''
57 conf.env.BUILD_DIRECTORY = conf.blddir
58 mkdir_p(os.path.join(conf.blddir, LIB_PATH))
59 mkdir_p(os.path.join(conf.blddir, LIB_PATH, "private"))
60 mkdir_p(os.path.join(conf.blddir, "modules"))
61 mkdir_p(os.path.join(conf.blddir, 'python/samba/dcerpc'))
62 # this allows all of the bin/shared and bin/python targets
63 # to be expressed in terms of build directory paths
64 mkdir_p(os.path.join(conf.blddir, 'default'))
65 for p in ['python','shared', 'modules']:
66 link_target = os.path.join(conf.blddir, 'default/' + p)
67 if not os.path.lexists(link_target):
68 os.symlink('../' + p, link_target)
70 # get perl to put the blib files in the build directory
71 blib_bld = os.path.join(conf.blddir, 'default/pidl/blib')
72 blib_src = os.path.join(conf.srcdir, 'pidl/blib')
73 mkdir_p(blib_bld + '/man1')
74 mkdir_p(blib_bld + '/man3')
75 if os.path.islink(blib_src):
77 elif os.path.exists(blib_src):
78 shutil.rmtree(blib_src)
81 def ADD_INIT_FUNCTION(bld, subsystem, target, init_function):
82 '''add an init_function to the list for a subsystem'''
83 if init_function is None:
85 bld.ASSERT(subsystem is not None, "You must specify a subsystem for init_function '%s'" % init_function)
86 cache = LOCAL_CACHE(bld, 'INIT_FUNCTIONS')
87 if not subsystem in cache:
89 cache[subsystem].append( { 'TARGET':target, 'INIT_FUNCTION':init_function } )
90 Build.BuildContext.ADD_INIT_FUNCTION = ADD_INIT_FUNCTION
94 #################################################################
95 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)
191 ldflags = TO_LIST(ldflags)
193 features = 'cc cshlib symlink_lib install_lib'
194 if target_type == 'PYTHON':
197 # this is quite strange. we should add pyext feature for pyext
198 # but that breaks the build. This may be a bug in the waf python tool
199 features += ' pyembed'
202 features += ' abi_check'
205 if bld.env.HAVE_LD_VERSION_SCRIPT:
207 version = "%s_%s" % (Utils.g_module.APPNAME, Utils.g_module.VERSION)
209 version = "%s_%s" % (libname, vnum)
213 vscript = "%s.vscript" % libname
214 bld.ABI_VSCRIPT(libname, abi_directory, version, 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))
218 if Options.is_install:
219 # also make the .inst file depend on the vscript
220 instname = bld.env.shlib_PATTERN % (bundled_name + '.inst')
221 bld.add_manual_dependency(bld.path.find_or_declare(instname), bld.path.find_or_declare(vscript))
222 vscript = os.path.join(bld.path.abspath(bld.env), vscript)
224 bld.SET_BUILD_GROUP(group)
228 target = bundled_name,
229 depends_on = depends_on,
230 samba_ldflags = ldflags,
232 samba_includes = includes,
233 version_script = vscript,
234 local_include = local_include,
238 samba_inst_path = install_path,
240 samba_realname = realname,
241 samba_install = install,
242 abi_directory = "%s/%s" % (bld.path.abspath(), abi_directory),
243 abi_match = abi_match,
244 private_library = private_library,
245 grouping_library=grouping_library
248 if realname and not link_name:
249 link_name = 'shared/%s' % realname
252 t.link_name = link_name
254 if pc_files is not None:
255 bld.PKG_CONFIG_FILES(pc_files, vnum=vnum)
257 if manpages is not None and 'XSLTPROC_MANPAGES' in bld.env and bld.env['XSLTPROC_MANPAGES']:
258 bld.MANPAGES(manpages)
261 Build.BuildContext.SAMBA_LIBRARY = SAMBA_LIBRARY
264 #################################################################
265 def SAMBA_BINARY(bld, binname, source,
275 use_global_deps=True,
286 '''define a Samba binary'''
289 SET_TARGET_TYPE(bld, binname, 'DISABLED')
292 if not SET_TARGET_TYPE(bld, binname, 'BINARY'):
295 features = 'cc cprogram symlink_bin install_bin'
297 features += ' pyembed'
299 obj_target = binname + '.objlist'
301 source = bld.EXPAND_VARIABLES(source, vars=vars)
302 source = unique_list(TO_LIST(source))
304 if group == 'binaries':
305 subsystem_group = 'main'
307 subsystem_group = group
309 # first create a target for building the object files for this binary
310 # by separating in this way, we avoid recompiling the C files
311 # separately for the install binary and the build binary
312 bld.SAMBA_SUBSYSTEM(obj_target,
317 group = subsystem_group,
318 autoproto = autoproto,
319 subsystem_name = subsystem_name,
320 local_include = local_include,
321 use_hostcc = use_hostcc,
323 use_global_deps= use_global_deps)
325 bld.SET_BUILD_GROUP(group)
327 # the binary itself will depend on that object target
329 deps.append(obj_target)
336 samba_includes = includes,
337 local_include = local_include,
338 samba_modules = modules,
340 samba_subsystem= subsystem_name,
342 samba_inst_path= install_path,
343 samba_install = install,
344 samba_ldflags = TO_LIST(ldflags)
347 if manpages is not None and 'XSLTPROC_MANPAGES' in bld.env and bld.env['XSLTPROC_MANPAGES']:
348 bld.MANPAGES(manpages)
350 Build.BuildContext.SAMBA_BINARY = SAMBA_BINARY
353 #################################################################
354 def SAMBA_MODULE(bld, modname, source,
359 module_init_name='samba_init_module',
361 autoproto_extra_source='',
363 internal_module=True,
369 '''define a Samba module.'''
371 source = bld.EXPAND_VARIABLES(source, vars=vars)
373 if internal_module or BUILTIN_LIBRARY(bld, modname):
374 bld.SAMBA_SUBSYSTEM(modname, source,
378 autoproto_extra_source=autoproto_extra_source,
380 local_include=local_include,
383 bld.ADD_INIT_FUNCTION(subsystem, modname, init_function)
387 SET_TARGET_TYPE(bld, modname, 'DISABLED')
390 obj_target = modname + '.objlist'
393 if subsystem is not None:
394 deps += ' ' + subsystem
395 while realname.startswith("lib"+subsystem+"_"):
396 realname = realname[len("lib"+subsystem+"_"):]
397 while realname.startswith(subsystem+"_"):
398 realname = realname[len(subsystem+"_"):]
400 realname = bld.make_libname(realname)
401 while realname.startswith("lib"):
402 realname = realname[len("lib"):]
404 build_link_name = "modules/%s/%s" % (subsystem, realname)
407 cflags += " -D%s=%s" % (init_function, module_init_name)
409 bld.SAMBA_LIBRARY(modname,
414 autoproto = autoproto,
415 local_include=local_include,
417 link_name=build_link_name,
418 install_path="${MODULESDIR}/%s" % subsystem,
423 Build.BuildContext.SAMBA_MODULE = SAMBA_MODULE
426 #################################################################
427 def SAMBA_SUBSYSTEM(bld, modname, source,
436 init_function_sentinal=None,
438 autoproto_extra_source='',
441 local_include_first=True,
445 use_global_deps=True,
449 '''define a Samba subsystem'''
452 SET_TARGET_TYPE(bld, modname, 'DISABLED')
455 # remember empty subsystems, so we can strip the dependencies
456 if ((source == '') or (source == [])) and deps == '' and public_deps == '':
457 SET_TARGET_TYPE(bld, modname, 'EMPTY')
460 if not SET_TARGET_TYPE(bld, modname, 'SUBSYSTEM'):
463 source = bld.EXPAND_VARIABLES(source, vars=vars)
464 source = unique_list(TO_LIST(source))
466 deps += ' ' + public_deps
468 bld.SET_BUILD_GROUP(group)
478 samba_cflags = CURRENT_CFLAGS(bld, modname, cflags, hide_symbols=hide_symbols),
479 depends_on = depends_on,
480 samba_deps = TO_LIST(deps),
481 samba_includes = includes,
482 local_include = local_include,
483 local_include_first = local_include_first,
484 samba_subsystem= subsystem_name,
485 samba_use_hostcc = use_hostcc,
486 samba_use_global_deps = use_global_deps
489 if cflags_end is not None:
490 t.samba_cflags.extend(TO_LIST(cflags_end))
492 if autoproto is not None:
493 bld.SAMBA_AUTOPROTO(autoproto, source + TO_LIST(autoproto_extra_source))
494 if public_headers is not None:
495 bld.PUBLIC_HEADERS(public_headers, header_path=header_path)
499 Build.BuildContext.SAMBA_SUBSYSTEM = SAMBA_SUBSYSTEM
502 def SAMBA_GENERATOR(bld, name, rule, source='', target='',
503 group='generators', enabled=True,
508 '''A generic source generator target'''
510 if not SET_TARGET_TYPE(bld, name, 'GENERATOR'):
516 bld.SET_BUILD_GROUP(group)
519 source=bld.EXPAND_VARIABLES(source, vars=vars),
521 shell=isinstance(rule, str),
525 samba_type='GENERATOR',
532 if public_headers is not None:
533 bld.PUBLIC_HEADERS(public_headers, header_path=header_path)
535 Build.BuildContext.SAMBA_GENERATOR = SAMBA_GENERATOR
540 def SETUP_BUILD_GROUPS(bld):
541 '''setup build groups used to ensure that the different build
542 phases happen consecutively'''
543 bld.p_ln = bld.srcnode # we do want to see all targets!
544 bld.env['USING_BUILD_GROUPS'] = True
545 bld.add_group('setup')
546 bld.add_group('build_compiler_source')
547 bld.add_group('vscripts')
548 bld.add_group('base_libraries')
549 bld.add_group('generators')
550 bld.add_group('compiler_prototypes')
551 bld.add_group('compiler_libraries')
552 bld.add_group('build_compilers')
553 bld.add_group('build_source')
554 bld.add_group('prototypes')
555 bld.add_group('main')
556 bld.add_group('symbolcheck')
557 bld.add_group('libraries')
558 bld.add_group('binaries')
559 bld.add_group('syslibcheck')
560 bld.add_group('final')
561 Build.BuildContext.SETUP_BUILD_GROUPS = SETUP_BUILD_GROUPS
564 def SET_BUILD_GROUP(bld, group):
565 '''set the current build group'''
566 if not 'USING_BUILD_GROUPS' in bld.env:
569 Build.BuildContext.SET_BUILD_GROUP = SET_BUILD_GROUP
574 def ENABLE_TIMESTAMP_DEPENDENCIES(conf):
575 """use timestamps instead of file contents for deps
576 this currently doesn't work"""
577 def h_file(filename):
579 st = os.stat(filename)
580 if stat.S_ISDIR(st[stat.ST_MODE]): raise IOError('not a file')
582 m.update(str(st.st_mtime))
583 m.update(str(st.st_size))
586 Utils.h_file = h_file
590 t = Task.simple_task_type('copy_script', 'rm -f "${LINK_TARGET}" && ln -s "${SRC[0].abspath(env)}" ${LINK_TARGET}',
591 shell=True, color='PINK', ext_in='.bin')
594 @feature('copy_script')
595 @before('apply_link')
596 def copy_script(self):
597 tsk = self.create_task('copy_script', self.allnodes[0])
598 tsk.env.TARGET = self.target
600 def SAMBA_SCRIPT(bld, name, pattern, installdir, installname=None):
601 '''used to copy scripts from the source tree into the build directory
602 for use by selftest'''
604 source = bld.path.ant_glob(pattern)
606 bld.SET_BUILD_GROUP('build_source')
607 for s in TO_LIST(source):
609 if installname != None:
611 target = os.path.join(installdir, iname)
612 tgtdir = os.path.dirname(os.path.join(bld.srcnode.abspath(bld.env), '..', target))
614 t = bld(features='copy_script',
619 t.env.LINK_TARGET = target
621 Build.BuildContext.SAMBA_SCRIPT = SAMBA_SCRIPT
623 def copy_and_fix_python_path(task):
624 pattern='sys.path.insert(0, "bin/python")'
625 if task.env["PYTHONARCHDIR"] in sys.path and task.env["PYTHONDIR"] in sys.path:
627 elif task.env["PYTHONARCHDIR"] == task.env["PYTHONDIR"]:
628 replacement="""sys.path.insert(0, "%s")""" % task.env["PYTHONDIR"]
630 replacement="""sys.path.insert(0, "%s")
631 sys.path.insert(1, "%s")""" % (task.env["PYTHONARCHDIR"], task.env["PYTHONDIR"])
633 installed_location=task.outputs[0].bldpath(task.env)
634 source_file = open(task.inputs[0].srcpath(task.env))
635 installed_file = open(installed_location, 'w')
636 for line in source_file:
639 newline = line.replace(pattern, replacement)
640 installed_file.write(newline)
641 installed_file.close()
642 os.chmod(installed_location, 0755)
646 def install_file(bld, destdir, file, chmod=MODE_644, flat=False,
647 python_fixup=False, destname=None, base_name=None):
649 destdir = bld.EXPAND_VARIABLES(destdir)
653 destname = os.path.basename(destname)
654 dest = os.path.join(destdir, destname)
656 # fixup the python path it will use to find Samba modules
657 inst_file = file + '.inst'
658 bld.SAMBA_GENERATOR('python_%s' % destname,
659 rule=copy_and_fix_python_path,
664 file = os.path.join(base_name, file)
665 bld.install_as(dest, file, chmod=chmod)
668 def INSTALL_FILES(bld, destdir, files, chmod=MODE_644, flat=False,
669 python_fixup=False, destname=None, base_name=None):
670 '''install a set of files'''
671 for f in TO_LIST(files):
672 install_file(bld, destdir, f, chmod=chmod, flat=flat,
673 python_fixup=python_fixup, destname=destname,
675 Build.BuildContext.INSTALL_FILES = INSTALL_FILES
678 def INSTALL_WILDCARD(bld, destdir, pattern, chmod=MODE_644, flat=False,
679 python_fixup=False, exclude=None, trim_path=None):
680 '''install a set of files matching a wildcard pattern'''
681 files=TO_LIST(bld.path.ant_glob(pattern))
685 files2.append(os_path_relpath(f, trim_path))
690 if fnmatch.fnmatch(f, exclude):
692 INSTALL_FILES(bld, destdir, files, chmod=chmod, flat=flat,
693 python_fixup=python_fixup, base_name=trim_path)
694 Build.BuildContext.INSTALL_WILDCARD = INSTALL_WILDCARD
697 def INSTALL_DIRS(bld, destdir, dirs):
698 '''install a set of directories'''
699 destdir = bld.EXPAND_VARIABLES(destdir)
700 dirs = bld.EXPAND_VARIABLES(dirs)
701 for d in TO_LIST(dirs):
702 bld.install_dir(os.path.join(destdir, d))
703 Build.BuildContext.INSTALL_DIRS = INSTALL_DIRS
706 re_header = re.compile('#include[ \t]*"([^"]+)"', re.I | re.M)
707 class header_task(Task.Task):
709 The public headers (the one installed on the system) have both
710 different paths and contents, so the rename is not enough.
712 Intermediate .inst.h files are created because path manipulation
713 may be slow. The substitution is thus performed only once.
718 vars = ['INCLUDEDIR', 'HEADER_DEPS']
721 txt = self.inputs[0].read(self.env)
723 # hard-coded string, but only present in samba4 (I promise, you won't feel a thing)
724 txt = txt.replace('#if _SAMBA_BUILD_ == 4', '#if 1\n')
726 # use a regexp to substitute the #include lines in the files
727 map = self.generator.bld.hnodemap
728 dirnodes = self.generator.bld.hnodedirs
733 # pokemon headers: gotta catch'em all!
735 if s.startswith('bin/default'):
736 node = self.generator.bld.srcnode.find_resource(s.replace('bin/default/', ''))
738 Logs.warn('could not find the public header for %r' % s)
742 Logs.warn('could not find the public header replacement for build header %r' % s)
744 # this part is more difficult since the path may be relative to anything
745 for dirnode in dirnodes:
746 node = dirnode.find_resource(s)
752 Logs.warn('could not find the public header replacement for source header %r %r' % (s, node))
754 Logs.warn('-> could not find the public header for %r' % s)
756 return "#include <%s>" % fin
759 txt = re_header.sub(repl, txt)
761 # and write the output file
764 f = open(self.outputs[0].abspath(self.env), 'w')
770 @TaskGen.feature('pubh')
771 def make_public_headers(self):
773 collect the public headers to process and to install, then
774 create the substitutions (name and contents)
777 if not self.bld.is_install:
778 # install time only (lazy)
782 # hnodedirs: list of folders for searching the headers
783 # hnodemap: node ids and replacement string (node objects are unique)
785 self.bld.hnodedirs.append(self.path)
786 except AttributeError:
787 self.bld.hnodemap = {}
788 self.bld.hnodedirs = [self.bld.srcnode, self.path]
790 for k in 'source4 source4/include lib/talloc lib/tevent/ source4/lib/ldb/include/'.split():
791 node = self.bld.srcnode.find_dir(k)
793 self.bld.hnodedirs.append(node)
795 header_path = getattr(self, 'header_path', None) or ''
797 for x in self.to_list(self.headers):
799 # too complicated, but what was the original idea?
800 if isinstance(header_path, list):
802 for (p1, dir) in header_path:
803 lst = self.to_list(p1)
805 if fnmatch.fnmatch(x, p2):
813 inst_path = header_path
817 if x.find(':') != -1:
822 inn = self.path.find_resource(name)
825 raise ValueError("could not find the public header %r in %r" % (name, self.path))
826 out = inn.change_ext('.inst.h')
827 self.create_task('header', inn, out)
833 inst_path = inst_path + '/'
834 inst_path = inst_path + dest
836 self.bld.install_as('${INCLUDEDIR}/%s' % inst_path, out, self.env)
838 self.bld.hnodemap[inn.id] = inst_path
840 # create a hash (not md5) to make sure the headers are re-created if something changes
842 lst = list(self.bld.hnodemap.keys())
845 val = hash((val, k, self.bld.hnodemap[k]))
846 self.bld.env.HEADER_DEPS = val
848 def PUBLIC_HEADERS(bld, public_headers, header_path=None):
849 '''install some headers
851 header_path may either be a string that is added to the INCLUDEDIR,
852 or it can be a dictionary of wildcard patterns which map to destination
853 directories relative to INCLUDEDIR
855 bld.SET_BUILD_GROUP('final')
856 ret = bld(features=['pubh'], headers=public_headers, header_path=header_path)
858 Build.BuildContext.PUBLIC_HEADERS = PUBLIC_HEADERS
861 def MANPAGES(bld, manpages):
862 '''build and install manual pages'''
863 bld.env.MAN_XSL = 'http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl'
864 for m in manpages.split():
866 bld.SAMBA_GENERATOR(m,
870 rule='${XSLTPROC} -o ${TGT} --nonet ${MAN_XSL} ${SRC}'
872 bld.INSTALL_FILES('${MANDIR}/man%s' % m[-1], m, flat=True)
873 Build.BuildContext.MANPAGES = MANPAGES
876 #############################################################
877 # give a nicer display when building different types of files
878 def progress_display(self, msg, fname):
879 col1 = Logs.colors(self.color)
880 col2 = Logs.colors.NORMAL
881 total = self.position[1]
883 fs = '[%%%dd/%%%dd] %s %%s%%s%%s\n' % (n, n, msg)
884 return fs % (self.position[0], self.position[1], col1, fname, col2)
886 def link_display(self):
887 if Options.options.progress_bar != 0:
888 return Task.Task.old_display(self)
889 fname = self.outputs[0].bldpath(self.env)
890 return progress_display(self, 'Linking', fname)
891 Task.TaskBase.classes['cc_link'].display = link_display
893 def samba_display(self):
894 if Options.options.progress_bar != 0:
895 return Task.Task.old_display(self)
897 targets = LOCAL_CACHE(self, 'TARGET_TYPE')
898 if self.name in targets:
899 target_type = targets[self.name]
900 type_map = { 'GENERATOR' : 'Generating',
901 'PROTOTYPE' : 'Generating'
903 if target_type in type_map:
904 return progress_display(self, type_map[target_type], self.name)
906 if len(self.inputs) == 0:
907 return Task.Task.old_display(self)
909 fname = self.inputs[0].bldpath(self.env)
910 if fname[0:3] == '../':
912 ext_loc = fname.rfind('.')
914 return Task.Task.old_display(self)
915 ext = fname[ext_loc:]
917 ext_map = { '.idl' : 'Compiling IDL',
918 '.et' : 'Compiling ERRTABLE',
919 '.asn1': 'Compiling ASN1',
922 return progress_display(self, ext_map[ext], fname)
923 return Task.Task.old_display(self)
925 Task.TaskBase.classes['Task'].old_display = Task.TaskBase.classes['Task'].display
926 Task.TaskBase.classes['Task'].display = samba_display
931 def apply_bundle_remove_dynamiclib_patch(self):
932 if self.env['MACBUNDLE'] or getattr(self,'mac_bundle',False):
933 if not getattr(self,'vnum',None):
935 self.env['LINKFLAGS'].remove('-dynamiclib')
936 self.env['LINKFLAGS'].remove('-single_module')