tauri_plugin/build/
mod.rs1use std::{
6 collections::BTreeMap,
7 path::{Path, PathBuf},
8};
9
10use anyhow::Result;
11use tauri_utils::acl::{self, Error};
12
13pub mod mobile;
14
15use serde::de::DeserializeOwned;
16
17use std::{env, io::Cursor};
18
19const RESERVED_PLUGIN_NAMES: &[&str] = &["core", "tauri"];
20
21pub fn plugin_config<T: DeserializeOwned>(name: &str) -> Option<T> {
22 let config_env_var_name = format!(
23 "TAURI_{}_PLUGIN_CONFIG",
24 name.to_uppercase().replace('-', "_")
25 );
26 if let Ok(config_str) = env::var(&config_env_var_name) {
27 println!("cargo:rerun-if-env-changed={config_env_var_name}");
28 serde_json::from_reader(Cursor::new(config_str))
29 .map(Some)
30 .expect("failed to parse configuration")
31 } else {
32 None
33 }
34}
35
36pub struct Builder<'a> {
37 commands: &'a [&'static str],
38 global_scope_schema: Option<schemars::Schema>,
39 global_api_script_path: Option<PathBuf>,
40 android_path: Option<PathBuf>,
41 ios_path: Option<PathBuf>,
42}
43
44impl<'a> Builder<'a> {
45 pub fn new(commands: &'a [&'static str]) -> Self {
46 Self {
47 commands,
48 global_scope_schema: None,
49 global_api_script_path: None,
50 android_path: None,
51 ios_path: None,
52 }
53 }
54
55 pub fn global_scope_schema(mut self, schema: schemars::Schema) -> Self {
57 self.global_scope_schema.replace(schema);
58 self
59 }
60
61 pub fn global_api_script_path<P: Into<PathBuf>>(mut self, path: P) -> Self {
65 self.global_api_script_path.replace(path.into());
66 self
67 }
68
69 pub fn android_path<P: Into<PathBuf>>(mut self, android_path: P) -> Self {
71 self.android_path.replace(android_path.into());
72 self
73 }
74
75 pub fn ios_path<P: Into<PathBuf>>(mut self, ios_path: P) -> Self {
77 self.ios_path.replace(ios_path.into());
78 self
79 }
80
81 pub fn build(self) {
83 if let Err(error) = self.try_build() {
84 println!("{}: {error:#}", env!("CARGO_PKG_NAME"));
85 std::process::exit(1);
86 }
87 }
88
89 pub fn try_build(self) -> Result<()> {
96 let name = build_var("CARGO_PKG_NAME")?;
98 if name.contains('_') {
99 anyhow::bail!("plugin names cannot contain underscores");
100 }
101 if RESERVED_PLUGIN_NAMES.contains(&name.as_str()) {
102 anyhow::bail!("plugin name `{name}` is reserved");
103 }
104
105 let out_dir = PathBuf::from(build_var("OUT_DIR")?);
106
107 let _links = std::env::var("CARGO_MANIFEST_LINKS").map_err(|_| Error::LinksMissing)?;
109
110 let docs_dir = Path::new("permissions").join(acl::build::AUTOGENERATED_FOLDER_NAME);
114
115 let _ = std::fs::remove_dir_all(docs_dir.join("commands"));
118
119 println!("cargo:rerun-if-changed=permissions");
120
121 let mut permission_files = Vec::new();
122
123 if !self.commands.is_empty() {
124 let commands_dir = out_dir
125 .join(acl::build::AUTOGENERATED_FOLDER_NAME)
126 .join("commands");
127 acl::build::autogenerate_command_permissions(&commands_dir, self.commands, "", false);
128 permission_files.extend(acl::build::collect_permission_files(
129 &commands_dir.join("*").to_string_lossy(),
130 |_| true,
131 )?);
132 }
133
134 permission_files.extend(acl::build::collect_permission_files(
135 "./permissions/**/*.*",
136 |_| true,
137 )?);
138
139 let permissions = acl::build::define_permissions_from_files(permission_files, &name, &out_dir)?;
140
141 if permissions.is_empty() {
142 let _ = std::fs::remove_file(format!(
143 "./permissions/{}/{}",
144 acl::PERMISSION_SCHEMAS_FOLDER_NAME,
145 acl::PERMISSION_SCHEMA_FILE_NAME
146 ));
147 let _ = std::fs::remove_file(docs_dir.join(acl::build::PERMISSION_DOCS_FILE_NAME));
148 } else {
149 acl::schema::generate_permissions_schema(&permissions, "./permissions")?;
150 std::fs::create_dir_all(&docs_dir).expect("unable to create permissions docs dir");
151 acl::build::generate_docs(
152 &permissions,
153 &docs_dir,
154 name.strip_prefix("tauri-plugin-").unwrap_or(&name),
155 )?;
156 }
157
158 let mut permissions_map = BTreeMap::new();
159 permissions_map.insert(name.clone(), permissions);
160 tauri_utils::acl::build::generate_allowed_commands(&out_dir, None, permissions_map)?;
161
162 if let Some(global_scope_schema) = self.global_scope_schema {
163 acl::build::define_global_scope_schema(global_scope_schema, &name, &out_dir)?;
164 }
165
166 if let Some(path) = self.global_api_script_path {
167 tauri_utils::plugin::define_global_api_script_path(&path);
168 }
169
170 mobile::setup(self.android_path, self.ios_path)?;
171
172 Ok(())
173 }
174}
175
176fn cfg_alias(alias: &str, has_feature: bool) {
177 println!("cargo:rustc-check-cfg=cfg({alias})");
178 if has_feature {
179 println!("cargo:rustc-cfg={alias}");
180 }
181}
182
183fn build_var(key: &'static str) -> Result<String, Error> {
185 std::env::var(key).map_err(|_| Error::BuildVar(key))
186}