|
1 """distutils.command.build_scripts |
|
2 |
|
3 Implements the Distutils 'build_scripts' command.""" |
|
4 |
|
5 # This module should be kept compatible with Python 2.1. |
|
6 |
|
7 __revision__ = "$Id: build_scripts.py 61000 2008-02-23 17:40:11Z christian.heimes $" |
|
8 |
|
9 import os, re |
|
10 from stat import ST_MODE |
|
11 from distutils import sysconfig |
|
12 from distutils.core import Command |
|
13 from distutils.dep_util import newer |
|
14 from distutils.util import convert_path |
|
15 from distutils import log |
|
16 |
|
17 # check if Python is called on the first line with this expression |
|
18 first_line_re = re.compile('^#!.*python[0-9.]*([ \t].*)?$') |
|
19 |
|
20 class build_scripts (Command): |
|
21 |
|
22 description = "\"build\" scripts (copy and fixup #! line)" |
|
23 |
|
24 user_options = [ |
|
25 ('build-dir=', 'd', "directory to \"build\" (copy) to"), |
|
26 ('force', 'f', "forcibly build everything (ignore file timestamps"), |
|
27 ('executable=', 'e', "specify final destination interpreter path"), |
|
28 ] |
|
29 |
|
30 boolean_options = ['force'] |
|
31 |
|
32 |
|
33 def initialize_options (self): |
|
34 self.build_dir = None |
|
35 self.scripts = None |
|
36 self.force = None |
|
37 self.executable = None |
|
38 self.outfiles = None |
|
39 |
|
40 def finalize_options (self): |
|
41 self.set_undefined_options('build', |
|
42 ('build_scripts', 'build_dir'), |
|
43 ('force', 'force'), |
|
44 ('executable', 'executable')) |
|
45 self.scripts = self.distribution.scripts |
|
46 |
|
47 def get_source_files(self): |
|
48 return self.scripts |
|
49 |
|
50 def run (self): |
|
51 if not self.scripts: |
|
52 return |
|
53 self.copy_scripts() |
|
54 |
|
55 |
|
56 def copy_scripts (self): |
|
57 """Copy each script listed in 'self.scripts'; if it's marked as a |
|
58 Python script in the Unix way (first line matches 'first_line_re', |
|
59 ie. starts with "\#!" and contains "python"), then adjust the first |
|
60 line to refer to the current Python interpreter as we copy. |
|
61 """ |
|
62 self.mkpath(self.build_dir) |
|
63 outfiles = [] |
|
64 for script in self.scripts: |
|
65 adjust = 0 |
|
66 script = convert_path(script) |
|
67 outfile = os.path.join(self.build_dir, os.path.basename(script)) |
|
68 outfiles.append(outfile) |
|
69 |
|
70 if not self.force and not newer(script, outfile): |
|
71 log.debug("not copying %s (up-to-date)", script) |
|
72 continue |
|
73 |
|
74 # Always open the file, but ignore failures in dry-run mode -- |
|
75 # that way, we'll get accurate feedback if we can read the |
|
76 # script. |
|
77 try: |
|
78 f = open(script, "r") |
|
79 except IOError: |
|
80 if not self.dry_run: |
|
81 raise |
|
82 f = None |
|
83 else: |
|
84 first_line = f.readline() |
|
85 if not first_line: |
|
86 self.warn("%s is an empty file (skipping)" % script) |
|
87 continue |
|
88 |
|
89 match = first_line_re.match(first_line) |
|
90 if match: |
|
91 adjust = 1 |
|
92 post_interp = match.group(1) or '' |
|
93 |
|
94 if adjust: |
|
95 log.info("copying and adjusting %s -> %s", script, |
|
96 self.build_dir) |
|
97 if not self.dry_run: |
|
98 outf = open(outfile, "w") |
|
99 if not sysconfig.python_build: |
|
100 outf.write("#!%s%s\n" % |
|
101 (self.executable, |
|
102 post_interp)) |
|
103 else: |
|
104 outf.write("#!%s%s\n" % |
|
105 (os.path.join( |
|
106 sysconfig.get_config_var("BINDIR"), |
|
107 "python" + sysconfig.get_config_var("VERSION") |
|
108 + sysconfig.get_config_var("EXE")), |
|
109 post_interp)) |
|
110 outf.writelines(f.readlines()) |
|
111 outf.close() |
|
112 if f: |
|
113 f.close() |
|
114 else: |
|
115 if f: |
|
116 f.close() |
|
117 self.copy_file(script, outfile) |
|
118 |
|
119 if os.name == 'posix': |
|
120 for file in outfiles: |
|
121 if self.dry_run: |
|
122 log.info("changing mode of %s", file) |
|
123 else: |
|
124 oldmode = os.stat(file)[ST_MODE] & 07777 |
|
125 newmode = (oldmode | 0555) & 07777 |
|
126 if newmode != oldmode: |
|
127 log.info("changing mode of %s from %o to %o", |
|
128 file, oldmode, newmode) |
|
129 os.chmod(file, newmode) |
|
130 |
|
131 # copy_scripts () |
|
132 |
|
133 # class build_scripts |