diff --git a/cmake/CxxQt.cmake b/cmake/CxxQt.cmake index 292c74f61..d9c0e87f5 100644 --- a/cmake/CxxQt.cmake +++ b/cmake/CxxQt.cmake @@ -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} ) @@ -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() @@ -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") diff --git a/cxx-qt-build/src/lib.rs b/cxx-qt-build/src/lib.rs index 10b27b250..96c9c7149 100644 --- a/cxx-qt-build/src/lib.rs +++ b/cxx-qt-build/src/lib.rs @@ -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 { - 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 { - 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 = - 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(); @@ -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); } diff --git a/cxx-qt-lib/Cargo.toml b/cxx-qt-lib/Cargo.toml index 5004f7cc1..5b99abcad 100644 --- a/cxx-qt-lib/Cargo.toml +++ b/cxx-qt-lib/Cargo.toml @@ -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" } diff --git a/cxx-qt-lib/build.rs b/cxx-qt-lib/build.rs index e472dfc79..245b3252a 100644 --- a/cxx-qt-lib/build.rs +++ b/cxx-qt-lib/build.rs @@ -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, 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, 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(); } } diff --git a/cxx-qt-lib/src/lib.rs b/cxx-qt-lib/src/lib.rs index eb78c9f90..ec2521206 100644 --- a/cxx-qt-lib/src/lib.rs +++ b/cxx-qt-lib/src/lib.rs @@ -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 { fn handle_update_request(&mut self, cpp: &mut C);