mirror of
https://github.com/fosskers/cargo-aur.git
synced 2026-03-11 17:34:53 -05:00
Compare commits
74 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
99f36f8f47 | ||
|
|
94bd36af28 | ||
|
|
7e75c4bf57 | ||
|
|
4bebc62331 | ||
|
|
5df440310e | ||
|
|
d08e994b52 | ||
|
|
fc23ad83fe | ||
|
|
9707e33204 | ||
|
|
4080395c00 | ||
|
|
4d99751ee6 | ||
|
|
4da661c605 | ||
|
|
c36fdec13a | ||
|
|
37e93df347 | ||
|
|
d08192a721 | ||
|
|
2bc4b8c7b5 | ||
|
|
55fc12153b | ||
|
|
0484b382eb | ||
|
|
c4aa908438 | ||
|
|
00c323a77d | ||
|
|
9cca4c1737 | ||
|
|
506cbdaa7f | ||
|
|
eb0e27f77a | ||
|
|
1a0fbf55f9 | ||
|
|
24eaa15769 | ||
|
|
cee24138d1 | ||
|
|
40f348f311 | ||
|
|
7f85ed3725 | ||
|
|
698e14bb14 | ||
|
|
10704fbc75 | ||
|
|
7e7f3d5217 | ||
|
|
ff1b93096c | ||
|
|
1227ef8e36 | ||
|
|
c2661279b7 | ||
|
|
da899130ae | ||
|
|
8d0ba5be31 | ||
|
|
227c3a7391 | ||
|
|
0c485c43fc | ||
|
|
000b742a22 | ||
|
|
861fa59aa3 | ||
|
|
6a1269e4ae | ||
|
|
7e353578c0 | ||
|
|
f837fd18a6 | ||
|
|
84f4a740b1 | ||
|
|
d8105b04b3 | ||
|
|
c64d112a5d | ||
|
|
428b273d91 | ||
|
|
a8c3736539 | ||
|
|
5b413061b5 | ||
|
|
a32a16489f | ||
|
|
d6f9faefdd | ||
|
|
8dbdc1ab71 | ||
|
|
b7eb36b74d | ||
|
|
5f1a44ab30 | ||
|
|
b9e6f8ce6d | ||
|
|
deb0768f3b | ||
|
|
113a00c206 | ||
|
|
a676669e63 | ||
|
|
0ea8be5a73 | ||
|
|
b8e33deed7 | ||
|
|
ffed75dcb0 | ||
|
|
6dd2e4c674 | ||
|
|
6c40291afe | ||
|
|
e1c9ff6fe1 | ||
|
|
64acbe589a | ||
|
|
ee5ee91cbc | ||
|
|
42f34e25dd | ||
|
|
32dc963fcd | ||
|
|
d47558481a | ||
|
|
f7871899d9 | ||
|
|
06c5ab9b69 | ||
|
|
8903c72da6 | ||
|
|
109d9a66a7 | ||
|
|
8e223c5a2d | ||
|
|
b1383ff6ff |
2
.github/workflows/rust.yml
vendored
2
.github/workflows/rust.yml
vendored
@@ -14,7 +14,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v4
|
||||||
- name: Build
|
- name: Build
|
||||||
run: cargo build --verbose
|
run: cargo build --verbose
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
|
|||||||
103
CHANGELOG.md
103
CHANGELOG.md
@@ -1,5 +1,108 @@
|
|||||||
# `cargo-aur` Changelog
|
# `cargo-aur` Changelog
|
||||||
|
|
||||||
|
## 1.7.1 (2024-03-18)
|
||||||
|
|
||||||
|
#### Fixed
|
||||||
|
|
||||||
|
- The crypt startup error `unexpected free argument aur`.
|
||||||
|
|
||||||
|
## 1.7.0 (2024-03-07)
|
||||||
|
|
||||||
|
#### Added
|
||||||
|
|
||||||
|
- The `--output` flag for customizing the location of the output produced by
|
||||||
|
`cargo aur`. If unused, the default remains `target/cargo-aur/`.
|
||||||
|
- A new `files` field in `[package.metadata.aur]`, which accepts a list-of-pairs
|
||||||
|
of additional files you want copied to the user's filesystem upon package
|
||||||
|
installation. Output looks like:
|
||||||
|
|
||||||
|
```
|
||||||
|
package() {
|
||||||
|
install -Dm755 cargo-aur -t "$pkgdir/usr/bin"
|
||||||
|
install -Dm644 LICENSE "$pkgdir/usr/share/licenses/$pkgname/LICENSE"
|
||||||
|
install -Dm644 "/path/to/original/foo.txt" "$pkgdir/path/to/target/foo.txt"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Fixed
|
||||||
|
|
||||||
|
- Supply the `url` from backup sources if `homepage` is not specified in the `Cargo.toml`.
|
||||||
|
|
||||||
|
## 1.6.0 (2023-10-02)
|
||||||
|
|
||||||
|
#### Changed
|
||||||
|
|
||||||
|
- The `[package.metadata]` section for adding extra dependency information
|
||||||
|
should now be named `[package.metadata.aur]`. The old syntax will still work,
|
||||||
|
but you will be warned. This fixes a conflict with other `cargo` subcommands.
|
||||||
|
- The PKGBUILD and tarball are now output to `target/cargo-aur/` to avoid
|
||||||
|
cluttering the top-level of the repo.
|
||||||
|
- Reduced binary size.
|
||||||
|
|
||||||
|
#### Fixed
|
||||||
|
|
||||||
|
- LICENSE file checking is now done via SPDX identifiers.
|
||||||
|
|
||||||
|
## 1.5.0 (2022-04-20)
|
||||||
|
|
||||||
|
#### Added
|
||||||
|
|
||||||
|
- Support for `[[bin]]` sections in `Cargo.toml`, allowing you to specify custom
|
||||||
|
binary names separate from the package name. [#13]
|
||||||
|
- Support for specifying PKGBUILD `depends` and `optdepends` via
|
||||||
|
`[package.metadata]`, as in:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[package.metadata]
|
||||||
|
depends = ["nachos", "pizza"]
|
||||||
|
optdepends = ["sushi", "ramen"]
|
||||||
|
```
|
||||||
|
|
||||||
|
[#13]: https://github.com/fosskers/cargo-aur/pull/13
|
||||||
|
|
||||||
|
## 1.4.1 (2021-09-06)
|
||||||
|
|
||||||
|
#### Fixed
|
||||||
|
|
||||||
|
- `cargo aur` now respects `CARGO_TARGET_DIR`. [#6]
|
||||||
|
|
||||||
|
[#6]: https://github.com/fosskers/cargo-aur/pull/6
|
||||||
|
|
||||||
|
## 1.4.0 (2021-06-07)
|
||||||
|
|
||||||
|
#### Added
|
||||||
|
|
||||||
|
- The `conflicts` field is now added to the `PKGBUILD`.
|
||||||
|
- Progress messages in the terminal.
|
||||||
|
- `LICENSE` detection and installation. If your Rust crate has a license not
|
||||||
|
found in `/usr/share/licenses/common/` (like `MIT`), then `cargo aur` will
|
||||||
|
copy it into the source tarball and have the PKGBUILD install it. Naturally
|
||||||
|
this means you must actually have a `LICENSE` file in your project, or `cargo aur` will complain.
|
||||||
|
|
||||||
|
## 1.3.0 (2021-04-05)
|
||||||
|
|
||||||
|
#### Changed
|
||||||
|
|
||||||
|
- `cargo aur` no longer outputs `options=("strip")`, since this is set by
|
||||||
|
default in `/etc/makepkg.conf`.
|
||||||
|
|
||||||
|
## 1.2.0 (2020-08-24)
|
||||||
|
|
||||||
|
#### Added
|
||||||
|
|
||||||
|
- A `--version` flag to display the current version of `cargo-aur`.
|
||||||
|
|
||||||
|
## 1.1.2 (2020-08-11)
|
||||||
|
|
||||||
|
#### Added
|
||||||
|
|
||||||
|
- When using `--musl`, the user is warned if they don't have the
|
||||||
|
`x86_64-unknown-linux-musl` target installed.
|
||||||
|
|
||||||
|
#### Changed
|
||||||
|
|
||||||
|
- Run `strip` on the release binary before `tar`ring it.
|
||||||
|
|
||||||
## 1.1.1 (2020-08-11)
|
## 1.1.1 (2020-08-11)
|
||||||
|
|
||||||
#### Fixed
|
#### Fixed
|
||||||
|
|||||||
22
Cargo.toml
22
Cargo.toml
@@ -1,8 +1,8 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "cargo-aur"
|
name = "cargo-aur"
|
||||||
version = "1.1.1"
|
version = "1.7.1"
|
||||||
authors = ["Colin Woodbury <colin@fosskers.ca>"]
|
authors = ["Colin Woodbury <colin@fosskers.ca>"]
|
||||||
edition = "2018"
|
edition = "2021"
|
||||||
description = "Prepare Rust projects to be released on the Arch Linux User Repository."
|
description = "Prepare Rust projects to be released on the Arch Linux User Repository."
|
||||||
homepage = "https://github.com/fosskers/cargo-aur"
|
homepage = "https://github.com/fosskers/cargo-aur"
|
||||||
repository = "https://github.com/fosskers/cargo-aur"
|
repository = "https://github.com/fosskers/cargo-aur"
|
||||||
@@ -12,13 +12,19 @@ keywords = ["cargo", "subcommand", "archlinux", "aur"]
|
|||||||
categories = ["command-line-utilities"]
|
categories = ["command-line-utilities"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0"
|
colored = "2.0"
|
||||||
gumdrop = "0.8"
|
gumdrop = "0.8"
|
||||||
hmac-sha256 = "0.1"
|
hmac-sha256 = "1.1"
|
||||||
itertools = "0.9"
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
serde = "1.0"
|
toml = "0.8"
|
||||||
serde_derive = "1.0"
|
|
||||||
toml = "0.5"
|
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
lto = true
|
lto = true
|
||||||
|
strip = true
|
||||||
|
opt-level = "z"
|
||||||
|
codegen-units = 1
|
||||||
|
panic = "abort"
|
||||||
|
|
||||||
|
[package.metadata.aur]
|
||||||
|
# depends = ["blah"]
|
||||||
|
# files = [[".github/dependabot.yml", "/usr/local/share/cargo-aur/dependabot.yml"]]
|
||||||
|
|||||||
21
LICENSE
Normal file
21
LICENSE
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2020 - 2021 Colin Woodbury
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
76
README.md
76
README.md
@@ -1,27 +1,27 @@
|
|||||||
# cargo-aur
|
# cargo-aur
|
||||||
|
|
||||||
[](https://github.com/fosskers/cargo-aur/actions)
|
[][3]
|
||||||
[](https://crates.io/crates/cargo-aur)
|
[][4]
|
||||||

|
![AUR version][5]
|
||||||
|
|
||||||
`cargo-aur` is a new subcommand for `cargo` that produces a release tarball and
|
`cargo-aur` is a new subcommand for `cargo` that produces a release tarball and
|
||||||
PKGBUILD file for a Rust project, so that it can be released on the Arch Linux
|
PKGBUILD file for a Rust project, so that it can be released on the Arch Linux
|
||||||
User Repository (AUR).
|
User Repository (AUR).
|
||||||
|
|
||||||
No extra configuration is necessary. As long as your `Cargo.toml` has [the usual
|
No extra configuration is necessary. As long as your `Cargo.toml` has [the usual
|
||||||
fields](https://rust-lang.github.io/api-guidelines/documentation.html#c-metadata),
|
fields][0], a PKGBUILD will be generated with all the necessary sections filled
|
||||||
a PKGBUILD will be generated with all the necessary sections filled out.
|
out.
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
Guess what? `cargo-aur` itself is on the AUR! Install it with an AUR-compatible
|
Guess what? `cargo-aur` itself is on the AUR! Install it with an AUR-compatible
|
||||||
package manager like [`aura`](https://github.com/fosskers/aura):
|
package manager like [`aura`][1]:
|
||||||
|
|
||||||
```
|
```
|
||||||
sudo aura -A cargo-aur-bin
|
sudo aura -A cargo-aur-bin
|
||||||
```
|
```
|
||||||
|
|
||||||
... or via `cargo`:
|
...or via `cargo`:
|
||||||
|
|
||||||
```
|
```
|
||||||
cargo install cargo-aur
|
cargo install cargo-aur
|
||||||
@@ -29,13 +29,16 @@ cargo install cargo-aur
|
|||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
|
### Basics
|
||||||
|
|
||||||
Navigate to a Rust project, and run:
|
Navigate to a Rust project, and run:
|
||||||
|
|
||||||
```
|
```
|
||||||
cargo aur
|
cargo aur
|
||||||
```
|
```
|
||||||
|
|
||||||
This will produce a `foobar-1.2.3-x86_64.tar.gz` tarball and a PKGBUILD.
|
This will produce a `foobar-1.2.3-x86_64.tar.gz` tarball and a PKGBUILD within
|
||||||
|
`target/cargo-aur`.
|
||||||
|
|
||||||
If you wish, you can now run `makepkg` to ensure that your package actually builds.
|
If you wish, you can now run `makepkg` to ensure that your package actually builds.
|
||||||
|
|
||||||
@@ -61,3 +64,60 @@ At this point, it is up to you to:
|
|||||||
|
|
||||||
Some of these steps may be automated in `cargo aur` at a later date if there is
|
Some of these steps may be automated in `cargo aur` at a later date if there is
|
||||||
sufficient demand.
|
sufficient demand.
|
||||||
|
|
||||||
|
### Custom Binary Names
|
||||||
|
|
||||||
|
If you specify a `[[bin]]` section in your `Cargo.toml` and set the `name`
|
||||||
|
field, this will be used as the binary name to install within the PKGBUILD.
|
||||||
|
|
||||||
|
### `depends` and `optdepends`
|
||||||
|
|
||||||
|
If your package requires other Arch packages at runtime, you can specify these
|
||||||
|
within your `Cargo.toml` like this:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[package.metadata.aur]
|
||||||
|
depends = ["nachos", "pizza"]
|
||||||
|
optdepends = ["sushi", "ramen"]
|
||||||
|
```
|
||||||
|
|
||||||
|
And these settings will be copied to your PKGBUILD.
|
||||||
|
|
||||||
|
### Including Additional Files
|
||||||
|
|
||||||
|
The `files` list can be used to designated initial files to be copied the user's
|
||||||
|
filesystem. So this:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[package.metadata.aur]
|
||||||
|
files = [["path/to/local/foo.txt", "/usr/local/share/your-app/foo.txt"]]
|
||||||
|
```
|
||||||
|
|
||||||
|
will result in this:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
package() {
|
||||||
|
install -Dm755 your-app -t "$pkgdir/usr/bin"
|
||||||
|
install -Dm644 LICENSE "$pkgdir/usr/share/licenses/$pkgname/LICENSE"
|
||||||
|
install -Dm644 "path/to/local/foo.txt" "$pkgdir/usr/local/share/your-app/foo.txt"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Static Binaries
|
||||||
|
|
||||||
|
Run with `--musl` to produce a release binary that is statically linked via
|
||||||
|
[MUSL][2].
|
||||||
|
|
||||||
|
```
|
||||||
|
> cargo aur --musl
|
||||||
|
> cd target/x86_64-unknown-linux-musl/release/
|
||||||
|
> ldd <your-binary>
|
||||||
|
not a dynamic executable
|
||||||
|
```
|
||||||
|
|
||||||
|
[0]: https://rust-lang.github.io/api-guidelines/documentation.html#c-metadata
|
||||||
|
[1]: https://github.com/fosskers/aura
|
||||||
|
[2]: https://musl.libc.org/
|
||||||
|
[3]: https://github.com/fosskers/cargo-aur/actions
|
||||||
|
[4]: https://crates.io/crates/cargo-aur
|
||||||
|
[5]: https://img.shields.io/aur/version/cargo-aur-bin
|
||||||
|
|||||||
52
src/error.rs
Normal file
52
src/error.rs
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
//! Errors that can occur in this application.
|
||||||
|
|
||||||
|
use std::{fmt::Display, path::PathBuf};
|
||||||
|
|
||||||
|
pub(crate) enum Error {
|
||||||
|
IO(std::io::Error),
|
||||||
|
Toml(toml::de::Error),
|
||||||
|
Utf8(std::str::Utf8Error),
|
||||||
|
Utf8OsString,
|
||||||
|
MissingMuslTarget,
|
||||||
|
MissingLicense,
|
||||||
|
TargetNotAbsolute(PathBuf),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Error {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Error::IO(e) => write!(f, "{}", e),
|
||||||
|
Error::Toml(e) => write!(f, "{}", e),
|
||||||
|
Error::Utf8(e) => write!(f, "{}", e),
|
||||||
|
Error::Utf8OsString => write!(f, "The `OsString` was not UTF-8!"),
|
||||||
|
Error::MissingMuslTarget => write!(
|
||||||
|
f,
|
||||||
|
"Missing target! Try: rustup target add x86_64-unknown-linux-musl"
|
||||||
|
),
|
||||||
|
Error::MissingLicense => {
|
||||||
|
write!(f, "Missing LICENSE file. See https://choosealicense.com/")
|
||||||
|
}
|
||||||
|
Error::TargetNotAbsolute(p) => {
|
||||||
|
write!(f, "Target filepath is not absolute: {}", p.display())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<std::str::Utf8Error> for Error {
|
||||||
|
fn from(v: std::str::Utf8Error) -> Self {
|
||||||
|
Self::Utf8(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<toml::de::Error> for Error {
|
||||||
|
fn from(v: toml::de::Error) -> Self {
|
||||||
|
Self::Toml(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<std::io::Error> for Error {
|
||||||
|
fn from(v: std::io::Error) -> Self {
|
||||||
|
Self::IO(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
167
src/lib.rs
Normal file
167
src/lib.rs
Normal file
@@ -0,0 +1,167 @@
|
|||||||
|
//! Independently testable types and functions.
|
||||||
|
|
||||||
|
use serde::Deserialize;
|
||||||
|
use std::ops::Not;
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
|
/// The git forge in which a project's source code is stored.
|
||||||
|
pub enum GitHost {
|
||||||
|
Github,
|
||||||
|
Gitlab,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GitHost {
|
||||||
|
pub fn source(&self, package: &Package) -> String {
|
||||||
|
match self {
|
||||||
|
GitHost::Github => format!(
|
||||||
|
"{}/releases/download/v$pkgver/{}-$pkgver-x86_64.tar.gz",
|
||||||
|
package.repository, package.name
|
||||||
|
),
|
||||||
|
GitHost::Gitlab => format!(
|
||||||
|
"{}/-/archive/v$pkgver/{}-$pkgver-x86_64.tar.gz",
|
||||||
|
package.repository, package.name
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The critical fields read from a `Cargo.toml` and rewritten into a PKGBUILD.
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
pub struct Package {
|
||||||
|
pub name: String,
|
||||||
|
pub version: String,
|
||||||
|
pub authors: Vec<String>,
|
||||||
|
pub description: String,
|
||||||
|
pub repository: String,
|
||||||
|
pub license: String,
|
||||||
|
pub metadata: Option<Metadata>,
|
||||||
|
pub homepage: Option<String>,
|
||||||
|
pub documentation: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Package {
|
||||||
|
/// The name of the tarball that should be produced from this `Package`.
|
||||||
|
pub fn tarball(&self, output: &Path) -> PathBuf {
|
||||||
|
output.join(format!("{}-{}-x86_64.tar.gz", self.name, self.version))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn git_host(&self) -> Option<GitHost> {
|
||||||
|
if self.repository.starts_with("https://github") {
|
||||||
|
Some(GitHost::Github)
|
||||||
|
} else if self.repository.starts_with("https://gitlab") {
|
||||||
|
Some(GitHost::Gitlab)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Fetch the package URL from its `homepage`, `documentation` or
|
||||||
|
/// `repository` field.
|
||||||
|
pub fn url(&self) -> &str {
|
||||||
|
self.homepage
|
||||||
|
.as_deref()
|
||||||
|
.or(self.documentation.as_deref())
|
||||||
|
.unwrap_or(&self.repository)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// {
|
||||||
|
// Package {
|
||||||
|
// name: "aura".to_string(),
|
||||||
|
// version: "1.2.3".to_string(),
|
||||||
|
// authors: vec![],
|
||||||
|
// description: "".to_string(),
|
||||||
|
// homepage: "".to_string(),
|
||||||
|
// repository: "".to_string(),
|
||||||
|
// license: "".to_string(),
|
||||||
|
// metadata: None,
|
||||||
|
// }.tarball(Path::new("foobar"))
|
||||||
|
// }
|
||||||
|
|
||||||
|
/// The `[package.metadata]` TOML block.
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
pub struct Metadata {
|
||||||
|
/// Deprecated.
|
||||||
|
#[serde(default)]
|
||||||
|
pub depends: Vec<String>,
|
||||||
|
/// Deprecated.
|
||||||
|
#[serde(default)]
|
||||||
|
pub optdepends: Vec<String>,
|
||||||
|
/// > [package.metadata.aur]
|
||||||
|
pub aur: Option<AUR>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Metadata {
|
||||||
|
/// The metadata block actually has some contents.
|
||||||
|
pub fn non_empty(&self) -> bool {
|
||||||
|
self.depends.is_empty().not()
|
||||||
|
|| self.optdepends.is_empty().not()
|
||||||
|
|| self
|
||||||
|
.aur
|
||||||
|
.as_ref()
|
||||||
|
.is_some_and(|aur| aur.depends.is_empty().not() || aur.optdepends.is_empty().not())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for Metadata {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
// Reconcile which section to read extra dependency information from.
|
||||||
|
// The format we hope the user is using is:
|
||||||
|
//
|
||||||
|
// > [package.metadata.aur]
|
||||||
|
//
|
||||||
|
// But version 1.5 originally supported:
|
||||||
|
//
|
||||||
|
// > [package.metadata]
|
||||||
|
//
|
||||||
|
// To avoid a sudden breakage for users, we support both definition
|
||||||
|
// locations but favour the newer one.
|
||||||
|
//
|
||||||
|
// We print a warning to the user elsewhere if they're still using the
|
||||||
|
// old way.
|
||||||
|
let (deps, opts) = if let Some(aur) = self.aur.as_ref() {
|
||||||
|
(aur.depends.as_slice(), aur.optdepends.as_slice())
|
||||||
|
} else {
|
||||||
|
(self.depends.as_slice(), self.optdepends.as_slice())
|
||||||
|
};
|
||||||
|
|
||||||
|
match deps {
|
||||||
|
[middle @ .., last] => {
|
||||||
|
write!(f, "depends=(")?;
|
||||||
|
for item in middle {
|
||||||
|
write!(f, "\"{}\" ", item)?;
|
||||||
|
}
|
||||||
|
if opts.is_empty().not() {
|
||||||
|
writeln!(f, "\"{}\")", last)?;
|
||||||
|
} else {
|
||||||
|
write!(f, "\"{}\")", last)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
[] => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
match opts {
|
||||||
|
[middle @ .., last] => {
|
||||||
|
write!(f, "optdepends=(")?;
|
||||||
|
for item in middle {
|
||||||
|
write!(f, "\"{}\" ", item)?;
|
||||||
|
}
|
||||||
|
write!(f, "\"{}\")", last)?;
|
||||||
|
}
|
||||||
|
[] => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The inner values of a `[package.metadata.aur]` TOML block.
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
pub struct AUR {
|
||||||
|
#[serde(default)]
|
||||||
|
depends: Vec<String>,
|
||||||
|
#[serde(default)]
|
||||||
|
optdepends: Vec<String>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub files: Vec<(PathBuf, PathBuf)>,
|
||||||
|
}
|
||||||
410
src/main.rs
410
src/main.rs
@@ -1,173 +1,351 @@
|
|||||||
|
mod error;
|
||||||
|
|
||||||
|
use crate::error::Error;
|
||||||
|
use cargo_aur::{GitHost, Package};
|
||||||
|
use colored::*;
|
||||||
use gumdrop::{Options, ParsingStyle};
|
use gumdrop::{Options, ParsingStyle};
|
||||||
use hmac_sha256::Hash;
|
use hmac_sha256::Hash;
|
||||||
use itertools::Itertools;
|
use serde::Deserialize;
|
||||||
use serde_derive::Deserialize;
|
use std::fs::{DirEntry, File};
|
||||||
use std::fs;
|
use std::io::{BufWriter, Write};
|
||||||
use std::process::{self, Command};
|
use std::ops::Not;
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
use std::process::{Command, ExitCode};
|
||||||
|
|
||||||
|
/// Licenses available from the Arch Linux `licenses` package.
|
||||||
|
///
|
||||||
|
/// That package contains other licenses, but I've excluded here those unlikely
|
||||||
|
/// to be used by Rust crates.
|
||||||
|
const LICENSES: &[&str] = &[
|
||||||
|
"AGPL-3.0-only",
|
||||||
|
"AGPL-3.0-or-later",
|
||||||
|
"Apache-2.0",
|
||||||
|
"BSL-1.0", // Boost Software License.
|
||||||
|
"GPL-2.0-only",
|
||||||
|
"GPL-2.0-or-later",
|
||||||
|
"GPL-3.0-only",
|
||||||
|
"GPL-3.0-or-later",
|
||||||
|
"LGPL-2.0-only",
|
||||||
|
"LGPL-2.0-or-later",
|
||||||
|
"LGPL-3.0-only",
|
||||||
|
"LGPL-3.0-or-later",
|
||||||
|
"MPL-2.0", // Mozilla Public License.
|
||||||
|
"Unlicense", // Not to be confused with "Unlicensed".
|
||||||
|
];
|
||||||
|
|
||||||
#[derive(Options)]
|
#[derive(Options)]
|
||||||
struct Args {
|
struct Args {
|
||||||
|
/// Display this help message.
|
||||||
help: bool,
|
help: bool,
|
||||||
|
/// Display the current version of this software.
|
||||||
#[options(free)]
|
version: bool,
|
||||||
args: Vec<String>,
|
/// Set a custom output directory (default: target/).
|
||||||
|
output: Option<PathBuf>,
|
||||||
/// Use the MUSL build target to produce a static binary.
|
/// Use the MUSL build target to produce a static binary.
|
||||||
musl: bool,
|
musl: bool,
|
||||||
}
|
/// Don't actually build anything.
|
||||||
|
dryrun: bool,
|
||||||
enum GitHost {
|
/// Absorbs any extra junk arguments.
|
||||||
Github,
|
#[options(free)]
|
||||||
Gitlab,
|
free: Vec<String>,
|
||||||
}
|
|
||||||
|
|
||||||
impl GitHost {
|
|
||||||
fn source(&self, package: &Package) -> String {
|
|
||||||
match self {
|
|
||||||
GitHost::Github => format!(
|
|
||||||
"{}/releases/download/v$pkgver/{}-$pkgver-x86_64.tar.gz",
|
|
||||||
package.repository, package.name
|
|
||||||
),
|
|
||||||
GitHost::Gitlab => format!(
|
|
||||||
"{}/-/archive/v$pkgver/{}-$pkgver-x86_64.tar.gz",
|
|
||||||
package.repository, package.name
|
|
||||||
),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Debug)]
|
#[derive(Deserialize, Debug)]
|
||||||
struct Config {
|
struct Config {
|
||||||
package: Package,
|
package: Package,
|
||||||
|
#[serde(default)]
|
||||||
|
bin: Vec<Binary>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Config {
|
||||||
|
/// The name of the compiled binary that should be copied to the tarball.
|
||||||
|
fn binary_name(&self) -> &str {
|
||||||
|
self.bin
|
||||||
|
.first()
|
||||||
|
.map(|bin| bin.name.as_str())
|
||||||
|
.unwrap_or(self.package.name.as_str())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Debug)]
|
#[derive(Deserialize, Debug)]
|
||||||
struct Package {
|
struct Binary {
|
||||||
name: String,
|
name: String,
|
||||||
version: String,
|
|
||||||
authors: Vec<String>,
|
|
||||||
description: String,
|
|
||||||
homepage: String,
|
|
||||||
repository: String,
|
|
||||||
license: String,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Package {
|
fn main() -> ExitCode {
|
||||||
/// The name of the tarball that should be produced from this `Package`.
|
|
||||||
fn tarball(&self) -> String {
|
|
||||||
format!("{}-{}-x86_64.tar.gz", self.name, self.version)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn git_host(&self) -> Option<GitHost> {
|
|
||||||
if self.repository.starts_with("https://github") {
|
|
||||||
Some(GitHost::Github)
|
|
||||||
} else if self.repository.starts_with("https://gitlab") {
|
|
||||||
Some(GitHost::Gitlab)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let args = Args::parse_args_or_exit(ParsingStyle::AllOptions);
|
let args = Args::parse_args_or_exit(ParsingStyle::AllOptions);
|
||||||
|
|
||||||
if let Err(e) = work(args) {
|
if args.version {
|
||||||
eprintln!("{}", e);
|
let version = env!("CARGO_PKG_VERSION");
|
||||||
process::exit(1)
|
println!("{}", version);
|
||||||
|
ExitCode::SUCCESS
|
||||||
|
} else if let Err(e) = work(args) {
|
||||||
|
eprintln!("{} {}: {}", "::".bold(), "Error".bold().red(), e);
|
||||||
|
ExitCode::FAILURE
|
||||||
|
} else {
|
||||||
|
println!("{} {}", "::".bold(), "Done.".bold().green());
|
||||||
|
ExitCode::SUCCESS
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn work(args: Args) -> anyhow::Result<()> {
|
fn work(args: Args) -> Result<(), Error> {
|
||||||
|
// We can't proceed if the user has specified `--musl` but doesn't have the
|
||||||
|
// target installed.
|
||||||
|
if args.musl {
|
||||||
|
p("Checking for musl toolchain...".bold());
|
||||||
|
musl_check()?
|
||||||
|
}
|
||||||
|
|
||||||
|
// Where cargo expects to read and write to. By default we want to read the
|
||||||
|
// built binary from `target/release` and we want to write our results to
|
||||||
|
// `target/cargo-aur`, but these are configurable by the user.
|
||||||
|
let cargo_target: PathBuf = match std::env::var_os("CARGO_TARGET_DIR") {
|
||||||
|
Some(p) => PathBuf::from(p),
|
||||||
|
None => PathBuf::from("target"),
|
||||||
|
};
|
||||||
|
|
||||||
|
let output = args.output.unwrap_or(cargo_target.join("cargo-aur"));
|
||||||
|
|
||||||
|
// Ensure the target can actually be written to. Otherwise the `tar`
|
||||||
|
// operation later on will fail.
|
||||||
|
std::fs::create_dir_all(&output)?;
|
||||||
|
|
||||||
let config = cargo_config()?;
|
let config = cargo_config()?;
|
||||||
release_build(args.musl)?;
|
|
||||||
tarball(args.musl, &config.package)?;
|
// Warn if the user if still using the old metadata definition style.
|
||||||
let sha256 = sha256sum(&config.package)?;
|
if let Some(metadata) = config.package.metadata.as_ref() {
|
||||||
let pkgbuild = pkgbuild(&config.package, &sha256);
|
if metadata.depends.is_empty().not() || metadata.optdepends.is_empty().not() {
|
||||||
fs::write("PKGBUILD", pkgbuild)?;
|
p("Use of [package.metadata] is deprecated. Please specify extra dependencies under [package.metadata.aur].".bold().yellow());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let license = if must_copy_license(&config.package.license) {
|
||||||
|
p("LICENSE file will be installed manually.".bold().yellow());
|
||||||
|
Some(license_file()?)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
if args.dryrun.not() {
|
||||||
|
release_build(args.musl)?;
|
||||||
|
tarball(args.musl, &cargo_target, &output, license.as_ref(), &config)?;
|
||||||
|
let sha256: String = sha256sum(&config.package, &output)?;
|
||||||
|
|
||||||
|
// Write the PKGBUILD.
|
||||||
|
let path = output.join("PKGBUILD");
|
||||||
|
let file = BufWriter::new(File::create(path)?);
|
||||||
|
pkgbuild(file, &config, &sha256, license.as_ref())?;
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cargo_config() -> anyhow::Result<Config> {
|
/// Read the `Cargo.toml` for all the fields of concern to this tool.
|
||||||
let content = fs::read_to_string("Cargo.toml")?;
|
fn cargo_config() -> Result<Config, Error> {
|
||||||
let proj = toml::from_str(&content)?;
|
// NOTE 2023-11-27 Yes it looks silly to be reading the whole thing into a
|
||||||
Ok(proj) // TODO Would like to do this in one line with the above.
|
// string here, but the `toml` library doesn't allow deserialization from
|
||||||
|
// anything else but a string.
|
||||||
|
let content = std::fs::read_to_string("Cargo.toml")?;
|
||||||
|
let proj: Config = toml::from_str(&content)?;
|
||||||
|
Ok(proj)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Produce a legal PKGBUILD.
|
/// If a AUR package's license isn't included in `/usr/share/licenses/common/`,
|
||||||
fn pkgbuild(package: &Package, sha256: &str) -> String {
|
/// then it must be installed manually by the PKGBUILD. MIT and BSD3 are such
|
||||||
format!(
|
/// missing licenses, and since many Rust crates use them we must make this
|
||||||
r#"{}
|
/// check.
|
||||||
pkgname={}-bin
|
fn must_copy_license(license: &str) -> bool {
|
||||||
pkgver={}
|
LICENSES.contains(&license).not()
|
||||||
pkgrel=1
|
}
|
||||||
pkgdesc="{}"
|
|
||||||
url="{}"
|
|
||||||
license=("{}")
|
|
||||||
arch=("x86_64")
|
|
||||||
provides=("{}")
|
|
||||||
options=("strip")
|
|
||||||
source=("{}")
|
|
||||||
sha256sums=("{}")
|
|
||||||
|
|
||||||
package() {{
|
/// The path to the `LICENSE` file.
|
||||||
install -Dm755 {} -t "$pkgdir/usr/bin/"
|
fn license_file() -> Result<DirEntry, Error> {
|
||||||
}}
|
std::fs::read_dir(".")?
|
||||||
"#,
|
.filter_map(|entry| entry.ok())
|
||||||
package
|
.find(|entry| {
|
||||||
.authors
|
entry
|
||||||
.iter()
|
.file_name()
|
||||||
.map(|a| format!("# Maintainer: {}", a))
|
.to_str()
|
||||||
.join("\n"),
|
.map(|s| s.starts_with("LICENSE"))
|
||||||
package.name,
|
.unwrap_or(false)
|
||||||
package.version,
|
})
|
||||||
package.description,
|
.ok_or(Error::MissingLicense)
|
||||||
package.homepage,
|
}
|
||||||
package.license,
|
|
||||||
package.name,
|
/// Write a legal PKGBUILD to some `Write` instance (a `File` in this case).
|
||||||
package
|
fn pkgbuild<T>(
|
||||||
.git_host()
|
mut file: T,
|
||||||
.unwrap_or(GitHost::Github)
|
config: &Config,
|
||||||
.source(package),
|
sha256: &str,
|
||||||
sha256,
|
license: Option<&DirEntry>,
|
||||||
package.name,
|
) -> Result<(), Error>
|
||||||
)
|
where
|
||||||
|
T: Write,
|
||||||
|
{
|
||||||
|
let package = &config.package;
|
||||||
|
let authors = package
|
||||||
|
.authors
|
||||||
|
.iter()
|
||||||
|
.map(|a| format!("# Maintainer: {}", a))
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join("\n");
|
||||||
|
let source = package
|
||||||
|
.git_host()
|
||||||
|
.unwrap_or(GitHost::Github)
|
||||||
|
.source(&config.package);
|
||||||
|
|
||||||
|
writeln!(file, "{}", authors)?;
|
||||||
|
writeln!(file, "#")?;
|
||||||
|
writeln!(
|
||||||
|
file,
|
||||||
|
"# This PKGBUILD was generated by `cargo aur`: https://crates.io/crates/cargo-aur"
|
||||||
|
)?;
|
||||||
|
writeln!(file)?;
|
||||||
|
writeln!(file, "pkgname={}-bin", package.name)?;
|
||||||
|
writeln!(file, "pkgver={}", package.version)?;
|
||||||
|
writeln!(file, "pkgrel=1")?;
|
||||||
|
writeln!(file, "pkgdesc=\"{}\"", package.description)?;
|
||||||
|
writeln!(file, "url=\"{}\"", package.url())?;
|
||||||
|
writeln!(file, "license=(\"{}\")", package.license)?;
|
||||||
|
writeln!(file, "arch=(\"x86_64\")")?;
|
||||||
|
writeln!(file, "provides=(\"{}\")", package.name)?;
|
||||||
|
writeln!(file, "conflicts=(\"{}\")", package.name)?;
|
||||||
|
|
||||||
|
match package.metadata.as_ref() {
|
||||||
|
Some(metadata) if metadata.non_empty() => {
|
||||||
|
writeln!(file, "{}", metadata)?;
|
||||||
|
}
|
||||||
|
Some(_) | None => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
writeln!(file, "source=(\"{}\")", source)?;
|
||||||
|
writeln!(file, "sha256sums=(\"{}\")", sha256)?;
|
||||||
|
writeln!(file)?;
|
||||||
|
writeln!(file, "package() {{")?;
|
||||||
|
writeln!(
|
||||||
|
file,
|
||||||
|
" install -Dm755 {} -t \"$pkgdir/usr/bin\"",
|
||||||
|
config.binary_name()
|
||||||
|
)?;
|
||||||
|
|
||||||
|
if let Some(lic) = license {
|
||||||
|
let file_name = lic
|
||||||
|
.file_name()
|
||||||
|
.into_string()
|
||||||
|
.map_err(|_| Error::Utf8OsString)?;
|
||||||
|
writeln!(
|
||||||
|
file,
|
||||||
|
" install -Dm644 {} \"$pkgdir/usr/share/licenses/$pkgname/{}\"",
|
||||||
|
file_name, file_name
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(aur) = package.metadata.as_ref().and_then(|m| m.aur.as_ref()) {
|
||||||
|
for (source, target) in aur.files.iter() {
|
||||||
|
if target.has_root().not() {
|
||||||
|
return Err(Error::TargetNotAbsolute(target.to_path_buf()));
|
||||||
|
} else {
|
||||||
|
writeln!(
|
||||||
|
file,
|
||||||
|
" install -Dm644 \"{}\" \"$pkgdir{}\"",
|
||||||
|
source.display(),
|
||||||
|
target.display()
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
writeln!(file, "}}")?;
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Run `cargo build --release`, potentially building statically.
|
/// Run `cargo build --release`, potentially building statically.
|
||||||
fn release_build(musl: bool) -> anyhow::Result<()> {
|
fn release_build(musl: bool) -> Result<(), Error> {
|
||||||
let mut args = vec!["build", "--release"];
|
let mut args = vec!["build", "--release"];
|
||||||
|
|
||||||
if musl {
|
if musl {
|
||||||
args.push("--target=x86_64-unknown-linux-musl");
|
args.push("--target=x86_64-unknown-linux-musl");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
p("Running release build...".bold());
|
||||||
Command::new("cargo").args(args).status()?;
|
Command::new("cargo").args(args).status()?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tarball(musl: bool, package: &Package) -> anyhow::Result<()> {
|
fn tarball(
|
||||||
let binary = if musl {
|
musl: bool,
|
||||||
format!("target/x86_64-unknown-linux-musl/release/{}", package.name)
|
cargo_target: &Path,
|
||||||
|
output: &Path,
|
||||||
|
license: Option<&DirEntry>,
|
||||||
|
config: &Config,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
let release_dir = if musl {
|
||||||
|
"x86_64-unknown-linux-musl/release"
|
||||||
} else {
|
} else {
|
||||||
format!("target/release/{}", package.name)
|
"release"
|
||||||
};
|
};
|
||||||
|
|
||||||
fs::copy(binary, &package.name)?;
|
let binary_name = config.binary_name();
|
||||||
Command::new("tar")
|
let binary = cargo_target.join(release_dir).join(binary_name);
|
||||||
|
|
||||||
|
strip(&binary)?;
|
||||||
|
std::fs::copy(binary, binary_name)?;
|
||||||
|
|
||||||
|
// Create the tarball.
|
||||||
|
p("Packing tarball...".bold());
|
||||||
|
let mut command = Command::new("tar");
|
||||||
|
command
|
||||||
.arg("czf")
|
.arg("czf")
|
||||||
.arg(package.tarball())
|
.arg(config.package.tarball(output))
|
||||||
.arg(&package.name)
|
.arg(binary_name);
|
||||||
.status()?;
|
if let Some(lic) = license {
|
||||||
fs::remove_file(&package.name)?;
|
command.arg(lic.path());
|
||||||
|
}
|
||||||
|
if let Some(files) = config
|
||||||
|
.package
|
||||||
|
.metadata
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|m| m.aur.as_ref())
|
||||||
|
.map(|a| a.files.as_slice())
|
||||||
|
{
|
||||||
|
for (file, _) in files {
|
||||||
|
command.arg(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
command.status()?;
|
||||||
|
|
||||||
|
std::fs::remove_file(binary_name)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sha256sum(package: &Package) -> anyhow::Result<String> {
|
/// Strip the release binary, so that we aren't compressing more bytes than we
|
||||||
let bytes = fs::read(package.tarball())?;
|
/// need to.
|
||||||
|
fn strip(path: &Path) -> Result<(), Error> {
|
||||||
|
p("Stripping binary...".bold());
|
||||||
|
Command::new("strip").arg(path).status()?;
|
||||||
|
Ok(()) // FIXME Would love to use my `void` package here and elsewhere.
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sha256sum(package: &Package, output: &Path) -> Result<String, Error> {
|
||||||
|
let bytes = std::fs::read(package.tarball(output))?;
|
||||||
let digest = Hash::hash(&bytes);
|
let digest = Hash::hash(&bytes);
|
||||||
let hex = digest.iter().map(|u| format!("{:02x}", u)).collect();
|
let hex = digest.iter().map(|u| format!("{:02x}", u)).collect();
|
||||||
Ok(hex)
|
Ok(hex)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Does the user have the `x86_64-unknown-linux-musl` target installed?
|
||||||
|
fn musl_check() -> Result<(), Error> {
|
||||||
|
let args = ["target", "list", "--installed"];
|
||||||
|
let output = Command::new("rustup").args(args).output()?.stdout;
|
||||||
|
|
||||||
|
std::str::from_utf8(&output)?
|
||||||
|
.lines()
|
||||||
|
.any(|tc| tc == "x86_64-unknown-linux-musl")
|
||||||
|
.then_some(())
|
||||||
|
.ok_or(Error::MissingMuslTarget)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn p(msg: ColoredString) {
|
||||||
|
println!("{} {}", "::".bold(), msg)
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user