Closed cimes-isi closed 2 years ago
Traceback (most recent call last): File "/Users/cimes/local/fogsys/EdgePipe/venv/bin/cmake", line 5, in <module> from cmake import cmake ModuleNotFoundError: No module named 'cmake'
It looks like CMake is not installed correctly in the temporary environment that Pip uses to build the package. You'll have to add cmake
to the [build-system].requires
list in your pyproject.toml
file.
Alternatively, you could try uninstalling CMake in your virtual environment (/Users/cimes/local/fogsys/EdgePipe/venv
) if you have CMake installed globally.
Interesting - thanks. I have cmake==3.22.2
in my virtualenv and the cmake binary on my PATH is from the virtualenv. Shouldn't cmake be a transitive dependency of py-build-cmake
?
I have
cmake==3.22.2
in my virtualenv and the cmake binary on my PATH is from the virtualenv.
Indeed, so when installing, the cmake “binary” used will be the one in your virtualenv. However, it's not really a binary, it's a Python wrapper that imports the cmake
module that then invokes the actual binary. Since the build uses its own virtual environment, the import fails, because the cmake
module is only installed in your own virtual environment, not the build environment.
I'm not sure why this happens, though, the entrypoints installed by pip should point to the correct Python interpreter, but that doesn't seem to be the case.
Shouldn't cmake be a transitive dependency of
py-build-cmake
?
I'm kind of on the fence about this: on the one hand it ensures that CMake will always be available, but on the other hand, many systems will have CMake installed globally, in which case it's kind of wasteful to install it through Pip in every virtual environment and for each build.
Instead of making it a hard dependency, I might consider checking whether CMake is available in the get_requires_for_build_wheel
hook, and returning it as a dependency if CMake is not in the path or if the one in the path doesn't work.
Something is clearly wrong if it's using the virtualenv's cmake
wrapper application which then can't find its cmake
module. I'd think if it's building in a new environment, it should just fall back on the (real) system cmake application. Outside my virtualenv:
$ which cmake
/opt/homebrew/bin/cmake
I certainly understand not wanting to add unnecessary dependencies. I'd hope that any workaround you might provide can be implemented reliably, otherwise you may end up playing whack-a-mole. If this is actually a bug in pip
, I suggest we open an issue upstream with them. Are you able to reproduce this issue?
Something is clearly wrong if it's using the virtualenv's cmake wrapper application which then can't find its cmake module. I'd think if it's building in a new environment, it should just fall back on the (real) system cmake application.
It should, but this is not possible, because the bin
folder of your virtual environment comes first in the PATH
, even when build
creates a a sub-environment for you.
You can run python -m build . -C=verbose
to see the PATH
used when building. For me, it is:
'PATH': '/tmp/build-env-xyzxyzx/bin:/home/pieter/GitHub/py-build-cmake/py-venv/bin:/home/pieter/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin'
As expected, the temporary build environment is first, then your own virtual environment, and finally the system paths.
If your own virtual environment contains a cmake
script/binary, it will always pick that one over the global one. To work around this, I would have to patch the PATH
manually, which I want to avoid.
I'd hope that any workaround you might provide can be implemented reliably, otherwise you may end up playing whack-a-mole.
I just pushed this change: https://github.com/tttapa/py-build-cmake/commit/5d192449c028b6db5c8eb64f6d6678d3a32c6b94 It conditionally checks whether CMake is available, new enough, and works correctly. If not, it tells the build frontend to install it as a build dependency.
If this is actually a bug in pip, I suggest we open an issue upstream with them. Are you able to reproduce this issue?
I'm not sure, I just checked on my machine (Ubuntu 20.04, Python 3.9, pip 22.0.4), and I get:
$ cat ./py-venv/bin/cmake
#!/home/pieter/GitHub/py-build-cmake/py-venv/bin/python3
# -*- coding: utf-8 -*-
import re
import sys
from cmake import cmake
if __name__ == '__main__':
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
So the shebang is correct and the script should work correctly, regardless of whether the virtual environment is activated or not. I'm not sure if that differs on macOS (I would hope not).
I also see a similar PATH
structure during build and my virtualenv cmake wrapper also correctly uses the virtualenv's python3
in the shebang. I can also run it without failure both inside and outside the virtualenv, so the virtualenv's python3
seems to find the cmake package dependency installed in the virtualenv correctly, even when the virtualenv isn't activated. That behavior seems strange to me, but I'm not a Python or virtualenv expert.
So if pip install
creates a sub-environment inheriting PATH
which has executables that depend on packages installed in the virtualenv, but it can't resolve them, that seems like a bug to me since it correctly found them when the virtualenv isn't activated. My intuition is then that the bug is in whatever package actually configures/manages the sub-environment, which sounds like pip
, but I don't know enough about the build process to say with any certainty.
I agree this would be difficult to work around and that you should not try to outsmart the existing PATH
.
RE: 5d19244: is cmake 3.20 really the minimum version required? I think a lot of systems won't have that new of a version. Ubuntu 20.04 only has cmake 3.16.3, and 18.04 has 3.10.2.
So if
pip install
creates a sub-environment inheritingPATH
which has executables that depend on packages installed in the virtualenv, but it can't resolve them, that seems like a bug to me since it correctly found them when the virtualenv isn't activated. My intuition is then that the bug is in whatever package actually configures/manages the sub-environment, which sounds likepip
, but I don't know enough about the build process to say with any certainty.
It might actually be a feature, not a bug: pip deliberately disallows access to system packages:
I could clear the PYTHONPATH
environment variable set by pip before invoking cmake, so that pip's override is not loaded. However, this will have unexpected side effects.
For now, I think the best option is manually checking whether CMake works and installing it if not.
RE: https://github.com/tttapa/py-build-cmake/commit/5d192449c028b6db5c8eb64f6d6678d3a32c6b94: is cmake 3.20 really the minimum version required? I think a lot of systems won't have that new of a version. Ubuntu 20.04 only has cmake 3.16.3, and 18.04 has 3.10.2.
No, older versions should work fine as well. I'll have to test some older versions and select an appropriate value before releasing.
That's interesting, but does pip
consider packages in the virtualenv to be "system" packages?
I also feel that clearing PYTHONPATH
is not a good idea. I'd hope you wouldn't have to modify anything global like that.
I'm trying to test your recent changes, but pip doesn't seem to want to use the version I installed locally from git into my virtualenv. pip freeze
shows -e git+ssh://git@github.com/tttapa/py-build-cmake.git@5d192449c028b6db5c8eb64f6d6678d3a32c6b94#egg=py_build_cmake
, but installing my project then with pip install . -vvv
seems to still be using 0.0.7, even after purging pip's cache. I feel like I'm missing something silly here - do I need to do something different to force pip install
to use the development version?
That's interesting, but does
pip
consider packages in the virtualenv to be "system" packages?
I believe so, yes. AFAICT, the code I referred to removes the packages in the virtualenv as well: it removes the paths in get_platlib()
and get_purelib()
. In my virtual environment, I get:
>>> from pip._internal.locations import get_platlib, get_purelib
>>> get_platlib()
'C:\\Users\\piete\\Documents\\GitHub\\py-build-cmake\\py-venv\\Lib\\site-packages'
>>> get_purelib()
'C:\\Users\\piete\\Documents\\GitHub\\py-build-cmake\\py-venv\\Lib\\site-packages'
do I need to do something different to force
pip install
to use the development version?
You have to point to the development version in your pyproject.toml
build requirements:
[build-system]
requires = ["py-build-cmake @ git+https://github.com/tttapa/py-build-cmake"]
build-backend = "py_build_cmake.build"
That's interesting, but does
pip
consider packages in the virtualenv to be "system" packages?I believe so, yes. AFAICT, the code I referred to removes the packages in the virtualenv as well: it removes the paths in
get_platlib()
andget_purelib()
. In my virtual environment, I get:>>> from pip._internal.locations import get_platlib, get_purelib >>> get_platlib() 'C:\\Users\\piete\\Documents\\GitHub\\py-build-cmake\\py-venv\\Lib\\site-packages' >>> get_purelib() 'C:\\Users\\piete\\Documents\\GitHub\\py-build-cmake\\py-venv\\Lib\\site-packages'
Testing this on my system gives similar results, so it looks like you're correct.
do I need to do something different to force
pip install
to use the development version?You have to point to the development version in your
pyproject.toml
build requirements:[build-system] requires = ["py-build-cmake @ git+https://github.com/tttapa/py-build-cmake"] build-backend = "py_build_cmake.build"
I figured it was something stupid-simple like that, thank you. Your changes appear to work without me having to explicitly declare cmake
as a build dependency.
RE: 5d19244: is cmake 3.20 really the minimum version required? I think a lot of systems won't have that new of a version. Ubuntu 20.04 only has cmake 3.16.3, and 18.04 has 3.10.2.
No, older versions should work fine as well. I'll have to test some older versions and select an appropriate value before releasing.
Related to automatically detecting cmake, I was thinking about version requirements a bit more and realized the a min cmake version may be specified in several places. Without having looked into your codebase, I expect there's at least the CLI compatibility that you need, which I'd expect would allow you to support considerably older versions. However, even if your version requirements are satisfied, the user's top-level project CMakeLists.txt
should define their own min version. In more complex setups, a user may even have a tree of cmake project
s which each define a potentially different minimum cmake version. The most reliable way to detect if an existing cmake is compatible might be to just run cmake
against the source_path
specified in pyproject.toml
prior to doing the build in the sub-environment, but it could be a fair bit of overhead to run cmake
twice. I'm not sure if this is a problem that actually needs to be solved, or if you should just let it fail and/or require the user to specify a min cmake version explicitly in pyproject.toml
. Anyway, just thinking out loud.
Cheers.
I expect there's at least the CLI compatibility that you need, which I'd expect would allow you to support considerably older versions.
I think the main things that affect compatibility are some features of the FindPython3 module.
I'm not sure if this is a problem that actually needs to be solved
Indeed, in my opinion, it's up to the user to make sure a new enough CMake version is available. Trying to find a suitable CMake version for the specific project is beyond the scope of this package. Installing CMake if pip breaks the existing version is reasonable, but I wouldn't go beyond that.
or require the user to specify a min cmake version explicitly in
pyproject.toml
.
That might be what I'll end up doing.
I expect there's at least the CLI compatibility that you need, which I'd expect would allow you to support considerably older versions.
I think the main things that affect compatibility are some features of the FindPython3 module.
I'd actually argue against that being a concern on your end, I think you should just enforce what is required for py-build-cmake to interact correctly with cmake. For example, my native code doesn't require a Python3 module at all, it's just building more traditional C/C++ without any Python awareness. If a user's CMakeLists uses FindPython3
, then any features they require from it may affect their minimum cmake version.
Since finding that module is likely to be a common use case though, there may be value in discussing it in your documentation. Depending on how much detail you provide, it could be a maintenance burden as that cmake module evolves.
That's just my 2 cents though :).
Thanks, I appreciate your input on this.
I have added a cmake.minimum_version
configuration option to pyproject.toml
(docs/Config.md#cmake). The default is none
(i.e. any version will do, as long as cmake --version
exits with status 0).
If you specify this option, e.g. minimum_version = "3.18"
, and if there is no cmake
program in the PATH
, if the one in the PATH
is broken, or if it is older than the version specified, py-build-cmake will add "cmake>=3.18"
to the build requirements, so it will automatically be installed before starting the build.
That appears to work, and is the most straightforward approach I can think of. Users will just have to remember to update the min version spec in both their CMakeLists.txt
and in pyproject.toml
if/when it changes.
From my perspective, this issue is resolved. Thanks again for your help. Are you planning on making a new release sometime soon? It would be helpful for me so that I can depend on a versioned package in PyPI rather than pointing my build dependency to the github repo. (I don't need it ASAP, but sometime in the near future would be nice.). Cheers.
I've just created a new release: https://github.com/tttapa/py-build-cmake/releases/tag/0.0.8
Thanks!
Thanks for working on this project. It seems to be what I'm looking for so I'm trying it out to build a C++ executable in my project which I use as a subprocess. This should be an even simpler use case than the current examples in that I don't actually need to generate Python bindings.
I'm using the latest version
0.0.7
.I can build:
But installation fails (both normal and editable):
It doesn't show the stdout/stderr for the cmake command that failed. My cmake project depends on another package (located with
find_package
) and links the executable against it, so perhaps thepip install
environment is somehow different than thebuild
environment? It's difficult to tell without additional output.CMakeLists.txt:
Snippet from pyproject.toml: