use super::Scripter; use super::Tarballer; use crate::compression::CompressionFormats; use crate::util::*; use anyhow::{bail, format_err, Context, Result}; use std::io::Write; use std::path::Path; actor! { #[derive(Debug)] pub struct Generator { /// The name of the product, for display #[clap(value_name = "NAME")] product_name: String = "Product", /// The name of the component, distinct from other installed components #[clap(value_name = "NAME")] component_name: String = "component", /// The name of the package, tarball #[clap(value_name = "NAME")] package_name: String = "package", /// The directory under lib/ where the manifest lives #[clap(value_name = "DIR")] rel_manifest_dir: String = "packagelib", /// The string to print after successful installation #[clap(value_name = "MESSAGE")] success_message: String = "Installed.", /// Places to look for legacy manifests to uninstall #[clap(value_name = "DIRS")] legacy_manifest_dirs: String = "", /// Directory containing files that should not be installed #[clap(value_name = "DIR")] non_installed_overlay: String = "", /// Path prefixes of directories that should be installed/uninstalled in bulk #[clap(value_name = "DIRS")] bulk_dirs: String = "", /// The directory containing the installation medium #[clap(value_name = "DIR")] image_dir: String = "./install_image", /// The directory to do temporary work #[clap(value_name = "DIR")] work_dir: String = "./workdir", /// The location to put the final image and tarball #[clap(value_name = "DIR")] output_dir: String = "./dist", /// The formats used to compress the tarball #[clap(value_name = "FORMAT", default_value_t)] compression_formats: CompressionFormats, } } impl Generator { /// Generates the actual installer tarball pub fn run(self) -> Result<()> { create_dir_all(&self.work_dir)?; let package_dir = Path::new(&self.work_dir).join(&self.package_name); if package_dir.exists() { remove_dir_all(&package_dir)?; } // Copy the image and write the manifest let component_dir = package_dir.join(&self.component_name); create_dir_all(&component_dir)?; copy_and_manifest(self.image_dir.as_ref(), &component_dir, &self.bulk_dirs)?; // Write the component name let components = package_dir.join("components"); writeln!(create_new_file(components)?, "{}", self.component_name) .context("failed to write the component file")?; // Write the installer version (only used by combine-installers.sh) let version = package_dir.join("rust-installer-version"); writeln!( create_new_file(version)?, "{}", crate::RUST_INSTALLER_VERSION ) .context("failed to write new installer version")?; // Copy the overlay if !self.non_installed_overlay.is_empty() { copy_recursive(self.non_installed_overlay.as_ref(), &package_dir)?; } // Generate the install script let output_script = package_dir.join("install.sh"); let mut scripter = Scripter::default(); scripter .product_name(self.product_name) .rel_manifest_dir(self.rel_manifest_dir) .success_message(self.success_message) .legacy_manifest_dirs(self.legacy_manifest_dirs) .output_script(path_to_str(&output_script)?.into()); scripter.run()?; // Make the tarballs create_dir_all(&self.output_dir)?; let output = Path::new(&self.output_dir).join(&self.package_name); let mut tarballer = Tarballer::default(); tarballer .work_dir(self.work_dir) .input(self.package_name) .output(path_to_str(&output)?.into()) .compression_formats(self.compression_formats.clone()); tarballer.run()?; Ok(()) } } /// Copies the `src` directory recursively to `dst`, writing `manifest.in` too. fn copy_and_manifest(src: &Path, dst: &Path, bulk_dirs: &str) -> Result<()> { let manifest = create_new_file(dst.join("manifest.in"))?; let bulk_dirs: Vec<_> = bulk_dirs .split(',') .filter(|s| !s.is_empty()) .map(Path::new) .collect(); copy_with_callback(src, dst, |path, file_type| { // We need paths to be compatible with both Unix and Windows. if path .components() .filter_map(|c| c.as_os_str().to_str()) .any(|s| s.contains('\\')) { bail!( "rust-installer doesn't support '\\' in path components: {:?}", path ); } // Normalize to Unix-style path separators. let normalized_string; let mut string = path.to_str().ok_or_else(|| { format_err!( "rust-installer doesn't support non-Unicode paths: {:?}", path ) })?; if string.contains('\\') { normalized_string = string.replace('\\', "/"); string = &normalized_string; } if file_type.is_dir() { // Only manifest directories that are explicitly bulk. if bulk_dirs.contains(&path) { writeln!(&manifest, "dir:{}", string)?; } } else { // Only manifest files that aren't under bulk directories. if !bulk_dirs.iter().any(|d| path.starts_with(d)) { writeln!(&manifest, "file:{}", string)?; } } Ok(()) }) }