Not to take away from your correct answer of course
When compiling a project once from a clean state make has no advantage over a shell script that would use a tool like "parallel" to distribute compilation jobs over all available CPU cores.
However writing such a script for parallel compilation is more complex than writing a simple Makefile executed with "make -j N".
As you say, the main reason of existence of "make" is the acceleration of the recompilation of a project after incremental changes in the source files, when only the files that depend on the changes are recompiled, saving time.
Now imagine that you want to run individual steps from time-to-time, instead of running your whole script start to finish. So maybe you write functions that represent each group of commands, and call the function based on an input argument.
Now imagine that some of your build-steps have dependencies on other build-steps, and you want to be able to say "run step X, plus whatever else you have to do first." Or maybe you want to retry a failed build from wherever it left off, instead of having to re-run the entire thing.
Now imagine that you'd like to have some level of isolation between build-steps, so that one step doesn't mess the other one up.
As you add more and more things to your build script, you'll find yourself writing an implementation of Make pretty quickly.
Now
- sometimes - your build fails halfway though, and you want to resume from where-ever it failed. S
The more possible variations you have to cater for the worse build scripts get.
If you try not to redo work that you did 5 minutes ago that's still valid then build scripts become even more complicated whereas the makefile doesn't change at all.
Other tools do declarative even more than make does but as they get easier to understand they tend to lose abilities.