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