Skip to content

Commit d8e4664

Browse files
Update Changelog and documentation
1 parent 6e4678d commit d8e4664

File tree

2 files changed

+35
-31
lines changed

2 files changed

+35
-31
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2626
- Allow creating a `QImage` from an `image::RgbaImage`.
2727
- Support for `cfg` attributes through to C++ generation
2828
- CXX-Qt-build: Improved compile time and propagation of initializers between crates
29+
- CXX-Qt-build: Multi-crate projects are now possible with Cargo and CMake (see `examples/qml_multi_crates`)
30+
- CXX-Qt-build: Allow forcing initialization of crates/QML modules (`cxx_qt::init_crate!`/`cxx_qt::init_qml_module!`)
2931

3032
### Fixed
3133

book/src/internals/build-system.md

+33-31
Original file line numberDiff line numberDiff line change
@@ -19,46 +19,17 @@ Qt code often contains initialization code that is called by a static variable t
1919

2020
However, when linking into a static library, and then linking into the main executable, the linker will discard everything from the library that isn't used by the main executable, including these static initializers, as they're never actually used and just exist to run their constructor code.
2121

22-
There are multiple ways to solve this:
23-
24-
- Export an object file and link that to the main binary. Object files are always included completely
25-
- Use the whole-archive linker flag which forces inclusion of every object within the static library.
26-
- If we include the entire static lib generated by cargo, then we'll likely get duplicate symbols, as this really includes **everything** that your Rust code **may** need, even if you don't use it.
27-
- This has caused some recent regressions with Rust 1.78+, where MSVC could no longer link CXX-Qt due to duplicate symbols
28-
- The way to solve this is to only export the static initializers as a library and link that into CMake.
29-
- Manually calling the static initializer code
30-
- This is basically what Q_INIT_RESOURCE and Q_IMPORT_PLUGIN do
31-
- They call the registration method directly, which circumvents the static initializers and forces the static initializers to be linked if they would otherwise be discarded.
32-
33-
At the moment we employ a mix of all methods.
34-
35-
First and foremost, we wrap all our initializers into functions with well-defined names (starting with `cxx_qt_init`) and C-compatible signatures.
36-
This allows us to manually call the initializers from any point in the linker chain, which forces their inclusion.
37-
These initializer functions call the initializer functions from their upstream dependencies so that the entire dependency tree is initialized.
38-
39-
However, we don't want to have to call the initializers manually in every resulting binary.
40-
To solve this, we use static initializers that simply call the initializer function of the crate/Qml module, thereby initializing all dependencies.
41-
As noted earlier, these static initializers are routinely optimized out by the linker.
42-
43-
For Cargo builds we prevent this by linking all initializers with +whole-archive which forces all of them to be included.
44-
Experience has shown that this gives us the best compatibility overall, as linking object files to Cargo builds turned out to be quite finicky.
45-
As the initializers contain very few symbols themselves, this should also rarely lead to issues with duplicate symbols.
46-
47-
In CMake we mirror Qts behavior, which is to build the static initializer as an `OBJECT` library.
48-
The initializer functions themselves are still built into the Rust static library and the `OBJECT` library must therefore link to it.
49-
This is taken care of by the `cxx_qt_import_crate`/`_import_qml_module` functions.
50-
5122
### Header files
5223

5324
We want to make the generated headers available, not just to CMake, but also within dependents in the cargo build chain (e.g. your crate will probably want to depend on the headers produced by cxx-qt-lib).
5425

5526
For this we need to export them to a stable directory so that both CMake and Cargo can find them.
5627

57-
### (Optional) Integration with CMake
28+
# (Optional) Integration with CMake
5829

5930
Somehow, all of this should be compatible with both CMake, and Cargo-only builds.
6031

61-
## The plan (for now)
32+
# The plan (for now)
6233

6334
After many rounds of refactoring this, we believe that we need to be able to share data between build scripts for this to work halfway ergonomically.
6435

@@ -97,6 +68,37 @@ Next to the crates directory, there should be a `qml_modules` directory, which c
9768

9869
Each module should include a `plugin_init.o`, `.qmltypes`, `qmldir`, and any other necessary files.
9970

71+
## Initializers with Cargo and CMake
72+
73+
There are multiple ways to solve the issues presented by static initializers:
74+
75+
- Export an object file and link that to the main binary. Object files are always included completely.
76+
- Use the whole-archive linker flag which forces inclusion of every object within the static library.
77+
- If we include the entire static lib generated by cargo, then we'll likely get duplicate symbols, as this really includes **everything** that your Rust code **may** need, even if you don't use it.
78+
- This has caused some recent regressions with Rust 1.78+, where MSVC could no longer link CXX-Qt due to duplicate symbols
79+
- The way to solve this is to only export the static initializers as a library and link that into CMake.
80+
- Manually calling the static initializer code
81+
- This is basically what Q_INIT_RESOURCE and Q_IMPORT_PLUGIN do
82+
- They call the registration method directly, which circumvents the static initializers and forces the static initializers to be linked if they would otherwise be discarded.
83+
84+
At the moment we employ a mix of all methods.
85+
86+
First and foremost, we wrap all our initializers into functions with well-defined names (starting with `cxx_qt_init`) and C-compatible signatures.
87+
This allows us to manually call the initializers from any point in the linker chain, which forces their inclusion.
88+
These initializer functions call the initializer functions from their upstream dependencies so that the entire dependency tree is initialized.
89+
90+
However, we don't want to have to call the initializers manually in every resulting binary.
91+
To solve this, we use static initializers that simply call the initializer function of the crate/Qml module, thereby initializing all dependencies.
92+
As noted earlier, these static initializers are routinely optimized out by the linker.
93+
94+
For Cargo builds we prevent this by linking all initializers with +whole-archive which forces all of them to be included.
95+
Experience has shown that this gives us the best compatibility overall, as linking object files to Cargo builds turned out to be quite finicky.
96+
As the initializers contain very few symbols themselves, this should also rarely lead to issues with duplicate symbols.
97+
98+
In CMake we mirror Qts behavior, which is to build the static initializer as an `OBJECT` library.
99+
The initializer functions themselves are still built into the Rust static library and the `OBJECT` library must therefore link to it.
100+
This is taken care of by the `cxx_qt_import_crate`/`_import_qml_module` functions.
101+
100102
## Integration with CMake
101103

102104
Via the `CXXQT_EXPORT_DIR` environment variable CMake should be able to change the location of the "target" directory.

0 commit comments

Comments
 (0)