8000 BUG: f2py cannot find .mod files automatically for the new meson backend · Issue #27018 · numpy/numpy · GitHub
[go: up one dir, main page]

Skip to content

BUG: f2py cannot find .mod files automatically for the new meson backend #27018

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
1234zou opened this issue Jul 23, 2024 · 14 comments
Closed

BUG: f2py cannot find .mod files automatically for the new meson backend #27018

1234zou opened this issue Jul 23, 2024 · 14 comments

Comments

@1234zou
Copy link
1234zou commented Jul 23, 2024

Describe the issue:

I'm trying to compile my Fortran code using f2py. For python>=3.12, the meson backend is now the default. But meson/f2py cannot automatically find the generated .mod files in the current directory. These .mod files could be automatically found for older versions of python and distutils backend. By the way, gcc/gfortran in this machine is 4.8.5.

Thanks for any kind help and suggestion!

Best,
Jingxiang

Reproduce the code example:

A simple example is shown using two Fortran files and one Python script:

here is a.f90

module para
 implicit none
 integer, parameter :: n = 5
end module para

b.f90

subroutine prt_n
 use para, only: n
 implicit none
 write(6,'(A,I2)') 'n=', n
end subroutine prt_n

Compiling commands are

gfortran a.f90 -c
f2py -c a.o b.f90 -m test

The first command would generate a.o and para.mod files in the current directory. para.mod can be automatically detected for the distutils backend, but not for the meson backend.

test.py

from test import prt_n
prt_n()

Assuming that the test.xxx.so is generated (which not True for the meson backend), the result would be n= 5.

Error message:

After running f2py -c a.o b.f90 -m test, the error message is

Found ninja-1.10.2 at /public/home/jxzou/software/anaconda3/envs/py312/bin/ninja
INFO: autodetecting backend as ninja
INFO: calculating backend command to run: /public/home/jxzou/software/anaconda3/envs/py312/bin/ninja -C /public/home/jxzou/tmp/tmpgljbyueg/bbdir
ninja: Entering directory `/public/home/jxzou/tmp/tmpgljbyueg/bbdir'
[3/6] Compiling Fortran object test.cpython-312-x86_64-linux-gnu.so.p/b.f90.o
FAILED: test.cpython-312-x86_64-linux-gnu.so.p/b.f90.o
gfortran -Itest.cpython-312-x86_64-linux-gnu.so.p -I. -I.. -I/public/home/jxzou/software/anaconda3/envs/py312/lib/python3.12/site-packages/numpy/core/include -I/public/home/jxzou/software/anaconda3/envs/py312/lib/python3.12/site-packages/numpy/f2py/src -I/public/home/jxzou/software/anaconda3/envs/py312/include/python3.12 -fvisibility=hidden -D_FILE_OFFSET_BITS=64 -Wall -O3 -fPIC -Jtest.cpython-312-x86_64-linux-gnu.so.p -o test.cpython-312-x86_64-linux-gnu.so.p/b.f90.o -c ../b.f90
../b.f90:2.5:

 use para, only: n
     1
Fatal Error: Can't open module file 'para.mod' for reading at (1): No such file or directory
[5/6] Compiling C object test.cpython-312-x86_64-linux-gnu.so.p/3fa16a5cf62c3f6199e0fa73eb3f172c17c452f1_.._.._f2py_src_fortranobject.c.o
ninja: build stopped: subcommand failed.
Traceback (most recent call last):
  File "/public/home/jxzou/software/anaconda3/envs/py312/bin/f2py", line 11, in <module>
    sys.exit(main())
             ^^^^^^
  File "/public/home/jxzou/software/anaconda3/envs/py312/lib/python3.12/site-packages/numpy/f2py/f2py2e.py", line 766, in main
    run_compile()
  File "/public/home/jxzou/software/anaconda3/envs/py312/lib/python3.12/site-packages/numpy/f2py/f2py2e.py", line 738, in run_compile
    builder.compile()
  File "/public/home/jxzou/software/anaconda3/envs/py312/lib/python3.12/site-packages/numpy/f2py/_backends/_meson.py", line 178, in compile
    self.run_meson(self.build_dir)
  File "/public/home/jxzou/software/anaconda3/envs/py312/lib/python3.12/site-packages/numpy/f2py/_backends/_meson.py", line 173, in run_meson
    self._run_subprocess_command(compile_command, build_dir)
  File "/public/home/jxzou/software/anaconda3/envs/py312/lib/python3.12/site-packages/numpy/f2py/_backends/_meson.py", line 167, in _run_subprocess_command
    subprocess.run(command, cwd=cwd, check=True)
  File "/public/home/jxzou/software/anaconda3/envs/py312/lib/python3.12/subprocess.py", line 571, in run
    raise CalledProcessError(retcode, process.args,
subprocess.CalledProcessError: Command '['meson', 'compile', '-C', 'bbdir']' returned non-zero exit status 1.

Python and NumPy Versions:

python>=3.12 and numpy>=1.26

Runtime Environment:

[{'numpy_version': '1.26.4',
  'python': '3.12.4 | packaged by Anaconda, Inc. | (main, Jun 18 2024, '
            '15:12:24) [GCC 11.2.0]',
  'uname': uname_result(system='Linux', node='mu003', release='3.10.0-862.el7.x86_64', version='#1 SMP Fri Apr 20 16:44:24 UTC 2018', machine='x86_64')},
 {'simd_extensions': {'baseline': ['SSE', 'SSE2', 'SSE3'],
                      'found': ['SSSE3',
                                'SSE41',
                                'POPCNT',
                                'SSE42',
                                'AVX',
                                'F16C',
                                'FMA3',
                                'AVX2',
                                'AVX512F',
                                'AVX512CD',
                                'AVX512_SKX',
                                'AVX512_CLX',
                                'AVX512_CNL',
                                'AVX512_ICL'],
                      'not_found': ['AVX512_KNL', 'AVX512_KNM']}},
 {'filepath': '/public/home/jxzou/software/anaconda3/envs/py312/lib/libmkl_rt.so.2',
  'internal_api': 'mkl',
  'num_threads': 64,
  'prefix': 'libmkl_rt',
  'threading_layer': 'intel',
  'user_api': 'blas',
  'version': '2023.1-Product'},
 {'filepath': '/public/home/jxzou/software/anaconda3/envs/py312/lib/libiomp5.so',
  'internal_api': 'openmp',
  'num_threads': 64,
  'prefix': 'libiomp',
  'user_api': 'openmp',
  'version': None}]

Context for the issue:

I'm a developer of the open source package MOKIT. By using f2py, we offer lots of convenient and efficient Python APIs to users in the computational chemistry field. We are grateful for your numpy f2py functionalities. MOKIT source code are mainly written using Fortran, so we heavily reply on the f2py to obtain corresponding .so files.

Currently my workaround is to change all .o filenames into .f90 ones in f2py commands, e.g.

f2py -c a.f90 b.f90 -m test

This is a tiny change for this simple example. But a somewhat big work to do in MOKIT. And this change would make test.so contain more APIs than previous. For example, if there are many subroutines in a.f90, it will be all embraced into test.so. This problem does not exist when the distutils backend is used previously. I do not know whether this is a feature or bug for the new meson backend. I'm wondering if there is any elegant solution.

@1234zou
Copy link
Author
1234zou commented Jul 24, 2024

Hi, @HaoZeke , would you please add the tag component: numpy.f2py for this issue? It seems that I cannot add this tag by myself. Thanks a lot.

@HaoZeke
Copy link
Member
HaoZeke commented Jul 24, 2024

Thanks for the issue and ping.

I'm thinking that there is probably no very good default solution here, since the meson backend doesn't have any information on the other .f90 or .mod files unless they are part of the f2py call right now (which is not desirable due to API scope creep as noted). The best way would be to use --builddir and modify the generated meson.build files to compile the Fortran codes (like param) and link them to the .so extension.

To make this automatic, something which we could support but really probably shouldn't is to take a list of fortran files to be compiled and linked but not otherwise processed by F2PY. In general though, for all but trivial cases this will not be enough so it doesn't make a lot of sense as a feature.

I think, with the new backend projects are slightly encouraged to transition the entire build system to meson for ease of use, but the documentation definitely should cover all this.

Another option is to try to pass the .mod files directly as object files, or as link arguments, will look into that.

@HaoZeke HaoZeke changed the title f2py cannot find .mod files automatically for the new meson backend BUG: f2py cannot find .mod files automatically for the new meson backend Jul 24, 2024
@1234zou
Copy link
Author
1234zou commented Jul 24, 2024

Oh, thanks for the quick and detailed response! My personal viewpoint is: if meson backend cannot auto-detect .mod files, it would be better to provide a command line argument, say -moddir, which tells meson the location of .mod files, in the future f2py. Just a suggestion.

Thanks again for your kind help.

@P-Kaempf
Copy link
Contributor
P-Kaempf commented Aug 9, 2024

The issue here is that the module cannot be found if there is no executable code inside. I had the same problem and posted a question to Stackexchange: https://stackoverflow.com/questions/78839287/f2py-in-numpy-2-0-1-does-not-expose-variables-the-way-numpy-1-26-did-how-can-i/78851726#78851726

An ugly workaround is to simply add some executable code inside the module, so it contains not only assignments. This got me unstuck. Just follow the answer on Stackexchange.

And I still think this is a bug, but it can be worked around.

@HaoZeke
Copy link
Member
HaoZeke commented Aug 9, 2024

The issue here is that the module cannot be found if there is no executable code inside. I had the same problem and posted a question to Stackexchange: https://stackoverflow.com/questions/78839287/f2py-in-numpy-2-0-1-does-not-expose-variables-the-way-numpy-1-26-did-how-can-i/78851726#78851726

An ugly workaround is to simply add some executable code inside the module, so it contains not only assignments. This got me unstuck. Just follow the answer on Stackexchange.

And I still think this is a bug, but it can be worked around.

No that's not really the issue discussed here. Adding executable code makes F2PY bind it, which has the same problem as discussed in the original code, i.e. there will be bindings which shouldn't be exposed to the user.

Arguably the right fix is not the stackexchange answer (might answer there later) but as discussed to make the project skeleton, and modify the meson.build to build the fortran module, before linking it to the target generated by f2py.

EDIT: The stackexchange question is actually separate and would be a bug, if the user passes a variable only fortran file, F2PY should wrap it, could you open another issue for that @P-Kaempf?

@P-Kaempf
Copy link
Contributor
P-Kaempf commented Aug 10, 2024

The Stackexchange answer is for a specific question. Maybe it is not exactly your question, but the answer still holds true for the original question. Did you really downvote it? That helps nobody.

@HaoZeke
Copy link
Member
HaoZeke commented Aug 10, 2024

The Stackexchange answer is for a specific question. Maybe it is not exactly your question, but the answer still holds true for the original question. Did you really downvote it? That helps nobody.

That was hasty, and reverted (though it is worth noting that it was irrelevant to this issue). Please open another issue related to the question.

@pescap
Copy link
pescap commented Sep 22, 2024

Dear all,
I am facing the same issue:

gfortran a.f90 -c
f2py -c a.o b.f90 -m test

Unfortunately, my a.f90 file includes custom data structures that cannot be compiled via f2py (e.g., type(curve)), but that I need to import to run f2py b.f90.

Everything was working fine with distutils, but I have not found a solution with meson. Do you suggest that I wait before migrating?

Thank you for your help,

@HaoZeke
Copy link
Member
HaoZeke commented Sep 23, 2024

Dear all, I am facing the same issue:

gfortran a.f90 -c
f2py -c a.o b.f90 -m test

Unfortunately, my a.f90 file includes custom data structures that cannot be compiled via f2py (e.g., type(curve)), but that I need to import to run f2py b.f90.

Everything was working fine with distutils, but I have not found a solution with meson. Do you suggest that I wait before migrating?

Thank you for your help,

Could you please provide a minimum working example showcasing the bug so it can be reproduced? In theory if the type(curve) part is not part of the F2PY call it should be fine, e.g. see the use of types in https://github.com/HaoZeke/GaussJacobiQuad

@jeanwsr
Copy link
jeanwsr commented Dec 10, 2024

Thanks for the issue and ping.

I'm thinking that there is probably no very good default solution here, since the meson backend doesn't have any information on the other .f90 or .mod files unless they are part of the f2py call right now (which is not desirable due to API scope creep as noted). The best way would be to use --builddir and modify the generated meson.build files to compile the Fortran codes (like param) and link them to the .so extension.

To make this automatic, something which we could support but really probably shouldn't is to take a list of fortran files to be compiled and linked but not otherwise processed by F2PY. In general though, for all but trivial cases this will not be enough so it doesn't make a lot of sense as a feature.

I think, with the new backend projects are slightly encouraged to transition the entire build system to meson for ease of use, but the documentation definitely should cover all this.

Another option is to try to pass the .mod files directly as object files, or as link arguments, will look into that.

Hi @HaoZeke , is there any plan for making current f2py support the "automatically find .mod" condition or adding documentations to show how to manually achieve this (by transition build system to meson)? We are looking forward to a final solution to this issue since it has prevent us from using new versions of numpy-f2py for a while.

@krystophny
Copy link
krystophny commented Jan 3, 2025

Part of a workaround:

export FFLAGS=-I`pwd`

However, the object code still cannot be found when loading the python module, maybe it is not linked. So still ping. The ability to add precompiled objects of modules is essential for f90wrap to work properly, see jameskermode/f90wrap#226 and in CMake build process. Many people will want to continue using CMake and scikit-build-core and not switch to Meson.

@HaoZeke
Copy link
Member
HaoZeke commented Jan 18, 2025

This is a bit of a red herring. The compiler and meson will will handle .mod files as long as they are part of the include directive, and passing -I to f2py works as intended.

Specifically for this example:

➜ python -m numpy.f2py -c a.o b.f90 -m test --build-dir bdir -I$(pwd) --backend meson
Using meson backend
Will pass --lower to f2py
See https://numpy.org/doc/stable/f2py/buildtools/meson.html
Reading fortran codes...
	Reading file 'b.f90' (format:free)
Post-processing...
	Block: test
			Block: prt_n
In: :test:b.f90:prt_n
get_useparameters: no module para info used by prt_n
Applying post-processing hooks...
  character_backward_compatibility_hook
Post-processing (stage 2)...
Building modules...
    Building module "test"...
    Generating possibly empty wrappers"
    Maybe empty "test-f2pywrappers.f"
        Constructing wrapper function "prt_n"...
          prt_n()
    Wrote C/API module "test" to file "./testmodule.c"
The Meson build system
Version: 1.6.0
Source dir: /home/rgoswami/Git/Github/Quansight/numscipy_playground/numpy_bugs/gh-27018/bdir
Build dir: /home/rgoswami/Git/Github/Quansight/numscipy_playground/numpy_bugs/gh-27018/bdir/bbdir
Build type: native build
Project name: test
Project version: 0.1
Fortran compiler for the host machine: /home/rgoswami/micromamba/envs/numpy-dev/bin/x86_64-conda-linux-gnu-gfortran (gcc 13.3.0 "GNU Fortran (conda-forge gcc 13.3.0-1) 13.3.0")
Fortran linker for the host machine: /home/rgoswami/micromamba/envs/numpy-dev/bin/x86_64-conda-linux-gnu-gfortran ld.bfd 2.43
C compiler for the host machine: /home/rgoswami/micromamba/envs/numpy-dev/bin/x86_64-conda-linux-gnu-cc (gcc 13.3.0 "x86_64-conda-linux-gnu-cc (conda-forge gcc 13.3.0-1) 13.3.0")
C linker for the host machine: /home/rgoswami/micromamba/envs/numpy-dev/bin/x86_64-conda-linux-gnu-cc ld.bfd 2.43
Host machine cpu family: x86_64
Host machine cpu: x86_64
Program /home/rgoswami/micromamba/envs/numpy-dev/bin/python found: YES (/home/rgoswami/micromamba/envs/numpy-dev/bin/python)
Found pkg-config: YES (/home/rgoswami/micromamba/envs/numpy-dev/bin/pkg-config) 0.29.2
Run-time dependency python found: YES 3.12
Library quadmath found: YES
Build targets in project: 1

Found ninja-1.12.1 at /home/rgoswami/micromamba/envs/numpy-dev/bin/ninja
INFO: autodetecting backend as ninja
INFO: calculating backend command to run: /home/rgoswami/micromamba/envs/numpy-dev/bin/ninja -C /home/rgoswami/Git/Github/Quansight/numscipy_playground/numpy_bugs/gh-27018/bdir/bbdir
ninja: Entering directory `/home/rgoswami/Git/Github/Quansight/numscipy_playground/numpy_bugs/gh-27018/bdir/bbdir'
[6/6] Linking target test.cpython-312-x86_64-linux-gnu.so

This then works as expected:

>>> import test
>>> test.prt_n()
n= 5

@krystophny the reason why it isn't enough to set the environment variable is because internally when f2py gets the -I flag it is translated into a section in the corresponding meson.build file (which you don't otherwise get).

py.extension_module('test',
                     [
                     '''b.f90''',
                     '''testmodule.c''',
                     '''test-f2pywrappers.f''',
                     fortranobject_c
                     ],
                     include_directories: [
                     inc_np,
                     '''/home/rgoswami/Git/Github/numscipy_playground/numpy_bugs/gh-27018''',
                     ],
                     dependencies : [
                     py_dep,
                     quadmath_dep,



                     ],

                     install : true)

Closing for now, but perhaps a documentation update would be worthwhile.

@HaoZeke HaoZeke closed this as completed Jan 18, 2025
@1234zou
Copy link
Author
1234zou commented Jan 20, 2025

Thanks a lot, @HaoZeke . You have shown a great solution for the original problem I proposed in this issue, which is adding -I$(pwd). And it does work. But I find that when it turns to a slightly advanced example, adding -I$(pwd) cannot solve the problem. For example, here is a.f90

module para
 implicit none
 integer :: n
end module para

and b.f90

subroutine prt_n
 use para, only: n
 implicit none
 n = 5
 write(6,'(A,I2)') 'n=', n
end subroutine prt_n

Compiling commands are

gfortran a.f90 -c
f2py -c a.o b.f90 -m test -I$(pwd)

The file test.cpython-312-x86_64-linux-gnu.so can be generated, but it leads to errors when importing

>>> from test import prt_n
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ImportError: /public/home/jxzou/wfu/pbc_pm_test/test.cpython-312-x86_64-linux-gnu.so: undefined symbol: __para_MOD_n

Do you have any suggestion?

Best wishes,
Jingxiang

@HaoZeke
Copy link
Member
HaoZeke commented Jan 20, 2025

Thanks a lot, @HaoZeke . You have shown a great solution for the original problem I proposed in this issue, which is adding -I$(pwd). And it does work. But I find that when it turns to a slightly advanced example, adding -I$(pwd) cannot solve the problem. For example, here is a.f90

module para
 implicit none
 integer :: n
end module para

and b.f90

subroutine prt_n
 use para, only: n
 implicit none
 n = 5
 write(6,'(A,I2)') 'n=', n
end subroutine prt_n

Compiling commands are

gfortran a.f90 -c
f2py -c a.o b.f90 -m test -I$(pwd)

The file test.cpython-312-x86_64-linux-gnu.so can be generated, but it leads to errors when importing

>>> from test import prt_n
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ImportError: /public/home/jxzou/wfu/pbc_pm_test/test.cpython-312-x86_64-linux-gnu.so: undefined symbol: __para_MOD_n

Do you have any suggestion?

Best wishes, Jingxiang

Thanks for the report (and the new issue). I've responded there, please feel free to continue the discussion there.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants
0