~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
349 by mattgiuca
setup.py: Changed so instead of directly calling gcc on the trampoline, calls
584
    curdir = os.getcwd()
585
    os.chdir('trampoline')
586
    action_runprog('make', [], dry)
587
    os.chdir(curdir)
107 by mattgiuca
setup.py: Added "action" functions which encapsulate calling OS functions.
588
589
    # Create the jail and its subdirectories
122 by mattgiuca
setup.py: build action now copies all operating system files into the jail.
590
    # Note: Other subdirs will be made by copying files
116 by mattgiuca
setup.py: mkdir now properly obeys "dry".
591
    action_mkdir('jail', dry)
592
    action_mkdir('jail/home', dry)
593
    action_mkdir('jail/tmp', dry)
107 by mattgiuca
setup.py: Added "action" functions which encapsulate calling OS functions.
594
122 by mattgiuca
setup.py: build action now copies all operating system files into the jail.
595
    # Copy all console and operating system files into the jail
118 by mattgiuca
setup.py: Added copytree and copylist actions.
596
    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.
597
    copy_os_files_jail(dry)
253 by mattgiuca
setup.py: chmods python-console when building.
598
    # Chmod the python console
599
    action_chmod_x('jail/opt/ivle/console/python-console', dry)
600
    
118 by mattgiuca
setup.py: Added copytree and copylist actions.
601
116 by mattgiuca
setup.py: mkdir now properly obeys "dry".
602
    # Compile .py files into .pyc or .pyo files
603
    compileall.compile_dir('www', quiet=True)
604
    compileall.compile_dir('console', quiet=True)
107 by mattgiuca
setup.py: Added "action" functions which encapsulate calling OS functions.
605
104 by mattgiuca
setup.py: Replaced the simple script with a full options processing script
606
    return 0
607
122 by mattgiuca
setup.py: build action now copies all operating system files into the jail.
608
def copy_os_files_jail(dry):
609
    """Copies necessary Operating System files from their usual locations
610
    into the jail/ directory of the cwd."""
611
    # Currently source paths are configured for Ubuntu.
251 by mattgiuca
setup.py: Worked the "files to copy into jail" out into a separate list
612
    for filename in JAIL_FILES:
613
        copy_file_to_jail(filename, dry)
614
    for src, dst in JAIL_LINKS.items():
615
        action_symlink(src, dst, dry)
616
    for src, dst in JAIL_COPYTREES.items():
617
        action_copytree(src, dst, dry)
122 by mattgiuca
setup.py: build action now copies all operating system files into the jail.
618
619
def copy_file_to_jail(src, dry):
620
    """Copies a single file from an absolute location into the same location
621
    within the jail. src must begin with a '/'. The jail will be located
622
    in a 'jail' subdirectory of the current path."""
623
    action_copyfile(src, 'jail' + src, dry)
624
104 by mattgiuca
setup.py: Replaced the simple script with a full options processing script
625
def install(args):
121 by mattgiuca
setup: build and install now read command line options
626
    # Get "dry" and "nojail" variables from command line
313 by mattgiuca
test/test_framework: Updated examples, a bit of better descriptions, sample
627
    (opts, args) = getopt.gnu_getopt(args, "n",
628
        ['dry', 'nojail', 'nosubjects'])
121 by mattgiuca
setup: build and install now read command line options
629
    opts = dict(opts)
630
    dry = '-n' in opts or '--dry' in opts
631
    nojail = '--nojail' in opts
313 by mattgiuca
test/test_framework: Updated examples, a bit of better descriptions, sample
632
    nosubjects = '--nosubjects' in opts
121 by mattgiuca
setup: build and install now read command line options
633
634
    if dry:
635
        print "Dry run (no actions will be executed\n"
119 by mattgiuca
setup.py: Added install action. Completely works!
636
637
    if not dry and os.geteuid() != 0:
638
        print >>sys.stderr, "Must be root to run install"
639
        print >>sys.stderr, "(I need to chown some files)."
640
        return 1
641
642
    # Create the target (install) directory
643
    action_mkdir(ivle_install_dir, dry)
644
645
    # Create bin and copy the compiled files there
646
    action_mkdir(os.path.join(ivle_install_dir, 'bin'), dry)
647
    tramppath = os.path.join(ivle_install_dir, 'bin/trampoline')
648
    action_copyfile('trampoline/trampoline', tramppath, dry)
649
    # chown trampoline to root and set setuid bit
650
    action_chown_setuid(tramppath, dry)
651
652
    # Copy the www directory using the list
653
    action_copylist(install_list.list_www, ivle_install_dir, dry)
654
655
    if not nojail:
656
        # Copy the local jail directory built by the build action
657
        # to the jails template directory (it will be used as a template
658
        # for all the students' jails).
659
        action_copytree('jail', os.path.join(jail_base, 'template'), dry)
313 by mattgiuca
test/test_framework: Updated examples, a bit of better descriptions, sample
660
    if not nosubjects:
661
        # Copy the subjects directory across
662
        action_copylist(install_list.list_subjects, subjects_base, dry,
663
            srcdir="./subjects")
119 by mattgiuca
setup.py: Added install action. Completely works!
664
256 by mattgiuca
Changed the way IVLE's path is loaded into Python's sys.path. Now a file
665
    # Append IVLE path to ivle.pth in python site packages
666
    # (Unless it's already there)
667
    ivle_pth = os.path.join(sys.prefix,
317 by mattgiuca
doc/dependencies: Added dependency on matplotlib.
668
        "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
669
    ivle_www = os.path.join(ivle_install_dir, "www")
670
    write_ivle_pth = True
671
    try:
672
        file = open(ivle_pth, 'r')
673
        for line in file:
674
            if line.strip() == ivle_www:
675
                write_ivle_pth = False
676
                break
677
    except (IOError, OSError):
678
        pass
679
    if write_ivle_pth:
680
        action_append(ivle_pth, ivle_www)
681
104 by mattgiuca
setup.py: Replaced the simple script with a full options processing script
682
    return 0
683
252 by mattgiuca
setup.py: Added action "updatejails" which wipes all student jails, replacing
684
def updatejails(args):
685
    # Get "dry" variable from command line
686
    (opts, args) = getopt.gnu_getopt(args, "n", ['dry'])
687
    opts = dict(opts)
688
    dry = '-n' in opts or '--dry' in opts
689
690
    if dry:
691
        print "Dry run (no actions will be executed\n"
692
693
    if not dry and os.geteuid() != 0:
694
        print >>sys.stderr, "Must be root to run install"
695
        print >>sys.stderr, "(I need to chown some files)."
696
        return 1
697
698
    # Update the template jail directory in case it hasn't been installed
699
    # recently.
700
    action_copytree('jail', os.path.join(jail_base, 'template'), dry)
701
702
    # Re-link all the files in all students jails.
703
    for dir in os.listdir(jail_base):
704
        if dir == 'template': continue
705
        # First back up the student's home directory
706
        temp_home = os.tmpnam()
707
        action_rename(os.path.join(jail_base, dir, 'home'), temp_home, dry)
708
        # Delete the student's jail and relink the jail files
709
        action_linktree(os.path.join(jail_base, 'template'),
710
            os.path.join(jail_base, dir), dry)
711
        # Restore the student's home directory
712
        action_rename(temp_home, os.path.join(jail_base, dir, 'home'), dry)
713
        # Set up the user's home directory just in case they don't have a
714
        # directory for this yet
715
        action_mkdir(os.path.join(jail_base, dir, 'home', dir), dry)
716
717
    return 0
718
107 by mattgiuca
setup.py: Added "action" functions which encapsulate calling OS functions.
719
# The actions call Python os functions but print actions and handle dryness.
720
# May still throw os exceptions if errors occur.
721
108 by mattgiuca
setup.py: Added RunError class. action_runprog now throws a RunError if the
722
class RunError:
723
    """Represents an error when running a program (nonzero return)."""
724
    def __init__(self, prog, retcode):
725
        self.prog = prog
726
        self.retcode = retcode
727
    def __str__(self):
728
        return str(self.prog) + " returned " + repr(self.retcode)
729
107 by mattgiuca
setup.py: Added "action" functions which encapsulate calling OS functions.
730
def action_runprog(prog, args, dry):
731
    """Runs a unix program. Searches in $PATH. Synchronous (waits for the
732
    program to return). Runs in the current environment. First prints the
733
    action as a "bash" line.
734
108 by mattgiuca
setup.py: Added RunError class. action_runprog now throws a RunError if the
735
    Throws a RunError with a retcode of the return value of the program,
736
    if the program did not return 0.
737
107 by mattgiuca
setup.py: Added "action" functions which encapsulate calling OS functions.
738
    prog: String. Name of the program. (No path required, if in $PATH).
739
    args: [String]. Arguments to the program.
740
    dry: Bool. If True, prints but does not execute.
741
    """
742
    print prog, string.join(args, ' ')
114 by mattgiuca
setup.py: Wrote listmake (and ancillary functions).
743
    if dry: return
744
    ret = os.spawnvp(os.P_WAIT, prog, args)
745
    if ret != 0:
746
        raise RunError(prog, ret)
107 by mattgiuca
setup.py: Added "action" functions which encapsulate calling OS functions.
747
252 by mattgiuca
setup.py: Added action "updatejails" which wipes all student jails, replacing
748
def action_rename(src, dst, dry):
749
    """Calls rename. Deletes the target if it already exists."""
750
    if os.access(dst, os.F_OK):
751
        print "rm -r", dst
752
        if not dry:
753
            shutil.rmtree(dst, True)
754
    print "mv ", src, dst
755
    if dry: return
756
    try:
757
        os.rename(src, dst)
758
    except OSError, (err, msg):
759
        if err != errno.EEXIST:
760
            raise
761
116 by mattgiuca
setup.py: mkdir now properly obeys "dry".
762
def action_mkdir(path, dry):
113 by mattgiuca
setup.py: Now loads defaults, if possible, from the existing conf.py.
763
    """Calls mkdir. Silently ignored if the directory already exists.
764
    Creates all parent directories as necessary."""
765
    print "mkdir -p", path
114 by mattgiuca
setup.py: Wrote listmake (and ancillary functions).
766
    if dry: return
107 by mattgiuca
setup.py: Added "action" functions which encapsulate calling OS functions.
767
    try:
113 by mattgiuca
setup.py: Now loads defaults, if possible, from the existing conf.py.
768
        os.makedirs(path)
107 by mattgiuca
setup.py: Added "action" functions which encapsulate calling OS functions.
769
    except OSError, (err, msg):
770
        if err != errno.EEXIST:
771
            raise
772
118 by mattgiuca
setup.py: Added copytree and copylist actions.
773
def action_copytree(src, dst, dry):
774
    """Copies an entire directory tree. Symlinks are seen as normal files and
775
    copies of the entire file (not the link) are made. Creates all parent
776
    directories as necessary.
777
778
    See shutil.copytree."""
779
    if os.access(dst, os.F_OK):
780
        print "rm -r", dst
781
        if not dry:
782
            shutil.rmtree(dst, True)
783
    print "cp -r", src, dst
784
    if dry: return
132 by mattgiuca
setup.py: File copying is more robust - preserves symlinks and permissions
785
    shutil.copytree(src, dst, True)
118 by mattgiuca
setup.py: Added copytree and copylist actions.
786
252 by mattgiuca
setup.py: Added action "updatejails" which wipes all student jails, replacing
787
def action_linktree(src, dst, dry):
788
    """Hard-links an entire directory tree. Same as copytree but the created
789
    files are hard-links not actual copies. Removes the existing destination.
790
    """
791
    if os.access(dst, os.F_OK):
792
        print "rm -r", dst
793
        if not dry:
794
            shutil.rmtree(dst, True)
795
    print "<cp with hardlinks> -r", src, dst
796
    if dry: return
797
    common.makeuser.linktree(src, dst)
798
313 by mattgiuca
test/test_framework: Updated examples, a bit of better descriptions, sample
799
def action_copylist(srclist, dst, dry, srcdir="."):
118 by mattgiuca
setup.py: Added copytree and copylist actions.
800
    """Copies all files in a list to a new location. The files in the list
801
    are read relative to the current directory, and their destinations are the
802
    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
803
    srcdir is "." by default, can be overridden.
118 by mattgiuca
setup.py: Added copytree and copylist actions.
804
    """
805
    for srcfile in srclist:
806
        dstfile = os.path.join(dst, srcfile)
313 by mattgiuca
test/test_framework: Updated examples, a bit of better descriptions, sample
807
        srcfile = os.path.join(srcdir, srcfile)
118 by mattgiuca
setup.py: Added copytree and copylist actions.
808
        dstdir = os.path.split(dstfile)[0]
809
        if not os.path.isdir(dstdir):
810
            action_mkdir(dstdir, dry)
811
        print "cp -f", srcfile, dstfile
812
        if not dry:
133 by mattgiuca
setup.py: Now allows IVLE to be installed over itself, in order to allow
813
            try:
814
                shutil.copyfile(srcfile, dstfile)
815
                shutil.copymode(srcfile, dstfile)
816
            except shutil.Error:
817
                pass
118 by mattgiuca
setup.py: Added copytree and copylist actions.
818
119 by mattgiuca
setup.py: Added install action. Completely works!
819
def action_copyfile(src, dst, dry):
820
    """Copies one file to a new location. Creates all parent directories
821
    as necessary.
318 by mattgiuca
setup.py: action_copyfile now handles errors as warnings, instead of quitting
822
    Warn if file not found.
119 by mattgiuca
setup.py: Added install action. Completely works!
823
    """
824
    dstdir = os.path.split(dst)[0]
825
    if not os.path.isdir(dstdir):
826
        action_mkdir(dstdir, dry)
827
    print "cp -f", src, dst
828
    if not dry:
133 by mattgiuca
setup.py: Now allows IVLE to be installed over itself, in order to allow
829
        try:
830
            shutil.copyfile(src, dst)
831
            shutil.copymode(src, dst)
318 by mattgiuca
setup.py: action_copyfile now handles errors as warnings, instead of quitting
832
        except (shutil.Error, IOError), e:
833
            print "Warning: " + str(e)
119 by mattgiuca
setup.py: Added install action. Completely works!
834
122 by mattgiuca
setup.py: build action now copies all operating system files into the jail.
835
def action_symlink(src, dst, dry):
836
    """Creates a symlink in a given location. Creates all parent directories
837
    as necessary.
838
    """
839
    dstdir = os.path.split(dst)[0]
840
    if not os.path.isdir(dstdir):
841
        action_mkdir(dstdir, dry)
131 by mattgiuca
setup.py:
842
    # Delete existing file
843
    if os.path.exists(dst):
844
        os.remove(dst)
845
    print "ln -fs", src, dst
122 by mattgiuca
setup.py: build action now copies all operating system files into the jail.
846
    if not dry:
847
        os.symlink(src, dst)
848
256 by mattgiuca
Changed the way IVLE's path is loaded into Python's sys.path. Now a file
849
def action_append(ivle_pth, ivle_www):
850
    file = open(ivle_pth, 'a+')
851
    file.write(ivle_www + '\n')
852
    file.close()
853
119 by mattgiuca
setup.py: Added install action. Completely works!
854
def action_chown_setuid(file, dry):
855
    """Chowns a file to root, and sets the setuid bit on the file.
856
    Calling this function requires the euid to be root.
857
    The actual mode of path is set to: rws--s--s
858
    """
859
    print "chown root:root", file
860
    if not dry:
861
        os.chown(file, 0, 0)
862
    print "chmod a+xs", file
863
    print "chmod u+rw", file
864
    if not dry:
865
        os.chmod(file, stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
866
            | stat.S_ISUID | stat.S_IRUSR | stat.S_IWUSR)
867
253 by mattgiuca
setup.py: chmods python-console when building.
868
def action_chmod_x(file, dry):
869
    """Chmod +xs a file (sets execute permission)."""
870
    print "chmod u+rwx", file
871
    if not dry:
872
        os.chmod(file, stat.S_IXUSR | stat.S_IRUSR | stat.S_IWUSR)
873
113 by mattgiuca
setup.py: Now loads defaults, if possible, from the existing conf.py.
874
def query_user(default, prompt):
104 by mattgiuca
setup.py: Replaced the simple script with a full options processing script
875
    """Prompts the user for a string, which is read from a line of stdin.
876
    Exits silently if EOF is encountered. Returns the string, with spaces
877
    removed from the beginning and end.
113 by mattgiuca
setup.py: Now loads defaults, if possible, from the existing conf.py.
878
879
    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
880
    """
113 by mattgiuca
setup.py: Now loads defaults, if possible, from the existing conf.py.
881
    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
882
    try:
883
        val = sys.stdin.readline()
884
    except KeyboardInterrupt:
885
        # Ctrl+C
886
        sys.stdout.write("\n")
887
        sys.exit(1)
888
    sys.stdout.write("\n")
113 by mattgiuca
setup.py: Now loads defaults, if possible, from the existing conf.py.
889
    # If EOF, exit
104 by mattgiuca
setup.py: Replaced the simple script with a full options processing script
890
    if val == '': sys.exit(1)
113 by mattgiuca
setup.py: Now loads defaults, if possible, from the existing conf.py.
891
    # If empty line, return default
892
    val = val.strip()
893
    if val == '': return default
894
    return val
104 by mattgiuca
setup.py: Replaced the simple script with a full options processing script
895
114 by mattgiuca
setup.py: Wrote listmake (and ancillary functions).
896
def filter_mutate(function, list):
897
    """Like built-in filter, but mutates the given list instead of returning a
898
    new one. Returns None."""
899
    i = len(list)-1
900
    while i >= 0:
901
        # Delete elements which do not match
902
        if not function(list[i]):
903
            del list[i]
904
        i -= 1
905
104 by mattgiuca
setup.py: Replaced the simple script with a full options processing script
906
if __name__ == "__main__":
907
    sys.exit(main())