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

97 by mattgiuca
Moved template.py and setup.py to better places.
1
#!/usr/bin/env python
2
# IVLE - Informatics Virtual Learning Environment
3
# Copyright (C) 2007-2008 The University of Melbourne
4
#
5
# This program is free software; you can redistribute it and/or modify
6
# it under the terms of the GNU General Public License as published by
7
# the Free Software Foundation; either version 2 of the License, or
8
# (at your option) any later version.
9
#
10
# This program is distributed in the hope that it will be useful,
11
# but WITHOUT ANY WARRANTY; without even the implied warranty of
12
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
# GNU General Public License for more details.
14
#
15
# You should have received a copy of the GNU General Public License
16
# along with this program; if not, write to the Free Software
17
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18
19
# Module: setup
20
# Author: Matt Giuca
21
# Date:   12/12/2007
22
23
# This is a command-line application, for use by the administrator.
104 by mattgiuca
setup.py: Replaced the simple script with a full options processing script
24
# This program configures, builds and installs IVLE in three separate steps.
25
# It is called with at least one argument, which specifies which operation to
26
# take.
27
118 by mattgiuca
setup.py: Added copytree and copylist actions.
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
316 by drtomc
setup.py - name the configuration command "config" to bring it into line with
34
# setup.py config [args]
104 by mattgiuca
setup.py: Replaced the simple script with a full options processing script
35
# Configures IVLE with machine-specific details, most notably, various paths.
36
# Either prompts the administrator for these details or accepts them as
37
# command-line args.
38
# Creates www/conf/conf.py and trampoline/conf.h.
39
40
# setup.py build
41
# Compiles all files and sets up a jail template in the source directory.
42
# Details:
43
# Compiles (GCC) trampoline/trampoline.c to trampoline/trampoline.
44
# Creates jail/.
45
# Creates standard subdirs inside the jail, eg bin, opt, home, tmp.
46
# Copies console/ to a location within the jail.
47
# Copies OS programs and files to corresponding locations within the jail
48
#   (eg. python and Python libs, ld.so, etc).
49
# Generates .pyc files for all the IVLE .py files.
50
51
# setup.py install [--nojail] [--dry|n]
52
# (Requires root)
53
# Create target install directory ($target).
54
# Create $target/bin.
55
# Copy trampoline/trampoline to $target/bin.
56
# chown and chmod the installed trampoline.
57
# Copy www/ to $target.
58
# Copy jail/ to jails template directory (unless --nojail specified).
97 by mattgiuca
Moved template.py and setup.py to better places.
59
60
import os
119 by mattgiuca
setup.py: Added install action. Completely works!
61
import stat
118 by mattgiuca
setup.py: Added copytree and copylist actions.
62
import shutil
97 by mattgiuca
Moved template.py and setup.py to better places.
63
import sys
104 by mattgiuca
setup.py: Replaced the simple script with a full options processing script
64
import getopt
107 by mattgiuca
setup.py: Added "action" functions which encapsulate calling OS functions.
65
import string
66
import errno
117 by mattgiuca
setup.py: listmake uses mimetypes to select all files with certain mime types,
67
import mimetypes
116 by mattgiuca
setup.py: mkdir now properly obeys "dry".
68
import compileall
120 by mattgiuca
setup.py: Added command-line argument mode for conf. This completely works!
69
import getopt
104 by mattgiuca
setup.py: Replaced the simple script with a full options processing script
70
252 by mattgiuca
setup.py: Added action "updatejails" which wipes all student jails, replacing
71
# Import modules from the website is tricky since they're in the www
72
# directory.
73
sys.path.append(os.path.join(os.getcwd(), 'www'))
74
import conf
75
import common.makeuser
76
317 by mattgiuca
doc/dependencies: Added dependency on matplotlib.
77
# Determine which Python version (2.4 or 2.5, for example) we are running,
78
# and use that as the filename to the Python directory.
79
# Just get the first 3 characters of sys.version.
80
PYTHON_VERSION = sys.version[0:3]
81
251 by mattgiuca
setup.py: Worked the "files to copy into jail" out into a separate list
82
# Operating system files to copy over into the jail.
83
# These will be copied from the given place on the OS file system into the
84
# same place within the jail.
85
JAIL_FILES = [
86
    '/lib/ld-linux.so.2',
87
    '/lib/tls/i686/cmov/libc.so.6',
88
    '/lib/tls/i686/cmov/libdl.so.2',
89
    '/lib/tls/i686/cmov/libm.so.6',
90
    '/lib/tls/i686/cmov/libpthread.so.0',
91
    '/lib/tls/i686/cmov/libutil.so.1',
253 by mattgiuca
setup.py: chmods python-console when building.
92
    '/etc/ld.so.conf',
93
    '/etc/ld.so.cache',
94
    # These 2 files do not exist in Ubuntu
95
    #'/etc/ld.so.preload',
96
    #'/etc/ld.so.nohwcap',
97
    # UNIX commands
98
    '/usr/bin/strace',
99
    '/bin/ls',
100
    '/bin/echo',
101
    # Needed by python
317 by mattgiuca
doc/dependencies: Added dependency on matplotlib.
102
    '/usr/bin/python%s' % PYTHON_VERSION,
253 by mattgiuca
setup.py: chmods python-console when building.
103
    # Needed by matplotlib
104
    '/usr/lib/i686/cmov/libssl.so.0.9.8',
105
    '/usr/lib/i686/cmov/libcrypto.so.0.9.8',
106
    '/lib/tls/i686/cmov/libnsl.so.1',
107
    '/usr/lib/libz.so.1',
108
    '/usr/lib/atlas/liblapack.so.3',
109
    '/usr/lib/atlas/libblas.so.3',
110
    '/usr/lib/libg2c.so.0',
111
    '/usr/lib/libstdc++.so.6',
112
    '/usr/lib/libfreetype.so.6',
113
    '/usr/lib/libpng12.so.0',
114
    '/usr/lib/libBLT.2.4.so.8.4',
115
    '/usr/lib/libtk8.4.so.0',
116
    '/usr/lib/libtcl8.4.so.0',
117
    '/usr/lib/tcl8.4/init.tcl',
118
    '/usr/lib/libX11.so.6',
119
    '/usr/lib/libXau.so.6',
120
    '/usr/lib/libXdmcp.so.6',
121
    '/lib/libgcc_s.so.1',
122
    '/etc/matplotlibrc',
251 by mattgiuca
setup.py: Worked the "files to copy into jail" out into a separate list
123
]
124
# Symlinks to make within the jail. Src mapped to dst.
125
JAIL_LINKS = {
317 by mattgiuca
doc/dependencies: Added dependency on matplotlib.
126
    'python%s' % PYTHON_VERSION: 'jail/usr/bin/python',
251 by mattgiuca
setup.py: Worked the "files to copy into jail" out into a separate list
127
}
128
# Trees to copy. Src mapped to dst (these will be passed to action_copytree).
129
JAIL_COPYTREES = {
317 by mattgiuca
doc/dependencies: Added dependency on matplotlib.
130
    '/usr/lib/python%s' % PYTHON_VERSION:
131
        'jail/usr/lib/python%s' % PYTHON_VERSION,
253 by mattgiuca
setup.py: chmods python-console when building.
132
    '/usr/share/matplotlib': 'jail/usr/share/matplotlib',
133
    '/etc/ld.so.conf.d': 'jail/etc/ld.so.conf.d',
251 by mattgiuca
setup.py: Worked the "files to copy into jail" out into a separate list
134
}
135
358 by mattgiuca
setup.py: Gutted out the config options code. It was getting so there were
136
class ConfigOption:
137
    """A configuration option; one of the things written to conf.py."""
138
    def __init__(self, option_name, default, prompt, comment):
139
        """Creates a configuration option.
140
        option_name: Name of the variable in conf.py. Also name of the
141
            command-line argument to setup.py conf.
142
        default: Default value for this variable.
143
        prompt: (Short) string presented during the interactive prompt in
144
            setup.py conf.
145
        comment: (Long) comment string stored in conf.py. Each line of this
146
            string should begin with a '#'.
147
        """
148
        self.option_name = option_name
149
        self.default = default
150
        self.prompt = prompt
151
        self.comment = comment
152
153
# Configuration options, defaults and descriptions
154
config_options = []
155
config_options.append(ConfigOption("root_dir", "/ivle",
156
    """Root directory where IVLE is located (in URL space):""",
157
    """
158
# In URL space, where in the site is IVLE located. (All URLs will be prefixed
159
# with this).
160
# eg. "/" or "/ivle"."""))
161
config_options.append(ConfigOption("ivle_install_dir", "/opt/ivle",
162
    'Root directory where IVLE will be installed (on the local file '
163
    'system):',
164
    """
165
# In the local file system, where IVLE is actually installed.
166
# This directory should contain the "www" and "bin" directories."""))
167
config_options.append(ConfigOption("jail_base", "/home/informatics/jails",
168
    """Root directory where the jails (containing user files) are stored
169
(on the local file system):""",
170
    """
171
# In the local file system, where are the student/user file spaces located.
172
# The user jails are expected to be located immediately in subdirectories of
173
# this location."""))
174
config_options.append(ConfigOption("subjects_base",
175
    "/home/informatics/subjects",
176
    """Root directory where the subject directories (containing worksheets
177
and other per-subject files) are stored (on the local file system):""",
178
    """
179
# In the local file system, where are the per-subject file spaces located.
180
# The individual subject directories are expected to be located immediately
181
# in subdirectories of this location."""))
182
config_options.append(ConfigOption("public_host", "public.localhost",
183
    """Hostname which will cause the server to go into "public mode",
184
providing login-free access to student's published work:""",
185
    """
186
# The server goes into "public mode" if the browser sends a request with this
187
# host. This is for security reasons - we only serve public student files on a
188
# separate domain to the main IVLE site.
189
# Public mode does not use cookies, and serves only public content.
190
# Private mode (normal mode) requires login, and only serves files relevant to
191
# the logged-in user."""))
192
config_options.append(ConfigOption("allowed_uids", "33",
193
    """UID of the web server process which will run IVLE.
194
Only this user may execute the trampoline. May specify multiple users as
195
a comma-separated list.
196
    (eg. "1002,78")""",
197
    """
198
# The User-ID of the web server process which will run IVLE, and any other
199
# users who are allowed to run the trampoline. This is stores as a string of
200
# comma-separated integers, simply because it is not used within Python, only
201
# used by the setup program to write to conf.h (see setup.py config)."""))
359 by mattgiuca
setup.py: Added config options for database settings.
202
config_options.append(ConfigOption("db_host", "localhost",
203
    """PostgreSQL Database config
204
==========================
205
Hostname of the DB server:""",
206
    """
207
### PostgreSQL Database config ###
208
# Database server hostname"""))
209
config_options.append(ConfigOption("db_port", "5432",
210
    """Port of the DB server:""",
211
    """
212
# Database server port"""))
363 by mattgiuca
setup.py: Added new config option - "database name"
213
config_options.append(ConfigOption("db_dbname", "ivle",
214
    """Database name:""",
215
    """
216
# Database name"""))
359 by mattgiuca
setup.py: Added config options for database settings.
217
config_options.append(ConfigOption("db_user", "postgres",
218
    """Username for DB server login:""",
219
    """
220
# Database username"""))
221
config_options.append(ConfigOption("db_password", "",
222
    """Password for DB server login:
223
    (Caution: This password is stored in plaintext in www/conf/conf.py)""",
224
    """
225
# Database password"""))
358 by mattgiuca
setup.py: Gutted out the config options code. It was getting so there were
226
113 by mattgiuca
setup.py: Now loads defaults, if possible, from the existing conf.py.
227
# Try importing existing conf, but if we can't just set up defaults
228
# The reason for this is that these settings are used by other phases
229
# of setup besides conf, so we need to know them.
230
# Also this allows you to hit Return to accept the existing value.
231
try:
232
    confmodule = __import__("www/conf/conf")
358 by mattgiuca
setup.py: Gutted out the config options code. It was getting so there were
233
    for opt in config_options:
234
        try:
235
            globals()[opt.option_name] = confmodule.__dict__[opt.option_name]
236
        except:
237
            globals()[opt.option_name] = opt.default
113 by mattgiuca
setup.py: Now loads defaults, if possible, from the existing conf.py.
238
except ImportError:
239
    # Just set reasonable defaults
358 by mattgiuca
setup.py: Gutted out the config options code. It was getting so there were
240
    for opt in config_options:
241
        globals()[opt.option_name] = opt.default
113 by mattgiuca
setup.py: Now loads defaults, if possible, from the existing conf.py.
242
118 by mattgiuca
setup.py: Added copytree and copylist actions.
243
# Try importing install_list, but don't fail if we can't, because listmake can
244
# function without it.
245
try:
246
    import install_list
247
except:
248
    pass
249
117 by mattgiuca
setup.py: listmake uses mimetypes to select all files with certain mime types,
250
# Mime types which will automatically be placed in the list by listmake.
251
# Note that listmake is not intended to be run by the final user (the system
252
# administrator who installs this), so the developers can customize the list
253
# as necessary, and include it in the distribution.
254
listmake_mimetypes = ['text/x-python', 'text/html',
255
    'application/x-javascript', 'application/javascript',
313 by mattgiuca
test/test_framework: Updated examples, a bit of better descriptions, sample
256
    'text/css', 'image/png', 'application/xml']
117 by mattgiuca
setup.py: listmake uses mimetypes to select all files with certain mime types,
257
104 by mattgiuca
setup.py: Replaced the simple script with a full options processing script
258
# Main function skeleton from Guido van Rossum
259
# http://www.artima.com/weblogs/viewpost.jsp?thread=4829
260
261
def main(argv=None):
262
    if argv is None:
263
        argv = sys.argv
264
265
    # Print the opening spiel including the GPL notice
266
267
    print """IVLE - Informatics Virtual Learning Environment Setup
97 by mattgiuca
Moved template.py and setup.py to better places.
268
Copyright (C) 2007-2008 The University of Melbourne
269
IVLE comes with ABSOLUTELY NO WARRANTY.
270
This is free software, and you are welcome to redistribute it
271
under certain conditions. See LICENSE.txt for details.
104 by mattgiuca
setup.py: Replaced the simple script with a full options processing script
272
273
IVLE Setup
274
"""
275
276
    # First argument is the name of the setup operation
277
    try:
278
        operation = argv[1]
279
    except IndexError:
280
        # Print usage message and exit
281
        help([])
282
        return 1
283
131 by mattgiuca
setup.py:
284
    # Disallow run as root unless installing
252 by mattgiuca
setup.py: Added action "updatejails" which wipes all student jails, replacing
285
    if (operation != 'install' and operation != 'updatejails'
286
        and os.geteuid() == 0):
131 by mattgiuca
setup.py:
287
        print >>sys.stderr, "I do not want to run this stage as root."
288
        print >>sys.stderr, "Please run as a normal user."
289
        return 1
104 by mattgiuca
setup.py: Replaced the simple script with a full options processing script
290
    # Call the requested operation's function
291
    try:
120 by mattgiuca
setup.py: Added command-line argument mode for conf. This completely works!
292
        oper_func = {
104 by mattgiuca
setup.py: Replaced the simple script with a full options processing script
293
            'help' : help,
316 by drtomc
setup.py - name the configuration command "config" to bring it into line with
294
            'config' : conf,
104 by mattgiuca
setup.py: Replaced the simple script with a full options processing script
295
            'build' : build,
296
            'listmake' : listmake,
297
            'install' : install,
252 by mattgiuca
setup.py: Added action "updatejails" which wipes all student jails, replacing
298
            'updatejails' : updatejails,
120 by mattgiuca
setup.py: Added command-line argument mode for conf. This completely works!
299
        }[operation]
104 by mattgiuca
setup.py: Replaced the simple script with a full options processing script
300
    except KeyError:
301
        print >>sys.stderr, (
302
            """Invalid operation '%s'. Try python setup.py help."""
303
            % operation)
129 by mattgiuca
setup.py: Minor fix, exits cleanly if arguments are invalid.
304
        return 1
120 by mattgiuca
setup.py: Added command-line argument mode for conf. This completely works!
305
    return oper_func(argv[2:])
104 by mattgiuca
setup.py: Replaced the simple script with a full options processing script
306
307
# Operation functions
308
309
def help(args):
310
    if args == []:
311
        print """Usage: python setup.py operation [args]
312
Operation (and args) can be:
313
    help [operation]
131 by mattgiuca
setup.py:
314
    listmake (developer use only)
316 by drtomc
setup.py - name the configuration command "config" to bring it into line with
315
    config [args]
104 by mattgiuca
setup.py: Replaced the simple script with a full options processing script
316
    build
313 by mattgiuca
test/test_framework: Updated examples, a bit of better descriptions, sample
317
    install [--nojail] [--nosubjects] [-n|--dry]
104 by mattgiuca
setup.py: Replaced the simple script with a full options processing script
318
"""
319
        return 1
320
    elif len(args) != 1:
321
        print """Usage: python setup.py help [operation]"""
322
        return 2
323
    else:
324
        operation = args[0]
325
326
    if operation == 'help':
327
        print """python setup.py help [operation]
328
Prints the usage message or detailed help on an operation, then exits."""
118 by mattgiuca
setup.py: Added copytree and copylist actions.
329
    elif operation == 'listmake':
330
        print """python setup.py listmake
331
(For developer use only)
332
Recurses through the source tree and builds a list of all files which should
333
be copied upon installation. This should be run by the developer before
334
cutting a distribution, and the listfile it generates should be included in
335
the distribution, avoiding the administrator having to run it."""
316 by drtomc
setup.py - name the configuration command "config" to bring it into line with
336
    elif operation == 'config':
337
        print """python setup.py config [args]
104 by mattgiuca
setup.py: Replaced the simple script with a full options processing script
338
Configures IVLE with machine-specific details, most notably, various paths.
339
Either prompts the administrator for these details or accepts them as
120 by mattgiuca
setup.py: Added command-line argument mode for conf. This completely works!
340
command-line args. Will be interactive only if there are no arguments given.
341
Takes defaults from existing conf file if it exists.
133 by mattgiuca
setup.py: Now allows IVLE to be installed over itself, in order to allow
342
343
To run IVLE out of the source directory (allowing development without having
344
to rebuild/install), just provide ivle_install_dir as the IVLE trunk
345
directory, and run build/install one time.
346
104 by mattgiuca
setup.py: Replaced the simple script with a full options processing script
347
Creates www/conf/conf.py and trampoline/conf.h.
133 by mattgiuca
setup.py: Now allows IVLE to be installed over itself, in order to allow
348
358 by mattgiuca
setup.py: Gutted out the config options code. It was getting so there were
349
Args are:"""
350
        for opt in config_options:
351
            print "    --" + opt.option_name
352
        print """As explained in the interactive prompt or conf.py.
104 by mattgiuca
setup.py: Replaced the simple script with a full options processing script
353
"""
354
    elif operation == 'build':
118 by mattgiuca
setup.py: Added copytree and copylist actions.
355
        print """python -O setup.py build [--dry|-n]
104 by mattgiuca
setup.py: Replaced the simple script with a full options processing script
356
Compiles all files and sets up a jail template in the source directory.
116 by mattgiuca
setup.py: mkdir now properly obeys "dry".
357
-O is recommended to cause compilation to be optimised.
104 by mattgiuca
setup.py: Replaced the simple script with a full options processing script
358
Details:
359
Compiles (GCC) trampoline/trampoline.c to trampoline/trampoline.
360
Creates jail/.
361
Creates standard subdirs inside the jail, eg bin, opt, home, tmp.
362
Copies console/ to a location within the jail.
363
Copies OS programs and files to corresponding locations within the jail
364
  (eg. python and Python libs, ld.so, etc).
118 by mattgiuca
setup.py: Added copytree and copylist actions.
365
Generates .pyc or .pyo files for all the IVLE .py files.
366
367
--dry | -n  Print out the actions but don't do anything."""
104 by mattgiuca
setup.py: Replaced the simple script with a full options processing script
368
    elif operation == 'install':
313 by mattgiuca
test/test_framework: Updated examples, a bit of better descriptions, sample
369
        print """sudo python setup.py install [--nojail] [--nosubjects][--dry|-n]
104 by mattgiuca
setup.py: Replaced the simple script with a full options processing script
370
(Requires root)
371
Create target install directory ($target).
372
Create $target/bin.
373
Copy trampoline/trampoline to $target/bin.
374
chown and chmod the installed trampoline.
375
Copy www/ to $target.
376
Copy jail/ to jails template directory (unless --nojail specified).
313 by mattgiuca
test/test_framework: Updated examples, a bit of better descriptions, sample
377
Copy subjects/ to subjects directory (unless --nosubjects specified).
104 by mattgiuca
setup.py: Replaced the simple script with a full options processing script
378
313 by mattgiuca
test/test_framework: Updated examples, a bit of better descriptions, sample
379
--nojail        Do not copy the jail.
380
--nosubjects    Do not copy the subjects.
104 by mattgiuca
setup.py: Replaced the simple script with a full options processing script
381
--dry | -n  Print out the actions but don't do anything."""
252 by mattgiuca
setup.py: Added action "updatejails" which wipes all student jails, replacing
382
    elif operation == 'updatejails':
383
        print """sudo python setup.py updatejails [--dry|-n]
384
(Requires root)
385
Copy jail/ to each subdirectory in jails directory.
386
387
--dry | -n  Print out the actions but don't do anything."""
104 by mattgiuca
setup.py: Replaced the simple script with a full options processing script
388
    else:
389
        print >>sys.stderr, (
390
            """Invalid operation '%s'. Try python setup.py help."""
391
            % operation)
392
    return 1
393
114 by mattgiuca
setup.py: Wrote listmake (and ancillary functions).
394
def listmake(args):
395
    # We build two separate lists, by walking www and console
396
    list_www = build_list_py_files('www')
397
    list_console = build_list_py_files('console')
313 by mattgiuca
test/test_framework: Updated examples, a bit of better descriptions, sample
398
    list_subjects = build_list_py_files('subjects', no_top_level=True)
114 by mattgiuca
setup.py: Wrote listmake (and ancillary functions).
399
    # Make sure that the files generated by conf are in the list
400
    # (since listmake is typically run before conf)
401
    if "www/conf/conf.py" not in list_www:
402
        list_www.append("www/conf/conf.py")
117 by mattgiuca
setup.py: listmake uses mimetypes to select all files with certain mime types,
403
    # Make sure that console/python-console is in the list
404
    if "console/python-console" not in list_console:
405
        list_console.append("console/python-console")
114 by mattgiuca
setup.py: Wrote listmake (and ancillary functions).
406
    # Write these out to a file
407
    cwd = os.getcwd()
408
    # the files that will be created/overwritten
409
    listfile = os.path.join(cwd, "install_list.py")
410
411
    try:
412
        file = open(listfile, "w")
413
414
        file.write("""# IVLE Configuration File
415
# install_list.py
117 by mattgiuca
setup.py: listmake uses mimetypes to select all files with certain mime types,
416
# Provides lists of all files to be installed by `setup.py install' from
417
# certain directories.
116 by mattgiuca
setup.py: mkdir now properly obeys "dry".
418
# Note that any files with the given filename plus 'c' or 'o' (that is,
419
# compiled .pyc or .pyo files) will be copied as well.
114 by mattgiuca
setup.py: Wrote listmake (and ancillary functions).
420
117 by mattgiuca
setup.py: listmake uses mimetypes to select all files with certain mime types,
421
# List of all installable files in www directory.
114 by mattgiuca
setup.py: Wrote listmake (and ancillary functions).
422
list_www = """)
423
        writelist_pretty(file, list_www)
424
        file.write("""
117 by mattgiuca
setup.py: listmake uses mimetypes to select all files with certain mime types,
425
# List of all installable files in console directory.
114 by mattgiuca
setup.py: Wrote listmake (and ancillary functions).
426
list_console = """)
427
        writelist_pretty(file, list_console)
313 by mattgiuca
test/test_framework: Updated examples, a bit of better descriptions, sample
428
        file.write("""
429
# List of all installable files in subjects directory.
430
# This is to install sample subjects and material.
431
list_subjects = """)
432
        writelist_pretty(file, list_subjects)
114 by mattgiuca
setup.py: Wrote listmake (and ancillary functions).
433
434
        file.close()
435
    except IOError, (errno, strerror):
436
        print "IO error(%s): %s" % (errno, strerror)
437
        sys.exit(1)
438
439
    print "Successfully wrote install_list.py"
440
441
    print
442
    print ("You may modify the set of installable files before cutting the "
443
            "distribution:")
444
    print listfile
445
    print
446
447
    return 0
448
313 by mattgiuca
test/test_framework: Updated examples, a bit of better descriptions, sample
449
def build_list_py_files(dir, no_top_level=False):
114 by mattgiuca
setup.py: Wrote listmake (and ancillary functions).
450
    """Builds a list of all py files found in a directory and its
313 by mattgiuca
test/test_framework: Updated examples, a bit of better descriptions, sample
451
    subdirectories. Returns this as a list of strings.
452
    no_top_level=True means the file paths will not include the top-level
453
    directory.
454
    """
114 by mattgiuca
setup.py: Wrote listmake (and ancillary functions).
455
    pylist = []
456
    for (dirpath, dirnames, filenames) in os.walk(dir):
457
        # Exclude directories beginning with a '.' (such as '.svn')
458
        filter_mutate(lambda x: x[0] != '.', dirnames)
459
        # All *.py files are added to the list
460
        pylist += [os.path.join(dirpath, item) for item in filenames
117 by mattgiuca
setup.py: listmake uses mimetypes to select all files with certain mime types,
461
            if mimetypes.guess_type(item)[0] in listmake_mimetypes]
313 by mattgiuca
test/test_framework: Updated examples, a bit of better descriptions, sample
462
    if no_top_level:
463
        for i in range(0, len(pylist)):
464
            _, pylist[i] = pylist[i].split(os.sep, 1)
114 by mattgiuca
setup.py: Wrote listmake (and ancillary functions).
465
    return pylist
466
467
def writelist_pretty(file, list):
468
    """Writes a list one element per line, to a file."""
469
    if list == []:
470
        file.write("[]\n")
471
    else:
472
        file.write('[\n')
473
        for elem in list:
474
            file.write('    %s,\n' % repr(elem))
475
        file.write(']\n')
476
104 by mattgiuca
setup.py: Replaced the simple script with a full options processing script
477
def conf(args):
364 by mattgiuca
setup.py: Fixed config command-line args (forgot to make general).
478
    global db_port
104 by mattgiuca
setup.py: Replaced the simple script with a full options processing script
479
    # Set up some variables
480
481
    cwd = os.getcwd()
482
    # the files that will be created/overwritten
483
    conffile = os.path.join(cwd, "www/conf/conf.py")
484
    conf_hfile = os.path.join(cwd, "trampoline/conf.h")
485
120 by mattgiuca
setup.py: Added command-line argument mode for conf. This completely works!
486
    # Get command-line arguments to avoid asking questions.
487
364 by mattgiuca
setup.py: Fixed config command-line args (forgot to make general).
488
    optnames = []
489
    for opt in config_options:
490
        optnames.append(opt.option_name + "=")
491
    (opts, args) = getopt.gnu_getopt(args, "", optnames)
120 by mattgiuca
setup.py: Added command-line argument mode for conf. This completely works!
492
493
    if args != []:
494
        print >>sys.stderr, "Invalid arguments:", string.join(args, ' ')
495
        return 2
496
497
    if opts == []:
498
        # Interactive mode. Prompt the user for all the values.
499
500
        print """This tool will create the following files:
100 by mattgiuca
setup.py: Added a new config variable, ivle_install_dir.
501
    %s
502
    %s
97 by mattgiuca
Moved template.py and setup.py to better places.
503
prompting you for details about your configuration. The file will be
504
overwritten if it already exists. It will *not* install or deploy IVLE.
505
506
Please hit Ctrl+C now if you do not wish to do this.
100 by mattgiuca
setup.py: Added a new config variable, ivle_install_dir.
507
""" % (conffile, conf_hfile)
97 by mattgiuca
Moved template.py and setup.py to better places.
508
120 by mattgiuca
setup.py: Added command-line argument mode for conf. This completely works!
509
        # Get information from the administrator
510
        # If EOF is encountered at any time during the questioning, just exit
511
        # silently
97 by mattgiuca
Moved template.py and setup.py to better places.
512
358 by mattgiuca
setup.py: Gutted out the config options code. It was getting so there were
513
        for opt in config_options:
514
            globals()[opt.option_name] = \
515
                query_user(globals()[opt.option_name], opt.prompt)
120 by mattgiuca
setup.py: Added command-line argument mode for conf. This completely works!
516
    else:
517
        opts = dict(opts)
518
        # Non-interactive mode. Parse the options.
358 by mattgiuca
setup.py: Gutted out the config options code. It was getting so there were
519
        for opt in config_options:
520
            if '--' + opt.option_name in opts:
521
                globals()[opt.option_name] = opts['--' + opt.option_name]
120 by mattgiuca
setup.py: Added command-line argument mode for conf. This completely works!
522
110 by mattgiuca
setup.py: Added to trampoline/conf.h an "allowed_uids" array. Asks the user
523
    # Error handling on input values
524
    try:
358 by mattgiuca
setup.py: Gutted out the config options code. It was getting so there were
525
        allowed_uids_list = map(int, allowed_uids.split(','))
110 by mattgiuca
setup.py: Added to trampoline/conf.h an "allowed_uids" array. Asks the user
526
    except ValueError:
112 by mattgiuca
setup.py: A few comment changes.
527
        print >>sys.stderr, (
528
        "Invalid UID list (%s).\n"
529
        "Must be a comma-separated list of integers." % allowed_uids)
110 by mattgiuca
setup.py: Added to trampoline/conf.h an "allowed_uids" array. Asks the user
530
        return 1
364 by mattgiuca
setup.py: Fixed config command-line args (forgot to make general).
531
    try:
532
        db_port = int(db_port)
533
        if db_port < 0 or db_port >= 65536: raise ValueError()
534
    except ValueError:
535
        print >>sys.stderr, (
536
        "Invalid DB port (%s).\n"
537
        "Must be an integer between 0 and 65535." % repr(db_port))
538
        return 1
104 by mattgiuca
setup.py: Replaced the simple script with a full options processing script
539
540
    # Write www/conf/conf.py
541
542
    try:
543
        conf = open(conffile, "w")
544
545
        conf.write("""# IVLE Configuration File
97 by mattgiuca
Moved template.py and setup.py to better places.
546
# conf.py
547
# Miscellaneous application settings
548
358 by mattgiuca
setup.py: Gutted out the config options code. It was getting so there were
549
""")
550
        for opt in config_options:
364 by mattgiuca
setup.py: Fixed config command-line args (forgot to make general).
551
            conf.write('%s\n%s = %s\n' % (opt.comment, opt.option_name,
552
                repr(globals()[opt.option_name])))
110 by mattgiuca
setup.py: Added to trampoline/conf.h an "allowed_uids" array. Asks the user
553
104 by mattgiuca
setup.py: Replaced the simple script with a full options processing script
554
        conf.close()
555
    except IOError, (errno, strerror):
556
        print "IO error(%s): %s" % (errno, strerror)
557
        sys.exit(1)
558
559
    print "Successfully wrote www/conf/conf.py"
560
561
    # Write trampoline/conf.h
562
563
    try:
564
        conf = open(conf_hfile, "w")
565
566
        conf.write("""/* IVLE Configuration File
100 by mattgiuca
setup.py: Added a new config variable, ivle_install_dir.
567
 * conf.h
568
 * Administrator settings required by trampoline.
569
 * Note: trampoline will have to be rebuilt in order for changes to this file
570
 * to take effect.
571
 */
572
573
/* In the local file system, where are the jails located.
574
 * The trampoline does not allow the creation of a jail anywhere besides
575
 * jail_base or a subdirectory of jail_base.
576
 */
577
static const char* jail_base = "%s";
110 by mattgiuca
setup.py: Added to trampoline/conf.h an "allowed_uids" array. Asks the user
578
579
/* Which user IDs are allowed to run the trampoline.
580
 * This list should be limited to the web server user.
581
 * (Note that root is an implicit member of this list).
582
 */
112 by mattgiuca
setup.py: A few comment changes.
583
static const int allowed_uids[] = { %s };
358 by mattgiuca
setup.py: Gutted out the config options code. It was getting so there were
584
""" % (jail_base, repr(allowed_uids_list)[1:-1]))
100 by mattgiuca
setup.py: Added a new config variable, ivle_install_dir.
585
104 by mattgiuca
setup.py: Replaced the simple script with a full options processing script
586
        conf.close()
587
    except IOError, (errno, strerror):
588
        print "IO error(%s): %s" % (errno, strerror)
589
        sys.exit(1)
590
591
    print "Successfully wrote trampoline/conf.h"
592
593
    print
594
    print "You may modify the configuration at any time by editing"
595
    print conffile
596
    print conf_hfile
597
    print
598
    return 0
599
600
def build(args):
121 by mattgiuca
setup: build and install now read command line options
601
    # Get "dry" variable from command line
602
    (opts, args) = getopt.gnu_getopt(args, "n", ['dry'])
603
    opts = dict(opts)
604
    dry = '-n' in opts or '--dry' in opts
605
606
    if dry:
607
        print "Dry run (no actions will be executed\n"
107 by mattgiuca
setup.py: Added "action" functions which encapsulate calling OS functions.
608
609
    # Compile the trampoline
349 by mattgiuca
setup.py: Changed so instead of directly calling gcc on the trampoline, calls
610
    curdir = os.getcwd()
611
    os.chdir('trampoline')
612
    action_runprog('make', [], dry)
613
    os.chdir(curdir)
107 by mattgiuca
setup.py: Added "action" functions which encapsulate calling OS functions.
614
615
    # Create the jail and its subdirectories
122 by mattgiuca
setup.py: build action now copies all operating system files into the jail.
616
    # Note: Other subdirs will be made by copying files
116 by mattgiuca
setup.py: mkdir now properly obeys "dry".
617
    action_mkdir('jail', dry)
618
    action_mkdir('jail/home', dry)
619
    action_mkdir('jail/tmp', dry)
107 by mattgiuca
setup.py: Added "action" functions which encapsulate calling OS functions.
620
122 by mattgiuca
setup.py: build action now copies all operating system files into the jail.
621
    # Copy all console and operating system files into the jail
118 by mattgiuca
setup.py: Added copytree and copylist actions.
622
    action_copylist(install_list.list_console, 'jail/opt/ivle', dry)
122 by mattgiuca
setup.py: build action now copies all operating system files into the jail.
623
    copy_os_files_jail(dry)
253 by mattgiuca
setup.py: chmods python-console when building.
624
    # Chmod the python console
625
    action_chmod_x('jail/opt/ivle/console/python-console', dry)
626
    
118 by mattgiuca
setup.py: Added copytree and copylist actions.
627
116 by mattgiuca
setup.py: mkdir now properly obeys "dry".
628
    # Compile .py files into .pyc or .pyo files
629
    compileall.compile_dir('www', quiet=True)
630
    compileall.compile_dir('console', quiet=True)
107 by mattgiuca
setup.py: Added "action" functions which encapsulate calling OS functions.
631
104 by mattgiuca
setup.py: Replaced the simple script with a full options processing script
632
    return 0
633
122 by mattgiuca
setup.py: build action now copies all operating system files into the jail.
634
def copy_os_files_jail(dry):
635
    """Copies necessary Operating System files from their usual locations
636
    into the jail/ directory of the cwd."""
637
    # Currently source paths are configured for Ubuntu.
251 by mattgiuca
setup.py: Worked the "files to copy into jail" out into a separate list
638
    for filename in JAIL_FILES:
639
        copy_file_to_jail(filename, dry)
640
    for src, dst in JAIL_LINKS.items():
641
        action_symlink(src, dst, dry)
642
    for src, dst in JAIL_COPYTREES.items():
643
        action_copytree(src, dst, dry)
122 by mattgiuca
setup.py: build action now copies all operating system files into the jail.
644
645
def copy_file_to_jail(src, dry):
646
    """Copies a single file from an absolute location into the same location
647
    within the jail. src must begin with a '/'. The jail will be located
648
    in a 'jail' subdirectory of the current path."""
649
    action_copyfile(src, 'jail' + src, dry)
650
104 by mattgiuca
setup.py: Replaced the simple script with a full options processing script
651
def install(args):
121 by mattgiuca
setup: build and install now read command line options
652
    # Get "dry" and "nojail" variables from command line
313 by mattgiuca
test/test_framework: Updated examples, a bit of better descriptions, sample
653
    (opts, args) = getopt.gnu_getopt(args, "n",
654
        ['dry', 'nojail', 'nosubjects'])
121 by mattgiuca
setup: build and install now read command line options
655
    opts = dict(opts)
656
    dry = '-n' in opts or '--dry' in opts
657
    nojail = '--nojail' in opts
313 by mattgiuca
test/test_framework: Updated examples, a bit of better descriptions, sample
658
    nosubjects = '--nosubjects' in opts
121 by mattgiuca
setup: build and install now read command line options
659
660
    if dry:
661
        print "Dry run (no actions will be executed\n"
119 by mattgiuca
setup.py: Added install action. Completely works!
662
663
    if not dry and os.geteuid() != 0:
664
        print >>sys.stderr, "Must be root to run install"
665
        print >>sys.stderr, "(I need to chown some files)."
666
        return 1
667
668
    # Create the target (install) directory
669
    action_mkdir(ivle_install_dir, dry)
670
671
    # Create bin and copy the compiled files there
672
    action_mkdir(os.path.join(ivle_install_dir, 'bin'), dry)
673
    tramppath = os.path.join(ivle_install_dir, 'bin/trampoline')
674
    action_copyfile('trampoline/trampoline', tramppath, dry)
675
    # chown trampoline to root and set setuid bit
676
    action_chown_setuid(tramppath, dry)
677
678
    # Copy the www directory using the list
679
    action_copylist(install_list.list_www, ivle_install_dir, dry)
680
681
    if not nojail:
682
        # Copy the local jail directory built by the build action
683
        # to the jails template directory (it will be used as a template
684
        # for all the students' jails).
685
        action_copytree('jail', os.path.join(jail_base, 'template'), dry)
313 by mattgiuca
test/test_framework: Updated examples, a bit of better descriptions, sample
686
    if not nosubjects:
687
        # Copy the subjects directory across
688
        action_copylist(install_list.list_subjects, subjects_base, dry,
689
            srcdir="./subjects")
119 by mattgiuca
setup.py: Added install action. Completely works!
690
256 by mattgiuca
Changed the way IVLE's path is loaded into Python's sys.path. Now a file
691
    # Append IVLE path to ivle.pth in python site packages
692
    # (Unless it's already there)
693
    ivle_pth = os.path.join(sys.prefix,
317 by mattgiuca
doc/dependencies: Added dependency on matplotlib.
694
        "lib/python%s/site-packages/ivle.pth" % PYTHON_VERSION)
256 by mattgiuca
Changed the way IVLE's path is loaded into Python's sys.path. Now a file
695
    ivle_www = os.path.join(ivle_install_dir, "www")
696
    write_ivle_pth = True
697
    try:
698
        file = open(ivle_pth, 'r')
699
        for line in file:
700
            if line.strip() == ivle_www:
701
                write_ivle_pth = False
702
                break
703
    except (IOError, OSError):
704
        pass
705
    if write_ivle_pth:
706
        action_append(ivle_pth, ivle_www)
707
104 by mattgiuca
setup.py: Replaced the simple script with a full options processing script
708
    return 0
709
252 by mattgiuca
setup.py: Added action "updatejails" which wipes all student jails, replacing
710
def updatejails(args):
711
    # Get "dry" variable from command line
712
    (opts, args) = getopt.gnu_getopt(args, "n", ['dry'])
713
    opts = dict(opts)
714
    dry = '-n' in opts or '--dry' in opts
715
716
    if dry:
717
        print "Dry run (no actions will be executed\n"
718
719
    if not dry and os.geteuid() != 0:
720
        print >>sys.stderr, "Must be root to run install"
721
        print >>sys.stderr, "(I need to chown some files)."
722
        return 1
723
724
    # Update the template jail directory in case it hasn't been installed
725
    # recently.
726
    action_copytree('jail', os.path.join(jail_base, 'template'), dry)
727
728
    # Re-link all the files in all students jails.
729
    for dir in os.listdir(jail_base):
730
        if dir == 'template': continue
731
        # First back up the student's home directory
732
        temp_home = os.tmpnam()
733
        action_rename(os.path.join(jail_base, dir, 'home'), temp_home, dry)
734
        # Delete the student's jail and relink the jail files
735
        action_linktree(os.path.join(jail_base, 'template'),
736
            os.path.join(jail_base, dir), dry)
737
        # Restore the student's home directory
738
        action_rename(temp_home, os.path.join(jail_base, dir, 'home'), dry)
739
        # Set up the user's home directory just in case they don't have a
740
        # directory for this yet
741
        action_mkdir(os.path.join(jail_base, dir, 'home', dir), dry)
742
743
    return 0
744
107 by mattgiuca
setup.py: Added "action" functions which encapsulate calling OS functions.
745
# The actions call Python os functions but print actions and handle dryness.
746
# May still throw os exceptions if errors occur.
747
108 by mattgiuca
setup.py: Added RunError class. action_runprog now throws a RunError if the
748
class RunError:
749
    """Represents an error when running a program (nonzero return)."""
750
    def __init__(self, prog, retcode):
751
        self.prog = prog
752
        self.retcode = retcode
753
    def __str__(self):
754
        return str(self.prog) + " returned " + repr(self.retcode)
755
107 by mattgiuca
setup.py: Added "action" functions which encapsulate calling OS functions.
756
def action_runprog(prog, args, dry):
757
    """Runs a unix program. Searches in $PATH. Synchronous (waits for the
758
    program to return). Runs in the current environment. First prints the
759
    action as a "bash" line.
760
108 by mattgiuca
setup.py: Added RunError class. action_runprog now throws a RunError if the
761
    Throws a RunError with a retcode of the return value of the program,
762
    if the program did not return 0.
763
107 by mattgiuca
setup.py: Added "action" functions which encapsulate calling OS functions.
764
    prog: String. Name of the program. (No path required, if in $PATH).
765
    args: [String]. Arguments to the program.
766
    dry: Bool. If True, prints but does not execute.
767
    """
768
    print prog, string.join(args, ' ')
114 by mattgiuca
setup.py: Wrote listmake (and ancillary functions).
769
    if dry: return
770
    ret = os.spawnvp(os.P_WAIT, prog, args)
771
    if ret != 0:
772
        raise RunError(prog, ret)
107 by mattgiuca
setup.py: Added "action" functions which encapsulate calling OS functions.
773
252 by mattgiuca
setup.py: Added action "updatejails" which wipes all student jails, replacing
774
def action_rename(src, dst, dry):
775
    """Calls rename. Deletes the target if it already exists."""
776
    if os.access(dst, os.F_OK):
777
        print "rm -r", dst
778
        if not dry:
779
            shutil.rmtree(dst, True)
780
    print "mv ", src, dst
781
    if dry: return
782
    try:
783
        os.rename(src, dst)
784
    except OSError, (err, msg):
785
        if err != errno.EEXIST:
786
            raise
787
116 by mattgiuca
setup.py: mkdir now properly obeys "dry".
788
def action_mkdir(path, dry):
113 by mattgiuca
setup.py: Now loads defaults, if possible, from the existing conf.py.
789
    """Calls mkdir. Silently ignored if the directory already exists.
790
    Creates all parent directories as necessary."""
791
    print "mkdir -p", path
114 by mattgiuca
setup.py: Wrote listmake (and ancillary functions).
792
    if dry: return
107 by mattgiuca
setup.py: Added "action" functions which encapsulate calling OS functions.
793
    try:
113 by mattgiuca
setup.py: Now loads defaults, if possible, from the existing conf.py.
794
        os.makedirs(path)
107 by mattgiuca
setup.py: Added "action" functions which encapsulate calling OS functions.
795
    except OSError, (err, msg):
796
        if err != errno.EEXIST:
797
            raise
798
118 by mattgiuca
setup.py: Added copytree and copylist actions.
799
def action_copytree(src, dst, dry):
800
    """Copies an entire directory tree. Symlinks are seen as normal files and
801
    copies of the entire file (not the link) are made. Creates all parent
802
    directories as necessary.
803
804
    See shutil.copytree."""
805
    if os.access(dst, os.F_OK):
806
        print "rm -r", dst
807
        if not dry:
808
            shutil.rmtree(dst, True)
809
    print "cp -r", src, dst
810
    if dry: return
132 by mattgiuca
setup.py: File copying is more robust - preserves symlinks and permissions
811
    shutil.copytree(src, dst, True)
118 by mattgiuca
setup.py: Added copytree and copylist actions.
812
252 by mattgiuca
setup.py: Added action "updatejails" which wipes all student jails, replacing
813
def action_linktree(src, dst, dry):
814
    """Hard-links an entire directory tree. Same as copytree but the created
815
    files are hard-links not actual copies. Removes the existing destination.
816
    """
817
    if os.access(dst, os.F_OK):
818
        print "rm -r", dst
819
        if not dry:
820
            shutil.rmtree(dst, True)
821
    print "<cp with hardlinks> -r", src, dst
822
    if dry: return
823
    common.makeuser.linktree(src, dst)
824
313 by mattgiuca
test/test_framework: Updated examples, a bit of better descriptions, sample
825
def action_copylist(srclist, dst, dry, srcdir="."):
118 by mattgiuca
setup.py: Added copytree and copylist actions.
826
    """Copies all files in a list to a new location. The files in the list
827
    are read relative to the current directory, and their destinations are the
828
    same paths relative to dst. Creates all parent directories as necessary.
313 by mattgiuca
test/test_framework: Updated examples, a bit of better descriptions, sample
829
    srcdir is "." by default, can be overridden.
118 by mattgiuca
setup.py: Added copytree and copylist actions.
830
    """
831
    for srcfile in srclist:
832
        dstfile = os.path.join(dst, srcfile)
313 by mattgiuca
test/test_framework: Updated examples, a bit of better descriptions, sample
833
        srcfile = os.path.join(srcdir, srcfile)
118 by mattgiuca
setup.py: Added copytree and copylist actions.
834
        dstdir = os.path.split(dstfile)[0]
835
        if not os.path.isdir(dstdir):
836
            action_mkdir(dstdir, dry)
837
        print "cp -f", srcfile, dstfile
838
        if not dry:
133 by mattgiuca
setup.py: Now allows IVLE to be installed over itself, in order to allow
839
            try:
840
                shutil.copyfile(srcfile, dstfile)
841
                shutil.copymode(srcfile, dstfile)
842
            except shutil.Error:
843
                pass
118 by mattgiuca
setup.py: Added copytree and copylist actions.
844
119 by mattgiuca
setup.py: Added install action. Completely works!
845
def action_copyfile(src, dst, dry):
846
    """Copies one file to a new location. Creates all parent directories
847
    as necessary.
318 by mattgiuca
setup.py: action_copyfile now handles errors as warnings, instead of quitting
848
    Warn if file not found.
119 by mattgiuca
setup.py: Added install action. Completely works!
849
    """
850
    dstdir = os.path.split(dst)[0]
851
    if not os.path.isdir(dstdir):
852
        action_mkdir(dstdir, dry)
853
    print "cp -f", src, dst
854
    if not dry:
133 by mattgiuca
setup.py: Now allows IVLE to be installed over itself, in order to allow
855
        try:
856
            shutil.copyfile(src, dst)
857
            shutil.copymode(src, dst)
318 by mattgiuca
setup.py: action_copyfile now handles errors as warnings, instead of quitting
858
        except (shutil.Error, IOError), e:
859
            print "Warning: " + str(e)
119 by mattgiuca
setup.py: Added install action. Completely works!
860
122 by mattgiuca
setup.py: build action now copies all operating system files into the jail.
861
def action_symlink(src, dst, dry):
862
    """Creates a symlink in a given location. Creates all parent directories
863
    as necessary.
864
    """
865
    dstdir = os.path.split(dst)[0]
866
    if not os.path.isdir(dstdir):
867
        action_mkdir(dstdir, dry)
131 by mattgiuca
setup.py:
868
    # Delete existing file
869
    if os.path.exists(dst):
870
        os.remove(dst)
871
    print "ln -fs", src, dst
122 by mattgiuca
setup.py: build action now copies all operating system files into the jail.
872
    if not dry:
873
        os.symlink(src, dst)
874
256 by mattgiuca
Changed the way IVLE's path is loaded into Python's sys.path. Now a file
875
def action_append(ivle_pth, ivle_www):
876
    file = open(ivle_pth, 'a+')
877
    file.write(ivle_www + '\n')
878
    file.close()
879
119 by mattgiuca
setup.py: Added install action. Completely works!
880
def action_chown_setuid(file, dry):
881
    """Chowns a file to root, and sets the setuid bit on the file.
882
    Calling this function requires the euid to be root.
883
    The actual mode of path is set to: rws--s--s
884
    """
885
    print "chown root:root", file
886
    if not dry:
887
        os.chown(file, 0, 0)
888
    print "chmod a+xs", file
889
    print "chmod u+rw", file
890
    if not dry:
891
        os.chmod(file, stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
892
            | stat.S_ISUID | stat.S_IRUSR | stat.S_IWUSR)
893
253 by mattgiuca
setup.py: chmods python-console when building.
894
def action_chmod_x(file, dry):
895
    """Chmod +xs a file (sets execute permission)."""
896
    print "chmod u+rwx", file
897
    if not dry:
898
        os.chmod(file, stat.S_IXUSR | stat.S_IRUSR | stat.S_IWUSR)
899
113 by mattgiuca
setup.py: Now loads defaults, if possible, from the existing conf.py.
900
def query_user(default, prompt):
104 by mattgiuca
setup.py: Replaced the simple script with a full options processing script
901
    """Prompts the user for a string, which is read from a line of stdin.
902
    Exits silently if EOF is encountered. Returns the string, with spaces
903
    removed from the beginning and end.
113 by mattgiuca
setup.py: Now loads defaults, if possible, from the existing conf.py.
904
905
    Returns default if a 0-length line (after spaces removed) was read.
104 by mattgiuca
setup.py: Replaced the simple script with a full options processing script
906
    """
113 by mattgiuca
setup.py: Now loads defaults, if possible, from the existing conf.py.
907
    sys.stdout.write('%s\n    (default: "%s")\n>' % (prompt, default))
104 by mattgiuca
setup.py: Replaced the simple script with a full options processing script
908
    try:
909
        val = sys.stdin.readline()
910
    except KeyboardInterrupt:
911
        # Ctrl+C
912
        sys.stdout.write("\n")
913
        sys.exit(1)
914
    sys.stdout.write("\n")
113 by mattgiuca
setup.py: Now loads defaults, if possible, from the existing conf.py.
915
    # If EOF, exit
104 by mattgiuca
setup.py: Replaced the simple script with a full options processing script
916
    if val == '': sys.exit(1)
113 by mattgiuca
setup.py: Now loads defaults, if possible, from the existing conf.py.
917
    # If empty line, return default
918
    val = val.strip()
919
    if val == '': return default
920
    return val
104 by mattgiuca
setup.py: Replaced the simple script with a full options processing script
921
114 by mattgiuca
setup.py: Wrote listmake (and ancillary functions).
922
def filter_mutate(function, list):
923
    """Like built-in filter, but mutates the given list instead of returning a
924
    new one. Returns None."""
925
    i = len(list)-1
926
    while i >= 0:
927
        # Delete elements which do not match
928
        if not function(list[i]):
929
            del list[i]
930
        i -= 1
931
104 by mattgiuca
setup.py: Replaced the simple script with a full options processing script
932
if __name__ == "__main__":
933
    sys.exit(main())