feat(cli): add support for Cargo's workspace inheritance for the package version, closes #5070 (#5775)

Co-authored-by: Lucas Nogueira <lucas@tauri.studio>
This commit is contained in:
Fabian-Lars 2022-12-14 17:39:05 +01:00 committed by GitHub
parent f7a080a121
commit cd8c074ae6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 3371 additions and 3 deletions

View File

@ -0,0 +1,5 @@
---
'cli.rs': 'minor'
---
Add support for Cargo's workspace inheritance for the package version.

View File

@ -3,7 +3,7 @@
# SPDX-License-Identifier: Apache-2.0
# SPDX-License-Identifier: MIT
declare -a examples=("api" "sidecar" "updater" "resources" "tauri-dynamic-lib")
declare -a examples=("api" "sidecar" "updater" "resources" "tauri-dynamic-lib" "workspace")
declare -a tooling=("bench" "cli" "webdriver")
for example in "${examples[@]}"

View File

@ -21,7 +21,8 @@ exclude = [
"examples/updater/src-tauri",
"examples/resources/src-tauri",
"examples/sidecar/src-tauri",
"examples/web/core"
"examples/web/core",
"examples/workspace"
]
# default to small, optimized workspace release binaries

3111
examples/workspace/Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,8 @@
[workspace]
members = [
"core",
"src-tauri"
]
[workspace.package]
version = "1.0.0"

View File

@ -0,0 +1,6 @@
[package]
name = "core-api"
version = { workspace = true }
edition = "2021"
[dependencies]

View File

@ -0,0 +1,3 @@
pub fn run() {
println!("run!");
}

View File

@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
</head>
<body>
<h1>Workspace Example!</h1>
</body>
</html>

View File

@ -0,0 +1,3 @@
# Generated by Cargo
# will have compiled files and executables
/target/

View File

@ -0,0 +1,27 @@
[package]
name = "app"
version = { workspace = true }
description = "A Tauri App"
authors = ["you"]
license = ""
repository = ""
default-run = "app"
edition = "2021"
rust-version = "1.59"
[build-dependencies]
tauri-build = { path = "../../../core/tauri-build", features = [] }
[dependencies]
serde_json = "1.0"
serde = { version = "1.0", features = ["derive"] }
tauri = { path = "../../../core/tauri", features = [] }
core-api = { path = "../core" }
[features]
# by default Tauri runs in production mode
# when `tauri dev` runs it is executed with `cargo run --no-default-features` if `devPath` is an URL
default = [ "custom-protocol" ]
# this feature is used for production builds where `devPath` points to the filesystem
# DO NOT remove this
custom-protocol = [ "tauri/custom-protocol" ]

View File

@ -0,0 +1,3 @@
fn main() {
tauri_build::build()
}

View File

@ -0,0 +1,11 @@
#![cfg_attr(
all(not(debug_assertions), target_os = "windows"),
windows_subsystem = "windows"
)]
fn main() {
core_api::run();
tauri::Builder::default()
.run(tauri::generate_context!())
.expect("error while running tauri application");
}

View File

@ -0,0 +1,66 @@
{
"build": {
"distDir": [
"../index.html"
],
"devPath": [
"../index.html"
]
},
"package": {
"productName": "workspace"
},
"tauri": {
"allowlist": {
"all": false
},
"bundle": {
"active": true,
"category": "DeveloperTool",
"copyright": "",
"deb": {
"depends": []
},
"externalBin": [],
"icon": [
"../../.icons/32x32.png",
"../../.icons/128x128.png",
"../../.icons/128x128@2x.png",
"../../.icons/icon.icns",
"../../.icons/icon.ico"
],
"identifier": "com.tauri.workspace",
"longDescription": "",
"macOS": {
"entitlements": null,
"exceptionDomain": "",
"frameworks": [],
"providerShortName": null,
"signingIdentity": null
},
"resources": [],
"shortDescription": "",
"targets": "all",
"windows": {
"certificateThumbprint": null,
"digestAlgorithm": "sha256",
"timestampUrl": ""
}
},
"security": {
"csp": null
},
"updater": {
"active": false
},
"windows": [
{
"fullscreen": false,
"height": 600,
"resizable": true,
"title": "Workspace",
"width": 800
}
]
}
}

20
tooling/cli/Cargo.lock generated
View File

@ -2005,6 +2005,15 @@ dependencies = [
"vcpkg",
]
[[package]]
name = "ordered-float"
version = "2.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7940cf2ca942593318d07fcf2596cdca60a85c9e7fab408a5e21a4f9dcd40d87"
dependencies = [
"num-traits",
]
[[package]]
name = "os_info"
version = "3.5.0"
@ -2755,6 +2764,16 @@ dependencies = [
"serde_derive",
]
[[package]]
name = "serde-value"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c"
dependencies = [
"ordered-float",
"serde",
]
[[package]]
name = "serde_derive"
version = "1.0.137"
@ -3152,6 +3171,7 @@ dependencies = [
"regex",
"semver",
"serde",
"serde-value",
"serde_json",
"shared_child",
"tauri-bundler",

View File

@ -79,6 +79,7 @@ html5ever = "0.25"
kuchiki = "0.8"
tokio = { version = "1", features = [ "macros", "sync" ] }
common-path = "1"
serde-value = "0.7.0"
[target."cfg(windows)".dependencies]
winapi = { version = "0.3", features = [ "handleapi", "processenv", "winbase", "wincon", "winnt" ] }

View File

@ -502,11 +502,77 @@ impl Rust {
}
}
// Taken from https://github.com/rust-lang/cargo/blob/70898e522116f6c23971e2a554b2dc85fd4c84cd/src/cargo/util/toml/mod.rs#L1008-L1065
/// Enum that allows for the parsing of `field.workspace = true` in a Cargo.toml
///
/// It allows for things to be inherited from a workspace or defined as needed
#[derive(Clone, Debug)]
pub enum MaybeWorkspace<T> {
Workspace(TomlWorkspaceField),
Defined(T),
}
impl<'de, T: Deserialize<'de>> serde::de::Deserialize<'de> for MaybeWorkspace<T> {
fn deserialize<D>(deserializer: D) -> Result<MaybeWorkspace<T>, D::Error>
where
D: serde::de::Deserializer<'de>,
{
let value = serde_value::Value::deserialize(deserializer)?;
if let Ok(workspace) = TomlWorkspaceField::deserialize(
serde_value::ValueDeserializer::<D::Error>::new(value.clone()),
) {
return Ok(MaybeWorkspace::Workspace(workspace));
}
T::deserialize(serde_value::ValueDeserializer::<D::Error>::new(value))
.map(MaybeWorkspace::Defined)
}
}
impl<T> MaybeWorkspace<T> {
fn resolve(
self,
label: &str,
get_ws_field: impl FnOnce() -> anyhow::Result<T>,
) -> anyhow::Result<T> {
match self {
MaybeWorkspace::Defined(value) => Ok(value),
MaybeWorkspace::Workspace(TomlWorkspaceField { workspace: true }) => {
get_ws_field().context(format!(
"error inheriting `{}` from workspace root manifest's `workspace.package.{}`",
label, label
))
}
MaybeWorkspace::Workspace(TomlWorkspaceField { workspace: false }) => Err(anyhow::anyhow!(
"`workspace=false` is unsupported for `package.{}`",
label,
)),
}
}
fn _as_defined(&self) -> Option<&T> {
match self {
MaybeWorkspace::Workspace(_) => None,
MaybeWorkspace::Defined(defined) => Some(defined),
}
}
}
#[derive(Deserialize, Clone, Debug)]
pub struct TomlWorkspaceField {
workspace: bool,
}
/// The `workspace` section of the app configuration (read from Cargo.toml).
#[derive(Clone, Debug, Deserialize)]
struct WorkspaceSettings {
/// the workspace members.
members: Option<Vec<String>>,
package: Option<WorkspacePackageSettings>,
}
#[derive(Clone, Debug, Deserialize)]
struct WorkspacePackageSettings {
/// the workspace members.
version: Option<String>,
}
#[derive(Clone, Debug, Deserialize)]
@ -521,7 +587,7 @@ pub struct CargoPackageSettings {
/// the package's name.
pub name: Option<String>,
/// the package's version.
pub version: Option<String>,
pub version: Option<MaybeWorkspace<String>>,
/// the package's description.
pub description: Option<String>,
/// the package's homepage.
@ -741,6 +807,9 @@ impl RustAppSettings {
.version
.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")
}),
description: cargo_package_settings
.description
@ -842,6 +911,27 @@ 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,