Anki/build/ninja_gen/src/archives.rs
Damien Elmes 56a1046ff8 Fix bundling
PyOxidizer binary is now going into a platform folder, and our Qt
deps moved.
2023-07-02 18:22:44 +10:00

186 lines
5.3 KiB
Rust

// Copyright: Ankitects Pty Ltd and contributors
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
use std::borrow::Cow;
use std::collections::HashMap;
use anyhow::Result;
use camino::Utf8Path;
use camino::Utf8PathBuf;
use crate::action::BuildAction;
use crate::input::BuildInput;
use crate::inputs;
use crate::Build;
#[derive(Clone, Copy, Debug)]
pub struct OnlineArchive {
pub url: &'static str,
pub sha256: &'static str,
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum Platform {
LinuxX64,
LinuxArm,
MacX64,
MacArm,
WindowsX64,
}
impl Platform {
pub fn current() -> Self {
if cfg!(windows) {
Self::WindowsX64
} else {
let os = std::env::consts::OS;
let arch = std::env::consts::ARCH;
match (os, arch) {
("linux", "x86_64") => Self::LinuxX64,
("linux", "aarch64") => Self::LinuxArm,
("macos", "x86_64") => Self::MacX64,
("macos", "aarch64") => Self::MacArm,
_ => panic!("unsupported os/arch {os} {arch} - PR welcome!"),
}
}
}
pub fn tls_feature() -> &'static str {
match Self::current() {
// On Linux, wheels are not allowed to link to OpenSSL, and linking setup
// caused pain for AnkiDroid in the past. On other platforms, we stick to
// native libraries, for smaller binaries.
Platform::LinuxX64 | Platform::LinuxArm => "rustls",
_ => "native-tls",
}
}
pub fn as_rust_triple(&self) -> &'static str {
match self {
Platform::LinuxX64 => "x86_64-unknown-linux-gnu",
Platform::LinuxArm => "aarch64-unknown-linux-gnu",
Platform::MacX64 => "x86_64-apple-darwin",
Platform::MacArm => "aarch64-apple-darwin",
Platform::WindowsX64 => "x86_64-pc-windows-msvc",
}
}
}
/// Append .exe to path if on Windows.
pub fn with_exe(path: &str) -> Cow<str> {
if cfg!(windows) {
format!("{path}.exe").into()
} else {
path.into()
}
}
struct DownloadArchive {
pub archive: OnlineArchive,
}
impl BuildAction for DownloadArchive {
fn command(&self) -> &str {
"$runner archive download $url $checksum $out"
}
fn files(&mut self, build: &mut impl crate::build::FilesHandle) {
let (_, filename) = self.archive.url.rsplit_once('/').unwrap();
let output_path = Utf8Path::new("download").join(filename);
build.add_variable("url", self.archive.url);
build.add_variable("checksum", self.archive.sha256);
build.add_outputs("out", &[output_path.into_string()])
}
fn check_output_timestamps(&self) -> bool {
true
}
}
struct ExtractArchive<'a, I> {
pub archive_path: BuildInput,
/// The folder that the archive should be extracted into, relative to
/// $builddir/extracted. If the archive contains a single top-level
/// folder, its contents will be extracted into the provided folder, so
/// that output like tool-1.2/ can be extracted into tool/.
pub extraction_folder_name: &'a str,
/// Files contained inside the archive, relative to the archive root, and
/// excluding the top-level folder if it is the sole top-level entry.
/// Any files you wish to use as part of subsequent rules
/// must be declared here.
pub file_manifest: HashMap<&'static str, I>,
}
impl<I> ExtractArchive<'_, I> {
fn extraction_folder(&self) -> Utf8PathBuf {
Utf8Path::new("$builddir")
.join("extracted")
.join(self.extraction_folder_name)
}
}
impl<I> BuildAction for ExtractArchive<'_, I>
where
I: IntoIterator,
I::Item: AsRef<str>,
{
fn command(&self) -> &str {
"$runner archive extract $in $extraction_folder"
}
fn files(&mut self, build: &mut impl crate::build::FilesHandle) {
build.add_inputs("in", inputs![self.archive_path.clone()]);
let folder = self.extraction_folder();
build.add_variable("extraction_folder", folder.to_string());
for (subgroup, files) in self.file_manifest.drain() {
build.add_outputs_ext(
subgroup,
files
.into_iter()
.map(|f| folder.join(f.as_ref()).to_string()),
!subgroup.is_empty(),
);
}
build.add_output_stamp(folder.with_extension("marker"));
}
fn name(&self) -> &'static str {
"extract"
}
fn check_output_timestamps(&self) -> bool {
true
}
}
/// See [DownloadArchive] and [ExtractArchive].
pub fn download_and_extract<I>(
build: &mut Build,
group_name: &str,
archive: OnlineArchive,
file_manifest: HashMap<&'static str, I>,
) -> Result<()>
where
I: IntoIterator,
I::Item: AsRef<str>,
{
let download_group = format!("download:{group_name}");
build.add_action(&download_group, DownloadArchive { archive })?;
let extract_group = format!("extract:{group_name}");
build.add_action(
extract_group,
ExtractArchive {
archive_path: inputs![format!(":{download_group}")],
extraction_folder_name: group_name,
file_manifest,
},
)?;
Ok(())
}
pub fn empty_manifest() -> HashMap<&'static str, &'static [&'static str]> {
Default::default()
}