[Distutils] another build_scripts patch
Greg Ward
gward@python.net
Wed, 24 May 2000 22:17:51 -0400
On 24 May 2000, Bastian Kleineidam said:
> Next try with the build_scripts patch. Again the changes:
OK, I've checked it in. A few minor comments (I'm checking these
changes in too, so howl if you don't like 'em and "cvs up" if you do.)
> - install_misc class is commented out because its not needed any more
You forgot to include this in the patch, but that's OK because that's
not what I meant! I just wanted a comment next to "install_misc" to
remind us that it's not (currently) used. I'll do it myself.
> data_files = ['doc/info.txt', # copy this file in install_scripts dir
> ('testdata', ['a.dat', 'b.dat']), # copy these files in
> # install_scripts/testdata
> ('/etc', ['packagerc']), # copy this in /etc. When --root is
> # given, copy this in rootdir/etc
> ]
Perfect! (Assuming it works. ;-)
> --- distutils.orig/distutils/command/__init__.py Sat May 13 03:48:15 2000
> +++ distutils.patched/distutils/command/__init__.py Wed May 24 10:11:54 2000
[...]
> @@ -99,5 +104,8 @@
> # into the build tree
> if self.distribution.has_ext_modules():
> self.run_peer ('build_ext')
> +
> + if self.distribution.scripts:
> + self.run_peer ('build_scripts')
This should use the 'has_scripts()' method of Distribution -- fixed.
> +# check if Python is called on the first line with this expression
> +first_line_re = re.compile(r"^#!.+python(\s-\w+)*")
I think we can get away with a looser regex: I've changed this to
r'^#!.*python(\s+.*)?'. Reasons:
* I seem to recall having seen "#!python" notation somewhere -- it's
a reasonable way to say, "Hey, <installation tool of choice>! Fix
this up for me at install time!"
* Why assume/impose a particular syntax on Python's command line?
let's just preserve any old args on the #! line.
> + def _copy_files(self):
> + """Copy all the scripts to the build dir"""
[...]
> + def _adjust_files(self):
> + """If the first line begins with #! and ends with python
> + replace it with the current python interpreter"""
[...]
This strikes me as a rather wasteful implementation: in the worst case,
we copy every file twice. I've merged them into the following method;
it works for me, please look it over and try it out (from CVS -- a few
other tweaks to build_scripts.py were needed to make this work):
def copy_scripts (self):
"""Copy each script listed in 'self.scripts'; if it's marked as a
Python script in the Unix way (first line matches 'first_line_re',
ie. starts with "\#!" and contains "python"), then adjust the first
line to refer to the current Python intepreter as we copy.
"""
outfiles = []
self.mkpath(self.build_dir)
for script in self.scripts:
adjust = 0
outfile = os.path.join(self.build_dir, script)
if not self.force and not newer(script, outfile):
self.announce("not copying %s (output up-to-date)" % script)
continue
# Always open the file, but ignore failures in dry-run mode --
# that way, we'll get accurate feedback if we can read the
# script.
try:
f = open(script, "r")
except IOError:
if not self.dry_run:
raise
f = None
else:
first_line = f.readline()
if not first_line:
self.warn("%s is an empty file (skipping)" % script)
continue
match = first_line_re.match(first_line)
if match:
adjust = 1
post_interp = match.group(1)
if adjust:
self.announce("copying and adjusting %s -> %s" %
(script, self.build_dir))
if not self.dry_run:
outf = open(outfile, "w")
outf.write("#!%s%s\n" %
(os.path.normpath(sys.executable), post_interp))
outf.writelines(f.readlines())
outf.close()
if f:
f.close()
else:
f.close()
self.copy_file(script, outfile)
# copy_scripts ()
> --- distutils.orig/distutils/command/install_data.py Sat May 13 05:09:50 2000
> +++ distutils.patched/distutils/command/install_data.py Wed May 24 10:41:51 2000
[...]
> def run (self):
> - self._copy_files(self.distribution.data_files)
> + self.mkpath(self.install_dir)
> + for f in self.data_files:
> + if type(f) == StringType:
> + # its a simple file, so copy it
> + self.copy_file(f, self.install_dir)
> + else:
> + # its a tuple with path to install to and a list of files
> + dir = f[0]
> + if not os.path.isabs(dir):
> + dir = os.path.join(self.install_dir, dir)
> + elif self.root:
> + dir = os.path.join(self.root, dir[1:])
> + self.mkpath(dir)
> + for data in f[1]:
> + self.copy_file(data, dir)
This looks unportable, in particular the 'dir[1:]' business. Should use
the 'distutils.util.change_root()' function, which handles non-Unix
platforms by blowing up with "I don't know how to do this!" error.
(Which is really a function of me not having sat down and carefully
thought through what it means to "change the root" of an MS-DOS
filename: the presence of drive letters complicates matters.)
Greg
--
Greg Ward - just another /P(erl|ython)/ hacker gward@python.net
http://starship.python.net/~gward/
I'm a GENIUS! I want to dispute sentence structure with SUSAN SONTAG!!