Re: [Distutils] distutils & Cygwin compiler on Win32

Greg Ward wrote:
Second, the patch which provides the help-compiler option (and help-format, help-formats whatever you need.) This is against the cvs from last monday.
Oh good.
I used the current cvs to create an up-to-date patch. It only introduces this help-option option, nothing else. ( Did you have a problem with bdist, I found the current cvs wasn't working, in the run method you wrote self.format which I had to change to format. ) file: help-option.patch
Finally, while I was working on this help patch, I found that everyone who creates a new compiler has to change ccompiler.py, which contains the base class for all other compilers. Wouldn't it be better, if we had a separate file for the compiler mapping, so no-one ever has to touch our base file?
I'm not convinced. If we actually had to change the CCompiler class, you'd have a point -- that would be a sign of inadequate design. But we just have to change the tables that drive the factory function, and those tables and the factory really belong in the same module as the base class. What's the big deal with tweaking that file occasionally?
You could introduce some other things like building the mapping table at runtime, so you only can choose compilers which are really available on your machine. I think such things don't belong in ccompiler.py, it would be to platform-specific. Look at this piece of code to understand what I mean. ----------------------------------- +# Map a platform ('posix', 'nt') to the default compiler type for +# that platform. +default_compiler = { 'posix': 'unix', + 'nt': 'msvc', + } + +# Map compiler types to (module_name, class_name) pairs -- ie. where to +# find the code that implements an interface to this compiler. (The module +# is assumed to be in the 'distutils' package.) +compiler_class = { + # standard for all platforms + 'unix': ('unixccompiler', 'UnixCCompiler',"standard UNIX-style compiler"), + } + +# on NT we have some compilers more +if os.name=='nt': + compiler_class.update({ + 'msvc': ('msvccompiler', 'MSVCCompiler',"Microsoft Visual C++"), + 'cygwin': ('cygwinccompiler', 'CygwinCCompiler',"Cygwin-Gnu-Win32-C-Compiler"), + 'mingw32': ('cygwinccompiler', 'Mingw32CCompiler',"MinGW32-C-Compiler (or cygwin in this mode)"), + }) + +# One could also check if the compiler is really available +# for example: (doesn't really work, because msvc's find_exe return always a name) +# import msvccompiler +# if msvccompiler.find_exe(...): +# compiler_class.update( ... msvc ... ) + ----------------------------------------- (Perhaps, we could divide it in ccompiler.py which contains only the class CCompiler and a file compiler_util.py or compiler_factory.py? for the rest.?)
And now some words to your second email.
- constructing a fake Makefile fragment to set sysconfig globals is excessively baroque: why not do it like this:
(I took it directly from a older version of cygwinccompiler.py)
g['CC'] = "cc" # not gcc?
I used 'cc' because it is the standard name for a C compiler. I overwrite UnixCCompilers self.cc to change it to 'gcc'. I think as a default assignment it should be set to the standard name 'cc'. (There are more free compilers for Windows out there, what they call their compiler? cc?)
>>>>>>>>>>>>>>>>>>>>>>>
* I'd rather do nothing about guessing the user's home directory on Windows -- my innocent question started a surprisingly lively thread on the topic in python-dev, and it seems possible that some sort of "userdir" module will wind up in Python 1.6. I'd rather use whatever code comes out of that discussion. However, it does sound reasonable to respect os.environ['HOME'], in which case Mac OS will be the odd man out in 'find_config_files()' instead of Unix. That way, people who really care about having a personal directory on Windows can communicate it to Distutils unambigiously through HOME -- at least until someone figures out the "right way" to find a home directory on Windows. <<<<<<<<<<<<<<<<<<<<<<<<<<<<< Ok, they could set HOME in their login scripts.
>>>>>>>>>>>>>>>>>>>>>>>
Can you redo your modification of dist.py to unify the "posix" and "nt" branches of 'find_config_files()', and drop the HOME-setting code in util.py? Thanks. <<<<<<<<<<<<<<<<<<<<<<<<<<<<< Ok. file: dist_home.patch (It contains only this patch.)
Finally, what's your thought on simplifying the CCompiler API and relying more on instance attributes? I'd rather do this now so you and Lyle Johnson (who's doing the Borland C++ port) have to adapt your code before I check it in. >evil smirk< Any feelings?
It sounds good, but if you subclass a compiler class, as I do, only to (ex)change some parameters for the call of its base class method, then you probably had to save instance variables, call the base class method and then restore the instance variables. (Ok, for my class this isn't a big problem, because I could set most changes in __init__(). But it is worth to think about, which parameters are really constant for all cases.) If you have a new version of UnixCCompiler, send me an email and I will change my classes, too. (I will send you then the complete file, not an patch.)
I also found some problems with building extensions using my classes. I tried to build a more complex extension (pygtk-0.6.5). This works without problems if you use msvc, but the resulting dll crashs if I use cygwin or mingw32, it is probably a problem with initialization of the dll's. If someone else has more experience with cygwin/mingw32, he could find out if there is a problem with my link parameters in the compiler classes.
I've just gone over your patch, and I think I'd like it better if you'd "cvs update" and regenerate it. There's at least one bug that we both fixed, so there may be other conflicts -- and those are easier to deal with using "cvs" than using "patch". ;-)
Most of it is either already checked in or attached to this mail. The rest concerns only my cygwinccompiler.py file, and I will send it as file not as patch. (later this week)
I found some problems with sdist. If you add a new package in setup.py (I mean one more entry in the packages list), then it is not included in the resulting source distribution. I think this happens because sdist doesn't check the date of MANIFEST against the date of setup.py (or however this file is called, __file__ would give you the name.) I will see if I can change this. Another problem: If I have an empty package (which contains only an empty __init__.py file, file length=0), then it is included in the zip-file, but you can't find it in the tar.gz-file. Is this a problem of tar and it is worth to be treated specially? (makes it ever sense to have such a module)
Kind regards
Rene Liebscher
diff -BurN --minimal --exclude=*.pyc distutils.orig/distutils/dist.py distutils.patched/distutils/dist.py --- distutils.orig/distutils/dist.py Tue Jun 6 11:17:02 2000 +++ distutils.patched/distutils/dist.py Tue Jun 6 12:47:42 2000 @@ -263,24 +263,25 @@ and setup.cfg in the current directory. """ files = [] - if os.name == "posix": - check_environ() + check_environ()
- sys_dir = os.path.dirname(sys.modules['distutils'].__file__) - sys_file = os.path.join(sys_dir, "pydistutils.cfg") - if os.path.isfile(sys_file): - files.append(sys_file) + if os.name=='posix': + sys_dir = os.path.dirname(sys.modules['distutils'].__file__) + user_filename = ".pydistutils.cfg" + else: + sys_dir = sysconfig.PREFIX + user_filename = "pydistutils.cfg" + + sys_file = os.path.join(sys_dir, "pydistutils.cfg") + if os.path.isfile(sys_file): + files.append(sys_file)
- user_file = os.path.join(os.environ.get('HOME'), - ".pydistutils.cfg") + if os.environ.has_key('HOME'): + user_file = os.path.join(os.environ.get('HOME'), + user_filename) if os.path.isfile(user_file): files.append(user_file)
- else: - sys_file = os.path.join (sysconfig.PREFIX, "pydistutils.cfg") - if os.path.isfile(sys_file): - files.append(sys_file) - # All platforms support local setup.cfg local_file = "setup.cfg" if os.path.isfile(local_file):
diff -BurN --minimal --exclude=*.pyc distutils.orig/distutils/archive_util.py distutils.patched/distutils/archive_util.py --- distutils.orig/distutils/archive_util.py Tue Jun 6 11:17:01 2000 +++ distutils.patched/distutils/archive_util.py Tue Jun 6 11:20:24 2000 @@ -110,11 +110,11 @@
ARCHIVE_FORMATS = { - 'gztar': (make_tarball, [('compress', 'gzip')]), - 'bztar': (make_tarball, [('compress', 'bzip2')]), - 'ztar': (make_tarball, [('compress', 'compress')]), - 'tar': (make_tarball, [('compress', None)]), - 'zip': (make_zipfile, []) + 'gztar': (make_tarball, [('compress', 'gzip')],"gzipped tar-file"), + 'bztar': (make_tarball, [('compress', 'bzip2')],"bzip2-ed tar-file"), + 'ztar': (make_tarball, [('compress', 'compress')],"compressed tar-file"), + 'tar': (make_tarball, [('compress', None)],"uncompressed tar-file"), + 'zip': (make_zipfile, [],"zip-file") }
def check_archive_formats (formats): diff -BurN --minimal --exclude=*.pyc distutils.orig/distutils/ccompiler.py distutils.patched/distutils/ccompiler.py --- distutils.orig/distutils/ccompiler.py Tue Jun 6 11:17:02 2000 +++ distutils.patched/distutils/ccompiler.py Tue Jun 6 11:22:15 2000 @@ -726,10 +726,22 @@ # Map compiler types to (module_name, class_name) pairs -- ie. where to # find the code that implements an interface to this compiler. (The module # is assumed to be in the 'distutils' package.) -compiler_class = { 'unix': ('unixccompiler', 'UnixCCompiler'), - 'msvc': ('msvccompiler', 'MSVCCompiler'), +compiler_class = { 'unix': ('unixccompiler', 'UnixCCompiler',"standard UNIX-style compiler"), + 'msvc': ('msvccompiler', 'MSVCCompiler',"Microsoft Visual C++"), + 'cygwin': ('cygwinccompiler', 'CygwinCCompiler',"Cygwin-Gnu-Win32-C-Compiler"), + 'mingw32': ('cygwinccompiler', 'Mingw32CCompiler',"MinGW32-C-Compiler (or cygwin in this mode)"), }
+# prints all possible arguments to --compiler +def show_compilers(): + from distutils.fancy_getopt import FancyGetopt + list_of_compilers=[] + for compiler in compiler_class.keys(): + list_of_compilers.append(("compiler="+compiler,None,compiler_class[compiler][2])) + list_of_compilers.sort() + pretty_printer=FancyGetopt(list_of_compilers) + pretty_printer.print_help("List of available compilers:") +
def new_compiler (plat=None, compiler=None, @@ -755,7 +767,7 @@ if compiler is None: compiler = default_compiler[plat]
- (module_name, class_name) = compiler_class[compiler] + (module_name, class_name,long_description) = compiler_class[compiler] except KeyError: msg = "don't know how to compile C/C++ code on platform '%s'" % plat if compiler is not None: diff -BurN --minimal --exclude=*.pyc distutils.orig/distutils/command/bdist.py distutils.patched/distutils/command/bdist.py --- distutils.orig/distutils/command/bdist.py Tue Jun 6 11:17:03 2000 +++ distutils.patched/distutils/command/bdist.py Tue Jun 6 11:35:12 2000 @@ -21,8 +21,7 @@ user_options = [('bdist-base=', 'b', "temporary directory for creating built distributions"), ('formats=', None, - "formats for distribution " + - "(gztar, bztar, zip, rpm, ... )"), + "formats for distribution"), ]
# The following commands do not take a format option from bdist @@ -33,12 +32,28 @@ default_format = { 'posix': 'gztar', 'nt': 'zip', }
- format_command = { 'gztar': 'bdist_dumb', - 'bztar': 'bdist_dumb', - 'ztar': 'bdist_dumb', - 'tar': 'bdist_dumb', - 'rpm': 'bdist_rpm', - 'zip': 'bdist_dumb', } + format_command = { 'gztar': ('bdist_dumb',"gzipped tar-file"), + 'bztar': ('bdist_dumb',"bzip2-ed tar-file"), + 'ztar': ('bdist_dumb',"compressed tar-file"), + 'tar': ('bdist_dumb',"tar-file"), + 'rpm': ('bdist_rpm',"rpm distribution"), + 'zip': ('bdist_dumb',"zip-file"), + } + + # prints all possible arguments to --format + def show_formats(): + from distutils.fancy_getopt import FancyGetopt + list_of_formats=[] + for format in bdist.format_command.keys(): + list_of_formats.append(("formats="+format,None,bdist.format_command[format][1])) + list_of_formats.sort() + pretty_printer=FancyGetopt(list_of_formats) + pretty_printer.print_help("List of available distribution formats:") + + help_options = [ + ('help-formats', None, + "lists available distribution formats",show_formats), + ]
def initialize_options (self): @@ -74,14 +89,14 @@ for format in self.formats:
try: - cmd_name = self.format_command[self.format] + cmd_name = self.format_command[format][0] except KeyError: raise DistutilsOptionError, \ - "invalid format '%s'" % self.format + "invalid format '%s'" % format
sub_cmd = self.reinitialize_command(cmd_name) if cmd_name not in self.no_format_option: - sub_cmd.format = self.format + sub_cmd.format = format self.run_command (cmd_name)
# run() diff -BurN --minimal --exclude=*.pyc distutils.orig/distutils/command/build.py distutils.patched/distutils/command/build.py --- distutils.orig/distutils/command/build.py Mon May 29 10:26:26 2000 +++ distutils.patched/distutils/command/build.py Tue Jun 6 11:20:24 2000 @@ -9,6 +9,7 @@ import sys, os from distutils.core import Command from distutils.util import get_platform +from distutils.ccompiler import show_compilers
class build (Command):
@@ -35,6 +36,10 @@ ('force', 'f', "forcibly build everything (ignore file timestamps)"), ] + help_options = [ + ('help-compiler', None, + "lists available compilers",show_compilers), + ]
def initialize_options (self): self.build_base = 'build' diff -BurN --minimal --exclude=*.pyc distutils.orig/distutils/command/build_clib.py distutils.patched/distutils/command/build_clib.py --- distutils.orig/distutils/command/build_clib.py Thu May 25 03:10:04 2000 +++ distutils.patched/distutils/command/build_clib.py Tue Jun 6 11:20:24 2000 @@ -23,7 +23,7 @@ from types import * from distutils.core import Command from distutils.errors import * -from distutils.ccompiler import new_compiler +from distutils.ccompiler import new_compiler,show_compilers
class build_clib (Command): @@ -42,6 +42,10 @@ ('compiler=', 'c', "specify the compiler type"), ] + help_options = [ + ('help-compiler', None, + "lists available compilers",show_compilers), + ]
def initialize_options (self): self.build_clib = None diff -BurN --minimal --exclude=*.pyc distutils.orig/distutils/command/build_ext.py distutils.patched/distutils/command/build_ext.py --- distutils.orig/distutils/command/build_ext.py Tue Jun 6 11:17:04 2000 +++ distutils.patched/distutils/command/build_ext.py Tue Jun 6 11:25:34 2000 @@ -14,6 +14,7 @@ from distutils.errors import * from distutils.dep_util import newer_group from distutils.extension import Extension +from distutils.ccompiler import show_compilers
# An extension name is just a dot-separated list of Python NAMEs (ie. # the same as a fully-qualified module name). @@ -72,6 +73,10 @@ ('compiler=', 'c', "specify the compiler type"), ] + help_options = [ + ('help-compiler', None, + "lists available compilers",show_compilers), + ]
def initialize_options (self): diff -BurN --minimal --exclude=*.pyc distutils.orig/distutils/command/sdist.py distutils.patched/distutils/command/sdist.py --- distutils.orig/distutils/command/sdist.py Tue Jun 6 11:17:06 2000 +++ distutils.patched/distutils/command/sdist.py Tue Jun 6 11:20:24 2000 @@ -13,7 +13,7 @@ from distutils.core import Command from distutils.util import newer, create_tree, remove_tree, convert_path, \ write_file -from distutils.archive_util import check_archive_formats +from distutils.archive_util import check_archive_formats,ARCHIVE_FORMATS from distutils.text_file import TextFile from distutils.errors import DistutilsExecError, DistutilsOptionError
@@ -35,11 +35,26 @@ ('force-manifest', 'f', "forcibly regenerate the manifest and carry on as usual"), ('formats=', None, - "formats for source distribution (tar, ztar, gztar, bztar, or zip)"), + "formats for source distribution"), ('keep-tree', 'k', "keep the distribution tree around after creating " + "archive file(s)"), ] + # prints all possible arguments to --formats + def show_formats(): + from distutils.fancy_getopt import FancyGetopt + list_of_formats=[] + for format in ARCHIVE_FORMATS.keys(): + list_of_formats.append(("formats="+format,None,ARCHIVE_FORMATS[format][2])) + list_of_formats.sort() + pretty_printer=FancyGetopt(list_of_formats) + pretty_printer.print_help("List of available distribution formats:") + + help_options = [ + ('help-formats', None, + "lists available distribution formats",show_formats), + ] + negative_opts = {'use-defaults': 'no-defaults'}
default_format = { 'posix': 'gztar', diff -BurN --minimal --exclude=*.pyc distutils.orig/distutils/dist.py distutils.patched/distutils/dist.py --- distutils.orig/distutils/dist.py Tue Jun 6 11:17:02 2000 +++ distutils.patched/distutils/dist.py Tue Jun 6 11:20:24 2000 @@ -433,16 +433,38 @@ negative_opt = copy (negative_opt) negative_opt.update (cmd_class.negative_opt)
+ # Check for help_options in command class + # They have a different format (tuple of four) so we need to preprocess them here + help_options = [] + if hasattr(cmd_class,"help_options") and type (cmd_class.help_options) is ListType: + help_options = map(lambda x:(x[0],x[1],x[2]),cmd_class.help_options) + # All commands support the global options too, just by adding # in 'global_options'. parser.set_option_table (self.global_options + - cmd_class.user_options) + cmd_class.user_options + help_options) parser.set_negative_aliases (negative_opt) (args, opts) = parser.getopt (args[1:]) if hasattr(opts, 'help') and opts.help: self._show_help(parser, display_options=0, commands=[cmd_class]) return
+ if hasattr(cmd_class,"help_options") and type (cmd_class.help_options) is ListType: + help_option_found=0 + for help_option in cmd_class.help_options: + if hasattr(opts, parser.get_attr_name(help_option[0])): + help_option_found=1 + #print "showing help for option %s of command %s" % (help_option[0],cmd_class) + if callable(help_option[3]): + help_option[3]() + else: + raise DistutilsClassError, \ + ("command class %s must provide " + + "a callable object for help_option '%s'") % \ + (cmd_class,help_option[0]) + if help_option_found: + return + # Put the options from the command-line into their official # holding pen, the 'command_options' dictionary. opt_dict = self.get_option_dict(command) @@ -492,7 +514,11 @@ klass = command else: klass = self.get_command_class (command) - parser.set_option_table (klass.user_options) + if hasattr(klass,"help_options") and type (klass.help_options) is ListType: + parser.set_option_table (klass.user_options+ + map(lambda x:(x[0],x[1],x[2]),klass.help_options)) + else: + parser.set_option_table (klass.user_options) parser.print_help ("Options for '%s' command:" % klass.__name__) print
@@ -500,7 +526,7 @@ return
# _show_help () - +
def handle_display_options (self, option_order): """If there were any non-global "display-only" options
participants (1)
-
Rene Liebscher