Mercurial > gub
view gub/build.py @ 6443:344d21a6233d
guile: bump to 1.9.14.
author | Jan Nieuwenhuizen <janneke@gnu.org> |
---|---|
date | Sat, 29 Jan 2011 15:48:48 +0100 |
parents | 72b46c22bf99 |
children |
line wrap: on
line source
import inspect import os import re import sys # from gub.syntax import printf from gub import commands from gub import context from gub import guppackage from gub import loggedos from gub import gub_log from gub import misc from gub import octal class Build (context.RunnableContext): '''How to build a piece of software TODO: move all non configure-make-make install stuff from AutoBuild here ''' source = '' branch = '' patches = [] dependencies = [] config_cache_flag_broken = True force_autoupdate = False install_after_build = True parallel_build_broken = False srcdir_build_broken = False autodir = '%(srcdir)s' config_cache_overrides = '' config_cache_file = '%(builddir)s/config.cache' configure_binary = '%(autodir)s/configure' configure_flags = ' --prefix=%(configure_prefix)s' configure_variables = '' compile_flags = '' make_flags = '' install_flags = ''' DESTDIR=%(install_root)s install''' destdir_install_broken = False install_flags_destdir_broken = misc.join_lines (''' bindir=%(install_prefix)s/bin aclocaldir=%(install_prefix)s/share/aclocal datadir=%(install_prefix)s/share exec_prefix=%(install_prefix)s gcc_tooldir=%(install_prefix)s includedir=%(install_prefix)s/include infodir=%(install_prefix)s/share/info libdir=%(install_prefix)s/lib libexecdir=%(install_prefix)s/lib mandir=%(install_prefix)s/share/man prefix=%(install_prefix)s sysconfdir=%(install_prefix)s/etc tooldir=%(install_prefix)s ''') configure_command = ' sh %(configure_binary)s%(configure_flags)s%(configure_variables)s' compile_command = 'make %(job_spec)s %(make_flags)s %(compile_flags)s' compile_command_native = 'make %(job_spec)s %(make_flags)s %(compile_flags)s' install_command = 'make %(make_flags)s %(install_flags)s ' license_files = ['%(srcdir)s/COPYING', '%(srcdir)s/COPYING.LIB', '%(srcdir)s/LICENSE', '%(srcdir)s/LICENCE',] def __init__ (self, settings, source): context.RunnableContext.__init__ (self, settings) self.source = source self.settings = settings self.source.connect_logger (gub_log.default_logger) if self.destdir_install_broken: self.install_command = 'make %(make_flags)s %(install_flags_destdir_broken)s %(install_flags)s' def connect_command_runner (self, runner): if runner: self.source.connect_logger (runner.logger) return context.RunnableContext.connect_command_runner (self, runner) @context.subst_method def checksum_file (self): return '%(packages)s/%(name)s%(vc_branch_suffix)s.checksum' def nop (self): pass def get_conflict_dict (self): return {} def get_done (self): return list () def is_done (self, stage): return stage in self.get_done () def set_done (self, stage): pass def stages (self): return list () @context.subst_method def stamp_file (self): return '%(statusdir)s/%(name)s-%(version)s-%(source_checksum)s' def get_stamp_file (self): return self.expand ('%(stamp_file)s') def apply_patch (self, patch, strip_components=1): name, parameters = misc.dissect_url (patch) strip = str (strip_components) strip = parameters.get ('strip', [strip])[0] strip = parameters.get ('strip_components', [strip])[0] self.system (''' cd %(srcdir)s && patch -p%(strip)s < %(patchdir)s/%(name)s ''', locals ()) def stage_message (self, stage): return self.expand (' *** Stage: %(stage)s (%(name)s, %(platform)s)\n', env=locals ()) def build (self, options=None, skip=[]): available = dict (inspect.getmembers (self, lambda x: hasattr (x, '__call__'))) stages = ['download'] + self.stages () tainted = False for stage in stages: if stage not in available or stage in skip: continue if self.is_done (stage): if stage not in ['download']: # optimization: excuse download cache from # tainting the build tainted = True continue self.runner.stage (self.stage_message (stage)) if (stage == 'package' and tainted and options and not options.force_package): msg = self.expand ('''This compile has previously been interrupted. To ensure a repeatable build, this will not be packaged. Run with --fresh # or issue rm %(stamp_file)s to force a full package rebuild, or --force-package to skip this check and risk a defective build. ''') gub_log.error (msg) self.system ('false') try: (available[stage]) () except: t, v, b = sys.exc_info () if t == misc.SystemFailed: # A failed patch will leave system in unpredictable state. if stage == 'patch': self.system ('rm %(stamp_file)s') raise if stage not in ['clean', 'download']: self.set_done (stage) def get_build_dependencies (self): return self.dependencies def with_platform (self, name): return misc.with_platform (name, self.settings.platform) def get_platform_build_dependencies (self): return [self.with_platform (n) for n in self.get_build_dependencies ()] def platform_name (self): return self.with_platform (self.name ()) @context.subst_method def platform (self): return self.settings.platform @context.subst_method def name (self): file = self.__class__.__module__ file = re.sub ('_xx_', '++', file) file = re.sub ('_x_', '+', file) return file @context.subst_method def pretty_name (self): name = self.__class__.__name__ name = re.sub ('__.*', '', name) return name @context.subst_method def file_name (self): return self.source.file_name () class AutoBuild (Build): '''Build a source package the traditional Unix way Based on the traditional configure; make; make install, this class tries to do everything including autotooling and libtool fooling. ''' def __init__ (self, settings, source): Build.__init__ (self, settings, source) self._dependencies = None self._build_dependencies = None self.split_packages = [] self.so_version = '1' def stages (self): return ['untar', 'patch', 'autoupdate', 'configure', 'compile', 'install', 'src_package', 'package', 'clean'] def configure_prepares_builddir (self): return True @context.subst_method def LD_PRELOAD (self): return '' @context.subst_method def libs (self): return '' @context.subst_method def so_extension (self): return '.so' @context.subst_method def rpath (self): return r'-Wl,-rpath -Wl,\$$ORIGIN/../lib -Wl,-rpath -Wl,%(system_prefix)s/lib' def get_substitution_dict (self, env={}): dict = { 'CPATH': '', 'CPLUS_INCLUDE_PATH': '', 'C_INCLUDE_PATH': '', 'LIBRARY_PATH': '/empty-means-cwd-in-feisty', } dict.update (env) d = context.RunnableContext.get_substitution_dict (self, dict).copy () return d def class_invoke_version (self, klas, name): name_version = name + '_' + self.version ().replace ('.', '_') if name_version in klas.__dict__: klas.__dict__[name_version] (self) def download (self): if not self.source.is_downloaded (): gub_log.default_logger.write_log (self.stage_message ('download'), 'stage') self.source.download () def get_repodir (self): return self.settings.downloads + '/' + self.name () def get_conflict_dict (self): """subpackage -> list of confict dict.""" return {'': [], 'devel': [], 'doc': [], 'runtime': []} def get_dependency_dict (self): """subpackage -> list of dependency dict.""" # FIMXE: '' always depends on runtime? return {'': [], 'devel': [], 'doc': [], 'runtime': [], 'x11': []} @context.subst_method def source_checksum (self): return self.source.checksum () @context.subst_method def basename (self): return misc.ball_basename (self.file_name ()) @context.subst_method def packaging_suffix_dir (self): return '' @context.subst_method def full_version (self): return self.version () @context.subst_method def build_dependencies_string (self): deps = sorted (set (self.get_build_dependencies ())) return ';'.join (deps) # FIXME: move version/branch/tracking macramee to Repository @context.subst_method def ball_suffix (self): # FIXME: ball suffix is also used by %(srcdir)s # for tracking repositories, the name of the source and # build dir must stay the same. if self.source.is_tracking (): return self.vc_branch_suffix () return '-' + self.source.version () @context.subst_method def vc_branch (self): return self.source.full_branch_name () @context.subst_method def vc_branch_suffix (self): b = self.vc_branch () if b: b = '-' + b return b @context.subst_method def version (self): return self.source.version () @context.subst_method def build_number (self): from gub import versiondb db = versiondb.VersionDataBase('versiondb/lilypond.versions') version_tup = misc.string_to_version (self.source.version() ) buildnumber = '%d' % db.get_next_build_number (version_tup) return buildnumber @context.subst_method def name_version (self): return '%s-%s' % (self.name (), self.version ()) @context.subst_method def srcdir (self): return '%(allsrcdir)s/%(name)s%(ball_suffix)s' @context.subst_method def builddir (self): return '%(allbuilddir)s/%(name)s%(ball_suffix)s' @context.subst_method def install_root (self): return '%(installdir)s/%(name)s-%(version)s-root' @context.subst_method def configure_prefix (self): return '%(prefix_dir)s' @context.subst_method def install_prefix (self): return '%(install_root)s%(prefix_dir)s' def aclocal_path (self): return [ '%(tools_prefix)s/share/aclocal', '%(system_prefix)s/share/aclocal', #'%(srcdir)s/m4', TODO #'%(srcdir)s', ] @context.subst_method def job_spec (self): if not self.parallel_build_broken: return '-j' + str (2 * int (self.settings.cpu_count_str)) return '' @context.subst_method def cpu_count (self): if not self.parallel_build_broken: return self.settings.cpu_count_str return '1' @context.subst_method def src_package_ball (self): return '%(src_package_uploads)s/%(name)s%(ball_suffix)s-src.%(platform)s.tar.gz' @context.subst_method def src_package_uploads (self): return '%(packages)s' def get_done (self): done = [] if os.path.exists (self.get_stamp_file ()): last = open (self.get_stamp_file ()).read ().strip () for stage in self.stages (): done += [stage] if stage == last: break if not last in done: done = [] return done def set_done (self, stage): self.dump (stage, self.get_stamp_file (), 'w') def patch (self): list (map (self.apply_patch, self.patches)) def autoupdate (self): # FIMXE: can we do this smarter? if self.force_autoupdate: self.runner._execute (commands.ForcedAutogenMagic (self)) else: self.runner._execute (commands.AutogenMagic (self)) def config_cache_settings (self): return self.config_cache_overrides def config_cache (self): string = self.config_cache_settings () if string: self.system ('mkdir -p %(builddir)s || true') self.dump (string, self.config_cache_file, permissions=octal.o755) def configure (self): if self.srcdir_build_broken: self.shadow () self.config_cache () self.system (''' mkdir -p %(builddir)s || true cd %(builddir)s && chmod +x %(configure_binary)s && %(configure_command)s ''') self.map_locate (libtool_disable_install_not_into_dot_libs_test, '%(builddir)s', 'libtool') def compile (self): self.system ('cd %(builddir)s && %(compile_command)s') def shadow (self): self.system ('rm -rf %(builddir)s') self.shadow_tree ('%(srcdir)s', '%(builddir)s') def update_config_guess_config_sub (self): guess = self.expand ('%(system_prefix)s/share/libtool/config/config.guess') sub = self.expand ('%(system_prefix)s/share/libtool/config/config.sub') for file in guess, sub: self.system ('cp -pv %(file)s %(autodir)s', locals ()) def update_libtool (self): self.map_locate (lambda logger, file: libtool_update (logger, self.expand ('%(system_prefix)s/bin/libtool'), file), '%(builddir)s', 'libtool') def pre_install (self): pass def install (self): '''Install package into %(install_root). Any overrides should follow this command, since it will erase the old install_root first. ''' self.system (''' rm -rf %(install_root)s ''') self.pre_install () self.system (''' cd %(builddir)s && %(install_command)s ''') self.post_install () def post_install (self): self.install_license () self.libtool_installed_la_fixups () self.system (''' rm -f \ %(install_root)s%(packaging_suffix_dir)s%(prefix_dir)s/info/dir \ %(install_root)s%(packaging_suffix_dir)s%(prefix_dir)s/info/dir.old \ %(install_root)s%(packaging_suffix_dir)s%(prefix_dir)s/share/info/dir \ %(install_root)s%(packaging_suffix_dir)s%(prefix_dir)s/share/info/dir.old \ %(install_root)s%(packaging_suffix_dir)s%(prefix_dir)s%(cross_dir)s/info/dir \ %(install_root)s%(packaging_suffix_dir)s%(prefix_dir)s%(cross_dir)s/info/dir.old \ %(install_root)s%(packaging_suffix_dir)s%(prefix_dir)s%(cross_dir)s/share/info/dir \ %(install_root)s%(packaging_suffix_dir)s%(prefix_dir)s%(cross_dir)s/share/info/dir.old \ ''') def install_license (self): def install (logger, lst): for file in lst: if os.path.exists (file): cmd = self.expand (''' mkdir -p %(install_root)s/license cp %(file)s %(install_root)s/license/%(name)s ''', locals ()) loggedos.system (logger, cmd) return self.func (install, list (map (self.expand, self.license_files))) def libtool_installed_la_fixups (self): def installed_la_fixup (logger, la): (dir, base) = os.path.split (la) base = base[3:-3] dir = re.sub (r"^\./", "/", dir) loggedos.file_sub (logger, [(''' *-L *[^\"\' ][^\"\' ]*''', ''), (self.expand ('''( |=|\')(/[^ ]*usr/lib|%(targetdir)s.*)/lib([^ \'/]*)\.(a|la|so)[^ \']*'''), '\\1-l\\3 '), ('^old_library=.*', self.expand ("""old_library='lib%(base)s.a'""", env=locals ())), ], la) if self.settings.platform.startswith ('mingw'): loggedos.file_sub (logger, [('library_names=.*', self.expand ("library_names='lib%(base)s.dll.a'", env=locals ()))], la) self.map_locate (installed_la_fixup, '%(install_root)s', 'lib*.la') def rewire_symlinks (self): def rewire (logger, file): if os.path.islink (file): s = os.readlink (file) if s.startswith ('/') and self.settings.system_root not in s: new_dest = os.path.join (self.settings.system_root, s[1:]) loggedos.remove (logger, file) loggedos.symlink (logger, new_dest, file) self.map_locate (rewire, '%(install_root)s', '*', silent=True) def package (self): self.rewire_symlinks () ps = self.get_packages () for p in ps: p.create_tarball () p.dump_header_file () p.clean () self.system ('rm -rf %(install_root)s') def get_subpackage_definitions (self): cross_dir = self.settings.cross_dir prefix_dir = self.settings.prefix_dir d = { 'base': [prefix_dir + '/share'], 'common': [prefix_dir + '/share'], 'devel': [ prefix_dir + '/bin/*-config', prefix_dir + '/include', prefix_dir + cross_dir + '/bin', prefix_dir + cross_dir + '/include', prefix_dir + cross_dir + '/lib', prefix_dir + cross_dir + '/libexec', prefix_dir + cross_dir + '/' + self.settings.target_architecture, prefix_dir + '/share/aclocal', prefix_dir + '/lib/lib*.a', prefix_dir + '/lib/pkgconfig', ], 'doc': [ prefix_dir + '/share/doc', prefix_dir + '/share/gtk-doc', prefix_dir + '/share/info', prefix_dir + '/share/man', prefix_dir + cross_dir + '/info', prefix_dir + cross_dir + '/man', ], 'runtime': ['/lib', prefix_dir + '/lib', prefix_dir + '/share'], 'x11': [prefix_dir + '/X11', prefix_dir + '/X11R6'], '' : ['/'], } return d subpackage_names = ['devel', 'doc', ''] # FIXME: when only patched in via MethodOverride, the real descr_dict, # category_dict are not pickled and cygwin packaging fails def description_dict (self): return {} def category_dict (self): return {} def get_packages (self): defs = dict (self.get_subpackage_definitions ()) ps = [] conflict_dict = self.get_conflict_dict () dep_dict = self.get_dependency_dict () descr_dict = self.description_dict () category_dict = self.category_dict () for sub in self.subpackage_names: filespecs = defs[sub] p = guppackage.GupPackage (self.runner) # FIXME: feature envy -> GupPackage constructor/factory p._file_specs = filespecs p.set_dict (self.get_substitution_dict (), sub) conflict_str = ';'.join (conflict_dict.get (sub, [])) if 'conflicts_string' in p._dict: conflict_str = p._dict['conflicts_string'] + ';' + conflict_str p._dict['conflicts_string'] = conflict_str dep_str = ';'.join (map (self.with_platform, dep_dict.get (sub, []))) dep_str = ';'.join (dep_dict.get (sub, [])) if 'dependencies_string' in p._dict: dep_str = p._dict['dependencies_string'] + ';' + dep_str p._dict['dependencies_string'] = dep_str # FIXME make generic: use cross.get_subpackage_dict_methods () or similar. desc_str = descr_dict.get (sub, '') p._dict['description'] = desc_str cat_str = category_dict.get (sub, '') p._dict['category'] = cat_str ps.append (p) return ps def src_package (self): # URG: basename may not be source dir name, eg, # package libjpeg uses jpeg-6b. Better fix at untar # stage? dir_name = re.sub (self.expand ('%(allsrcdir)s/'), '', self.expand ('%(srcdir)s')) _v = '' #self.os_interface.verbose_flag () self.system (''' tar -C %(allsrcdir)s --exclude "*~" --exclude "*.orig"%(_v)s -zcf %(src_package_ball)s %(dir_name)s ''', locals ()) def clean (self): self.system ('rm -rf %(stamp_file)s %(install_root)s', locals ()) if self.source.is_tracking (): # URG return self.system ('''rm -rf %(srcdir)s %(builddir)s''', locals ()) def untar (self): self.runner._execute (commands.UpdateSourceDir (self)) def pre_install_smurf_exe (self): def un_exe (logger, file): base = os.path.splitext (file)[0] loggedos.system (logger, self.expand ('mv %(file)s %(base)s', locals ())) self.map_locate (un_exe, '%(builddir)s', '*.exe') def post_install_smurf_exe (self): def add_exe (logger_why_already_in_self, file): if (not os.path.islink (file) and not os.path.splitext (file)[1] and loggedos.read_pipe (logger_why_already_in_self, self.expand ('file -b %(file)s', locals ())).startswith ('MS-DOS executable PE')): loggedos.system (logger_why_already_in_self, self.expand ('mv %(file)s %(file)s.exe', locals ())) self.map_locate (add_exe, '%(install_root)s/bin', '*') self.map_locate (add_exe, '%(install_prefix)s/bin', '*') def install_readmes (self): cmd = self.system (''' mkdir -p %(install_prefix)s/share/doc/%(name)s ''') def copy_readme (logger, file): if (os.path.isfile (file) and not os.path.basename (file).startswith ('Makefile') and not os.path.basename (file).startswith ('GNUmakefile')): loggedos.system (logger, self.expand ('cp %(file)s %(install_prefix)s/share/doc/%(name)s', locals ())) self.map_locate (copy_readme, '%(srcdir)s', '[A-Z]*') def build_version (self): "the version in the shipped package." # FIXME: ugly workaround needed for lilypond package... return '%(version)s' def disable_libtool_la_files (self, pattern): def disable_la (logger, file_name): loggedos.rename (logger, file_name, file_name + '-') self.map_find_files (disable_la, '%(install_prefix)s', 'lib' + pattern + '.la') # Used in mingw python and liblpsolve. Better replace this by # fixing the gcc linking command? def generate_dll_a_and_la (self, libname, depend=''): # ugh, atexit, _onexit mutliply defined in crt2.o symbols = 'ABbCDdGgiNpRrSsTtUuVvWw-?' #defined_symbols = 'tT' #defined_symbols = 'ACGgiNpSsTtuVvWw' defined_symbols = 'ABCDGgIiNpSsTtuRVvWw' self.system (misc.join_lines (''' cd %(install_prefix)s && echo EXPORTS > lib/lib%(libname)s.a.def && %(toolchain_prefix)snm bin/lib%(libname)s.dll | grep ' [%(defined_symbols)s] _' | sed -e 's/.* [%(defined_symbols)s] _//' | grep -Ev '^(atexit|__main|_onexit|_pei386_runtime_relocator|DllMain|DllMainCRTStartup)(@|$)' | sed -e 's/_imp__//' | sort | uniq -c | grep '^ *1 ' | sed -e 's/^ *1 //' >> lib/lib%(libname)s.a.def && (grep '@' lib/lib%(libname)s.a.def | sed -e 's/@.*//' >> lib/lib%(libname)s.a.def || :) && %(toolchain_prefix)sdlltool --def lib/lib%(libname)s.a.def --dllname bin/lib%(libname)s.dll --output-lib lib/lib%(libname)s.dll.a '''), locals ()) self.file_sub ([('LIBRARY', '%(libname)s'), ('STATICLIB', ''), ('DEPEND', ' %(depend)s'), ('LIBDIR', '%(prefix_dir)s/lib')], '%(sourcefiledir)s/libtool.la', '%(install_prefix)s/lib/lib%(libname)s.la', env=locals ()) class BinaryBuild (AutoBuild): def stages (self): return ['untar', 'install', 'package', 'clean'] def install (self): self.system ('mkdir -p %(install_root)s') _v = '' #self.os_interface.verbose_flag () self.system ('cd %(srcdir)s && tar -C %(srcdir)s -cf- . | tar -C %(install_root)s%(_v)s -p -xf-', env=locals ()) self.libtool_installed_la_fixups () # FIXME: splitting makes that cygwin's gettext + -devel subpackage # gets overwritten by cygwin's gettext-devel + '' base package subpackage_names = [''] class NullBuild (AutoBuild): """Placeholder for downloads """ def stages (self): return ['patch', 'install', 'package', 'clean'] subpackage_names = [''] def install (self): self.system ('mkdir -p %(install_root)s') class SdkBuild (NullBuild): def stages (self): return ['untar', 'patch', 'install', 'package', 'clean'] def install_root (self): return self.srcdir () def libtool_disable_install_not_into_dot_libs_test (logger, file): '''libtool: install: error: cannot install `libexslt.la' to a directory not ending in /home/janneke/vc/gub/target/mingw/build/libxslt-1.1.24/libexslt/.libs''' loggedos.file_sub (logger, [ (r'if test "\$inst_prefix_dir" = "\$destdir"; then', 'if false && test "$inst_prefix_dir" = "$destdir"; then'), (r' test "\$inst_prefix_dir" = "\$destdir" &&', ' false && test "$inst_prefix_dir" = "$destdir" &&')], file) def libtool_update (logger, libtool, file): if not os.path.exists (libtool): message = 'Cannot update libtool: no such file: %(libtool)s' % locals () logger.write_log (message, 'error') raise Exception (message) loggedos.system (logger, 'cp %(file)s %(file)s~' % locals ()) loggedos.system (logger, 'cp %(libtool)s %(file)s' % locals ()) libtool_disable_install_not_into_dot_libs_test (logger, file) loggedos.system (logger, 'chmod 755 %(file)s' % locals ()) def libtool_force_infer_tag (logger, tag, file): ''' libtool: compile: unable to infer tagged configuration ''' loggedos.file_sub (logger, [('^func_infer_tag ', '''func_infer_tag () { tagname=%(tag)s } old_func_infer_tag ''' % locals ())], file) def libtool_force_infer_tag_CXX (logger, file): ''' CXX seems the only valid tag name? ''' libtool_force_infer_tag (logger, 'CXX', file) def libtool_update_preserve_CC (logger, libtool, file): CC_re = '^CC="([^"]*)"' orig_CC = re.search ('(?m)' + CC_re, open (file).read ()).group (0) libtool_update (logger, libtool, file) loggedos.file_sub (logger, [(CC_re, orig_CC)], file) def libtool_update_preserve_vars (logger, libtool, vars, file): printf ('preserve: ', file) old = open (file).read () open (file + '.old', 'w').write (old) libtool_update (logger, libtool, file) new = open (file).read () open (file + '.new', 'w').write (new) def subst_vars (o, n): for v in vars: v_re = '(?m)^%(v)s="([^"]*)"' % locals () orig_m = re.search (v_re, o) if not orig_m: # some generated libtool thingies only have the first part # but vars in the second part must always be substituted printf ('from first part') orig_m = re.search (v_re, old) if orig_m: b = n n = re.sub (v_re, orig_m.group (0), n) printf ('replace:', orig_m.group (0)) if b == n: printf ('NODIFF:', v_re) else: printf ('not found:', v_re) return n # libtool comes in two parts which define the same/similar variables marker = '\nexit ' n1 = subst_vars (old[:old.find (marker)], new[:new.find (marker)]) n2 = subst_vars (old[old.find (marker):], new[new.find (marker):]) open (file, 'w').write (n1 + n2) loggedos.chmod (logger, file, octal.o755) class Change_dict: def __init__ (self, package, override): self._dict_method = package.get_substitution_dict self._add_dict = override def get_dict (self, env={}): env_copy = env.copy () env_copy.update (self._add_dict) d = self._dict_method (env_copy) return d def append_dict (self, env={}): d = self._dict_method () for (k, v) in list (self._add_dict.items ()): d[k] += v d.update (env) d = context.recurse_substitutions (d) return d def add_dict (self, env={}): d = self._dict_method () for (k, v) in list (self._add_dict.items ()): d[k] = v d.update (env) d = context.recurse_substitutions (d) return d def change_dict (package, add_dict): """Override the get_substitution_dict () method of PACKAGE.""" try: package.get_substitution_dict = Change_dict (package, add_dict).get_dict except AttributeError: pass def add_dict (package, add_dict): """Override the get_substitution_dict () method of PACKAGE.""" try: package.get_substitution_dict = Change_dict (package, add_dict).add_dict except AttributeError: pass def append_dict (package, add_dict): """Override the get_substitution_dict () method of PACKAGE.""" try: package.get_substitution_dict = Change_dict (package, add_dict).append_dict except AttributeError: pass