Skip to content

Commit 02e5be6

Browse files
Allow cxx-qt-build to deal with build scripts running in parallel (#999)
* refactor: Rename CXXQT_* variables to CXX_QT_* This is more in line with our crate naming (cxx-qt, not cxxqt) and is more consistent, as before we were using both. Fix #996 * refactor: Rename cxxqt_ CMake functions to cxx_qt_ This is more consistent with all our other naming. * fix: build: Use export_dir on final crate only Cargo can sometimes build different configurations of the same crate in parallel. This caused some failures with our new build system. However, we can solve this by reverting to the OUT_DIR again. Using the OUT_DIR is the only supported way to write artifacts anyway. However, we still need to export data for CMake. Therefore, we define an additional flag for each exact crate that CMake is trying to import. Only this exact crate will then export its data. As CMake should only build a single configuration at a time, this should not conflict. * book: Document user-facing changes to CMake build * cmake: Resolve ${CRATE} and ${APP_NAME} variables At least do so in `qml_minimal`. I got the feedback from Milian that this is rather hard to read otherwise, which I agree with. Especially as that's the code we include in our getting-started guide it should be as simple as possible. * book: Update internals documentation This is now slightly different, as we're no longer using a shared export directory
1 parent c7922e4 commit 02e5be6

File tree

19 files changed

+115
-104
lines changed

19 files changed

+115
-104
lines changed

book/src/concepts/build_systems.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,9 @@ CXX-Qt can be integrated into existing CMake projects or built with only cargo.
1212
- [Cargo Integration](../getting-started/4-cargo-executable.md)
1313
- [CMake Integration](../getting-started/5-cmake-integration.md)
1414

15-
CXX-Qt could work with any C++ build system so long as the `QMAKE` and `CXXQT_EXPORT_DIR` environment variables are set before calling Cargo,
16-
as documented in [CMake integration](../getting-started/5-cmake-integration.md). However, using C++ build systems besides CMake with CXX-Qt is untested.
15+
CXX-Qt could work with any C++ build system so long as the `QMAKE`, `CXX_QT_EXPORT_DIR` and `CXX_QT_EXPORT_CRATE_<CRATE-NAME>` environment variables are set before calling Cargo.
16+
Take a look at our CMake code for how this can be used.
17+
However, using C++ build systems besides Cargo or CMake with CXX-Qt is untested and the use of these environment variables is SemVer-exempt!
1718

1819
## `CxxQtBuilder`
1920

book/src/getting-started/2-our-first-cxx-qt-module.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ SPDX-License-Identifier: MIT OR Apache-2.0
1010
As with all things Rust, we'll want to create a cargo project, run the following command inside the `tutorial` folder to initialize the Rust part of the project.
1111

1212
```shell
13-
$ cargo new cxx-qt-tutorial
14-
$ cd cxx-qt-tutorial
13+
$ cargo new qml_minimal
14+
$ cd qml_minimal
1515
```
1616

1717
> If you want to skip building with Cargo and try building with CMake directly

book/src/getting-started/4-cargo-executable.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ Add the dependencies to the `Cargo.toml` file.
2727
We'll need `cxx`, `cxx-qt`, `cxx-qt-lib` and `cxx-qt-build`:
2828

2929
```toml,ignore
30+
{{#include ../../../examples/qml_minimal/rust/Cargo.toml:book_package_name}}
3031
{{#include ../../../examples/cargo_without_cmake/Cargo.toml:book_cargo_toml_no_cmake}}
3132
cxx = "1.0.95"
3233
cxx-qt = "0.6"

book/src/getting-started/5-cmake-integration.md

Lines changed: 26 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -37,20 +37,20 @@ You can add as much C++ code as you want in addition to this.
3737
## Using Rust QObjects in C++
3838

3939
For every `#[cxx_qt::bridge]` that we define in Rust, CXX-Qt will generate a corresponding C++ header file.
40-
They will always be in the `cxx-qt-gen/` include path and use the snake_case naming convention.
40+
To include any of the generated files, use the crates name as the include directory.
4141
The name of the header file will be the name of the Rust module of your `#[cxx_qt::bridge]`, followed by `.cxxqt.h`.
42-
So in our case: `#include cxx-qt-gen/qobject.cxxqt.h`
42+
So in our case: `#include <qml_minimal/qobject.cxxqt.h>`
4343

4444
> Note that the [`cxx_file_stem`](../bridge/index.md#cxx_file_stem) option can be specified in the bridge macro to choose the file name.
4545
46-
Including the generated header allows accessing the `MyObject` C++ class, just like any other C++ class.
46+
Including the generated header allows us to access the `MyObject` C++ class, just like any other C++ class.
4747
Inherit from it, connect signals and slots to it, put it in a QVector, do whatever you want with it.
4848
That's the power of CXX-Qt.
4949

5050
## Cargo setup
5151

5252
Before we can get started on building Qt with CMake, we first need to make our Cargo build ready for it.
53-
If you've generated your project with the `cargo new --lib` or `cargo init --lib [folder]` command, your `Cargo.toml` should look something like this:
53+
If you've generated your project with e.g. `cargo new --lib qml_minimal` or `cargo init --lib [folder]` command, your `Cargo.toml` should look something like this:
5454

5555
```toml,ignore
5656
[package]
@@ -107,7 +107,9 @@ The files and resources in the module are then exposed in the same way as the [q
107107

108108
> Note that in order for CXX-Qt to work, the `qmake` executable must be located. This is because CXX-Qt relies on `qmake` to locate the necessary Qt libraries and header files on your system.
109109
>
110-
> This will be done in the `CMakeLists.txt` file by setting the `QMAKE` environment variable from CMake, ensuring that CMake and Cargo use the same Qt binaries.
110+
> Usually, the CMake code that CXX-Qt provides you to import a crate should already take care of this.
111+
>
112+
> To overwrite the path to qmake, you may pass the `QMAKE` option to cxx_qt_import_crate, ensuring that CMake and Cargo use the same Qt binaries.
111113
112114
We'll also need to remove the `src/main.rs` and replace it with a `src/lib.rs` file.
113115
This file only needs to include a single line:
@@ -130,27 +132,34 @@ For this example, we are [supporting both Qt5 and Qt6 with CMake](https://doc.qt
130132
{{#include ../../../examples/qml_minimal/CMakeLists.txt:book_cmake_setup}}
131133
```
132134

133-
Locate [Corrosion](https://github.com/corrosion-rs/corrosion), a tool for integrating Rust libraries into CMake.
134-
If Corrosion is not installed, automatically download it:
135+
Download CXX-Qts CMake code with FetchContent:
135136

136137
```cmake,ignore
137-
{{#include ../../../examples/qml_minimal/CMakeLists.txt:book_cmake_find_corrosion}}
138+
{{#include ../../../examples/qml_minimal/CMakeLists.txt:book_cmake_find_cxx_qt}}
138139
```
139140

140-
To ensure that cxx-qt-build uses the same version of Qt as your CMake targets, use the `Qt` CMake target to locate the qmake executable. Then, pass `qmake` executable path to `build.rs` with the environment variable `QMAKE` using `corrosion_set_env_vars`.
141+
This provides you with a few wrappers around [Corrosion](https://github.com/corrosion-rs/corrosion), a tool for integrating Rust libraries into CMake:
142+
143+
1. `cxx_qt_import_crate` - A wrapper around [corrosion_import_crate](https://corrosion-rs.github.io/corrosion/usage.html). It supports the same arguments as corrosion_import_crate, with two new optional arguments:
144+
- `CXX_QT_EXPORT_DIR` - Manually specify the path where CXX-Qt artifacts will be exported to.
145+
- This is usually not necessary. However, if you're importing the same crate with different feature sets in the same CMake build configuration, you will need to specify seperate `CXX_QT_EXPORT_DIR`s to avoid multiple versions of the crate exporting to the same directory.
146+
- `QMAKE` - Override the path to the QMAKE executable
147+
2. `cxx_qt_import_qml_module` - This function imports a QML modules as a new target. It requires the following arguments:
148+
- TARGET_NAME - Specify the name of the CMake target that this function will create
149+
- `URI` - The URI of the qml module to import - this needs to exactly match the URI in the `CxxQtBuilder::qml_module` call in your build script.
150+
- `SOURCE_CRATE` The crate that exports the QML module (this crate must have been imported with `cxx_qt_import_crate`).
141151

142152
```cmake,ignore
143-
{{#include ../../../examples/qml_minimal/CMakeLists.txt:book_cmake_find_qmake}}
153+
{{#include ../../../examples/qml_minimal/CMakeLists.txt:book_cmake_use_cxx_qt}}
144154
```
145155

146-
Use Corrosion to create a CMake library target for the Rust library. CXX-Qt requires a few more steps beyond using
147-
a typical Rust library with Corrosion:
156+
This will create two new CMake targets:
148157

149-
```cmake,ignore
150-
{{#include ../../../examples/qml_minimal/CMakeLists.txt:book_cmake_use_corrosion}}
151-
```
158+
1. `qml_minimal` - The static library exported by our crate
159+
2. `qml_minimal_qml_module` - The QML Module exported by our crate
160+
- The `_qml_module` target will automatically link to the `qml_minimal` target, so linking to the `_qml_module` is sufficient for our executable target
152161

153-
Finally, create the CMake executable target and link it to the Rust library:
162+
Finally, we can create the CMake executable target and link it to our crate:
154163

155164
```cmake,ignore
156165
{{#include ../../../examples/qml_minimal/CMakeLists.txt:book_cmake_executable}}
@@ -189,7 +198,7 @@ This should now configure and compile our project.
189198
If this was successful, you can now run our little project.
190199

191200
```shell
192-
$ build/examples/qml_minimal/example_qml_minimal
201+
$ ./build/examples/qml_minimal/example_qml_minimal
193202
```
194203

195204
You should now see the two Labels that display the state of our `MyObject`, as well as the two buttons to call our two Rust functions.

book/src/internals/build-system.md

Lines changed: 14 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -39,38 +39,33 @@ Somehow, all of this should be compatible with both CMake, and Cargo-only builds
3939

4040
## The plan (for now)
4141

42-
After many rounds of refactoring this, we believe the best way to go is to not rely on cargos OUT_DIR exclusivly.
43-
Not being able to share artifacts between crates is very limiting.
42+
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.
4443

45-
We want to use a similar approach to CXX, which stores its data within either:
44+
We want to use a similar approach to CXX, which uses Cargos `links` key to ensure a correct build order (see the documentation [here](https://doc.rust-lang.org/cargo/reference/build-scripts.html#the-links-manifest-key)).
45+
When building with cxx-qt-build, you may simply specify that your code depends on another crate.
46+
Cargo will then make sure that the build scripts of the dependencies have run **before** the build script of this crate.
4647

47-
- Cargos `target/` directory under the `cxxbridge` subfolder.
48-
- A custom `scratch` directory
48+
We can additionally pass metadata between build scripts, which we use to find the `manifest.json` of each crate and the path to their "target" directory.
4949

50-
See: [https://github.com/dtolnay/cxx/blob/afd4aa3f3d4e5d5e9a3a41d09df3408f5f86a469/gen/build/src/target.rs#L10](https://github.com/dtolnay/cxx/blob/afd4aa3f3d4e5d5e9a3a41d09df3408f5f86a469/gen/build/src/target.rs#L10) \
51-
and: [https://github.com/dtolnay/cxx/blob/afd4aa3f3d4e5d5e9a3a41d09df3408f5f86a469/gen/build/src/lib.rs#L178](https://github.com/dtolnay/cxx/blob/afd4aa3f3d4e5d5e9a3a41d09df3408f5f86a469/gen/build/src/lib.rs#L178).
50+
## The "target" directory
5251

53-
We will likely mirror this and use the `cxxqtbridge` naming.
52+
Each build script can export artifacts into a folder with a well-known layout.
53+
It is also required to export a `manifest.json` file that tells downstream dependencies which of these artifacts to include and how to configure their own build.
5454

55-
## Contents of `cxxqtbridge`
55+
This "target" directory is usually in the OUT_DIR, but can be exported using `CXX_QT_EXPORT_DIR` and `CXX_QT_EXPORT_CRATE_[crate-name]` environment variables.
56+
Which is used by CMake to import the artifacts. (See: [Integration with CMake](#integration-with-cmake))
5657

5758
### `crates` directory
5859

59-
Inside cxxqtbridge, there should be a `crates` folder with one subfolder per crate.
60+
Inside the target directory, there should be a `crates` folder with one subfolder per crate.
6061
Each crates subfolder should contain the following:
6162

6263
- `include/`
6364
- `crate-name` - A folder for all headers that are exported by this crate
64-
- `cxx-qt-lib -> ../../cxx-qt-lib/include` - Symbolic links for every dependency
65+
- `cxx-qt-lib -> <path-to-dependency>/include/cxx-qt-lib` - Symbolic links for every dependency
6566
- `manifest.json` - This file describes which headers this library makes available, if it needs any Qt modules, etc.
6667
- `initializers.o` - The initializers of this crate + all it's dependencies to be linked in by CMake
6768

68-
When building with cxx-qt-build, you may simply specify that your code depends on another crate.
69-
However, we also need to make sure that the order in which the build scripts are run works out, as e.g. the build script of cxx-qt-lib needs to run **before** any dependents build scripts run.
70-
71-
For this use-case, Cargo has the `links` key (see the documentation [here](https://doc.rust-lang.org/cargo/reference/build-scripts.html#the-links-manifest-key)).
72-
It allows us to ensure the correct order, and also to pass metadata between build scripts.
73-
We specifically use this to let downstream dependents know where to find our manifest.json.
7469
Via the `manifest.json`, we are then able to figure out which header paths of this dependency to include, which Qt modules to link, etc.
7570

7671
To make sure the correct data ends up in the manifest.json, we provide the `cxx_qt_build::Interface` struct which uses the builder pattern to specify all the necessary data.
@@ -83,14 +78,14 @@ Each module should include a `plugin_init.o`, `.qmltypes`, `qmldir`, and any oth
8378

8479
## Integration with CMake
8580

86-
Via the `CXXQT_EXPORT_DIR` environment variable CMake should be able to change the location of the `cxxqtbridge` directory.
81+
Via the `CXXQT_EXPORT_DIR` environment variable CMake should be able to change the location of the "target" directory.
8782
CMake can then expect required artifacts to exist at pre-defined locations, which can be added as dependency, include directories, objects, etc. to the Crate target.
8883

8984
We will rely on Corrosion to import the crate and provide targets for it.
9085

9186
However, we also want to provide some custom functions that wrap corrosion and set up the import of our own artifacts.
9287

93-
Currently we plan to provide two functions:
88+
Currently we provide two functions:
9489

9590
- cxxqt_import_crate
9691
- A wrapper over corrosion_import_crate that defines the `CXXQT_EXPORT_DIR`, imports the initializers object files, etc.

cmake/CxxQt.cmake

Lines changed: 24 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -16,57 +16,60 @@ if(NOT Corrosion_FOUND)
1616
FetchContent_MakeAvailable(Corrosion)
1717
endif()
1818

19-
function(cxxqt_import_crate)
20-
cmake_parse_arguments(IMPORT_CRATE "" "CXXQT_EXPORT_DIR;QMAKE" "" ${ARGN})
19+
function(cxx_qt_import_crate)
20+
cmake_parse_arguments(IMPORT_CRATE "" "CXX_QT_EXPORT_DIR;QMAKE" "" ${ARGN})
2121

22-
corrosion_import_crate(IMPORTED_CRATES __cxxqt_imported_crates ${IMPORT_CRATE_UNPARSED_ARGUMENTS})
22+
corrosion_import_crate(IMPORTED_CRATES __cxx_qt_imported_crates ${IMPORT_CRATE_UNPARSED_ARGUMENTS})
2323

24-
message(STATUS "CXX-Qt Found crate(s): ${__cxxqt_imported_crates}")
24+
message(STATUS "CXX-Qt Found crate(s): ${__cxx_qt_imported_crates}")
2525

26-
if (NOT DEFINED IMPORT_CRATE_CXXQT_EXPORT_DIR)
27-
set(IMPORT_CRATE_CXXQT_EXPORT_DIR "${CMAKE_CURRENT_BINARY_DIR}/cxxqt/")
26+
if (NOT DEFINED IMPORT_CRATE_CXX_QT_EXPORT_DIR)
27+
set(IMPORT_CRATE_CXX_QT_EXPORT_DIR "${CMAKE_CURRENT_BINARY_DIR}/cxxqt/")
2828
endif()
29-
message(VERBOSE "CXX-Qt EXPORT_DIR: ${IMPORT_CRATE_CXXQT_EXPORT_DIR}")
29+
message(VERBOSE "CXX-Qt EXPORT_DIR: ${IMPORT_CRATE_CXX_QT_EXPORT_DIR}")
3030

3131
if (NOT DEFINED IMPORT_CRATE_QMAKE)
3232
get_target_property(QMAKE Qt::qmake IMPORTED_LOCATION)
3333
if (NOT QMAKE STREQUAL "QMAKE-NOTFOUND")
3434
set(IMPORT_CRATE_QMAKE "${QMAKE}")
3535
else()
36-
message(FATAL_ERROR "cxxqt_import_crate: QMAKE is not defined and could not be queried from the Qt::qmake target!\nPlease use the QMAKE argument to specify the path to the qmake executable or use find_package(Qt) before calling this function.")
36+
message(FATAL_ERROR "cxx_qt_import_crate: QMAKE is not defined and could not be queried from the Qt::qmake target!\nPlease use the QMAKE argument to specify the path to the qmake executable or use find_package(Qt) before calling cxx_qt_import_crate.")
3737
endif()
3838
endif()
3939

40-
foreach(CRATE ${__cxxqt_imported_crates})
40+
foreach(CRATE ${__cxx_qt_imported_crates})
4141
corrosion_set_env_vars(${CRATE}
42-
"CXXQT_EXPORT_DIR=${IMPORT_CRATE_CXXQT_EXPORT_DIR}"
42+
# Tell cxx-qt-build where to export the data
43+
"CXX_QT_EXPORT_DIR=${IMPORT_CRATE_CXX_QT_EXPORT_DIR}"
44+
# Tell cxx-qt-build which crate to export
45+
"CXX_QT_EXPORT_CRATE_${CRATE}=1"
4346
"QMAKE=${IMPORT_CRATE_QMAKE}"
4447
$<$<BOOL:${CMAKE_RUSTC_WRAPPER}>:RUSTC_WRAPPER=${CMAKE_RUSTC_WRAPPER}>)
4548

46-
file(MAKE_DIRECTORY "${IMPORT_CRATE_CXXQT_EXPORT_DIR}/crates/${CRATE}/include/")
47-
target_include_directories(${CRATE} INTERFACE "${IMPORT_CRATE_CXXQT_EXPORT_DIR}/crates/${CRATE}/include/")
49+
file(MAKE_DIRECTORY "${IMPORT_CRATE_CXX_QT_EXPORT_DIR}/crates/${CRATE}/include/")
50+
target_include_directories(${CRATE} INTERFACE "${IMPORT_CRATE_CXX_QT_EXPORT_DIR}/crates/${CRATE}/include/")
4851

4952
set_target_properties(${CRATE}
5053
PROPERTIES
51-
CXXQT_EXPORT_DIR "${IMPORT_CRATE_CXXQT_EXPORT_DIR}")
54+
CXX_QT_EXPORT_DIR "${IMPORT_CRATE_CXX_QT_EXPORT_DIR}")
5255

5356
# cxx-qt-build generates object files that need to be linked to the final target.
5457
# These are the static initializers that would be removed as an optimization if they're not referenced.
5558
# So add them to an object library instead.
56-
file(MAKE_DIRECTORY "${IMPORT_CRATE_CXXQT_EXPORT_DIR}/crates/${CRATE}/")
59+
file(MAKE_DIRECTORY "${IMPORT_CRATE_CXX_QT_EXPORT_DIR}/crates/${CRATE}/")
5760
# When using the Ninja generator, we need to provide **some** way to generate the object file
5861
# Unfortunately I'm not able to tell corrosion that this obj file is indeed a byproduct, so
5962
# create a fake target for it.
6063
# This target doesn't need to do anything, because the file should already exist after building the crate.
6164
add_custom_target(${CRATE}_mock_initializers
6265
COMMAND ${CMAKE_COMMAND} -E true
6366
DEPENDS ${CRATE}
64-
BYPRODUCTS "${IMPORT_CRATE_CXXQT_EXPORT_DIR}/crates/${CRATE}/initializers.o")
67+
BYPRODUCTS "${IMPORT_CRATE_CXX_QT_EXPORT_DIR}/crates/${CRATE}/initializers.o")
6568

6669
add_library(${CRATE}_initializers OBJECT IMPORTED)
6770
set_target_properties(${CRATE}_initializers
6871
PROPERTIES
69-
IMPORTED_OBJECTS "${IMPORT_CRATE_CXXQT_EXPORT_DIR}/crates/${CRATE}/initializers.o")
72+
IMPORTED_OBJECTS "${IMPORT_CRATE_CXX_QT_EXPORT_DIR}/crates/${CRATE}/initializers.o")
7073
# Note that we need to link using TARGET_OBJECTS, so that the object files are included **transitively**, otherwise
7174
# Only the linker flags from the object library would be included, but not the actual object files.
7275
# See also the "Linking Object Libraries" and "Linking Object Libraries via $<TARGET_OBJECTS>" sections:
@@ -77,22 +80,22 @@ function(cxxqt_import_crate)
7780
endfunction()
7881

7982

80-
function(cxxqt_import_qml_module target)
83+
function(cxx_qt_import_qml_module target)
8184
cmake_parse_arguments(QML_MODULE "" "URI;SOURCE_CRATE" "" ${ARGN})
8285

8386
if (NOT DEFINED QML_MODULE_URI)
84-
message(FATAL_ERROR "cxxqt_import_qml_module: URI must be specified!")
87+
message(FATAL_ERROR "cxx_qt_import_qml_module: URI must be specified!")
8588
endif()
8689

8790
if (NOT DEFINED QML_MODULE_SOURCE_CRATE)
88-
message(FATAL_ERROR "cxxqt_import_qml_module: SOURCE_CRATE must be specified!")
91+
message(FATAL_ERROR "cxx_qt_import_qml_module: SOURCE_CRATE must be specified!")
8992
endif()
9093

91-
get_target_property(QML_MODULE_EXPORT_DIR ${QML_MODULE_SOURCE_CRATE} CXXQT_EXPORT_DIR)
94+
get_target_property(QML_MODULE_EXPORT_DIR ${QML_MODULE_SOURCE_CRATE} CXX_QT_EXPORT_DIR)
9295
get_target_property(QML_MODULE_CRATE_TYPE ${QML_MODULE_SOURCE_CRATE} TYPE)
9396

9497
if (${QML_MODULE_EXPORT_DIR} STREQUAL "QML_MODULE_EXPORT_DIR-NOTFOUND")
95-
message(FATAL_ERROR "cxxqt_import_qml_module: SOURCE_CRATE must be a valid target that has been imported with cxxqt_import_crate!")
98+
message(FATAL_ERROR "cxx_qt_import_qml_module: SOURCE_CRATE must be a valid target that has been imported with cxx_qt_import_crate!")
9699
endif()
97100

98101
# Note: This needs to match the URI conversion in cxx-qt-build

crates/cxx-qt-build/Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ quote.workspace = true
2222
qt-build-utils.workspace = true
2323
codespan-reporting = "0.11"
2424
version_check = "0.9"
25-
scratch = "1.0"
2625
serde = { version = "1.0", features = ["default", "derive"] }
2726
serde_json = "1.0"
2827

0 commit comments

Comments
 (0)