# ____________________________________________________________________________________
#
# Pyomo: Python Optimization Modeling Objects
# Copyright (c) 2008-2026 National Technology and Engineering Solutions of Sandia, LLC
# Under the terms of Contract DE-NA0003525 with National Technology and Engineering
# Solutions of Sandia, LLC, the U.S. Government retains certain rights in this
# software. This software is distributed under the 3-clause BSD License.
# ____________________________________________________________________________________
import glob
import logging
import os
import shutil
import sys
import subprocess
from pyomo.common.download import FileDownloader
from pyomo.common.envvar import PYOMO_CONFIG_DIR
from pyomo.common.fileutils import find_library, this_file_dir
from pyomo.common.tempfiles import TempfileManager
logger = logging.getLogger(__name__ if __name__ != '__main__' else 'pyomo')
[docs]
def build_ginac_library(parallel=None, argv=None, env=None):
sys.stdout.write("\n**** Building GiNaC library ****\n")
configure_cmd = [
os.path.join('.', 'configure'),
'--prefix=' + PYOMO_CONFIG_DIR,
'--disable-static',
]
make_cmd = ['make']
if parallel:
make_cmd.append(f'-j{parallel}')
install_cmd = ['make', 'install']
env = dict(os.environ)
pcdir = os.path.join(PYOMO_CONFIG_DIR, 'lib', 'pkgconfig')
if 'PKG_CONFIG_PATH' in env:
pcdir += os.pathsep + env['PKG_CONFIG_PATH']
env['PKG_CONFIG_PATH'] = pcdir
with TempfileManager.new_context() as tempfile:
tmpdir = tempfile.mkdtemp()
downloader = FileDownloader()
if argv:
downloader.parse_args(argv)
url = 'https://www.ginac.de/CLN/cln-1.3.7.tar.bz2'
cln_dir = os.path.join(tmpdir, 'cln')
downloader.set_destination_filename(cln_dir)
logger.info(
"Fetching CLN from %s and installing it to %s"
% (url, downloader.destination())
)
downloader.get_tar_archive(url, dirOffset=1)
assert subprocess.run(configure_cmd, cwd=cln_dir, env=env).returncode == 0
logger.info("\nBuilding CLN\n")
assert subprocess.run(make_cmd, cwd=cln_dir, env=env).returncode == 0
assert subprocess.run(install_cmd, cwd=cln_dir, env=env).returncode == 0
url = 'https://www.ginac.de/ginac-1.8.10.tar.bz2'
ginac_dir = os.path.join(tmpdir, 'ginac')
downloader.set_destination_filename(ginac_dir)
logger.info(
"Fetching GiNaC from %s and installing it to %s"
% (url, downloader.destination())
)
downloader.get_tar_archive(url, dirOffset=1)
assert subprocess.run(configure_cmd, cwd=ginac_dir, env=env).returncode == 0
logger.info("\nBuilding GiNaC\n")
assert subprocess.run(make_cmd, cwd=ginac_dir, env=env).returncode == 0
assert subprocess.run(install_cmd, cwd=ginac_dir, env=env).returncode == 0
print("Installed GiNaC to %s" % (ginac_dir,))
def _find_include(libdir, incpaths):
rel_path = ('include',) + incpaths
while 1:
basedir = os.path.dirname(libdir)
if not basedir or basedir == libdir:
return None
if os.path.exists(os.path.join(basedir, *rel_path)):
return os.path.join(basedir, *(rel_path[: -len(incpaths)]))
libdir = basedir
[docs]
def build_ginac_interface(parallel=None, args=None):
from distutils.dist import Distribution
from pybind11.setup_helpers import Pybind11Extension, build_ext
from pyomo.common.cmake_builder import handleReadonly
sys.stdout.write("\n**** Building GiNaC interface ****\n")
if args is None:
args = []
sources = [
os.path.join(this_file_dir(), 'ginac', 'src', fname)
for fname in ['ginac_interface.cpp']
]
ginac_lib = find_library('ginac')
if not ginac_lib:
raise RuntimeError(
'could not find the GiNaC library; please make sure either to install '
'the library and development headers system-wide, or include the '
'path to the library in the LD_LIBRARY_PATH environment variable'
)
ginac_lib_dir = os.path.dirname(ginac_lib)
ginac_include_dir = _find_include(ginac_lib_dir, ('ginac', 'ginac.h'))
if not ginac_include_dir:
raise RuntimeError('could not find GiNaC include directory')
cln_lib = find_library('cln')
if not cln_lib:
raise RuntimeError(
'could not find the CLN library; please make sure either to install '
'the library and development headers system-wide, or include the '
'path to the library in the LD_LIBRARY_PATH environment variable'
)
cln_lib_dir = os.path.dirname(cln_lib)
cln_include_dir = _find_include(cln_lib_dir, ('cln', 'cln.h'))
if not cln_include_dir:
raise RuntimeError('could not find CLN include directory')
extra_args = ['-std=c++11']
ext = Pybind11Extension(
'ginac_interface',
sources=sources,
language='c++',
include_dirs=[cln_include_dir, ginac_include_dir],
library_dirs=[cln_lib_dir, ginac_lib_dir],
libraries=['cln', 'ginac'],
extra_compile_args=extra_args,
)
class ginacBuildExt(build_ext):
def run(self):
basedir = os.path.abspath(os.path.curdir)
with TempfileManager.new_context() as tempfile:
if self.inplace:
tmpdir = os.path.join(this_file_dir(), 'ginac')
else:
tmpdir = os.path.abspath(tempfile.mkdtemp())
sys.stdout.write("Building in '%s'\n" % tmpdir)
os.chdir(tmpdir)
super(ginacBuildExt, self).run()
if not self.inplace:
library = glob.glob("build/*/ginac_interface.*")[0]
target = os.path.join(
PYOMO_CONFIG_DIR,
'lib',
'python%s.%s' % sys.version_info[:2],
'site-packages',
'.',
)
if not os.path.exists(target):
os.makedirs(target)
sys.stdout.write(f"Installing {library} in {target}\n")
shutil.copy(library, target)
package_config = {
'name': 'ginac_interface',
'packages': [],
'ext_modules': [ext],
'cmdclass': {"build_ext": ginacBuildExt},
}
dist = Distribution(package_config)
dist.script_args = ['build_ext'] + args
dist.parse_command_line()
dist.run_command('build_ext')
[docs]
class GiNaCInterfaceBuilder:
def __call__(self, parallel):
return build_ginac_interface(parallel)
def skip(self):
return not find_library('ginac')
if __name__ == '__main__':
import argparse
parser = argparse.ArgumentParser()
parser.add_argument(
"-j",
dest='parallel',
type=int,
default=None,
help="Enable parallel build with PARALLEL cores",
)
parser.add_argument(
"--build-deps",
dest='build_deps',
action='store_true',
default=False,
help="Download and build the CLN/GiNaC libraries",
)
options, argv = parser.parse_known_args(sys.argv)
logging.getLogger('pyomo').setLevel(logging.INFO)
if options.build_deps:
build_ginac_library(options.parallel, [])
build_ginac_interface(options.parallel, argv[1:])