Avoid having many static libraries by adding the optional sources to the target bitcoin_crypto
directly.
Set the necessary compile options at the source file level, rather than the target level.
fixes: #31697
Avoid having many static libraries by adding the optional sources to the target bitcoin_crypto
directly.
Set the necessary compile options at the source file level, rather than the target level.
fixes: #31697
The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.
For details see: https://corecheck.dev/bitcoin/bitcoin/pulls/31268.
See the guideline for information on the review process.
Type | Reviewers |
---|---|
ACK | hebasto, s373nZ, TheCharlatan |
Concept ACK | sipa, theuni |
Approach ACK | BrandonOdiwuor |
If your review is incorrectly listed, please react with 👎 to this comment and the bot will ignore it on the next update.
Reviewers, this pull request conflicts with the following ones:
libbitcoinkernel
dependencies explicit by hebasto)If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first.
72 if(HAVE_ARM_SHANI)
73- add_library(bitcoin_crypto_arm_shani STATIC EXCLUDE_FROM_ALL
74- sha256_arm_shani.cpp
75+ target_compile_definitions(bitcoin_crypto PRIVATE ENABLE_ARM_SHANI)
76+ target_sources(bitcoin_crypto PRIVATE sha256_arm_shani.cpp)
77+ set_property(SOURCE sha256_x86_shani.cpp PROPERTY
x86
should be arm
?
🚧 At least one of the CI tasks failed. Debug: https://github.com/bitcoin/bitcoin/runs/32802162894
Try to run the tests locally, according to the documentation. However, a CI failure may still happen due to a number of reasons, for example:
Possibly due to a silent merge conflict (the changes in this pull request being incompatible with the current code in the target branch). If so, make sure to rebase on the latest commit of the target branch.
A sanitizer issue, which can only be found by compiling with the sanitizer and running the affected test.
An intermittent issue.
Leave a comment here, if you need help tracking down a confusing failure.
Avoid having many static libraries
Can you expand the motivation.
There are four alternative implementations for sha256
, each in its own source file which has to be compiled with dedicated compiler options. The current approach is to build a static library per source file, set the necessary compiler options for that library, and eventually link bitcoin_crypto
against those libraries. This is needless complexity.
My proposed approach is to compile the source file with its dedicated options, but link the object files into bitcoin_crypto
directly.
Approach ACK. Great catch!
Nice simplification! Adding the optional source files directly to bitcoin_crypto
eliminating the need for intermediate libraries like bitcoin_crypto_sse41, bitcoin_crypto_avx2, and bitcoin_crypto_x86_shani
Avoid having many static libraries by adding the optional sources to the target
bitcoin_crypto
directly. Set the necessary compile options at the source file level, rather than the target level.
While working on the CMake staging branch, this was done on purpose (see https://github.com/hebasto/bitcoin/pull/172 and similar https://github.com/hebasto/bitcoin/pull/171). However, the goal PR has since been replaced.
Additionally, from the Professional CMake: A Practical Guide 19th Edition:
Developers should be aware of an implementation detail which may present a strong deterrent to their use in some situations. For some CMake generators (notably the Unix Makefiles generator), the dependencies between sources and source properties are stronger than one might expect. If source properties are used to modify the compiler flags for specific source files rather than for a whole target, changing the source’s compiler flags will still result in all of the target’s sources being rebuilt, not just the affected source file. This is a limitation of how the dependency details are handled in the Makefile, where testing whether each source file’s compiler flags have changed brings with it a prohibitively big performance hit. The relevant Makefile dependencies were implemented at the target level instead to avoid that problem.
If source properties are used to modify the compiler flags for specific source files rather than for a whole target, changing the source’s compiler flags will still result in all of the target’s sources being rebuilt, not just the affected source file.
I don’t think that applies here. The source’s compile flags are never changed between builds: sha256_avx2.cpp
is either compiled with ${AVX2_CXXFLAGS}
, or it is not compiled at all. And AVX2_CXXFLAGS
is only ever set to -mavx -mavx2
. The issue that all of the libraries source files get recompiled never occurs.
@hebasto, can you elaborate why you think the source-specific compile options are hard to reason about?
@hebasto, can you elaborate why you think the source-specific compile options are hard to reason about?
Sure. Consider this minimal example:
0cmake_minimum_required(VERSION 3.22)
1project(test CXX)
2
3add_library(interface INTERFACE)
4target_compile_options(interface INTERFACE -Wno-mock-interface)
5
6add_executable(test main.cpp)
7set_property(SOURCE main.cpp PROPERTY COMPILE_OPTIONS -Wno-mock-source)
8target_compile_options(test PRIVATE -Wno-mock-target)
9target_link_libraries(test PRIVATE interface)
It is not straightforward to determine the actual order of compiler flags passed to the compiler when the COMPILE_OPTIONS
target property is used.
0-Wno-mock-target -Wno-mock-interface -Wno-mock-source
Why is that hard to reason about? If I add
0add_compile_options(-Wno-mock-global)
the resulting order will be -Wno-mock-global -Wno-mock-target -Wno-mock-interface -Wno-mock-source
, so the flags are ordered from most generic to most specific.
That aside, I don’t think the order of arguments is relevant here.
Why is that hard to reason about?
People unfamiliar with such peculiarities might not realise that compiler options specified as a source’s property take precedence over other compiler options.
I also find the reasons against doing this do be pretty uncompelling.
I consider the current approach to be a leftover autootools artifact. This is a nice cleanup and simplification, and the (stated) downsides seem pretty minimal. It looks like the crc32c lib would benefit from this approach as well.
Unless there’s some actual use case or generator that this breaks, +1 from me.
7@@ -8,7 +8,7 @@ declare -A LIBS
8 LIBS[cli]="libbitcoin_cli.a"
9 LIBS[common]="libbitcoin_common.a"
10 LIBS[consensus]="libbitcoin_consensus.a"
11-LIBS[crypto]="crypto/libbitcoin_crypto.a crypto/libbitcoin_crypto_x86_shani.a crypto/libbitcoin_crypto_sse41.a crypto/libbitcoin_crypto_avx2.a"
12+LIBS[crypto]="crypto/libbitcoin_crypto.a"
I consider the current approach to be a leftover autootools artifact.
I agree. Concept ACK.
It looks like the crc32c lib would benefit from this approach as well.
Could we extend this PR to include the crc32c
library as well?
Could we extend this PR to include the crc32c library as well?
Since crc32c is a subtree, I have submitted a cleanup upstream: https://github.com/google/crc32c/pull/66
Could we extend this PR to include the crc32c library as well?
Since crc32c is a subtree, I have submitted a cleanup upstream: google/crc32c#66
FYI we don’t use the upstream CMake buildsystem, rather we handle the sources ourselves. See cmake/crc32c.cmake
.
82-target_compile_definitions(crc32c_common INTERFACE
83+add_library(crc32c STATIC EXCLUDE_FROM_ALL
84+ ${PROJECT_SOURCE_DIR}/src/crc32c/src/crc32c.cc
85+ ${PROJECT_SOURCE_DIR}/src/crc32c/src/crc32c_portable.cc
86+)
87+target_compile_definitions(crc32c PRIVATE
PRIVATE
keyword on its own line with proper indentation, as in all surrounding code?
127- )
128- target_compile_options(crc32c_arm64 PRIVATE ${ARM64_CRC_CXXFLAGS})
129- target_link_libraries(crc32c_arm64 PRIVATE crc32c_common)
130- set_target_properties(crc32c_arm64 PROPERTIES EXPORT_COMPILE_COMMANDS OFF)
131- target_link_libraries(crc32c PRIVATE crc32c_arm64)
132+ set(srcs ${PROJECT_SOURCE_DIR}/src/crc32c/src/crc32c_arm64.cc)
srcs
variable at the end to avoid polluting the global namespace?
_crc32_src
.
ACK 7a53ce742207c5dfaeda7858a098049627b03d55, tested on Ubuntu 24.04, both x86_64 and aarch64.
These changes effectively revert https://github.com/hebasto/bitcoin/pull/171 and https://github.com/hebasto/bitcoin/pull/172.
Could you please mention the changes to cmake/crc32c.cmake
in the PR title and description, and separate them into their own commit?
Additionally, it seems reasonable to mention this in the PR description.
I think that APPEND
ing to the COMPILE_OPTIONS
property should be preferred over overriding it.
fixes: #31268
I think that APPENDing to the COMPILE_OPTIONS property should be preferred over overriding it.
The command line flags that are passed to the compiler are constructed by several variables and properties that are global, target specific, and file specific. Since this is the only file specific option, APPENDing has no different effect as setting.
ACK 9cf746d6631739df9c9f80accd5812b319efcfec
Tested compilation on Ubuntu 24.04 x86_64. Learned a lot looking over this PR. Nice work!
Agree with @hebasto that it’d be more straightforward to use
target_compile_options()
, if only for the sake of avoiding future footguns.
Very generally in software development, future footguns are avoided by narrow contracts.
I understand the desire of narrowing the contract between the authors and CMake. One may want to avoid advanced features like setting properties on individual source files. Applying this approach consistently, one should also avoid commands like target_compile_options()
and just call add_compile_definitions()
, add_compile_options()
, and include_directories()
once in the top-level CMakeLists.txt
. Keep it simple, stupid. The generated build system however, will have a wide contract: All targets are affected by all compile flags.
The approach that I am advocating on the other hand, is to use advanced cmake features in order to strive for a narrow contract in the generated build system. I would define an object library if it has to be consumed by multiple other targets. If it is consumed by a single target only, adding the source file to the consuming target directly narrows the contract and therefore better avoids possible footguns.
purpleKarrot
DrahtBot
willcl-ark
fanquake
sipa
BrandonOdiwuor
hebasto
theuni
s373nZ
TheCharlatan
Labels
Build system
Milestone
29.0