Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
910eb9e
Modify inner meson.build files
whophil Apr 1, 2025
936256f
Modify outer meson.build
whophil Apr 1, 2025
4c978a9
setup.py -> pyproject.toml
whophil Apr 2, 2025
30e2bab
remove unused helper
whophil Apr 2, 2025
70be944
Add meson-python dep to win GHA
whophil Apr 2, 2025
8c0ec4b
Remove extra flags
whophil Apr 2, 2025
0f54b33
Remove chdir to tools
whophil Apr 2, 2025
8805551
Missed install: true for pyPSQP
whophil Apr 2, 2025
fad6c61
Add permalink to scipy snippets
whophil Apr 2, 2025
7250410
Remove extra #
whophil Apr 2, 2025
ba991c7
enable pyNSGA2 build
whophil Apr 2, 2025
af40d96
Get version dynamically via meson
whophil Apr 2, 2025
9463281
Replace py3_target -> py3
whophil Apr 2, 2025
f47c996
Remove postprocessing/meson.build
whophil Apr 2, 2025
a23ded1
Replace py3_command -> py3
whophil Apr 2, 2025
7072340
add back cyipopt in testing dep
ewu63 Apr 9, 2025
f891241
update NLPQLP
ewu63 Apr 9, 2025
52c9ef4
Exclude sources files, subdir README from wheel
whophil Apr 14, 2025
80f94a0
remove cycipopt from testing deps
ewu63 Apr 17, 2025
257b9f6
missed IPOPT in rebase
ewu63 Aug 14, 2025
309f359
fix nsga2
ewu63 Aug 14, 2025
c2f20aa
require ninja
ewu63 Aug 15, 2025
0e93e71
pre-commit
ewu63 Oct 8, 2025
078d5aa
update docs to reflect editable install instructions
ewu63 Oct 8, 2025
3655118
handle meson editable installs which have a separate builddir
ewu63 Nov 19, 2025
7d4df1f
handle case of empth tuple
ewu63 Nov 19, 2025
e5cb8a6
add [dev]
ewu63 Nov 19, 2025
7e51397
Merge branch 'main' into meson-py
kanekosh Dec 8, 2025
29e7771
Merge branch 'main' into meson-py
marcomangano Dec 12, 2025
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
19 changes: 19 additions & 0 deletions doc/contribute.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,22 @@ If you have an issue with pyOptSparse, a bug to report, or a feature to request,
This lets other users know about the issue.
If you are comfortable fixing the issue, please do so and submit a pull request.

Editable Installs
-----------------
Due to the use of ``meson-python`` as the backend, the typical process of using ``pip install -e .`` to generate an editable install cannot be used.
Instead, based on the instructions `here <https://mesonbuild.com/meson-python/how-to-guides/editable-installs.html#editable-installs>`__,
you must first install the `build dependencies` yourself.
This can be done by looking at the ``requires`` field of the ``[build-system]`` section of the ``pyproject.toml`` file, or via
``pip install .[dev]``

Then, do the following:

.. prompt:: bash

pip install --no-build-isolation --editable .

To run tests, ensure that the testing dependencies specified in the ``pyproject.toml`` file are also installed.

Coding style
------------
We use `ruff <https://github.com/astral-sh/ruff>`_ and `pre-commit <https://github.com/pre-commit/pre-commit/>`_ for linting and formatting.
Expand Down Expand Up @@ -49,6 +65,9 @@ When you add code or functionality, add tests that cover the new or modified cod
These may be units tests for individual components or regression tests for entire models that use the new functionality.
All the existing tests can be found under the ``test`` folder.

To run tests, ensure that the testing dependencies have been installed (see `pyproject.toml`).


Pull requests
-------------
Finally, after adding or modifying code, and making sure the steps above are followed, submit a pull request via the GitHub interface.
Expand Down
6 changes: 1 addition & 5 deletions doc/install.rst
Original file line number Diff line number Diff line change
Expand Up @@ -98,11 +98,7 @@ If you encounter a ``no module named tkinter`` error when trying to run optview,
Testing
-------
pyOptSparse provides a set of unit and regression tests to verify the installation.
To run these tests, first install ``testflo`` which is a testing framework developed by the OpenMDAO team:

.. prompt:: bash

pip install testflo
To run these tests, first install testing dependencies via ``pip install .[testing]``.

Then, in the project root directory, type:

Expand Down
74 changes: 55 additions & 19 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,44 @@ 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

if ff.has_argument('-Wno-conversion')
add_project_arguments('-Wno-conversion', 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 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
py3_dep = py3_target.dependency()

# --!>

# install python sources
install_subdir('pyoptsparse',
exclude_directories: [
'pyCONMIN/source',
'pyCONMIN/README',
'pyNLPQLP/source',
'pyNLPQLP/README',
'pyNSGA2/source',
'pyNSGA2/README',
'pyPSQP/source',
'pyPSQP/README',
'pySLSQP/source',
'pySLSQP/README',
'pySNOPT/source',
'pySNOPT/README'
],
install_dir: py3.get_install_dir())

# install non-python sources
subdir('pyoptsparse')
129 changes: 46 additions & 83 deletions pyoptsparse/meson.build
Original file line number Diff line number Diff line change
@@ -1,97 +1,60 @@
# 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.

Loading
Loading