#!/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()