CMakeSL - proposal of a new scripting language for CMake
Proposal about using CMakeSL (CMake Scripting Language) as an alternative CMake's scripting language.
Hello, I've been working on an alternative scripting language for CMake and I'd like to propose merging it into the CMake codebase.
Table of Contents
- Proposal about using CMakeSL (CMake Scripting Language) as an alternative CMake's scripting language.
- Purpose of this proposal
- Quick links
- tl;dr list of pros and cons of the language
- Integration with CMake codebase
- CMakeSL
- Why not use any of the ready made languages
- Work to be done before the eventual merge
- Summary
- Thanks
Purpose of this proposal
My goal is to present the approach that I took with all its pros and cons, and explain why I think it's a good way for the future of CMake.
The thing that I'd not want to spend too much time on is CMakeSL's API. In this proposal and in CMakeSL's documentation you'll find code like cmake::minimum_required(cmake::version(3, 14, 4));
where the cmake::
seems to be a boilerplate, the code looks verbose etc. I want to explicitly point out that it's only an API and, if there will be a need, it can be very easily changed.
Quick links
- CMakeSL repo: https://github.com/stryku/cmakesl
- Language user guide: https://github.com/stryku/cmakesl/blob/master/doc/UserGuide.md
- Builtin stuff documentation files: https://github.com/stryku/cmakesl/tree/master/examples. Documentation is generated from them, using Doxygen.
- Example usage of CMakeSL and its tools: https://github.com/stryku/cmakesl/tree/master/examples (for more information about the tools, see the tools section in README)
tl;dr list of pros and cons of the language
Pros
- Easy to pick up by people with C/C++ (and similar) background.
- C++ tools like clang-format and Doxygen work out-of-the-box.
- Easy to divide into smaller files.
- Can be mixed with 'old' CMake scripts (at
add_subdirectory()
level), which eases migration process. - Features like classes, namespaces, modularity, type safety and more, which help to work with huge projects.
- It's easy to write tools that provide semantic information: indexer and syntax completion, which help IDE creators.
Cons
- Probably not the easiest way to write simple things. That, actually, depends on what API CMakeSL would eventually have.
- C++ish syntax can be verbose.
Integration with CMake codebase
Here's a high-level diagram of CMakeSL and how it's integrated with CMake codebase.
CMakeSL bases on CMake 3.14 (that was the latest version when I started working on it). You can find forked repo here: https://github.com/stryku/cmake_for_cmakesl. All required changes are on cmakesl
branch. A good starting point to check out is the cmCMakeFacade class. That's the interface between CMakeSL and complex CMake systems.
CMakeSL
CMakeSL is basically a set of libraries that implement a statically typed scripting language. The scripting language is (using some integration code) used by CMake to execute CMakeLists.cmsl
scripts. The libraries are statically linked to the cmake
binary, so the result is a regular cmake
binary with 'old' CMake and CMakeSL support. In the script you can find how to build CMake with CMakeSL support.
It is implemented in C++17 and a little of python3.
Current state of CMakeSL
CMakeSL is advanced enough to build itself (without generating documentation). See CMakeLists.cmsl
files in the repo.
Hello world
int main()
{
cmake::minimum_required(cmake::version(3, 14, 3));
auto hello_world = cmake::project("Hello world");
auto sources = { "main.cpp" };
hello_world.add_executable("hw_exec", sources);
return 0;
}
High-level design
The only thing that CMakeSL is supposed to change in a CMake User's life is how they write CMake scripts. CMakeSL is a scripting language and the scripting language only. All well known CMake's concepts like targets, modules, exporting etc. are supposed to be preserved. You can think of CMakeSL as a thin layer, an API to complex CMake systems.
The only stage of CMake usage that CMakeSL affects is the configuration step. The generation step, all the already supported generators, building and other CMake features remain the same.
C++ish style
You hate it or you love it. I know that a lot of people don't like it and I know that a lot of people complain about imperative paradigm. CMakeSL is highly inspired by C++. It looks and works a lot like C++. It provides well known concepts like types (including builtin generics, e.g. list<T>
), variables, references, functions, classes with members and methods, namespaces and more. Here's why:
- A lot of C/C++ project uses CMake - If user comes from C/C++ or similar background, it'll be very easy to they to pick up the CMakeSL language, read and maintain its code. Additionally, it's easier to switch between C/C++ and CMakeSL files/tasks.
- In C/C++ projects it's almost certain that there is integrated code formatter and other tools that works with C/C++ language. Thanks to the fact that CMakeSL looks almost exactly like subset of C++, all C/C++ tools, that don't require semantic information, work with CMakeSL files as well. An example can be the CMakeSL repository itself. All
*.cmsl
files there are formatted usingclang-format
, with a style got from the.clang-format
file - the very same file that is used to format C++ code in the repository. Another example can be CMakeSL's builtin stuff documentation. All its files are underdoc/builtin
directory. There are files that document all the builtin types and namespaces. The documentation is generated using Doxygen. Doxygen also works with regular CMakeSL files.
The whole CMakeSL machinery seems to be an overkill for such a simple task as writing a couple of scripts that instruct CMake how to build a project. Well, that's partially right. It seems complex and unnecessary if you think about the hello world example or a one-executable project. But, CMakeSL aims to make doing simple tasks simple, that's for sure. Furthermore, it provides features like functions, classes, namespaces, modularity, type safety and more, which make it powerful enough to be clear and elegant in huge projects, with thousands of lines of CMakeSL code.
Mixing CMakeSL with 'old' CMake scripts
CMakeSL scripts can be mixed with 'old' CMake scripts at a directory level. You can NOT mix them in one file or even include()
'old' CMake module in a CMakeSL script.
On the other hand, in a CMakeSL script you CAN call add_subdirectory()
with a CMakeLists.txt
written in 'old' CMake. In the CMakeSL repo you can see an example of such. In the root CMakeLists.cmsl
file, there is an add_subdirectory call that adds external/googletest
subdirectory. Of course the googletest
library doesn't provie CMakeSL scripts. It provides 'old' CMakeLists.txt
. The gtest
and gmock
libraries created there are later on used in tests, in CMakeLists.cmsl
scripts. Such behavior mitigates eventual migration of a given project, form 'old' CMake to CMakeSL. You don't need to migrate the whole project at once. You migrate a directory after directory, which eases the process.
It works the other way as well. In 'old' CMakeLists.txt
you can call add_subdirectory()
with a script implemented in CMakeSL.
Tools
While C/C++ tools that don't require semantic information (e.g. clang-format, Doxygen) work out-of-the-box, tools like clang code completion can not be used just like that. Because of that, CMakeSL brings two tools that provide CMakeSL's code semantic information. Indexer and code completion. They are implemented in cmsl_tools
library. It has C language interface, so it can be used practically everywhere. See the tools section in README for more info.
Tests
Every CMakeSL library has automated tests. They are run on CI. Additionally, Clang's AddressSanitizer
, LeakSanitizer
and UndefinedBehaviorSanitizer
don't complain (they are not integrated on CI yet).
Why not use any of the ready made languages
That's a very good question that I heard a lot. I agree that using an already existing language would be and option, but I think that it'd be an option in the short term. Look, CMake has been here for almost 20 years. It has a great community. It's been used by huge projects and companies. More projects are migrating to CMake, e.g. Qt. In short, it seems that CMake is going to be used for a long, long time. Which is great, of course, but when you're in a perspective of a couple of decades of project maintaining, you want to keep as many parts of the project as possible in your garden. I believe creating and maintaining scripting language will pay-off in the long term. That's why I designed and wrote CMakeSL without a big list of dependencies and requirements. It's relatively easy to embed CMakeSL into the existing CMake codebase.
Work to be done before the eventual merge
- The most basic one is the coding style. CMakeSL has different from the rest of CMake. It would need to be aligned.
- Refactor of the integration code. The code at the
cmake_for_cmakesl/cmakesl
branch. - Review of the CMakeSL code. I've been implementing it basically on my own, without a second pair of reviewing eyes. I believe there is a lot of room for improvement.
- Rewrite Doxygen docs to Sphinx.
- C++17 is required, so CI would need to be updated.
- Test compilation on more architectures and compilers.
Summary
I think CMakeSL, thanks to its features and compatibility with a lot of C/C++ tools, would be a step in the right direction of CMake life.
I'm open for discussion about any aspect of the project and I'm very curious what do you think about all this.
Thanks
I'd like to thank to Bartosz Duszel, Piotr Płaza, Stanisław Kubiak and Wojciech Stróżyński for help with this proposal (: