[Numpy-svn] r4634 - in branches/distutils_scons_command/numpy/distutils: . command
numpy-svn at scipy.org
numpy-svn at scipy.org
Sat Dec 22 04:00:09 EST 2007
Author: cdavid
Date: 2007-12-22 02:59:58 -0600 (Sat, 22 Dec 2007)
New Revision: 4634
Added:
branches/distutils_scons_command/numpy/distutils/command/scons.py
Modified:
branches/distutils_scons_command/numpy/distutils/core.py
Log:
Add scons command to numpy.distutils
Added: branches/distutils_scons_command/numpy/distutils/command/scons.py
===================================================================
--- branches/distutils_scons_command/numpy/distutils/command/scons.py 2007-12-22 08:44:52 UTC (rev 4633)
+++ branches/distutils_scons_command/numpy/distutils/command/scons.py 2007-12-22 08:59:58 UTC (rev 4634)
@@ -0,0 +1,322 @@
+import os
+import os.path
+from os.path import join as pjoin, dirname as pdirname
+
+from distutils.errors import DistutilsPlatformError
+from distutils.errors import DistutilsExecError, DistutilsSetupError
+
+from numpy.distutils.command.build_ext import build_ext as old_build_ext
+from numpy.distutils.ccompiler import CCompiler
+from numpy.distutils.fcompiler import FCompiler
+from numpy.distutils.exec_command import find_executable
+from numpy.distutils import log
+from numpy.distutils.misc_util import get_numpy_include_dirs
+
+def get_scons_build_dir():
+ """Return the top path where everything produced by scons will be put.
+
+ The path is relative to the top setup.py"""
+ return os.path.join('build', 'scons')
+
+def get_scons_configres_dir():
+ """Return the top path where everything produced by scons will be put.
+
+ The path is relative to the top setup.py"""
+ return os.path.join('build', 'scons-configres')
+
+def get_scons_configres_filename():
+ """Return the top path where everything produced by scons will be put.
+
+ The path is relative to the top setup.py"""
+ return '__configres.py'
+
+def get_scons_local_path():
+ """This returns the full path where scons.py for scons-local is located."""
+ import numpy.distutils
+ return pjoin(pdirname(numpy.distutils.__file__), 'scons-local')
+
+def get_python_exec_invoc():
+ """This returns the python executable from which this file is invocated."""
+ # Do we need to take into account the PYTHONPATH, in a cross platform way,
+ # that is the string returned can be executed directly on supported
+ # platforms, and the sys.path of the executed python should be the same
+ # than the caller ? This may not be necessary, since os.system is said to
+ # take into accound os.environ. This actually also works for my way of
+ # using "local python", using the alias facility of bash.
+ import sys
+ return sys.executable
+
+def dirl_to_str(dirlist):
+ """Given a list of directories, returns a string where the paths are
+ concatenated by the path separator.
+
+ example: ['foo/bar', 'bar/foo'] will return 'foo/bar:bar/foo'."""
+ return os.pathsep.join(dirlist)
+
+def dist2sconscc(compiler):
+ """This converts the name passed to distutils to scons name convention (C
+ compiler). compiler should be a CCompiler instance.
+
+ Example:
+ --compiler=intel -> intelc"""
+ compiler_type = compiler.compiler_type
+ if compiler_type == 'msvc':
+ return 'msvc'
+ elif compiler_type == 'intel':
+ return 'intelc'
+ elif compiler_type == 'mingw32':
+ return 'mingw'
+ else:
+ return compiler.compiler[0]
+
+def dist2sconsfc(compiler):
+ """This converts the name passed to distutils to scons name convention
+ (Fortran compiler). The argument should be a FCompiler instance.
+
+ Example:
+ --fcompiler=intel -> ifort on linux, ifl on windows"""
+ if compiler.compiler_type == 'intel':
+ #raise NotImplementedError('FIXME: intel fortran compiler name ?')
+ return 'ifort'
+ elif compiler.compiler_type == 'gnu':
+ return 'g77'
+ elif compiler.compiler_type == 'gnu95':
+ return 'gfortran'
+ else:
+ # XXX: Just give up for now, and use generic fortran compiler
+ return 'fortran'
+
+def dist2sconscxx(compiler):
+ """This converts the name passed to distutils to scons name convention
+ (C++ compiler). The argument should be a Compiler instance."""
+ if compiler.compiler_type == 'gnu':
+ return 'g++'
+ else:
+ return 'c++'
+ return compiler.compiler_cxx[0]
+
+def get_compiler_executable(compiler):
+ """For any give CCompiler instance, this gives us the name of C compiler
+ (the actual executable).
+
+ NOTE: does NOT work with FCompiler instances."""
+ # Geez, why does distutils has no common way to get the compiler name...
+ if compiler.compiler_type == 'msvc':
+ # this is harcoded in distutils... A bit cleaner way would be to
+ # initialize the compiler instance and then get compiler.cc, but this
+ # may be costly: we really just want a string.
+ # XXX: we need to initialize the compiler anyway, so do not use
+ # hardcoded string
+ #compiler.initialize()
+ #print compiler.cc
+ return 'cl.exe'
+ else:
+ return compiler.compiler[0]
+
+def get_f77_compiler_executable(compiler):
+ """For any give FCompiler instance, this gives us the name of F77 compiler
+ (the actual executable)."""
+ return compiler.compiler_f77[0]
+
+def get_cxxcompiler_executable(compiler):
+ """For any give CCompiler instance, this gives us the name of CXX compiler
+ (the actual executable).
+
+ NOTE: does NOT work with FCompiler instances."""
+ # Geez, why does distutils has no common way to get the compiler name...
+ if compiler.compiler_type == 'msvc':
+ # this is harcoded in distutils... A bit cleaner way would be to
+ # initialize the compiler instance and then get compiler.cc, but this
+ # may be costly: we really just want a string.
+ # XXX: we need to initialize the compiler anyway, so do not use
+ # hardcoded string
+ #compiler.initialize()
+ #print compiler.cc
+ return 'cl.exe'
+ else:
+ return compiler.compiler_cxx[0]
+
+def get_tool_path(compiler):
+ """Given a distutils.ccompiler.CCompiler class, returns the path of the
+ toolset related to C compilation."""
+ fullpath_exec = find_executable(get_compiler_executable(compiler))
+ if fullpath_exec:
+ fullpath = pdirname(fullpath_exec)
+ else:
+ raise DistutilsSetupError("Could not find compiler executable info for scons")
+ return fullpath
+
+def get_f77_tool_path(compiler):
+ """Given a distutils.ccompiler.FCompiler class, returns the path of the
+ toolset related to F77 compilation."""
+ fullpath_exec = find_executable(get_f77_compiler_executable(compiler))
+ if fullpath_exec:
+ fullpath = pdirname(fullpath_exec)
+ else:
+ raise DistutilsSetupError("Could not find F77 compiler executable "\
+ "info for scons")
+ return fullpath
+
+def get_cxx_tool_path(compiler):
+ """Given a distutils.ccompiler.CCompiler class, returns the path of the
+ toolset related to C compilation."""
+ fullpath_exec = find_executable(get_cxxcompiler_executable(compiler))
+ if fullpath_exec:
+ fullpath = pdirname(fullpath_exec)
+ else:
+ raise DistutilsSetupError("Could not find compiler executable info for scons")
+ return fullpath
+
+def protect_path(path):
+ """Convert path (given as a string) to something the shell will have no
+ problem to understand (space, etc... problems)."""
+ # XXX: to this correctly, this is totally bogus for now (does not check for
+ # already quoted path, for example).
+ return '"' + path + '"'
+
+class scons(old_build_ext):
+ # XXX: add an option to the scons command for configuration (auto/force/cache).
+ description = "Scons builder"
+ user_options = old_build_ext.user_options + \
+ [('jobs=', None,
+ "specify number of worker threads when executing scons"),
+ ('scons-tool-path=', None, 'specify additional path '\
+ '(absolute) to look for scons tools'),
+ ('silent=', None, 'specify whether scons output should be silent '\
+ '(1), super silent (2) or not (0, default)')]
+
+ def initialize_options(self):
+ old_build_ext.initialize_options(self)
+ self.jobs = None
+ self.silent = 0
+ self.scons_tool_path = ''
+ # If true, we bypass distutils to find the c compiler altogether. This
+ # is to be used in desperate cases (like incompatible visual studio
+ # version).
+ self._bypass_distutils_cc = False
+ self.scons_compiler = None
+ self.scons_compiler_path = None
+ self.scons_fcompiler = None
+
+ def finalize_options(self):
+ old_build_ext.finalize_options(self)
+ if self.distribution.has_scons_scripts():
+ self.sconscripts = self.distribution.get_scons_scripts()
+ self.pre_hooks = self.distribution.get_scons_pre_hooks()
+ self.post_hooks = self.distribution.get_scons_post_hooks()
+ self.pkg_names = self.distribution.get_scons_parent_names()
+ else:
+ self.sconscripts = []
+ self.pre_hooks = []
+ self.post_hooks = []
+ self.pkg_names = []
+
+ # Try to get the same compiler than the ones used by distutils: this is
+ # non trivial because distutils and scons have totally different
+ # conventions on this one (distutils uses PATH from user's environment,
+ # whereas scons uses standard locations). The way we do it is once we
+ # got the c compiler used, we use numpy.distutils function to get the
+ # full path, and add the path to the env['PATH'] variable in env
+ # instance (this is done in numpy.distutils.scons module).
+
+ # XXX: The logic to bypass distutils is ... not so logic.
+ compiler_type = self.compiler
+ if compiler_type == 'msvc':
+ self._bypass_distutils_cc = True
+ from numpy.distutils.ccompiler import new_compiler
+ try:
+ distutils_compiler = new_compiler(compiler=compiler_type,
+ verbose=self.verbose,
+ dry_run=self.dry_run,
+ force=self.force)
+ distutils_compiler.customize(self.distribution)
+ # This initialization seems necessary, sometimes, for find_executable to work...
+ if hasattr(distutils_compiler, 'initialize'):
+ distutils_compiler.initialize()
+ self.scons_compiler = dist2sconscc(distutils_compiler)
+ self.scons_compiler_path = protect_path(get_tool_path(distutils_compiler))
+ except DistutilsPlatformError, e:
+ if not self._bypass_distutils_cc:
+ raise e
+ else:
+ self.scons_compiler = compiler_type
+
+ # We do the same for the fortran compiler ...
+ fcompiler_type = self.fcompiler
+ from numpy.distutils.fcompiler import new_fcompiler
+ self.fcompiler = new_fcompiler(compiler = fcompiler_type,
+ verbose = self.verbose,
+ dry_run = self.dry_run,
+ force = self.force)
+ if self.fcompiler is not None:
+ self.fcompiler.customize(self.distribution)
+
+ # And the C++ compiler
+ cxxcompiler = new_compiler(compiler = compiler_type,
+ verbose = self.verbose,
+ dry_run = self.dry_run,
+ force = self.force)
+ if cxxcompiler is not None:
+ cxxcompiler.customize(self.distribution, need_cxx = 1)
+ cxxcompiler.customize_cmd(self)
+ self.cxxcompiler = cxxcompiler.cxx_compiler()
+ #print self.cxxcompiler.compiler_cxx[0]
+
+ def run(self):
+ # XXX: when a scons script is missing, scons only prints warnings, and
+ # does not return a failure (status is 0). We have to detect this from
+ # distutils (this cannot work for recursive scons builds...)
+
+ # XXX: passing everything at command line may cause some trouble where
+ # there is a size limitation ? What is the standard solution in thise
+ # case ?
+
+ scons_exec = get_python_exec_invoc()
+ scons_exec += ' ' + protect_path(pjoin(get_scons_local_path(), 'scons.py'))
+
+ for sconscript, pre_hook, post_hook, pkg_name in zip(self.sconscripts,
+ self.pre_hooks, self.post_hooks,
+ self.pkg_names):
+ if pre_hook:
+ pre_hook()
+
+ cmd = [scons_exec, "-f", sconscript, '-I.']
+ if self.jobs:
+ cmd.append(" --jobs=%d" % int(self.jobs))
+ cmd.append('scons_tool_path="%s"' % self.scons_tool_path)
+ cmd.append('src_dir="%s"' % pdirname(sconscript))
+ cmd.append('pkg_name="%s"' % pkg_name)
+ #cmd.append('distutils_libdir=%s' % protect_path(pjoin(self.build_lib,
+ # pdirname(sconscript))))
+ cmd.append('distutils_libdir=%s' % protect_path(pjoin(self.build_lib)))
+
+ if not self._bypass_distutils_cc:
+ cmd.append('cc_opt=%s' % self.scons_compiler)
+ cmd.append('cc_opt_path=%s' % self.scons_compiler_path)
+ else:
+ cmd.append('cc_opt=%s' % self.scons_compiler)
+
+
+ if self.fcompiler:
+ cmd.append('f77_opt=%s' % dist2sconsfc(self.fcompiler))
+ cmd.append('f77_opt_path=%s' % protect_path(get_f77_tool_path(self.fcompiler)))
+
+ if self.cxxcompiler:
+ cmd.append('cxx_opt=%s' % dist2sconscxx(self.cxxcompiler))
+ cmd.append('cxx_opt_path=%s' % protect_path(get_cxx_tool_path(self.cxxcompiler)))
+
+ cmd.append('include_bootstrap=%s' % dirl_to_str(get_numpy_include_dirs()))
+ if self.silent:
+ if int(self.silent) == 1:
+ cmd.append('-Q')
+ elif int(self.silent) == 2:
+ cmd.append('-s')
+ cmdstr = ' '.join(cmd)
+ log.info("Executing scons command: %s ", cmdstr)
+ st = os.system(cmdstr)
+ if st:
+ print "status is %d" % st
+ raise DistutilsExecError("Error while executing scons command "\
+ "%s (see above)" % cmdstr)
+ if post_hook:
+ post_hook()
Modified: branches/distutils_scons_command/numpy/distutils/core.py
===================================================================
--- branches/distutils_scons_command/numpy/distutils/core.py 2007-12-22 08:44:52 UTC (rev 4633)
+++ branches/distutils_scons_command/numpy/distutils/core.py 2007-12-22 08:59:58 UTC (rev 4634)
@@ -24,7 +24,7 @@
from numpy.distutils.numpy_distribution import NumpyDistribution
from numpy.distutils.command import config, config_compiler, \
build, build_py, build_ext, build_clib, build_src, build_scripts, \
- sdist, install_data, install_headers, install, bdist_rpm
+ sdist, install_data, install_headers, install, bdist_rpm, scons
from numpy.distutils.misc_util import get_data_files, is_sequence, is_string
numpy_cmdclass = {'build': build.build,
@@ -37,6 +37,7 @@
'build_py': build_py.build_py,
'build_clib': build_clib.build_clib,
'sdist': sdist.sdist,
+ 'scons': scons.scons,
'install_data': install_data.install_data,
'install_headers': install_headers.install_headers,
'install': install.install,
More information about the Numpy-svn
mailing list