tauri_build/codegen/
context.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
// Copyright 2019-2024 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT

use anyhow::{Context, Result};
use std::{
  env::var,
  fs::{create_dir_all, File},
  io::{BufWriter, Write},
  path::{Path, PathBuf},
};
use tauri_codegen::{context_codegen, ContextData};
use tauri_utils::config::FrontendDist;

// TODO docs
/// A builder for generating a Tauri application context during compile time.
#[cfg_attr(docsrs, doc(cfg(feature = "codegen")))]
#[derive(Debug)]
pub struct CodegenContext {
  config_path: PathBuf,
  out_file: PathBuf,
  capabilities: Option<Vec<PathBuf>>,
}

impl Default for CodegenContext {
  fn default() -> Self {
    Self {
      config_path: PathBuf::from("tauri.conf.json"),
      out_file: PathBuf::from("tauri-build-context.rs"),
      capabilities: None,
    }
  }
}

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`.
  #[must_use]
  pub fn config_path(mut self, config_path: impl Into<PathBuf>) -> Self {
    self.config_path = config_path.into();
    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::tauri_build_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::tauri_build_context!`]: https://docs.rs/tauri/latest/tauri/macro.tauri_build_context.html
  #[must_use]
  pub fn out_file(mut self, filename: PathBuf) -> Self {
    self.out_file = filename;
    self
  }

  /// Adds a capability file to the generated context.
  #[must_use]
  pub fn capability<P: AsRef<Path>>(mut self, path: P) -> Self {
    self
      .capabilities
      .get_or_insert_with(Default::default)
      .push(path.as_ref().to_path_buf());
    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.
  pub(crate) fn try_build(self) -> Result<PathBuf> {
    let (config, config_parent) = tauri_codegen::get_config(&self.config_path)?;

    // rerun if changed
    match &config.build.frontend_dist {
      Some(FrontendDist::Directory(p)) => {
        let dist_path = config_parent.join(p);
        if dist_path.exists() {
          println!("cargo:rerun-if-changed={}", dist_path.display());
        }
      }
      Some(FrontendDist::Files(files)) => {
        for path in files {
          println!(
            "cargo:rerun-if-changed={}",
            config_parent.join(path).display()
          );
        }
      }
      _ => (),
    }
    for icon in &config.bundle.icon {
      println!(
        "cargo:rerun-if-changed={}",
        config_parent.join(icon).display()
      );
    }
    if let Some(tray_icon) = config.app.tray_icon.as_ref().map(|t| &t.icon_path) {
      println!(
        "cargo:rerun-if-changed={}",
        config_parent.join(tray_icon).display()
      );
    }

    #[cfg(target_os = "macos")]
    {
      let info_plist_path = config_parent.join("Info.plist");
      if info_plist_path.exists() {
        println!("cargo:rerun-if-changed={}", info_plist_path.display());
      }
    }

    let code = context_codegen(ContextData {
      dev: crate::is_dev(),
      config,
      config_parent,
      // it's very hard to have a build script for unit tests, so assume this is always called from
      // outside the tauri crate, making the ::tauri root valid.
      root: quote::quote!(::tauri),
      capabilities: self.capabilities,
      assets: None,
      test: false,
    })?;

    // 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!(file, "{code}").with_context(|| {
      format!(
        "Unable to write tokenstream to out file during tauri-build {}",
        out.display()
      )
    })?;

    Ok(out)
  }
}