mirror of
https://github.com/ankitects/anki.git
synced 2025-09-21 15:32:23 -04:00
186 lines
5.3 KiB
Rust
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()
|
|
}
|