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

« back to all changes in this revision

Viewing changes to setup.py

  • Committer: mattgiuca
  • Date: 2008-02-05 01:41:15 UTC
  • Revision ID: svn-v3-trunk0:2b9c9e99-6f39-0410-b283-7f802c844ae2:trunk:409
Moved www/conf and www/common to a new directory lib. This separates the "web"
part of IVLE from what is becoming less web oriented (at least from Apache's
standpoint).
Modified setup.py to install this lib directory correctly and write conf in
the right place. Also adds the lib directory to ivle.pth.

Show diffs side-by-side

added added

removed removed

Lines of Context:
31
31
# cutting a distribution, and the listfile it generates should be included in
32
32
# the distribution, avoiding the administrator having to run it.
33
33
 
34
 
# setup.py conf [args]
 
34
# setup.py config [args]
35
35
# Configures IVLE with machine-specific details, most notably, various paths.
36
36
# Either prompts the administrator for these details or accepts them as
37
37
# command-line args.
38
 
# Creates www/conf/conf.py and trampoline/conf.h.
 
38
# Creates lib/conf/conf.py and trampoline/conf.h.
39
39
 
40
40
# setup.py build
41
41
# Compiles all files and sets up a jail template in the source directory.
70
70
 
71
71
# Import modules from the website is tricky since they're in the www
72
72
# directory.
73
 
sys.path.append(os.path.join(os.getcwd(), 'www'))
 
73
sys.path.append(os.path.join(os.getcwd(), 'lib'))
74
74
import conf
75
75
import common.makeuser
76
76
 
 
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
 
77
82
# Operating system files to copy over into the jail.
78
83
# These will be copied from the given place on the OS file system into the
79
84
# same place within the jail.
94
99
    '/bin/ls',
95
100
    '/bin/echo',
96
101
    # Needed by python
97
 
    '/usr/bin/python2.5',
 
102
    '/usr/bin/python%s' % PYTHON_VERSION,
98
103
    # Needed by matplotlib
99
104
    '/usr/lib/i686/cmov/libssl.so.0.9.8',
100
105
    '/usr/lib/i686/cmov/libcrypto.so.0.9.8',
118
123
]
119
124
# Symlinks to make within the jail. Src mapped to dst.
120
125
JAIL_LINKS = {
121
 
    'python2.5': 'jail/usr/bin/python',
 
126
    'python%s' % PYTHON_VERSION: 'jail/usr/bin/python',
122
127
}
123
128
# Trees to copy. Src mapped to dst (these will be passed to action_copytree).
124
129
JAIL_COPYTREES = {
125
 
    '/usr/lib/python2.5': 'jail/usr/lib/python2.5',
 
130
    '/usr/lib/python%s' % PYTHON_VERSION:
 
131
        'jail/usr/lib/python%s' % PYTHON_VERSION,
126
132
    '/usr/share/matplotlib': 'jail/usr/share/matplotlib',
127
133
    '/etc/ld.so.conf.d': 'jail/etc/ld.so.conf.d',
128
134
}
129
135
 
 
136
class ConfigOption:
 
137
    """A configuration option; one of the things written to conf.py."""
 
138
    def __init__(self, option_name, default, prompt, comment):
 
139
        """Creates a configuration option.
 
140
        option_name: Name of the variable in conf.py. Also name of the
 
141
            command-line argument to setup.py conf.
 
142
        default: Default value for this variable.
 
143
        prompt: (Short) string presented during the interactive prompt in
 
144
            setup.py conf.
 
145
        comment: (Long) comment string stored in conf.py. Each line of this
 
146
            string should begin with a '#'.
 
147
        """
 
148
        self.option_name = option_name
 
149
        self.default = default
 
150
        self.prompt = prompt
 
151
        self.comment = comment
 
152
 
 
153
# Configuration options, defaults and descriptions
 
154
config_options = []
 
155
config_options.append(ConfigOption("root_dir", "/ivle",
 
156
    """Root directory where IVLE is located (in URL space):""",
 
157
    """
 
158
# In URL space, where in the site is IVLE located. (All URLs will be prefixed
 
159
# with this).
 
160
# eg. "/" or "/ivle"."""))
 
161
config_options.append(ConfigOption("ivle_install_dir", "/opt/ivle",
 
162
    'Root directory where IVLE will be installed (on the local file '
 
163
    'system):',
 
164
    """
 
165
# In the local file system, where IVLE is actually installed.
 
166
# This directory should contain the "www" and "bin" directories."""))
 
167
config_options.append(ConfigOption("jail_base", "/home/informatics/jails",
 
168
    """Root directory where the jails (containing user files) are stored
 
169
(on the local file system):""",
 
170
    """
 
171
# In the local file system, where are the student/user file spaces located.
 
172
# The user jails are expected to be located immediately in subdirectories of
 
173
# this location."""))
 
174
config_options.append(ConfigOption("subjects_base",
 
175
    "/home/informatics/subjects",
 
176
    """Root directory where the subject directories (containing worksheets
 
177
and other per-subject files) are stored (on the local file system):""",
 
178
    """
 
179
# In the local file system, where are the per-subject file spaces located.
 
180
# The individual subject directories are expected to be located immediately
 
181
# in subdirectories of this location."""))
 
182
config_options.append(ConfigOption("problems_base",
 
183
    "/home/informatics/problems",
 
184
    """Root directory where the problem directories (containing
 
185
subject-independent problem sheets) are stored (on the local file
 
186
system):""",
 
187
    """
 
188
# In the local file system, where are the subject-independent problem sheet
 
189
# file spaces located."""))
 
190
config_options.append(ConfigOption("public_host", "public.localhost",
 
191
    """Hostname which will cause the server to go into "public mode",
 
192
providing login-free access to student's published work:""",
 
193
    """
 
194
# The server goes into "public mode" if the browser sends a request with this
 
195
# host. This is for security reasons - we only serve public student files on a
 
196
# separate domain to the main IVLE site.
 
197
# Public mode does not use cookies, and serves only public content.
 
198
# Private mode (normal mode) requires login, and only serves files relevant to
 
199
# the logged-in user."""))
 
200
config_options.append(ConfigOption("allowed_uids", "33",
 
201
    """UID of the web server process which will run IVLE.
 
202
Only this user may execute the trampoline. May specify multiple users as
 
203
a comma-separated list.
 
204
    (eg. "1002,78")""",
 
205
    """
 
206
# The User-ID of the web server process which will run IVLE, and any other
 
207
# users who are allowed to run the trampoline. This is stores as a string of
 
208
# comma-separated integers, simply because it is not used within Python, only
 
209
# used by the setup program to write to conf.h (see setup.py config)."""))
 
210
config_options.append(ConfigOption("db_host", "localhost",
 
211
    """PostgreSQL Database config
 
212
==========================
 
213
Hostname of the DB server:""",
 
214
    """
 
215
### PostgreSQL Database config ###
 
216
# Database server hostname"""))
 
217
config_options.append(ConfigOption("db_port", "5432",
 
218
    """Port of the DB server:""",
 
219
    """
 
220
# Database server port"""))
 
221
config_options.append(ConfigOption("db_dbname", "ivle",
 
222
    """Database name:""",
 
223
    """
 
224
# Database name"""))
 
225
config_options.append(ConfigOption("db_user", "postgres",
 
226
    """Username for DB server login:""",
 
227
    """
 
228
# Database username"""))
 
229
config_options.append(ConfigOption("db_password", "",
 
230
    """Password for DB server login:
 
231
    (Caution: This password is stored in plaintext in lib/conf/conf.py)""",
 
232
    """
 
233
# Database password"""))
 
234
 
130
235
# Try importing existing conf, but if we can't just set up defaults
131
236
# The reason for this is that these settings are used by other phases
132
237
# of setup besides conf, so we need to know them.
133
238
# Also this allows you to hit Return to accept the existing value.
134
239
try:
135
 
    confmodule = __import__("www/conf/conf")
136
 
    try:
137
 
        root_dir = confmodule.root_dir
138
 
    except:
139
 
        root_dir = "/ivle"
140
 
    try:
141
 
        ivle_install_dir = confmodule.ivle_install_dir
142
 
    except:
143
 
        ivle_install_dir = "/opt/ivle"
144
 
    try:
145
 
        jail_base = confmodule.jail_base
146
 
    except:
147
 
        jail_base = "/home/informatics/jails"
 
240
    confmodule = __import__("lib/conf/conf")
 
241
    for opt in config_options:
 
242
        try:
 
243
            globals()[opt.option_name] = confmodule.__dict__[opt.option_name]
 
244
        except:
 
245
            globals()[opt.option_name] = opt.default
148
246
except ImportError:
149
247
    # Just set reasonable defaults
150
 
    root_dir = "/ivle"
151
 
    ivle_install_dir = "/opt/ivle"
152
 
    jail_base = "/home/informatics/jails"
153
 
# Always defaults
154
 
allowed_uids = "0"
 
248
    for opt in config_options:
 
249
        globals()[opt.option_name] = opt.default
155
250
 
156
251
# Try importing install_list, but don't fail if we can't, because listmake can
157
252
# function without it.
166
261
# as necessary, and include it in the distribution.
167
262
listmake_mimetypes = ['text/x-python', 'text/html',
168
263
    'application/x-javascript', 'application/javascript',
169
 
    'text/css', 'image/png']
 
264
    'text/css', 'image/png', 'application/xml']
170
265
 
171
266
# Main function skeleton from Guido van Rossum
172
267
# http://www.artima.com/weblogs/viewpost.jsp?thread=4829
204
299
    try:
205
300
        oper_func = {
206
301
            'help' : help,
207
 
            'conf' : conf,
 
302
            'config' : conf,
208
303
            'build' : build,
209
304
            'listmake' : listmake,
210
305
            'install' : install,
225
320
Operation (and args) can be:
226
321
    help [operation]
227
322
    listmake (developer use only)
228
 
    conf [args]
 
323
    config [args]
229
324
    build
230
 
    install [--nojail] [-n|--dry]
 
325
    install [--nojail] [--nosubjects] [-n|--dry]
231
326
"""
232
327
        return 1
233
328
    elif len(args) != 1:
246
341
be copied upon installation. This should be run by the developer before
247
342
cutting a distribution, and the listfile it generates should be included in
248
343
the distribution, avoiding the administrator having to run it."""
249
 
    elif operation == 'conf':
250
 
        print """python setup.py conf [args]
 
344
    elif operation == 'config':
 
345
        print """python setup.py config [args]
251
346
Configures IVLE with machine-specific details, most notably, various paths.
252
347
Either prompts the administrator for these details or accepts them as
253
348
command-line args. Will be interactive only if there are no arguments given.
257
352
to rebuild/install), just provide ivle_install_dir as the IVLE trunk
258
353
directory, and run build/install one time.
259
354
 
260
 
Creates www/conf/conf.py and trampoline/conf.h.
 
355
Creates lib/conf/conf.py and trampoline/conf.h.
261
356
 
262
 
Args are:
263
 
    --root_dir
264
 
    --ivle_install_dir
265
 
    --jail_base
266
 
    --allowed_uids
267
 
As explained in the interactive prompt or conf.py.
 
357
Args are:"""
 
358
        for opt in config_options:
 
359
            print "    --" + opt.option_name
 
360
        print """As explained in the interactive prompt or conf.py.
268
361
"""
269
362
    elif operation == 'build':
270
363
        print """python -O setup.py build [--dry|-n]
281
374
 
282
375
--dry | -n  Print out the actions but don't do anything."""
283
376
    elif operation == 'install':
284
 
        print """sudo python setup.py install [--nojail] [--dry|-n]
 
377
        print """sudo python setup.py install [--nojail] [--nosubjects][--dry|-n]
285
378
(Requires root)
286
379
Create target install directory ($target).
287
380
Create $target/bin.
289
382
chown and chmod the installed trampoline.
290
383
Copy www/ to $target.
291
384
Copy jail/ to jails template directory (unless --nojail specified).
 
385
Copy subjects/ to subjects directory (unless --nosubjects specified).
292
386
 
293
 
--nojail    Do not copy the jail.
 
387
--nojail        Do not copy the jail.
 
388
--nosubjects    Do not copy the subjects and problems directories.
294
389
--dry | -n  Print out the actions but don't do anything."""
295
390
    elif operation == 'updatejails':
296
391
        print """sudo python setup.py updatejails [--dry|-n]
307
402
def listmake(args):
308
403
    # We build two separate lists, by walking www and console
309
404
    list_www = build_list_py_files('www')
 
405
    list_lib = build_list_py_files('lib')
310
406
    list_console = build_list_py_files('console')
 
407
    list_subjects = build_list_py_files('subjects', no_top_level=True)
 
408
    list_problems = build_list_py_files('problems', no_top_level=True)
311
409
    # Make sure that the files generated by conf are in the list
312
410
    # (since listmake is typically run before conf)
313
 
    if "www/conf/conf.py" not in list_www:
314
 
        list_www.append("www/conf/conf.py")
 
411
    if "lib/conf/conf.py" not in list_lib:
 
412
        list_www.append("lib/conf/conf.py")
315
413
    # Make sure that console/python-console is in the list
316
414
    if "console/python-console" not in list_console:
317
415
        list_console.append("console/python-console")
334
432
list_www = """)
335
433
        writelist_pretty(file, list_www)
336
434
        file.write("""
 
435
# List of all installable files in lib directory.
 
436
list_lib = """)
 
437
        writelist_pretty(file, list_lib)
 
438
        file.write("""
337
439
# List of all installable files in console directory.
338
440
list_console = """)
339
441
        writelist_pretty(file, list_console)
 
442
        file.write("""
 
443
# List of all installable files in subjects directory.
 
444
# This is to install sample subjects and material.
 
445
list_subjects = """)
 
446
        writelist_pretty(file, list_subjects)
 
447
        file.write("""
 
448
# List of all installable files in problems directory.
 
449
# This is to install sample exercise material.
 
450
list_problems = """)
 
451
        writelist_pretty(file, list_problems)
340
452
 
341
453
        file.close()
342
454
    except IOError, (errno, strerror):
353
465
 
354
466
    return 0
355
467
 
356
 
def build_list_py_files(dir):
 
468
def build_list_py_files(dir, no_top_level=False):
357
469
    """Builds a list of all py files found in a directory and its
358
 
    subdirectories. Returns this as a list of strings."""
 
470
    subdirectories. Returns this as a list of strings.
 
471
    no_top_level=True means the file paths will not include the top-level
 
472
    directory.
 
473
    """
359
474
    pylist = []
360
475
    for (dirpath, dirnames, filenames) in os.walk(dir):
361
476
        # Exclude directories beginning with a '.' (such as '.svn')
363
478
        # All *.py files are added to the list
364
479
        pylist += [os.path.join(dirpath, item) for item in filenames
365
480
            if mimetypes.guess_type(item)[0] in listmake_mimetypes]
 
481
    if no_top_level:
 
482
        for i in range(0, len(pylist)):
 
483
            _, pylist[i] = pylist[i].split(os.sep, 1)
366
484
    return pylist
367
485
 
368
486
def writelist_pretty(file, list):
376
494
        file.write(']\n')
377
495
 
378
496
def conf(args):
379
 
    global root_dir, ivle_install_dir, jail_base, allowed_uids
 
497
    global db_port
380
498
    # Set up some variables
381
499
 
382
500
    cwd = os.getcwd()
383
501
    # the files that will be created/overwritten
384
 
    conffile = os.path.join(cwd, "www/conf/conf.py")
 
502
    conffile = os.path.join(cwd, "lib/conf/conf.py")
385
503
    conf_hfile = os.path.join(cwd, "trampoline/conf.h")
386
504
 
387
505
    # Get command-line arguments to avoid asking questions.
388
506
 
389
 
    (opts, args) = getopt.gnu_getopt(args, "", ['root_dir=',
390
 
                    'ivle_install_dir=', 'jail_base=', 'allowed_uids='])
 
507
    optnames = []
 
508
    for opt in config_options:
 
509
        optnames.append(opt.option_name + "=")
 
510
    (opts, args) = getopt.gnu_getopt(args, "", optnames)
391
511
 
392
512
    if args != []:
393
513
        print >>sys.stderr, "Invalid arguments:", string.join(args, ' ')
409
529
        # If EOF is encountered at any time during the questioning, just exit
410
530
        # silently
411
531
 
412
 
        root_dir = query_user(root_dir,
413
 
        """Root directory where IVLE is located (in URL space):""")
414
 
        ivle_install_dir = query_user(ivle_install_dir,
415
 
        'Root directory where IVLE will be installed (on the local file '
416
 
        'system):')
417
 
        jail_base = query_user(jail_base,
418
 
        """Root directory where the jails (containing user files) are stored
419
 
(on the local file system):""")
420
 
        allowed_uids = query_user(allowed_uids,
421
 
        """UID of the web server process which will run IVLE.
422
 
Only this user may execute the trampoline. May specify multiple users as
423
 
a comma-separated list.
424
 
    (eg. "1002,78")""")
425
 
 
 
532
        for opt in config_options:
 
533
            globals()[opt.option_name] = \
 
534
                query_user(globals()[opt.option_name], opt.prompt)
426
535
    else:
427
536
        opts = dict(opts)
428
537
        # Non-interactive mode. Parse the options.
429
 
        if '--root_dir' in opts:
430
 
            root_dir = opts['--root_dir']
431
 
        if '--ivle_install_dir' in opts:
432
 
            ivle_install_dir = opts['--ivle_install_dir']
433
 
        if '--jail_base' in opts:
434
 
            jail_base = opts['--jail_base']
435
 
        if '--allowed_uids' in opts:
436
 
            allowed_uids = opts['--allowed_uids']
 
538
        for opt in config_options:
 
539
            if '--' + opt.option_name in opts:
 
540
                globals()[opt.option_name] = opts['--' + opt.option_name]
437
541
 
438
542
    # Error handling on input values
439
543
    try:
440
 
        allowed_uids = map(int, allowed_uids.split(','))
 
544
        allowed_uids_list = map(int, allowed_uids.split(','))
441
545
    except ValueError:
442
546
        print >>sys.stderr, (
443
547
        "Invalid UID list (%s).\n"
444
548
        "Must be a comma-separated list of integers." % allowed_uids)
445
549
        return 1
 
550
    try:
 
551
        db_port = int(db_port)
 
552
        if db_port < 0 or db_port >= 65536: raise ValueError()
 
553
    except ValueError:
 
554
        print >>sys.stderr, (
 
555
        "Invalid DB port (%s).\n"
 
556
        "Must be an integer between 0 and 65535." % repr(db_port))
 
557
        return 1
446
558
 
447
 
    # Write www/conf/conf.py
 
559
    # Write lib/conf/conf.py
448
560
 
449
561
    try:
450
562
        conf = open(conffile, "w")
453
565
# conf.py
454
566
# Miscellaneous application settings
455
567
 
456
 
 
457
 
# In URL space, where in the site is IVLE located. (All URLs will be prefixed
458
 
# with this).
459
 
# eg. "/" or "/ivle".
460
 
root_dir = "%s"
461
 
 
462
 
# In the local file system, where IVLE is actually installed.
463
 
# This directory should contain the "www" and "bin" directories.
464
 
ivle_install_dir = "%s"
465
 
 
466
 
# In the local file system, where are the student/user file spaces located.
467
 
# The user jails are expected to be located immediately in subdirectories of
468
 
# this location.
469
 
jail_base = "%s"
470
 
""" % (root_dir, ivle_install_dir, jail_base))
 
568
""")
 
569
        for opt in config_options:
 
570
            conf.write('%s\n%s = %s\n' % (opt.comment, opt.option_name,
 
571
                repr(globals()[opt.option_name])))
471
572
 
472
573
        conf.close()
473
574
    except IOError, (errno, strerror):
474
575
        print "IO error(%s): %s" % (errno, strerror)
475
576
        sys.exit(1)
476
577
 
477
 
    print "Successfully wrote www/conf/conf.py"
 
578
    print "Successfully wrote lib/conf/conf.py"
478
579
 
479
580
    # Write trampoline/conf.h
480
581
 
499
600
 * (Note that root is an implicit member of this list).
500
601
 */
501
602
static const int allowed_uids[] = { %s };
502
 
""" % (jail_base, repr(allowed_uids)[1:-1]))
 
603
""" % (jail_base, repr(allowed_uids_list)[1:-1]))
503
604
 
504
605
        conf.close()
505
606
    except IOError, (errno, strerror):
525
626
        print "Dry run (no actions will be executed\n"
526
627
 
527
628
    # Compile the trampoline
528
 
    action_runprog('gcc', ['-Wall', '-o', 'trampoline/trampoline',
529
 
        'trampoline/trampoline.c'], dry)
 
629
    curdir = os.getcwd()
 
630
    os.chdir('trampoline')
 
631
    action_runprog('make', [], dry)
 
632
    os.chdir(curdir)
530
633
 
531
634
    # Create the jail and its subdirectories
532
635
    # Note: Other subdirs will be made by copying files
566
669
 
567
670
def install(args):
568
671
    # Get "dry" and "nojail" variables from command line
569
 
    (opts, args) = getopt.gnu_getopt(args, "n", ['dry', 'nojail'])
 
672
    (opts, args) = getopt.gnu_getopt(args, "n",
 
673
        ['dry', 'nojail', 'nosubjects'])
570
674
    opts = dict(opts)
571
675
    dry = '-n' in opts or '--dry' in opts
572
676
    nojail = '--nojail' in opts
 
677
    nosubjects = '--nosubjects' in opts
573
678
 
574
679
    if dry:
575
680
        print "Dry run (no actions will be executed\n"
589
694
    # chown trampoline to root and set setuid bit
590
695
    action_chown_setuid(tramppath, dry)
591
696
 
592
 
    # Copy the www directory using the list
 
697
    # Copy the www and lib directories using the list
593
698
    action_copylist(install_list.list_www, ivle_install_dir, dry)
 
699
    action_copylist(install_list.list_lib, ivle_install_dir, dry)
594
700
 
595
701
    if not nojail:
596
702
        # Copy the local jail directory built by the build action
597
703
        # to the jails template directory (it will be used as a template
598
704
        # for all the students' jails).
599
705
        action_copytree('jail', os.path.join(jail_base, 'template'), dry)
 
706
    if not nosubjects:
 
707
        # Copy the subjects and problems directories across
 
708
        action_copylist(install_list.list_subjects, subjects_base, dry,
 
709
            srcdir="./subjects")
 
710
        action_copylist(install_list.list_problems, problems_base, dry,
 
711
            srcdir="./problems")
600
712
 
601
713
    # Append IVLE path to ivle.pth in python site packages
602
714
    # (Unless it's already there)
603
715
    ivle_pth = os.path.join(sys.prefix,
604
 
        "lib/python2.5/site-packages/ivle.pth")
 
716
        "lib/python%s/site-packages/ivle.pth" % PYTHON_VERSION)
605
717
    ivle_www = os.path.join(ivle_install_dir, "www")
 
718
    ivle_lib = os.path.join(ivle_install_dir, "lib")
606
719
    write_ivle_pth = True
 
720
    write_ivle_lib_pth = True
607
721
    try:
608
722
        file = open(ivle_pth, 'r')
609
723
        for line in file:
610
724
            if line.strip() == ivle_www:
611
725
                write_ivle_pth = False
612
 
                break
 
726
            elif line.strip() == ivle_lib:
 
727
                write_ivle_lib_pth = False
 
728
        file.close()
613
729
    except (IOError, OSError):
614
730
        pass
615
731
    if write_ivle_pth:
616
732
        action_append(ivle_pth, ivle_www)
 
733
    if write_ivle_lib_pth:
 
734
        action_append(ivle_pth, ivle_lib)
617
735
 
618
736
    return 0
619
737
 
732
850
    if dry: return
733
851
    common.makeuser.linktree(src, dst)
734
852
 
735
 
def action_copylist(srclist, dst, dry):
 
853
def action_copylist(srclist, dst, dry, srcdir="."):
736
854
    """Copies all files in a list to a new location. The files in the list
737
855
    are read relative to the current directory, and their destinations are the
738
856
    same paths relative to dst. Creates all parent directories as necessary.
 
857
    srcdir is "." by default, can be overridden.
739
858
    """
740
859
    for srcfile in srclist:
741
860
        dstfile = os.path.join(dst, srcfile)
 
861
        srcfile = os.path.join(srcdir, srcfile)
742
862
        dstdir = os.path.split(dstfile)[0]
743
863
        if not os.path.isdir(dstdir):
744
864
            action_mkdir(dstdir, dry)
753
873
def action_copyfile(src, dst, dry):
754
874
    """Copies one file to a new location. Creates all parent directories
755
875
    as necessary.
 
876
    Warn if file not found.
756
877
    """
757
878
    dstdir = os.path.split(dst)[0]
758
879
    if not os.path.isdir(dstdir):
762
883
        try:
763
884
            shutil.copyfile(src, dst)
764
885
            shutil.copymode(src, dst)
765
 
        except shutil.Error:
766
 
            pass
 
886
        except (shutil.Error, IOError), e:
 
887
            print "Warning: " + str(e)
767
888
 
768
889
def action_symlink(src, dst, dry):
769
890
    """Creates a symlink in a given location. Creates all parent directories
799
920
            | stat.S_ISUID | stat.S_IRUSR | stat.S_IWUSR)
800
921
 
801
922
def action_chmod_x(file, dry):
802
 
    """Chmod +xs a file (sets execute permission)."""
803
 
    print "chmod u+rwx", file
 
923
    """Chmod 755 a file (sets permissions to rwxr-xr-x)."""
 
924
    print "chmod 755", file
804
925
    if not dry:
805
 
        os.chmod(file, stat.S_IXUSR | stat.S_IRUSR | stat.S_IWUSR)
 
926
        os.chmod(file, stat.S_IXUSR | stat.S_IRUSR | stat.S_IWUSR
 
927
            | stat.S_IXGRP | stat.S_IRGRP | stat.S_IXOTH | stat.S_IROTH)
806
928
 
807
929
def query_user(default, prompt):
808
930
    """Prompts the user for a string, which is read from a line of stdin.