CMake Python scripting frontend experiment

This has come a couple times in the past but now something actually usable is here.

I extended CMake with an embedded Python interpreter that allows optionally replacing parts (or the whole) of the configure process with Python. No grand plans, endless discussions, politics, just working code - proof of concept mind you! - you can try out right now.

#!cmakepy
import cmake

cmake.fn.message("STATUS", "Hello from CMake python")

cmake.cmake_minimum_required("3.25")
cmake.project("pytest", version="0.1", description="Hello from CMake python", languages=["C","CXX"])
cmake.add_executable("pytest", ["main.cpp"])

The full CMake command interface is available, so any CMake function can be called from python.

Works on any file normally executed by CMake

Just put the text #!cmakepy as the first line in any script and it’ll be treated as Python. This includes any file included by CMake include() or add_subdirectory().
This also means that to execute subdir scripts (added by add_subdirectory()) CMakeLists.txt is still used. Use #!cmakepy and it’ll be executed as Python.
So no CMakeLists.py for now. (Why? Because it was easier to do it this way at the moment…)

From CMake to Python and back

Two-way interop between CMake script code and Python code also works, meaning CMake code can call Python functions and vice-versa.

test.cmake: Run with: cmake -P test.cmake

function(cmfunc)
    message("Back to CMake: ${ARGN}")
endfunction()

# let's call some python
include(${CMAKE_CURRENT_LIST_DIR}/pyfunc.cmake.py)
pyfunc(first "second;2" third and the;rest "of;the" args)

pyfunc.cmake.py:

#!cmakepy
import cmake

def pyfunc(arg1, arg2, arg3, *args):
    print("We're in python now!")
    print(f"    arg1 : {arg1}")
    print(f"    arg2 : {arg2}")
    print(f"    arg3 : {arg3}")
    print(f"    *args: {args}")
    # let's call back some CMake
    cmake.fn.cmfunc("Hi", "from", "python")

cmake.export_function(pyfunc)

Where’s the documentation?

Nowhere, yet. The above should get anyone started.
The first example above uses hand-written python wrappers (except for message), but those are just a few dummies for now. The raw CMake API and any user-defined CMake functions are available through cmake.fn.XYZ() (note the “.fn.” !).
Other than that, take a look at <installdir>/share/cmake-3.27/Modules/CMakePy/cmake.py for what’s available.

Additional features include: CMake variable access (get/set), scoping between cmake/python function calls, exceptions (propagating through cmake/python boundaries). As to how well they work: YMMV!

Why do all this?

To experiment. To be able to better answer “what if?” questions like “What if CMake had another frontend language?”. Now it does - sort of.

Because it is optional and non-intrusive, it’s possible to augment any existing CMake project of any size with Python snippets to experiment with all sorts of scenarios. That’s the primary intent of this experiment.

Have fun!

2 Likes

See also neek78/pycmake: python vs cmake (github.com) and the corresponding reddit discussion So I made a branch of CMake that supports Python scripting in addition to regular CMake script : cpp (reddit.com)

Well well, what are the odds?
That experiment seems have taken a more opinionated and grand approach, while I took a minimalistic one (the diff size of that branch against mainline CMake is 340k excluding README.rst, mine is 17k), even though the underlying idea is the same. Yet that one doesn’t seems to allow to include() python scripts from CMake, though I don’t think it’d be difficult to do that there either.
My intent was to get it going with as little code as possible with as little modifications to core CMake functionality as possible (the changes required in existing CMake code itself is around ~50 lines, all the rest is isolated in cmCMakePy.cxx and that itself is also not that much ~200 lines).
Try both and see which one you prefer.