#!/usr/bin/env python #This file is part of pymkself. # #pymkself is free software: you can redistribute it and/or modify #it under the terms of the GNU Lesser General Public License as published by #the Free Software Foundation, either version 3 of the License, or #(at your option) any later version. # #pymkself is distributed in the hope that it will be useful, #but WITHOUT ANY WARRANTY; without even the implied warranty of #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #GNU Lesser General Public License for more details. # #You should have received a copy of the GNU Lesser General Public License #along with pymkself. If not, see . """ pymkself is a naive alternative to makeself shell script. It enables to pack into a single python run script: - a tarball with any type of data (.tar, .tar.gz, .tar.bz2) - a Python installation script Python run script auto extracts data from tarball and then makes use of specified installation script to perform user-defined tasks. It supports 2 different methods for encoding binary data into run script: - 'base64' based on base64.b64encode / base64.b64decode functions - 'binascii' based on binascii.hexlify / binascii.unhexlify functions Please refer to command line help for further instructions: pymkself --help Examples: pymkself -a data/toto.tar.gz -i install.py tmp/toto.run Will create a toto.run script in tmp directory using default encoding (base64) including toto.tar.gz tarball from data directory and install.py script from current working directory. Once in tmp directory, toto.run can be run as a classical Python script: toto.run or python toto.run It will unpack tarball archive and installation script in current directory. Then it runs installation script giving him all command line arguments received by toto.run. @author: "Cedric Campguilhem" @contact: Cedric dot Campguilhem at gmail.com @licence: LGPL v3 """ # Epydoc data #__docformat__ == "epytext" #fails with python 2.4 ??? # Imports import optparse import base64 import binascii import os # Global variables ENCODERS = {'base64': base64.b64encode, 'hexlify': binascii.hexlify} # Configure function (optparse parser) def configure(): """ Configures pymkself with command line arguments using U{optparse}. @return: U{options} and run script path @rtype: C{tuple}(C{optparse.Value}, C{str}) """ usage = "usage: %prog [options] script" description = "Not available." parser = optparse.OptionParser(usage=usage, description=description) parser.add_option('-a', '--archive', help='archive to be included in output script', dest='archive', default=None) parser.add_option('-i', '--install', help='path to install script', dest='install', default=None) parser.add_option('-e', '--encoder', help='select encoding method (defaults to %default)', choices=ENCODERS.keys(), default='base64', dest='encoder') (options, args) = parser.parse_args() if len(args) != 1: raise ValueError('One and only one argument must be provided.') return options, args[0] # Build function def build(options, runscript): """ Build run script. @param runscript: name of output run script @type runscript: C{str} @param options: command line options @type options: U{http://docs.python.org/library/optparse.html#parsing-arguments} """ b = open(runscript, 'wb') # run script header b.write('#!/usr/bin/env python\n') b.write('# WARNING ---------------------------------------------\n') b.write('# This file has been automatically created by pymkself.\n') b.write('# Do not try to modify it. \n') b.write('# -----------------------------------------------------\n') # required modules b.write('import base64\n') b.write('import binascii\n') b.write('import os\n') b.write('import sys\n') b.write('import subprocess\n') b.write('import tarfile\n') # binary payload encode = ENCODERS[options.encoder] b.write('ENCODER = "%s"\n' % options.encoder) if options.archive is not None: b.write('ARCHIVE = "%s"\n' % encode(open(options.archive, 'rb').read())) else: b.write('ARCHIVE = None\n') if options.install is not None: b.write('INSTALL = "%s"\n' % encode(open(options.install, 'rb').read())) else: b.write('INSTALL = None\n') # run script body core = """\ DECODERS = {'base64': base64.b64decode, 'hexlify': binascii.unhexlify} TMPTAR = 'out.tar.gz' INSTPY = 'install.py' decode = DECODERS[ENCODER] def untar(archive): if archive.endswith('.tar.gz') or archive.endswith('.tgz'): mode = 'r:gz' elif archive.endswith('.bz2'): mode = 'r:bz2' else: mode = 'r' tar = tarfile.open(archive, mode=mode) members = tar.getnames() for m in members: tar.extract(m, path='') tar.close() if ARCHIVE is not None: print "unpacking archive... ", open(TMPTAR, 'wb').write(decode(ARCHIVE)) print "done" print "untaring archive...", untar(TMPTAR) os.remove(TMPTAR) print "done" if INSTALL is not None: print "unpacking installation script... ", open(INSTPY, 'wb').write(decode(INSTALL)) os.chmod('install.py', 0755) print "done" install_args = sys.argv[1:] args = ["install.py"] args.extend(install_args) p = subprocess.Popen(args, shell=True) p.wait() """ b.write(core) os.chmod(runscript,0755) b.close() # Main function def main(): """ Main function of pymkself. """ # configure from command line arguments options, runscript = configure() # try to create output path if not existing try: os.makedirs(os.path.dirname(runscript)) except OSError: pass # build run script build(options, runscript) if __name__ == '__main__': main()