diff carton.py @ 0:1c7da6388dd9

initial commit of carton
author Jeff Hammel <jhammel@mozilla.com>
date Fri, 08 Jul 2011 07:20:44 -0700
parents
children 75919adb199a
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/carton.py	Fri Jul 08 07:20:44 2011 -0700
@@ -0,0 +1,187 @@
+#!/usr/bin/env python
+
+"""
+make a self-extracting virtualenv from directories or URLs
+of packages
+
+To package up all files in a virtualenvs source directory (e.g.):
+
+python path/to/carton.py mozmill mozmill/src/*
+
+This will create a self-extracting file, `mozmill.py`, that will unfold
+a virtualenv
+"""
+
+# imports
+import os
+import sys
+import tarfile
+import tempfile
+import urllib2
+from optparse import OptionParser
+from StringIO import StringIO
+
+# global variables
+usage = "%prog [options] environment_name directory|url [...]"
+virtualenv_url = 'http://pypi.python.org/packages/source/v/virtualenv/virtualenv-1.6.1.tar.gz'
+template = """#!/usr/bin/env python
+
+import os
+import shutil
+import subprocess
+import sys
+import tarfile
+import tempfile
+from optparse import OptionParser
+from StringIO import StringIO
+
+try:
+    call = subprocess.check_call
+except AttributeError:
+    # old python; boo :(
+    call = subprocess.call
+
+# virtualenv name
+ENV='''%(ENV)s'''
+
+# packed files
+VIRTUAL_ENV='''%(VIRTUAL_ENV)s'''.decode('base64')
+PACKAGE_SOURCES='''%(PACKAGE_SOURCES)s'''.decode('base64')
+
+# unpack virtualenv
+tempdir = tempfile.mkdtemp()
+buffer = StringIO()
+buffer.write(VIRTUAL_ENV)
+buffer.seek(0)
+tf = tarfile.open(mode='r', fileobj=buffer)
+tf.extractall(tempdir)
+
+# find the virtualenv
+for root, dirs, files in os.walk(tempdir):
+    if 'virtualenv.py' in files:
+        virtualenv = os.path.join(root, 'virtualenv.py')
+        break
+else:
+    raise Exception("virtualenv.py not found in " + tempdir)
+print virtualenv
+        
+# create the virtualenv
+call([sys.executable, virtualenv, ENV])
+
+# unpack the sources
+srcdir = os.path.join(ENV, 'src')
+os.mkdir(srcdir)
+buffer = StringIO()
+buffer.write(PACKAGE_SOURCES)
+buffer.seek(0)
+tf = tarfile.open(mode='r', fileobj=buffer)
+tf.extractall(srcdir)
+
+# find the bin/scripts directory
+for i in ('bin', 'Scripts'):
+    scripts_dir = os.path.abspath(os.path.join(ENV, i))
+    if os.path.exists(scripts_dir):
+        break
+else:
+    raise Exception("Scripts directory not found in " + ENV)
+
+# find the virtualenv's python
+for i in ('python', 'python.exe'):
+    python = os.path.join(scripts_dir, i)
+    if os.path.exists(python):
+        break
+else:
+    raise Exception("python not found in " + scripts_dir)
+
+# activate the virtualenv
+#activate = os.path.join(scripts_dir, 'activate_this.py')
+#assert os.path.exists(activate), activate + " does not exist"
+#execfile(activate, dict(__file__=activate))
+
+# setup the sources for development
+for i in os.listdir(srcdir):
+    subdir = os.path.join(srcdir, i)
+    if os.path.exists(os.path.join(srcdir, i, 'setup.py')):
+        call([python, 'setup.py', 'develop'], cwd=subdir)
+
+# cleanup tempdir
+# shutil.rmtree(tempdir)
+
+# TODO:
+# - add carton to the virtualenv (!)
+# - add virtualenv to the virtualenv (!)
+"""
+
+def isURL(path):
+    return path.startswith('http://') or path.startswith('https://')
+
+def main(args=sys.argv[1:]):
+
+    # parse CLI arguments
+    parser = OptionParser(usage=usage, description=__doc__)
+    parser.add_option('-o', dest='outfile',
+                      help="specify outfile; otherwise it will come from environment_name")
+    parser.add_option('--virtualenv', dest='virtualenv',
+                      help="use virtualenv URL")
+    options, args = parser.parse_args(args)
+    if len(args) < 2:
+        parser.print_usage()
+        parser.exit()
+    environment = args[0]
+    if environment.endswith('.py'):
+        # stop on .py; will add it in later
+        environment = environment[:-3]
+    sources = args[1:]
+
+    # tar up the sources
+    tempdir = tempfile.mkdtemp()
+    source_buffer = StringIO()
+    sources_tar = tarfile.open(mode="w:gz", fileobj=source_buffer)
+    for source in sources:
+
+        if isURL(source):
+            # remote tarball
+            raise NotImplementedError
+        else:
+            # local directory or tarball
+            sources_tar.add(source, arcname=os.path.basename(source))
+        # could use git, hg, etc repos. but probably shouldn't
+    sources_tar.close()
+
+    # tar up virtualenv if not available
+    if options.virtualenv:
+        if isURL(options.virtualenv):
+            globals()['VIRTUAL_ENV'] = urllib2.urlopen(options.virtualenv).read()
+        else:
+            assert os.path.exists(options.virtualenv)
+            if os.path.isdir(options.virtualenv):
+                raise NotImplementedError("Hypothetically you should be able to use a local directory or tarball, but I haven't done this yet")
+            else:
+                # assert a tarfile
+                assert tarfile.is_tarfile(options.virtualenv), "%s must be a tar file" % options.virtualenv
+                globals()['VIRTUAL_ENV'] = file(options.virtualenv).read()
+    else:
+        globals()['VIRTUAL_ENV'] = urllib2.urlopen(virtualenv_url).read()
+        # TODO: used the below hashed value of VIRTUAL_ENV if set
+        # (set that with another file)
+
+    # interpolate "template" -> output
+    outfile = options.outfile
+    if outfile is None:
+        outfile = environment + '.py'
+    variables = {'VIRTUAL_ENV': VIRTUAL_ENV.encode('base64'),
+                 'ENV': environment,
+                 'PACKAGE_SOURCES': source_buffer.getvalue().encode('base64')}
+    f = file(outfile, 'w')
+    f.write(template % variables)
+    f.close()
+    try:
+        os.chmod(outfile, 0755)
+    except:
+        # you probably don't have os.chmod
+        pass
+
+VIRTUAL_ENV = """"""
+
+if __name__ == '__main__':
+    main()