]> git.proxmox.com Git - rustc.git/blob - src/tools/rust-installer/src/combiner.rs
2ec09d67e3e62acca475575e88962fc42870f5d5
[rustc.git] / src / tools / rust-installer / src / combiner.rs
1 use super::Scripter;
2 use super::Tarballer;
3 use crate::{
4 compression::{CompressionFormat, CompressionFormats},
5 util::*,
6 };
7 use anyhow::{bail, Context, Result};
8 use std::io::{Read, Write};
9 use std::path::Path;
10 use tar::Archive;
11
12 actor! {
13 #[derive(Debug)]
14 pub struct Combiner {
15 /// The name of the product, for display.
16 #[clap(value_name = "NAME")]
17 product_name: String = "Product",
18
19 /// The name of the package tarball.
20 #[clap(value_name = "NAME")]
21 package_name: String = "package",
22
23 /// The directory under lib/ where the manifest lives.
24 #[clap(value_name = "DIR")]
25 rel_manifest_dir: String = "packagelib",
26
27 /// The string to print after successful installation.
28 #[clap(value_name = "MESSAGE")]
29 success_message: String = "Installed.",
30
31 /// Places to look for legacy manifests to uninstall.
32 #[clap(value_name = "DIRS")]
33 legacy_manifest_dirs: String = "",
34
35 /// Installers to combine.
36 #[clap(value_name = "FILE,FILE")]
37 input_tarballs: String = "",
38
39 /// Directory containing files that should not be installed.
40 #[clap(value_name = "DIR")]
41 non_installed_overlay: String = "",
42
43 /// The directory to do temporary work.
44 #[clap(value_name = "DIR")]
45 work_dir: String = "./workdir",
46
47 /// The location to put the final image and tarball.
48 #[clap(value_name = "DIR")]
49 output_dir: String = "./dist",
50
51 /// The formats used to compress the tarball
52 #[clap(value_name = "FORMAT", default_value_t)]
53 compression_formats: CompressionFormats,
54 }
55 }
56
57 impl Combiner {
58 /// Combines the installer tarballs.
59 pub fn run(self) -> Result<()> {
60 create_dir_all(&self.work_dir)?;
61
62 let package_dir = Path::new(&self.work_dir).join(&self.package_name);
63 if package_dir.exists() {
64 remove_dir_all(&package_dir)?;
65 }
66 create_dir_all(&package_dir)?;
67
68 // Merge each installer into the work directory of the new installer.
69 let components = create_new_file(package_dir.join("components"))?;
70 for input_tarball in self
71 .input_tarballs
72 .split(',')
73 .map(str::trim)
74 .filter(|s| !s.is_empty())
75 {
76 // Extract the input tarballs
77 let compression =
78 CompressionFormat::detect_from_path(input_tarball).ok_or_else(|| {
79 anyhow::anyhow!("couldn't figure out the format of {}", input_tarball)
80 })?;
81 Archive::new(compression.decode(input_tarball)?)
82 .unpack(&self.work_dir)
83 .with_context(|| {
84 format!(
85 "unable to extract '{}' into '{}'",
86 &input_tarball, self.work_dir
87 )
88 })?;
89
90 let pkg_name =
91 input_tarball.trim_end_matches(&format!(".tar.{}", compression.extension()));
92 let pkg_name = Path::new(pkg_name).file_name().unwrap();
93 let pkg_dir = Path::new(&self.work_dir).join(&pkg_name);
94
95 // Verify the version number.
96 let mut version = String::new();
97 open_file(pkg_dir.join("rust-installer-version"))
98 .and_then(|mut file| Ok(file.read_to_string(&mut version)?))
99 .with_context(|| format!("failed to read version in '{}'", input_tarball))?;
100 if version.trim().parse() != Ok(crate::RUST_INSTALLER_VERSION) {
101 bail!("incorrect installer version in {}", input_tarball);
102 }
103
104 // Copy components to the new combined installer.
105 let mut pkg_components = String::new();
106 open_file(pkg_dir.join("components"))
107 .and_then(|mut file| Ok(file.read_to_string(&mut pkg_components)?))
108 .with_context(|| format!("failed to read components in '{}'", input_tarball))?;
109 for component in pkg_components.split_whitespace() {
110 // All we need to do is copy the component directory. We could
111 // move it, but rustbuild wants to reuse the unpacked package
112 // dir for OS-specific installers on macOS and Windows.
113 let component_dir = package_dir.join(&component);
114 create_dir(&component_dir)?;
115 copy_recursive(&pkg_dir.join(&component), &component_dir)?;
116
117 // Merge the component name.
118 writeln!(&components, "{}", component).context("failed to write new components")?;
119 }
120 }
121 drop(components);
122
123 // Write the installer version.
124 let version = package_dir.join("rust-installer-version");
125 writeln!(
126 create_new_file(version)?,
127 "{}",
128 crate::RUST_INSTALLER_VERSION
129 )
130 .context("failed to write new installer version")?;
131
132 // Copy the overlay.
133 if !self.non_installed_overlay.is_empty() {
134 copy_recursive(self.non_installed_overlay.as_ref(), &package_dir)?;
135 }
136
137 // Generate the install script.
138 let output_script = package_dir.join("install.sh");
139 let mut scripter = Scripter::default();
140 scripter
141 .product_name(self.product_name)
142 .rel_manifest_dir(self.rel_manifest_dir)
143 .success_message(self.success_message)
144 .legacy_manifest_dirs(self.legacy_manifest_dirs)
145 .output_script(path_to_str(&output_script)?.into());
146 scripter.run()?;
147
148 // Make the tarballs.
149 create_dir_all(&self.output_dir)?;
150 let output = Path::new(&self.output_dir).join(&self.package_name);
151 let mut tarballer = Tarballer::default();
152 tarballer
153 .work_dir(self.work_dir)
154 .input(self.package_name)
155 .output(path_to_str(&output)?.into())
156 .compression_formats(self.compression_formats.clone());
157 tarballer.run()?;
158
159 Ok(())
160 }
161 }