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> {
52 let config_env_var_name = format!(
53 "TAURI_{}_PLUGIN_CONFIG",
54 name.to_uppercase().replace('-', "_")
55 );
56 if let Ok(config_str) = env::var(&config_env_var_name) {
57 println!("cargo:rerun-if-env-changed={config_env_var_name}");
58 serde_json::from_reader(Cursor::new(config_str))
59 .map(Some)
60 .expect("failed to parse configuration")
61 } else {
62 None
63 }
64}
65
66pub struct Builder<'a> {
82 commands: &'a [&'static str],
83 global_scope_schema: Option<schemars::Schema>,
84 global_api_script_path: Option<PathBuf>,
85 android_path: Option<PathBuf>,
86 ios_path: Option<PathBuf>,
87}
88
89impl<'a> Builder<'a> {
90 pub fn new(commands: &'a [&'static str]) -> Self {
100 Self {
101 commands,
102 global_scope_schema: None,
103 global_api_script_path: None,
104 android_path: None,
105 ios_path: None,
106 }
107 }
108
109 pub fn global_scope_schema(mut self, schema: schemars::Schema) -> Self {
111 self.global_scope_schema.replace(schema);
112 self
113 }
114
115 pub fn global_api_script_path<P: Into<PathBuf>>(mut self, path: P) -> Self {
119 self.global_api_script_path.replace(path.into());
120 self
121 }
122
123 pub fn android_path<P: Into<PathBuf>>(mut self, android_path: P) -> Self {
125 self.android_path.replace(android_path.into());
126 self
127 }
128
129 pub fn ios_path<P: Into<PathBuf>>(mut self, ios_path: P) -> Self {
131 self.ios_path.replace(ios_path.into());
132 self
133 }
134
135 pub fn build(self) {
137 if let Err(error) = self.try_build() {
138 println!("{}: {error:#}", env!("CARGO_PKG_NAME"));
139 std::process::exit(1);
140 }
141 }
142
143 pub fn try_build(self) -> Result<()> {
150 let name = build_var("CARGO_PKG_NAME")?;
152 if name.contains('_') {
153 anyhow::bail!("plugin names cannot contain underscores");
154 }
155 if RESERVED_PLUGIN_NAMES.contains(&name.as_str()) {
156 anyhow::bail!("plugin name `{name}` is reserved");
157 }
158
159 let out_dir = PathBuf::from(build_var("OUT_DIR")?);
160
161 let _links = std::env::var("CARGO_MANIFEST_LINKS").map_err(|_| Error::LinksMissing)?;
163
164 let docs_dir = Path::new("permissions").join(acl::build::AUTOGENERATED_FOLDER_NAME);
168
169 let _ = std::fs::remove_dir_all(docs_dir.join("commands"));
172
173 println!("cargo:rerun-if-changed=permissions");
174
175 let mut permission_files = Vec::new();
176
177 if !self.commands.is_empty() {
178 let commands_dir = out_dir
179 .join(acl::build::AUTOGENERATED_FOLDER_NAME)
180 .join("commands");
181 acl::build::autogenerate_command_permissions(&commands_dir, self.commands, "", false);
182 permission_files.extend(acl::build::collect_permission_files(
183 &commands_dir.join("*").to_string_lossy(),
184 |_| true,
185 )?);
186 }
187
188 permission_files.extend(acl::build::collect_permission_files(
189 "./permissions/**/*.*",
190 |_| true,
191 )?);
192
193 let permissions = acl::build::define_permissions_from_files(permission_files, &name, &out_dir)?;
194
195 if permissions.is_empty() {
196 let _ = std::fs::remove_file(format!(
197 "./permissions/{}/{}",
198 acl::PERMISSION_SCHEMAS_FOLDER_NAME,
199 acl::PERMISSION_SCHEMA_FILE_NAME
200 ));
201 let _ = std::fs::remove_file(docs_dir.join(acl::build::PERMISSION_DOCS_FILE_NAME));
202 } else {
203 acl::schema::generate_permissions_schema(&permissions, "./permissions")?;
204 std::fs::create_dir_all(&docs_dir).expect("unable to create permissions docs dir");
205 acl::build::generate_docs(
206 &permissions,
207 &docs_dir,
208 name.strip_prefix("tauri-plugin-").unwrap_or(&name),
209 )?;
210 }
211
212 let mut permissions_map = BTreeMap::new();
213 permissions_map.insert(name.clone(), permissions);
214 tauri_utils::acl::build::generate_allowed_commands(&out_dir, None, permissions_map)?;
215
216 if let Some(global_scope_schema) = self.global_scope_schema {
217 acl::build::define_global_scope_schema(global_scope_schema, &name, &out_dir)?;
218 }
219
220 if let Some(path) = self.global_api_script_path {
221 tauri_utils::plugin::define_global_api_script_path(&path);
222 }
223
224 mobile::setup(self.android_path, self.ios_path)?;
225
226 Ok(())
227 }
228}
229
230fn cfg_alias(alias: &str, has_feature: bool) {
231 println!("cargo:rustc-check-cfg=cfg({alias})");
232 if has_feature {
233 println!("cargo:rustc-cfg={alias}");
234 }
235}
236
237fn build_var(key: &'static str) -> Result<String, Error> {
239 std::env::var(key).map_err(|_| Error::BuildVar(key))
240}