1# ___________________________________________________________________________ 2# 3# Pyomo: Python Optimization Modeling Objects 4# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC 5# Under the terms of Contract DE-NA0003525 with National Technology and 6# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain 7# rights in this software. 8# This software is distributed under the 3-clause BSD License. 9# ___________________________________________________________________________ 10 11import errno 12import os 13import shutil 14import stat 15import sys 16import tempfile 17 18import pyomo.common.envvar as envvar 19from pyomo.common.fileutils import this_file_dir, find_executable 20 21def handleReadonly(function, path, excinfo): 22 excvalue = excinfo[1] 23 if excvalue.errno == errno.EACCES: 24 os.chmod(path, stat.S_IRWXU| stat.S_IRWXG| stat.S_IRWXO) # 0777 25 function(path) 26 else: 27 raise 28 29def build_pynumero(user_args=[], parallel=None): 30 import distutils.core 31 from setuptools import Extension 32 from distutils.command.build_ext import build_ext 33 34 class _CMakeBuild(build_ext, object): 35 def run(self): 36 project_dir = self.extensions[0].project_dir 37 38 cmake_config = 'Debug' if self.debug else 'Release' 39 cmake_args = [ 40 '-DCMAKE_INSTALL_PREFIX=' + envvar.PYOMO_CONFIG_DIR, 41 '-DBUILD_AMPLMP_IF_NEEDED=ON', 42 #'-DCMAKE_BUILD_TYPE=' + cmake_config, 43 ] + user_args 44 45 try: 46 # Redirect all stderr to stdout (to prevent powershell 47 # from inadvertently failing builds) 48 sys.stderr.flush() 49 sys.stdout.flush() 50 old_stderr = os.dup(sys.stderr.fileno()) 51 os.dup2(sys.stdout.fileno(), sys.stderr.fileno()) 52 old_environ = dict(os.environ) 53 if parallel: 54 # --parallel was only added in cmake 3.12. Use an 55 # environment variable so that we don't have to bump 56 # the minimum cmake version. 57 os.environ['CMAKE_BUILD_PARALLEL_LEVEL'] = str(parallel) 58 59 cmake = find_executable('cmake') 60 if cmake is None: 61 raise IOError("cmake not found in the system PATH") 62 self.spawn([cmake, project_dir] + cmake_args) 63 if not self.dry_run: 64 # Skip build and go straight to install: the build 65 # harness should take care of dependencies and this 66 # will prevent repeated builds in MSVS 67 # 68 #self.spawn(['cmake', '--build', '.', 69 # '--config', cmake_config]) 70 self.spawn([cmake, '--build', '.', 71 '--target', 'install', 72 '--config', cmake_config]) 73 finally: 74 # Restore stderr 75 sys.stderr.flush() 76 sys.stdout.flush() 77 os.dup2(old_stderr, sys.stderr.fileno()) 78 os.environ = old_environ 79 80 class CMakeExtension(Extension, object): 81 def __init__(self, name): 82 # don't invoke the original build_ext for this special extension 83 super(CMakeExtension, self).__init__(name, sources=[]) 84 self.project_dir = os.path.join(this_file_dir(), name) 85 86 sys.stdout.write("\n**** Building PyNumero libraries ****\n") 87 package_config = { 88 'name': 'pynumero_libraries', 89 'packages': [], 90 'ext_modules': [CMakeExtension("src")], 91 'cmdclass': {'build_ext': _CMakeBuild}, 92 } 93 dist = distutils.core.Distribution(package_config) 94 try: 95 basedir = os.path.abspath(os.path.curdir) 96 tmpdir = os.path.abspath(tempfile.mkdtemp()) 97 os.chdir(tmpdir) 98 dist.run_command('build_ext') 99 install_dir = os.path.join(envvar.PYOMO_CONFIG_DIR, 'lib') 100 finally: 101 os.chdir(basedir) 102 shutil.rmtree(tmpdir, onerror=handleReadonly) 103 sys.stdout.write("Installed PyNumero libraries to %s\n" % ( install_dir, )) 104 105 106class PyNumeroBuilder(object): 107 def __call__(self, parallel): 108 return build_pynumero(parallel=parallel) 109 110if __name__ == "__main__": 111 build_pynumero(sys.argv[1:]) 112 113