Run tests with $PATH set
[0export.git] / unpacker.py
blob9a508eed5f7a6bf05c912b5d3884b87a1d6bf343
1 import tempfile, sys, shutil, os, subprocess, gobject, tarfile, optparse
3 INSTALLER_MODE = @INSTALLER_MODE@
5 def get_busy_pointer():
6 # See http://mail.gnome.org/archives/gtk-list/2007-May/msg00100.html
7 bit_data = "\
8 \x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\
9 \x0c\x00\x00\x00\x1c\x00\x00\x00\x3c\x00\x00\x00\
10 \x7c\x00\x00\x00\xfc\x00\x00\x00\xfc\x01\x00\x00\
11 \xfc\x3b\x00\x00\x7c\x38\x00\x00\x6c\x54\x00\x00\
12 \xc4\xdc\x00\x00\xc0\x44\x00\x00\x80\x39\x00\x00\
13 \x80\x39\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
14 \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
15 \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
16 \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
17 \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
18 \x00\x00\x00\x00\x00\x00\x00\x00"
19 import gtk
20 try:
21 pix = gtk.gdk.bitmap_create_from_data(None, bit_data, 32, 32)
22 color = gtk.gdk.Color()
23 return gtk.gdk.Cursor(pix, pix, color, color, 2, 2)
24 except:
25 #old bug http://bugzilla.gnome.org/show_bug.cgi?id=103616
26 return gtk.gdk.Cursor(gtk.gdk.WATCH)
28 class GUI():
29 def __init__(self):
30 w = gtk.Dialog(title = "Zero Install")
32 w.set_resizable(False)
33 w.set_has_separator(False)
34 w.vbox.set_border_width(4)
35 hbox = gtk.HBox(False, 12)
36 hbox.set_border_width(5)
37 image = gtk.image_new_from_stock(gtk.STOCK_DIALOG_INFO, gtk.ICON_SIZE_DIALOG)
38 image.set_alignment(0.5, 0.5)
40 message_vbox = gtk.VBox(False, 12)
41 message_vbox.pack_start(gtk.Label('The software is being unpacked. Please wait...'), True, True)
42 message_vbox.set_border_width(12)
44 progress_bar = gtk.ProgressBar()
45 message_vbox.add(progress_bar)
47 actions_vbox = gtk.VBox(False, 4)
48 label = gtk.Label('Actions:')
49 label.set_alignment(0, 0.5)
50 actions_vbox.pack_start(label, False, True, 0)
52 if subprocess.call('which xdg-desktop-menu',
53 shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT):
54 self.add_to_menu_option = gtk.CheckButton('(no xdg-desktop-menu command;\n'
55 'to add menu items, install xdg-utils first)')
56 actions_vbox.pack_start(self.add_to_menu_option, False, True, 0)
57 self.add_to_menu_option.set_active(False)
58 self.add_to_menu_option.set_sensitive(False)
59 else:
60 self.add_to_menu_option = gtk.CheckButton('Add to menu')
61 actions_vbox.pack_start(self.add_to_menu_option, False, True, 0)
62 self.add_to_menu_option.set_active(True)
64 self.run_option = gtk.CheckButton('Run program')
65 self.run_option.set_active(True)
66 actions_vbox.pack_start(self.run_option, False, True, 0)
68 def update_sensitive(option):
69 w.set_response_sensitive(gtk.RESPONSE_OK,
70 self.add_to_menu_option.get_active() or self.run_option.get_active())
71 self.add_to_menu_option.connect('toggled', update_sensitive)
72 self.run_option.connect('toggled', update_sensitive)
74 self.notebook = gtk.Notebook()
75 self.notebook.set_show_tabs(False)
76 self.notebook.set_show_border(False)
77 self.notebook.append_page(message_vbox, None)
78 self.notebook.append_page(actions_vbox, None)
80 hbox.pack_start(image, False, False, 0)
81 hbox.pack_start(self.notebook, True, True)
82 w.vbox.pack_start(hbox, False, False, 0)
83 cancel_button = w.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
84 cancel_button.unset_flags(gtk.CAN_DEFAULT)
85 self.ok_button = w.add_button(gtk.STOCK_OK, gtk.RESPONSE_OK)
86 w.set_response_sensitive(gtk.RESPONSE_OK, False)
87 w.set_position(gtk.WIN_POS_MOUSE)
88 w.set_title('Zero Install')
89 w.set_default_size(400, 300)
90 w.show()
91 w.window.set_cursor(get_busy_pointer())
92 gtk.gdk.flush()
93 w.vbox.show_all()
94 def response(w, resp):
95 print >>sys.stderr, "Cancelled at user's request"
96 if installer:
97 installer.abort()
98 sys.exit(1)
99 self.response_handler = w.connect('response', response)
100 self.w = w
102 def finish_install(self):
103 import gtk
104 self.notebook.next_page()
105 self.w.disconnect(self.response_handler)
106 self.w.window.set_cursor(None)
107 self.w.set_response_sensitive(gtk.RESPONSE_OK, True)
108 self.ok_button.grab_focus()
109 resp = self.w.run()
111 self.w.destroy()
112 gtk.gdk.flush()
114 if resp != gtk.RESPONSE_OK:
115 raise Exception("Cancelled at user's request")
117 if self.add_to_menu_option.get_active():
118 install.add_to_menu(toplevel_uris)
120 tmp = tempfile.mkdtemp(prefix = '0export-')
121 try:
122 w = None
123 progress_bar = None
124 installer = None
125 print "Extracting bootstrap data..."
127 setup_path = sys.argv[1]
128 archive_offset = int(sys.argv[2])
129 del sys.argv[1:3]
131 parser = optparse.OptionParser(usage="usage: %s\n"
132 "Run self-extracting installer" % setup_path)
134 parser.add_option("-v", "--verbose", help="more verbose output", action='count')
136 (options, args) = parser.parse_args()
138 if options.verbose:
139 import logging
140 logger = logging.getLogger()
141 if options.verbose == 1:
142 logger.setLevel(logging.INFO)
143 else:
144 logger.setLevel(logging.DEBUG)
146 if len(args) == 0 and 'DISPLAY' in os.environ:
147 import pygtk; pygtk.require('2.0')
148 import gtk
149 if gtk.gdk.get_display() is None:
150 print >>sys.stderr, "Failed to open display; using console mode"
151 w = None
152 else:
153 w = GUI()
154 else:
155 w = None
157 self_stream = file(setup_path, 'rb')
158 self_stream.seek(archive_offset)
159 old_umask = os.umask(077)
160 mainloop = gobject.MainLoop(gobject.main_context_default())
161 archive = tarfile.open(name=self_stream.name, mode='r|',
162 fileobj=self_stream)
164 # Extract the bootstrap data (interfaces, 0install itself)
165 bootstrap_stream = None
166 for tarmember in archive:
167 if tarmember.name == 'bootstrap.tar.bz2':
168 bootstrap_stream = archive.extractfile(tarmember)
169 break
170 else:
171 raise Exception("No bootstrap data in archive (broken?)")
173 bootstrap_tar = tarfile.open(name=bootstrap_stream.name, mode='r|bz2',
174 fileobj=bootstrap_stream)
175 umask = os.umask(0)
176 os.umask(umask)
177 items = []
178 for tarinfo in bootstrap_tar:
179 tarinfo.mode = (tarinfo.mode | 0644) & ~umask
180 bootstrap_tar.extract(tarinfo, tmp)
181 if tarinfo.isdir():
182 items.append(tarinfo)
183 for tarinfo in items:
184 path = os.path.join(tmp, tarinfo.name)
185 os.utime(path, (tarinfo.mtime, tarinfo.mtime))
186 bootstrap_tar.close()
187 bootstrap_stream.close()
188 archive.close()
190 # Stop Python adding .pyc files
191 for root, dirs, files in os.walk(os.path.join(tmp, 'zeroinstall')):
192 os.chmod(root, 0500)
194 sys.path.insert(0, tmp)
195 sys.argv[0] = os.path.join(tmp, 'install.py')
197 print "Installing..."
198 import install
199 installer = install.Installer()
200 toplevel_uris = installer.do_install(self_stream, progress_bar, archive_offset)
201 self_stream.close()
203 if w:
204 w.finish_install()
205 finally:
206 print "Removing temporary files..."
207 for root, dirs, files in os.walk(os.path.join(tmp, 'zeroinstall')):
208 os.chmod(root, 0700)
209 shutil.rmtree(tmp)
211 if w is None or w.run_option.get_active():
212 print "Running..."
213 install.run(toplevel_uris[0], not INSTALLER_MODE and ['--offline'] or [], args)
214 elif INSTALLER_MODE:
215 print "Downloading..."
216 assert not args, "Download only mode, but arguments given: %s" % args
217 install.run(toplevel_uris[0], ['--download-only'])