> Starting from the above I have managed to write a tiny Python-based
> makefile that does the expected:

> https://github.com/stefanseefeld/boost.build/commit/47d4e11f04bbaa4e658a490cc2ad0d1330d486dd
This is pretty neat.
Just wanted to mention that I started a pure Python rewrite of Boost.Build at the beginning of this year.
In my rewrite, instead of registering target types by type.register('EXE'), I allowed for simply subclassing
the TypedTarget class, e.g.
class Exe(TypedTarget):
    # ...
my_exe_target = Exe('hello', ['hello.c'])
Additionally, I started off the rewrite by converting the updater/scheduler (mostly make.c and make1.c)
to pure Python. During this phase, instead of having Jam action strings that are converted to shell
scripts, I created Action classes instead that required a run() method to be implemented. The run()
method itself is in charge of updating the target(s) e.g. by making subprocess calls directly. Or, in the
event of something like "common.copy", I just used shutil.copy(), etc.
This rewrite is still under construction as I'm re-considering certain implementations. However, with having
the updating part of the C engine ported, I am able to declare low-level targets and declare actions.
By hardcoding a default toolset, I am able to run simple C -> O -> EXE updating actions much like
what you have here Stefan. 
This brings me to another thought/question: does the low-level updater/scheduler really need to be written
in C/C++? After performing the port of this code to Python, the majority of this code is going to be
heavily I/O bound, so I would assume that any speed that would be seen from C++ would be negligible due to
the file I/O that's occurring? The INCLUDE scanning might be faster in C++, but that's all I can think of at the moment.
During my adventures with Boost.Build and trying to optimize it, I've found that the
biggest slowdown is anything to do with Property Sets and Properties within the Metatarget -> Virtual Target
construction. Of course, being mostly a Python developer, I am biased :)
All of the rewrite that I've done so far is stored on a private GitLab instance, but I could potentially
make some of the code public if anyone were interested. It's been months since I've worked on it,
so I really can't remember any of the specifics of the state of the code other than that I got simple
actions working (phase 3) and phases 1 and 2 aren't anywhere near ready.