Skip to content

Commit

Permalink
cxx-qt-lib: use cxx_build instead of CMake for C++
Browse files Browse the repository at this point in the history
This is a step towards obviating the need for CMake to build
autogenerated code, which will simplify the build process by moving
the invocation of Cargo to CMake build time rather than CMake configure
time.
  • Loading branch information
Be-ing committed Jul 25, 2022
1 parent 4b79129 commit 50628a6
Show file tree
Hide file tree
Showing 5 changed files with 59 additions and 220 deletions.
11 changes: 9 additions & 2 deletions cmake/CxxQt.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,10 @@ function(cxx_qt_generate_cpp GEN_SOURCES)

# Run cargo during config to ensure the cpp source file list is created
execute_process(
COMMAND ${CARGO_CMD}
COMMAND cmake -E env
"CARGO_TARGET_DIR=${CMAKE_CURRENT_SOURCE_DIR}/target"
"QT_VERSION_MAJOR=${QT_VERSION_MAJOR}"
${CARGO_CMD}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
)

Expand All @@ -62,6 +65,7 @@ function(cxx_qt_include APP_NAME)
target_include_directories(${APP_NAME} PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include")
# Include the target folder so that cxx-qt-gen and cxx-qt-lib can be included
target_include_directories(${APP_NAME} PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/target")
target_include_directories(${APP_NAME} PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/target/cxxbridge")
# Our cxx_qt and cxx headers are in this folder and need to be included
target_include_directories(${APP_NAME} PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/target/cxx-qt-gen/statics")
endfunction()
Expand Down Expand Up @@ -102,7 +106,10 @@ function(cxx_qt_link_rustlib APP_NAME)
endif()
add_custom_target(
"${APP_NAME}_rustlib"
COMMAND ${CARGO_CMD}
COMMAND cmake -E env
"CARGO_TARGET_DIR=${CMAKE_CURRENT_SOURCE_DIR}/target"
"QT_VERSION_MAJOR=${QT_VERSION_MAJOR}"
${CARGO_CMD}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
)
add_dependencies(${APP_NAME} "${APP_NAME}_rustlib")
Expand Down
63 changes: 1 addition & 62 deletions cxx-qt-build/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -297,61 +297,6 @@ fn write_cpp_sources_list(paths: &[PathBuf]) {
}
}

/// Write our a given cxx-qt-lib header and source set to the given folder
fn write_cxx_qt_lib_set(
file_name: &str,
target_dir: &str,
header: &str,
source: &str,
) -> Vec<PathBuf> {
let mut paths = vec![];
let path_h = PathBuf::from(format!("{}/include/{}.h", target_dir, file_name));
let path_cpp = PathBuf::from(format!("{}/src/{}.cpp", target_dir, file_name));

let mut file = std::fs::File::create(&path_h).expect("Could not create header file");
file.write_all(header.as_bytes())
.expect("Could not write header file");
paths.push(path_h);

let mut file = std::fs::File::create(&path_cpp).expect("Could not create source file");
file.write_all(source.as_bytes())
.expect("Could not write source file");
paths.push(path_cpp);

paths
}

/// Find all the cxx-qt-lib sources and write them to the target directory
fn write_cxx_qt_lib_sources() -> Vec<PathBuf> {
let cxx_qt_lib_target_dir = format!("{}/target/cxx-qt-lib", manifest_dir());
let cxx_qt_lib_include_dir = format!("{}/include", cxx_qt_lib_target_dir);
let cxx_qt_lib_src_dir = format!("{}/src", cxx_qt_lib_target_dir);
std::fs::create_dir_all(&cxx_qt_lib_include_dir).unwrap();
std::fs::create_dir_all(&cxx_qt_lib_src_dir).unwrap();

let mut paths = vec![];
// Add the hand written qt_types file
paths.append(&mut write_cxx_qt_lib_set(
"qt_types",
&cxx_qt_lib_target_dir,
cxx_qt_lib::QT_TYPES_HEADER,
cxx_qt_lib::QT_TYPES_SOURCE,
));
// Add the generated CXX files
let generated: Vec<GeneratedType> =
serde_json::from_str(cxx_qt_lib::QT_TYPES_CXX_JSON).unwrap();
for gen in generated {
paths.append(&mut write_cxx_qt_lib_set(
&gen.name,
&cxx_qt_lib_target_dir,
&gen.header,
&gen.source,
));
}

paths
}

/// Write out the static header file for both the cxx
fn write_cxx_static_header() {
let manifest_dir = manifest_dir();
Expand Down Expand Up @@ -417,19 +362,13 @@ impl CxxQtBuilder {
// TODO: later use the module::object to turn into module/object.h

// Generate files
let mut cpp_paths = write_cxx_generated_files_for_cargo(&self.rust_sources);
let cpp_paths = write_cxx_generated_files_for_cargo(&self.rust_sources);

// TODO: in large projects where where CXX-Qt is used in multiple individual
// components that end up being linked together, having these same static
// files in each one could cause issues.
write_cxx_static_header();

// Check if we have Qt support enabled
if self.qt_enabled {
// Write the cxx-qt-lib sources into the folder
cpp_paths.append(&mut write_cxx_qt_lib_sources());
}

// TODO: find a way to only do this when cargo is called during the config stage of CMake
write_cpp_sources_list(&cpp_paths);
}
Expand Down
7 changes: 2 additions & 5 deletions cxx-qt-lib/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,5 @@ links = "cxx-qt-lib"
cxx = "1.0"

[build-dependencies]
cxx-gen = "0.7"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
syn = { version = "1.0", features = ["printing"] }
quote = "1.0"
cxx-build = "1.0"
qt-build = { path = "../qt-build" }
171 changes: 47 additions & 124 deletions cxx-qt-lib/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,138 +3,61 @@
//
// SPDX-License-Identifier: MIT OR Apache-2.0

use quote::ToTokens;
use serde::{Deserialize, Serialize};
use std::{fs::File, io::Write, path::PathBuf};

/// Representation of a generated CXX header, source, and name
#[derive(Serialize, Deserialize)]
struct GeneratedType {
header: String,
name: String,
source: String,
}

/// Generate a CXX header, source, name for a given Rust file
fn gen_cxx_sources(folder: &str, file_stem: &str) -> GeneratedType {
// Read the rust source files
let path = format!("{}/{}.rs", folder, file_stem);
println!("cargo:rerun-if-changed={}", path);
let content = std::fs::read_to_string(path).expect("Could not read Rust file");
let file = syn::parse_file(&content).unwrap();

// Generate the CXX header and cc for the file
let opt = cxx_gen::Opt::default();
let generated = cxx_gen::generate_header_and_cc(file.into_token_stream(), &opt)
.expect("Could not generate C++ from Rust file");

GeneratedType {
header: String::from_utf8(generated.header).unwrap(),
name: format!("{}_cxx", file_stem),
source: String::from_utf8(generated.implementation).unwrap(),
}
}

/// Write generates types to a given file as JSON
fn write_cxx_sources(gen: &Vec<GeneratedType>, path: &str) {
let file = std::fs::File::create(path).expect("Could not create generated file");
serde_json::to_writer(file, &gen).unwrap();
}

fn create_and_write_file(path: &impl AsRef<std::path::Path>, file_contents: &str) {
let path = path.as_ref();
File::create(&path)
.unwrap_or_else(|_| panic!("Could not create file {}", path.display()))
.write_all(file_contents.as_bytes())
.unwrap_or_else(|_| panic!("Could not write file {}", path.display()));
}
use std::env;

fn main() {
// Read the cargo folder and out folder
let mut dir_manifest = std::env::var("CARGO_MANIFEST_DIR").expect("Could not get manifest dir");
if cfg!(windows) {
dir_manifest = dir_manifest.replace('\\', "/");
let qt_modules = vec!["Core", "Gui"]
.iter()
.map(|m| String::from(*m))
.collect();
let qtbuild = qt_build::QtBuild::new(qt_modules).expect("Could not find Qt installation");

let bridge_files = [
"src/types/qcolor.rs",
"src/types/qdate.rs",
"src/types/qdatetime.rs",
"src/types/qpoint.rs",
"src/types/qpointf.rs",
"src/types/qrect.rs",
"src/types/qrectf.rs",
"src/types/qsize.rs",
"src/types/qsizef.rs",
"src/types/qstring.rs",
"src/types/qtime.rs",
"src/types/qurl.rs",
"src/types/qvariant.rs",
"src/types/update_requester.rs",
];
for bridge_file in bridge_files {
println!("cargo:rerun-if-changed={}", bridge_file);
}
println!("cargo:rerun-if-env-changed=CARGO_MANIFEST_DIR");

let mut dir_out = std::env::var("OUT_DIR").expect("Could not get out dir");
if cfg!(windows) {
dir_out = dir_out.replace('\\', "/");
for include_path in qtbuild.include_paths() {
cxx_build::CFG
.exported_header_dirs
.push(include_path.as_path());
}
println!("cargo:rerun-if-env-changed=OUT_DIR");

// Prepare cxx-qt-lib dir we'll write to
let path = format!("{}/cxx-qt-lib", dir_out);
std::fs::create_dir_all(&path).expect("Could not create cxx-qt-lib dir");

// Read the types directory for CXX objects
let types_dir = format!("{}/src/types/", dir_manifest);
// If any of the files in this directory change, then we need to re-run
println!("cargo:rerun-if-changed={}", types_dir);

let mut generated = vec![];

for entry in std::fs::read_dir(&types_dir).expect("Could not open types folder") {
let path = entry.expect("Could not open file").path();
let file_stem = path
.file_stem()
.expect("Could not find file name")
.to_str()
.expect("Could not convert to unicode");

// Check we are a file and not the mod.rs
if path.is_file() && file_stem != "mod" {
generated.push(gen_cxx_sources(&types_dir, file_stem));
}
let mut builder = cxx_build::bridges(&bridge_files);
for cpp_file in ["src/qt_types.cpp"] {
builder.file(cpp_file);
println!("cargo:rerun-if-changed={}", cpp_file);
}

// Write the generated sources to a qt_types_cxx.json file
write_cxx_sources(&generated, &format!("{}/qt_types_cxx.json", path));

// Write the generated sources to CXX_QT_LIB_OUT_DIR if set
println!("cargo:rerun-if-env-changed=CXX_QT_LIB_OUT_DIR");
if let Ok(env_var) = std::env::var("CXX_QT_LIB_OUT_DIR") {
let directory = PathBuf::from(env_var);
if !directory.is_dir() {
panic!(
"CXX_QT_LIB_OUT_DIR {} is not a directory",
directory.display()
);
}

let include_directory_path = PathBuf::from(format!("{}/include", &directory.display()));
std::fs::create_dir_all(&include_directory_path)
.expect("Could not create CXX_QT_LIB_OUT_DIR include dir");
let source_directory_path = PathBuf::from(format!("{}/src", &directory.display()));
std::fs::create_dir_all(&source_directory_path)
.expect("Could not create CXX_QT_LIB_OUT_DIR source dir");

std::fs::copy(
format!("{}/include/qt_types.h", dir_manifest),
format!("{}/qt_types.h", include_directory_path.display()),
)
.expect("Could not copy qt_types.h to CXX_QT_LIB_OUT_DIR");
builder.flag_if_supported("-std=c++17");
builder.compile("cxx-qt-lib");

// Copy qt_types.h so CMake can include it.
// By design, CARGO_TARGET_DIR is not set by cargo when running build scripts.
// Copying the header is only needed for making the header available to a C++
// build system, in which case CARGO_TARGET_DIR will be set by
// the C++ build system.
if let Ok(target_dir) = env::var("CARGO_TARGET_DIR") {
let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
std::fs::create_dir_all(&format!("{}/cxxbridge/cxx-qt-lib/include", target_dir)).unwrap();
std::fs::copy(
format!("{}/src/qt_types.cpp", dir_manifest),
format!("{}/qt_types.cpp", source_directory_path.display()),
&format!("{}/include/qt_types.h", manifest_dir),
&format!("{}/cxxbridge/cxx-qt-lib/include/qt_types.h", target_dir),
)
.expect("Could not copy qt_types.cpp to CXX_QT_LIB_OUT_DIR");

create_and_write_file(
&format!("{}/cxx.h", include_directory_path.display()),
cxx_gen::HEADER,
);

for class in generated {
create_and_write_file(
&format!("{}/{}.h", include_directory_path.display(), class.name),
&class.header,
);

create_and_write_file(
&format!("{}/{}.cpp", source_directory_path.display(), class.name),
&class.source,
);
}
.unwrap();
}
}
27 changes: 0 additions & 27 deletions cxx-qt-lib/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,33 +11,6 @@ pub use types::*;
// this is because include_str requires th correct and non-mixed path separators
//
// https://github.com/rust-lang/rust/issues/75075
#[cfg(not(windows))]
macro_rules! sep {
() => {
"/"
};
}

#[cfg(windows)]
macro_rules! sep {
() => {
"\\"
};
}

/// JSON representation of the generated CXX sources for qt_types
pub const QT_TYPES_CXX_JSON: &str = include_str!(concat!(
env!("OUT_DIR"),
sep!(),
"cxx-qt-lib",
sep!(),
"qt_types_cxx.json"
));
/// The header for qt_types
pub const QT_TYPES_HEADER: &str =
include_str!(concat!("..", sep!(), "include", sep!(), "qt_types.h"));
/// The source for qt_types
pub const QT_TYPES_SOURCE: &str = include_str!("qt_types.cpp");

pub trait UpdateRequestHandler<C> {
fn handle_update_request(&mut self, cpp: &mut C);
Expand Down

0 comments on commit 50628a6

Please sign in to comment.