File: Main.py

package info (click to toggle)
aap 1.072-1.1
  • links: PTS
  • area: main
  • in suites: etch, etch-m68k, lenny
  • size: 4,976 kB
  • ctags: 2,160
  • sloc: python: 15,113; makefile: 62; sh: 13
file content (310 lines) | stat: -rwxr-xr-x 9,983 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
#! /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:  # planned exit
        pass
    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)

    return msg, 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 = "More info 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)
            (dir, file) = os.path.split(component)
            resolved = os.path.normpath(os.path.join(dir, 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: