1 # jhbuild - a build script for GNOME 1.x and 2.x
2 # Copyright (C) 2001-2006 James Henstridge
3 # Copyright (C) 2003-2004 Seth Nickell
5 # terminal.py: build logic for a terminal interface
7 # This program is free software; you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 2 of the License, or
10 # (at your option) any later version.
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
17 # You should have received a copy of the GNU General Public License
18 # along with this program; if not, write to the Free Software
19 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27 from jhbuild
.frontends
import buildscript
28 from jhbuild
.utils
import cmds
29 from jhbuild
.utils
import trayicon
30 from jhbuild
.utils
import notify
31 from jhbuild
.errors
import CommandError
33 term
= os
.environ
.get('TERM', '')
34 is_xterm
= term
.find('xterm') >= 0 or term
== 'rxvt'
37 try: t_bold
= cmds
.get_output(['tput', 'bold'])
39 try: t_reset
= cmds
.get_output(['tput', 'sgr0'])
44 t_colour
[i
] = cmds
.get_output(['tput', 'setf', '%d' % i
])
45 t_colour
[i
+8] = t_bold
+ t_colour
[i
]
49 user_shell
= os
.environ
.get('SHELL', '/bin/sh')
63 icondir
= os
.path
.join(DATADIR
, 'jhbuild')
65 icondir
= os
.path
.join(os
.path
.dirname(__file__
), 'icons')
67 'checkout': 'checkout.png',
68 'force_checkout': 'checkout.png',
69 'download': 'checkout.png',
70 'unpack': 'checkout.png',
71 'patch': 'checkout.png',
72 'configure': 'configure.png',
73 #'clean': 'clean.png',
76 'install': 'install.png',
79 class TerminalBuildScript(buildscript
.BuildScript
):
81 is_end_of_build
= False
83 def __init__(self
, config
, module_list
):
84 buildscript
.BuildScript
.__init
__(self
, config
, module_list
)
85 self
.trayicon
= trayicon
.TrayIcon(config
)
86 self
.notify
= notify
.Notify(config
)
88 def message(self
, msg
, module_num
=-1):
89 '''Display a message to the user'''
92 module_num
= self
.module_num
94 progress
= ' [%d/%d]' % (module_num
, len(self
.modulelist
))
98 if not (self
.config
.quiet_mode
and self
.config
.progress_bar
):
99 uprint('%s*** %s ***%s%s' % (t_bold
, msg
, progress
, t_reset
))
101 progress_percent
= 1.0 * (module_num
-1) / len(self
.modulelist
)
102 self
.display_status_line(progress_percent
, module_num
, msg
)
105 sys
.stdout
.write('\033]0;jhbuild:%s%s\007' % (uencode(msg
), progress
))
107 self
.trayicon
.set_tooltip('%s%s' % (msg
, progress
))
109 def set_action(self
, action
, module
, module_num
=-1, action_target
=None):
111 module_num
= self
.module_num
112 if not action_target
:
113 action_target
= module
.name
114 self
.message('%s %s' % (action
, action_target
), module_num
)
116 def display_status_line(self
, progress
, module_num
, message
):
117 if self
.is_end_of_build
:
118 # hardcode progress to 100% at the end of the build
121 columns
= curses
.tigetnum('cols')
123 num_hashes
= int(round(progress
* width
))
124 progress_bar
= '[' + (num_hashes
* '=') + ((width
- num_hashes
) * '-') + ']'
126 module_no_digits
= len(str(len(self
.modulelist
)))
127 format_str
= '%%%dd' % module_no_digits
128 module_pos
= '[' + format_str
% module_num
+ '/' + format_str
% len(self
.modulelist
) + ']'
130 output
= '%s %s %s%s%s' % (progress_bar
, module_pos
, t_bold
, message
, t_reset
)
131 if len(output
) > columns
:
132 output
= output
[:columns
]
134 output
+= ' ' * (columns
-len(output
))
136 sys
.stdout
.write(output
+ '\r')
137 if self
.is_end_of_build
:
138 sys
.stdout
.write('\n')
142 def execute(self
, command
, hint
=None, cwd
=None, extra_env
=None):
144 raise CommandError(_('No command given'))
149 if isinstance(command
, (str, unicode)):
151 pretty_command
= command
153 pretty_command
= ' '.join(command
)
155 if not self
.config
.quiet_mode
:
158 kws
['stdin'] = subprocess
.PIPE
159 if hint
in ('cvs', 'svn', 'hg-update.py'):
160 kws
['stdout'] = subprocess
.PIPE
161 kws
['stderr'] = subprocess
.STDOUT
166 if self
.config
.quiet_mode
:
167 kws
['stdout'] = subprocess
.PIPE
168 kws
['stderr'] = subprocess
.STDOUT
173 if extra_env
is not None:
174 kws
['env'] = os
.environ
.copy()
175 kws
['env'].update(extra_env
)
178 p
= subprocess
.Popen(command
, **kws
)
180 raise CommandError(str(e
))
183 if hint
in ('cvs', 'svn', 'hg-update.py'):
185 def format_line(line
, error_output
, conflicts
= conflicts
, output
= output
):
186 if line
.startswith('C '):
187 conflicts
.append(line
)
189 if self
.config
.quiet_mode
:
193 if line
[-1] == '\n': line
= line
[:-1]
194 if not self
.config
.pretty_print
:
199 if line
.startswith('C '):
200 print '%s%s%s' % (t_colour
[12], line
, t_reset
)
201 elif line
.startswith('M '):
202 print '%s%s%s' % (t_colour
[10], line
, t_reset
)
203 elif line
.startswith('? '):
204 print '%s%s%s' % (t_colour
[8], line
, t_reset
)
208 cmds
.pprint_output(p
, format_line
)
210 uprint(_('\nConflicts during checkout:\n'))
211 for line
in conflicts
:
212 sys
.stdout
.write('%s %s%s\n'
213 % (t_colour
[12], line
, t_reset
))
214 # make sure conflicts fail
215 if p
.returncode
== 0 and hint
== 'cvs': p
.returncode
= 1
216 elif self
.config
.quiet_mode
:
217 def format_line(line
, error_output
, output
= output
):
219 cmds
.pprint_output(p
, format_line
)
223 except KeyboardInterrupt:
225 os
.kill(p
.pid
, signal
.SIGINT
)
227 # process might already be dead.
231 if self
.config
.quiet_mode
:
232 print ''.join(output
)
233 raise CommandError(_('########## Error running %s') % pretty_command
)
235 # it could happen on a really badly-timed ctrl-c (see bug 551641)
236 raise CommandError(_('########## Error running %s') % pretty_command
)
238 def start_phase(self
, module
, phase
):
239 self
.trayicon
.set_icon(os
.path
.join(icondir
,
240 phase_map
.get(phase
, 'build.png')))
242 def end_build(self
, failures
):
243 self
.is_end_of_build
= True
244 if len(failures
) == 0:
245 self
.message(_('success'))
247 self
.message(_('the following modules were not built'))
248 for module
in failures
:
252 def handle_error(self
, module
, phase
, nextphase
, error
, altphases
):
253 '''handle error during build'''
254 summary
= _('error during phase %(phase)s of %(module)s') % {
255 'phase': phase
, 'module':module
.name
}
257 error_message
= error
.args
[0]
258 self
.message('%s: %s' % (summary
, error_message
))
261 self
.message(summary
)
262 self
.trayicon
.set_icon(os
.path
.join(icondir
, 'error.png'))
263 self
.notify
.notify(summary
= summary
, body
= error_message
,
264 icon
= 'dialog-error', expire
= 20)
266 if self
.config
.trycheckout
:
267 if self
.triedcheckout
is None and altphases
.count('configure'):
268 self
.triedcheckout
= 'configure'
269 self
.message(_('automatically retrying configure'))
271 elif self
.triedcheckout
== 'configure' and altphases
.count('force_checkout'):
272 self
.triedcheckout
= 'done'
273 self
.message(_('automatically forcing a fresh checkout'))
274 return 'force_checkout'
275 self
.triedcheckout
= None
277 if not self
.config
.interact
:
281 uprint(_(' [1] rerun phase %s') % phase
)
283 uprint(_(' [2] ignore error and continue to %s') % nextphase
)
285 uprint(_(' [2] ignore error and continue to next module'))
286 uprint(_(' [3] give up on module'))
287 uprint(_(' [4] start shell'))
288 uprint(_(' [5] reload configuration'))
290 for altphase
in (altphases
or []):
291 uprint(_(' [%d] go to phase %s') % (i
, altphase
))
293 val
= raw_input(uencode(_('choice: ')))
304 os
.chdir(module
.get_builddir(self
))
306 os
.chdir(self
.config
.checkoutroot
)
307 uprint(_('exit shell to continue with build'))
308 os
.system(user_shell
)
309 os
.chdir(cwd
) # restor working directory
315 return altphases
[val
- nb_options
]
317 uprint(_('invalid choice'))
318 assert False, 'not reached'
320 BUILD_SCRIPT
= TerminalBuildScript