build_ext.py 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598
  1. """ Modified version of build_ext that handles fortran source files.
  2. """
  3. from __future__ import division, absolute_import, print_function
  4. import os
  5. import subprocess
  6. from glob import glob
  7. from distutils.dep_util import newer_group
  8. from distutils.command.build_ext import build_ext as old_build_ext
  9. from distutils.errors import DistutilsFileError, DistutilsSetupError,\
  10. DistutilsError
  11. from distutils.file_util import copy_file
  12. from numpy.distutils import log
  13. from numpy.distutils.exec_command import filepath_from_subprocess_output
  14. from numpy.distutils.system_info import combine_paths, system_info
  15. from numpy.distutils.misc_util import filter_sources, has_f_sources, \
  16. has_cxx_sources, get_ext_source_files, \
  17. get_numpy_include_dirs, is_sequence, get_build_architecture, \
  18. msvc_version
  19. from numpy.distutils.command.config_compiler import show_fortran_compilers
  20. class build_ext (old_build_ext):
  21. description = "build C/C++/F extensions (compile/link to build directory)"
  22. user_options = old_build_ext.user_options + [
  23. ('fcompiler=', None,
  24. "specify the Fortran compiler type"),
  25. ('parallel=', 'j',
  26. "number of parallel jobs"),
  27. ]
  28. help_options = old_build_ext.help_options + [
  29. ('help-fcompiler', None, "list available Fortran compilers",
  30. show_fortran_compilers),
  31. ]
  32. def initialize_options(self):
  33. old_build_ext.initialize_options(self)
  34. self.fcompiler = None
  35. self.parallel = None
  36. def finalize_options(self):
  37. if self.parallel:
  38. try:
  39. self.parallel = int(self.parallel)
  40. except ValueError:
  41. raise ValueError("--parallel/-j argument must be an integer")
  42. # Ensure that self.include_dirs and self.distribution.include_dirs
  43. # refer to the same list object. finalize_options will modify
  44. # self.include_dirs, but self.distribution.include_dirs is used
  45. # during the actual build.
  46. # self.include_dirs is None unless paths are specified with
  47. # --include-dirs.
  48. # The include paths will be passed to the compiler in the order:
  49. # numpy paths, --include-dirs paths, Python include path.
  50. if isinstance(self.include_dirs, str):
  51. self.include_dirs = self.include_dirs.split(os.pathsep)
  52. incl_dirs = self.include_dirs or []
  53. if self.distribution.include_dirs is None:
  54. self.distribution.include_dirs = []
  55. self.include_dirs = self.distribution.include_dirs
  56. self.include_dirs.extend(incl_dirs)
  57. old_build_ext.finalize_options(self)
  58. self.set_undefined_options('build', ('parallel', 'parallel'))
  59. def run(self):
  60. if not self.extensions:
  61. return
  62. # Make sure that extension sources are complete.
  63. self.run_command('build_src')
  64. if self.distribution.has_c_libraries():
  65. if self.inplace:
  66. if self.distribution.have_run.get('build_clib'):
  67. log.warn('build_clib already run, it is too late to '
  68. 'ensure in-place build of build_clib')
  69. build_clib = self.distribution.get_command_obj(
  70. 'build_clib')
  71. else:
  72. build_clib = self.distribution.get_command_obj(
  73. 'build_clib')
  74. build_clib.inplace = 1
  75. build_clib.ensure_finalized()
  76. build_clib.run()
  77. self.distribution.have_run['build_clib'] = 1
  78. else:
  79. self.run_command('build_clib')
  80. build_clib = self.get_finalized_command('build_clib')
  81. self.library_dirs.append(build_clib.build_clib)
  82. else:
  83. build_clib = None
  84. # Not including C libraries to the list of
  85. # extension libraries automatically to prevent
  86. # bogus linking commands. Extensions must
  87. # explicitly specify the C libraries that they use.
  88. from distutils.ccompiler import new_compiler
  89. from numpy.distutils.fcompiler import new_fcompiler
  90. compiler_type = self.compiler
  91. # Initialize C compiler:
  92. self.compiler = new_compiler(compiler=compiler_type,
  93. verbose=self.verbose,
  94. dry_run=self.dry_run,
  95. force=self.force)
  96. self.compiler.customize(self.distribution)
  97. self.compiler.customize_cmd(self)
  98. self.compiler.show_customization()
  99. # Setup directory for storing generated extra DLL files on Windows
  100. self.extra_dll_dir = os.path.join(self.build_temp, '.libs')
  101. if not os.path.isdir(self.extra_dll_dir):
  102. os.makedirs(self.extra_dll_dir)
  103. # Create mapping of libraries built by build_clib:
  104. clibs = {}
  105. if build_clib is not None:
  106. for libname, build_info in build_clib.libraries or []:
  107. if libname in clibs and clibs[libname] != build_info:
  108. log.warn('library %r defined more than once,'
  109. ' overwriting build_info\n%s... \nwith\n%s...'
  110. % (libname, repr(clibs[libname])[:300], repr(build_info)[:300]))
  111. clibs[libname] = build_info
  112. # .. and distribution libraries:
  113. for libname, build_info in self.distribution.libraries or []:
  114. if libname in clibs:
  115. # build_clib libraries have a precedence before distribution ones
  116. continue
  117. clibs[libname] = build_info
  118. # Determine if C++/Fortran 77/Fortran 90 compilers are needed.
  119. # Update extension libraries, library_dirs, and macros.
  120. all_languages = set()
  121. for ext in self.extensions:
  122. ext_languages = set()
  123. c_libs = []
  124. c_lib_dirs = []
  125. macros = []
  126. for libname in ext.libraries:
  127. if libname in clibs:
  128. binfo = clibs[libname]
  129. c_libs += binfo.get('libraries', [])
  130. c_lib_dirs += binfo.get('library_dirs', [])
  131. for m in binfo.get('macros', []):
  132. if m not in macros:
  133. macros.append(m)
  134. for l in clibs.get(libname, {}).get('source_languages', []):
  135. ext_languages.add(l)
  136. if c_libs:
  137. new_c_libs = ext.libraries + c_libs
  138. log.info('updating extension %r libraries from %r to %r'
  139. % (ext.name, ext.libraries, new_c_libs))
  140. ext.libraries = new_c_libs
  141. ext.library_dirs = ext.library_dirs + c_lib_dirs
  142. if macros:
  143. log.info('extending extension %r defined_macros with %r'
  144. % (ext.name, macros))
  145. ext.define_macros = ext.define_macros + macros
  146. # determine extension languages
  147. if has_f_sources(ext.sources):
  148. ext_languages.add('f77')
  149. if has_cxx_sources(ext.sources):
  150. ext_languages.add('c++')
  151. l = ext.language or self.compiler.detect_language(ext.sources)
  152. if l:
  153. ext_languages.add(l)
  154. # reset language attribute for choosing proper linker
  155. if 'c++' in ext_languages:
  156. ext_language = 'c++'
  157. elif 'f90' in ext_languages:
  158. ext_language = 'f90'
  159. elif 'f77' in ext_languages:
  160. ext_language = 'f77'
  161. else:
  162. ext_language = 'c' # default
  163. if l and l != ext_language and ext.language:
  164. log.warn('resetting extension %r language from %r to %r.' %
  165. (ext.name, l, ext_language))
  166. ext.language = ext_language
  167. # global language
  168. all_languages.update(ext_languages)
  169. need_f90_compiler = 'f90' in all_languages
  170. need_f77_compiler = 'f77' in all_languages
  171. need_cxx_compiler = 'c++' in all_languages
  172. # Initialize C++ compiler:
  173. if need_cxx_compiler:
  174. self._cxx_compiler = new_compiler(compiler=compiler_type,
  175. verbose=self.verbose,
  176. dry_run=self.dry_run,
  177. force=self.force)
  178. compiler = self._cxx_compiler
  179. compiler.customize(self.distribution, need_cxx=need_cxx_compiler)
  180. compiler.customize_cmd(self)
  181. compiler.show_customization()
  182. self._cxx_compiler = compiler.cxx_compiler()
  183. else:
  184. self._cxx_compiler = None
  185. # Initialize Fortran 77 compiler:
  186. if need_f77_compiler:
  187. ctype = self.fcompiler
  188. self._f77_compiler = new_fcompiler(compiler=self.fcompiler,
  189. verbose=self.verbose,
  190. dry_run=self.dry_run,
  191. force=self.force,
  192. requiref90=False,
  193. c_compiler=self.compiler)
  194. fcompiler = self._f77_compiler
  195. if fcompiler:
  196. ctype = fcompiler.compiler_type
  197. fcompiler.customize(self.distribution)
  198. if fcompiler and fcompiler.get_version():
  199. fcompiler.customize_cmd(self)
  200. fcompiler.show_customization()
  201. else:
  202. self.warn('f77_compiler=%s is not available.' %
  203. (ctype))
  204. self._f77_compiler = None
  205. else:
  206. self._f77_compiler = None
  207. # Initialize Fortran 90 compiler:
  208. if need_f90_compiler:
  209. ctype = self.fcompiler
  210. self._f90_compiler = new_fcompiler(compiler=self.fcompiler,
  211. verbose=self.verbose,
  212. dry_run=self.dry_run,
  213. force=self.force,
  214. requiref90=True,
  215. c_compiler=self.compiler)
  216. fcompiler = self._f90_compiler
  217. if fcompiler:
  218. ctype = fcompiler.compiler_type
  219. fcompiler.customize(self.distribution)
  220. if fcompiler and fcompiler.get_version():
  221. fcompiler.customize_cmd(self)
  222. fcompiler.show_customization()
  223. else:
  224. self.warn('f90_compiler=%s is not available.' %
  225. (ctype))
  226. self._f90_compiler = None
  227. else:
  228. self._f90_compiler = None
  229. # Build extensions
  230. self.build_extensions()
  231. # Copy over any extra DLL files
  232. # FIXME: In the case where there are more than two packages,
  233. # we blindly assume that both packages need all of the libraries,
  234. # resulting in a larger wheel than is required. This should be fixed,
  235. # but it's so rare that I won't bother to handle it.
  236. pkg_roots = {
  237. self.get_ext_fullname(ext.name).split('.')[0]
  238. for ext in self.extensions
  239. }
  240. for pkg_root in pkg_roots:
  241. shared_lib_dir = os.path.join(pkg_root, '.libs')
  242. if not self.inplace:
  243. shared_lib_dir = os.path.join(self.build_lib, shared_lib_dir)
  244. for fn in os.listdir(self.extra_dll_dir):
  245. if not os.path.isdir(shared_lib_dir):
  246. os.makedirs(shared_lib_dir)
  247. if not fn.lower().endswith('.dll'):
  248. continue
  249. runtime_lib = os.path.join(self.extra_dll_dir, fn)
  250. copy_file(runtime_lib, shared_lib_dir)
  251. def swig_sources(self, sources):
  252. # Do nothing. Swig sources have beed handled in build_src command.
  253. return sources
  254. def build_extension(self, ext):
  255. sources = ext.sources
  256. if sources is None or not is_sequence(sources):
  257. raise DistutilsSetupError(
  258. ("in 'ext_modules' option (extension '%s'), " +
  259. "'sources' must be present and must be " +
  260. "a list of source filenames") % ext.name)
  261. sources = list(sources)
  262. if not sources:
  263. return
  264. fullname = self.get_ext_fullname(ext.name)
  265. if self.inplace:
  266. modpath = fullname.split('.')
  267. package = '.'.join(modpath[0:-1])
  268. base = modpath[-1]
  269. build_py = self.get_finalized_command('build_py')
  270. package_dir = build_py.get_package_dir(package)
  271. ext_filename = os.path.join(package_dir,
  272. self.get_ext_filename(base))
  273. else:
  274. ext_filename = os.path.join(self.build_lib,
  275. self.get_ext_filename(fullname))
  276. depends = sources + ext.depends
  277. if not (self.force or newer_group(depends, ext_filename, 'newer')):
  278. log.debug("skipping '%s' extension (up-to-date)", ext.name)
  279. return
  280. else:
  281. log.info("building '%s' extension", ext.name)
  282. extra_args = ext.extra_compile_args or []
  283. macros = ext.define_macros[:]
  284. for undef in ext.undef_macros:
  285. macros.append((undef,))
  286. c_sources, cxx_sources, f_sources, fmodule_sources = \
  287. filter_sources(ext.sources)
  288. if self.compiler.compiler_type == 'msvc':
  289. if cxx_sources:
  290. # Needed to compile kiva.agg._agg extension.
  291. extra_args.append('/Zm1000')
  292. # this hack works around the msvc compiler attributes
  293. # problem, msvc uses its own convention :(
  294. c_sources += cxx_sources
  295. cxx_sources = []
  296. # Set Fortran/C++ compilers for compilation and linking.
  297. if ext.language == 'f90':
  298. fcompiler = self._f90_compiler
  299. elif ext.language == 'f77':
  300. fcompiler = self._f77_compiler
  301. else: # in case ext.language is c++, for instance
  302. fcompiler = self._f90_compiler or self._f77_compiler
  303. if fcompiler is not None:
  304. fcompiler.extra_f77_compile_args = (ext.extra_f77_compile_args or []) if hasattr(
  305. ext, 'extra_f77_compile_args') else []
  306. fcompiler.extra_f90_compile_args = (ext.extra_f90_compile_args or []) if hasattr(
  307. ext, 'extra_f90_compile_args') else []
  308. cxx_compiler = self._cxx_compiler
  309. # check for the availability of required compilers
  310. if cxx_sources and cxx_compiler is None:
  311. raise DistutilsError("extension %r has C++ sources"
  312. "but no C++ compiler found" % (ext.name))
  313. if (f_sources or fmodule_sources) and fcompiler is None:
  314. raise DistutilsError("extension %r has Fortran sources "
  315. "but no Fortran compiler found" % (ext.name))
  316. if ext.language in ['f77', 'f90'] and fcompiler is None:
  317. self.warn("extension %r has Fortran libraries "
  318. "but no Fortran linker found, using default linker" % (ext.name))
  319. if ext.language == 'c++' and cxx_compiler is None:
  320. self.warn("extension %r has C++ libraries "
  321. "but no C++ linker found, using default linker" % (ext.name))
  322. kws = {'depends': ext.depends}
  323. output_dir = self.build_temp
  324. include_dirs = ext.include_dirs + get_numpy_include_dirs()
  325. c_objects = []
  326. if c_sources:
  327. log.info("compiling C sources")
  328. c_objects = self.compiler.compile(c_sources,
  329. output_dir=output_dir,
  330. macros=macros,
  331. include_dirs=include_dirs,
  332. debug=self.debug,
  333. extra_postargs=extra_args,
  334. **kws)
  335. if cxx_sources:
  336. log.info("compiling C++ sources")
  337. c_objects += cxx_compiler.compile(cxx_sources,
  338. output_dir=output_dir,
  339. macros=macros,
  340. include_dirs=include_dirs,
  341. debug=self.debug,
  342. extra_postargs=extra_args,
  343. **kws)
  344. extra_postargs = []
  345. f_objects = []
  346. if fmodule_sources:
  347. log.info("compiling Fortran 90 module sources")
  348. module_dirs = ext.module_dirs[:]
  349. module_build_dir = os.path.join(
  350. self.build_temp, os.path.dirname(
  351. self.get_ext_filename(fullname)))
  352. self.mkpath(module_build_dir)
  353. if fcompiler.module_dir_switch is None:
  354. existing_modules = glob('*.mod')
  355. extra_postargs += fcompiler.module_options(
  356. module_dirs, module_build_dir)
  357. f_objects += fcompiler.compile(fmodule_sources,
  358. output_dir=self.build_temp,
  359. macros=macros,
  360. include_dirs=include_dirs,
  361. debug=self.debug,
  362. extra_postargs=extra_postargs,
  363. depends=ext.depends)
  364. if fcompiler.module_dir_switch is None:
  365. for f in glob('*.mod'):
  366. if f in existing_modules:
  367. continue
  368. t = os.path.join(module_build_dir, f)
  369. if os.path.abspath(f) == os.path.abspath(t):
  370. continue
  371. if os.path.isfile(t):
  372. os.remove(t)
  373. try:
  374. self.move_file(f, module_build_dir)
  375. except DistutilsFileError:
  376. log.warn('failed to move %r to %r' %
  377. (f, module_build_dir))
  378. if f_sources:
  379. log.info("compiling Fortran sources")
  380. f_objects += fcompiler.compile(f_sources,
  381. output_dir=self.build_temp,
  382. macros=macros,
  383. include_dirs=include_dirs,
  384. debug=self.debug,
  385. extra_postargs=extra_postargs,
  386. depends=ext.depends)
  387. if f_objects and not fcompiler.can_ccompiler_link(self.compiler):
  388. unlinkable_fobjects = f_objects
  389. objects = c_objects
  390. else:
  391. unlinkable_fobjects = []
  392. objects = c_objects + f_objects
  393. if ext.extra_objects:
  394. objects.extend(ext.extra_objects)
  395. extra_args = ext.extra_link_args or []
  396. libraries = self.get_libraries(ext)[:]
  397. library_dirs = ext.library_dirs[:]
  398. linker = self.compiler.link_shared_object
  399. # Always use system linker when using MSVC compiler.
  400. if self.compiler.compiler_type in ('msvc', 'intelw', 'intelemw'):
  401. # expand libraries with fcompiler libraries as we are
  402. # not using fcompiler linker
  403. self._libs_with_msvc_and_fortran(
  404. fcompiler, libraries, library_dirs)
  405. elif ext.language in ['f77', 'f90'] and fcompiler is not None:
  406. linker = fcompiler.link_shared_object
  407. if ext.language == 'c++' and cxx_compiler is not None:
  408. linker = cxx_compiler.link_shared_object
  409. if fcompiler is not None:
  410. objects, libraries = self._process_unlinkable_fobjects(
  411. objects, libraries,
  412. fcompiler, library_dirs,
  413. unlinkable_fobjects)
  414. linker(objects, ext_filename,
  415. libraries=libraries,
  416. library_dirs=library_dirs,
  417. runtime_library_dirs=ext.runtime_library_dirs,
  418. extra_postargs=extra_args,
  419. export_symbols=self.get_export_symbols(ext),
  420. debug=self.debug,
  421. build_temp=self.build_temp,
  422. target_lang=ext.language)
  423. def _add_dummy_mingwex_sym(self, c_sources):
  424. build_src = self.get_finalized_command("build_src").build_src
  425. build_clib = self.get_finalized_command("build_clib").build_clib
  426. objects = self.compiler.compile([os.path.join(build_src,
  427. "gfortran_vs2003_hack.c")],
  428. output_dir=self.build_temp)
  429. self.compiler.create_static_lib(
  430. objects, "_gfortran_workaround", output_dir=build_clib, debug=self.debug)
  431. def _process_unlinkable_fobjects(self, objects, libraries,
  432. fcompiler, library_dirs,
  433. unlinkable_fobjects):
  434. libraries = list(libraries)
  435. objects = list(objects)
  436. unlinkable_fobjects = list(unlinkable_fobjects)
  437. # Expand possible fake static libraries to objects
  438. for lib in list(libraries):
  439. for libdir in library_dirs:
  440. fake_lib = os.path.join(libdir, lib + '.fobjects')
  441. if os.path.isfile(fake_lib):
  442. # Replace fake static library
  443. libraries.remove(lib)
  444. with open(fake_lib, 'r') as f:
  445. unlinkable_fobjects.extend(f.read().splitlines())
  446. # Expand C objects
  447. c_lib = os.path.join(libdir, lib + '.cobjects')
  448. with open(c_lib, 'r') as f:
  449. objects.extend(f.read().splitlines())
  450. # Wrap unlinkable objects to a linkable one
  451. if unlinkable_fobjects:
  452. fobjects = [os.path.relpath(obj) for obj in unlinkable_fobjects]
  453. wrapped = fcompiler.wrap_unlinkable_objects(
  454. fobjects, output_dir=self.build_temp,
  455. extra_dll_dir=self.extra_dll_dir)
  456. objects.extend(wrapped)
  457. return objects, libraries
  458. def _libs_with_msvc_and_fortran(self, fcompiler, c_libraries,
  459. c_library_dirs):
  460. if fcompiler is None:
  461. return
  462. for libname in c_libraries:
  463. if libname.startswith('msvc'):
  464. continue
  465. fileexists = False
  466. for libdir in c_library_dirs or []:
  467. libfile = os.path.join(libdir, '%s.lib' % (libname))
  468. if os.path.isfile(libfile):
  469. fileexists = True
  470. break
  471. if fileexists:
  472. continue
  473. # make g77-compiled static libs available to MSVC
  474. fileexists = False
  475. for libdir in c_library_dirs:
  476. libfile = os.path.join(libdir, 'lib%s.a' % (libname))
  477. if os.path.isfile(libfile):
  478. # copy libname.a file to name.lib so that MSVC linker
  479. # can find it
  480. libfile2 = os.path.join(self.build_temp, libname + '.lib')
  481. copy_file(libfile, libfile2)
  482. if self.build_temp not in c_library_dirs:
  483. c_library_dirs.append(self.build_temp)
  484. fileexists = True
  485. break
  486. if fileexists:
  487. continue
  488. log.warn('could not find library %r in directories %s'
  489. % (libname, c_library_dirs))
  490. # Always use system linker when using MSVC compiler.
  491. f_lib_dirs = []
  492. for dir in fcompiler.library_dirs:
  493. # correct path when compiling in Cygwin but with normal Win
  494. # Python
  495. if dir.startswith('/usr/lib'):
  496. try:
  497. dir = subprocess.check_output(['cygpath', '-w', dir])
  498. except (OSError, subprocess.CalledProcessError):
  499. pass
  500. else:
  501. dir = filepath_from_subprocess_output(dir)
  502. f_lib_dirs.append(dir)
  503. c_library_dirs.extend(f_lib_dirs)
  504. # make g77-compiled static libs available to MSVC
  505. for lib in fcompiler.libraries:
  506. if not lib.startswith('msvc'):
  507. c_libraries.append(lib)
  508. p = combine_paths(f_lib_dirs, 'lib' + lib + '.a')
  509. if p:
  510. dst_name = os.path.join(self.build_temp, lib + '.lib')
  511. if not os.path.isfile(dst_name):
  512. copy_file(p[0], dst_name)
  513. if self.build_temp not in c_library_dirs:
  514. c_library_dirs.append(self.build_temp)
  515. def get_source_files(self):
  516. self.check_extensions_list(self.extensions)
  517. filenames = []
  518. for ext in self.extensions:
  519. filenames.extend(get_ext_source_files(ext))
  520. return filenames
  521. def get_outputs(self):
  522. self.check_extensions_list(self.extensions)
  523. outputs = []
  524. for ext in self.extensions:
  525. if not ext.sources:
  526. continue
  527. fullname = self.get_ext_fullname(ext.name)
  528. outputs.append(os.path.join(self.build_lib,
  529. self.get_ext_filename(fullname)))
  530. return outputs