~azzar1/unity/add-show-desktop-key

« back to all changes in this revision

Viewing changes to setup.py

  • Committer: mattgiuca
  • Date: 2008-01-13 23:38:58 UTC
  • Revision ID: svn-v3-trunk0:2b9c9e99-6f39-0410-b283-7f802c844ae2:trunk:213
Fileservice / Files (Python and JS files):
    Added code to handle "nice filetypes" and "nice svn status".
    New JSON value for dir listings, "type_nice" which contains the nice
    filetype calculated by the server.
    The client calculates the nice svn status.
    Added new icons for svn status for added, deleted, missing.

Show diffs side-by-side

added added

removed removed

Lines of Context:
25
25
# It is called with at least one argument, which specifies which operation to
26
26
# take.
27
27
 
 
28
# setup.py listmake (for developer use only)
 
29
# Recurses through the source tree and builds a list of all files which should
 
30
# be copied upon installation. This should be run by the developer before
 
31
# cutting a distribution, and the listfile it generates should be included in
 
32
# the distribution, avoiding the administrator having to run it.
 
33
 
28
34
# setup.py conf [args]
29
35
# Configures IVLE with machine-specific details, most notably, various paths.
30
36
# Either prompts the administrator for these details or accepts them as
42
48
#   (eg. python and Python libs, ld.so, etc).
43
49
# Generates .pyc files for all the IVLE .py files.
44
50
 
45
 
# setup.py listmake (for developer use only)
46
 
# Recurses through the source tree and builds a list of all files which should
47
 
# be copied upon installation. This should be run by the developer before
48
 
# cutting a distribution, and the listfile it generates should be included in
49
 
# the distribution, avoiding the administrator having to run it.
50
 
 
51
51
# setup.py install [--nojail] [--dry|n]
52
52
# (Requires root)
53
53
# Create target install directory ($target).
57
57
# Copy www/ to $target.
58
58
# Copy jail/ to jails template directory (unless --nojail specified).
59
59
 
60
 
# TODO: List in help, and handle, args for the conf operation
61
 
 
62
60
import os
 
61
import stat
 
62
import shutil
63
63
import sys
64
64
import getopt
65
65
import string
66
66
import errno
 
67
import mimetypes
 
68
import compileall
 
69
import getopt
67
70
 
68
71
# Try importing existing conf, but if we can't just set up defaults
69
72
# The reason for this is that these settings are used by other phases
71
74
# Also this allows you to hit Return to accept the existing value.
72
75
try:
73
76
    confmodule = __import__("www/conf/conf")
74
 
    root_dir = confmodule.root_dir
75
 
    ivle_install_dir = confmodule.ivle_install_dir
76
 
    jail_base = confmodule.jail_base
 
77
    try:
 
78
        root_dir = confmodule.root_dir
 
79
    except:
 
80
        root_dir = "/ivle"
 
81
    try:
 
82
        ivle_install_dir = confmodule.ivle_install_dir
 
83
    except:
 
84
        ivle_install_dir = "/opt/ivle"
 
85
    try:
 
86
        jail_base = confmodule.jail_base
 
87
    except:
 
88
        jail_base = "/home/informatics/jails"
77
89
except ImportError:
78
90
    # Just set reasonable defaults
79
91
    root_dir = "/ivle"
82
94
# Always defaults
83
95
allowed_uids = "0"
84
96
 
 
97
# Try importing install_list, but don't fail if we can't, because listmake can
 
98
# function without it.
 
99
try:
 
100
    import install_list
 
101
except:
 
102
    pass
 
103
 
 
104
# Mime types which will automatically be placed in the list by listmake.
 
105
# Note that listmake is not intended to be run by the final user (the system
 
106
# administrator who installs this), so the developers can customize the list
 
107
# as necessary, and include it in the distribution.
 
108
listmake_mimetypes = ['text/x-python', 'text/html',
 
109
    'application/x-javascript', 'application/javascript',
 
110
    'text/css', 'image/png']
 
111
 
85
112
# Main function skeleton from Guido van Rossum
86
113
# http://www.artima.com/weblogs/viewpost.jsp?thread=4829
87
114
 
88
 
class Usage(Exception):
89
 
    def __init__(self, msg):
90
 
        self.msg = msg
91
 
 
92
115
def main(argv=None):
93
116
    if argv is None:
94
117
        argv = sys.argv
112
135
        help([])
113
136
        return 1
114
137
 
 
138
    # Disallow run as root unless installing
 
139
    if operation != 'install' and os.geteuid() == 0:
 
140
        print >>sys.stderr, "I do not want to run this stage as root."
 
141
        print >>sys.stderr, "Please run as a normal user."
 
142
        return 1
115
143
    # Call the requested operation's function
116
144
    try:
117
 
        return {
 
145
        oper_func = {
118
146
            'help' : help,
119
147
            'conf' : conf,
120
148
            'build' : build,
121
149
            'listmake' : listmake,
122
150
            'install' : install,
123
 
        }[operation](argv[2:])
 
151
        }[operation]
124
152
    except KeyError:
125
153
        print >>sys.stderr, (
126
154
            """Invalid operation '%s'. Try python setup.py help."""
127
155
            % operation)
128
 
 
129
 
    try:
130
 
        try:
131
 
            opts, args = getopt.getopt(argv[1:], "h", ["help"])
132
 
        except getopt.error, msg:
133
 
            raise Usage(msg)
134
 
        # more code, unchanged
135
 
    except Usage, err:
136
 
        print >>sys.stderr, err.msg
137
 
        print >>sys.stderr, "for help use --help"
138
 
        return 2
 
156
        return 1
 
157
    return oper_func(argv[2:])
139
158
 
140
159
# Operation functions
141
160
 
144
163
        print """Usage: python setup.py operation [args]
145
164
Operation (and args) can be:
146
165
    help [operation]
 
166
    listmake (developer use only)
147
167
    conf [args]
148
168
    build
149
169
    install [--nojail] [-n|--dry]
158
178
    if operation == 'help':
159
179
        print """python setup.py help [operation]
160
180
Prints the usage message or detailed help on an operation, then exits."""
 
181
    elif operation == 'listmake':
 
182
        print """python setup.py listmake
 
183
(For developer use only)
 
184
Recurses through the source tree and builds a list of all files which should
 
185
be copied upon installation. This should be run by the developer before
 
186
cutting a distribution, and the listfile it generates should be included in
 
187
the distribution, avoiding the administrator having to run it."""
161
188
    elif operation == 'conf':
162
189
        print """python setup.py conf [args]
163
190
Configures IVLE with machine-specific details, most notably, various paths.
164
191
Either prompts the administrator for these details or accepts them as
165
 
command-line args.
 
192
command-line args. Will be interactive only if there are no arguments given.
 
193
Takes defaults from existing conf file if it exists.
 
194
 
 
195
To run IVLE out of the source directory (allowing development without having
 
196
to rebuild/install), just provide ivle_install_dir as the IVLE trunk
 
197
directory, and run build/install one time.
 
198
 
166
199
Creates www/conf/conf.py and trampoline/conf.h.
 
200
 
167
201
Args are:
 
202
    --root_dir
 
203
    --ivle_install_dir
 
204
    --jail_base
 
205
    --allowed_uids
 
206
As explained in the interactive prompt or conf.py.
168
207
"""
169
208
    elif operation == 'build':
170
 
        print """python setup.py build
 
209
        print """python -O setup.py build [--dry|-n]
171
210
Compiles all files and sets up a jail template in the source directory.
 
211
-O is recommended to cause compilation to be optimised.
172
212
Details:
173
213
Compiles (GCC) trampoline/trampoline.c to trampoline/trampoline.
174
214
Creates jail/.
176
216
Copies console/ to a location within the jail.
177
217
Copies OS programs and files to corresponding locations within the jail
178
218
  (eg. python and Python libs, ld.so, etc).
179
 
Generates .pyc files for all the IVLE .py files."""
180
 
    elif operation == 'listmake':
181
 
        print """python setup.py listmake
182
 
(For developer use only)
183
 
Recurses through the source tree and builds a list of all files which should
184
 
be copied upon installation. This should be run by the developer before
185
 
cutting a distribution, and the listfile it generates should be included in
186
 
the distribution, avoiding the administrator having to run it."""
 
219
Generates .pyc or .pyo files for all the IVLE .py files.
 
220
 
 
221
--dry | -n  Print out the actions but don't do anything."""
187
222
    elif operation == 'install':
188
223
        print """sudo python setup.py install [--nojail] [--dry|-n]
189
224
(Requires root)
210
245
    # (since listmake is typically run before conf)
211
246
    if "www/conf/conf.py" not in list_www:
212
247
        list_www.append("www/conf/conf.py")
 
248
    # Make sure that console/python-console is in the list
 
249
    if "console/python-console" not in list_console:
 
250
        list_console.append("console/python-console")
213
251
    # Write these out to a file
214
252
    cwd = os.getcwd()
215
253
    # the files that will be created/overwritten
220
258
 
221
259
        file.write("""# IVLE Configuration File
222
260
# install_list.py
223
 
# Provides lists of all Python files to be installed by `setup.py install'.
 
261
# Provides lists of all files to be installed by `setup.py install' from
 
262
# certain directories.
 
263
# Note that any files with the given filename plus 'c' or 'o' (that is,
 
264
# compiled .pyc or .pyo files) will be copied as well.
224
265
 
225
 
# List of all installable Python files in www directory.
 
266
# List of all installable files in www directory.
226
267
list_www = """)
227
268
        writelist_pretty(file, list_www)
228
269
        file.write("""
229
 
# List of all installable Python files in console directory.
 
270
# List of all installable files in console directory.
230
271
list_console = """)
231
272
        writelist_pretty(file, list_console)
232
273
 
254
295
        filter_mutate(lambda x: x[0] != '.', dirnames)
255
296
        # All *.py files are added to the list
256
297
        pylist += [os.path.join(dirpath, item) for item in filenames
257
 
            if item.endswith('.py')]
 
298
            if mimetypes.guess_type(item)[0] in listmake_mimetypes]
258
299
    return pylist
259
300
 
260
301
def writelist_pretty(file, list):
276
317
    conffile = os.path.join(cwd, "www/conf/conf.py")
277
318
    conf_hfile = os.path.join(cwd, "trampoline/conf.h")
278
319
 
279
 
    # Fixed config options that we don't ask the admin
280
 
 
281
 
    default_app = "dummy"
282
 
 
283
 
    print """This tool will create the following files:
 
320
    # Get command-line arguments to avoid asking questions.
 
321
 
 
322
    (opts, args) = getopt.gnu_getopt(args, "", ['root_dir=',
 
323
                    'ivle_install_dir=', 'jail_base=', 'allowed_uids='])
 
324
 
 
325
    if args != []:
 
326
        print >>sys.stderr, "Invalid arguments:", string.join(args, ' ')
 
327
        return 2
 
328
 
 
329
    if opts == []:
 
330
        # Interactive mode. Prompt the user for all the values.
 
331
 
 
332
        print """This tool will create the following files:
284
333
    %s
285
334
    %s
286
335
prompting you for details about your configuration. The file will be
289
338
Please hit Ctrl+C now if you do not wish to do this.
290
339
""" % (conffile, conf_hfile)
291
340
 
292
 
    # Get information from the administrator
293
 
    # If EOF is encountered at any time during the questioning, just exit
294
 
    # silently
 
341
        # Get information from the administrator
 
342
        # If EOF is encountered at any time during the questioning, just exit
 
343
        # silently
295
344
 
296
 
    root_dir = query_user(root_dir,
297
 
    """Root directory where IVLE is located (in URL space):""")
298
 
    ivle_install_dir = query_user(ivle_install_dir,
299
 
    'Root directory where IVLE will be installed (on the local file '
300
 
    'system):')
301
 
    jail_base = query_user(jail_base,
302
 
    """Root directory where the jails (containing user files) are stored
 
345
        root_dir = query_user(root_dir,
 
346
        """Root directory where IVLE is located (in URL space):""")
 
347
        ivle_install_dir = query_user(ivle_install_dir,
 
348
        'Root directory where IVLE will be installed (on the local file '
 
349
        'system):')
 
350
        jail_base = query_user(jail_base,
 
351
        """Root directory where the jails (containing user files) are stored
303
352
(on the local file system):""")
304
 
    allowed_uids = query_user(allowed_uids,
305
 
    """UID of the web server process which will run IVLE.
 
353
        allowed_uids = query_user(allowed_uids,
 
354
        """UID of the web server process which will run IVLE.
306
355
Only this user may execute the trampoline. May specify multiple users as
307
356
a comma-separated list.
308
357
    (eg. "1002,78")""")
309
358
 
 
359
    else:
 
360
        opts = dict(opts)
 
361
        # Non-interactive mode. Parse the options.
 
362
        if '--root_dir' in opts:
 
363
            root_dir = opts['--root_dir']
 
364
        if '--ivle_install_dir' in opts:
 
365
            ivle_install_dir = opts['--ivle_install_dir']
 
366
        if '--jail_base' in opts:
 
367
            jail_base = opts['--jail_base']
 
368
        if '--allowed_uids' in opts:
 
369
            allowed_uids = opts['--allowed_uids']
 
370
 
310
371
    # Error handling on input values
311
 
 
312
372
    try:
313
373
        allowed_uids = map(int, allowed_uids.split(','))
314
374
    except ValueError:
340
400
# The user jails are expected to be located immediately in subdirectories of
341
401
# this location.
342
402
jail_base = "%s"
343
 
 
344
 
# Which application to load by default (if the user navigates to the top level
345
 
# of the site). This is the app's URL name.
346
 
# Note that if this app requires authentication, the user will first be
347
 
# presented with the login screen.
348
 
default_app = "%s"
349
 
""" % (root_dir, ivle_install_dir, jail_base, default_app))
 
403
""" % (root_dir, ivle_install_dir, jail_base))
350
404
 
351
405
        conf.close()
352
406
    except IOError, (errno, strerror):
395
449
    return 0
396
450
 
397
451
def build(args):
398
 
    dry = False     # Set to True later if --dry
 
452
    # Get "dry" variable from command line
 
453
    (opts, args) = getopt.gnu_getopt(args, "n", ['dry'])
 
454
    opts = dict(opts)
 
455
    dry = '-n' in opts or '--dry' in opts
 
456
 
 
457
    if dry:
 
458
        print "Dry run (no actions will be executed\n"
399
459
 
400
460
    # Compile the trampoline
401
461
    action_runprog('gcc', ['-Wall', '-o', 'trampoline/trampoline',
402
462
        'trampoline/trampoline.c'], dry)
403
463
 
404
464
    # Create the jail and its subdirectories
405
 
    action_mkdir('jail')
406
 
    action_mkdir('jail/bin')
407
 
    action_mkdir('jail/lib')
408
 
    action_mkdir('jail/usr/bin')
409
 
    action_mkdir('jail/usr/lib')
410
 
    action_mkdir('jail/opt/ivle')
411
 
    action_mkdir('jail/home')
412
 
    action_mkdir('jail/tmp')
413
 
 
414
 
    # TODO: Copy console into the jail
415
 
    # TODO: Copy operating system files into the jail
416
 
    # TODO: Compile .py files into .pyc files
 
465
    # Note: Other subdirs will be made by copying files
 
466
    action_mkdir('jail', dry)
 
467
    action_mkdir('jail/home', dry)
 
468
    action_mkdir('jail/tmp', dry)
 
469
 
 
470
    # Copy all console and operating system files into the jail
 
471
    action_copylist(install_list.list_console, 'jail/opt/ivle', dry)
 
472
    copy_os_files_jail(dry)
 
473
 
 
474
    # Compile .py files into .pyc or .pyo files
 
475
    compileall.compile_dir('www', quiet=True)
 
476
    compileall.compile_dir('console', quiet=True)
417
477
 
418
478
    return 0
419
479
 
 
480
def copy_os_files_jail(dry):
 
481
    """Copies necessary Operating System files from their usual locations
 
482
    into the jail/ directory of the cwd."""
 
483
    # Currently source paths are configured for Ubuntu.
 
484
    copy_file_to_jail('/lib/ld-linux.so.2', dry)
 
485
    copy_file_to_jail('/lib/tls/i686/cmov/libc.so.6', dry)
 
486
    copy_file_to_jail('/lib/tls/i686/cmov/libdl.so.2', dry)
 
487
    copy_file_to_jail('/lib/tls/i686/cmov/libm.so.6', dry)
 
488
    copy_file_to_jail('/lib/tls/i686/cmov/libpthread.so.0', dry)
 
489
    copy_file_to_jail('/lib/tls/i686/cmov/libutil.so.1', dry)
 
490
    copy_file_to_jail('/usr/bin/python2.5', dry)
 
491
    action_symlink('python2.5', 'jail/usr/bin/python', dry)
 
492
    action_copytree('/usr/lib/python2.5', 'jail/usr/lib/python2.5', dry)
 
493
 
 
494
def copy_file_to_jail(src, dry):
 
495
    """Copies a single file from an absolute location into the same location
 
496
    within the jail. src must begin with a '/'. The jail will be located
 
497
    in a 'jail' subdirectory of the current path."""
 
498
    action_copyfile(src, 'jail' + src, dry)
 
499
 
420
500
def install(args):
421
 
    print "Install"
 
501
    # Get "dry" and "nojail" variables from command line
 
502
    (opts, args) = getopt.gnu_getopt(args, "n", ['dry', 'nojail'])
 
503
    opts = dict(opts)
 
504
    dry = '-n' in opts or '--dry' in opts
 
505
    nojail = '--nojail' in opts
 
506
 
 
507
    if dry:
 
508
        print "Dry run (no actions will be executed\n"
 
509
 
 
510
    if not dry and os.geteuid() != 0:
 
511
        print >>sys.stderr, "Must be root to run install"
 
512
        print >>sys.stderr, "(I need to chown some files)."
 
513
        return 1
 
514
 
 
515
    # Create the target (install) directory
 
516
    action_mkdir(ivle_install_dir, dry)
 
517
 
 
518
    # Create bin and copy the compiled files there
 
519
    action_mkdir(os.path.join(ivle_install_dir, 'bin'), dry)
 
520
    tramppath = os.path.join(ivle_install_dir, 'bin/trampoline')
 
521
    action_copyfile('trampoline/trampoline', tramppath, dry)
 
522
    # chown trampoline to root and set setuid bit
 
523
    action_chown_setuid(tramppath, dry)
 
524
 
 
525
    # Copy the www directory using the list
 
526
    action_copylist(install_list.list_www, ivle_install_dir, dry)
 
527
 
 
528
    if not nojail:
 
529
        # Copy the local jail directory built by the build action
 
530
        # to the jails template directory (it will be used as a template
 
531
        # for all the students' jails).
 
532
        action_copytree('jail', os.path.join(jail_base, 'template'), dry)
 
533
 
422
534
    return 0
423
535
 
424
536
# The actions call Python os functions but print actions and handle dryness.
450
562
    if ret != 0:
451
563
        raise RunError(prog, ret)
452
564
 
453
 
def action_mkdir(path):
 
565
def action_mkdir(path, dry):
454
566
    """Calls mkdir. Silently ignored if the directory already exists.
455
567
    Creates all parent directories as necessary."""
456
568
    print "mkdir -p", path
461
573
        if err != errno.EEXIST:
462
574
            raise
463
575
 
 
576
def action_copytree(src, dst, dry):
 
577
    """Copies an entire directory tree. Symlinks are seen as normal files and
 
578
    copies of the entire file (not the link) are made. Creates all parent
 
579
    directories as necessary.
 
580
 
 
581
    See shutil.copytree."""
 
582
    if os.access(dst, os.F_OK):
 
583
        print "rm -r", dst
 
584
        if not dry:
 
585
            shutil.rmtree(dst, True)
 
586
    print "cp -r", src, dst
 
587
    if dry: return
 
588
    shutil.copytree(src, dst, True)
 
589
 
 
590
def action_copylist(srclist, dst, dry):
 
591
    """Copies all files in a list to a new location. The files in the list
 
592
    are read relative to the current directory, and their destinations are the
 
593
    same paths relative to dst. Creates all parent directories as necessary.
 
594
    """
 
595
    for srcfile in srclist:
 
596
        dstfile = os.path.join(dst, srcfile)
 
597
        dstdir = os.path.split(dstfile)[0]
 
598
        if not os.path.isdir(dstdir):
 
599
            action_mkdir(dstdir, dry)
 
600
        print "cp -f", srcfile, dstfile
 
601
        if not dry:
 
602
            try:
 
603
                shutil.copyfile(srcfile, dstfile)
 
604
                shutil.copymode(srcfile, dstfile)
 
605
            except shutil.Error:
 
606
                pass
 
607
 
 
608
def action_copyfile(src, dst, dry):
 
609
    """Copies one file to a new location. Creates all parent directories
 
610
    as necessary.
 
611
    """
 
612
    dstdir = os.path.split(dst)[0]
 
613
    if not os.path.isdir(dstdir):
 
614
        action_mkdir(dstdir, dry)
 
615
    print "cp -f", src, dst
 
616
    if not dry:
 
617
        try:
 
618
            shutil.copyfile(src, dst)
 
619
            shutil.copymode(src, dst)
 
620
        except shutil.Error:
 
621
            pass
 
622
 
 
623
def action_symlink(src, dst, dry):
 
624
    """Creates a symlink in a given location. Creates all parent directories
 
625
    as necessary.
 
626
    """
 
627
    dstdir = os.path.split(dst)[0]
 
628
    if not os.path.isdir(dstdir):
 
629
        action_mkdir(dstdir, dry)
 
630
    # Delete existing file
 
631
    if os.path.exists(dst):
 
632
        os.remove(dst)
 
633
    print "ln -fs", src, dst
 
634
    if not dry:
 
635
        os.symlink(src, dst)
 
636
 
 
637
def action_chown_setuid(file, dry):
 
638
    """Chowns a file to root, and sets the setuid bit on the file.
 
639
    Calling this function requires the euid to be root.
 
640
    The actual mode of path is set to: rws--s--s
 
641
    """
 
642
    print "chown root:root", file
 
643
    if not dry:
 
644
        os.chown(file, 0, 0)
 
645
    print "chmod a+xs", file
 
646
    print "chmod u+rw", file
 
647
    if not dry:
 
648
        os.chmod(file, stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
 
649
            | stat.S_ISUID | stat.S_IRUSR | stat.S_IWUSR)
 
650
 
464
651
def query_user(default, prompt):
465
652
    """Prompts the user for a string, which is read from a line of stdin.
466
653
    Exits silently if EOF is encountered. Returns the string, with spaces