feat: build without proc macros (#1226)
Co-authored-by: Lucas Nogueira <lucas@tauri.studio>
3
.gitignore
vendored
@@ -85,8 +85,5 @@ target
|
||||
# todo: needs a proper fic
|
||||
/cli/tauri.js/tauri.conf.js
|
||||
|
||||
# doing this because the task-runner (`mask prepare`) will clone gh:tauri-apps/examples
|
||||
/examples
|
||||
|
||||
# ignore frida handlers
|
||||
__handlers__/
|
||||
|
||||
20
Cargo.toml
@@ -4,7 +4,21 @@ members = [
|
||||
"tauri-api",
|
||||
"tauri-macros",
|
||||
"tauri-utils",
|
||||
|
||||
# core
|
||||
"core/tauri-build",
|
||||
"core/tauri-codegen",
|
||||
|
||||
# examples
|
||||
"examples/api/src-tauri",
|
||||
"examples/helloworld/src-tauri",
|
||||
"examples/multiwindow/src-tauri",
|
||||
]
|
||||
exclude = [
|
||||
"examples",
|
||||
]
|
||||
|
||||
# default to small, optimized workspace release binaries
|
||||
[profile.release]
|
||||
panic = "abort"
|
||||
codegen-units = 1
|
||||
lto = true
|
||||
incremental = false
|
||||
opt-level = "s"
|
||||
|
||||
@@ -135,8 +135,10 @@ pub fn get_bundler_settings(config: &Config, debug: bool) -> crate::Result<Bundl
|
||||
}
|
||||
}
|
||||
|
||||
if binaries.len() == 1 {
|
||||
binaries.get_mut(0).unwrap().set_main(true);
|
||||
match binaries.len() {
|
||||
0 => binaries.push(BundleBinary::new(package.name.clone(), true)),
|
||||
1 => binaries.get_mut(0).unwrap().set_main(true),
|
||||
_ => {}
|
||||
}
|
||||
|
||||
Ok(BundlerSettings {
|
||||
|
||||
@@ -77,29 +77,34 @@ Run \`tauri init --force template\` to overwrite.`)
|
||||
if (!force) return false
|
||||
}
|
||||
|
||||
const resolveTauriPath = (tauriPath: string): string => {
|
||||
const resolveTauriPath = (tauriPath: string, crate: string): string => {
|
||||
const resolvedPath = isAbsolute(tauriPath)
|
||||
? join(tauriPath, 'tauri') // we received a full path as argument
|
||||
: join('..', tauriPath, 'tauri') // we received a relative path
|
||||
? join(tauriPath, crate) // we received a full path as argument
|
||||
: join('..', tauriPath, crate) // we received a relative path
|
||||
return resolvedPath.replace(/\\/g, '/')
|
||||
}
|
||||
|
||||
const resolveCurrentTauriVersion = (): string => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-var-requires, @typescript-eslint/no-unsafe-member-access
|
||||
const tauriManifest = require('../../../../tauri/Cargo.toml') as CargoManifest
|
||||
const resolveCurrentTauriVersion = (crate: string): string => {
|
||||
const manifestPath = `../../../../${crate}/Cargo.toml`
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-var-requires, @typescript-eslint/no-unsafe-member-access, security/detect-non-literal-require
|
||||
const tauriManifest = require(manifestPath) as CargoManifest
|
||||
const version = tauriManifest.package.version
|
||||
return version.substring(0, version.lastIndexOf('.'))
|
||||
}
|
||||
|
||||
const tauriDep = tauriPath
|
||||
? `{ path = "${resolveTauriPath(tauriPath)}" }`
|
||||
: `{ version = "${resolveCurrentTauriVersion()}" }`
|
||||
? `{ path = "${resolveTauriPath(tauriPath, 'tauri')}" }`
|
||||
: `{ version = "${resolveCurrentTauriVersion('tauri')}" }`
|
||||
const tauriBuildDep = tauriPath
|
||||
? `{ path = "${resolveTauriPath(tauriPath, 'core/tauri-build')}" }`
|
||||
: `{ version = "${resolveCurrentTauriVersion('core/tauri-build')}" }`
|
||||
|
||||
removeSync(dir)
|
||||
copyTemplates({
|
||||
source: resolve(__dirname, '../../templates/src-tauri'),
|
||||
scope: {
|
||||
tauriDep
|
||||
tauriDep,
|
||||
tauriBuildDep
|
||||
},
|
||||
target: dir
|
||||
})
|
||||
|
||||
@@ -11,17 +11,17 @@ build = "src/build.rs"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[build-dependencies]
|
||||
tauri-build = <%= tauriBuildDep %>
|
||||
|
||||
[dependencies]
|
||||
serde_json = "1.0"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
tauri = <%= tauriDep || `{ version = "0.5" }` %>
|
||||
tauri = <%= tauriDep %>
|
||||
|
||||
[target."cfg(windows)".build-dependencies]
|
||||
winres = "0.1"
|
||||
|
||||
[features]
|
||||
default = [ "custom-protocol" ]
|
||||
custom-protocol = [ "tauri/custom-protocol" ]
|
||||
|
||||
[[bin]]
|
||||
name = "app"
|
||||
path = "src/main.rs"
|
||||
|
||||
@@ -1,16 +1,3 @@
|
||||
#[cfg(windows)]
|
||||
extern crate winres;
|
||||
|
||||
#[cfg(windows)]
|
||||
fn main() {
|
||||
if std::path::Path::new("icons/icon.ico").exists() {
|
||||
let mut res = winres::WindowsResource::new();
|
||||
res.set_icon_with_id("icons/icon.ico", "32512");
|
||||
res.compile().expect("Unable to find visual studio tools");
|
||||
} else {
|
||||
panic!("No Icon.ico found. Please add one or check the path");
|
||||
}
|
||||
tauri_build::build()
|
||||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
fn main() {}
|
||||
|
||||
@@ -3,12 +3,8 @@
|
||||
windows_subsystem = "windows"
|
||||
)]
|
||||
|
||||
#[derive(tauri::FromTauriContext)]
|
||||
struct Context;
|
||||
|
||||
fn main() {
|
||||
tauri::AppBuilder::<Context>::new()
|
||||
.build()
|
||||
.unwrap()
|
||||
.run();
|
||||
let context = tauri::generate_tauri_context!();
|
||||
|
||||
tauri::AppBuilder::default().build(context).run();
|
||||
}
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
use tauri::ApplicationDispatcherExt;
|
||||
|
||||
#[derive(tauri::FromTauriContext)]
|
||||
struct Context;
|
||||
|
||||
fn main() {
|
||||
tauri::AppBuilder::<tauri::flavors::Wry, Context>::new()
|
||||
let context = tauri::generate_tauri_context!();
|
||||
|
||||
tauri::AppBuilder::default()
|
||||
.setup(|webview_manager| async move {
|
||||
let mut webview_manager_ = webview_manager.clone();
|
||||
tauri::event::listen(String::from("hello"), move |_| {
|
||||
@@ -24,7 +23,6 @@ fn main() {
|
||||
webview_manager.close().unwrap();
|
||||
}
|
||||
})
|
||||
.build()
|
||||
.unwrap()
|
||||
.build(context)
|
||||
.run();
|
||||
}
|
||||
|
||||
30
core/tauri-build/Cargo.toml
Normal file
@@ -0,0 +1,30 @@
|
||||
[package]
|
||||
name = "tauri-build"
|
||||
version = "0.1.0"
|
||||
authors = [ "Tauri Community" ]
|
||||
categories = [ "gui", "web-programming" ]
|
||||
license = "MIT"
|
||||
homepage = "https://tauri.studio"
|
||||
repository = "https://github.com/tauri-apps/tauri/tree/dev/core/tauri-build"
|
||||
description = "build time code to pair with https://crates.io/crates/tauri"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1"
|
||||
|
||||
# context dependencies
|
||||
proc-macro2 = "1"
|
||||
quote = "1"
|
||||
tauri-codegen = { path = "../tauri-codegen", optional = true }
|
||||
|
||||
[target."cfg(windows)".dependencies]
|
||||
winres = "0.1"
|
||||
|
||||
[features]
|
||||
# keep context non-default to prevent some relatively large deps being pulled
|
||||
codegen = ["tauri-codegen"]
|
||||
|
||||
# enable feature flags on https://docs.rs/tauri-build
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
rustdoc-args = ["--cfg", "doc_cfg"]
|
||||
134
core/tauri-build/src/codegen/context.rs
Normal file
@@ -0,0 +1,134 @@
|
||||
use anyhow::{Context, Result};
|
||||
use proc_macro2::Ident;
|
||||
use quote::format_ident;
|
||||
use std::{
|
||||
env::var,
|
||||
fs::{create_dir_all, File},
|
||||
io::{BufWriter, Write},
|
||||
path::PathBuf,
|
||||
};
|
||||
use tauri_codegen::{context_codegen, ContextData};
|
||||
|
||||
/// A builder for generating a Tauri application context during compile time.
|
||||
///
|
||||
/// Meant to be used with [`tauri::include_codegen_context!`] inside your application code.
|
||||
///
|
||||
/// [`tauri::include_codegen_context!`]: https://docs.rs/tauri/0.12/tauri/macro.include_codegen_context.html
|
||||
#[cfg_attr(doc_cfg, doc(cfg(feature = "codegen")))]
|
||||
#[derive(Debug)]
|
||||
pub struct CodegenContext {
|
||||
config_path: PathBuf,
|
||||
struct_ident: Ident,
|
||||
out_file: PathBuf,
|
||||
}
|
||||
|
||||
impl Default for CodegenContext {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
config_path: PathBuf::from("tauri.conf.json"),
|
||||
struct_ident: format_ident!("TauriBuildCodegenContext"),
|
||||
out_file: PathBuf::from("tauri-build-context.rs"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CodegenContext {
|
||||
/// Create a new [`CodegenContext`] builder that is already filled with the default options.
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
/// Set the path to the `tauri.conf.json` (relative to the package's directory).
|
||||
///
|
||||
/// This defaults to a file called `tauri.conf.json` inside of the current working directory of
|
||||
/// the package compiling; does not need to be set manually if that config file is in the same
|
||||
/// directory as your `Cargo.toml`.
|
||||
pub fn config_path(mut self, config_path: impl Into<PathBuf>) -> Self {
|
||||
self.config_path = config_path.into();
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the name of the generated struct.
|
||||
///
|
||||
/// Don't set this if you are using [`tauri::include_codegen_context!`] as that helper macro
|
||||
/// expects the default value. This option can be useful if you are not using the helper and
|
||||
/// instead using [`std::include!`] on the generated code yourself.
|
||||
///
|
||||
/// Defaults to `TauriBuildCodegenContext`.
|
||||
///
|
||||
/// [`tauri::include_codegen_context!`]: https://docs.rs/tauri/0.12/tauri/macro.include_codegen_context.html
|
||||
pub fn struct_ident(mut self, ident: impl AsRef<str>) -> Self {
|
||||
self.struct_ident = format_ident!("{}", ident.as_ref());
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the output file's path.
|
||||
///
|
||||
/// **Note:** This path should be relative to the `OUT_DIR`.
|
||||
///
|
||||
/// Don't set this if you are using [`tauri::include_codegen_context!`] as that helper macro
|
||||
/// expects the default value. This option can be useful if you are not using the helper and
|
||||
/// instead using [`std::include!`] on the generated code yourself.
|
||||
///
|
||||
/// Defaults to `tauri-build-context.rs`.
|
||||
///
|
||||
/// [`tauri::include_codegen_context!`]: https://docs.rs/tauri/0.12/tauri/macro.include_codegen_context.html
|
||||
pub fn out_file(mut self, filename: PathBuf) -> Self {
|
||||
self.out_file = filename;
|
||||
self
|
||||
}
|
||||
|
||||
/// Generate the code and write it to the output file - returning the path it was saved to.
|
||||
///
|
||||
/// Unless you are doing something special with this builder, you don't need to do anything with
|
||||
/// the returned output path.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// If any parts of the codegen fail, this will panic with the related error message. This is
|
||||
/// typically desirable when running inside a build script; see [`Self::try_build`] for no panics.
|
||||
pub fn build(self) -> PathBuf {
|
||||
match self.try_build() {
|
||||
Ok(out) => out,
|
||||
Err(error) => panic!("Error found during Codegen::build: {}", error),
|
||||
}
|
||||
}
|
||||
|
||||
/// Non-panicking [`Self::build`]
|
||||
pub fn try_build(self) -> Result<PathBuf> {
|
||||
let (config, config_parent) = tauri_codegen::get_config(&self.config_path)?;
|
||||
let code = context_codegen(ContextData {
|
||||
config,
|
||||
config_parent,
|
||||
struct_ident: self.struct_ident.clone(),
|
||||
})?;
|
||||
|
||||
// get the full output file path
|
||||
let out = var("OUT_DIR")
|
||||
.map(PathBuf::from)
|
||||
.map(|path| path.join(&self.out_file))
|
||||
.with_context(|| "unable to find OUT_DIR during tauri-build")?;
|
||||
|
||||
// make sure any nested directories in OUT_DIR are created
|
||||
let parent = out.parent().with_context(|| {
|
||||
"`Codegen` could not find the parent to `out_file` while creating the file"
|
||||
})?;
|
||||
create_dir_all(parent)?;
|
||||
|
||||
let mut file = File::create(&out).map(BufWriter::new).with_context(|| {
|
||||
format!(
|
||||
"Unable to create output file during tauri-build {}",
|
||||
out.display()
|
||||
)
|
||||
})?;
|
||||
|
||||
writeln!(&mut file, "{}", code).with_context(|| {
|
||||
format!(
|
||||
"Unable to write tokenstream to out file during tauri-build {}",
|
||||
out.display()
|
||||
)
|
||||
})?;
|
||||
|
||||
Ok(out)
|
||||
}
|
||||
}
|
||||
1
core/tauri-build/src/codegen/mod.rs
Normal file
@@ -0,0 +1 @@
|
||||
pub(crate) mod context;
|
||||
56
core/tauri-build/src/lib.rs
Normal file
@@ -0,0 +1,56 @@
|
||||
#![cfg_attr(doc_cfg, feature(doc_cfg))]
|
||||
|
||||
pub use anyhow::Result;
|
||||
|
||||
#[cfg(feature = "codegen")]
|
||||
mod codegen;
|
||||
|
||||
#[cfg(feature = "codegen")]
|
||||
pub use codegen::context::CodegenContext;
|
||||
|
||||
/// Run all build time helpers for your Tauri Application.
|
||||
///
|
||||
/// The current helpers include the following:
|
||||
/// * Generates a Windows Resource file when targeting Windows.
|
||||
///
|
||||
/// # Platforms
|
||||
///
|
||||
/// [`build()`] should be called inside of `build.rs` regardless of the platform:
|
||||
/// * New helpers may target more platforms in the future.
|
||||
/// * Platform specific code is handled by the helpers automatically.
|
||||
/// * A build script is required in order to activate some cargo environmental variables that are
|
||||
/// used when generating code and embedding assets - so [`build()`] may as well be called.
|
||||
///
|
||||
/// In short, this is saying don't put the call to [`build()`] behind a `#[cfg(windows)]`.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// If any of the build time helpers fail, they will [`std::panic!`] with the related error message.
|
||||
/// This is typically desirable when running inside a build script; see [`try_build`] for no panics.
|
||||
pub fn build() {
|
||||
if let Err(error) = try_build() {
|
||||
panic!("error found during tauri-build: {}", error);
|
||||
}
|
||||
}
|
||||
|
||||
/// Non-panicking [`build()`].
|
||||
pub fn try_build() -> Result<()> {
|
||||
#[cfg(windows)]
|
||||
{
|
||||
use anyhow::{anyhow, Context};
|
||||
use std::path::Path;
|
||||
use winres::WindowsResource;
|
||||
|
||||
if Path::new("icons/icon.ico").exists() {
|
||||
let mut res = WindowsResource::new();
|
||||
res.set_icon_with_id("icons/icon.ico", "32512");
|
||||
res.compile().with_context(|| {
|
||||
"failed to compile icons/icon.ico into a Windows Resource file during tauri-build"
|
||||
})?;
|
||||
} else {
|
||||
return Err(anyhow!("no icons/icon.ico file found; required for generating a Windows Resource file during tauri-build"));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
20
core/tauri-codegen/Cargo.toml
Normal file
@@ -0,0 +1,20 @@
|
||||
[package]
|
||||
name = "tauri-codegen"
|
||||
version = "0.1.0"
|
||||
authors = [ "Tauri Community" ]
|
||||
categories = [ "gui", "web-programming" ]
|
||||
license = "MIT"
|
||||
homepage = "https://tauri.studio"
|
||||
repository = "https://github.com/tauri-apps/tauri/tree/dev/core/tauri-codegen"
|
||||
description = "code generation meant to be consumed inside of `tauri` through `tauri-build` or `tauri-macros`"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
proc-macro2 = "1"
|
||||
quote = "1"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
tauri-api = { path = "../../tauri-api", features = ["build"] }
|
||||
thiserror = "1"
|
||||
walkdir = "2"
|
||||
zstd = "0.6"
|
||||
69
core/tauri-codegen/src/context.rs
Normal file
@@ -0,0 +1,69 @@
|
||||
use crate::embedded_assets::{EmbeddedAssets, EmbeddedAssetsError};
|
||||
use proc_macro2::{Ident, TokenStream};
|
||||
use quote::quote;
|
||||
use std::path::PathBuf;
|
||||
use tauri_api::config::Config;
|
||||
|
||||
/// Necessary data needed by [`codegen_context`] to generate code for a Tauri application context.
|
||||
pub struct ContextData {
|
||||
pub config: Config,
|
||||
pub config_parent: PathBuf,
|
||||
pub struct_ident: Ident,
|
||||
}
|
||||
|
||||
/// Build an `AsTauriContext` implementation for including in application code.
|
||||
pub fn context_codegen(data: ContextData) -> Result<TokenStream, EmbeddedAssetsError> {
|
||||
let ContextData {
|
||||
config,
|
||||
config_parent,
|
||||
struct_ident,
|
||||
} = data;
|
||||
let dist_dir = config_parent.join(&config.build.dist_dir);
|
||||
|
||||
// generate the assets inside the dist dir into a perfect hash function
|
||||
let assets = EmbeddedAssets::new(&dist_dir)?;
|
||||
|
||||
// handle default window icons for Windows targets
|
||||
let default_window_icon = if cfg!(windows) {
|
||||
let icon_path = config_parent.join("icons/icon.ico").display().to_string();
|
||||
quote!(Some(include_bytes!(#icon_path)))
|
||||
} else {
|
||||
quote!(None)
|
||||
};
|
||||
|
||||
let tauri_script_path = dist_dir.join("__tauri.js").display().to_string();
|
||||
|
||||
// double braces are purposeful to force the code into a block expression
|
||||
Ok(quote! {{
|
||||
use ::tauri::api::private::{OnceCell, AsTauriContext};
|
||||
|
||||
static CONFIG: OnceCell<::tauri::api::config::Config>= OnceCell::new();
|
||||
|
||||
/// Generated by `tauri-codegen`.
|
||||
struct #struct_ident;
|
||||
|
||||
impl AsTauriContext for #struct_ident {
|
||||
/// Return a static reference to the config we parsed at build time
|
||||
fn config() -> &'static ::tauri::api::config::Config {
|
||||
CONFIG.get_or_init(|| #config)
|
||||
}
|
||||
|
||||
/// Inject assets we generated during build time
|
||||
fn assets() -> &'static ::tauri::api::assets::EmbeddedAssets {
|
||||
#assets
|
||||
}
|
||||
|
||||
/// Make the __tauri.js a dependency for the compiler
|
||||
fn raw_tauri_script() -> &'static str {
|
||||
include_str!(#tauri_script_path)
|
||||
}
|
||||
|
||||
/// Default window icon to set automatically if exists
|
||||
fn default_window_icon() -> Option<&'static [u8]> {
|
||||
#default_window_icon
|
||||
}
|
||||
}
|
||||
|
||||
#struct_ident {}
|
||||
}})
|
||||
}
|
||||
135
core/tauri-codegen/src/embedded_assets.rs
Normal file
@@ -0,0 +1,135 @@
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::{quote, ToTokens, TokenStreamExt};
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
env::var,
|
||||
fs::File,
|
||||
io::BufReader,
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
use tauri_api::assets::AssetKey;
|
||||
use thiserror::Error;
|
||||
use walkdir::WalkDir;
|
||||
|
||||
/// (key, (original filepath, compressed bytes))
|
||||
type Asset = (AssetKey, (String, Vec<u8>));
|
||||
|
||||
/// All possible errors while reading and compressing an [`EmbeddedAssets`] directory
|
||||
#[derive(Debug, Error)]
|
||||
pub enum EmbeddedAssetsError {
|
||||
#[error("failed to read asset at {path} because {error}")]
|
||||
AssetRead {
|
||||
path: PathBuf,
|
||||
error: std::io::Error,
|
||||
},
|
||||
|
||||
#[error("failed to write asset from {path} to Vec<u8> because {error}")]
|
||||
AssetWrite {
|
||||
path: PathBuf,
|
||||
error: std::io::Error,
|
||||
},
|
||||
|
||||
#[error("invalid prefix {prefix} used while including path {path}")]
|
||||
PrefixInvalid { prefix: PathBuf, path: PathBuf },
|
||||
|
||||
#[error("failed to walk directory {path} because {error}")]
|
||||
Walkdir {
|
||||
path: PathBuf,
|
||||
error: walkdir::Error,
|
||||
},
|
||||
}
|
||||
|
||||
/// Represent a directory of assets that are compressed and embedded.
|
||||
///
|
||||
/// This is the compile time generation of [`tauri_api::assets::Assets`] from a directory. Assets
|
||||
/// from the directory are added as compiler dependencies by dummy including the original,
|
||||
/// uncompressed assets.
|
||||
///
|
||||
/// The assets are compressed during this runtime, and can only be represented as a [`TokenStream`]
|
||||
/// through [`ToTokens`]. The generated code is meant to be injected into an application to include
|
||||
/// the compressed assets in that application's binary.
|
||||
pub struct EmbeddedAssets(HashMap<AssetKey, (String, Vec<u8>)>);
|
||||
|
||||
impl EmbeddedAssets {
|
||||
/// Compress a directory of assets, ready to be generated into a [`tauri_api::assets::Assets`].
|
||||
pub fn new(path: &Path) -> Result<Self, EmbeddedAssetsError> {
|
||||
WalkDir::new(&path)
|
||||
.follow_links(true)
|
||||
.into_iter()
|
||||
.filter_map(|entry| match entry {
|
||||
// we only serve files, not directory listings
|
||||
Ok(entry) if entry.file_type().is_dir() => None,
|
||||
|
||||
// compress all files encountered
|
||||
Ok(entry) => Some(Self::compress_file(path, entry.path())),
|
||||
|
||||
// pass down error through filter to fail when encountering any error
|
||||
Err(error) => Some(Err(EmbeddedAssetsError::Walkdir {
|
||||
path: path.to_owned(),
|
||||
error,
|
||||
})),
|
||||
})
|
||||
.collect::<Result<_, _>>()
|
||||
.map(Self)
|
||||
}
|
||||
|
||||
/// Use highest compression level for release, the fastest one for everything else
|
||||
fn compression_level() -> i32 {
|
||||
match var("PROFILE").as_ref().map(String::as_str) {
|
||||
Ok("release") => 22,
|
||||
_ => -5,
|
||||
}
|
||||
}
|
||||
|
||||
/// Compress a file and spit out the information in a [`HashMap`] friendly form.
|
||||
fn compress_file(prefix: &Path, path: &Path) -> Result<Asset, EmbeddedAssetsError> {
|
||||
let reader =
|
||||
File::open(&path)
|
||||
.map(BufReader::new)
|
||||
.map_err(|error| EmbeddedAssetsError::AssetRead {
|
||||
path: path.to_owned(),
|
||||
error,
|
||||
})?;
|
||||
|
||||
// entirely read compressed asset into bytes
|
||||
let bytes = zstd::encode_all(reader, Self::compression_level()).map_err(|error| {
|
||||
EmbeddedAssetsError::AssetWrite {
|
||||
path: path.to_owned(),
|
||||
error,
|
||||
}
|
||||
})?;
|
||||
|
||||
// get a key to the asset path without the asset directory prefix
|
||||
let key = path
|
||||
.strip_prefix(prefix)
|
||||
.map(AssetKey::from) // format the path for use in assets
|
||||
.map_err(|_| EmbeddedAssetsError::PrefixInvalid {
|
||||
prefix: prefix.to_owned(),
|
||||
path: path.to_owned(),
|
||||
})?;
|
||||
|
||||
Ok((key, (path.display().to_string(), bytes)))
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for EmbeddedAssets {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
let mut map = TokenStream::new();
|
||||
for (key, (original, bytes)) in &self.0 {
|
||||
let key: &str = key.as_ref();
|
||||
|
||||
// add original asset as a compiler dependency, rely on dead code elimination to clean it up
|
||||
map.append_all(quote!(#key => {
|
||||
const _: &[u8] = include_bytes!(#original);
|
||||
&[#(#bytes),*]
|
||||
},));
|
||||
}
|
||||
|
||||
// we expect phf related items to be in path when generating the path code
|
||||
tokens.append_all(quote! {
|
||||
use ::tauri::api::assets::{EmbeddedAssets, phf, phf::phf_map};
|
||||
static ASSETS: EmbeddedAssets = EmbeddedAssets::from_zstd(phf_map! { #map });
|
||||
&ASSETS
|
||||
});
|
||||
}
|
||||
}
|
||||
80
core/tauri-codegen/src/lib.rs
Normal file
@@ -0,0 +1,80 @@
|
||||
pub use context::{context_codegen, ContextData};
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
fs::File,
|
||||
io::BufReader,
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
pub use tauri_api::config::Config;
|
||||
use thiserror::Error;
|
||||
|
||||
mod context;
|
||||
pub mod embedded_assets;
|
||||
|
||||
/// Represents all the errors that can happen while reading the config.
|
||||
#[derive(Debug, Error)]
|
||||
pub enum ConfigError {
|
||||
#[error("unable to access current working directory: {0}")]
|
||||
CurrentDir(std::io::Error),
|
||||
|
||||
// this error should be "impossible" because we use std::env::current_dir() - cover it anyways
|
||||
#[error("Tauri config file has no parent, this shouldn't be possible. file an issue on https://github.com/tauri-apps/tauri - target {0}")]
|
||||
Parent(PathBuf),
|
||||
|
||||
#[error("unable to parse inline TAURI_CONFIG env var: {0}")]
|
||||
FormatInline(serde_json::Error),
|
||||
|
||||
#[error("unable to parse Tauri config file at {path} because {error}")]
|
||||
Format {
|
||||
path: PathBuf,
|
||||
error: serde_json::Error,
|
||||
},
|
||||
|
||||
#[error("unable to read Tauri config file at {path} because {error}")]
|
||||
Io {
|
||||
path: PathBuf,
|
||||
error: std::io::Error,
|
||||
},
|
||||
}
|
||||
|
||||
/// Get the [`Config`] from the `TAURI_CONFIG` environmental variable, or read from the passed path.
|
||||
///
|
||||
/// If the passed path is relative, it should be relative to the current working directory of the
|
||||
/// compiling crate.
|
||||
pub fn get_config(path: &Path) -> Result<(Config, PathBuf), ConfigError> {
|
||||
let path = if path.is_relative() {
|
||||
let cwd = std::env::current_dir().map_err(ConfigError::CurrentDir)?;
|
||||
Cow::Owned(cwd.join(path))
|
||||
} else {
|
||||
Cow::Borrowed(path)
|
||||
};
|
||||
|
||||
// in the future we may want to find a way to not need the TAURI_CONFIG env var so that
|
||||
// it is impossible for the content of two separate configs to get mixed up. The chances are
|
||||
// already unlikely unless the developer goes out of their way to run the cli on a different
|
||||
// project than the target crate.
|
||||
let config = if let Ok(env) = std::env::var("TAURI_CONFIG") {
|
||||
serde_json::from_str(&env).map_err(ConfigError::FormatInline)?
|
||||
} else {
|
||||
File::open(&path)
|
||||
.map_err(|error| ConfigError::Io {
|
||||
path: path.clone().into_owned(),
|
||||
error,
|
||||
})
|
||||
.map(BufReader::new)
|
||||
.and_then(|file| {
|
||||
serde_json::from_reader(file).map_err(|error| ConfigError::Format {
|
||||
path: path.clone().into_owned(),
|
||||
error,
|
||||
})
|
||||
})?
|
||||
};
|
||||
|
||||
// this should be impossible because of the use of `current_dir()` above, but handle it anyways
|
||||
let parent = path
|
||||
.parent()
|
||||
.map(ToOwned::to_owned)
|
||||
.ok_or_else(|| ConfigError::Parent(path.into_owned()))?;
|
||||
|
||||
Ok((config, parent))
|
||||
}
|
||||
@@ -5,7 +5,7 @@
|
||||
"build": "rollup -c",
|
||||
"dev": "rollup -c -w",
|
||||
"start": "sirv public",
|
||||
"tauri": "node ../../../cli/tauri.js/bin/tauri"
|
||||
"tauri": "node ../../cli/tauri.js/bin/tauri"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@rollup/plugin-commonjs": "17.1.0",
|
||||
@@ -17,7 +17,7 @@
|
||||
"svelte": "3.35.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@tauri-apps/api": "link:../../../api",
|
||||
"@tauri-apps/api": "link:../../api",
|
||||
"sirv-cli": "1.0.11"
|
||||
}
|
||||
}
|
||||
332
examples/api/public/__tauri.js
Normal file
17
examples/api/public/build/bundle.js
Normal file
1
examples/api/public/build/bundle.js.map
Normal file
|
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 3.1 KiB |
|
Before Width: | Height: | Size: 88 KiB After Width: | Height: | Size: 88 KiB |
|
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 35 KiB |
17
examples/api/src-tauri/Cargo.toml
Normal file
@@ -0,0 +1,17 @@
|
||||
[package]
|
||||
name = "api"
|
||||
version = "0.1.0"
|
||||
description = "An example Tauri Application showcasing the api"
|
||||
edition = "2018"
|
||||
|
||||
[build-dependencies]
|
||||
tauri-build = { path = "../../../core/tauri-build" }
|
||||
|
||||
[dependencies]
|
||||
serde_json = "1.0"
|
||||
serde = { version = "1.0", features = [ "derive" ] }
|
||||
tauri = { path = "../../../tauri", features =["api-all", "cli"]}
|
||||
|
||||
[features]
|
||||
default = [ "custom-protocol" ]
|
||||
custom-protocol = [ "tauri/custom-protocol" ]
|
||||
3
examples/api/src-tauri/build.rs
Normal file
@@ -0,0 +1,3 @@
|
||||
fn main() {
|
||||
tauri_build::build()
|
||||
}
|
||||
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 37 KiB |
|
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 41 KiB After Width: | Height: | Size: 41 KiB |
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 45 KiB After Width: | Height: | Size: 45 KiB |
|
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 3.7 KiB |
|
Before Width: | Height: | Size: 7.2 KiB After Width: | Height: | Size: 7.2 KiB |
|
Before Width: | Height: | Size: 9.6 KiB After Width: | Height: | Size: 9.6 KiB |
|
Before Width: | Height: | Size: 4.4 KiB After Width: | Height: | Size: 4.4 KiB |
|
Before Width: | Height: | Size: 56 KiB After Width: | Height: | Size: 56 KiB |
|
Before Width: | Height: | Size: 88 KiB After Width: | Height: | Size: 88 KiB |
@@ -12,11 +12,10 @@ struct Reply {
|
||||
data: String,
|
||||
}
|
||||
|
||||
#[derive(tauri::FromTauriContext)]
|
||||
struct Context;
|
||||
|
||||
fn main() {
|
||||
tauri::AppBuilder::<Context>::new()
|
||||
let context = tauri::generate_tauri_context!();
|
||||
|
||||
tauri::AppBuilder::default()
|
||||
.setup(|webview_manager| async move {
|
||||
let dispatcher = webview_manager.current_webview().await.unwrap();
|
||||
let dispatcher_ = dispatcher.clone();
|
||||
@@ -35,7 +34,6 @@ fn main() {
|
||||
cmd::log_operation,
|
||||
cmd::perform_request
|
||||
])
|
||||
.build()
|
||||
.unwrap()
|
||||
.build(context)
|
||||
.run();
|
||||
}
|
||||
@@ -62,8 +62,9 @@
|
||||
estree-walker "^1.0.1"
|
||||
picomatch "^2.2.2"
|
||||
|
||||
"@tauri-apps/api@link:../../../api":
|
||||
version "0.1.0"
|
||||
"@tauri-apps/api@link:../../api":
|
||||
version "0.0.0"
|
||||
uid ""
|
||||
|
||||
"@types/estree@*":
|
||||
version "0.0.46"
|
||||
@@ -1,20 +1,16 @@
|
||||
workspace = { }
|
||||
|
||||
[package]
|
||||
name = "helloworld"
|
||||
version = "0.1.0"
|
||||
description = "A Tauri App"
|
||||
authors = [ "you" ]
|
||||
license = ""
|
||||
repository = ""
|
||||
default-run = "helloworld"
|
||||
description = "A very simple Tauri Appplication"
|
||||
edition = "2018"
|
||||
build = "src/build.rs"
|
||||
|
||||
[build-dependencies]
|
||||
tauri-build = { path = "../../../core/tauri-build", features = [ "codegen" ]}
|
||||
|
||||
[dependencies]
|
||||
serde_json = "1.0"
|
||||
serde = { version = "1.0", features = [ "derive" ] }
|
||||
tauri = { path = "../../..", features =["api-all"]}
|
||||
tauri = { path = "../../../tauri", features =["api-all"]}
|
||||
|
||||
[target."cfg(windows)".build-dependencies]
|
||||
winres = "0.1"
|
||||
@@ -22,7 +18,3 @@ winres = "0.1"
|
||||
[features]
|
||||
default = [ "custom-protocol" ]
|
||||
custom-protocol = [ "tauri/custom-protocol" ]
|
||||
|
||||
[[bin]]
|
||||
name = "helloworld"
|
||||
path = "src/main.rs"
|
||||
4
examples/helloworld/src-tauri/build.rs
Normal file
@@ -0,0 +1,4 @@
|
||||
fn main() {
|
||||
tauri_build::CodegenContext::default().build();
|
||||
tauri_build::build();
|
||||
}
|
||||
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 37 KiB |
|
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 41 KiB After Width: | Height: | Size: 41 KiB |
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 45 KiB After Width: | Height: | Size: 45 KiB |
|
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 3.7 KiB |
|
Before Width: | Height: | Size: 7.2 KiB After Width: | Height: | Size: 7.2 KiB |
|
Before Width: | Height: | Size: 9.6 KiB After Width: | Height: | Size: 9.6 KiB |
|
Before Width: | Height: | Size: 4.4 KiB After Width: | Height: | Size: 4.4 KiB |
|
Before Width: | Height: | Size: 56 KiB After Width: | Height: | Size: 56 KiB |
|
Before Width: | Height: | Size: 88 KiB After Width: | Height: | Size: 88 KiB |
@@ -3,19 +3,16 @@
|
||||
windows_subsystem = "windows"
|
||||
)]
|
||||
|
||||
#[derive(tauri::FromTauriContext)]
|
||||
#[config_path = "examples/helloworld/src-tauri/tauri.conf.json"]
|
||||
struct Context;
|
||||
|
||||
#[tauri::command]
|
||||
fn my_custom_command(argument: String) {
|
||||
println!("{}", argument);
|
||||
}
|
||||
|
||||
fn main() {
|
||||
tauri::AppBuilder::<Context>::new()
|
||||
let context = tauri::generate_tauri_context!();
|
||||
|
||||
tauri::AppBuilder::default()
|
||||
.invoke_handler(tauri::generate_handler![my_custom_command])
|
||||
.build()
|
||||
.unwrap()
|
||||
.build(context)
|
||||
.run();
|
||||
}
|
||||
332
examples/multiwindow/dist/__tauri.js
vendored
Normal file
18
examples/multiwindow/src-tauri/Cargo.toml
Normal file
@@ -0,0 +1,18 @@
|
||||
[package]
|
||||
name = "multiwindow"
|
||||
version = "0.1.0"
|
||||
description = "An example Tauri Multi-Window Application"
|
||||
edition = "2018"
|
||||
|
||||
[build-dependencies]
|
||||
tauri-build = { path = "../../../core/tauri-build", features = ["codegen"] }
|
||||
|
||||
[dependencies]
|
||||
tauri = { path = "../../../tauri", features =["api-all"]}
|
||||
|
||||
[target."cfg(windows)".build-dependencies]
|
||||
winres = "0.1"
|
||||
|
||||
[features]
|
||||
default = [ "custom-protocol" ]
|
||||
custom-protocol = [ "tauri/custom-protocol" ]
|
||||
4
examples/multiwindow/src-tauri/build.rs
Normal file
@@ -0,0 +1,4 @@
|
||||
fn main() {
|
||||
tauri_build::CodegenContext::default().build();
|
||||
tauri_build::build();
|
||||
}
|
||||
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 37 KiB |
|
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 41 KiB After Width: | Height: | Size: 41 KiB |
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 45 KiB After Width: | Height: | Size: 45 KiB |