Skip to content
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

feat(build): support plugins that are defined in app crate #8781

Merged
merged 2 commits into from
Feb 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changes/inline-plugins.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'tauri-build': patch:enhance
---

Added `Attributes::plugin()` to register a plugin that is inlined in the application crate.
91 changes: 90 additions & 1 deletion core/tauri-build/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ use tauri_utils::{
};

use std::{
collections::HashMap,
env::var_os,
fs::copy,
path::{Path, PathBuf},
Expand Down Expand Up @@ -331,6 +332,41 @@ impl WindowsAttributes {
}
}

/// Definition of a plugin that is part of the Tauri application instead of having its own crate.
///
/// By default it generates a plugin manifest that parses permissions from the `permissions/$plugin-name` directory.
/// To change the glob pattern that is used to find permissions, use [`Self::permissions_path_pattern`].
///
/// To autogenerate permissions for each of the plugin commands, see [`Self::commands`].
#[derive(Debug, Default)]
pub struct InlinedPlugin {
commands: &'static [&'static str],
permissions_path_pattern: Option<&'static str>,
}

impl InlinedPlugin {
pub fn new() -> Self {
Self::default()
}

/// Define a list of commands that gets permissions autogenerated in the format of `allow-$command` and `deny-$command`
/// where $command is the command in kebab-case.
pub fn commands(mut self, commands: &'static [&'static str]) -> Self {
self.commands = commands;
self
}

/// Sets a glob pattern that is used to find the permissions of this inlined plugin.
///
/// **Note:** You must emit [rerun-if-changed] instructions for the plugin permissions directory.
///
/// By default it is `./permissions/$plugin-name/**/*`
pub fn permissions_path_pattern(mut self, pattern: &'static str) -> Self {
self.permissions_path_pattern.replace(pattern);
self
}
}

/// The attributes used on the build.
#[derive(Debug, Default)]
pub struct Attributes {
Expand All @@ -339,6 +375,7 @@ pub struct Attributes {
capabilities_path_pattern: Option<&'static str>,
#[cfg(feature = "codegen")]
codegen: Option<codegen::context::CodegenContext>,
inlined_plugins: HashMap<&'static str, InlinedPlugin>,
}

impl Attributes {
Expand All @@ -365,6 +402,14 @@ impl Attributes {
self
}

/// Adds the given plugin to the list of inlined plugins (a plugin that is part of your application).
///
/// See [`InlinedPlugin`] for more information.
pub fn plugin(mut self, name: &'static str, plugin: InlinedPlugin) -> Self {
self.inlined_plugins.insert(name, plugin);
self
}

#[cfg(feature = "codegen")]
#[cfg_attr(docsrs, doc(cfg(feature = "codegen")))]
#[must_use]
Expand Down Expand Up @@ -473,7 +518,51 @@ pub fn try_build(attributes: Attributes) -> Result<()> {
let out_dir = PathBuf::from(std::env::var("OUT_DIR").unwrap());

manifest::check(&config, &mut manifest)?;
let plugin_manifests = acl::get_plugin_manifests()?;
let mut plugin_manifests = acl::get_plugin_manifests()?;
for (name, plugin) in attributes.inlined_plugins {
let plugin_out_dir = out_dir.join("plugins").join(name);

let mut permission_files = if plugin.commands.is_empty() {
Vec::new()
} else {
tauri_utils::acl::build::autogenerate_command_permissions(
&plugin_out_dir,
plugin.commands,
"",
);
tauri_utils::acl::build::define_permissions(
&plugin_out_dir.join("*").to_string_lossy(),
name,
&plugin_out_dir,
)?
};

if let Some(pattern) = plugin.permissions_path_pattern {
permission_files.extend(tauri_utils::acl::build::define_permissions(
pattern,
name,
&plugin_out_dir,
)?);
} else {
let default_permissions_path = Path::new("permissions").join(name);
println!(
"cargo:rerun-if-changed={}",
default_permissions_path.display()
);
permission_files.extend(tauri_utils::acl::build::define_permissions(
&default_permissions_path
.join("**")
.join("*")
.to_string_lossy(),
name,
&plugin_out_dir,
)?);
}

let manifest = tauri_utils::acl::plugin::Manifest::new(permission_files, None);
plugin_manifests.insert(name.into(), manifest);
}

std::fs::write(
out_dir.join(PLUGIN_MANIFESTS_FILE_NAME),
serde_json::to_string(&plugin_manifests)?,
Expand Down
16 changes: 8 additions & 8 deletions examples/api/src-tauri/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 5 additions & 2 deletions examples/api/src-tauri/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ fn main() {
codegen = codegen.dev();
}

tauri_build::try_build(tauri_build::Attributes::new().codegen(codegen))
.expect("failed to run tauri-build");
tauri_build::try_build(tauri_build::Attributes::new().codegen(codegen).plugin(
"app-menu",
tauri_build::InlinedPlugin::new().commands(&["toggle", "popup"]),
))
.expect("failed to run tauri-build");
}
1 change: 1 addition & 0 deletions examples/api/src-tauri/capabilities/run-app.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"description": "permissions to run the app",
"windows": ["main", "main-*"],
"permissions": [
"app-menu:default",
"sample:allow-ping-scoped",
"sample:global-scope",
"path:default",
Expand Down
3 changes: 3 additions & 0 deletions examples/api/src-tauri/permissions/app-menu/default.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[default]
description = "Default permissions for the plugin"
permissions = ["allow-toggle", "allow-popup"]
34 changes: 0 additions & 34 deletions examples/api/src-tauri/src/cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,37 +29,3 @@ pub fn perform_request(endpoint: String, body: RequestBody) -> ApiResponse {
message: "message response".into(),
}
}

#[cfg(all(desktop, not(target_os = "macos")))]
#[command]
pub fn toggle_menu<R: tauri::Runtime>(window: tauri::Window<R>) {
if window.is_menu_visible().unwrap_or_default() {
let _ = window.hide_menu();
} else {
let _ = window.show_menu();
}
}

#[cfg(target_os = "macos")]
#[command]
pub fn toggle_menu<R: tauri::Runtime>(
app: tauri::AppHandle<R>,
app_menu: tauri::State<'_, crate::AppMenu<R>>,
) {
if let Some(menu) = app.remove_menu().unwrap() {
app_menu.0.lock().unwrap().replace(menu);
} else {
app
.set_menu(app_menu.0.lock().unwrap().clone().expect("no app menu"))
.unwrap();
}
}

#[cfg(desktop)]
#[command]
pub fn popup_context_menu<R: tauri::Runtime>(
window: tauri::Window<R>,
popup_menu: tauri::State<'_, crate::PopupMenu<R>>,
) {
window.popup_menu(&popup_menu.0).unwrap();
}
7 changes: 3 additions & 4 deletions examples/api/src-tauri/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

mod cmd;
#[cfg(desktop)]
mod menu_plugin;
#[cfg(desktop)]
mod tray;

use serde::Serialize;
Expand Down Expand Up @@ -46,6 +48,7 @@ pub fn run_app<R: Runtime, F: FnOnce(&App<R>) + Send + 'static>(
let handle = app.handle();
tray::create_tray(handle)?;
handle.plugin(tauri_plugin_cli::init())?;
handle.plugin(menu_plugin::init())?;
}

#[cfg(target_os = "macos")]
Expand Down Expand Up @@ -140,10 +143,6 @@ pub fn run_app<R: Runtime, F: FnOnce(&App<R>) + Send + 'static>(
.invoke_handler(tauri::generate_handler![
cmd::log_operation,
cmd::perform_request,
#[cfg(desktop)]
cmd::toggle_menu,
#[cfg(desktop)]
cmd::popup_context_menu
])
.build(tauri::tauri_build_context!())
.expect("error while building tauri application");
Expand Down
48 changes: 48 additions & 0 deletions examples/api/src-tauri/src/menu_plugin.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT

use tauri::{
command,
plugin::{Builder, TauriPlugin},
Runtime,
};

#[cfg(not(target_os = "macos"))]
#[command]
pub fn toggle<R: tauri::Runtime>(window: tauri::Window<R>) {
if window.is_menu_visible().unwrap_or_default() {
let _ = window.hide_menu();
} else {
let _ = window.show_menu();
}
}

#[cfg(target_os = "macos")]
#[command]
pub fn toggle<R: tauri::Runtime>(
app: tauri::AppHandle<R>,
app_menu: tauri::State<'_, crate::AppMenu<R>>,
) {
if let Some(menu) = app.remove_menu().unwrap() {
app_menu.0.lock().unwrap().replace(menu);
} else {
app
.set_menu(app_menu.0.lock().unwrap().clone().expect("no app menu"))
.unwrap();
}
}

#[command]
pub fn popup<R: tauri::Runtime>(
window: tauri::Window<R>,
popup_menu: tauri::State<'_, crate::PopupMenu<R>>,
) {
window.popup_menu(&popup_menu.0).unwrap();
}

pub fn init<R: Runtime>() -> TauriPlugin<R> {
Builder::new("app-menu")
.invoke_handler(tauri::generate_handler![popup, toggle])
.build()
}