Chris@87: """ Modified version of build_clib that handles fortran source files. Chris@87: """ Chris@87: from __future__ import division, absolute_import, print_function Chris@87: Chris@87: import os Chris@87: from glob import glob Chris@87: import shutil Chris@87: from distutils.command.build_clib import build_clib as old_build_clib Chris@87: from distutils.errors import DistutilsSetupError, DistutilsError, \ Chris@87: DistutilsFileError Chris@87: Chris@87: from numpy.distutils import log Chris@87: from distutils.dep_util import newer_group Chris@87: from numpy.distutils.misc_util import filter_sources, has_f_sources,\ Chris@87: has_cxx_sources, all_strings, get_lib_source_files, is_sequence, \ Chris@87: get_numpy_include_dirs Chris@87: Chris@87: # Fix Python distutils bug sf #1718574: Chris@87: _l = old_build_clib.user_options Chris@87: for _i in range(len(_l)): Chris@87: if _l[_i][0] in ['build-clib', 'build-temp']: Chris@87: _l[_i] = (_l[_i][0]+'=',)+_l[_i][1:] Chris@87: # Chris@87: Chris@87: class build_clib(old_build_clib): Chris@87: Chris@87: description = "build C/C++/F libraries used by Python extensions" Chris@87: Chris@87: user_options = old_build_clib.user_options + [ Chris@87: ('fcompiler=', None, Chris@87: "specify the Fortran compiler type"), Chris@87: ('inplace', 'i', 'Build in-place'), Chris@87: ] Chris@87: Chris@87: boolean_options = old_build_clib.boolean_options + ['inplace'] Chris@87: Chris@87: def initialize_options(self): Chris@87: old_build_clib.initialize_options(self) Chris@87: self.fcompiler = None Chris@87: self.inplace = 0 Chris@87: return Chris@87: Chris@87: def have_f_sources(self): Chris@87: for (lib_name, build_info) in self.libraries: Chris@87: if has_f_sources(build_info.get('sources', [])): Chris@87: return True Chris@87: return False Chris@87: Chris@87: def have_cxx_sources(self): Chris@87: for (lib_name, build_info) in self.libraries: Chris@87: if has_cxx_sources(build_info.get('sources', [])): Chris@87: return True Chris@87: return False Chris@87: Chris@87: def run(self): Chris@87: if not self.libraries: Chris@87: return Chris@87: Chris@87: # Make sure that library sources are complete. Chris@87: languages = [] Chris@87: Chris@87: # Make sure that extension sources are complete. Chris@87: self.run_command('build_src') Chris@87: Chris@87: for (lib_name, build_info) in self.libraries: Chris@87: l = build_info.get('language', None) Chris@87: if l and l not in languages: languages.append(l) Chris@87: Chris@87: from distutils.ccompiler import new_compiler Chris@87: self.compiler = new_compiler(compiler=self.compiler, Chris@87: dry_run=self.dry_run, Chris@87: force=self.force) Chris@87: self.compiler.customize(self.distribution, Chris@87: need_cxx=self.have_cxx_sources()) Chris@87: Chris@87: libraries = self.libraries Chris@87: self.libraries = None Chris@87: self.compiler.customize_cmd(self) Chris@87: self.libraries = libraries Chris@87: Chris@87: self.compiler.show_customization() Chris@87: Chris@87: if self.have_f_sources(): Chris@87: from numpy.distutils.fcompiler import new_fcompiler Chris@87: self._f_compiler = new_fcompiler(compiler=self.fcompiler, Chris@87: verbose=self.verbose, Chris@87: dry_run=self.dry_run, Chris@87: force=self.force, Chris@87: requiref90='f90' in languages, Chris@87: c_compiler=self.compiler) Chris@87: if self._f_compiler is not None: Chris@87: self._f_compiler.customize(self.distribution) Chris@87: Chris@87: libraries = self.libraries Chris@87: self.libraries = None Chris@87: self._f_compiler.customize_cmd(self) Chris@87: self.libraries = libraries Chris@87: Chris@87: self._f_compiler.show_customization() Chris@87: else: Chris@87: self._f_compiler = None Chris@87: Chris@87: self.build_libraries(self.libraries) Chris@87: Chris@87: if self.inplace: Chris@87: for l in self.distribution.installed_libraries: Chris@87: libname = self.compiler.library_filename(l.name) Chris@87: source = os.path.join(self.build_clib, libname) Chris@87: target = os.path.join(l.target_dir, libname) Chris@87: self.mkpath(l.target_dir) Chris@87: shutil.copy(source, target) Chris@87: Chris@87: def get_source_files(self): Chris@87: self.check_library_list(self.libraries) Chris@87: filenames = [] Chris@87: for lib in self.libraries: Chris@87: filenames.extend(get_lib_source_files(lib)) Chris@87: return filenames Chris@87: Chris@87: def build_libraries(self, libraries): Chris@87: for (lib_name, build_info) in libraries: Chris@87: self.build_a_library(build_info, lib_name, libraries) Chris@87: Chris@87: def build_a_library(self, build_info, lib_name, libraries): Chris@87: # default compilers Chris@87: compiler = self.compiler Chris@87: fcompiler = self._f_compiler Chris@87: Chris@87: sources = build_info.get('sources') Chris@87: if sources is None or not is_sequence(sources): Chris@87: raise DistutilsSetupError(("in 'libraries' option (library '%s'), " + Chris@87: "'sources' must be present and must be " + Chris@87: "a list of source filenames") % lib_name) Chris@87: sources = list(sources) Chris@87: Chris@87: c_sources, cxx_sources, f_sources, fmodule_sources \ Chris@87: = filter_sources(sources) Chris@87: requiref90 = not not fmodule_sources or \ Chris@87: build_info.get('language', 'c')=='f90' Chris@87: Chris@87: # save source type information so that build_ext can use it. Chris@87: source_languages = [] Chris@87: if c_sources: source_languages.append('c') Chris@87: if cxx_sources: source_languages.append('c++') Chris@87: if requiref90: source_languages.append('f90') Chris@87: elif f_sources: source_languages.append('f77') Chris@87: build_info['source_languages'] = source_languages Chris@87: Chris@87: lib_file = compiler.library_filename(lib_name, Chris@87: output_dir=self.build_clib) Chris@87: depends = sources + build_info.get('depends', []) Chris@87: if not (self.force or newer_group(depends, lib_file, 'newer')): Chris@87: log.debug("skipping '%s' library (up-to-date)", lib_name) Chris@87: return Chris@87: else: Chris@87: log.info("building '%s' library", lib_name) Chris@87: Chris@87: config_fc = build_info.get('config_fc', {}) Chris@87: if fcompiler is not None and config_fc: Chris@87: log.info('using additional config_fc from setup script '\ Chris@87: 'for fortran compiler: %s' \ Chris@87: % (config_fc,)) Chris@87: from numpy.distutils.fcompiler import new_fcompiler Chris@87: fcompiler = new_fcompiler(compiler=fcompiler.compiler_type, Chris@87: verbose=self.verbose, Chris@87: dry_run=self.dry_run, Chris@87: force=self.force, Chris@87: requiref90=requiref90, Chris@87: c_compiler=self.compiler) Chris@87: if fcompiler is not None: Chris@87: dist = self.distribution Chris@87: base_config_fc = dist.get_option_dict('config_fc').copy() Chris@87: base_config_fc.update(config_fc) Chris@87: fcompiler.customize(base_config_fc) Chris@87: Chris@87: # check availability of Fortran compilers Chris@87: if (f_sources or fmodule_sources) and fcompiler is None: Chris@87: raise DistutilsError("library %s has Fortran sources"\ Chris@87: " but no Fortran compiler found" % (lib_name)) Chris@87: Chris@87: if fcompiler is not None: Chris@87: fcompiler.extra_f77_compile_args = build_info.get('extra_f77_compile_args') or [] Chris@87: fcompiler.extra_f90_compile_args = build_info.get('extra_f90_compile_args') or [] Chris@87: Chris@87: macros = build_info.get('macros') Chris@87: include_dirs = build_info.get('include_dirs') Chris@87: if include_dirs is None: Chris@87: include_dirs = [] Chris@87: extra_postargs = build_info.get('extra_compiler_args') or [] Chris@87: Chris@87: include_dirs.extend(get_numpy_include_dirs()) Chris@87: # where compiled F90 module files are: Chris@87: module_dirs = build_info.get('module_dirs') or [] Chris@87: module_build_dir = os.path.dirname(lib_file) Chris@87: if requiref90: self.mkpath(module_build_dir) Chris@87: Chris@87: if compiler.compiler_type=='msvc': Chris@87: # this hack works around the msvc compiler attributes Chris@87: # problem, msvc uses its own convention :( Chris@87: c_sources += cxx_sources Chris@87: cxx_sources = [] Chris@87: Chris@87: objects = [] Chris@87: if c_sources: Chris@87: log.info("compiling C sources") Chris@87: objects = compiler.compile(c_sources, Chris@87: output_dir=self.build_temp, Chris@87: macros=macros, Chris@87: include_dirs=include_dirs, Chris@87: debug=self.debug, Chris@87: extra_postargs=extra_postargs) Chris@87: Chris@87: if cxx_sources: Chris@87: log.info("compiling C++ sources") Chris@87: cxx_compiler = compiler.cxx_compiler() Chris@87: cxx_objects = cxx_compiler.compile(cxx_sources, Chris@87: output_dir=self.build_temp, Chris@87: macros=macros, Chris@87: include_dirs=include_dirs, Chris@87: debug=self.debug, Chris@87: extra_postargs=extra_postargs) Chris@87: objects.extend(cxx_objects) Chris@87: Chris@87: if f_sources or fmodule_sources: Chris@87: extra_postargs = [] Chris@87: f_objects = [] Chris@87: Chris@87: if requiref90: Chris@87: if fcompiler.module_dir_switch is None: Chris@87: existing_modules = glob('*.mod') Chris@87: extra_postargs += fcompiler.module_options(\ Chris@87: module_dirs, module_build_dir) Chris@87: Chris@87: if fmodule_sources: Chris@87: log.info("compiling Fortran 90 module sources") Chris@87: f_objects += fcompiler.compile(fmodule_sources, Chris@87: output_dir=self.build_temp, Chris@87: macros=macros, Chris@87: include_dirs=include_dirs, Chris@87: debug=self.debug, Chris@87: extra_postargs=extra_postargs) Chris@87: Chris@87: if requiref90 and self._f_compiler.module_dir_switch is None: Chris@87: # move new compiled F90 module files to module_build_dir Chris@87: for f in glob('*.mod'): Chris@87: if f in existing_modules: Chris@87: continue Chris@87: t = os.path.join(module_build_dir, f) Chris@87: if os.path.abspath(f)==os.path.abspath(t): Chris@87: continue Chris@87: if os.path.isfile(t): Chris@87: os.remove(t) Chris@87: try: Chris@87: self.move_file(f, module_build_dir) Chris@87: except DistutilsFileError: Chris@87: log.warn('failed to move %r to %r' \ Chris@87: % (f, module_build_dir)) Chris@87: Chris@87: if f_sources: Chris@87: log.info("compiling Fortran sources") Chris@87: f_objects += fcompiler.compile(f_sources, Chris@87: output_dir=self.build_temp, Chris@87: macros=macros, Chris@87: include_dirs=include_dirs, Chris@87: debug=self.debug, Chris@87: extra_postargs=extra_postargs) Chris@87: else: Chris@87: f_objects = [] Chris@87: Chris@87: objects.extend(f_objects) Chris@87: Chris@87: # assume that default linker is suitable for Chris@87: # linking Fortran object files Chris@87: compiler.create_static_lib(objects, lib_name, Chris@87: output_dir=self.build_clib, Chris@87: debug=self.debug) Chris@87: Chris@87: # fix library dependencies Chris@87: clib_libraries = build_info.get('libraries', []) Chris@87: for lname, binfo in libraries: Chris@87: if lname in clib_libraries: Chris@87: clib_libraries.extend(binfo[1].get('libraries', [])) Chris@87: if clib_libraries: Chris@87: build_info['libraries'] = clib_libraries