-
Notifications
You must be signed in to change notification settings - Fork 82
cxx-qt-build: return an Interface from CxxQtBuilder #1224
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
ahayzen-kdab
wants to merge
9
commits into
KDAB:main
Choose a base branch
from
ahayzen-kdab:1125-build-system-changes
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+407
−390
Open
Changes from all commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
b5247f6
cxx-qt-build: split interface into a separate file
ahayzen-kdab e7eaece
cxx-qt-build: move write_manifest to export of Interface
ahayzen-kdab d902013
cxx-qt-build: move write of exported include directories to interface
ahayzen-kdab 94dba0e
cxx-qt-build: store manifest and dependencies in Interface for now
ahayzen-kdab 08d7be1
cxx-qt-build: return an Interface rather than taking as input
ahayzen-kdab b62b038
cxx-qt-build: check the links key when exporting
ahayzen-kdab ec4edf3
cxx-qt-build: split include and exported include dirs
ahayzen-kdab 956e7fc
cxx-qt-build: include headers automatically like CXX
ahayzen-kdab 2418af1
cxx-qt-build: remove CxxQtBuilder::library instead use new and export
ahayzen-kdab File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,210 @@ | ||
// SPDX-FileCopyrightText: 2024 Klarälvdalens Datakonsult AB, a KDAB Group company <[email protected]> | ||
// SPDX-FileContributor: Leon Matthes <[email protected]> | ||
// | ||
// SPDX-License-Identifier: MIT OR Apache-2.0 | ||
|
||
//! This modules contains utilities for specifying interfaces with cxx-qt-build. | ||
|
||
use std::collections::HashSet; | ||
|
||
use std::path::{Path, PathBuf}; | ||
|
||
use crate::{dir, dir::INCLUDE_VERB, Dependency, Manifest}; | ||
|
||
/// When generating a library with cxx-qt-build, the library may need to export certain flags or headers. | ||
/// These are all specified by this Interface struct. | ||
pub struct Interface { | ||
// The name of the links keys, whose CXX-Qt dependencies to reexport | ||
pub(crate) reexport_links: HashSet<String>, | ||
pub(crate) exported_include_prefixes: Vec<String>, | ||
pub(crate) exported_include_directories: Vec<(PathBuf, String)>, | ||
pub(crate) manifest: Manifest, | ||
pub(crate) dependencies: Vec<Dependency>, | ||
// TODO: In future, we want to also set up the include paths so that you can include anything | ||
// from the crates source directory. | ||
// Once this is done, this flag should indicate whether or not to export our own crates source | ||
// directory to downstream dependencies? | ||
// export_crate_directory: bool, | ||
} | ||
|
||
impl Default for Interface { | ||
fn default() -> Self { | ||
Self { | ||
reexport_links: HashSet::new(), | ||
exported_include_prefixes: vec![super::crate_name()], | ||
exported_include_directories: Vec::new(), | ||
manifest: Manifest::default(), | ||
dependencies: Vec::new(), | ||
} | ||
} | ||
} | ||
|
||
impl Interface { | ||
/// Export all headers with the given prefix to downstream dependencies | ||
/// | ||
/// Note: This will overwrite any previously specified header_prefixes, including the default | ||
/// header_prefix of this crate. | ||
/// | ||
/// This function will panic if any of the given prefixes are already exported through the | ||
/// [Self::export_include_directory] function. | ||
pub fn export_include_prefixes<'a>( | ||
mut self, | ||
prefixes: impl IntoIterator<Item = &'a str>, | ||
) -> Self { | ||
let prefixes = prefixes.into_iter().map(|item| item.to_string()).collect(); | ||
|
||
let mut exported_prefixes = self | ||
.exported_include_directories | ||
.iter() | ||
.map(|(_path, prefix)| prefix); | ||
for prefix in &prefixes { | ||
if let Some(duplicate) = | ||
exported_prefixes.find(|exported_prefix| exported_prefix.starts_with(prefix)) | ||
{ | ||
panic!("Duplicate export_prefix! Cannot export `{prefix}`, as `{duplicate}` is already exported as an export_include_directory!"); | ||
} | ||
} | ||
|
||
self.exported_include_prefixes = prefixes; | ||
self | ||
} | ||
|
||
/// Add a directory that will be added as an include directory under the given prefix. | ||
/// | ||
/// The prefix will automatically be exported (see also: [Self::export_include_prefixes]) | ||
/// | ||
/// This function will panic if the given prefix is already exported. | ||
pub fn export_include_directory(mut self, directory: impl AsRef<Path>, prefix: &str) -> Self { | ||
let mut exported_prefixes = self.exported_include_prefixes.iter().chain( | ||
self.exported_include_directories | ||
.iter() | ||
.map(|(_path, prefix)| prefix), | ||
); | ||
if let Some(duplicate) = | ||
exported_prefixes.find(|exported_prefix| exported_prefix.starts_with(prefix)) | ||
{ | ||
panic!("Duplicate export_prefix! Cannot export `{prefix}`, as `{duplicate}` is already exported!"); | ||
} | ||
|
||
self.exported_include_directories | ||
.push((directory.as_ref().into(), prefix.to_owned())); | ||
self | ||
} | ||
|
||
/// Reexport the dependency with the given link name. | ||
/// This will make the dependency available to downstream dependencies. | ||
/// | ||
/// Specifically it will reexport all include_prefixes of the given dependency | ||
/// as well as any definitions made by that dependency. | ||
/// | ||
/// Note that the link name may differ from the crate name. | ||
/// Check your dependencies manifest file for the correct link name. | ||
pub fn reexport_dependency(mut self, link_name: &str) -> Self { | ||
self.reexport_links.insert(link_name.to_owned()); | ||
self | ||
} | ||
|
||
/// Export the manifest for this crate so that is can be used by downstream | ||
/// crates or CMake | ||
pub fn export(mut self) { | ||
// Ensure that a link name has been set | ||
if self.manifest.link_name.is_empty() { | ||
panic!("The links key must be set when exporting with CXX-Qt-build"); | ||
} | ||
|
||
self.write_exported_include_directories(); | ||
|
||
// We automatically reexport all qt_modules and downstream dependencies | ||
// as they will always need to be enabled in the final binary. | ||
// However, we only reexport the headers of libraries that | ||
// are marked as re-export. | ||
let dependencies = reexported_dependencies(&self, &self.dependencies); | ||
|
||
self.manifest.exported_include_prefixes = all_include_prefixes(&self, &dependencies); | ||
|
||
let manifest_path = dir::crate_target().join("manifest.json"); | ||
let manifest_json = serde_json::to_string_pretty(&self.manifest) | ||
.expect("Failed to convert Manifest to JSON!"); | ||
std::fs::write(&manifest_path, manifest_json).expect("Failed to write manifest.json!"); | ||
println!( | ||
"cargo::metadata=CXX_QT_MANIFEST_PATH={}", | ||
manifest_path.to_string_lossy() | ||
); | ||
} | ||
|
||
fn write_exported_include_directories(&self) { | ||
// Add any export directories as a child of the include interface directory | ||
let header_root_interface = dir::header_root_interface(); | ||
std::fs::create_dir_all(&header_root_interface) | ||
.expect("Failed to create header root for interface"); | ||
for (header_dir, dest) in &self.exported_include_directories { | ||
let dest_dir = header_root_interface.join(dest); | ||
// Relative paths are take from under the header root | ||
// | ||
// TODO: is this correct? | ||
let header_dir = if header_dir.is_relative() { | ||
dir::header_root().join(header_dir) | ||
} else { | ||
header_dir.clone() | ||
}; | ||
match dir::symlink_or_copy_directory(&header_dir, &dest_dir) { | ||
Ok(true) => {}, | ||
Ok(false) => panic!("Failed to create symlink folder for `{dest}`"), | ||
Err(e) => panic!( | ||
"Failed to {INCLUDE_VERB} `{dest}` for export_include_directory `{dir_name}`: {e:?}", | ||
dir_name = header_dir.to_string_lossy() | ||
) | ||
}; | ||
} | ||
|
||
// Add any reexport links as a child of the include interface directory | ||
let header_root = dir::header_root(); | ||
for reexport in &self.reexport_links { | ||
let source_dir = header_root.join(reexport); | ||
let dest_dir = header_root_interface.join(reexport); | ||
match dir::symlink_or_copy_directory(&source_dir, &dest_dir) { | ||
Ok(true) => {}, | ||
Ok(false) => panic!("Failed to create symlink folder for `{reexport}`"), | ||
Err(e) => panic!( | ||
"Failed to {INCLUDE_VERB} `{reexport}` for export_include_directory `{dir_name}`: {e:?}", | ||
dir_name = source_dir.to_string_lossy() | ||
) | ||
}; | ||
} | ||
} | ||
} | ||
|
||
fn all_include_prefixes(interface: &Interface, dependencies: &[Dependency]) -> Vec<String> { | ||
interface | ||
.exported_include_prefixes | ||
.iter() | ||
.cloned() | ||
.chain( | ||
interface | ||
.exported_include_directories | ||
.iter() | ||
.map(|(_path, prefix)| prefix.clone()), | ||
) | ||
.chain( | ||
dependencies | ||
.iter() | ||
.flat_map(|dep| &dep.manifest.exported_include_prefixes) | ||
.cloned(), | ||
) | ||
.collect() | ||
} | ||
|
||
fn reexported_dependencies(interface: &Interface, dependencies: &[Dependency]) -> Vec<Dependency> { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should be in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could be, this has just moved the existing code :-) |
||
let mut exported_dependencies = Vec::new(); | ||
for link_name in &interface.reexport_links { | ||
if let Some(dependency) = dependencies | ||
.iter() | ||
.find(|dep| &dep.manifest.link_name == link_name) | ||
{ | ||
exported_dependencies.push(dependency.clone()); | ||
} else { | ||
panic!("Could not find dependency with link name `{link_name}` to reexport!"); | ||
} | ||
} | ||
exported_dependencies | ||
} |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This assumes we have a links key set, right?
We should probably panic if it isn't the case here.
However, then we'd have to check whether we're currently exporting to CMake, as then it's not needed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A few lines further up (at the start of this export() function) we panic if the link name is empty?