mirror of
https://github.com/tauri-apps/tauri.git
synced 2026-02-06 14:17:02 +00:00
Co-authored-by: Lucas Nogueira <lucas@tauri.studio>
This commit is contained in:
parent
9da996073f
commit
d20a728892
@ -1,5 +1,6 @@
|
||||
---
|
||||
'cli.rs': 'minor'
|
||||
'tauri-build': 'minor'
|
||||
---
|
||||
|
||||
Add support for Cargo's workspace inheritance for the package version.
|
||||
Added support for Cargo's workspace inheritance for package information. The cli now also detects inherited `tauri` and `tauri-build` dependencies and disables manifest rewrites accordingly.
|
||||
|
||||
@ -22,6 +22,7 @@ quote = { version = "1", optional = true }
|
||||
tauri-codegen = { version = "1.2.1", path = "../tauri-codegen", optional = true }
|
||||
tauri-utils = { version = "1.2.1", path = "../tauri-utils", features = [ "build", "resources" ] }
|
||||
cargo_toml = "0.14"
|
||||
serde = "1"
|
||||
serde_json = "1"
|
||||
heck = "0.4"
|
||||
json-patch = "0.3"
|
||||
|
||||
@ -5,9 +5,13 @@
|
||||
#![cfg_attr(doc_cfg, feature(doc_cfg))]
|
||||
|
||||
pub use anyhow::Result;
|
||||
use cargo_toml::{Dependency, Manifest};
|
||||
use heck::AsShoutySnakeCase;
|
||||
|
||||
use tauri_utils::resources::{external_binaries, resource_relpath, ResourcePaths};
|
||||
use tauri_utils::{
|
||||
config::Config,
|
||||
resources::{external_binaries, resource_relpath, ResourcePaths},
|
||||
};
|
||||
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
@ -242,8 +246,6 @@ pub fn build() {
|
||||
#[allow(unused_variables)]
|
||||
pub fn try_build(attributes: Attributes) -> Result<()> {
|
||||
use anyhow::anyhow;
|
||||
use cargo_toml::{Dependency, Manifest};
|
||||
use tauri_utils::config::{Config, TauriConfig};
|
||||
|
||||
println!("cargo:rerun-if-env-changed=TAURI_CONFIG");
|
||||
println!("cargo:rerun-if-changed=tauri.conf.json");
|
||||
@ -268,50 +270,33 @@ pub fn try_build(attributes: Attributes) -> Result<()> {
|
||||
|
||||
cfg_alias("dev", !has_feature("custom-protocol"));
|
||||
|
||||
let mut manifest = Manifest::from_path("Cargo.toml")?;
|
||||
let ws_path = get_workspace_dir()?;
|
||||
let mut manifest =
|
||||
Manifest::<cargo_toml::Value>::from_slice_with_metadata(&std::fs::read("Cargo.toml")?)?;
|
||||
|
||||
if let Ok(ws_manifest) = Manifest::from_path(ws_path.join("Cargo.toml")) {
|
||||
Manifest::complete_from_path_and_workspace(
|
||||
&mut manifest,
|
||||
Path::new("Cargo.toml"),
|
||||
Some((&ws_manifest, ws_path.as_path())),
|
||||
)?;
|
||||
} else {
|
||||
Manifest::complete_from_path(&mut manifest, Path::new("Cargo.toml"))?;
|
||||
}
|
||||
|
||||
if let Some(tauri_build) = manifest.build_dependencies.remove("tauri-build") {
|
||||
let error_message = check_features(&config, tauri_build, true);
|
||||
|
||||
if !error_message.is_empty() {
|
||||
return Err(anyhow!("
|
||||
The `tauri-build` dependency features on the `Cargo.toml` file does not match the allowlist defined under `tauri.conf.json`.
|
||||
Please run `tauri dev` or `tauri build` or {}.
|
||||
", error_message));
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(tauri) = manifest.dependencies.remove("tauri") {
|
||||
let features = match tauri {
|
||||
Dependency::Simple(_) => Vec::new(),
|
||||
Dependency::Detailed(dep) => dep.features,
|
||||
Dependency::Inherited(dep) => dep.features,
|
||||
};
|
||||
|
||||
let all_cli_managed_features = TauriConfig::all_features();
|
||||
let diff = features_diff(
|
||||
&features
|
||||
.into_iter()
|
||||
.filter(|f| all_cli_managed_features.contains(&f.as_str()))
|
||||
.collect::<Vec<String>>(),
|
||||
&config
|
||||
.tauri
|
||||
.features()
|
||||
.into_iter()
|
||||
.map(|f| f.to_string())
|
||||
.collect::<Vec<String>>(),
|
||||
);
|
||||
|
||||
let mut error_message = String::new();
|
||||
if !diff.remove.is_empty() {
|
||||
error_message.push_str("remove the `");
|
||||
error_message.push_str(&diff.remove.join(", "));
|
||||
error_message.push_str(if diff.remove.len() == 1 {
|
||||
"` feature"
|
||||
} else {
|
||||
"` features"
|
||||
});
|
||||
if !diff.add.is_empty() {
|
||||
error_message.push_str(" and ");
|
||||
}
|
||||
}
|
||||
if !diff.add.is_empty() {
|
||||
error_message.push_str("add the `");
|
||||
error_message.push_str(&diff.add.join(", "));
|
||||
error_message.push_str(if diff.add.len() == 1 {
|
||||
"` feature"
|
||||
} else {
|
||||
"` features"
|
||||
});
|
||||
}
|
||||
let error_message = check_features(&config, tauri, false);
|
||||
|
||||
if !error_message.is_empty() {
|
||||
return Err(anyhow!("
|
||||
@ -485,6 +470,89 @@ fn features_diff(current: &[String], expected: &[String]) -> Diff {
|
||||
Diff { remove, add }
|
||||
}
|
||||
|
||||
fn check_features(config: &Config, dependency: Dependency, is_tauri_build: bool) -> String {
|
||||
use tauri_utils::config::{PatternKind, TauriConfig};
|
||||
|
||||
let features = match dependency {
|
||||
Dependency::Simple(_) => Vec::new(),
|
||||
Dependency::Detailed(dep) => dep.features,
|
||||
Dependency::Inherited(dep) => dep.features,
|
||||
};
|
||||
|
||||
let all_cli_managed_features = if is_tauri_build {
|
||||
vec!["isolation"]
|
||||
} else {
|
||||
TauriConfig::all_features()
|
||||
};
|
||||
|
||||
let expected = if is_tauri_build {
|
||||
match config.tauri.pattern {
|
||||
PatternKind::Isolation { .. } => vec!["isolation".to_string()],
|
||||
_ => vec![],
|
||||
}
|
||||
} else {
|
||||
config
|
||||
.tauri
|
||||
.features()
|
||||
.into_iter()
|
||||
.map(|f| f.to_string())
|
||||
.collect::<Vec<String>>()
|
||||
};
|
||||
|
||||
let diff = features_diff(
|
||||
&features
|
||||
.into_iter()
|
||||
.filter(|f| all_cli_managed_features.contains(&f.as_str()))
|
||||
.collect::<Vec<String>>(),
|
||||
&expected,
|
||||
);
|
||||
|
||||
let mut error_message = String::new();
|
||||
if !diff.remove.is_empty() {
|
||||
error_message.push_str("remove the `");
|
||||
error_message.push_str(&diff.remove.join(", "));
|
||||
error_message.push_str(if diff.remove.len() == 1 {
|
||||
"` feature"
|
||||
} else {
|
||||
"` features"
|
||||
});
|
||||
if !diff.add.is_empty() {
|
||||
error_message.push_str(" and ");
|
||||
}
|
||||
}
|
||||
if !diff.add.is_empty() {
|
||||
error_message.push_str("add the `");
|
||||
error_message.push_str(&diff.add.join(", "));
|
||||
error_message.push_str(if diff.add.len() == 1 {
|
||||
"` feature"
|
||||
} else {
|
||||
"` features"
|
||||
});
|
||||
}
|
||||
|
||||
error_message
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize)]
|
||||
struct CargoMetadata {
|
||||
workspace_root: PathBuf,
|
||||
}
|
||||
|
||||
fn get_workspace_dir() -> Result<PathBuf> {
|
||||
let output = std::process::Command::new("cargo")
|
||||
.args(["metadata", "--no-deps", "--format-version", "1"])
|
||||
.output()?;
|
||||
|
||||
if !output.status.success() {
|
||||
return Err(anyhow::anyhow!(
|
||||
"cargo metadata command exited with a non zero exit code: {}",
|
||||
String::from_utf8(output.stderr)?
|
||||
));
|
||||
}
|
||||
|
||||
Ok(serde_json::from_slice::<CargoMetadata>(&output.stdout)?.workspace_root)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Diff;
|
||||
|
||||
24
examples/workspace/Cargo.lock
generated
24
examples/workspace/Cargo.lock
generated
@ -2334,7 +2334,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri"
|
||||
version = "1.2.3"
|
||||
version = "1.2.4"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"cocoa",
|
||||
@ -2384,9 +2384,10 @@ dependencies = [
|
||||
"heck 0.4.0",
|
||||
"json-patch",
|
||||
"semver 1.0.16",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tauri-utils",
|
||||
"winres",
|
||||
"tauri-winres",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2487,6 +2488,16 @@ dependencies = [
|
||||
"windows",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tauri-winres"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b7a78dc04f75fb5ab815e66ac561c81e92a968a40f29e7c21afd152d694fad8"
|
||||
dependencies = [
|
||||
"toml",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.3.0"
|
||||
@ -3045,15 +3056,6 @@ version = "0.42.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd"
|
||||
|
||||
[[package]]
|
||||
name = "winres"
|
||||
version = "0.1.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b68db261ef59e9e52806f688020631e987592bd83619edccda9c47d42cde4f6c"
|
||||
dependencies = [
|
||||
"toml",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wry"
|
||||
version = "0.24.1"
|
||||
|
||||
@ -6,3 +6,7 @@ members = [
|
||||
|
||||
[workspace.package]
|
||||
version = "1.0.0"
|
||||
|
||||
[workspace.dependencies]
|
||||
tauri-build = { path = "../../core/tauri-build", features = [] }
|
||||
tauri = { path = "../../core/tauri", features = [] }
|
||||
|
||||
@ -10,12 +10,12 @@ edition = "2021"
|
||||
rust-version = "1.59"
|
||||
|
||||
[build-dependencies]
|
||||
tauri-build = { path = "../../../core/tauri-build", features = [] }
|
||||
tauri-build = { workspace = true }
|
||||
|
||||
[dependencies]
|
||||
serde_json = "1.0"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
tauri = { path = "../../../core/tauri", features = [] }
|
||||
tauri = { workspace = true }
|
||||
core-api = { path = "../core" }
|
||||
|
||||
[features]
|
||||
|
||||
2
tooling/cli/Cargo.lock
generated
2
tooling/cli/Cargo.lock
generated
@ -3185,7 +3185,6 @@ dependencies = [
|
||||
"tempfile",
|
||||
"thiserror",
|
||||
"time",
|
||||
"toml",
|
||||
"uuid 1.1.2",
|
||||
"walkdir",
|
||||
"winreg",
|
||||
@ -3211,6 +3210,7 @@ dependencies = [
|
||||
"ignore",
|
||||
"image",
|
||||
"include_dir",
|
||||
"itertools",
|
||||
"json-patch 0.2.6",
|
||||
"jsonschema",
|
||||
"kuchiki",
|
||||
|
||||
@ -80,6 +80,7 @@ kuchiki = "0.8"
|
||||
tokio = { version = "1", features = [ "macros", "sync" ] }
|
||||
common-path = "1"
|
||||
serde-value = "0.7.0"
|
||||
itertools = "0.10"
|
||||
|
||||
[target."cfg(windows)".dependencies]
|
||||
winapi = { version = "0.3", features = [ "handleapi", "processenv", "winbase", "wincon", "winnt" ] }
|
||||
|
||||
@ -567,7 +567,9 @@ struct WorkspaceSettings {
|
||||
|
||||
#[derive(Clone, Debug, Deserialize)]
|
||||
struct WorkspacePackageSettings {
|
||||
/// the workspace members.
|
||||
authors: Option<Vec<String>>,
|
||||
description: Option<String>,
|
||||
homepage: Option<String>,
|
||||
version: Option<String>,
|
||||
}
|
||||
|
||||
@ -585,11 +587,11 @@ pub struct CargoPackageSettings {
|
||||
/// the package's version.
|
||||
pub version: Option<MaybeWorkspace<String>>,
|
||||
/// the package's description.
|
||||
pub description: Option<String>,
|
||||
pub description: Option<MaybeWorkspace<String>>,
|
||||
/// the package's homepage.
|
||||
pub homepage: Option<String>,
|
||||
pub homepage: Option<MaybeWorkspace<String>>,
|
||||
/// the package's authors.
|
||||
pub authors: Option<Vec<String>>,
|
||||
pub authors: Option<MaybeWorkspace<Vec<String>>>,
|
||||
/// the default binary to run.
|
||||
pub default_run: Option<String>,
|
||||
}
|
||||
@ -794,6 +796,11 @@ impl RustAppSettings {
|
||||
}
|
||||
};
|
||||
|
||||
let ws_package_settings = CargoSettings::load(&get_workspace_dir()?)
|
||||
.with_context(|| "failed to load cargo settings from workspace root")?
|
||||
.workspace
|
||||
.and_then(|v| v.package);
|
||||
|
||||
let package_settings = PackageSettings {
|
||||
product_name: config.package.product_name.clone().unwrap_or_else(|| {
|
||||
cargo_package_settings
|
||||
@ -807,15 +814,51 @@ impl RustAppSettings {
|
||||
.clone()
|
||||
.expect("Cargo manifest must have the `package.version` field")
|
||||
.resolve("version", || {
|
||||
get_workspace_version()?.context("Workspace Cargo manifest must have the `workspace.package.version` field if a member tries to inherit the version")
|
||||
}).expect("Cargo project does not have a version")
|
||||
ws_package_settings
|
||||
.as_ref()
|
||||
.and_then(|p| p.version.clone())
|
||||
.ok_or_else(|| anyhow::anyhow!("Couldn't inherit value for `version` from workspace"))
|
||||
})
|
||||
.expect("Cargo project does not have a version")
|
||||
}),
|
||||
description: cargo_package_settings
|
||||
.description
|
||||
.clone()
|
||||
.map(|description| {
|
||||
description
|
||||
.resolve("description", || {
|
||||
ws_package_settings
|
||||
.as_ref()
|
||||
.and_then(|v| v.description.clone())
|
||||
.ok_or_else(|| {
|
||||
anyhow::anyhow!("Couldn't inherit value for `description` from workspace")
|
||||
})
|
||||
})
|
||||
.unwrap()
|
||||
})
|
||||
.unwrap_or_default(),
|
||||
homepage: cargo_package_settings.homepage.clone(),
|
||||
authors: cargo_package_settings.authors.clone(),
|
||||
homepage: cargo_package_settings.homepage.clone().map(|homepage| {
|
||||
homepage
|
||||
.resolve("homepage", || {
|
||||
ws_package_settings
|
||||
.as_ref()
|
||||
.and_then(|v| v.homepage.clone())
|
||||
.ok_or_else(|| {
|
||||
anyhow::anyhow!("Couldn't inherit value for `homepage` from workspace")
|
||||
})
|
||||
})
|
||||
.unwrap()
|
||||
}),
|
||||
authors: cargo_package_settings.authors.clone().map(|authors| {
|
||||
authors
|
||||
.resolve("authors", || {
|
||||
ws_package_settings
|
||||
.as_ref()
|
||||
.and_then(|v| v.authors.clone())
|
||||
.ok_or_else(|| anyhow::anyhow!("Couldn't inherit value for `authors` from workspace"))
|
||||
})
|
||||
.unwrap()
|
||||
}),
|
||||
default_run: cargo_package_settings.default_run.clone(),
|
||||
};
|
||||
|
||||
@ -913,27 +956,6 @@ pub fn get_workspace_dir() -> crate::Result<PathBuf> {
|
||||
)
|
||||
}
|
||||
|
||||
pub fn get_workspace_version() -> crate::Result<Option<String>> {
|
||||
// This will already fail because `cargo metadata` fails if there is no version in the workspace root when a package tries to inherit it.
|
||||
let toml_path = get_workspace_dir()
|
||||
.map(|p| p.join("Cargo.toml"))
|
||||
.with_context(|| "failed to get workspace Cargo.toml file")?;
|
||||
|
||||
let mut toml_str = String::new();
|
||||
let mut toml_file = File::open(toml_path).with_context(|| "failed to open Cargo.toml")?;
|
||||
toml_file
|
||||
.read_to_string(&mut toml_str)
|
||||
.with_context(|| "failed to read Cargo.toml")?;
|
||||
|
||||
Ok(
|
||||
toml::from_str::<CargoSettings>(&toml_str)
|
||||
.with_context(|| "failed to parse Cargo.toml")?
|
||||
.workspace
|
||||
.and_then(|ws| ws.package)
|
||||
.and_then(|p| p.version),
|
||||
)
|
||||
}
|
||||
|
||||
#[allow(unused_variables)]
|
||||
fn tauri_config_to_bundle_settings(
|
||||
manifest: &Manifest,
|
||||
|
||||
@ -8,6 +8,8 @@ use crate::helpers::{
|
||||
};
|
||||
|
||||
use anyhow::Context;
|
||||
use itertools::Itertools;
|
||||
use log::info;
|
||||
use toml_edit::{Array, Document, InlineTable, Item, Table, Value};
|
||||
|
||||
use std::{
|
||||
@ -82,7 +84,7 @@ fn get_enabled_features(list: &HashMap<String, Vec<String>>, feature: &str) -> V
|
||||
f
|
||||
}
|
||||
|
||||
fn read_manifest(manifest_path: &Path) -> crate::Result<Document> {
|
||||
pub fn read_manifest(manifest_path: &Path) -> crate::Result<Document> {
|
||||
let mut manifest_str = String::new();
|
||||
|
||||
let mut manifest_file = File::open(manifest_path)
|
||||
@ -114,6 +116,16 @@ fn write_features(
|
||||
) -> crate::Result<bool> {
|
||||
let item = dependencies.entry(dependency_name).or_insert(Item::None);
|
||||
|
||||
// do not rewrite if dependency uses workspace inheritance
|
||||
if item
|
||||
.get("workspace")
|
||||
.and_then(|v| v.as_bool())
|
||||
.unwrap_or_default()
|
||||
{
|
||||
info!("`{dependency_name}` dependency has workspace inheritance enabled. The features array won't be automatically rewritten. Expected features: [{}]", features.iter().join(", "));
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
if let Some(dep) = item.as_table_mut() {
|
||||
let manifest_features = dep.entry("features").or_insert(Item::None);
|
||||
if let Item::Value(Value::Array(f)) = &manifest_features {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user