
Study & contribute to Bitcoin and lightning open source

Hands on, guided learning to make you confident in bitcoin development.

List of good first issues from curated bitcoin FOSS projects

Interactive AI chat to learn about Bitcoin technology and its history

Technical Bitcoin search engine

Daily summary of key Bitcoin tech development discussions and updates

Engaging Bitcoin dev intro for coders using technical texts and code challenges

Review technical Bitcoin transcripts and earn sats
Date
22 October, 2025
Speakers
Not Available
Transcript by
Not Available
This presentation discusses CMake fundamentals and its future direction, brought into the context of Bitcoin Core. The speaker notes not to expect too many best practices, as these have been discussed before. Best practices for Bitcoin are not best practices for many other projects.
The goal is to explain what CMake is and what direction it should go in the future, to help understand the full scope of what it can be used for and to show some things that can change, because best practices evolve over time.
The Language:
What's different about this language:
Stability and Portability:
CMake contains five different executables. Each of the tools use a file as an entry point that is written in the CMake language.
Entry Points:
Build Targets:
Common Problem: If the only CMake tool you know is the build system generator, you will end up writing CI scripts in bash that execute:
cmake --build build --target test
If you want to run multiple processes in parallel this is harder.
The problem is that it will lead to a coupling. You will end up setting
CI-specific settings in the CMakeLists.txt file.
The customers in this case are other projects that use that project as a dependency, e.g., secp256k1, and Core in the GUI, etc.
Respect their local preferences. They can set the build type and the generator tool in their dotfiles and environment variables. We should respect those settings.
Recommendation: Use Ninja
Can be you or anyone in a role you currently have.
Shipping software that actually works. Not npm. Is reproducible.
Core has chosen to disable the EXPORT_COMPILE_COMMANDS. This disables code
completion in IDEs, but is better for the maintainer. So the maintainer needs to
find a better workaround.
Potential solution: You can set the tidy property on a per-target basis, which would not break people's setups. There might be other workarounds.
The goal must be: if you have a metabuild that consists of multiple dependencies, for ABI reasons, that everything is good for the same compile flags. But I want to make sure their compiler doesn't do something that causes a bug for the user. I only, Bitcoin Core, care about this project.
But what about libsecp? What if someone wants to use Core as a dependency?
At the moment Core is the top level. So it dictates all the dependencies. But what if something else is at the top level?
But we want to enforce something for security reasons. Do we actually care if someone is packaging Core?
Are we just adding obstacles? But inconveniences of users outside of this group who may not be experts. Who do we care about more?
(See slide)
We define the library without passing it any sources.
Instead we define it without any source files then use the target source files commands to add headers and source files.
We can link dependencies. Where we do this we always use the keyword, public or private.
We install the target. We do not specify any location. Not specify the runtime, archive location, just use the defaults. All we have to say is we want to install the fileset.
We record the information in this export set. And what this will do is create
something that can be imported with find_package. The imported one will have a
namespace target. How this is bound together, we export the targets into the
namespace. We want to make sure the people who do not use find_package, but a
subdirectory, can use it in the same way, whether it is imported or not.
If you build the target on the command line this is the target we have to build. So we add this alias and we need to match the export name and the namespace.
If you link against something that has a namespace, that means CMake checks the target exists. If you type something without a namespace it will just assume this is a library that exists on your system. Always use keywords and make sure the dependencies use namespaces.
All customers will use that alias.
CMake requires that the same signature should be used across all targets. You cannot mix and match. At some point someone should open a pull request and fix this in Core.
All these commands can be used conditionally. You should not define a variable depending on the platform then use it in an unconditional way.
What happens if you have a namespace without a typo (or not) and it links to an incorrect library? This can happen. E.g., experimental secp and Core links against the production one.
target_include_directories use case after CMake 3.22target_link_libraries()Don't set any variable that begins with CMAKE_. Keep defaults. Can be
different if you are the top level project.
Don't set any variable or property that is not strictly necessary.
You want to ship your binaries with the hardening flags.
We also need to help users who compile to do so correctly with hardened flags.
Write CI scripts that are reusable across projects. Define compile flags as toolchains. Make sure that all dependencies are built with consistent flags. Know the direction of future development.
We use target_include_directories before we make the switch to
target_sources, we should limit the use of target_include_directories.
Example: Imagine we have foo and bar, two libraries. Bar depends on foo. Bar
contains this include foo line. foo.c would compile without setting the
include flag, but when we compile bar.c the include directory needs to be set,
otherwise the file cannot be found. In the past we would have used
target_include_directories on bar.
New approach is compatible with target_sources. Work towards this.
Ongoing discussion in libmultiprocess. Minimum supported CMake. What are benefits of supporting old versions of CMake which are not available in distros, e.g., 3.12?
Brown proposed to use latest available, not a version range. This creates additional maintainer burden. Would like to avoid. Even having a range creates a maintenance burden.
Speaker's opinion: There are two different concepts:
For the minimum version, just document. Don't try and be backwards compatible. For policy, you can do many things. Just pick one.
When a warning is actually printed. When code is affected by policy, it will execute the old path and new path, then compare. If different, a warning is printed. If results match, no warning. If old, only old. If new, only new. If unset, see comparison approach just described.
Don't include anything that is strictly necessary โ the warning flags, hardening
flags โ CMakeLists.txt file is not the right place for that. What do we do
with that advice?
We need to agree on what the goals are for people that want to use our components as dependencies.
I want to choose the middle ground. Our current choice seems to be a good default. A middle ground.
Multiprocess doesn't need to have this discussion. We are really focused on warnings and hardening.
For Core, as long as it's a product, it can be kept as it is. Or we move to toolchain files. At some point where Core is more separate, e.g., kernel, then the goal of kernel is to be integrated and used by developers.
Have warning and hardening and still make kernel easy to consume. Is there no good answer?
I don't think we can do both. Hardenings on by default, set in the toolchain file, is this possible, and then provide a preset that provides that toolchain file.
Just write the compile flags into this depends-provided toolchain file.
Question: Can we set something like a default preset? A default default?
If it was only in the depends toolchain, there will be people who miss those flags. We have pushed back against external packaging of Core. By design.
The only supported thing we produce are the Guix builds. Anything else is a best effort. Which uses the generated toolchain file.
But we don't know how people use the source code.
But that's why we set all the hardening flags and try to avoid all the footguns.
Instead of setting 20 as the language standard, because someone else might pull it into their project, that could create ABI issues.
Can we give them this flexibility and use what we want as defaults?
Therefore the best approach is a defensive approach. Let the customer decide. And if they use an incompatible language version, throw an error message.
We want it to be configurable with the defaults we like and are safe.
We don't have a way to turn off the hardening. I don't know if we should.
We can easily update the depends toolchain to include the flags.
There are real problems with hardcoding things, and philosophical problems we can debate about.
Convenience vs security tradeoff. No free lunch.
Any modularization that might happen in the future. Kernel library. That will be moving upstream. And it won't have as much build system as we have. C++ with possibly no dependencies. Minimal build that Core is consuming and adding its own stuff too. All through the stack, this needs to align.
That's why you set configs at highest level. It's a packaging thing. A distribution.
Interesting idea: Top level CMake.
Depends. Now we have a bunch of new repositories. The hardening flags don't exist in the...
If kernel goes to a separate repository, maybe you have the flags in two places. When they are in one place, it's easier to keep tabs on them.
Explicit flag setting. A CMake variable. Is it uncommon? If it's necessary, it's set as a target property. Tied to the target.
Hardening flags don't fall into this category.
Conceptual discussion over where we might set compiler flags like hardening etc.
If Bitcoin Core starts being used as a dependency in different places
(downstream), then we might not want to be setting these in our CMakeLists.txt
(or anywhere other than a toolchain, possibly in a packaging repo).
We have hardening flags and things encapsulated in a single target currently.
Community-maintained archive to unlocking knowledge from technical Bitcoin transcripts




