Hi all -- as promised earlier in the week, I have completed the beginning steps along the road to a 'build_ext' command that will work under Unix. The legions of eager developers who watch every movement on the distutils-checkins list will know that I've just added two modules, ccompiler and unixccompiler, which provide the classes CCompiler and UnixCCompiler. The basic idea is this: CCompiler defines the interface to a generic C/C++ compiler, and UnixCCompiler implements an interface to the traditional Unix "cc -Dmacro -Iincludedir -Umacro -c foo.c -o foo.o" compiler invocation (and -l/-L linker invocation). So far all it does is generate and print out command lines, but that's enough to convince me that it works on my Linux/gcc system, i.e. it generates the command lines I intended it to generate, and gets the right preprocessor/compiler/linker flags from Python's Makefile (so that the basic compile/link steps are essentially the same as would be done by a Makefile.pre.in-generated Makefile). Please, take a look at the code. To encourage this, you'll find the bulk of ccompiler.py below: it's mostly comments and docstrings, since after all it mostly exists to define an interface. It's crucial that this interface be capable of what we need to build Python extensions on Unix, Windows, and Mac OS, and I'm relying on you folks to tell me what needs to be added to support Windows and Mac OS. (Well, if I missed something for Unix, be sure to tell me about that too. That's less likely, though, and I'll have a clue what you're talking about when politely inform me of my errors.) In particular: is this interface sufficient to handle Windows .def files? Is it enough for the weird case of using Oracle's libraries that Greg Stein mentioned (or other libraries with hairy interdependencies)? What Mac C compiler is supported, and is there a way to drive it programmatically? (Ie. is this even *possible* on the Mac?) Oh, if you're looking for some example code: see test/test_cc.py. Gives whatever CCompiler class applies on your platform a run for its money. (Currently only works when os.name == 'posix', since so far only UnixCCompiler is implemented.) Anyways, here's that hunk of ccompiler.py: class CCompiler: """Abstract base class to define the interface that must be implemented by real compiler abstraction classes. Might have some use as a place for shared code, but it's not yet clear what code can be shared between compiler abstraction models for different platforms. The basic idea behind a compiler abstraction class is that each instance can be used for all the compile/link steps in building a single project. Thus, attributes common to all of those compile and link steps -- include directories, macros to define, libraries to link against, etc. -- are attributes of the compiler instance. To allow for variability in how individual files are treated, most (all?) of those attributes may be varied on a per-compilation or per-link basis.""" # XXX things not handled by this compiler abstraction model: # * client can't provide additional options for a compiler, # e.g. warning, optimization, debugging flags. Perhaps this # should be the domain of concrete compiler abstraction classes # (UnixCCompiler, MSVCCompiler, etc.) -- or perhaps the base # class should have methods for the common ones. # * can't put output files (object files, libraries, whatever) # into a separate directory from their inputs. Should this be # handled by an 'output_dir' attribute of the whole object, or a # parameter to the compile/link_* methods, or both? # * can't completely override the include or library searchg # path, ie. no "cc -I -Idir1 -Idir2" or "cc -L -Ldir1 -Ldir2". # I'm not sure how widely supported this is even by POSIX # compilers, much less on other platforms. And I'm even less # sure how useful it is; probably for cross-compiling, but I # have no intention of supporting that. # * can't do really freaky things with the library list/library # dirs, e.g. "-Ldir1 -lfoo -Ldir2 -lfoo" to link against # different versions of libfoo.a in different locations. I # think this is useless without the ability to null out the # library search path anyways. # * don't deal with verbose and dry-run flags -- probably a # CCompiler object should just drag them around the way the # Distribution object does (either that or we have to drag # around a Distribution object, which is what Command objects # do... but might be kind of annoying) [...] # -- Bookkeeping methods ------------------------------------------- def define_macro (self, name, value=None): """Define a preprocessor macro for all compilations driven by this compiler object. The optional parameter 'value' should be a string; if it is not supplied, then the macro will be defined without an explicit value and the exact outcome depends on the compiler used (XXX true? does ANSI say anything about this?)""" def undefine_macro (self, name): """Undefine a preprocessor macro for all compilations driven by this compiler object. If the same macro is defined by 'define_macro()' and undefined by 'undefine_macro()' the last call takes precedence (including multiple redefinitions or undefinitions). If the macro is redefined/undefined on a per-compilation basis (ie. in the call to 'compile()'), then that takes precedence.""" def add_include_dir (self, dir): """Add 'dir' to the list of directories that will be searched for header files. The compiler is instructed to search directories in the order in which they are supplied by successive calls to 'add_include_dir()'.""" def set_include_dirs (self, dirs): """Set the list of directories that will be searched to 'dirs' (a list of strings). Overrides any preceding calls to 'add_include_dir()'; subsequence calls to 'add_include_dir()' add to the list passed to 'set_include_dirs()'. This does not affect any list of standard include directories that the compiler may search by default.""" def add_library (self, libname): """Add 'libname' to the list of libraries that will be included in all links driven by this compiler object. Note that 'libname' should *not* be the name of a file containing a library, but the name of the library itself: the actual filename will be inferred by the linker, the compiler, or the compiler abstraction class (depending on the platform). The linker will be instructed to link against libraries in the order they were supplied to 'add_library()' and/or 'set_libraries()'. It is perfectly valid to duplicate library names; the linker will be instructed to link against libraries as many times as they are mentioned.""" def set_libraries (self, libnames): """Set the list of libraries to be included in all links driven by this compiler object to 'libnames' (a list of strings). This does not affect any standard system libraries that the linker may include by default.""" def add_library_dir (self, dir): """Add 'dir' to the list of directories that will be searched for libraries specified to 'add_library()' and 'set_libraries()'. The linker will be instructed to search for libraries in the order they are supplied to 'add_library_dir()' and/or 'set_library_dirs()'.""" def set_library_dirs (self, dirs): """Set the list of library search directories to 'dirs' (a list of strings). This does not affect any standard library search path that the linker may search by default.""" def add_link_object (self, object): """Add 'object' to the list of object files (or analogues, such as explictly named library files or the output of "resource compilers") to be included in every link driven by this compiler object.""" def set_link_objects (self, objects): """Set the list of object files (or analogues) to be included in every link to 'objects'. This does not affect any standard object files that the linker may include by default (such as system libraries).""" # -- Worker methods ------------------------------------------------ # (must be implemented by subclasses) def compile (self, sources, macros=None, includes=None): """Compile one or more C/C++ source files. 'sources' must be a list of strings, each one the name of a C/C++ source file. Return a list of the object filenames generated (one for each source filename in 'sources'). 'macros', if given, must be a list of macro definitions. A macro definition is either a (name, value) 2-tuple or a (name,) 1-tuple. The former defines a macro; if the value is None, the macro is defined without an explicit value. The 1-tuple case undefines a macro. Later definitions/redefinitions/ undefinitions take precedence. 'includes', if given, must be a list of strings, the directories to add to the default include file search path for this compilation only.""" pass # XXX this is kind of useless without 'link_binary()' or # 'link_executable()' or something -- or maybe 'link_static_lib()' # should not exist at all, and we just have 'link_binary()'? def link_static_lib (self, objects, output_libname, libraries=None, library_dirs=None): """Link a bunch of stuff together to create a static library file. The "bunch of stuff" consists of the list of object files supplied as 'objects', the extra object files supplied to 'add_link_object()' and/or 'set_link_objects()', the libraries supplied to 'add_library()' and/or 'set_libraries()', and the libraries supplied as 'libraries' (if any). 'output_libname' should be a library name, not a filename; the filename will be inferred from the library name. 'library_dirs', if supplied, should be a list of additional directories to search on top of the system default and those supplied to 'add_library_dir()' and/or 'set_library_dirs()'.""" pass # XXX what's better/more consistent/more universally understood # terminology: "shared library" or "dynamic library"? def link_shared_lib (self, objects, output_libname, libraries=None, library_dirs=None): """Link a bunch of stuff together to create a shared library file. Has the same effect as 'link_static_lib()' except that the filename inferred from 'output_libname' will most likely be different, and the type of file generated will almost certainly be different.""" pass def link_shared_object (self, objects, output_filename, libraries=None, library_dirs=None): """Link a bunch of stuff together to create a shared object file. Much like 'link_shared_lib()', except the output filename is explicitly supplied as 'output_filename'.""" pass # class CCompiler Hope you enjoyed that as much as I did. ;-) Greg -- Greg Ward - software developer gward@cnri.reston.va.us Corporation for National Research Initiatives 1895 Preston White Drive voice: +1-703-620-8990 Reston, Virginia, USA 20191-5434 fax: +1-703-620-0913
[ccompiler.py] Looks nice. Somethings that might be of use for the build process writer: · a way to test compiler features a la configure, e.g. a simple way to pass a small C program and evaluate the compiler error message (error or no error should suffice) · a way to test the availability of (shared) libs in much the same way, e.g. try to link an empty object file against a set of given libs to see if the linker finds the libs or not · a way to access the compiler/linker name and version Apart from this, I would also need a standard way to figure out the platform. os.uname() does help a bit, but it usually does not include e.g. the Linux distribution name or libc version. Is there a way to add APIs for these to the distutils ? [I need the distribution name to be able to preset paths to libs.] If these things are already included in the distutils please ignore this message: I'm only following this list every now and then... you're doing a great job, BTW :-) Cheers, -- Marc-Andre Lemburg ______________________________________________________________________ Y2000: 174 days left Business: http://www.lemburg.com/ Python Pages: http://www.lemburg.com/python/
On Sat, 10 Jul 1999 11:09:53 +0200, "M.-A. Lemburg"
[ccompiler.py]
Looks nice. Somethings that might be of use for the build process writer:
· a way to test compiler features a la configure, e.g. a simple way to pass a small C program and evaluate the compiler error message (error or no error should suffice)
· a way to test the availability of (shared) libs in much the same way, e.g. try to link an empty object file against a set of given libs to see if the linker finds the libs or not
· a way to access the compiler/linker name and version
This sounds a lot like GNU autoconf so why we are not using it?
I agree that some things are still missing from autoconf, things like how to
build shared libraries and dynamically loadable object files, but these could
be defined for each system in a configuration file, perhaps written in Python.
Look for example how libtool works, it's very simple to write a new makefile.
Or take a look at the GNUstep's makefile package, is a combination of autoconf
and makefiles, although it requires GNUmake (see http://www.gnustep.org).
The advantage of using autoconf is that we use something that already works
very well with C code, so building C extensions is very simple. Plus developes
are used to how autoconf/configure works. Not to mention we don't have to
rewrite what configure does in Python, there are lots of ugly details we have
to figure out in order for this to work really well.
In my view, I see autoconf plus some configuration files that define the flags
used for building shared libraries (Python files, makefiles, shell scripts,
doesn't matter) as being the right tools for building C extensions. For
installing Python files, a simple Python tool that checks for package
dependencies and installs the files in the appropriate places should be enough.
Am I missing something?
Best regards,
--
Ovidiu Predescu
Ovidiu Predescu writes:
This sounds a lot like GNU autoconf so why we are not using it?
Yes, it does.
Am I missing something?
Macintosh, Windows, and other operating systems. We cannot assume
that GNUish tools are installed on those systems.
For a number of people, there's a licensing issue with GNUish tools
as well.
-Fred
--
Fred L. Drake, Jr.
On Mon, 12 Jul 1999 13:45:08 -0400 (EDT), "Fred L. Drake"
Am I missing something?
Macintosh, Windows, and other operating systems. We cannot assume that GNUish tools are installed on those systems.
The way I saw things happening with other free software packages is to provide already configured files for Windows and Mac OS. You simple don't run the configure tools at all but come up with some assumptions on those platforms.
For a number of people, there's a licensing issue with GNUish tools as well.
The output generated by autoconf, aka the configure script, is not covered by
GPL, so there's no licensing issue here. Only the autoconf package itself is
covered by GPL, but not its result.The autoconf manual also specifies this very
clear:
<quote>
What are the restrictions on distributing `configure'
scripts that Autoconf generates? How does that affect my
programs that use them?
There are no restrictions on how the configuration scripts that
Autoconf produces may be distributed or used. In Autoconf version 1,
they were covered by the GNU General Public License. We still
encourage software authors to distribute their work under terms like
those of the GPL, but doing so is not required to use Autoconf.
Of the other files that might be used with `configure',
`config.h.in' is under whatever copyright you use for your
`configure.in', since it is derived from that file and from the public
domain file `acconfig.h'. `config.sub' and `config.guess' have an
exception to the GPL when they are used with an Autoconf-generated
`configure' script, which permits you to distribute them under the same
terms as the rest of your package. `install-sh' is from the X
Consortium and is not copyrighted.
</quote>
Using autoconf does not present any problem since we are to distribute the
generated configure script and not the autoconf package itself. And all the
extension packages could use a configure script even if the licensing of the
package is not under GPL or LGPL.
Greetings,
--
Ovidiu Predescu
Ovidiu Predescu writes:
The way I saw things happening with other free software packages is to provide already configured files for Windows and Mac OS. You simple don't run the configure tools at all but come up with some assumptions on those platforms.
For a finished, end-user package, this is true. Based on the developer's day discussion at IPC7, distutils will also be used to help the developers of those packages. Many authors are not able to provide binary distributions for all platforms. The ideal would be for me to put together a source distribution as a distutils-based package; people with access to various platforms can pull that down, build any platform-dependent components, and then create an installable package for others to use. Hopefully this can be reduced to one or two commands. There are also people who will want to build from sources regardless of platform; having a Python-only system makes this a lot easier; only the compiler issues become platform-dependent, and that can be isolated within the distutils package.
The output generated by autoconf, aka the configure script, is not covered by GPL, so there's no licensing issue here. Only the autoconf package itself is covered by GPL, but not its result.The autoconf manual also specifies this very
I agree. This issue is not entirely a matter of legal
interpretation, unfortunately; some organizations will (reportedly; I
don't know of any documented cases) fire people for installing
un-approved software, and the GPL or GNU label can make corporate
software managers very leery. Whether or not it should is *not* the
issue.
Having the system entirely in Python also helps when creating
packages on non-Unix systems; autoconf may not do the right thing on a
Macintosh!
-Fred
--
Fred L. Drake, Jr.
[Charset iso-8859-1 unsupported, filtering to ASCII...]
On Sat, 10 Jul 1999 11:09:53 +0200, "M.-A. Lemburg"
wrote: [ccompiler.py]
Looks nice. Somethings that might be of use for the build process writer:
_ a way to test compiler features a la configure, e.g. a simple way to pass a small C program and evaluate the compiler error message (error or no error should suffice)
_ a way to test the availability of (shared) libs in much the same way, e.g. try to link an empty object file against a set of given libs to see if the linker finds the libs or not
_ a way to access the compiler/linker name and version
This sounds a lot like GNU autoconf so why we are not using it?
I agree that some things are still missing from autoconf, things like how to build shared libraries and dynamically loadable object files, but these could be defined for each system in a configuration file, perhaps written in Python. Look for example how libtool works, it's very simple to write a new makefile. Or take a look at the GNUstep's makefile package, is a combination of autoconf and makefiles, although it requires GNUmake (see http://www.gnustep.org).
The advantage of using autoconf is that we use something that already works very well with C code, so building C extensions is very simple. Plus developes are used to how autoconf/configure works. Not to mention we don't have to rewrite what configure does in Python, there are lots of ugly details we have to figure out in order for this to work really well.
In my view, I see autoconf plus some configuration files that define the flags used for building shared libraries (Python files, makefiles, shell scripts, doesn't matter) as being the right tools for building C extensions. For installing Python files, a simple Python tool that checks for package dependencies and installs the files in the appropriate places should be enough.
Am I missing something?
Please read the charter and requirements doc for the Distutils-SIG. You will find that the tools being discussed and developed are for the developers, not necessarily for the installers/end-users. Autoconf is a tool for the developer, but does not test anything.. it generates a ./configure script, for UNIX-based systems only. Autoconf requires m4 to be compiled and understood. M4 is very nice, but it is a language that not everyone can understand easily. Asking everyone to learn autoconf and m4 is as bad as the Perl crew requiring that Perl/C extensions be written in XS. So the point of the Distutils SIG was to develop some standards and some (Python or C based) tools to aid the aspiring module developers out there in creating easily packagable distributions. Autoconf doesn't cut the mustard (tho I like it :), especially for non-UNIX developing. -Arcege -- ------------------------------------------------------------------------ | Michael P. Reilly, Release Engineer | Email: arcege@shore.net | | Salem, Mass. USA 01970 | | ------------------------------------------------------------------------
On Mon, 12 Jul 1999 14:39:36 -0400 (EDT), "Fred L. Drake"
Ovidiu Predescu writes:
The way I saw things happening with other free software packages is to provide already configured files for Windows and Mac OS. You simple don't run the configure tools at all but come up with some assumptions on those platforms.
For a finished, end-user package, this is true. Based on the developer's day discussion at IPC7, distutils will also be used to help the developers of those packages. Many authors are not able to provide binary distributions for all platforms. The ideal would be for me to put together a source distribution as a distutils-based package; people with access to various platforms can pull that down, build any platform-dependent components, and then create an installable package for others to use. Hopefully this can be reduced to one or two commands. There are also people who will want to build from sources regardless of platform; having a Python-only system makes this a lot easier; only the compiler issues become platform-dependent, and that can be isolated within the distutils package.
OK, I think I can understand that. What I'm saying is that there are two different things: - a tool to help the compilation of C extensions - a tool to install a binary package on a Python distribution The distutils package could help both developers and packagers accomplish the second thing. The autoconf package however helps both crowds deal with platform dependent issues. There are too many details we need to take care of that has already been taken care of in autoconf. There is a lot of work we need to put in figuring out all the things that autoconf solves, header files, libraries, behaviors of various function libraries and system calls. All these may be important for a C extension that does heavy use them. In my mind a combination of these two tools is the best. Maybe a Python configure tool would have its merits, however I would take a pragmatic approach. I think we first need to focus on a packaging tool and then on a distribution site for Python packages (sort of CPAN) and then worry about the rest.
The output generated by autoconf, aka the configure script, is not covered by GPL, so there's no licensing issue here. Only the autoconf package itself is covered by GPL, but not its result.The autoconf manual also specifies this very
I agree. This issue is not entirely a matter of legal interpretation, unfortunately; some organizations will (reportedly; I don't know of any documented cases) fire people for installing un-approved software, and the GPL or GNU label can make corporate software managers very leery. Whether or not it should is *not* the issue.
I wouldn't worry about such people. I think these days there are less people that think like this. In the future, with big companies like HP, SGI, Sun and others actively supporting free software there will be even less people thinking like that.
Having the system entirely in Python also helps when creating packages on non-Unix systems; autoconf may not do the right thing on a Macintosh!
As I pointed out above, we are talking about two different things. The
packaging is one thing while identifying system specific issues is a different
one. I don't argue about building a Python packaging tool, I think this is a
very good thing, just about the merits of creating a new tool for
determining system specific things.
Greetings,
--
Ovidiu Predescu
On Mon, 12 Jul 1999 14:56:21 -0400 (EDT), "Michael P. Reilly"
Please read the charter and requirements doc for the Distutils-SIG. You will find that the tools being discussed and developed are for the developers, not necessarily for the installers/end-users.
Autoconf is a tool for the developer, but does not test anything.. it generates a ./configure script, for UNIX-based systems only.
Autoconf requires m4 to be compiled and understood. M4 is very nice, but it is a language that not everyone can understand easily. Asking everyone to learn autoconf and m4 is as bad as the Perl crew requiring that Perl/C extensions be written in XS.
So the point of the Distutils SIG was to develop some standards and some (Python or C based) tools to aid the aspiring module developers out there in creating easily packagable distributions. Autoconf doesn't cut the mustard (tho I like it :), especially for non-UNIX developing.
Yes, I read the requirements doc for this SIG when I joined it, that's exactly
why I joined this SIG. I need a tool that helps me package the Python code that
I'm writing.
I used autoconf to write the configuration process for all the free or
proprietary software that I wrote. I see no problem in learning how to write a
configure.in script. m4 and autoconf are small little tools that are very easy
to learn compared to the amount of software an experienced developer has to
learn.
As I already wrote in a previous post, I don't argue about the utility of a
Python packager tool but about how useful would be to work on a configuration
tool right now.
Some of the extensions that I wrote use configure to check for header files and
library versions. These things are very well done by autoconf, and my point is
that we probably don't need another tool to do this. What we need though is
standard way to integrate configure in the process of building a package. In a
normal C package that uses configure, this is done by running configure and
then make. The question is how configure could be integrated with the distutils
setup.py script? How are the configure options passed to it and what is the
order of execution, first configure and then setup.py? Just wondering if this
is reasonable...
Greetings,
--
Ovidiu Predescu
Ovidiu Predescu wrote:
As I already wrote in a previous post, I don't argue about the utility of a Python packager tool but about how useful would be to work on a configuration tool right now.
Some of the extensions that I wrote use configure to check for header files and library versions. These things are very well done by autoconf, and my point is that we probably don't need another tool to do this. What we need though is standard way to integrate configure in the process of building a package. In a normal C package that uses configure, this is done by running configure and then make. The question is how configure could be integrated with the distutils setup.py script? How are the configure options passed to it and what is the order of execution, first configure and then setup.py? Just wondering if this is reasonable...
Well, you are certainly right in saying that autoconf already handles what I proposed in an earlier mail. OTOH, the distutils package is intended to be cross-platform and the autoconf output does not run on non-Unix platforms. Given that the compiler interface provides an abstract interface to the compiler (+ the linker) and its installation, it only seems natural to use the interface to "try out some things" much like the generated configure scripts do. I don't want to reinvent a wheel here: most config setups are very primitive and don't need the full-blown autoconf mechanisms. Some more methods on the compiler interface would enable this for those who want to use the feature, something like .testcompile(program_string[,options]) .testcompileandlink(program_string[,options,libs]) which return 1/0 to state "success" / "failure" and implement proper cleanup of the intermediate files. Very simple, really no need to argue here, IMHO... Cheers, -- Marc-Andre Lemburg ______________________________________________________________________ Y2000: 169 days left Business: http://www.lemburg.com/ Python Pages: http://www.lemburg.com/python/
participants (5)
-
Fred L. Drake
-
gward@cnri.reston.va.us
-
M.-A. Lemburg
-
Michael P. Reilly
-
Ovidiu Predescu