Greg Ward wrote:
On 26 May 2000, M.-A. Lemburg said:
Hmm, the pre/post-install hooks are definitely an install thing.
Again, there are two meanings to install: install from source and install from a built distribution. Hooks at install-from-source time should be doable with the Distutils' existing extension mechanism (if a bit cumbersome -- you have to know how to write a Distutils command class, which is a tad idiosyncratic).
I was referring to installing a (pre)built binary -- just before copying the compiled files to their final install location and right after that step is done. "install-from-source" would execute these hooks too: right after having built the binaries.
We'd also need a pre-build, though, for the things I mentioned below, e.g. finding header files and libs, checking the compiler, etc.
In principle, that should all be doable with the Distutils' existing facilities. I'd be willing to grease the wheels by adding a standard "prebuild" or "configure" command that runs before "build", but I'd leave it empty and open to subclassing -- there's just too many things that you might want to do there!
Ok... if you beat me to it, I'll do some subclassing then ;-)
So eg. you might do this in your setup script:
class configure (Command): user_options = [('foo-inc', None, ("where to search for foo headers"), ('foo-lib', None, "where to search for foo library"), ]
def initialize_options (self): self.foo_inc = None self.foo_lib = None
def finalize_options (self): # if user doesn't define foo_inc and foo_lib, leave them # alone (we will search for foo.h and libfoo) pass
def run (self): if self.foo_inc is None: for dir in (...): # list would vary by platform # try to compile "#include <foo.h>" with -Idir # break if success else: # die, couldn't find foo.h
# similar loop, trying to link with -lfoo and -Ldir
and then a little later: setup (..., cmdclass = {'configure': configure}, ..., )
Looks feasable :-)
Now, what the hell do we do with 'foo_inc' and 'foo_lib' -- as written, the 'configure' command finds the foo header and library paths, and then quits without doing anything with that information.
If you happen to know the guts of the Distutils, you could be evil and sneaky and do something like this:
build_ext_opts = self.distribution.get_option_dict('build_ext') include_dirs = build_ext_opts.get('include_dirs') if include_dirs is None: include_dirs = build_ext_opts['include_dirs'] = [] include_dirs.append(self.foo_inc)
...and then similar code to modify build_ext's 'library_dirs' option from 'self.foo_lib'.
This is nasty, though. Come to think of it, it's not entirely reliable: if the "build_ext" command object has already been created by the time we run "configure", then it's too late to go frobbing the option dict owned by the Distribution object -- you'd want to frob the "build_ext" object directly.
Well, it's a common idiom to *fetch* options from another command object. And, oh yeah, I decided many months ago to stick with this "pull" model -- if command X needs option Y from command Z, then it's X's responsibility to dig up a Z object and get attribute Y from it. Just search the code for 'find_peer' to see how often this happens. Eg. in bdist.py: build_base = self.find_peer('build').build_base to find the build base directory.
Wouldn't a method interface be more reliable and provide better means of extension using subclassing ? I usually wrap these attributes in .get_foobar(), .set_foobar() methods -- this also makes it clear which attributes are read-only, read-write or "better don't touch" :-)
But there's no way the general-purpose "build" command can know what's defined in your particular "configure" command -- so this is one place where we seem to need to support "pushing" options. The problem with pushing options from one command to another is that option initialization is *tricky*, because we need to be able to derive default values in an intelligent way. See build_ext.py for a rich, meaty, but comprehensible example; or install.py for an insanely complex example.
I think the difficulty of pushing options boils down to the fact that 'finalize_options()' only expects to be called once, and most commands are written in such a way that they die horribly if it is called more than once. (I have accidentally ventured into option-pushing territory once or twice in the past, and quickly retreated, licking my wounds.) This is a design/implementation flaw that I have lived with up to now, but I might not be able to any longer.
Now do you see why I have avoided a "configure" command? ;-)
Ehm, yes... but I can't really follow here: ok, I don't know much about the internals of distutils, but wouldn't passing a (more-or-less) intelligent context object around solve the problem ? The context object would know which parts are readable, changeable or write-once, etc. (I've been doing this in an 55k LOC application server and it works great.)
Other little things...
Why not add some keywords to the constructor ?!
import mx.ODBC.Misc.DistSupport setup( preinstall = mx.ODBC.Misc.DistSupport.preinstall, postinstall = ...postinstall, prebuild = ...prebuild, postbuild = ...postbuild )
I realize that the OO write-your-own-class alternative is a little more clunky, but I don't think it's clunky enough to mandate a function-passing interface. Can you buy that?
Ok.
Naa... no need to rewrite Autoconf in Python: the simple tests can easily be done using a few lines of Python provided that the compiler classes allow these trial-and-error approaches.
You may be right, based on the above hypothetical configure command. Abstracting some of these common functions away shouldn't be too hard.
Basically, I need: rc = compile("""#include "foobar.h"\nmain(){}""", output="delete.me") if rc != 0: HAVE_FOOBAR_H = 0 else: HAVE_FOOBAR_H = 1 os.unlink("delete.me") and sometimes: rc = compile("""#include "foobar.h" main() { int x = frobnicate(); exit(x); }""", output="run-and-then-delete.me") if rc != 0: HAVE_FROBNICATE = 0 else: HAVE_FROBNICATE = 1 # Run and get rc = os.system("run-and-then-delete.me") FROBINATE_VALUE = rc os.unlink("run-and-then-delete.me") -- Marc-Andre Lemburg ______________________________________________________________________ Business: http://www.lemburg.com/ Python Pages: http://www.lemburg.com/python/