cargo_packager/package/
mod.rs1use std::path::PathBuf;
6
7use crate::{config, shell::CommandExt, util, Config, PackageFormat};
8
9use self::context::Context;
10
11mod app;
12#[cfg(any(
13 target_os = "linux",
14 target_os = "dragonfly",
15 target_os = "freebsd",
16 target_os = "netbsd",
17 target_os = "openbsd"
18))]
19mod appimage;
20#[cfg(any(
21 target_os = "linux",
22 target_os = "dragonfly",
23 target_os = "freebsd",
24 target_os = "netbsd",
25 target_os = "openbsd"
26))]
27mod deb;
28#[cfg(target_os = "macos")]
29mod dmg;
30mod nsis;
31#[cfg(any(
32 target_os = "linux",
33 target_os = "dragonfly",
34 target_os = "freebsd",
35 target_os = "netbsd",
36 target_os = "openbsd"
37))]
38mod pacman;
39#[cfg(windows)]
40mod wix;
41
42mod context;
43
44#[derive(Debug, Clone)]
46#[non_exhaustive]
47pub struct PackageOutput {
48 pub format: PackageFormat,
50 pub paths: Vec<PathBuf>,
52}
53
54impl PackageOutput {
55 pub fn new(format: PackageFormat, paths: Vec<PathBuf>) -> Self {
60 Self { format, paths }
61 }
62}
63
64#[tracing::instrument(level = "trace", skip(config))]
66pub fn package(config: &Config) -> crate::Result<Vec<PackageOutput>> {
67 let mut formats = config
68 .formats
69 .clone()
70 .unwrap_or_else(|| PackageFormat::platform_default().to_vec());
71
72 if formats.is_empty() {
73 return Ok(Vec::new());
74 }
75
76 if formats.contains(&PackageFormat::Default) {
77 formats = PackageFormat::platform_default().to_vec();
78 }
79
80 if formats.contains(&PackageFormat::All) {
81 formats = PackageFormat::platform_all().to_vec();
82 }
83
84 formats.sort_by_key(|f| f.priority());
85
86 let formats_comma_separated = formats
87 .iter()
88 .map(|f| f.short_name())
89 .collect::<Vec<_>>()
90 .join(",");
91
92 run_before_packaging_command_hook(config, &formats_comma_separated)?;
93
94 let ctx = Context::new(config)?;
95 tracing::trace!(ctx = ?ctx);
96
97 let mut packages = Vec::new();
98 for format in &formats {
99 run_before_each_packaging_command_hook(
100 config,
101 &formats_comma_separated,
102 format.short_name(),
103 )?;
104
105 let paths = match format {
106 PackageFormat::App => app::package(&ctx),
107 #[cfg(target_os = "macos")]
108 PackageFormat::Dmg => {
109 if !packages
111 .iter()
112 .any(|b: &PackageOutput| b.format == PackageFormat::App)
113 {
114 let paths = app::package(&ctx)?;
115 packages.push(PackageOutput {
116 format: PackageFormat::App,
117 paths,
118 });
119 }
120 dmg::package(&ctx)
121 }
122 #[cfg(target_os = "windows")]
123 PackageFormat::Wix => wix::package(&ctx),
124 PackageFormat::Nsis => nsis::package(&ctx),
125 #[cfg(any(
126 target_os = "linux",
127 target_os = "dragonfly",
128 target_os = "freebsd",
129 target_os = "netbsd",
130 target_os = "openbsd"
131 ))]
132 PackageFormat::Deb => deb::package(&ctx),
133 #[cfg(any(
134 target_os = "linux",
135 target_os = "dragonfly",
136 target_os = "freebsd",
137 target_os = "netbsd",
138 target_os = "openbsd"
139 ))]
140 PackageFormat::AppImage => appimage::package(&ctx),
141 #[cfg(any(
142 target_os = "linux",
143 target_os = "dragonfly",
144 target_os = "freebsd",
145 target_os = "netbsd",
146 target_os = "openbsd"
147 ))]
148 PackageFormat::Pacman => pacman::package(&ctx),
149
150 _ => {
151 tracing::warn!("ignoring {}", format.short_name());
152 continue;
153 }
154 }?;
155
156 packages.push(PackageOutput {
157 format: *format,
158 paths,
159 });
160 }
161
162 #[cfg(target_os = "macos")]
163 {
164 if !formats.contains(&PackageFormat::App) {
166 if let Some(app_bundle_paths) = packages
167 .iter()
168 .position(|b| b.format == PackageFormat::App)
169 .map(|i| packages.remove(i))
170 .map(|b| b.paths)
171 {
172 for p in &app_bundle_paths {
173 use crate::Error;
174 use std::fs;
175
176 tracing::debug!("Cleaning {}", p.display());
177 match p.is_dir() {
178 true => {
179 fs::remove_dir_all(p).map_err(|e| Error::IoWithPath(p.clone(), e))?
180 }
181 false => fs::remove_file(p).map_err(|e| Error::IoWithPath(p.clone(), e))?,
182 };
183 }
184 }
185 }
186 }
187
188 Ok(packages)
189}
190
191fn run_before_each_packaging_command_hook(
192 config: &Config,
193 formats_comma_separated: &str,
194 format: &str,
195) -> crate::Result<()> {
196 if let Some(hook) = &config.before_each_package_command {
197 let (mut cmd, script) = match hook {
198 config::HookCommand::Script(script) => {
199 let cmd = util::cross_command(script);
200 (cmd, script)
201 }
202 config::HookCommand::ScriptWithOptions { script, dir } => {
203 let mut cmd = util::cross_command(script);
204 if let Some(dir) = dir {
205 cmd.current_dir(dir);
206 }
207 (cmd, script)
208 }
209 };
210
211 tracing::info!("Running beforeEachPackageCommand [{format}] `{script}`");
212 let output = cmd
213 .env("CARGO_PACKAGER_FORMATS", formats_comma_separated)
214 .env("CARGO_PACKAGER_FORMAT", format)
215 .output_ok_info()
216 .map_err(|e| {
217 crate::Error::HookCommandFailure(
218 "beforeEachPackageCommand".into(),
219 script.into(),
220 e,
221 )
222 })?;
223
224 if !output.status.success() {
225 return Err(crate::Error::HookCommandFailureWithExitCode(
226 "beforeEachPackageCommand".into(),
227 script.into(),
228 output.status.code().unwrap_or_default(),
229 ));
230 }
231 }
232
233 Ok(())
234}
235
236fn run_before_packaging_command_hook(
237 config: &Config,
238 formats_comma_separated: &str,
239) -> crate::Result<()> {
240 if let Some(hook) = &config.before_packaging_command {
241 let (mut cmd, script) = match hook {
242 config::HookCommand::Script(script) => {
243 let cmd = util::cross_command(script);
244 (cmd, script)
245 }
246 config::HookCommand::ScriptWithOptions { script, dir } => {
247 let mut cmd = util::cross_command(script);
248 if let Some(dir) = dir {
249 cmd.current_dir(dir);
250 }
251 (cmd, script)
252 }
253 };
254
255 tracing::info!("Running beforePackageCommand `{script}`");
256 let output = cmd
257 .env("CARGO_PACKAGER_FORMATS", formats_comma_separated)
258 .output_ok_info()
259 .map_err(|e| {
260 crate::Error::HookCommandFailure("beforePackagingCommand".into(), script.into(), e)
261 })?;
262
263 if !output.status.success() {
264 return Err(crate::Error::HookCommandFailureWithExitCode(
265 "beforePackagingCommand".into(),
266 script.into(),
267 output.status.code().unwrap_or_default(),
268 ));
269 }
270 }
271
272 Ok(())
273}