comparison 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
comparison
equal deleted inserted replaced
-1:000000000000 0:1c7da6388dd9
1 #!/usr/bin/env python
2
3 """
4 make a self-extracting virtualenv from directories or URLs
5 of packages
6
7 To package up all files in a virtualenvs source directory (e.g.):
8
9 python path/to/carton.py mozmill mozmill/src/*
10
11 This will create a self-extracting file, `mozmill.py`, that will unfold
12 a virtualenv
13 """
14
15 # imports
16 import os
17 import sys
18 import tarfile
19 import tempfile
20 import urllib2
21 from optparse import OptionParser
22 from StringIO import StringIO
23
24 # global variables
25 usage = "%prog [options] environment_name directory|url [...]"
26 virtualenv_url = 'http://pypi.python.org/packages/source/v/virtualenv/virtualenv-1.6.1.tar.gz'
27 template = """#!/usr/bin/env python
28
29 import os
30 import shutil
31 import subprocess
32 import sys
33 import tarfile
34 import tempfile
35 from optparse import OptionParser
36 from StringIO import StringIO
37
38 try:
39 call = subprocess.check_call
40 except AttributeError:
41 # old python; boo :(
42 call = subprocess.call
43
44 # virtualenv name
45 ENV='''%(ENV)s'''
46
47 # packed files
48 VIRTUAL_ENV='''%(VIRTUAL_ENV)s'''.decode('base64')
49 PACKAGE_SOURCES='''%(PACKAGE_SOURCES)s'''.decode('base64')
50
51 # unpack virtualenv
52 tempdir = tempfile.mkdtemp()
53 buffer = StringIO()
54 buffer.write(VIRTUAL_ENV)
55 buffer.seek(0)
56 tf = tarfile.open(mode='r', fileobj=buffer)
57 tf.extractall(tempdir)
58
59 # find the virtualenv
60 for root, dirs, files in os.walk(tempdir):
61 if 'virtualenv.py' in files:
62 virtualenv = os.path.join(root, 'virtualenv.py')
63 break
64 else:
65 raise Exception("virtualenv.py not found in " + tempdir)
66 print virtualenv
67
68 # create the virtualenv
69 call([sys.executable, virtualenv, ENV])
70
71 # unpack the sources
72 srcdir = os.path.join(ENV, 'src')
73 os.mkdir(srcdir)
74 buffer = StringIO()
75 buffer.write(PACKAGE_SOURCES)
76 buffer.seek(0)
77 tf = tarfile.open(mode='r', fileobj=buffer)
78 tf.extractall(srcdir)
79
80 # find the bin/scripts directory
81 for i in ('bin', 'Scripts'):
82 scripts_dir = os.path.abspath(os.path.join(ENV, i))
83 if os.path.exists(scripts_dir):
84 break
85 else:
86 raise Exception("Scripts directory not found in " + ENV)
87
88 # find the virtualenv's python
89 for i in ('python', 'python.exe'):
90 python = os.path.join(scripts_dir, i)
91 if os.path.exists(python):
92 break
93 else:
94 raise Exception("python not found in " + scripts_dir)
95
96 # activate the virtualenv
97 #activate = os.path.join(scripts_dir, 'activate_this.py')
98 #assert os.path.exists(activate), activate + " does not exist"
99 #execfile(activate, dict(__file__=activate))
100
101 # setup the sources for development
102 for i in os.listdir(srcdir):
103 subdir = os.path.join(srcdir, i)
104 if os.path.exists(os.path.join(srcdir, i, 'setup.py')):
105 call([python, 'setup.py', 'develop'], cwd=subdir)
106
107 # cleanup tempdir
108 # shutil.rmtree(tempdir)
109
110 # TODO:
111 # - add carton to the virtualenv (!)
112 # - add virtualenv to the virtualenv (!)
113 """
114
115 def isURL(path):
116 return path.startswith('http://') or path.startswith('https://')
117
118 def main(args=sys.argv[1:]):
119
120 # parse CLI arguments
121 parser = OptionParser(usage=usage, description=__doc__)
122 parser.add_option('-o', dest='outfile',
123 help="specify outfile; otherwise it will come from environment_name")
124 parser.add_option('--virtualenv', dest='virtualenv',
125 help="use virtualenv URL")
126 options, args = parser.parse_args(args)
127 if len(args) < 2:
128 parser.print_usage()
129 parser.exit()
130 environment = args[0]
131 if environment.endswith('.py'):
132 # stop on .py; will add it in later
133 environment = environment[:-3]
134 sources = args[1:]
135
136 # tar up the sources
137 tempdir = tempfile.mkdtemp()
138 source_buffer = StringIO()
139 sources_tar = tarfile.open(mode="w:gz", fileobj=source_buffer)
140 for source in sources:
141
142 if isURL(source):
143 # remote tarball
144 raise NotImplementedError
145 else:
146 # local directory or tarball
147 sources_tar.add(source, arcname=os.path.basename(source))
148 # could use git, hg, etc repos. but probably shouldn't
149 sources_tar.close()
150
151 # tar up virtualenv if not available
152 if options.virtualenv:
153 if isURL(options.virtualenv):
154 globals()['VIRTUAL_ENV'] = urllib2.urlopen(options.virtualenv).read()
155 else:
156 assert os.path.exists(options.virtualenv)
157 if os.path.isdir(options.virtualenv):
158 raise NotImplementedError("Hypothetically you should be able to use a local directory or tarball, but I haven't done this yet")
159 else:
160 # assert a tarfile
161 assert tarfile.is_tarfile(options.virtualenv), "%s must be a tar file" % options.virtualenv
162 globals()['VIRTUAL_ENV'] = file(options.virtualenv).read()
163 else:
164 globals()['VIRTUAL_ENV'] = urllib2.urlopen(virtualenv_url).read()
165 # TODO: used the below hashed value of VIRTUAL_ENV if set
166 # (set that with another file)
167
168 # interpolate "template" -> output
169 outfile = options.outfile
170 if outfile is None:
171 outfile = environment + '.py'
172 variables = {'VIRTUAL_ENV': VIRTUAL_ENV.encode('base64'),
173 'ENV': environment,
174 'PACKAGE_SOURCES': source_buffer.getvalue().encode('base64')}
175 f = file(outfile, 'w')
176 f.write(template % variables)
177 f.close()
178 try:
179 os.chmod(outfile, 0755)
180 except:
181 # you probably don't have os.chmod
182 pass
183
184 VIRTUAL_ENV = """"""
185
186 if __name__ == '__main__':
187 main()