#scikit-build
1 messages · Page 1 of 1 (latest)
It's probably fine here, you can always create a thread for it!
We aren't too formal here, just ask your questions so you get the answers you need faster :)
Okay, so currently I'm building a Python wheel which uses Pybind11 to make bindings for a C++ project, and this is all managed through setup.py and CMake. It currently links to the C++ library which is built with CMake from a CMakeLists.txt in a parent folder, and the bindings are in a subfolder (bindings/python/...).
What I did to make this work was add code to build and run a few CMake commands in the setup.py by adding to a build_ext in the cmdclass of the setup() call, much like how it is done in this SO post: https://stackoverflow.com/questions/42585210/extending-setuptools-extension-to-use-cmake-in-setup-py
This way I can cmake -S ../../ ...(etc) to build the dependency using the CMakeLists.txt in the parent project's folder, then run a cmake --build . --target pyExample --config Release to do the build of the bindings, all within a single call to python setup.py bdist_wheel giving me a wheel file. The intermediary .pyd is moved using -DCMAKE_LIBRARY_OUTPUT_DIRECTORY_RELEASE and -DCMAKE_LIBRARY_OUTPUT_DIRECTORY so the wheel can be built with it.
I'm running into some issues trying to move this whole process to a python -m build --wheel call with scikit-build-core, specifically with how to run cmake on the parent source folder to build the C++ library to link against. Is there a standard approach for how to do this, or any advice you all may have? Maybe some examples or another project that is doing this? Build systems are definitely not my strong suit and I could use some help, thanks in advance
Just checking in to see if anyone has any ideas on this, maybe @urban turtle ?
Ahh, I guess I thought by making the channel I’d be notified about posts here!
Scikit-build and scikit-build-core both go here. Same thing as the GitHub discussions, there’s just one for both. Eventually scikit-build will use scikit-build-core too.
Do you have a repo or anything I can see? I think what you want is pretty standard. Main thing different from the “setuptools” examples is you’ll want to use the install step too
Why you have might be similar to pybind/cmake_example, you want something like pybind/scikit_build_example.
Unfortunately I can't share the exact repo I'm working with, but after some searching on github it looks like this project is setup very similarly by building the dependency in a parent directory with cmake first and then moving the resultant file: https://github.com/lief-project/LIEF/blob/master/api/python/setup.py
I realized if I just move the pyproject.toml to the root directory I can kick everything off there and handle the directory issue. I feel kinda dumb for not realizing that earlier, thanks for all the help
Yes, you can specify a subdirectory for your CMakeLists, but things are much easier if you have pyproject.toml at top-level, especially if you are using files from other places in the repo.
I'm trying to migrate a scikit-build-core based install to a build server which has no internet connection, but I'm failing when installing the packages in the venv:
ERROR: Could not find a version that satisfies the requirement scikit-build-core (from versions: none)
Is there a way to specify a local directory to use for the installation in this case? @urban turtle
Do you mean when pip installing it or when doing the build? If the latter, then no build isolation is the easiest way usually. But you can actually still use build isolation if you specify a local wheel dir via env var. I do that for our tests, actually.
But need a tiny bit more context to provide recipes. 🙂
was trying out scikit-build-core because i'm looking to replace my hacks with editable.rebuild=true
it's working pretty well so far, but i think editable.rebuild is racy when running multiple processes, probably needs a filelock somewhere
Thanks a bunch for this! I'm just getting back to this and will give a local dir via env var a try, looking for the test cases to help. It's during the build, so that sounds like exactly what I need 🙏
"But you can actually still use build isolation if you specify a local wheel dir via env var. I do that for our tests, actually"
Do you know which env var? could you link me to the tests, @urban turtle ?
Yes, it's here: https://github.com/scikit-build/scikit-build-core/blob/851d8790dfcf556d684f98d08c188ce1e90d8e3a/tests/conftest.py#L137-L138
Is there a way I can specify a specific cmake executable to use for a build?
I could make a custom wheel and use that from a local directory, but I'd rather avoid that if I can
Currently it looks for the installed cmake package, then cmake3 and cmake in the path. We don't currently have an envvar for that.
I see, but the env installer determines it needs cmake earlier than that, right? https://github.com/pypa/build/blob/699d7a018b641e732c8375c2538cb8b8625fc4c0/src/build/env.py#L114
Does that use a requirements.txt from scikit-build-core or something else? If I can make that find the version of cmake I want it seems to me that SBC would inherit from that too, yeah?
That code is triggered from get_requires_for_build_wheel, which is a hook in scikit-build-core called by build during the install phase. If you have a sufficent version of cmake or cmake3 on your path, it will not request cmake the package.
(Unless you list cmake in your package requirements, which you shouldn't)
Great info, thanks again
Hope you don't mind a repost on this channel since in #cpython it did not get any response:
I am using a design like:
ctypes.cdll.LoadLibrary(Path(bundled_lib))
import _python_binding
In this example, the _python_binding module is built with target_link_libraries to the one in bundled_lib. As far as I have tested, it works fine, but I got a report that on alpine docker (muslinux specifically I think) this one failed, i.e. the library in bundled_lib is not detected by _python_binding. Anyone got an idea of that?
I am trying to get in touch with relevant upstream about this issue. Any idea who/where to ping about this, other than cpython repo?
you could try https://discuss.python.org/
Yeah, I've posted there as well
Basically I have a snippet that does: ctypes.cdll.LoadLibrary(Path(bundled_lib)) import _python_binding where the _python_binding module is built with link_libraries to the one in bundled_lib. As far as I have tested, it works fine on manylinux, but I got a report that on alpine docker (muslinux) it fails. Anyone got an idea of what...
Does scikit-build-core provide a way to use different cmake args/build flags when running on different environments?
For example:
[tool.scikit-build]
cmake.args = [
"-DLLD_DIR=C:/..."
Could I specify different cmake.args if building on say, linux vs windows?
Not yet, that's overrides, which I haven't implemented yet. https://github.com/scikit-build/scikit-build-core/issues/332
I started to implement it but then figured out how to do conditional Limited API / Stable API nicely without it, so it hasn't been a priority yet. Though it looked pretty straightforward, so it's probably not too far off.
I've done it once before, in cibuildwheel
Adding the schema's in 0.5 complicates adding it a little bit from my initial attempt, but https://github.com/pypa/cibuildwheel/pull/1622 managed it for cibuildwheel, so it should be possible to do it nicely.
Started working on it here: https://github.com/scikit-build/scikit-build-core/pull/514
Calling all bikeshedders! if, when, or select for overrides? https://github.com/scikit-build/scikit-build-core/issues/332#issuecomment-1740136316
That was fast, thank you so much @urban turtle ! Already using the feature 💯
Do you have a link where I can see it in use? Curious to see it in practice. 🙂
Sadly no, it's not a public project (not my code base) 😦
I'll likely use it on something open source though, and will ping you when/if I do!
That's okay, and thanks!
np, I made a pull request for an additional override choice that I'm using if that's any consolation
Monthly Scikit-build Community meeting starting in 25 mins. https://meet.google.com/dvx-jkai-xhq
I'm doing a build of a C/C++ tool that has python bindings, and it's built via cmake and python -m build with a scikit-build-core backend.
In the isolated build environment, the compiled .so is bundled into the wheel and saved on disk. Is there a way I can also save the .so, not just the .whl? I could post-process the wheel and extract the .so, but I'd rather save it directly if I can.
I believe the solution would be something to the effect of what build_ext was
We are the 15th most popular build backend with 138 projects on PyPI (counting leaving it empty as a separate backend) out of 127,368 projects with a pyproject.toml:
0 setuptools.build_meta: 52481
1 poetry.core.masonry.api: 35852
2 unknown: 12989
3 hatchling.build: 9039
4 poetry.masonry.api: 7082
5 flit_core.buildapi: 4107
6 maturin: 1373
7 setuptools.build_meta:__legacy__: 932
8 pdm.backend: 873
9 poetry_dynamic_versioning.backend: 630
10 pdm.pep517.api: 584
11 flit.buildapi: 519
12 jupyter_packaging.build_api: 184
13 scikit_build_core.build: 138
14 mesonpy: 87
How did you get that list? Some way to query PyPI for that data or did you download and analyze packages manually?
i mean there's inspector.pypi.org
(based on @mystic linden's post and https://framapiaf.org/@fcodvpt/111540079686191842)
🥳 Happy that that post is being put to good use 🙂
Also see this msg which looks at tool.* sections. #cibuildwheel message - I'll be using that soon to see what the average usage of our settings looks like.
I also have seen the exact number of packages that have a CMakeLists.txt in them (around 2200 IIRC, would have to rerun the query). Probably are other interesting things to look at too.
I can ask really interesting questions now, like what is the most popular setting for tool.black.line-length (120). For our settings, here's the breakdown:
wheel: 99
cmake: 85
sdist: 56
metadata: 51
build-dir: 46
minimum-version: 36
ninja: 19
logging: 13
experimental: 12
generate: 5
install: 4
strict-config: 2
overrides: 1
The two projects that set strict-config set it to True (the default), interestingly.
1?
1 what?
Oh, I thought line-length was set to 1.
Ah, no, 120 was the most popular. I could see if there's a 1, would not suprise me in the least. There's crasy stuff in the tails. A few projects don't even have parsable pyproject.tomls.
3 projects set it to 1000. A fourth sets it to "1000", since black supports using strings anywhere (also, I'm not bothering with line_length, which is also supported)
The smallest line-length I see is 30.
After 120, what are the next most popular?
All the ones over 100:
120: 5755
88: 3578
79: 2876
100: 2714
80: 834
99: 811
119: 485
110: 359
90: 269
95: 169
140: 165
160: 112
(Less than that, then I probably should start including strings & the other way(s?) to specify it like line_length) Total is 19,076 pyproject.tomls that have tool.black
Here's a better breakdown for scikit-build:
tool.scikit-build.*.*:
metadata.version: 49
wheel.packages: 49
build-dir: 46
cmake.minimum-version: 42
sdist.include: 39
sdist.exclude: 37
minimum-version: 36
wheel.expand-macos-universal-tags: 35
cmake.define: 33
wheel.py-api: 29
cmake.build-type: 27
cmake.verbose: 26
cmake.args: 21
wheel.install-dir: 16
ninja.minimum-version: 15
logging.level: 13
experimental: 12
cmake.targets: 10
wheel.license-files: 8
ninja.make-fallback: 8
metadata.readme: 5
generate: 5
sdist.reproducible: 5
cmake.source-dir: 4
install.components: 3
metadata.scripts: 2
wheel.exclude: 2
strict-config: 2
metadata.optional-dependencies: 1
sdist.cmake: 1
overrides: 1
install.strip: 1
By the way, it is fantastic that this is so clean. Anything without the equivalent of strict-config=True has a weird tail.
For example, build-system has only three allowed entries (the top three below).
build-system.*:
requires: 118470
build-backend: 114328
backend-path: 104
packages: 29
dependencies: 21
requires_python: 19
classifiers: 18
requires-python: 17
build_backend: 16
include: 12
readme: 11
license: 10
keywords: 8
sdist: 8
exclude: 7
bdist_wheel: 7
name: 6
version: 5
(Many, many lines with <5 omitted, with things like require, build-system (again), etc)
Similar story for top-level:
*:
build-system: 118488
tool: 85504
project: 39624
options: 250
tools: 237
metadata: 168
coverage: 131
flake8: 116
mypy: 116
requires: 114
build-backend: 75
virtualenvs: 74
pytest: 71
dependencies: 61
bdist_wheel: 34
build: 30
build_system: 29
(Again, many more omitted, like build-sytem, too, took, rool, bluid-system, tool-<stuff>, and lots of things that should have been in subsections)
Hugo linked me here from the Black channel, this is very interesting to know. I discovered today that Black likely has a bug if you set your line length to an approximately 80-digit number and use a file system with short limits on path length, so it's good to have confirmation that's likely not a problem in practice 😛
I'll show some of these stats at the scikit-build developers meeting in 7 minutes (http://meet.google.com/qbx-nkkm-mup)
https://docs.google.com/presentation/d/1TTXEZhEBcJq5vgpFy4xMG_7h7irWZ16LqS0t1tIn2XQ/edit?usp=sharing
I'm building a wheel file using skikit-build-core that uses a .pyd and some bindings with pybind11. The wheel is generated, but the structure isn't what I'm going for. Inside the wheel, a .lib is in a lib subdirectory and the .pyd is in the bin subdirectory. How can I place these both inside the proj subdirectory where the __init__.py file is?
Do you have the CMakeLists? You need to tell CMake not to use standard subdirs.
That sounds like it might be what I'm looking for. Do you know what the name of the dir would be for the "main" project folder of the wheel (where init.py is) ? So instead of placing it in bin/ for example, it'd be "whatever dir has init.py"
That’s dependent on your project. If it’s the name of the project, then IIRC ${SKBUILD_PROJECT_NAME}
Installing to “.” is site-packages. You can have multiple packages so we don’t assume a package name. You can set one as wheel.install-dir though.
Perfect, thank you so much!
Noob question, I started using scikit-build to build a PyTorch extension. I have been setting the build-dir to "build" and mostly been using the no build env editable mode. My cmake lists creates a .so: python_add_library(driss_torch SHARED ${CU_SOURCES} ${CPP_SOURCES} )
That I load at runtime based off the /build path. I am trying to figure out how this should work for pip install . commands with wheels. How do I get my library to get installed in the correct location to site package?
I have noticed that every example uses MODULE not sure if that makes a differenc
From looking at the above, cmake stack overflow I imagine that I need to install my lib into a set path in the top level project and not the build dir
hmmm idk looking at the site packages even though i have a package / lib/ sharedlib.so
The sitepackage seems to drop this for some reason
You need to install for scikit-build-core. You generally shouldn’t run from the build-dir. I’d try scikit-build-core’s editable modes (there are two to pick from).
I think python_add_library treats everything as MODULE since that’s the only thing that is importable in Python.
So you’d install(TARGET driss_torch LIBRARY DESTINATION package/lib) or wherever you want it to go.
I think what I am trying to do is even more naive than this;
Scikit build properly builds the shared lib on invocation both in in editable and top level pip install . its really just that this .so lib is not being added to the final wheel.
I tried doing:
[tool.scikit-build.package-data]
driss_torch = ["lib/*"]
but this is not an option. Idk if I am fundamentally not using scikit-build-core in the manner it was designed for
Do you have source somewhere to look at?
Still a lil rough tbh: https://github.com/drisspg/driss_torch/pull/7
hmm okay after some more monkeying around I i seem to now get the lib installed into the directory and non editable installs seem to work fo rme
tool.setuptools doesn’t do anything, we aren’t setuptools and don’t use setuptools. 🙂
It’s the install command that fixed it.
Ahaha okay that makes much more sense, and was just AB testing that now
Thanks for the help!
Hello! I apologize if this is a dumb question but here we go...
Context:
In scikit-build-core the examples use a CMakeLists.txt file which seems like a superset of what a CMakeLists.txt file would be for just the C++ code itself. For the codebase I'm working on (call it X), there is already a very large (~1000 lines) CMakeLists.txt file. For the python wrapper (call it pyX) we currently have a massive and messy setup.py that uses setuptools to 1) download and install all the dependencies for X (calling their make/cmake files), 2) build X by passing in CMake arguments, 3) run swig on the interface files to generate the wrapper code, 4) build the python library.
Questions:
- Can
scikit-buildorscikit-build-corereplace all of these steps (including git clone, checkout, etc)? - Would I need to edit the
CMakeLists.txtfile ofXor could I keep it separate in thepyXlibrary?
In theory I really like the idea of scikit-build and see a lot of projects I subscribe to using it, but I haven't seen any large examples with swig that are doing our pyX build is doing. I'm an amateur with cmake and just starting to explore scikit-buidl so I apologize if this is naive. Thanks!
Sorry @proper ridge, I'm on parental leave so haven't been following closely (all the spam causing all channels to be "unread" didn't help.
- Yes, you can write a CMakeLists instead of a setup.py that does all of that. As long as you don't mind setting a pretty recent version of CMake (which scikit-build-core helps you with), it's pretty simple and clean.
- You can put your Python CMakeLists in a subdirectory. However, you can't include upward in CMake, so I'd recommend some minor edits to the main CMakeLists to add the python subdirectory if the user is trying to build bindings.
Some larger examples (not sure the binding tech, though) include ITK and VTK moving to it, and rapidai. SWIG should be pretty easy from CMake, though, there's a built-in module for it, I've used it in the past.
Hello,
I asked the question is another channel and I was told to come here
I have a cuda/python package with nanobind as the binder
How can I use scikit build core to compile, upload to pypi so users just pip install and get the wheel according to their OS?.
I already have a scikit build core TOML that builds when I do pip install.
I want to be able to compile myself (no cibuild) because I use NVC++ as compiler .
Upload to pypi
Then a user can use pip install to get the binary that corresponds to their OS.
Thank you
can someone tell me how can I properly use a shared library in a scikit build core built project?
Using an old setup.py it worked like a charm
I'm running into a problem when installing a package in user space (pip install --user). There script generated in the bin folder relies on the platlib, but that will point to /usr/local whic is wrong since the binaries are left in .local/... . Has anyone seen that before?
Note; this is for scikit-build-core
CUDA is a hard problem without a good solution yet. Shipping an SDist and letting the user build it with CUDA is an option, but wheels are hard. It's technically impossible to ship true manylinux wheels, since you have to have an external dependency (CUDA can't be redistributed, it's not open source, plus it's big). But even if you add the CUDA libs to auditwheel's allowlist, you still have a CUDA dependency; CUDA 11 vs. CUDA 12, for example. nvidia/warp handles this by shipping a CUDA 11 wheel and putting the CUDA 12 wheel as a GitHub download. cupy and several others do this by adding the cuda version to the name of the package, and just releasing lots of packages. You can look around; RapidsAI's packages use scikit-build-core, but it really doesn't matter, that just makes building easier on user machines, building the binaries face the same challenges.
At a guess, you generally would be using cibuildwheel, adding CUDA to a manylinux image, maybe saving and using the resulting image since this might be slow (?), and listing the cuda libs in the allowlist. Then solving the CUDA versioning issues mentioned above. I'll be researching this in more detail in the future.
This was a major point at the packaging summit at PyCon, by the way; there's work toward a way to improve this.
Any example? It should work the same way. Unless you were skipping a wheel and using python setup.py install or something. You need to use a repair tool (via cibuildwheel, for example) if you are distributing the wheel and aren't building the shared lib yourself. There's not much to go on here.
You can't rely on platlib from scripts any any system (scikit-build-core or otherwise). There's no way to get the path to platlib from scripts. I'd love this, since if there was I could ship cmake in the scripts dir and avoid the Python wrapper generated by the console entry point, which would save the Python start time and would also avoid issues forwarding things. (That's the solution, by the way, you have to use console entry points instead of writing to scripts if you have something that needs platlib)
(The installer does know where platlib is, and writes it into the script it generates from the console entrypoint, which is why it works that way)
I forget which nvidia package it was but they now also have a build-backend that downloads wheels from their index based on more-granular platform compatibility/capability checks, and only upload an sdist to PyPI that uses that build backend.
Thanks for the reply! That makes sense, but then the executing script should be updated. I do wonder where the script is generated, do you know? I'm happy to contribute towards a solution
executing script should be updated
Which script? Installers like pip generate the correct script shebang for an entypoint, and I'd forgotten about it, but if you have a python script in/scripts, then#!pythonis converted to the correct shabang path, and scikit-build-core (like setuptools) will rewrite a "normal" shabang line like#!/usr/local/env pythoninto#!pythonfor you so that the installer will write out the correct shabang path.
Installers can't rewrite random scripts though. They only process shabang lines.
I see, that makes sense. I was referring to the packaging process, which somehow generates a Python script that executes a compiled binary (see original issue here https://github.com/aestream/aestream/issues/112).
I'm honestly a little confused about the entire process, so I don't know where that script file is coming from. Do you know where I can go to figure that our/learn more about it?
I'll need to investigate, something strange is happening here. Scikit-build-core doesn't add anything like that, but it seems to be in the wheel (https://inspector.pypi.io/project/aestream/0.6.4/packages/57/e7/47d82fda143b650b9e5273a7f81d1d567c5f4cf6133b81f39c2b2e749cda/aestream-0.6.4-cp310-cp310-manylinux_2_28_x86_64.whl/), while other packages like https://inspector.pypi.io/project/clang-format/18.1.7/packages/f7/dc/099a5e4b2b9b8cccfafa6a73d089178371ad53b6735a05fd6a9d8d9aa005/clang_format-18.1.7-py2.py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl/ are fine.
FYI, cibuildwheel doesn't use pipx anymore, you don't need to install it for ACT running.
If you configure cibuildwheel via pyproject.toml, it's much easier to test locally (or use other CIs, etc)
This is where it's coming from: https://github.com/pypa/auditwheel/blob/71f4894eae1fd1616138c3a5ef61e13f3d0f0e3e/src/auditwheel/repair.py#L275
And it's running from here: https://github.com/pypa/auditwheel/blob/71f4894eae1fd1616138c3a5ef61e13f3d0f0e3e/src/auditwheel/repair.py#L248-L272
The core problem is the executable links to other things in platlib, which is invalid; you should swap it for a console entry point wrapping the executable, or statically link so that it doesn't depend on platlib. auditwheel is trying to fix this for you, and is applying a fix that ignores user platlib.
Wait, that's a possibility? Thanks, that would be great!
Excellent find! Thank you so much for taking the time to look into this! Can I ask how you found it? I find the tech stack quite challenging to navigate.
Right, so just to be clear, would you recommend both statically linking the binary and wrapping it in a console entry point?
My only concern with the static linking is that we're compiling against CUDA (for one flavour of the package). Won't it risk blowing up the binary?
Don't statically link CUDA. But CUDA isn't in your site-packages, is it?
(And the explicit wrapper method is fine, IMO it's better to do it manually rather than hope that auditwheel fixes it via this method)
I knew it wasn't coming from scikit-build-core (as I wrote that), and I searched your repo for "execv" without success, so I checked inspector.pypi.io and saw it in the wheel, so it had to come from the repair step and not the installer. Your repair step was not exactly default, but not different in any important way, so then I searched for "execv" in auditwheel and found it.
Feel free to open an issue if no one comments on the closed one I commented on a few days.
would you recommend both
No, either-or. 🙂
Cuda with scikit build core
Thanks a lot for unwrapping this! That was seriously helpful
Thanks! I'll monitor it and see how to proceed. For now, I'll explore the pyproject setup. The more I can keep things in simple configurations, the better
Quick question regarding CUDA; I would love an install option like package[cuda]. To my knowledge there are no ways to install cudatoolkit via pip, so I wonder whether this is possible at all (without asking the user to install the dependencies beforehand). Do you happen to know of a way to drag in cudatoolkit as a pip-based dependency?
I don't know much about this, but I think you can install CUDA with pip: https://nvidia.github.io/cuda-python/install.html
Isn't this only the runtime? We would need at least nvcc
Ah, then no. 🙂
Thanks for the link though 🙂 Would another option would be to pre-build the wheels? Ofc that would require cross-compiling for all CUDA versions, which sounds like a bit of a 🤯
cuda-python is a low level Python library for CUDA that high level libraries like cupy are supposed to use. You have to have drivers + the CUDA toolkit (only the runtime is requied) to use cuda-python. You can't install the cudatoolkit from pip, even if it's possible there likley would be licencing issues. It was quite hard to get it into conda apperently because of this, requried special permissions from NVIDIA.
Pre-built wheels would still require the CUDA redistributable, but not a compiler.
Thank you for the explanation. Prebuliding the wheels sounds like the way to go, but I don't think I fully understand the entire process. Is there a way on the user side to decide which version of CUDA is available locally in order to pull the right wheel?
(Feel free to redirect the topic to a different/more appropriate channel, btw)
Depends on user setup. You can install it with your system package manager in some cases, usually from an NVIDIA repo. NVIDIA has instructions. For example, on our clusters at Princeton, you'd use module load cuda/12.4 or something like that (many HPC clusters use Lua Environment Modules, "lmod"). Conda-forge has the ability to distribute CUDA, but you'd still need a sufficient driver version.
Right, that makes sense. I'm wondering whether pip can (1) pick up the right driver version and (2) fetch the right wheel without too much hassle on the user-side. Ideally, the user would just do pip install ... and then it'd automatically find the right wheel.
As an example, I recall that you had to manually add the path for your wheels to ealier versions of PyTorch. Can that be avoided now in "vanilla" pip?
Teaching it to do that is what was being discussed at the packaging summit at PyCon this year. It's a lot of work, especially if the system is fairly general. You can come up with several other use cases, like MPI, AVX builds, etc. Building from SDist can detect what you have, but not wheels.
There's extensive discussion about this happening on discuss.python.org as well.
I would like to install the libraries in my CMake to a specifique path
Using setuptools this was trivial and I did it like so
cmake_args = [ f"-DCMAKE_LIBRARY_OUTPUT_DIRECTORY={extdir}{os.sep}", f"-DPYTHON_EXECUTABLE={sys.executable}", f"-DCMAKE_BUILD_TYPE={cfg}", # not used on MSVC, but no harm ]
Where extdir is a relative path that I gave (relative to site-packages)
Using scikit-build-core this is much more difficult
this
`[tool.scikit-build]
minimum-version = "0.8"
cmake.version = ">=3.19"
build-dir = "build/{wheel_tag}"
wheel.py-api = "py3"
cmake.build-type = "Release"
Add any additional configurations for scikit-build if necessary
cmake.args = [
"-DCMAKE_INSTALL_PREFIX:PATH=my-package/_src/",
"-DCMAKE_LIBRARY_OUTPUT_DIRECTORY:PATH=my-package/_src/",
]`
is setting the path to be absoulute paths and they are not relative to site packages
if I do this
`cmake.args = [
"-DCMAKE_INSTALL_PREFIX:PATH=${SKBUILD_PLATLIB_DIR}/my-package/_src/",
"-DCMAKE_LIBRARY_OUTPUT_DIRECTORY:PATH=${SKBUILD_PLATLIB_DIR}/my-package/_src/",
]``
The path is not expanded
I can just install my packges but DCMAKE_LIBRARY_OUTPUT_DIRECTORY was convinient because I also use shared libraries
Anyone know a solution?
possibly just by using a TOML without a python setup.py
Don't set CMAKE_INSTALL_PREFIX, scikit-build-core doesn't use it. Use wheel.install-dir = "my-package/_src". CMAKE_LIBRARY_OUTPUT_DIRECTORY, if you need it, then should be relative to the install dir, IIRC.
(Internally, scikit-build-core is passing --prefix to cmake --install)
[tool.scikit-build]
minimum-version = "0.8"
cmake.version = ">=3.19"
build-dir = "build/{wheel_tag}"
wheel.py-api = "py3"
wheel.install-dir = "my-package/_src"
(And, only if you need it)
[tool.scikit-build.cmake.define]
CMAKE_LIBRARY_OUTPUT_DIRECTORY = ""
Thank you for your answer
This did not work I did not find the libraries installed in the path I selected.
Do I need to call install in my CMake?
When I was using setuptools, all I had to do is set CMAKE_LIBRARY_OUTPUT_DIRECTORY in the setup.py
So this https://github.com/DifferentiableUniverseInitiative/jaxDecomp/blob/main/setup.py works
but this https://github.com/DifferentiableUniverseInitiative/jaxDecomp/blob/push-to-pypi/pyproject.toml does not
All libraries and shared libraries went to the correct path
Yes, scikit-build-core always installs. That way CMake correctly handles RPaths, and strips the binaries, etc. You aren't supposed to run from the build directory.
Okay I see
I think the setuptools script worked by accident
Where should I installed a shared library to get it to work?
I install it to my-package/_src
but without having the correct LD_LIBRARY_PATH it didn't work
I don't expect my users to update their LD_LIBRARY_PATH
Running from the build path usually works, but it's not correct and isn't going to work in some situations.
There's a setting for RPath, let me check
Ok thank you 🙂
Did your old SDist work? I get add_subdirectory given source "third_party/cuDecomp" which is not an existing directory. when doing source -> SDist -> wheel on your original code.
it is a submodule
Yes, I pulled it, it still doesn't like going through the SDist first. Going directly to wheel now.
I'm going directly to wheel now.
running what command (sorry I am new to packaging)
python -m build?
I usually pip install and the setuptools does some magic
Yes, pipx run build technically, and that goes source -> SDist -> wheel. Using pipx run build --wheel which skips the SDist just like pip install.
Now just working on setting up an environment. It's quite picky: Requested CUDA version not available: 12.2 Available versions: 12.4 🙂
Yeah building it will be a pain
it needs NVHPC
Yes, I'm using nvhpc/24.5 and openmpi/nvhpc-24.5/4.1.6
And cudatoolkit/12.2 but it doesn't seem to like that
yeah It seems that on this branch I had coded it
One sec
Ah, I see that. Can modify
yeah just do
option(NVHPC_CUDA_VERSION "CUDA version to build for" 12.4)
Yeah, it's much happier now. Is there something I can run to see if the wheel works?
option is boolean, by the way, you need set(... CACHE)
Oh ok thanks for the info
ModuleNotFoundError: No module named 'jax'
yeah you need to do
pip install jax[cuda12]
you are going through so much trouble, thank you so much
ImportError: /home/henryfs/jaxDecomp/.venv/lib/python3.12/site-packages/jaxdecomp/_src/_jaxdecomp.cpython-312-x86_64-linux-gnu.so: undefined symbol: _ZNSt10filesystem7__cxx114path14_M_split_cmptsEv
Is there a missing link to stdc++fs?
Yes, 24.5
Adding target_link_libraries(_jaxdecomp PRIVATE stdc++fs) fixed it. Okay, that's the old one working, now will try the new one.
I don't use python3.12 maybe that's why (this helps me alot thank you 🙂 )
If you use std::filesystem, older stdlib's require this link. Maybe you have a newer stdlib?
I use the one provided by my HPC.
But I will add the link to my CMake so it works for everyone (thank you again)
Hmm, interesting, one problem is that CMAKE_INSTALL_PREFIX is hardcoded into cuDecomp's CMakeLists. It shouldn't be, install defaults to CMAKE_INSTALL_PREFIX and hard coding it messes with using --prefix. I can probably make that work, though. As a quick fix, I've removed it manually.
Yeah this is why I had to add it
Maybe I should ask them to make it more flexible
but if you remove it it works?
There's one more issue I'm working on now with the RPath.
set_target_properties(_jaxdecomp PROPERTIES INSTALL_RPATH "$ORIGIN/lib") did it. Was looking for a more elegant solution, but that does do it. Also removed the extra install line for cudecomp.
I'll see if I can make CMAKE_INSTALL_PREFIX not break things.
No, it's part of the RPath spec.
Ok I see.
I am gonna make an issue to ask them to remove the CMAKE_INSTALL_PREFIX
Thank you for your help 🙂 🙂
(Though they should fix their lib still)
Also, I highly recommend using src structure for your lib, I had to use -P (PYTHONSAFEPATH, 3.11+ only) to make it respect the install over the local path. But if you use src structure, that just works.
And I'd avoid calling the directory inside your package "src" if possible, that's usually reserved for src-structure.
what is an src structure ?
using src and include folders ?
This guide is maintained by the scientific Python community for the benefit of fellow scientists and research software engineers.
okay I see
Yes, so that import jaxdecomp will never try to import the source directory instead of the installed package.
ahh okay
This is way I have a circular dependance
pipx run build --installer=uv --wheel
uv venv
uv pip install jax[cuda12]
uv pip install dist/jaxdecomp-0.1.0-py3-none-linux_x86_64.whl
# salloc --nodes=1 --ntasks=1 --mem=32G --time=00:10:00 --gres=gpu:1
.venv/bin/python -Pc "import jaxdecomp"
This is working for me with using the scikit-build-core PR linked above.
0.9.8 should support this (but please still open an issue with cuDecomp, they should not be doing this)
Hi 👋
I'm trying to adapt the scikit-build-core example with swig to c++. I'm able to use scikit-build just fine with the c example, not sure what I did wrong with the adaptation. pip installing works fine, but on import I get an error.
>>> import example
Traceback (most recent call last):
File "<input>", line 1, in <module>
import example
File "/home//.local/lib/python3.8/site-packages/example.py", line 12, in <module>
import _example
ImportError: dynamic module does not define module export function (PyInit__example)
square.hpp
#ifndef SQUARE_HPP
#define SQUARE_HPP
float square(float N);
#endif
square.cpp
#include "square.hpp"
float square(float N) {
return N * N;
}
example.i
%module example
%{
#include "square.hpp"
%}
%include "square.hpp"
pyproject.toml
[build-system]
requires = ["scikit-build-core", "swig", "numpy"]
build-backend = "scikit_build_core.build"
[project]
name = "example"
version = "0.0.1"
CMakeLists.txt
cmake_minimum_required(VERSION 3.15...3.26)
project(${SKBUILD_PROJECT_NAME} LANGUAGES CXX)
find_package(Python3 REQUIRED COMPONENTS Interpreter Development.Module NumPy)
find_package(SWIG REQUIRED COMPONENTS python)
include(UseSWIG)
include_directories(${Python3_INCLUDE_DIRS} ${Python3_NumPy_INCLUDE_DIRS})
swig_add_library(
example
LANGUAGE python
OUTPUT_DIR "${SKBUILD_PLATLIB_DIR}"
SOURCES src/square.cpp src/example.i
)
if(WIN32)
set_property(TARGET example
PROPERTY SUFFIX ".${Python3_SOABI}.pyd")
else()
set_property(TARGET example
PROPERTY SUFFIX ".${Python3_SOABI}${CMAKE_SHARED_MODULE_SUFFIX}")
endif()
target_link_libraries(example PRIVATE Python3::Module)
install(TARGETS example DESTINATION .)
IIRC there’s a property to set on the file to mark it as c++ for swig. Can check on a computer later.
It’s in the UseSWIG docs on CMake.kitware.org
Link to Python::NumPy too and then you can drop the include directories.
set_property(SOURCE example.i PROPERTY CPLUSPLUS ON)
hmm i tried adding this and get the same error
before swig_add_library
I’ll have to try it then. It’s not getting the init function, usually because the name is wrong.
You shouldn’t output directly to platlib, and use install instead.
sorry I'm still pretty new to cmake. I mostly just copied directly from the scikit-build-core swig c example. By "use install instead" do you mean editing the final line install(TARGETS example DESTINATION .)?
I think there are some minor issues with the example, was helping someone a couple of days ago and noticed that. Will update once they report back that it's working for them.
I'm referring to the OUTPUT_DIR "${SKBUILD_PLATLIB_DIR}", that looks problematic to me.
oops i should have clarified, my question implied deleting that line, but I wasn't sure what to do instead.
Okay, thank you!
Yeah, the current way adds a examplePYTHON_wrap.c into the wheel.
Ahh, missed it was src/example.i, did you correct that?
(I'm breifly on a computer now)
oo doh
let me try, i missed that too
ah okay, that did the trick (+ adding src to include_directories). it didn't import without the PLATLIB line though so I added that back in
It must not be getting the helper file
is not using PLATLIB specific to the fact this is c++ or is that something that is deprecated in scikit-build-core?
There’s probably a way to install that file (other than knowing it’s “example.py”)
I think the simple example is a little bit too simple and it doesn’t use enough of swig, it passes missing a bit that it actually needs for any more realistic example
yeah to be fair I guess a lot of this can be found out through cmake docs
I'm kind of new to both so I'm pretty lost haha. thanks for the help
The general pattern is that build product should go in the build directory. Then install puts everything in platlib. But not sure how to nicely install the generated example.py.
(On my phone again)
oh yeah just to clarify, I did get the import to work, using PLATLIB
In the docs, I see a couple of ways to do it. Will update here later.
thank you!
This is the "easy" method: set OUTFILE_DIR so that the cpp file gets generated in in the binary dir instead:
cmake_minimum_required(VERSION 3.15...3.26)
project(${SKBUILD_PROJECT_NAME} LANGUAGES CXX)
find_package(Python3 REQUIRED COMPONENTS Interpreter Development.Module NumPy)
find_package(SWIG REQUIRED COMPONENTS python)
include(UseSWIG)
set_property(SOURCE src/example.i PROPERTY CPLUSPLUS ON)
swig_add_library(
example
LANGUAGE python
OUTPUT_DIR "${SKBUILD_PLATLIB_DIR}"
OUTFILE_DIR "${CMAKE_CURRENT_BINARY_DIR}"
SOURCES src/square.cpp src/example.i
)
if(WIN32)
set_property(TARGET example
PROPERTY SUFFIX ".${Python3_SOABI}.pyd")
else()
set_property(TARGET example
PROPERTY SUFFIX ".${Python3_SOABI}${CMAKE_SHARED_MODULE_SUFFIX}")
endif()
target_link_libraries(example PRIVATE Python3::Module Python3::NumPy)
target_include_directories(example PRIVATE src)
install(TARGETS example DESTINATION .)
Note that you should surround filenames with variables in them with quotes, so they will work if they have a space in them. No need if there's no variable.
You might want SWIG_USE_TARGET_INCLUDE_DIRECTORIES too. Now there are several steps inbetween, but I'm going to try to do it the nice/modern way, avoiding writing to the platlib dir inside the build step, which is not ideal (but supported).
cmake_minimum_required(VERSION 3.15...3.26)
project(${SKBUILD_PROJECT_NAME} LANGUAGES CXX)
find_package(Python3 REQUIRED COMPONENTS Interpreter Development.Module NumPy)
find_package(SWIG REQUIRED COMPONENTS python)
set(UseSWIG_MODULE_VERSION 2)
include(UseSWIG)
set_property(SOURCE src/example.i PROPERTY CPLUSPLUS ON)
swig_add_library(
example
LANGUAGE python
SOURCES src/square.cpp src/example.i
)
if(WIN32)
set_property(TARGET example
PROPERTY SUFFIX ".${Python3_SOABI}.pyd")
else()
set_property(TARGET example
PROPERTY SUFFIX ".${Python3_SOABI}${CMAKE_SHARED_MODULE_SUFFIX}")
endif()
target_link_libraries(example PRIVATE Python3::Module Python3::NumPy)
target_include_directories(example PRIVATE src)
get_property(support_dir TARGET example PROPERTY SWIG_SUPPORT_FILES_DIRECTORY)
install(TARGETS example DESTINATION .)
install(DIRECTORY "${support_dir}/" DESTINATION .)
I'm using pipx run build --installer=uv --wheel && unzip -l dist/*.whl to see what it makes.
Sorry to ask about another topic - regarding passing flags, is the recommended way in scikit-build-core to use environmental variables? I am trying to modernize our old build script which has many (dozens) of flags; some of them could probably be removed but many will need to stay, for example --parallel will install MPI, mpi4py, and also pass a flag to the cmake of a dependency. I am looking at the docs and it seems like this is possible through config-settings but it is pretty verbose, e.g. to set a parallel flag that gets passed to cmake I think I'd need to do this? pip install . --config-settings=cmake.define.parallel=1 (this is using tool.scikit-build.cmake.define in the toml file, not sure if there is a better way)
i'll try this
edit: this works for me! using python -m pip install . in the base directory
Ouch, I'd not use --parallel for comping MPI, that's usually building in parallel. -Ccmake.define.USE_MPI=1 would be a good way. You'd also want to add an extra that installs MPI and mpi4py (I'm not sure installing MPI is a good idea, though, clusters usually have their own MPI installs). I'd probalby have it detect MPI by default, but allow it to be opted in or out depending on the value passed to the define.
You just need to use it in the CMake file, you don't need it in both config-settings and pyproject.toml.
The extra would look like pip install .[mpi] for example.
I've been tempted to add a D shortcut in the past for that, -CDUSE_MPI=1 if that were added. But before doing that, I have something else I want to try, probably for 0.11 or so.
Hi! I've recently converted the build-system of python bindings for a c-library from setuptools to scikit-build-core. I have started considering how to create an sdist (so we can upload to PyPI) and I've noticed there are some weird challenges that are probably tied to our directory layout. It seems like I might not be doing things "properly"(since my scenario seems would be fairly common), and I wanted to ask if there was an established convention for doing things.
For concreteness, the c library is called Grackle (it is used in C or C++ or Fortran programs that run numerical simulations) and the python bindings are called Pygrackle. The bindings originated as a way to easily run integration tests and are now used for post-processing simulation-data. Below is a rough sketch of my project's directory layout:
grackle/
CMakeLists.txt
cmake/
*.cmake
src/
clib/
CMakeLists.txt
*.c
*.h
python/
CMakeLists.txt
pyproject.toml
tests/
pygrackle/
*.py
*.pyx
The src/python/CMakeLists.txt file builds the extension-module portions of Pygrackle by:
- (for testing) linking to an existing build/install of
Grackleviafind_packageOR - (preferred) embedding a
Gracklebuild viaadd_subdirectory(../../ my-sub-build-dir).
Other than that (ugly)add_subdirectorycall, thesrc/python/CMakeLists.txtfile is completely isolated from the other cmake files.
When installing from an sdist, I would want to use the same sort of add_subdirectory logic. But (as I understand it), the files outside of the grackle/src/python directory. I know I can hack something together that works based on (this part)[https://scikit-build-core.readthedocs.io/en/latest/configuration.html#configuring-source-file-inclusion] of the docs, but that approach doesn't seem ideal (the fact the sdist would effectively have an inverted layout from the source-directory would probably be confusing). Is there a convention for how people normally address this sort of problem?
I could imagine moving my pyproject.toml to the root directory:
- is there a way I can tell
scikit-build-coreto invokecmakeusing theCMakeLists.txtfile within the python subdirectory? - Or would it need to use the
CMakeLists.txtfile in the same directory? (I don't really want to mix python build logic into the root-levelCMakeLists.txtfile, but if that is the established "Right Thing to Do," I'll do it).
(Sorry about the length -- if this isn't the proper forum to ask these questions just let me know. I was just trying to be detailed)
The usual way to do this is to have pyproject.toml at the root. That's easiest for people using your repo directly (name @ git+https://.... works), and it means the repo and the SDist have the same structure. You certainly can point at a CMakeLists.txt in a subdirectory with cmake.source-dir.
oh, I didn't know about cmake.source-dir
That sounds like it would help. (I also hadn't thought about installing from my repo -- that's a great point)
Thank you!
Though if you need to build other parts of your repo, you might want the main CMakeLists.txt and just have checks for if you are building (SKBUILD is defined to 2, for example, or you can set a define like BUILD_PYTHON_BINDINGS)
(Though you can do it the way you described too; that's basically how pybind11 does it)
Pybind11's actually an example of a pyroject.toml in a subdirectory, it's for building the tests so it's in the tests subdirectory. So you can do it that way, it's just not usually the best way.
(Might still be in a pybind11 PR, it's the one adding pyodide WebAssembly tests, I think we haven't merged since I was discussing adding some flags)
Though if you need to build other parts of your repo, you might want the main CMakeLists.txt and just have checks for if you are building (SKBUILD is defined to 2, for example, or you can set a define like BUILD_PYTHON_BINDINGS)
Yeah, I thought a little about that. Our root-level CMakeLists.txt file is already a little more "noisy" than I would like and I worry that doing this would add more noise that would complicate/confuse people (it's probably worth revisiting down the road after I get time to refactor).
I took a look at that pybind11 PR -- It was insightful. With that said, I'm definitely going to move the pyproject.toml to the root of the repository. It definitely sounds better for our scenario.
Hi again 👋
Is it possible to use scikit-build-core with a pre-built c++ library (or one that has been built earlier in the cmake file)? The examples all have the c++ source code in the same directory as the toml file. Where should I be building or copying a pre-built c++ library to? I see in ~/.local/lib/python3/site-packages/ there is: (_example.cpython...so), example-0.0.1.dist-info/, and example.py. My best guess is somewhere in dist-info? Thank you
oh, forgot to mention this is using swig
Yes, I'd assume "earlier in the file" - and you'd just install it to where you want it to go? If you built it separately, that's a lot tricker, since you need to make sure it gets built (pip install . on the repo woudl likley fail, for example, as would building from SDist). But same idea, just "install" the file you want. That does in the wheel.
The best place to put files is in site-packages/yourpackage, but you can use the other available wheel directories if you want. SKBUILD_SCRIPTS_DIR goes to /bin (or /Scripts on Windows), SKBUILD_DATA_DIR is the root of the install tree (this coudl be / or /usr/local on some machines without venvs!), SKBUILD_HEADERS_DIR is for headers (no one uses this), and SKBUILD_METADATA_DIR is the dist-info dir, it's very rarely used, mostly for adding licenses or special plugin-like things.
it might be separate because people have their own custom versions of the library they want to use. in the example there are the lines
get_property(support_dir TARGET example PROPERTY SWIG_SUPPORT_FILES_DIRECTORY)
install(DIRECTORY "${support_dir}/" DESTINATION .)
should I be using support_dir for this?
also to find the site-packages/yourpackage directory should I be using SKBUILD_PLATLIB_DIR (or Python3_SITEARCH)?
You are asking where to install to, not from, right? I assume you know where the custom version of the library someone supplied is, so that's where you get it from. Just like support_dir above is where SWIG put the support files, and you have to install them to the wheel.
"." is wheel.install-dir, which by default should be thought of as site-packages (and is SKBUILD_PLATLIB_DIR). If you set that to something (usually the name of the package), then it's ${SKBUILD_PLATLIB_DIR}/whatever-you-set-it-to.
So install(FILE x.so DESTINATION .) will install directly to site-packages unless you've set wheel.install-dir. install(FILE x.so DESTINATION yourpackage) or wheel.install-dir="yourpackage" , your pick (just don't do both at the same time!)
corrrect
sorry I'm not being very clear. My main question is where to build the c++ library/dependencies that swig will need to bind to. In a previous answer you mentioned that building in the build dir and transferring everything to the platlib is the recommended way, even though installing into platlib directly is possible. In that case I think I'd want to build the c++ library in SKBUILD_DATA_DIR?
Building in the build dir and then installing is best. IIRC previously for SWIG we were handling the extra support files it generates.
Building into the platlib directly is not recommended. CMake fixes up rpaths and strips the binaries when installing.
I'm trying to pull & build an external library, but when I pip install with scikit-build-core I get an error about not being able to find Ninja.
Here is my CMakeLists.txt. I can run it fine using cmake/make by itself, the error is only when trying to pip install. Any ideas on whats going on? Thank you!
cmake_minimum_required(VERSION 3.15...3.26)
# project(${SKBUILD_PROJECT_NAME} LANGUAGES CXX)
project(pymfem_dev LANGUAGES CXX)
include(ExternalProject)
find_package(Python3 REQUIRED COMPONENTS Interpreter Development.Module NumPy)
message(STATUS "SKBUILD_DATA_DIR : ${SKBUILD_DATA_DIR}")
# (Step 1) Add mfem
ExternalProject_Add(
mfem
GIT_REPOSITORY https://github.com/mfem/mfem.git
CMAKE_ARGS
-DBUILD_SHARED_LIBS=1
-DMFEM_ENABLE_EXAMPLES=1
-DMFEM_ENABLE_MINIAPPS=1
-DCMAKE_SHARED_LINKER_FLAGS=
-DMFEM_USE_ZLIB=1
-DCMAKE_CXX_FLAGS=-std=c++11
-DCMAKE_CXX_COMPILER=c++
-DMFEM_USE_EXCEPTIONS=1
)
Ah, sorry, missed this. In a week-long event, and forgot about this. This is an issue in the inner project not finding ninja (the outer one is working fine). Quick thing to check: Can you just pass through -DCMAKE_MAKE_PROGRAM="${CMAKE_MAKE_PROGRAM}"?
how do you export debug info? setting build-type to Debug or RelWithDebInfo isn't doing it
Hello
I am wondering if there a clean way that we can use to send "options" or arguments with the pip install to define some variables in the CMake building process
Other than env variables ofcourse
-Ccmake.define.FOO=BAR?
Exactly what I want.
Thanks again @urban turtle
Hello,
I think I found an issue with skbuild when trying to call python scripts from CMake
I am following this tutorial here
https://jax.readthedocs.io/en/latest/ffi.html#foreign-function-interface-ffi
using this CMake
Where we use the python interpeter to find include dirs
Like so
find_package(Python 3.8
REQUIRED COMPONENTS Interpreter Development.Module
OPTIONAL_COMPONENTS Development.SABIModule)
message(STATUS "Python_EXECUTABLE: ${Python_EXECUTABLE}")
execute_process(
COMMAND "${Python_EXECUTABLE}"
"-c" "from jax.extend import ffi; print(ffi.include_dir())"
OUTPUT_STRIP_TRAILING_WHITESPACE OUTPUT_VARIABLE XLA_DIR)
message(STATUS "XLA include directory: ${XLA_DIR}")
But it says that I don't have JAX, but I definitly do
When I printed the PYTHONPATH I got
-- PYTHONPATH: /tmp/pip-build-env-hkmybt1g/site
Which explains why I found no modules or packages
Anyone knows a workaround
Did you add JAX to your build dependencies?
oh in the TOML?
Yeah.
yup that was it
Thx
Heya, I’m trying to package micromamba 2.0 for Arch Linux.
Before 2.0, one could just build all the C++ stuff together, then just package the Python module:
cd micromamba-root
cmake -D BUILD_LIBMAMBAPY=ON --preset=...
cd libmambapy
python -m build --wheel --no-isolation
but now that fails because the last line runs CMake again for just the libmambapy target instead of just packaging what’s there.
I tried using SKBUILD_CONFIGURE_OPTIONS=... with the options I used before, but it didn’t work since it runs just for the libmambapy target and tries to find the libmamba.so (which isn’t installed system-wide at that point)
Is there a way to either
- skip running CMake and just packaging stuff when running
python -m build, or - tell scikit-build to target the
CMakeLists.txtin the parent directory to build everything instead of just theCMakeLists.txtin the libmambapy directory?
Is this the wrong forum to ask? If so, where can I get an answer to this?
This is probably fine, I’m just a bit behind. 2 should work, you can set the source dir to find CMake from
Thanks! Do I have to patch the package’s setup.py to set skbuild.setup(..., cmake_source_dir='..') or is there a way to override this option using environment variables?
Ah, this is scikit-build classic? With scikit-build-core, yes. I seem to remember starting to look into this.
What's the current state? Does it just rebuild, are you missing stuff, etc? I see several things I could improve, so I'll probably investicate and make a PR.
I’ll tell you next weekend. I was on holiday and now I’m away from my personal machine.
hello. I am starting to use the scikit build core. would be possible to use this tool to both package CPP extensions and python code. In my current situation I need to build a cpp extension using pybind11 and I want to avoid to have two repositories one for the extension and another for the python code. thanks
Yes, most projects have both some Python and extensions inside. Scikit-build-core will auto discover src/<package_name> and a few similar patterns, or you can explicitly list packages in tool.scikit-build.wheel.packages.
hello @urban turtle even after setting tool.scikit-build.wheel.packages the python files aren't copied after the installation any hints ?
Do you have a repo?
It needs to be the path to the top level of the package, including the package name.
hello @urban turtle it worked. after I have done python -m build
I was using pip3 install .
cmake parent directory
Hey, I am trying to build a fortran90 module using scitkit-buld core - basically just using the example here (https://scikit-build-core.readthedocs.io/en/latest/getting_started.html), slightly modified.
[build-system]
requires = ["scikit-build-core", "numpy"]
build-backend = "scikit_build_core.build"
[project]
name = "example"
version = "0.0.1"
dependencies = ["numpy"]
[tool.scikit-build]
ninja.version = ">=1.10"
cmake.version = ">=3.17.2"
cmake_minimum_required(VERSION 3.17.2...3.29)
project(${SKBUILD_PROJECT_NAME} LANGUAGES C Fortran)
find_package(
Python
COMPONENTS Interpreter Development.Module NumPy
REQUIRED)
# F2PY headers
execute_process(
COMMAND "${PYTHON_EXECUTABLE}" -c
"import numpy.f2py; print(numpy.f2py.get_include())"
OUTPUT_VARIABLE F2PY_INCLUDE_DIR
OUTPUT_STRIP_TRAILING_WHITESPACE)
add_library(fortranobject OBJECT "${F2PY_INCLUDE_DIR}/fortranobject.c")
target_link_libraries(fortranobject PUBLIC Python::NumPy)
target_include_directories(fortranobject PUBLIC "${F2PY_INCLUDE_DIR}")
set_property(TARGET fortranobject PROPERTY POSITION_INDEPENDENT_CODE ON)
add_custom_command(
OUTPUT examplemodule.c example-f2pywrappers.f
DEPENDS example.f90
VERBATIM
COMMAND "${Python_EXECUTABLE}" -m numpy.f2py
"${CMAKE_CURRENT_SOURCE_DIR}/example.f90" -m example --lower)
python_add_library(
example MODULE "${CMAKE_CURRENT_BINARY_DIR}/examplemodule.c"
"${CMAKE_CURRENT_BINARY_DIR}/example-f2pywrappers.f"
"${CMAKE_CURRENT_SOURCE_DIR}/example.f90" WITH_SOABI)
target_link_libraries(example PRIVATE fortranobject)
install(TARGETS example DESTINATION .)
finally, the fortran file. This works fine:
real function square(x)
real, intent(in) :: x
square = x * x
end function square
python3 -c "import example; print(example.square(2))"
nicely returns 4.0
However, if I convert it to a module, it does not work any longer!
module math_operations
implicit none
contains
real function square(x)
real, intent(in) :: x
square = x * x
end function square
end module math_operations
now I get this:
What I am wondering is, why does it say Fortran 90 wrappers are saved to "./example-f2pywrappers2.f90" where does the "2" come from? Now it makes sense it does not find /tmp/tmpstav11xc/build/example-f2pywrappers.f
But what do I have to change to make it work with a module? I was not able to find any documentation for that.
Just using f2py, it works
Discord does not allow me to paste the whole output...
Could you try https://github.com/scikit-build/f2py-cmake ?
F2Py helpers for CMake. Contribute to scikit-build/f2py-cmake development by creating an account on GitHub.
Also, did you remove the output dir before rerunning as a module? Just in case that's why it changed the name.
I'm not very familar with Fortran. I'd probalby start by investicating why f2py adds a 2.
Mhm, now I get Fatal Error: Cannot open module file ‘math_operations.mod’ for reading at (1):
in the example (https://github.com/scikit-build/f2py-cmake?tab=readme-ov-file#example), it should probably be target_link_libraries, not target_link_library?
okay, I can at least reproduce the 2 with just numpy and f2py, let's see why it does that...
Ah, yes, that's a typo, thanks!
I got it working now with
cmake_minimum_required(VERSION 3.17.2...3.29)
project(
${SKBUILD_PROJECT_NAME}
LANGUAGES C Fortran
VERSION ${SKBUILD_PROJECT_VERSION})
set(ModuleName "_thermal_comfort")
set(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} -O3 -Wall -march=native -ftree-vectorize -funroll-loops")
find_package(
Python
COMPONENTS Interpreter Development.Module NumPy ${SKBUILD_SABI_COMPONENT}
REQUIRED)
# F2PY headers
execute_process(
COMMAND "${PYTHON_EXECUTABLE}" -c
"import numpy.f2py; print(numpy.f2py.get_include())"
OUTPUT_VARIABLE F2PY_INCLUDE_DIR
OUTPUT_STRIP_TRAILING_WHITESPACE)
add_library(fortranobject OBJECT "${F2PY_INCLUDE_DIR}/fortranobject.c")
target_link_libraries(fortranobject PUBLIC Python::NumPy)
target_include_directories(fortranobject PUBLIC "${F2PY_INCLUDE_DIR}")
set_property(TARGET fortranobject PROPERTY POSITION_INDEPENDENT_CODE ON)
add_custom_command(
OUTPUT "${ModuleName}module.c" "${ModuleName}-f2pywrappers2.f90"
DEPENDS "src/${ModuleName}.f90"
VERBATIM
COMMAND "${Python_EXECUTABLE}" -m numpy.f2py
"${CMAKE_CURRENT_SOURCE_DIR}/src/${ModuleName}.f90" -m "${ModuleName}" --lower)
if(NOT "${SKBUILD_SABI_COMPONENT}" STREQUAL "")
python_add_library(
${ModuleName} MODULE
"${CMAKE_CURRENT_BINARY_DIR}/${ModuleName}module.c"
"${CMAKE_CURRENT_BINARY_DIR}/${ModuleName}-f2pywrappers2.f90"
"${CMAKE_CURRENT_SOURCE_DIR}/src/${ModuleName}.f90"
WITH_SOABI USE_SABI 3.7)
else()
python_add_library(
${ModuleName} MODULE
"${CMAKE_CURRENT_BINARY_DIR}/${ModuleName}module.c"
"${CMAKE_CURRENT_BINARY_DIR}/${ModuleName}-f2pywrappers2.f90"
"${CMAKE_CURRENT_SOURCE_DIR}/src/${ModuleName}.f90"
WITH_SOABI)
endif()
target_link_libraries("${ModuleName}" PRIVATE fortranobject)
install(TARGETS "${ModuleName}" DESTINATION "thermal_comfort")
still no idea though why the 2 is appended though...
...but it paid of 57x seedup for scalar values and 5x speedup for array operations 🥳
https://numpy.org/devdocs/f2py/buildtools/index.html lists it as part of Fortran 90
Adding F90 should fix it with f2py-cmake
Hello, I'm trying to set a CMake define based on the path of the pyproject.toml, is there a way to do that?
Hello, I'm trying to set a CMake define
Hello! I'm running into an issue where scikit-build-core doesn't produce a valid importable wheel. Here are the contents of the unzipped wheel:
$ tree -L 1
.
├── bin
├── finch_mlir-0.1.dev60+g27552e4.dist-info
├── include
├── lib
├── python_packages
├── share
└── src
And here is my scikit-build config in pyproject.toml:
[tool.scikit-build]
minimum-version = "build-system.requires"
ninja.make-fallback = false
cmake.args = ["-UNB_SUFFIX", "-UNB_SUFFIX_S"]
wheel.packages = ["python_packages/finch/mlir_finch"]
wheel.exclude = ["bin/**", "lib/**", "share/**", "src/**", "python_packages/mlir_core/**"]
...
As you can see, unnecessary directories are included whereas the necessary python_packages/finch/mlir_finch isn't really moved to site-packages, IIUC.
Hello! I'm running into an issue where
Hello. I'm having issues getting scikit-build-core to play nice with ccache. I test it by repeating the following steps twice:
ccache -C && rm -rf build dist(clear ccache only the first time)time python -m build --sdist --wheel(note timing)ccache -s
Funnily enough, using the setuptools plugin, ccache works like a charm, but ${SKBUILD_PLATLIB_DIR} is undefined. Is there an alternative in that case as a workaround? Also I lose the ability to control the contents of the wheel there.
The repo/branch is here: https://github.com/hameerabbasi/Finch-mlir/tree/pyproject-toml
Hello. I'm having issues getting `scikit
The latest scikit-build-core from Git seems to have an incorrect version in the docs: https://scikit-build-core.readthedocs.io/en/latest/
I came here to report the same thing. the stable version is correct (0.10.7), but latest points to 0.1dev which is quite old. the link that shows up on the google search results (0.10.8.dev44) reports a 404: https://scikit-build-core.readthedocs.io/en/latest/configuration.html (as this page probably didn't exist on 0.1dev).
maybe the tags are missing, and the version can't be figured out because of that? RTD makes a shallow checkout IIRC (50 commits). so https://github.com/scikit-build/scikit-build-core/blob/main/.readthedocs.yml should unshallow it with a post_checkout: command.
I put together a PR here: https://github.com/scikit-build/scikit-build-core/pull/1001
Dev meeting in about half an hour (12 Eastern)
[scikit-build-core as backend] Hi, I want to skip the CMake build when deploying my project's documentation through an integration process like Cloudflare Pages. I don’t want the deployment process to install my project—only the dependencies specified in optional.doc. However, it seems that --no-deps only skips dependency installation, not the CMake build. I’d prefer not to use a requirements-doc.txt file. Is there a way to skip the CMake build directly in the pip command?
Ah, I seem to forget to check this - do you still need an answer? Yes, it's possible, -Cwheel.cmake=False would do it. Though it would be better to use dependnecy-groups instead of project.optional-dependencies. The next version of pip will have it, but you can simply use the pip install dependency-groups package or uv today to get a way to install them.
Hi. I am trying to port an existing setuptools based package to scikit-build-core and have run into an issue with creation of an sdist. What is the right way to provide what files should be included in the sdist tarball? I have a [tool.scikit-build.sdist] section in my pyproject.toml where I specify the include list. However, it seems that irrespective of what I add there everything in my source tree gets added to the sdist. I have used python -c "from scikit_build_core.build import build_sdist; build_sdist('dist')" and also python -m build --sdist --no-isolation with the same result. I will be very grateful for some insight into how to get sdist build to work.
Do you have a exclude like !.*? Excludes apply on top of includes.
Yes, I do.
"**/*.o",
"**/*.so",
"**/*.dylib",
"**/*.a",
"**/__pycache__/**/*",
"**/*.pyc",
"**/.git/**/*",
"**/.vscode/**/*",
"**/.DS_Store"
Do you have a repo to look at? I'm looking for a negative exclude
Sure that will be awesome. Let me push my changes to my repo quickly.
https://github.com/diptorupd/flashinfer/tree/feature/scikit-build. This my work in progress branch.
Do you have an example of a file you don't want in the SDist that's getting added?
Yes, I am seeing all files inside the 3rdparty directory getting added instead of just the 3rdparty/cutlass/include/**/*. I also see every folder in the root directory such as benchmarks getting added. I want only the directories I specified in the include list.
Perfect, thanks, checking on it
Thank you very much!
[tool.scikit-build.sdist]
cmake = false
exclude = [
"**",
]
include = [
"3rdparty/cutlass/include/**/*",
"3rdparty/cutlass/tools/util/include/**/*",
"cmake/**/*",
"flashinfer/**/*",
"libflashinfer/**/*",
"tvm_binding/**/*",
"LICENSE",
"README.md",
"pyproject.toml",
"!.venv/**",
]
Should do what you want. There are default excludes, so you don't need those. You do need !.venv since LICENSE and README.md will match every LICENSE and README.md, even in virtual environments - I think that's a bug, we need to shortcut checking inside directories if the directory is ignored. Also, in the future there will be a "mode" setting that should include an "explicit" mode, which will not include everything that doesn't match a rule.
Currently, everything is checked per file, and includes always win over excludes.
Let me quickly try. Thank you for taking time out to look at the issue.
I'm adding INFO logging to explain why each file matches or is excluded to make understanding this easier (that's what I used here)
It did generate a much better sdist than before, but I still see some issues. Everything in 3rdparty got added and the _skbuild build directory also was added.
(fi-devel-torch24-cuda124-py312) ~/devel/flashinfer/dist$ ls flashinfer-0.2.4.post18
3rdparty flashinfer LICENSE profiler README.md tvm_binding
cmake libflashinfer PKG-INFO pyproject.toml _skbuild
(fi-devel-torch24-cuda124-py312) ~/devel/flashinfer/dist$ ls flashinfer-0.2.4.post18/3rdparty/
composable_kernels cutlass googletest mscclpp nvbench spdlog
The profiler directory also somehow got added.
The build directory should be auto-ignored, that's worrying. Let me check. I'm fiddling with scikit-build-core's source and I've done an editable install of it, so will take a minute 🙂
I think I have a hunch why profiler gets added. I see a README in that directory.
Yes, I'm working on fixing the recusion now. Though if a parent directory is not ignored, it's correct to include it
Did you intentionally not include your /licenses folder, by the way?
No, that was an oversight. 🙂
The most common pattern is to just take the default includes (which is based on the .gitignore) and add extra ignores on top for things that shouldn't be in the SDist but should be in the git repo.
Ahh, I see why _skbuild is slipping in, it's due to it having some of those files, and it's added as an exclude, so the includes are taking priority.
The directory thing would fix that
I should then flip the inclusion/exclusion logic. Just add excludes explicitly and not the other way round.
That's what works best, usually.
The directory fix will help a lot (and it's faster!), and we'll have an explicit mode for this in the future.
is that part of 0.12?
The fix I think is fine for a patch release (which is soon).
Mode probably could probably be 0.12, or maybe 1.0 if it misses that.
nice!
The check-sdist tool is nice if you'd like to check with that.
I'd recommend not putting ninja and cmake in your build-system.requires, scikit-build-core will intelligently add them as needed (and you should even see a warning the way it is now)
I am trying out the excludes patterns now. I wanted to ask you another question. How can I package the _version.py file that setuptools-scm generates inside the sdist? Do I need to run cmake before sdist generation?
Yes, I have been meaning to take them out after I saw those warnings 🙂
Oh, you can remove setuptools_scm too when using the provider, it adds it for you. Doesn't really hurt anything though.
If you use the tool.setuptools_scm options, it works just like it does with setuptools. It adds the file in-tree and you need to add it to the explicit includes (one reason that's there and overrides the excludes, since it will be in your gitignore!). However, you can also use the generate feature which gives you three choices about where to add the file and automically handles the include part.
Side-comment/observation mostly for @urban turtle because I stumbled over that sometime in the past: This auto addition will not work if you are building without build isolation since scikit-build-core auto adds CMake and Ninja to the build dependencies, which are ignored for builds without isolation. In that case, one needs to remember to explicitly install CMake and/or Ninja if a more recent version is needed than currently provided by the system.
If you are building without build isolation, you are installing everything yourself anyway, not pulling from the build-system.requires table?
Yeah. I just stumbled over this because the isolated build will automagically add CMake & Ninja as required without them being present in the build-system.requires table, while the non-isolated build will not pull these in.
I was installing all the build-system.requires dependencies manually for the non-isolation build. I suppose there is no easy way for scikit-build-core to inject the CMake and/or Ninja packages in a non-isolated build, right? Probably by definition of a non-isolated build.
By definition.
I mean, technically scikit-build-core could mess with the environment, but that would be not be playing nice.
Hmmm, interesting command idea. scikit-build build-deps could spit out the list of packages needed to build. (We don't really have a working CLI yet)
FWIW, this also goes into the heart of why a --only-build-deps pip install flag is hard, because build dependencies can be added dynamically. Most users of such a flag probably don't care and wouldn't want pip to call the backend at all, but it wouldn't be fully spec-compliant and cause issues when there are dynamically added dependencies.
I don't know what the current consensus on the design is, the conversation happened way before I became a pip maintainer.
I mean, you can do this .venv/bin/python -c "from scikit_build_core.build import get_requires_for_build_wheel as bw; print(bw({}))" -> ['cmake>=3.26.1', 'setuptools-scm']. Combined with tomllib you could print out what you needed.
Setting the excludes explicitly as you had suggested works so much better. The only "troublesome" directory is 3rdparty/cutlass. I need to include just two sub-directories from this directory and having to manually exclude everything each sub-directory makes it a bit fiddly.
But for a general tool it would have to support -C, --wheel/--sdist
Oh yeah, I forgot that sdist dependencies can be different. Fortunately, pip never builds a sdist and exposes it to the user so that doesn't actually matter(...?)
That sounds interesting..
Right now, we are kind of duplicating the build time dependencies as a build dependency group that we use to set up the environment for a non-isolated build. It works. but still feels kind of awkward and redundant. And, we do not include CMake/Ninja in that dependency group.
Depends on what you are doing and why you want them. 🙂 But in pip's case, doing just the build one is likely what you'd want since it can't build SDists.
we have pip wheel as the --build flag, no?
Ah you meant --wheel. That makes more sense.
A pip install --build-deps would have to be two stage, first installing the static build deps, then running the build backend to get the dynamic ones
IIRC people didn't want pip to call the backend for some reason.
I should read that thread in full...
I almost got it to work. With the following config, I am able to exclude all of 3rdparty/cutlass except the two sub-directories I want to add to the sdist.
[tool.scikit-build.sdist]
cmake = false
exclude = [
"examples",
"profiler",
"benchmarks",
"docker",
"scripts",
"tests",
"docs",
"3rdparty/mscclpp/**/*",
"3rdparty/gooogletest/**/*",
"3rdparty/nvbench/**/*",
"3rdparty/spdlog/**/*",
"3rdparty/composable_kernels/**/*",
"3rdparty/cutlass/**/*",
"ci",
"aot_build_utils",
"Jenkinsfile",
"format.sh",
"custom_backend.py",
"version.txt",
]
include = [
"3rdparty/cutlass/include/**/*",
"3rdparty/cutlass/tools/**/*",
]
Although, for some reason googletest is getting added despite me excluding it.
Of course! it is googletest with two o and not three! 😄
@urban turtle Thank you for your support today! I very much appreciate it.
The bottom of the Google page used to have goooooooogle IIRC. 🙂
This script gets build-requirements fine. 😄
Hi! I need some help with enabling editable installs in my repo is https://github.com/diptorupd/flashinfer/tree/feature/scikit-build. The issue may be due to something incorrect I am doing in my CMake scripts.
The project is a CUDA header-only kernel library with a C++ API and a Python API that exposes PyTorch extension modules. The headers are installed as part of the package inside ("${SKBUILD_PLATLIB_DIR}/flashinfer/include") since the Python API includes a JIT compilation functionality. The installation works as expected during Wheels builds. When doing an editable install I encounter the following behaviour:
a) the headers get installed properly the first time I run python -m pip install --no-build-isolation --config-settings=editable.rebuild=true -ve..
b) Any subsequent change to a header lead to a rebuild of the kernels and the torch extension as expected. The rebuilt extension gets installed into the ${SKBUILD_PLATLIB_DIR}/flashinfer/correctly.
c) But, the headers themselves are not reinstalled.
It will get great to get some insight. The most relevant CMake files in my repo are: https://github.com/diptorupd/flashinfer/blob/feature/scikit-build/libflashinfer/CMakeLists.txt, https://github.com/diptorupd/flashinfer/blob/feature/scikit-build/flashinfer/csrc/CMakeLists.txt.
@urban turtle I investigated some more about the editable install configuration for my project using scikit-build-core and wanted to clarify a conceptual thing.
In my project I need to install some CUDA headers inside the package as they are used at runtime for JIT compilation. I have the following install command in my CMakeLists.txt
https://github.com/diptorupd/flashinfer/blob/49d94305fbc100c6959f41a81ddd618a5b21ad28/libflashinfer/CMakeLists.txt#L176
Then to look up the location of the headers, I have a method like NumPy's get_include:
import os
import pathlib
from sysconfig import get_path
def get_include():
platlib = get_path("platlib")
rootdir = os.path.join(platlib, "flashinfer")
include_dir = os.path.join(rootdir, "include")
return str(include_dir)
The logic works fine for regular wheel build. For editable installs I see the headers getting installed into site-packages\flashinfer\include when I first run python -m pip install --no-build-isolation --config-settings=editable.rebuild=true -ve. However, on subsequent re-installs of the headers I see them getting installed only into the tmp directory. Is this the expected behaviour?
Is this the expected behaviour?
I don't think so. The rebuild uses logic that's embedded into the hook it installs, so I bet we might be making a mistake there.
Log excerpt from first pip install -ve.
-- Build files have been written to: /home/diptodeb/devel/flashinfer/_skbuild
2025-04-28 16:28:24,198 - scikit_build_core - WARNING - Unsupported CMAKE_ARGS ignored: -DCMAKE_BUILD_TYPE=Release
*** Building project with Ninja...
2025-04-28 16:28:24,202 - scikit_build_core - INFO - RUN: /data/diptorup/micromamba/envs/fi-devel-torch26-cude126-py312/lib/python3.12/site-packages/cmake/data/bin/cmake --build _skbuild -v
Change Dir: '/home/diptodeb/devel/flashinfer/_skbuild'
Run Build Command(s): /data/diptorup/micromamba/envs/fi-devel-torch26-cude126-py312/bin/ninja -v
ninja: no work to do.
*** Installing project into wheel...
2025-04-28 16:28:24,278 - scikit_build_core - INFO - RUN: /data/diptorup/micromamba/envs/fi-devel-torch26-cude126-py312/lib/python3.12/site-packages/cmake/data/bin/cmake --install _skbuild --prefix /scratch/diptorup/tmp/tmp_ara78wm/wheel/platlib --strip
-- Install configuration: "Release"
-- Installing: /scratch/diptorup/tmp/tmp_ara78wm/wheel/platlib/flashinfer/include/flashinfer/configure.h
-- <elided ...>
-- Installing: /scratch/diptorup/tmp/tmp_ara78wm/wheel/platlib/flashinfer/include/flashinfer/layout.cuh
-- <elided ...>
*** Making editable...
2025-04-28 16:28:24,469 - scikit_build_core - INFO - Discovered Python package at flashinfer
*** Created flashinfer-0.2.5.post17-cp39-abi3-linux_x86_64.whl
Building editable for flashinfer (pyproject.toml): finished with status 'done'
Created wheel for flashinfer: filename=flashinfer-0.2.5.post17-cp39-abi3-linux_x86_64.whl size=226183 sha256=b9a0732e8c2ee91c84a2eb8192fdb5ba395b3cfd87f1562e6040582fdd0d1f2d
Stored in directory: /scratch/diptorup/tmp/pip-ephem-wheel-cache-ilsb8g0k/wheels/3b/20/68/059c8d9639eca00a03199fb022caa95846b079d98094cddd18
Successfully built flashinfer
Installing collected packages: flashinfer
Successfully installed flashinfer-0.2.5.post17
The first time I build how is the include directory copied from the tmp directory into the actual site-packges directory /micromamba/envs/fi-devel-torch26-cude126-py312/lib/python3.12/site-packages/flashinfer/include?
It's copied to the temp wheel directory, then that temp wheel is installed. That's how wheel-based editable installs work. The auto rebuild feature directly installs, since it's not able to rely on your installer's temporary wheels.
I don't think so. The rebuild uses logic that's embedded into the hook it installs, so I bet we might be making a mistake there.
auto rebuild feature directly installs,
The auto rebuild should install into the actual install location (site-packages/flashinfer/include) and not into the temp wheel. Is that a right understanding of your first statement?
Yes
Bit of a long shot but is it possible to avoid full rebuilds without having to specify --no-build-isolation? Finding that just setting build-dir isn't enough. Guessing it might be something quirky with temporarily installing cmake but I wouldn't really be able to prove that.
The issue is typically with the python build dependencies. E.g. if you use pybind11 then it will reconfigure and rebuild each time
Ah. I'm using nanobind but it's probably the same problem. I suppose I could set up a venv to get nanobind pre-installed but at that point may as well just pre-install everything into the venv and pass --no-build-isolation :P
Still, knowing where the problem probably was is valuable information so thank you.
You can conditionally add the build dependency for nanobind, although we don't have a built-in way for doing that based on find_package calls
--no-build-isolation appears to solve multiple problems in my life. This may be a bit of a tangent.
I asked my question in the first place because I was having trouble building my package as part of a docker image or inside a docker container. Project is C++ with build dependencies on scikit-build-core and nanobind. pip install was using twice as much memory inside the docker build than it was on the host (~7GBvs~3GB). Even worse, project had dependencies with CUDA code and building that took 16-24GB. When the host is a Jetson with 8GB memory, that doesn't quite work!
For giggles, I ran pip install --no-build-isolation inside the container and my problem appear to have disappeared, the memory usage while building was the same inside the container as I would see outside.
It's a bit confusing. If it was just the bloat of temporary requirements, I could imagine higher memory usage but I'd expect it to happen immediately and not continue climbing as things were being built. And I wouldn't expect the CUDA compilation to take so much more memory either when the cost is negligible on the host.
That is to say, I don't know if this is a scikit-build-core issue, a pip issue, a PEBKAC, or something else.
Perhaps the build requirements fetched in the isolated environment differ (in version, particularly) from what your container image has?
but yeah if you're using a container then build isolation is normally redundant, unless you have to build multiple things with conflicting build tools for some reason
(which TBF, I have no idea how common that is!)
In my case, I'm copying the venv from one stage to the next which makes build isolation desirable because it means not putting build dependencies into the final venv. I can of course spawn a new venv just to build in or even make a separate stage for it.
If you already have a Docker container for building stuff, it’s indeed the cleanest solution to install your build dependencies system-wide instead of using virtual environments. That way you have one predictable build environment (the Docker container) instead of environments inside of environments.
The same applies if you e.g. want to use another dependency manager like when you make a package for a Linux package manager that wraps a Python package. You’d like to use that package manager’s build dependency system here instead of having pip or so download stuff. The disadvantage is that you need to translate the dependencies (e.g. on Arch Linux, some system packages contain multiple Python packages so you don’t have a 1:1 package name mapping).
@urban turtle does this sound familiar at all to you? https://github.com/ofek/coincurve/pull/211#issuecomment-3422217259
reading https://discuss.python.org/t/adding-extension-module-examples-to-the-packaging-user-guide/105111/15 (and trying not to make the DPO thread longer), i've been thinking about how to improve the getting started situation with better tooling. instead of writing, can we start them with a template and a tool that does the distribution part
in uv, we have uv init --build-backend scikit that tries to kinda invert the problem steps by setting you up with a package and then letting the user fill in their C or C++, though i'm not sure if that fulfills the distribution aspect
Having something like maturin new for the other backends would be extremely helpful. The fact that maturin new gives you a working, importable python project out of the box made it easy to experiment. I could immediately try small changes, like switching sum_as_string(u32, u32) to sum_as_string(String, String), and generally get a feel for how everything fits together. In contrast, if I just want to quickly try an equivalent C extension that implements something as simple as sum_as_string(int, int) -> str, it currently takes a lot more setup and effort.
You can use cookiecutter gh:scientific-python/cookie, that supports scikit-build-core, meson-python, and maturin (and several other pure python backends). uv init also supports scikit-build-core and maturin, and is a bit lighter weight. We could probably add something like this pretty easily to scikit-build-core itself, too. (The light-weight one)
Hmm, interesting about distribution. That is going to be tied to CI. The scientific-python cookiecutter assumes GHA (or GitLab CI if you put a gitlab URL for the project, a little less featured though)
Ralf Gommers was just talking to me about a project to test all these combinations of tools quickly to aid development of the new stable ABI for python 3.15. Seems to be a lot of overlap. I’ll bring it up with him that there seems to be a lot of overlap in this space and it’s also a documentation issue
Ralf pointed me at https://github.com/mesonbuild/meson-python/tree/main/tests/packages which might be useful from the meson-python end for templates.
shower thought: make a website called pyproject.new that asks a bunch of questions about the kind of project (app/library, pure python or extensions, which language the extensions are in, maybe other axes) and then generates a URL a user can clone with git to get going.
anything more constructive to offer than a meme?
A zipped folder would be much easier than trying to initialize a git repo in a web app backend
This is an established pattern, see https://start.spring.io/
Also note that the .new TLD is one of those "brand name" TLDs that's absurdly expensive for no good reason
(Price in USD)
yikes, different url hack needed then
!xkcd 927? 😛
If there is an interface you like among the well-known tools, we could probably help make some templates for those rather than making yet anothe thing
that is what most tools offer on <tool> new/<tool> init, just via CLI not via web UI
I've played with the idea of running something like cookiecutter in WebAssembly. I also worked with a student in the past to make a Textual cookiecutter/copier interface, the project is still around, still a CLI though
Crazy coffee thoughts.
When using scikit-build-core as a backend for a C++ project using Conan, would it be possible to have the conan dependencies installed by the build system instead of as a separate step? This gets a bit tricky because I don't know how dependencies are actually resolved for a package, could be impossible if it really depends on pulling from an index or something python package-like
Doesn't fixing build-dir do that?
I'm not following, sorry. I should also say that this isn't a specific trouble I'm encountering, just a thought (though of course it's relevant to work I'm doing)
Coming from limited to no knowledge of how conan works, but if the build cache (wherever it builds/installs the dependencies) is under the CMake build directory or somewhere similarly predictable, then if you make that persistent (e.g. setting tool.scikit-build.build-dir) then the dependencies would persist across builds
On re-read, are you more looking about running conan/vcpkg etc. commands within pip install, right before the scikit-build-core's cmake build? It may depend what do you need as arguments for those commands, but it could be possible to expose a hook for that. Would everyone have to use conan to build your package though?
yes this is the scenario I'm imagining. And I can understand why it wouldn't be a feature in general because of that hard requirement on conan (though conan is available on pypi!) as well as conan's build cache being separate from everything else but in my case, it's all in-house building.
The real value add, and I suppose this might be the harder thing to do depending on what hooks are available, would be better layer management in a dockerfile. With uv, you can do uv sync --locked --no-install-project to install the dependencies of a project without installing the project itself. How it does that, I don't know. That's where I'm wondering if it's possible at all.
About adding it I've seen projects that wrap around scikit-build-core overwriting some functions to inject some calls, you can of course do that. Putting it in scikit-build-core proper would not be impossible, it's more of a question of what interface to provide under (can go into more workshoping in an issue if you want to open it)
For something like dockerfile, wouldn't you want to run conan separately for the caching of layers?
On the uv note, that would not be relevant unless you build with no-build-isolation
While there is a general component to this, I feel like my specific goals might be too niche to justify opening an issue. And there's too many ways to utilize conan in a project that it would be hard to handle them all.
As for the dockerfile, "yes". But there's a few ways to go about that. You could of course run conan separately. And technically that is the best way to do it since those C++ dependencies probably aren't changing as often as Python dependencies. But say the project is in a workspace and every other project in that workspace just requires the uv sync to handle dependencies. Now this other package requires special treatment. And if the image is configured to not install the package, you're still paying the cost of installing its dependencies because there is no conditional layer.
I suppose there's no perfect solution to the problem, just a lot of thoughts effectively tangential to scikit-build-core as a project. maybe I'm rambling too much 😅
On the nichness, I don't think it would be that niche. Basically would not support conan specifically, just a way to hook into various points of the build process
Hooking into the uv sync, that would not be possible by us, and I don't think uv would want to expose that either. Maybe a different question, why not break the dockerfile into smaller components (and join them with multiple FROM at the workspace containerfile) that can be templated and extended as needed?
That's a work in progress.
And it looks like if I'm reading PEP 517 correctly, there is the hook to add additional requirements but they still have to be valid PEP 508 dependency specifications. There's no PEPy hook for arbitrary work to be done when installing dependencies. Which is probably a good thing but foils my coffee thoughts :P
There are peps for supporting system/distro packages, but discussion and progress on those seem to stall or get sidetracked
If anyone involved in those discussions are prone to impulse thoughts like me, can definitely understand why that would happen :P
It's more like too many conflicting opinions coming from different ecosystems ultimately discovering why distro packaging is a thing and the way it is now
Cargo ecosystem has a better approach to this imo. It's swiss cheese when it comes to implementation, but the concept is sane
this is the problem that conda/pixi solves, FWIW
if anyone with an apple silicon mac wants to help get rid of some gnarly setup.py and add more scikit-build-core, I opened this issue and the maintainers seem interested https://github.com/ml-explore/mlx/issues/2876
mlx is basically, "pytorch, but on apple silicon GPUs"
I thought you would be showing a custom setup.py monstrosity. That one is quite straightforward
What build dependencies do you need?
sorry for taking so long to get back to you, I had to remember why I was asking for this in the first place even though I rambled about the specifics later on.
build dependencies would just be conan I think. But the tricky bit is that conan has its separate list of dependencies that it wants to install when invoked and those dependencies are from a different, non-python index (conan center).