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='',
344 internal_module=True,
350 '''define a Samba module.'''
352 source = bld.EXPAND_VARIABLES(source, vars=vars)
354 if internal_module or BUILTIN_LIBRARY(bld, modname):
355 bld.SAMBA_SUBSYSTEM(modname, source,
359 autoproto_extra_source=autoproto_extra_source,
361 local_include=local_include,
364 bld.ADD_INIT_FUNCTION(subsystem, modname, init_function)
368 SET_TARGET_TYPE(bld, modname, 'DISABLED')
371 if aliases is not None:
372 # if we have aliases, then create a private base library, and a set
373 # of modules on top of that library
375 cflags += " -D%s=%s" % (init_function, module_init_name)
377 basename = modname + '-base'
378 bld.SAMBA_LIBRARY(basename,
382 autoproto = autoproto,
383 local_include=local_include,
389 aliases = TO_LIST(aliases)
390 aliases.append(modname)
392 for alias in aliases:
393 bld.SAMBA_MODULE(alias,
395 internal_module=False,
397 init_function=init_function,
402 obj_target = modname + '.objlist'
405 if subsystem is not None:
406 deps += ' ' + subsystem
407 while realname.startswith("lib"+subsystem+"_"):
408 realname = realname[len("lib"+subsystem+"_"):]
409 while realname.startswith(subsystem+"_"):
410 realname = realname[len(subsystem+"_"):]
412 realname = bld.make_libname(realname)
413 while realname.startswith("lib"):
414 realname = realname[len("lib"):]
416 build_link_name = "modules/%s/%s" % (subsystem, realname)
419 cflags += " -D%s=%s" % (init_function, module_init_name)
421 bld.SAMBA_LIBRARY(modname,
426 autoproto = autoproto,
427 local_include=local_include,
429 link_name=build_link_name,
430 install_path="${MODULESDIR}/%s" % subsystem,
435 Build.BuildContext.SAMBA_MODULE = SAMBA_MODULE
438 #################################################################
439 def SAMBA_SUBSYSTEM(bld, modname, source,
448 init_function_sentinal=None,
450 autoproto_extra_source='',
453 local_include_first=True,
457 use_global_deps=True,
461 '''define a Samba subsystem'''
464 SET_TARGET_TYPE(bld, modname, 'DISABLED')
467 # remember empty subsystems, so we can strip the dependencies
468 if ((source == '') or (source == [])) and deps == '' and public_deps == '':
469 SET_TARGET_TYPE(bld, modname, 'EMPTY')
472 if not SET_TARGET_TYPE(bld, modname, 'SUBSYSTEM'):
475 source = bld.EXPAND_VARIABLES(source, vars=vars)
476 source = unique_list(TO_LIST(source))
478 deps += ' ' + public_deps
480 bld.SET_BUILD_GROUP(group)
490 samba_cflags = CURRENT_CFLAGS(bld, modname, cflags, hide_symbols=hide_symbols),
491 depends_on = depends_on,
492 samba_deps = TO_LIST(deps),
493 samba_includes = includes,
494 local_include = local_include,
495 local_include_first = local_include_first,
496 samba_subsystem= subsystem_name,
497 samba_use_hostcc = use_hostcc,
498 samba_use_global_deps = use_global_deps
501 if cflags_end is not None:
502 t.samba_cflags.extend(TO_LIST(cflags_end))
504 if autoproto is not None:
505 bld.SAMBA_AUTOPROTO(autoproto, source + TO_LIST(autoproto_extra_source))
506 if public_headers is not None:
507 bld.PUBLIC_HEADERS(public_headers, header_path=header_path)
511 Build.BuildContext.SAMBA_SUBSYSTEM = SAMBA_SUBSYSTEM
514 def SAMBA_GENERATOR(bld, name, rule, source='', target='',
515 group='generators', enabled=True,
520 '''A generic source generator target'''
522 if not SET_TARGET_TYPE(bld, name, 'GENERATOR'):
528 bld.SET_BUILD_GROUP(group)
531 source=bld.EXPAND_VARIABLES(source, vars=vars),
533 shell=isinstance(rule, str),
542 if public_headers is not None:
543 bld.PUBLIC_HEADERS(public_headers, header_path=header_path)
545 Build.BuildContext.SAMBA_GENERATOR = SAMBA_GENERATOR
550 def SETUP_BUILD_GROUPS(bld):
551 '''setup build groups used to ensure that the different build
552 phases happen consecutively'''
553 bld.p_ln = bld.srcnode # we do want to see all targets!
554 bld.env['USING_BUILD_GROUPS'] = True
555 bld.add_group('setup')
556 bld.add_group('build_compiler_source')
557 bld.add_group('base_libraries')
558 bld.add_group('generators')
559 bld.add_group('compiler_prototypes')
560 bld.add_group('compiler_libraries')
561 bld.add_group('build_compilers')
562 bld.add_group('build_source')
563 bld.add_group('prototypes')
564 bld.add_group('main')
565 bld.add_group('symbolcheck')
566 bld.add_group('libraries')
567 bld.add_group('binaries')
568 bld.add_group('syslibcheck')
569 bld.add_group('final')
570 Build.BuildContext.SETUP_BUILD_GROUPS = SETUP_BUILD_GROUPS
573 def SET_BUILD_GROUP(bld, group):
574 '''set the current build group'''
575 if not 'USING_BUILD_GROUPS' in bld.env:
578 Build.BuildContext.SET_BUILD_GROUP = SET_BUILD_GROUP
583 def ENABLE_TIMESTAMP_DEPENDENCIES(conf):
584 """use timestamps instead of file contents for deps
585 this currently doesn't work"""
586 def h_file(filename):
588 st = os.stat(filename)
589 if stat.S_ISDIR(st[stat.ST_MODE]): raise IOError('not a file')
591 m.update(str(st.st_mtime))
592 m.update(str(st.st_size))
595 Utils.h_file = h_file
599 t = Task.simple_task_type('copy_script', 'rm -f ${LINK_TARGET} && ln -s ${SRC[0].abspath(env)} ${LINK_TARGET}',
600 shell=True, color='PINK', ext_in='.bin')
603 @feature('copy_script')
604 @before('apply_link')
605 def copy_script(self):
606 tsk = self.create_task('copy_script', self.allnodes[0])
607 tsk.env.TARGET = self.target
609 def SAMBA_SCRIPT(bld, name, pattern, installdir, installname=None):
610 '''used to copy scripts from the source tree into the build directory
611 for use by selftest'''
613 source = bld.path.ant_glob(pattern)
615 bld.SET_BUILD_GROUP('build_source')
616 for s in TO_LIST(source):
618 if installname != None:
620 target = os.path.join(installdir, iname)
621 tgtdir = os.path.dirname(os.path.join(bld.srcnode.abspath(bld.env), '..', target))
623 t = bld(features='copy_script',
628 t.env.LINK_TARGET = target
630 Build.BuildContext.SAMBA_SCRIPT = SAMBA_SCRIPT
633 def install_file(bld, destdir, file, chmod=MODE_644, flat=False,
634 python_fixup=False, destname=None, base_name=None):
636 destdir = bld.EXPAND_VARIABLES(destdir)
640 destname = os.path.basename(destname)
641 dest = os.path.join(destdir, destname)
643 # fixup the python path it will use to find Samba modules
644 inst_file = file + '.inst'
645 if bld.env["PYTHONDIR"] not in sys.path:
646 regex = "s|\(sys.path.insert.*\)bin/python\(.*\)$|\\1${PYTHONDIR}\\2|g"
648 # Eliminate updating sys.path if the target python dir is already
650 regex = "s|sys.path.insert.*bin/python.*$||g"
651 bld.SAMBA_GENERATOR('python_%s' % destname,
652 rule="sed '%s' < ${SRC} > ${TGT}" % regex,
657 file = os.path.join(base_name, file)
658 bld.install_as(dest, file, chmod=chmod)
661 def INSTALL_FILES(bld, destdir, files, chmod=MODE_644, flat=False,
662 python_fixup=False, destname=None, base_name=None):
663 '''install a set of files'''
664 for f in TO_LIST(files):
665 install_file(bld, destdir, f, chmod=chmod, flat=flat,
666 python_fixup=python_fixup, destname=destname,
668 Build.BuildContext.INSTALL_FILES = INSTALL_FILES
671 def INSTALL_WILDCARD(bld, destdir, pattern, chmod=MODE_644, flat=False,
672 python_fixup=False, exclude=None, trim_path=None):
673 '''install a set of files matching a wildcard pattern'''
674 files=TO_LIST(bld.path.ant_glob(pattern))
678 files2.append(os_path_relpath(f, trim_path))
683 if fnmatch.fnmatch(f, exclude):
685 INSTALL_FILES(bld, destdir, files, chmod=chmod, flat=flat,
686 python_fixup=python_fixup, base_name=trim_path)
687 Build.BuildContext.INSTALL_WILDCARD = INSTALL_WILDCARD
690 def INSTALL_DIRS(bld, destdir, dirs):
691 '''install a set of directories'''
692 destdir = bld.EXPAND_VARIABLES(destdir)
693 dirs = bld.EXPAND_VARIABLES(dirs)
694 for d in TO_LIST(dirs):
695 bld.install_dir(os.path.join(destdir, d))
696 Build.BuildContext.INSTALL_DIRS = INSTALL_DIRS
699 re_header = re.compile('#include[ \t]*"([^"]+)"', re.I | re.M)
700 class header_task(Task.Task):
702 The public headers (the one installed on the system) have both
703 different paths and contents, so the rename is not enough.
705 Intermediate .inst.h files are created because path manipulation
706 may be slow. The substitution is thus performed only once.
711 vars = ['INCLUDEDIR', 'HEADER_DEPS']
714 txt = self.inputs[0].read(self.env)
716 # hard-coded string, but only present in samba4 (I promise, you won't feel a thing)
717 txt = txt.replace('#if _SAMBA_BUILD_ == 4', '#if 1\n')
719 # use a regexp to substitute the #include lines in the files
720 map = self.generator.bld.hnodemap
721 dirnodes = self.generator.bld.hnodedirs
726 # pokemon headers: gotta catch'em all!
728 if s.startswith('bin/default'):
729 node = self.generator.bld.srcnode.find_resource(s.replace('bin/default/', ''))
731 Logs.warn('could not find the public header for %r' % s)
735 Logs.warn('could not find the public header replacement for build header %r' % s)
737 # this part is more difficult since the path may be relative to anything
738 for dirnode in dirnodes:
739 node = dirnode.find_resource(s)
745 Logs.warn('could not find the public header replacement for source header %r %r' % (s, node))
747 Logs.warn('-> could not find the public header for %r' % s)
749 return "#include <%s>" % fin
752 txt = re_header.sub(repl, txt)
754 # and write the output file
757 f = open(self.outputs[0].abspath(self.env), 'w')
763 @TaskGen.feature('pubh')
764 def make_public_headers(self):
766 collect the public headers to process and to install, then
767 create the substitutions (name and contents)
770 if not self.bld.is_install:
771 # install time only (lazy)
775 # hnodedirs: list of folders for searching the headers
776 # hnodemap: node ids and replacement string (node objects are unique)
778 self.bld.hnodedirs.append(self.path)
779 except AttributeError:
780 self.bld.hnodemap = {}
781 self.bld.hnodedirs = [self.bld.srcnode, self.path]
783 for k in 'source4 source4/include lib/talloc lib/tevent/ source4/lib/ldb/include/'.split():
784 node = self.bld.srcnode.find_dir(k)
786 self.bld.hnodedirs.append(node)
788 header_path = getattr(self, 'header_path', None) or ''
790 for x in self.to_list(self.headers):
792 # too complicated, but what was the original idea?
793 if isinstance(header_path, list):
795 for (p1, dir) in header_path:
796 lst = self.to_list(p1)
798 if fnmatch.fnmatch(x, p2):
806 inst_path = header_path
810 if x.find(':') != -1:
815 inn = self.path.find_resource(name)
818 raise ValueError("could not find the public header %r in %r" % (name, self.path))
819 out = inn.change_ext('.inst.h')
820 self.create_task('header', inn, out)
826 inst_path = inst_path + '/'
827 inst_path = inst_path + dest
829 self.bld.install_as('${INCLUDEDIR}/%s' % inst_path, out, self.env)
831 self.bld.hnodemap[inn.id] = inst_path
833 # create a hash (not md5) to make sure the headers are re-created if something changes
835 lst = list(self.bld.hnodemap.keys())
838 val = hash((val, k, self.bld.hnodemap[k]))
839 self.bld.env.HEADER_DEPS = val
841 def PUBLIC_HEADERS(bld, public_headers, header_path=None):
842 '''install some headers
844 header_path may either be a string that is added to the INCLUDEDIR,
845 or it can be a dictionary of wildcard patterns which map to destination
846 directories relative to INCLUDEDIR
848 bld.SET_BUILD_GROUP('final')
849 ret = bld(features=['pubh'], headers=public_headers, header_path=header_path)
851 Build.BuildContext.PUBLIC_HEADERS = PUBLIC_HEADERS
854 def subst_at_vars(task):
855 '''substiture @VAR@ style variables in a file'''
856 src = task.inputs[0].srcpath(task.env)
857 tgt = task.outputs[0].bldpath(task.env)
863 a = re.split('(@\w+@)', s)
866 back_sub = [ ('PREFIX', '${prefix}'), ('EXEC_PREFIX', '${exec_prefix}')]
868 if re.match('@\w+@', v):
870 if not vname in task.env and vname.upper() in task.env:
871 vname = vname.upper()
872 if not vname in task.env:
873 Logs.error("Unknown substitution %s in %s" % (v, task.name))
875 v = SUBST_VARS_RECURSIVE(task.env[vname], task.env)
876 # now we back substitute the allowed pc vars
877 for (b, m) in back_sub:
880 if not b in done_var:
881 # we don't want to substitute the first usage
887 contents = ''.join(out)
889 s = f.write(contents)
894 def PKG_CONFIG_FILES(bld, pc_files, vnum=None):
895 '''install some pkg_config pc files'''
896 dest = '${PKGCONFIGDIR}'
897 dest = bld.EXPAND_VARIABLES(dest)
898 for f in TO_LIST(pc_files):
899 base=os.path.basename(f)
900 t = bld.SAMBA_GENERATOR('PKGCONFIG_%s' % base,
905 t.env.PACKAGE_VERSION = vnum
906 INSTALL_FILES(bld, dest, f, flat=True, destname=base)
907 Build.BuildContext.PKG_CONFIG_FILES = PKG_CONFIG_FILES
910 def MANPAGES(bld, manpages):
911 '''build and install manual pages'''
912 bld.env.MAN_XSL = 'http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl'
913 for m in manpages.split():
915 bld.SAMBA_GENERATOR(m,
919 rule='${XSLTPROC} -o ${TGT} --nonet ${MAN_XSL} ${SRC}'
921 bld.INSTALL_FILES('${MANDIR}/man%s' % m[-1], m, flat=True)
922 Build.BuildContext.MANPAGES = MANPAGES
925 #############################################################
926 # give a nicer display when building different types of files
927 def progress_display(self, msg, fname):
928 col1 = Logs.colors(self.color)
929 col2 = Logs.colors.NORMAL
930 total = self.position[1]
932 fs = '[%%%dd/%%%dd] %s %%s%%s%%s\n' % (n, n, msg)
933 return fs % (self.position[0], self.position[1], col1, fname, col2)
935 def link_display(self):
936 if Options.options.progress_bar != 0:
937 return Task.Task.old_display(self)
938 fname = self.outputs[0].bldpath(self.env)
939 return progress_display(self, 'Linking', fname)
940 Task.TaskBase.classes['cc_link'].display = link_display
942 def samba_display(self):
943 if Options.options.progress_bar != 0:
944 return Task.Task.old_display(self)
946 targets = LOCAL_CACHE(self, 'TARGET_TYPE')
947 if self.name in targets:
948 target_type = targets[self.name]
949 type_map = { 'GENERATOR' : 'Generating',
950 'PROTOTYPE' : 'Generating'
952 if target_type in type_map:
953 return progress_display(self, type_map[target_type], self.name)
955 if len(self.inputs) == 0:
956 return Task.Task.old_display(self)
958 fname = self.inputs[0].bldpath(self.env)
959 if fname[0:3] == '../':
961 ext_loc = fname.rfind('.')
963 return Task.Task.old_display(self)
964 ext = fname[ext_loc:]
966 ext_map = { '.idl' : 'Compiling IDL',
967 '.et' : 'Compiling ERRTABLE',
968 '.asn1': 'Compiling ASN1',
971 return progress_display(self, ext_map[ext], fname)
972 return Task.Task.old_display(self)
974 Task.TaskBase.classes['Task'].old_display = Task.TaskBase.classes['Task'].display
975 Task.TaskBase.classes['Task'].display = samba_display
980 def apply_bundle_remove_dynamiclib_patch(self):
981 if self.env['MACBUNDLE'] or getattr(self,'mac_bundle',False):
982 if not getattr(self,'vnum',None):
984 self.env['LINKFLAGS'].remove('-dynamiclib')
985 self.env['LINKFLAGS'].remove('-single_module')