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 shutil
12import glob
13import os
14import sys
15import tempfile
16
17from pyomo.common.envvar import PYOMO_CONFIG_DIR
18from pyomo.common.fileutils import this_file_dir
19
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
29
30def build_appsi(args=[]):
31    print('\n\n**** Building APPSI ****')
32    import setuptools
33    from distutils.dist import Distribution
34    from pybind11.setup_helpers import Pybind11Extension, build_ext
35    import pybind11.setup_helpers
36
37    appsi_root = this_file_dir()
38    sources = [
39        os.path.join(appsi_root, 'cmodel', 'src', file_)
40        for file_ in (
41                'expression.cpp',
42                'common.cpp',
43                'nl_writer.cpp',
44                'lp_writer.cpp',
45                'cmodel_bindings.cpp',
46        )
47    ]
48
49    class appsi_build_ext(build_ext):
50        def run(self):
51            basedir = os.path.abspath(os.path.curdir)
52            if self.inplace:
53                tmpdir = os.path.join(this_file_dir(), 'cmodel')
54            else:
55                tmpdir = os.path.abspath(tempfile.mkdtemp())
56            print("Building in '%s'" % tmpdir)
57            os.chdir(tmpdir)
58            try:
59                super(appsi_build_ext, self).run()
60                if not self.inplace:
61                    library = glob.glob("build/*/appsi_cmodel.*")[0]
62                    target = os.path.join(
63                        PYOMO_CONFIG_DIR, 'lib',
64                        'python%s.%s' % sys.version_info[:2],
65                        'site-packages', '.')
66                    if not os.path.exists(target):
67                        os.makedirs(target)
68                    shutil.copy(library, target)
69            finally:
70                os.chdir(basedir)
71                if not self.inplace:
72                    shutil.rmtree(tmpdir, onerror=handleReadonly)
73
74    try:
75        original_pybind11_setup_helpers_macos = pybind11.setup_helpers.MACOS
76        pybind11.setup_helpers.MACOS = False
77
78        package_config = {
79            'name': 'appsi_cmodel',
80            'packages': [],
81            'ext_modules': [
82                Pybind11Extension("appsi_cmodel", sources)
83            ],
84            'cmdclass': {
85                "build_ext": appsi_build_ext,
86            },
87        }
88
89        dist = Distribution(package_config)
90        dist.script_args = ['build_ext'] + args
91        dist.parse_command_line()
92        dist.run_command('build_ext')
93    finally:
94        pybind11.setup_helpers.MACOS = original_pybind11_setup_helpers_macos
95
96
97class AppsiBuilder(object):
98    def __call__(self, parallel):
99        return build_appsi()
100
101
102if __name__ == '__main__':
103    # Note: this recognizes the "--inplace" command line argument: build
104    # directory will be put in the source tree (and preserved), and the
105    # SO will be left in appsi/cmodel.
106    build_appsi(sys.argv[1:])
107