1#![deny(missing_docs)]
2#![allow(clippy::single_match, clippy::result_large_err)]
3
4use announce::TagSettings;
14use axoasset::LocalAsset;
15use axoprocess::Cmd;
16use backend::{
17 ci::CiInfo,
18 installer::{
19 self, macpkg::PkgInstallerInfo, msi::MsiInstallerInfo, HomebrewImpl, InstallerImpl,
20 },
21};
22use build::generic::{build_generic_target, run_extra_artifacts_build};
23use build::{
24 cargo::{build_cargo_target, rustup_toolchain},
25 fake::{build_fake_cargo_target, build_fake_generic_target},
26};
27use camino::{Utf8Path, Utf8PathBuf};
28use cargo_dist_schema::{ArtifactId, ChecksumValue, ChecksumValueRef, DistManifest, TripleName};
29use config::{
30 ArtifactMode, ChecksumStyle, CompressionImpl, Config, DirtyMode, GenerateMode, ZipStyle,
31};
32use semver::Version;
33use temp_dir::TempDir;
34use tracing::info;
35
36use errors::*;
37pub use init::{do_init, do_migrate, InitArgs};
38pub use tasks::*;
39
40pub mod announce;
41pub mod backend;
42pub mod build;
43pub mod config;
44pub mod env;
45pub mod errors;
46pub mod host;
47mod init;
48pub mod linkage;
49pub mod manifest;
50pub mod net;
51pub mod platform;
52pub mod sign;
53pub mod tasks;
54#[cfg(test)]
55mod tests;
56
57pub fn do_env_test(cfg: &Config) -> DistResult<()> {
59 let (dist, _manifest) = tasks::gather_work(cfg)?;
60
61 let local_builds = matches!(
62 cfg.artifact_mode,
63 ArtifactMode::Local | ArtifactMode::All | ArtifactMode::Host
64 );
65
66 let builds = dist.config.builds;
67
68 let need_cargo_auditable = builds.cargo.cargo_auditable && local_builds;
70 let need_omnibor = builds.omnibor;
72 let mut need_xwin = false;
73 let mut need_zigbuild = false;
74
75 let tools = dist.tools;
76 let host = tools.host_target.parse()?;
77
78 for step in dist.local_build_steps.iter() {
79 if cfg.artifact_mode == ArtifactMode::Lies {
81 break;
82 }
83
84 match step {
85 BuildStep::Cargo(step) => {
86 let target = step.target_triple.parse()?;
87 let wrapper = tasks::build_wrapper_for_cross(&host, &target)?;
88
89 match wrapper {
90 Some(CargoBuildWrapper::Xwin) => {
91 need_xwin = true;
92 }
93 Some(CargoBuildWrapper::ZigBuild) => {
94 need_zigbuild = true;
95 }
96 None => {}
97 }
98 }
99 _ => {}
100 }
101 }
102
103 let all_tools: Vec<Option<DistResult<&Tool>>> = vec![
108 need_cargo_auditable.then(|| tools.cargo_auditable()),
109 need_omnibor.then(|| tools.omnibor()),
110 need_xwin.then(|| tools.cargo_xwin()),
111 need_zigbuild.then(|| tools.cargo_zigbuild()),
112 ];
113
114 let needed_tools = all_tools.into_iter().flatten();
116
117 let missing: Vec<String> = needed_tools
118 .filter_map(|t| match t {
119 Ok(_) => None,
121 Err(DistError::ToolMissing { tool: ref name }) => Some(name.to_owned()),
123 Err(_) => unreachable!(
126 "do_env_test() got an Err that wasn't DistError::ToolMissing. This is a dist bug."
127 ),
128 })
129 .collect();
130
131 missing
132 .is_empty()
133 .then_some(())
134 .ok_or(DistError::EnvToolsMissing { tools: missing })
135}
136
137pub fn do_build(cfg: &Config) -> DistResult<DistManifest> {
139 do_env_test(cfg)?;
140 check_integrity(cfg)?;
141
142 let (dist, mut manifest) = tasks::gather_work(cfg)?;
143
144 if !dist.dist_dir.exists() {
152 LocalAsset::create_dir_all(&dist.dist_dir)?;
153 }
154
155 eprintln!("building artifacts:");
156 for artifact in &dist.artifacts {
157 eprintln!(" {}", artifact.id);
158 init_artifact_dir(&dist, artifact)?;
159 }
160 eprintln!();
161
162 for step in &dist.local_build_steps {
164 if dist.local_builds_are_lies {
165 build_fake(&dist, step, &mut manifest)?;
166 } else {
167 run_build_step(&dist, step, &mut manifest)?;
168 }
169 }
170
171 for step in &dist.global_build_steps {
173 if dist.local_builds_are_lies {
174 build_fake(&dist, step, &mut manifest)?;
175 } else {
176 run_build_step(&dist, step, &mut manifest)?;
177 }
178 }
179
180 Ok(manifest)
181}
182
183pub fn do_manifest(cfg: &Config) -> DistResult<DistManifest> {
185 check_integrity(cfg)?;
186 let (_dist, manifest) = gather_work(cfg)?;
187
188 Ok(manifest)
189}
190
191fn run_build_step(
193 dist_graph: &DistGraph,
194 target: &BuildStep,
195 manifest: &mut DistManifest,
196) -> DistResult<()> {
197 match target {
198 BuildStep::Generic(target) => build_generic_target(dist_graph, manifest, target)?,
199 BuildStep::Cargo(target) => build_cargo_target(dist_graph, manifest, target)?,
200 BuildStep::Rustup(cmd) => rustup_toolchain(dist_graph, cmd)?,
201 BuildStep::CopyFile(CopyStep {
202 src_path,
203 dest_path,
204 }) => copy_file(src_path, dest_path)?,
205 BuildStep::CopyDir(CopyStep {
206 src_path,
207 dest_path,
208 }) => copy_dir(src_path, dest_path)?,
209 BuildStep::CopyFileOrDir(CopyStep {
210 src_path,
211 dest_path,
212 }) => copy_file_or_dir(src_path, dest_path)?,
213 BuildStep::Zip(ZipDirStep {
214 src_path,
215 dest_path,
216 zip_style,
217 with_root,
218 }) => zip_dir(src_path, dest_path, zip_style, with_root.as_deref())?,
219 BuildStep::GenerateInstaller(installer) => {
220 generate_installer(dist_graph, installer, manifest)?
221 }
222 BuildStep::Checksum(ChecksumImpl {
223 checksum,
224 src_path,
225 dest_path,
226 for_artifact,
227 }) => generate_and_write_checksum(
228 manifest,
229 checksum,
230 src_path,
231 dest_path.as_deref(),
232 for_artifact.as_ref(),
233 )?,
234 BuildStep::UnifiedChecksum(UnifiedChecksumStep {
235 checksum,
236 dest_path,
237 }) => generate_unified_checksum(manifest, *checksum, dest_path)?,
238 BuildStep::OmniborArtifactId(OmniborArtifactIdImpl {
239 src_path,
240 dest_path,
241 }) => generate_omnibor_artifact_id(dist_graph, src_path, dest_path)?,
242 BuildStep::GenerateSourceTarball(SourceTarballStep {
243 committish,
244 prefix,
245 target,
246 working_dir,
247 recursive,
248 }) => {
249 if *recursive {
250 generate_recursive_source_tarball(
251 dist_graph,
252 committish,
253 prefix,
254 target,
255 working_dir,
256 )?
257 } else {
258 generate_source_tarball(dist_graph, committish, prefix, target, working_dir)?
259 }
260 }
261 BuildStep::Extra(target) => run_extra_artifacts_build(dist_graph, target)?,
262 BuildStep::Updater(updater) => fetch_updater(dist_graph, updater)?,
263 };
264 Ok(())
265}
266
267const AXOUPDATER_ASSET_ROOT: &str = "https://github.com/axodotdev/axoupdater/releases";
268const AXOUPDATER_MINIMUM_VERSION: &str = "0.9.0";
269
270fn axoupdater_latest_asset_root() -> String {
271 format!("{AXOUPDATER_ASSET_ROOT}/latest/download")
272}
273
274fn axoupdater_asset_root() -> String {
275 format!("{AXOUPDATER_ASSET_ROOT}/download/v{}", axoupdater::VERSION)
276}
277
278pub fn fetch_updater(dist_graph: &DistGraph, updater: &UpdaterStep) -> DistResult<()> {
280 let ext = if updater.target_triple.is_windows() {
281 ".zip"
282 } else {
283 ".tar.xz"
284 };
285
286 let asset_root = if updater.use_latest {
287 axoupdater_latest_asset_root()
288 } else {
289 axoupdater_asset_root()
290 };
291
292 let expected_url = format!(
293 "{}/axoupdater-cli-{}{ext}",
294 asset_root, updater.target_triple
295 );
296
297 let handle = tokio::runtime::Handle::current();
298 let resp = handle
299 .block_on(dist_graph.axoclient.head(&expected_url))
300 .map_err(|_| DistError::AxoupdaterReleaseCheckFailed {})?;
301
302 if resp.status().is_success() {
304 fetch_updater_from_binary(dist_graph, updater, &expected_url)
305 } else if resp.status() == axoasset::reqwest::StatusCode::NOT_FOUND {
307 Err(DistError::NoAxoupdaterForTarget {
308 target: updater.target_triple.to_string(),
309 })
310 } else {
312 Err(DistError::AxoupdaterReleaseCheckFailed {})
313 }
314}
315
316pub fn create_tmp() -> DistResult<(TempDir, Utf8PathBuf)> {
319 let tmp_dir = TempDir::new()?;
320 let tmp_root =
321 Utf8PathBuf::from_path_buf(tmp_dir.path().to_owned()).expect("tempdir isn't utf8!?");
322 Ok((tmp_dir, tmp_root))
323}
324
325fn fetch_updater_from_binary(
327 dist_graph: &DistGraph,
328 updater: &UpdaterStep,
329 asset_url: &str,
330) -> DistResult<()> {
331 let (_tmp_dir, tmp_root) = create_tmp()?;
332 let zipball_target = tmp_root.join("archive");
333
334 let handle = tokio::runtime::Handle::current();
335 handle.block_on(
336 dist_graph
337 .axoclient
338 .load_and_write_to_file(asset_url, &zipball_target),
339 )?;
340 let suffix = if updater.target_triple.is_windows() {
341 ".exe"
342 } else {
343 ""
344 };
345 let requested_filename = format!("axoupdater{suffix}");
346
347 let bytes = if asset_url.ends_with(".tar.xz") {
348 LocalAsset::untar_xz_file(&zipball_target, &requested_filename)?
349 } else if asset_url.ends_with(".tar.gz") {
350 LocalAsset::untar_gz_file(&zipball_target, &requested_filename)?
351 } else if asset_url.ends_with(".zip") {
352 LocalAsset::unzip_file(&zipball_target, &requested_filename)?
353 } else {
354 let extension = Utf8PathBuf::from(asset_url)
355 .extension()
356 .unwrap_or("unable to determine")
357 .to_owned();
358 return Err(DistError::UnrecognizedCompression { extension });
359 };
360
361 let target = dist_graph.target_dir.join(&updater.target_filename);
362 std::fs::write(target, bytes)?;
363
364 Ok(())
365}
366
367fn build_fake(
368 dist_graph: &DistGraph,
369 target: &BuildStep,
370 manifest: &mut DistManifest,
371) -> DistResult<()> {
372 match target {
373 BuildStep::Generic(target) => build_fake_generic_target(dist_graph, manifest, target)?,
376 BuildStep::Cargo(target) => build_fake_cargo_target(dist_graph, manifest, target)?,
377 BuildStep::Rustup(_) => {}
379 BuildStep::CopyFile(CopyStep {
381 src_path,
382 dest_path,
383 }) => copy_file(src_path, dest_path)?,
384 BuildStep::CopyDir(CopyStep {
385 src_path,
386 dest_path,
387 }) => copy_dir(src_path, dest_path)?,
388 BuildStep::CopyFileOrDir(CopyStep {
389 src_path,
390 dest_path,
391 }) => copy_file_or_dir(src_path, dest_path)?,
392 BuildStep::Zip(ZipDirStep {
394 src_path,
395 dest_path,
396 zip_style,
397 with_root,
398 }) => zip_dir(src_path, dest_path, zip_style, with_root.as_deref())?,
399 BuildStep::GenerateInstaller(installer) => match installer {
400 InstallerImpl::Msi(msi) => generate_fake_msi(dist_graph, msi, manifest)?,
402 InstallerImpl::Pkg(pkg) => generate_fake_pkg(dist_graph, pkg, manifest)?,
403 _ => generate_installer(dist_graph, installer, manifest)?,
404 },
405 BuildStep::Checksum(ChecksumImpl {
406 checksum,
407 src_path,
408 dest_path,
409 for_artifact,
410 }) => generate_and_write_checksum(
411 manifest,
412 checksum,
413 src_path,
414 dest_path.as_deref(),
415 for_artifact.as_ref(),
416 )?,
417 BuildStep::UnifiedChecksum(UnifiedChecksumStep {
418 checksum,
419 dest_path,
420 }) => generate_unified_checksum(manifest, *checksum, dest_path)?,
421 BuildStep::OmniborArtifactId(OmniborArtifactIdImpl {
422 src_path,
423 dest_path,
424 }) => generate_omnibor_artifact_id(dist_graph, src_path, dest_path)?,
425 BuildStep::GenerateSourceTarball(SourceTarballStep {
433 committish,
434 prefix,
435 target,
436 working_dir,
437 recursive: _,
438 }) => generate_fake_source_tarball(dist_graph, committish, prefix, target, working_dir)?,
439 BuildStep::Extra(target) => run_fake_extra_artifacts_build(dist_graph, target)?,
441 BuildStep::Updater(_) => unimplemented!(),
442 }
443 Ok(())
444}
445
446fn run_fake_extra_artifacts_build(dist: &DistGraph, target: &ExtraBuildStep) -> DistResult<()> {
447 for artifact in &target.artifact_relpaths {
448 let path = dist.dist_dir.join(artifact);
449 LocalAsset::write_new_all("", &path)?;
450 }
451
452 Ok(())
453}
454
455fn generate_fake_msi(
456 _dist: &DistGraph,
457 msi: &MsiInstallerInfo,
458 _manifest: &DistManifest,
459) -> DistResult<()> {
460 LocalAsset::write_new_all("", &msi.file_path)?;
461
462 Ok(())
463}
464
465fn generate_fake_pkg(
466 _dist: &DistGraph,
467 pkg: &PkgInstallerInfo,
468 _manifest: &DistManifest,
469) -> DistResult<()> {
470 LocalAsset::write_new_all("", &pkg.file_path)?;
471
472 Ok(())
473}
474
475fn generate_omnibor_artifact_id(
476 dist_graph: &DistGraph,
477 src_path: &Utf8Path,
478 dest_path: &Utf8Path,
479) -> DistResult<()> {
480 let omnibor = dist_graph.tools.omnibor()?;
481 let mut cmd = Cmd::new(&omnibor.cmd, "generate an OmniBOR Artifact ID");
482 cmd.arg("artifact")
483 .arg("id")
484 .arg("--format")
485 .arg("short")
486 .arg("--path")
487 .arg(src_path);
488
489 let output = cmd.output()?.stdout;
490 let output = String::from_utf8_lossy(&output);
491
492 LocalAsset::write_new_all(&output, dest_path)?;
493
494 Ok(())
495}
496
497fn generate_and_write_checksum(
499 manifest: &mut DistManifest,
500 checksum: &ChecksumStyle,
501 src_path: &Utf8Path,
502 dest_path: Option<&Utf8Path>,
503 for_artifact: Option<&ArtifactId>,
504) -> DistResult<()> {
505 let output = generate_checksum(checksum, src_path)?;
506 if let Some(dest_path) = dest_path {
507 let name = src_path.file_name().expect("hashing file with no name!?");
508 write_checksum_file(&[(name, &output)], dest_path)?;
509 }
510 if let Some(artifact_id) = for_artifact {
511 if let Some(artifact) = manifest.artifacts.get_mut(artifact_id) {
512 artifact.checksums.insert(checksum.ext().to_owned(), output);
513 }
514 }
515 Ok(())
516}
517
518fn generate_unified_checksum(
520 manifest: &DistManifest,
521 checksum: ChecksumStyle,
522 dest_path: &Utf8Path,
523) -> DistResult<()> {
524 let expected_checksum_ext = checksum.ext();
525 let mut entries: Vec<(&str, &ChecksumValueRef)> = vec![];
526
527 for artifact in manifest.artifacts.values() {
528 let artifact_name = if let Some(artifact_name) = artifact.name.as_deref() {
529 artifact_name
530 } else {
531 continue;
532 };
533
534 for (checksum_ext, checksum) in &artifact.checksums {
535 if checksum_ext == expected_checksum_ext {
536 entries.push((artifact_name.as_str(), checksum));
537 }
538 }
539 }
540 write_checksum_file(&entries, dest_path)?;
541
542 Ok(())
543}
544
545fn generate_checksum(checksum: &ChecksumStyle, src_path: &Utf8Path) -> DistResult<ChecksumValue> {
547 info!("generating {checksum:?} for {src_path}");
548 use sha2::Digest;
549 use std::fmt::Write;
550
551 let file_bytes = axoasset::LocalAsset::load_bytes(src_path.as_str())?;
552
553 let hash = match checksum {
554 ChecksumStyle::Sha256 => {
555 let mut hasher = sha2::Sha256::new();
556 hasher.update(&file_bytes);
557 hasher.finalize().as_slice().to_owned()
558 }
559 ChecksumStyle::Sha512 => {
560 let mut hasher = sha2::Sha512::new();
561 hasher.update(&file_bytes);
562 hasher.finalize().as_slice().to_owned()
563 }
564 ChecksumStyle::Sha3_256 => {
565 let mut hasher = sha3::Sha3_256::new();
566 hasher.update(&file_bytes);
567 hasher.finalize().as_slice().to_owned()
568 }
569 ChecksumStyle::Sha3_512 => {
570 let mut hasher = sha3::Sha3_512::new();
571 hasher.update(&file_bytes);
572 hasher.finalize().as_slice().to_owned()
573 }
574 ChecksumStyle::Blake2s => {
575 let mut hasher = blake2::Blake2s256::new();
576 hasher.update(&file_bytes);
577 hasher.finalize().as_slice().to_owned()
578 }
579 ChecksumStyle::Blake2b => {
580 let mut hasher = blake2::Blake2b512::new();
581 hasher.update(&file_bytes);
582 hasher.finalize().as_slice().to_owned()
583 }
584 ChecksumStyle::False => {
585 unreachable!()
586 }
587 };
588 let mut output = String::with_capacity(hash.len() * 2);
589 for byte in hash {
590 write!(&mut output, "{:02x}", byte).unwrap();
591 }
592 Ok(ChecksumValue::new(output))
593}
594
595fn generate_source_tarball(
599 graph: &DistGraph,
600 committish: &str,
601 prefix: &str,
602 target: &Utf8Path,
603 working_dir: &Utf8Path,
604) -> DistResult<()> {
605 let git = if let Some(tool) = &graph.tools.git {
606 tool.cmd.to_owned()
607 } else {
608 return Err(DistError::ToolMissing {
609 tool: "git".to_owned(),
610 });
611 };
612
613 Cmd::new(git, "generate a source tarball for your project")
614 .arg("archive")
615 .arg(committish)
616 .arg("--format=tar.gz")
617 .arg("--prefix")
618 .arg(prefix)
619 .arg("--output")
620 .arg(target)
621 .current_dir(working_dir)
622 .run()?;
623
624 Ok(())
625}
626
627fn generate_recursive_source_tarball(
628 graph: &DistGraph,
629 _committish: &str,
630 prefix: &str,
631 target: &Utf8Path,
632 working_dir: &Utf8Path,
633) -> DistResult<()> {
634 let git = if let Some(tool) = &graph.tools.git {
635 tool.cmd.to_owned()
636 } else {
637 return Err(DistError::ToolMissing {
638 tool: "git".to_owned(),
639 });
640 };
641
642 let output = Cmd::new(git, "generate a source tarball for your project")
644 .arg("ls-files")
645 .arg("--recurse-submodules")
647 .arg("-z")
649 .current_dir(working_dir)
650 .output()?;
651
652 let file_paths = output.stdout.split(|byte| *byte == 0);
653 let (_temp_dir, temp_path) = create_tmp()?;
654
655 for raw_relpath in file_paths {
656 let relpath = String::from_utf8(raw_relpath.to_owned()).expect("non-utf8 path");
657 let src_path = working_dir.join(&relpath);
658 let dest_path = temp_path.join(prefix).join(&relpath);
659
660 if !src_path.exists() {
663 continue;
664 }
665
666 if src_path.is_dir() {
667 axoasset::LocalAsset::create_dir_all(dest_path)?;
668 } else {
669 if let Some(dest_parent) = dest_path.parent() {
671 axoasset::LocalAsset::create_dir_all(dest_parent)?;
672 }
673 if src_path.is_symlink() {
674 if cfg!(unix) {
675 #[cfg(unix)]
676 {
677 let link_name = std::fs::read_link(&src_path)?;
678 std::os::unix::fs::symlink(link_name, dest_path).unwrap();
679 }
680 } else {
681 axoasset::LocalAsset::create_dir_all(dest_path)?;
684 }
685 } else {
686 axoasset::LocalAsset::copy_file_to_file(src_path, dest_path)?;
687 }
688 }
689 }
690
691 axoasset::LocalAsset::tar_gz_dir(temp_path.join(prefix), target, None::<&Utf8Path>)?;
692
693 Ok(())
694}
695
696fn generate_fake_source_tarball(
697 _graph: &DistGraph,
698 _committish: &str,
699 _prefix: &str,
700 target: &Utf8Path,
701 _working_dir: &Utf8Path,
702) -> DistResult<()> {
703 LocalAsset::write_new_all("", target)?;
704
705 Ok(())
706}
707
708fn write_checksum_file(
710 entries: &[(&str, &ChecksumValueRef)],
711 dest_path: &Utf8Path,
712) -> DistResult<()> {
713 let mut contents = String::new();
725 for (file_path, checksum) in entries {
726 use std::fmt::Write;
727 writeln!(&mut contents, "{checksum} *{file_path}",).unwrap();
728 }
729 contents.push('\n');
731
732 axoasset::LocalAsset::write_new(&contents, dest_path)?;
733 Ok(())
734}
735
736fn init_artifact_dir(_dist: &DistGraph, artifact: &Artifact) -> DistResult<()> {
738 if artifact.file_path.exists() {
740 LocalAsset::remove_file(&artifact.file_path)?;
741 }
742
743 let Some(archive) = &artifact.archive else {
744 return Ok(());
746 };
747 info!("recreating artifact dir: {}", archive.dir_path);
748
749 if archive.dir_path.exists() {
751 LocalAsset::remove_dir_all(&archive.dir_path)?;
752 }
753 LocalAsset::create_dir(&archive.dir_path)?;
754
755 Ok(())
756}
757
758pub(crate) fn copy_file(src_path: &Utf8Path, dest_path: &Utf8Path) -> DistResult<()> {
759 LocalAsset::copy_file_to_file(src_path, dest_path)?;
760 Ok(())
761}
762
763pub(crate) fn copy_dir(src_path: &Utf8Path, dest_path: &Utf8Path) -> DistResult<()> {
764 LocalAsset::copy_dir_to_dir(src_path, dest_path)?;
765 Ok(())
766}
767
768pub(crate) fn copy_file_or_dir(src_path: &Utf8Path, dest_path: &Utf8Path) -> DistResult<()> {
769 if src_path.is_dir() {
770 copy_dir(src_path, dest_path)
771 } else {
772 copy_file(src_path, dest_path)
773 }
774}
775
776fn zip_dir(
777 src_path: &Utf8Path,
778 dest_path: &Utf8Path,
779 zip_style: &ZipStyle,
780 with_root: Option<&Utf8Path>,
781) -> DistResult<()> {
782 match zip_style {
783 ZipStyle::Zip => LocalAsset::zip_dir(src_path, dest_path, with_root)?,
784 ZipStyle::Tar(CompressionImpl::Gzip) => {
785 LocalAsset::tar_gz_dir(src_path, dest_path, with_root)?
786 }
787 ZipStyle::Tar(CompressionImpl::Xzip) => {
788 LocalAsset::tar_xz_dir(src_path, dest_path, with_root)?
789 }
790 ZipStyle::Tar(CompressionImpl::Zstd) => {
791 LocalAsset::tar_zstd_dir(src_path, dest_path, with_root)?
792 }
793 ZipStyle::TempDir => {
794 }
796 }
797 Ok(())
798}
799
800#[derive(Debug)]
802pub struct GenerateArgs {
803 pub check: bool,
805 pub modes: Vec<GenerateMode>,
807}
808
809fn do_generate_preflight_checks(dist: &DistGraph) -> DistResult<()> {
810 if let Some(desired_version) = &dist.config.dist_version {
818 let current_version: Version = std::env!("CARGO_PKG_VERSION").parse().unwrap();
819 if desired_version != ¤t_version
820 && !desired_version.pre.starts_with("github-")
821 && !matches!(dist.allow_dirty, DirtyMode::AllowAll)
822 {
823 return Err(DistError::MismatchedDistVersion {
824 config_version: desired_version.to_string(),
825 running_version: current_version.to_string(),
826 });
827 }
828 }
829 if !dist.is_init {
830 return Err(DistError::NeedsInit);
831 }
832
833 Ok(())
834}
835
836pub fn do_generate(cfg: &Config, args: &GenerateArgs) -> DistResult<()> {
838 let (dist, _manifest) = gather_work(cfg)?;
839
840 run_generate(&dist, args)?;
841
842 Ok(())
843}
844
845pub fn run_generate(dist: &DistGraph, args: &GenerateArgs) -> DistResult<()> {
847 do_generate_preflight_checks(dist)?;
848
849 let inferred = args.modes.is_empty();
852 let modes = if inferred {
853 &[GenerateMode::Ci, GenerateMode::Msi]
854 } else {
855 for &mode in &args.modes {
857 if !dist.allow_dirty.should_run(mode)
858 && matches!(dist.allow_dirty, DirtyMode::AllowList(..))
859 {
860 Err(DistError::ContradictoryGenerateModes {
861 generate_mode: mode,
862 })?;
863 }
864 }
865 &args.modes[..]
866 };
867
868 for &mode in modes {
871 if dist.allow_dirty.should_run(mode) {
872 match mode {
873 GenerateMode::Ci => {
874 let CiInfo { github } = &dist.ci;
876 if let Some(github) = github {
877 if args.check {
878 github.check(dist)?;
879 } else {
880 github.write_to_disk(dist)?;
881 }
882 }
883 }
884 GenerateMode::Msi => {
885 for artifact in &dist.artifacts {
886 if let ArtifactKind::Installer(InstallerImpl::Msi(msi)) = &artifact.kind {
887 if args.check {
888 msi.check_config()?;
889 } else {
890 msi.write_config_to_disk()?;
891 }
892 }
893 }
894 }
895 }
896 }
897 }
898
899 Ok(())
900}
901
902pub fn check_integrity(cfg: &Config) -> DistResult<()> {
906 let check_config = Config {
909 tag_settings: TagSettings {
911 needs_coherence: false,
912 tag: cfg.tag_settings.tag.clone(),
917 },
918 create_hosting: false,
920 artifact_mode: ArtifactMode::All,
921 no_local_paths: false,
922 allow_all_dirty: cfg.allow_all_dirty,
923 targets: vec![],
924 ci: vec![],
925 installers: vec![],
926 root_cmd: "check".to_owned(),
927 };
928 let (dist, _manifest) = tasks::gather_work(&check_config)?;
929
930 run_generate(
931 &dist,
932 &GenerateArgs {
933 modes: vec![],
934 check: true,
935 },
936 )
937}
938
939fn generate_installer(
941 dist: &DistGraph,
942 style: &InstallerImpl,
943 manifest: &DistManifest,
944) -> DistResult<()> {
945 match style {
946 InstallerImpl::Shell(info) => {
947 installer::shell::write_install_sh_script(dist, info, manifest)?
948 }
949 InstallerImpl::Powershell(info) => {
950 installer::powershell::write_install_ps_script(dist, info)?
951 }
952 InstallerImpl::Npm(info) => installer::npm::write_npm_project(dist, info)?,
953 InstallerImpl::Homebrew(HomebrewImpl { info, fragments }) => {
954 installer::homebrew::write_homebrew_formula(dist, info, fragments, manifest)?
955 }
956 InstallerImpl::Msi(info) => info.build(dist)?,
957 InstallerImpl::Pkg(info) => info.build()?,
958 }
959 Ok(())
960}
961
962pub fn default_desktop_targets() -> Vec<TripleName> {
964 use crate::platform::targets as t;
965
966 vec![
967 t::TARGET_X64_LINUX_GNU.to_owned(),
969 t::TARGET_X64_WINDOWS.to_owned(),
970 t::TARGET_X64_MAC.to_owned(),
971 t::TARGET_ARM64_MAC.to_owned(),
972 t::TARGET_ARM64_LINUX_GNU.to_owned(),
973 ]
976}
977
978pub fn known_desktop_targets() -> Vec<TripleName> {
980 use crate::platform::targets as t;
981
982 vec![
983 t::TARGET_X64_LINUX_GNU.to_owned(),
985 t::TARGET_X64_LINUX_MUSL.to_owned(),
986 t::TARGET_X64_WINDOWS.to_owned(),
987 t::TARGET_X64_MAC.to_owned(),
988 t::TARGET_ARM64_MAC.to_owned(),
989 t::TARGET_ARM64_LINUX_GNU.to_owned(),
990 t::TARGET_ARM64_WINDOWS.to_owned(),
991 ]
992}