diff --git a/doc/is.1.rst b/doc/is.1.rst index ac0104cec8110b2641c8da1d5d64d4857b03a923..0e56162936a9cb790318583dc5927ccfb8dedfb3 100644 --- a/doc/is.1.rst +++ b/doc/is.1.rst @@ -337,7 +337,7 @@ InstallSystems use two kind of images: **source image** - Each image available in repositories has to be build. The image before building is called a source image. In a source image, there are four directories and two files. Each images make the distinction between scripts and payloads. + Each image available in repositories has to be build. The image before building is called a source image. In a source image, there are five directories and two files. Each images make the distinction between scripts and payloads. build/ Scripts to customize the build process for the image. @@ -348,6 +348,9 @@ InstallSystems use two kind of images: setup/ The scripts with logical steps of the install are in this directory. + lib/ + Python modules which are embeded in image. + payload/ This directory embeds one or more payloads (typically rootfs) for the image. diff --git a/installsystems/image.py b/installsystems/image.py index 127a35df37cf3b6eab7594bedc372c606721301c..dc7ac60f34e5363a2dd02110b843b9dc10d3ec99 100644 --- a/installsystems/image.py +++ b/installsystems/image.py @@ -33,6 +33,7 @@ import re import shutil import stat import subprocess +import sys import tarfile import time import installsystems @@ -78,6 +79,36 @@ class Image(object): ''' return istools.compare_versions(v1, v2) + def _load_modules(self, lib_list, get_str): + ''' + Load python module embedded in image + + Return a dict of {module_name: module object} + ''' + if not lib_list: + return {} + arrow(u"Load libs") + old_level = arrowlevel(1) + gl ={} + # order matter! + lib_list.sort() + for filename in lib_list: + arrow(os.path.basename(filename)) + name = os.path.basename(filename).split('-', 1)[1][:-3] + if name in gl: + error('Module %s already loaded' % name) + # extract source code + try: + code = get_str(filename) + except Exception as e: + raise ISError(u"Extracting lib %s fail: %s" % + (filename, e)) + gl[name] = istools.string2module(name, code, filename) + # avoid ImportError when exec 'import name' + sys.modules[name] = gl[name] + arrowlevel(level=old_level) + return gl + class SourceImage(Image): ''' Image source manipulation class @@ -96,10 +127,12 @@ class SourceImage(Image): parser_path = os.path.join(path, "parser") setup_path = os.path.join(path, "setup") payload_path = os.path.join(path, "payload") + lib_path = os.path.join(path, "lib") # create base directories arrow("Creating base directories") try: - for d in (path, build_path, parser_path, setup_path, payload_path): + for d in (path, build_path, parser_path, setup_path, payload_path, + lib_path): if not os.path.exists(d) or not os.path.isdir(d): os.mkdir(d) except Exception as e: @@ -153,7 +186,7 @@ class SourceImage(Image): raise NotImplementedError("SourceImage must be local") Image.__init__(self) self.base_path = os.path.abspath(path) - for pathtype in ("build", "parser", "setup", "payload"): + for pathtype in ("build", "parser", "setup", "payload", "lib"): setattr(self, u"%s_path" % pathtype, os.path.join(self.base_path, pathtype)) self.check_source_image() self.description = self.parse_description() @@ -168,7 +201,7 @@ class SourceImage(Image): Check if we are a valid SourceImage directories ''' for d in (self.base_path, self.build_path, self.parser_path, - self.setup_path, self.payload_path): + self.setup_path, self.payload_path, self.lib_path): if not os.path.exists(d): raise ISError(u"Invalid source image: directory %s is missing" % d) if not os.path.isdir(d): @@ -187,9 +220,9 @@ class SourceImage(Image): raise ISError("Tarball already exists. Remove it before") # check python scripts if check: - self.check_scripts(self.build_path) - self.check_scripts(self.parser_path) - self.check_scripts(self.setup_path) + for d in (self.build_path, self.parser_path, self.setup_path, + self.lib_path): + self.check_scripts(d) # remove list rl = set() # run build script @@ -235,6 +268,8 @@ class SourceImage(Image): self.add_scripts(tarball, self.parser_path) # add setup scripts self.add_scripts(tarball, self.setup_path) + # add lib + self.add_scripts(tarball, self.lib_path) # closing tarball file tarball.close() except (SystemExit, KeyboardInterrupt): @@ -421,6 +456,11 @@ class SourceImage(Image): rebuild_list = [] cwd = os.getcwd() arrowlevel(1) + # load modules + lib_list = [fp.encode(locale.getpreferredencoding()) + for fp, fn in self.select_scripts(self.lib_path)] + func = lambda f: open(f).read() + modules = self._load_modules(lib_list, func) for fp, fn in self.select_scripts(script_directory): arrow(fn) os.chdir(exec_directory) @@ -433,6 +473,8 @@ class SourceImage(Image): # define execution context gl = {"rebuild": rebuild_list, "image": self} + # add embedded modules + gl.update(modules) # execute source code try: exec o_scripts in gl @@ -845,6 +887,9 @@ class PackageImage(Image): ''' arrow(u"Run %s scripts" % directory) arrowlevel(1) + # load modules + lib_list = self._tarball.getnames(re_pattern="lib/.*\.py") + modules = self._load_modules(lib_list, self._tarball.get_str) # get list of parser scripts l_scripts = self._tarball.getnames(re_pattern="%s/.*\.py" % directory) # order matter! @@ -868,6 +913,8 @@ class PackageImage(Image): for k in kwargs: gl[k] = kwargs[k] gl["image"] = self + # Add embedded modules + gl.update(modules) # execute source code try: exec o_scripts in gl diff --git a/installsystems/tools.py b/installsystems/tools.py index da4f50a914f7902fd20c970a3244e7abbc94cf9e..11d1de2d987936d1311e604e788a921dc0382eb6 100644 --- a/installsystems/tools.py +++ b/installsystems/tools.py @@ -21,6 +21,7 @@ InstallSystems Generic Tools Library ''' import hashlib +import imp import jinja2 import locale import math @@ -669,3 +670,19 @@ def render_templates(target, context, tpl_ext=".istpl", force=False, keep=False) os.chmod(file_path, st.st_mode) if not keep: os.unlink(tpl_path) + +def string2module(name, code, filename): + ''' + Create a python module from a string + ''' + # create an empty module + module = imp.new_module(name) + # compile module code + try: + bytecode = compile(code, filename, "exec") + except Exception as e: + raise ISError(u"Unable to compile %s fail: %s" % + (filename, e)) + # fill module + exec bytecode in module.__dict__ + return module