#! /usr/bin/env python # Part of the A-A-P recipe executive: The main function. # Copyright (C) 2002-2003 Stichting NLnet Labs # Permission to copy and use this file is specified in the file COPYING. # If this file is missing you can find it here: http://www.a-a-p.org/COPYING # Main Python code that executes an A-A-P recipe import sys from traceback import print_exception, format_exception # Each phase of executing a recipe is done by one module from DoAddDef import doadddef from DoArgs import doargs from DoBuild import dobuild from DoRead import doread from DoInstall import doinstall from Cache import dump_cache from Error import * from Sign import sign_write_all, sign_clear_all from Util import * import Global from Message import * # Globals exit_status = 0 # exit status; default is zero (success) exit_info = None # exception stack when something went wrong. def error_msg(recdict, msg): """Print an error message and set the exit status to one.""" global exit_status msg_error(recdict, msg) if exit_status == 0: exit_status = 1 profiling = 0 def do_the_work(argv, find_recipe, commands): """Common function for main() and execute(). "argv" is the list of command line arguments (excluding the program name). When "find_recipe" is non-zero, search for a recipe to load. When "commands" is None, execute the specified or default target(s). When "commands" is empty, do nothing. When "commands" is a non-empty string, execute these commands. Returns an error message and a work object. exit_status is set to a non-zero value when something failed. """ global exit_status, exit_info exit_status = 0 exit_info = None # We require Python 1.5 or later. if sys.version[0] == '1' and int(sys.version[2]) < 5: exit_status = 1 return "A-A-P requires Python version 1.5 or later.", None # Need to know the directory of this module. But __file__ isn't defined # when executed directly, then use argv[0] instead, see below. if not Global.aap_rootdir: Global.set_aap_rootdir(os.path.abspath(os.path.dirname(__file__))) # Internationalisation inits: setlocale and gettext. i18n_init() # # Do the main work. # msg = None work = None try: # 1. Process the command line arguments. Global.cmd_args = doargs(argv) if Global.cmd_args.has_option("verbose"): Global.cmd_args.printit() # When profiling is requested and it wasn't started yet, start all over # with profiling enabled. global profiling if not profiling and Global.cmd_args.has_option("profile"): profiling = 1 import profile prof = profile.Profile() try: res = prof.runcall(do_the_work, argv, find_recipe, commands) finally: prof.dump_stats(Global.cmd_args.options.get("profile")) return res # When "--install pkg" is used: install a package. if Global.cmd_args.has_option("install"): work = doinstall(Global.cmd_args.options.get("install")) else: # 2. Read the recipe and included recipes. Assignments and commands # are executed directly, rules and dependencies are stored. # "work" is a Work object with collected items from the recipe. work = doread(find_recipe) # 3. Add the default dependencies doadddef(work, work.recdict, 1) # 4. Build each target or execute the commands. if commands: from ParsePos import ParsePos from RecPos import RecPos from Process import Process fp = ParsePos([ RecPos(_('execute()'), 0) ], string = commands + '\n') Process(fp, work.recdict, 0) elif commands is None: dobuild(work) except NormalExit, r: # planned exit exit_status = r except SystemExit, r: exit_status = r msg = _("Aborted") except KeyboardInterrupt: exit_status = 1 msg = _("Interrupted") except UserError, e: exit_status = 1 msg = e.args except SyntaxError, e: exit_status = 1 exit_info = sys.exc_info() msg = _("Syntax error") + str(e) except: exit_status = 1 exit_info = sys.exc_info() msg = _("Internal Error") if work: # Dump entries for the downloaded files. dump_cache(work.recdict) # Dump the sign files. Clear all signatures (for any next run). sign_write_all(work.recdict) sign_clear_all() # Close any subshell for installing packages. from Port import close_sushell close_sushell(work.recdict) # Close any root shell. close_rootshell(work.recdict) # Cleanup for any configure checks. import DoConf DoConf.cleanup(work.recdict) if msg == None: return None, work else: return msg[0], work def main(setroot = 0): """ The main function to execute an A-A-P recipe. """ if setroot: # We need to know the location of our modules (find ccskim there). try: progname = os.path.realpath(sys.argv[0]) except: # Doesn't have os.path.realpath(), it's new in Python 2.2 # Use our copy of it. try: progname = myrealpath(sys.argv[0]) except: # Still not working? Fall back to using abspath(). progname = os.path.abspath(sys.argv[0]) Global.set_aap_rootdir(os.path.dirname(progname)) # When started with a relative path and changing directories we still # need to be able to find our modules. sys.path.append(Global.aap_rootdir) # Do the work. msg, work = do_the_work(sys.argv[1:], 1, None) handle_work_done(msg, work) sys.exit(exit_status) def handle_work_done(msg, work): """Handle errors after calling do_the_work().""" if msg: if work: error_msg(work.recdict, msg) else: error_msg(None, msg) if exit_info: print_exception(exit_info[0], exit_info[1], exit_info[2]) error_msg(None, string.join(format_exception(exit_info[0], exit_info[1], exit_info[2]))) # Get the log file name before it's cleared by stopping the log. # Stop the log before the message, so that it doesn't get into the log. if exit_status: if msg_logname(): logmsg = "All messages are in the logfile: " + msg_logname() else: logmsg = '' msg_stoplog() if exit_status and logmsg: if work: msg_info(work.recdict, logmsg) else: msg_info(None, logmsg) # Return a canonical path (i.e. the absolute location of a file on the # filesystem). This is from os.path of Python 2.2. def myrealpath(filename): """Return the canonical path of the specified filename, eliminating any symbolic links encountered in the path.""" filename = os.path.abspath(filename) bits = ['/'] + filename.split('/')[1:] for i in range(2, len(bits)+1): component = apply(os.path.join, tuple(bits[0:i])) if os.path.islink(component): resolved = os.readlink(component) (adir, afile) = os.path.split(component) resolved = os.path.normpath(os.path.join(adir, resolved)) newpath = apply(os.path.join, tuple([resolved] + bits[i:])) return myrealpath(newpath) return filename # When executed directly, call the main function. if __name__ == '__main__': main(1) # # Other programs may call this funtion to execute one or more recipe commands. # For example: execute(":do view thisfile") # def execute(commands, argv = [], find_recipe = 0): """Execute recipe commands "commands". See do_the_work() for details about "commands". "argv" is a list of command line arguments. "find_recipe" is non-zero to find a default recipe. Returns an error message or None.""" msg, work = do_the_work(argv, find_recipe, commands) if exit_info: print msg print_exception(exit_info[0], exit_info[1], exit_info[2]) msg_stoplog() return msg # # Other programs may call this function to fetch a list of files. # Uses the "main.aap" recipe, unless "argv" specifies another recipe to use. # def fetch(fnames, argv = []): """Fetch files in list "fnames". "argv" is a list of command line arguments. Will search for a default recipe if none is specified. Returns an error message or None.""" from Dictlist import list2str return execute(":fetch %s" % list2str(fnames), argv, 1) # # Other programs may call this function to obtain the list of nodes and # recdict. It was added to be used by the IDE. # Uses the "main.aap" recipe, unless "argv" specifies another recipe to use. # def get_nodelist(argv = []): """Obtain the information from the recipe specified with "argv" or the default recipe. Return a tuple with the list of nodes and a dictionary for the global variables.""" msg, work = do_the_work(argv, 1, '') handle_work_done(msg, work) if not work: return [], {} return work.nodes.values(), work.recdict def get_actionlist(argv = []): """Obtain a dictionary that lists the actions supported for each file type. Can be used to make sure that executing the action will actually work. "argv" is usually empty.""" msg, work = do_the_work(argv, 0, '') handle_work_done(msg, work) if not work: return {} import Action return Action.action_get_list() # vim: set sw=4 et sts=4 tw=79 fo+=l: