Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/environment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ dependencies:
- numpy >=2.0
- swig
- meson >=1.3.2
- meson-python
- compilers
- pkg-config
- pip
Expand Down
61 changes: 41 additions & 20 deletions meson.build
Original file line number Diff line number Diff line change
@@ -1,24 +1,33 @@
# Much of this is from SciPy

project(
'pyoptsparse',
'c', 'cpp',
# unnecessary metadata commented out until Meson supports PEP517 and installation with pip
# version: 'x.x.x',
# license: 'GPL-3',
meson_version: '>= 0.60',
version: run_command(
'python', '-c',
'''
import re
from pathlib import Path
init_file = Path("pyoptsparse/__init__.py")
match = re.search(r'__version__ = ["\\\']([\d\\.]+)["\\\']', init_file.read_text())
print(match.group(1))
'''
).stdout().strip(),
meson_version: '>= 0.64',
default_options: [
'buildtype=debugoptimized',
'c_std=c99',
'cpp_std=c++14',
'b_ndebug=if-release',
'c_std=c17',
'cpp_std=c++17',
],
)

fortranobject_c = '../fortranobject.c'
# <!-- from https://github.com/scipy/scipy/blob/4d9f5e65af06d4cc3f770407f1a66a185675eea9/meson.build

cc = meson.get_compiler('c')
cpp = meson.get_compiler('cpp')

py3 = import('python').find_installation(pure: false)
py3_dep = py3.dependency()

# We need -lm for all C code (assuming it uses math functions, which is safe to
# assume for SciPy). For C++ it isn't needed, because libstdc++/libc++ is
# guaranteed to depend on it. For Fortran code, Meson already adds `-lm`.
Expand All @@ -29,17 +38,29 @@ endif

# Adding at project level causes many spurious -lgfortran flags.
add_languages('fortran', native: false)
ff = meson.get_compiler('fortran')
if ff.get_id() == 'gcc'
# -std=legacy is not supported by all Fortran compilers, but very useful with
# gfortran since it avoids a ton of warnings that we don't care about.
# Needs fixing in Meson, see https://github.com/mesonbuild/meson/issues/11633.
add_project_arguments('-std=legacy', language: 'fortran')
endif

# https://mesonbuild.com/Python-module.html
# Here we differentiate from the python used by meson, py3_command, and that python target, py3_target. This is useful
# when cross compiling like on conda-forge
py_mod = import('python')
py3_command = py_mod.find_installation()
if get_option('python_target') != ''
py3_target = py_mod.find_installation(get_option('python_target'))
else
py3_target = py3_command
if ff.has_argument('-Wno-conversion')
add_project_arguments('-Wno-conversion', language: 'fortran')
endif
py3_dep = py3_target.dependency()

subdir('pyoptsparse')
if host_machine.system() == 'darwin'
if cc.has_link_argument('-Wl,-dead_strip')
# Allow linker to strip unused symbols
add_project_link_arguments('-Wl,-dead_strip', language : ['c', 'cpp', 'fortran'])
endif
endif

# --!>

# install python sources
install_subdir('pyoptsparse', install_dir: py3.get_install_dir())

# install non-python sources
subdir('pyoptsparse')
128 changes: 46 additions & 82 deletions pyoptsparse/meson.build
Original file line number Diff line number Diff line change
@@ -1,97 +1,61 @@
# NumPy include directory - needed in all submodules
incdir_numpy = get_option('incdir_numpy')
if incdir_numpy == ''
incdir_numpy = run_command(py3_target,
[
'-c',
'import os; os.chdir(".."); import numpy; print(numpy.get_include())'
],
check: true
).stdout().strip()
# <!-- from https://github.com/scipy/scipy/blob/4d9f5e65af06d4cc3f770407f1a66a185675eea9/scipy/meson.build

incdir_numpy = meson.get_external_property('numpy-include-dir', 'not-given')
if incdir_numpy == 'not-given'
incdir_numpy = run_command(py3,
[
'-c',
'''import os
import numpy as np
try:
incdir = os.path.relpath(np.get_include())
except Exception:
incdir = np.get_include()
print(incdir)
'''
],
check: true
).stdout().strip()

# We do need an absolute path to feed to `cc.find_library` below
_incdir_numpy_abs = run_command(py3,
['-c', 'import os; os.chdir(".."); import numpy; print(numpy.get_include())'],
check: true
).stdout().strip()
else
_incdir_numpy_abs = incdir_numpy
endif
# this creates a raw string which is useful for Windows use of '\' for paths
incdir_numpy = '''@0@'''.format(incdir_numpy)

# HACK: Meson prefixes filenames of intermediate compiled objects with their filepath. This poses a problem for conda builds
# since conda ensures the host environment directory has 255 characters so the meson object filenames then exceed 255
# characters. To remedy this, the fortranobject.c file from numpy is copied into pyoptsparse so that the meson build
# uses a relative path, rather than an absolute path, thus reducing the auto generated object filename
# see for example https://github.com/mesonbuild/meson/issues/4226
run_command(py3_command,
[
'-c',
'import os; os.chdir(".."); import shutil; shutil.copy(os.path.join(r"' + incdir_numpy + '", "..", "..", "f2py", "src", "fortranobject.c"), "pyoptsparse")'
],
check: true
)

inc_np = include_directories(incdir_numpy)

# Don't use the deprecated NumPy C API. Define this to a fixed version instead of
# NPY_API_VERSION in order not to break compilation for released SciPy versions
# when NumPy introduces a new deprecation.
numpy_nodepr_api = ['-DNPY_NO_DEPRECATED_API=NPY_1_9_API_VERSION']
np_dep = declare_dependency(include_directories: inc_np, compile_args: numpy_nodepr_api)

# TODO: pyoptsparse supports numpy>=1.16 but numpy.f2py.get_include() wasnt added until later, raise numpy version?
#incdir_f2py = run_command(py3_target,
# [
# '-c',
# 'import os; os.chdir(".."); import numpy.f2py; print(numpy.f2py.get_include())'
# ],
# check : true
#).stdout().strip()
incdir_f2py = incdir_numpy / '..' / '..' / 'f2py' / 'src'
inc_f2py = include_directories(incdir_f2py)
fortranobject_c = incdir_f2py / 'fortranobject.c'

# Share this object across multiple modules.
fortranobject_lib = static_library('_fortranobject',
fortranobject_c,
c_args: numpy_nodepr_api,
dependencies: py3_dep,
include_directories: [inc_np, inc_f2py],
gnu_symbol_visibility: 'hidden',
)
fortranobject_dep = declare_dependency(
link_with: fortranobject_lib,
include_directories: [inc_np, inc_f2py],
)


# TODO: this is kept in here so that when meson becomes pep517-compliant
# we can uncomment these to have meson install the source files

#python_sources = [
# '__init__.py',
# 'pyOpt_MPI.py',
# 'pyOpt_constraint.py',
# 'pyOpt_error.py',
# 'pyOpt_gradient.py',
# 'pyOpt_history.py',
# 'pyOpt_objective.py',
# 'pyOpt_optimization.py',
# 'pyOpt_optimizer.py',
# 'pyOpt_solution.py',
# 'pyOpt_utils.py',
# 'pyOpt_variable.py',
# 'types.py'
#]

#py3_target.install_sources(
# python_sources,
# pure: true,
# subdir: 'pyoptsparse'
#)
# --!>

subdir('pySNOPT')
subdir('pySLSQP')
subdir('pyCONMIN')
subdir('pyNLPQLP')
subdir('pyNSGA2')
subdir('pyPSQP')
#subdir('pyALPSO')
#subdir('pyParOpt')
#subdir('postprocessing')

# test imports
# envdata = environment()
# python_paths = [join_paths(meson.current_build_dir(), '..')]
# envdata.prepend('PYTHONPATH', python_paths)

# progs = [['SLSQP', 'pySLSQP', 'slsqp'],
# ['CONMIN', 'pyCONMIN', 'conmin'],
# ['PSQP', 'pyPSQP', 'psqp'],
# ['NSGA2', 'pyNSGA2', 'nsga2']]


# foreach p : progs
# import_command = 'from pyoptsparse.' + p[1] + ' import '+p[2]+'; print('+p[2]+'.__file__)'
# test(
# 'import test for '+p[0],
# py3_command,
# args: ['-c', import_command],
# env: envdata
# )
# endforeach
27 changes: 0 additions & 27 deletions pyoptsparse/postprocessing/meson.build

This file was deleted.

13 changes: 0 additions & 13 deletions pyoptsparse/pyALPSO/meson.build

This file was deleted.

22 changes: 4 additions & 18 deletions pyoptsparse/pyCONMIN/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ conmin_source = custom_target('conminmodule.c',
input : ['source/f2py/conmin.pyf',
],
output : ['conminmodule.c', 'conmin-f2pywrappers.f'],
command: [py3_command, '-m', 'numpy.f2py', '@INPUT@',
command: [py3, '-m', 'numpy.f2py', '@INPUT@',
'--lower', '--build-dir', 'pyoptsparse/pyCONMIN']
)

py3_target.extension_module('conmin',
py3.extension_module('conmin',
'source/openunit.f',
'source/cnmn00.f',
'source/cnmn01.f',
Expand All @@ -21,21 +21,7 @@ py3_target.extension_module('conmin',
'source/conmin.f',
'source/closeunit.f',
conmin_source,
fortranobject_c,
include_directories: [inc_np, inc_f2py],
dependencies : py3_dep,
dependencies: [fortranobject_dep],
subdir: 'pyoptsparse/pyCONMIN',
install : false,
install: true,
build_rpath: '')

#python_sources = [
# '__init__.py',
# 'pyCONMIN.py',
# 'LICENSE'
#]
#
#py3_target.install_sources(
# python_sources,
# pure: false,
# subdir: 'pyoptsparse/pyCONMIN'
#)
10 changes: 0 additions & 10 deletions pyoptsparse/pyIPOPT/meson.build

This file was deleted.

18 changes: 3 additions & 15 deletions pyoptsparse/pyNLPQLP/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ if HAS_NLPQLP
nlpqlp_source = custom_target('nlpqlpmodule.c',
input : ['source/f2py/nlpqlp.pyf'],
output : ['nlpqlpmodule.c'],
command: [py3_command, '-m', 'numpy.f2py', '@INPUT@',
command: [py3, '-m', 'numpy.f2py', '@INPUT@',
'--lower', '--build-dir', 'pyoptsparse/pyNLPQLP']
)

py3_target.extension_module('nlpqlp',
py3.extension_module('nlpqlp',
'source/wrapper.F90',
'source/NLPQLP.F',
'source/QL.F',
Expand All @@ -24,18 +24,6 @@ if HAS_NLPQLP
include_directories: [inc_np, inc_f2py],
dependencies : py3_dep,
subdir: 'pyoptsparse/pyNLPQLP',
install : false
install: true
)
endif

#python_sources = [
# '__init__.py',
# 'pyNLPQLP.py',
# 'LICENSE'
#]
#
#py3_target.install_sources(
# python_sources,
# pure: false,
# subdir: 'pyoptsparse/pyNLPQLP'
#)
16 changes: 2 additions & 14 deletions pyoptsparse/pyNSGA2/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ if swig.found()
command: ['swig', '-o', 'pyoptsparse/pyNSGA2/nsga2_wrap.c', '-python', '-interface', 'nsga2', '@INPUT@']
)

py3_target.extension_module('nsga2',
py3.extension_module('nsga2',
'source/allocate.c',
'source/auxiliary.c',
'source/crossover.c',
Expand All @@ -31,19 +31,7 @@ if swig.found()
dependencies : py3_dep,
subdir: 'pyoptsparse/pyNSGA2',
link_language: 'c',
install : false)
install: true)
else
message('SWIG was not found, therefore NSGA2 will not be built.')
endif

#python_sources = [
# '__init__.py',
# 'pyNSGA2.py',
# 'LICENSE'
#]
#
#py3_target.install_sources(
# python_sources,
# pure: false,
# subdir: 'pyoptsparse/pyNSGA2'
#)
Loading
Loading