forked from haikuports/haikuporter
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
cargo-to-recipe.sh: new tool (haikuports#176)
This tool allows to partially automate the generation of a recipe from a Cargo dependency file. It reduces the amount of work needed to package Rust software.
- Loading branch information
Showing
1 changed file
with
329 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,329 @@ | ||
#!/usr/bin/env bash | ||
|
||
die() { | ||
printf %s "${*+$'\e[31merror: \e[0m'$*$'\n'}" | ||
(( usage )) && usage | ||
exit 1 | ||
} >&2 | ||
|
||
usage() { | ||
cat <<-EOF | ||
Usage: $0 [options] [uri://to/source-tarball] category/port | ||
Creates a recipe template for a crates.io package, filled with | ||
information at hand. Requires awk, coreutils, sed, tar (with the | ||
appropriate decompression utility), and wget. | ||
Options: | ||
-h, --help show this help message and exit | ||
-k, --keep keep the generated temporary directory | ||
-nc, --no-clobber | ||
do not overwrite existing files | ||
-psd, --print-source-directories | ||
also print SOURCE_DIRs | ||
-b, --bump | ||
bump the crates.io dependencies of the port's highest | ||
versioned recipe instead (overrides --no-clobber) | ||
-c CMD, --cmd=CMD | ||
specify the command runtime | ||
EOF | ||
} | ||
|
||
temp() { rm -rf "$tempdir"; } | ||
|
||
. "$(finddir B_USER_SETTINGS_DIRECTORY)"/haikuports.conf | ||
(( $# )) || usage=1 die | ||
while (( $# )); do | ||
case $1 in | ||
-h|--help) | ||
usage | ||
exit 0 | ||
;; | ||
-k|--keep) | ||
temp() { printf '%s\n' "Kept $tempdir"; } | ||
;; | ||
-nc|--no-clobber) | ||
nc=1 | ||
shopt -s expand_aliases | ||
alias mv='mv -n' | ||
alias cp='cp -n' | ||
;; | ||
-psd|--print-source-directories) | ||
psd=3 | ||
;; | ||
-b|--bump) | ||
bump=1 | ||
;; | ||
-c) | ||
shift | ||
;& | ||
--cmd=*) | ||
cmd=${1#*=} | ||
;; | ||
*://*) | ||
SOURCE_URI=$1 | ||
;; | ||
*-*/*) | ||
directory=$TREE_PATH/$1 | ||
: "${1//-/_}" | ||
portName=${_#*/} | ||
;; | ||
-?*) | ||
usage=1 die "Invalid option." | ||
;; | ||
*) | ||
usage=1 die "Invalid argument." | ||
;; | ||
esac | ||
shift | ||
done | ||
|
||
mkdir -p "$directory"/download | ||
cd "$directory" || die "Invalid port directory." | ||
|
||
if (( bump )); then | ||
eval "recipe=$( | ||
ls -v --quoting-style=shell-escape \ | ||
"$portName"-[0-9]*.[0-9]*.[0-9]*.recipe | tail -n 1 | ||
)" | ||
|
||
portVersionedName=${recipe%.recipe} | ||
portVersion=${portVersionedName#$portName-} | ||
|
||
defineDebugInfoPackage() { :; } | ||
getPackagePrefix() { :; } | ||
|
||
. "$recipe" || die "Sourcing the recipe file failed." | ||
fi | ||
|
||
: "${cmd:=$portName}" "${psd:=2}" "${SOURCE_URI%/}" | ||
source_file=${_##*/} | ||
: "${SOURCE_FILENAME:=$source_file}" | ||
|
||
case "" in | ||
$directory) | ||
usage=1 die "No category and/or port specified." | ||
;;& | ||
$SOURCE_URI) | ||
usage=1 die "SOURCE_URI is empty or unset." | ||
;;& | ||
$CHECKSUM_SHA256) | ||
if [[ nc -eq 0 || ! -e download/$SOURCE_FILENAME ]]; then | ||
wget -O download/"$SOURCE_FILENAME" "$SOURCE_URI" || | ||
die "Failed to download the source file." | ||
fi | ||
CHECKSUM_SHA256=1 | ||
;; | ||
esac | ||
|
||
if [[ $CHECKSUM_SHA256 != 1 ]]; then | ||
for (( i = 0; i < 3; ++i )); do | ||
printf '%s\n' "$CHECKSUM_SHA256 download/$SOURCE_FILENAME" | | ||
sha256sum -c && break | ||
(( i < 2 )) && wget -O download/"$SOURCE_FILENAME" \ | ||
"$( (( i < 1 )) && printf -- "-c")" "$SOURCE_URI" | ||
done || die "Checksum verification failed." | ||
else | ||
: "$(sha256sum download/"$SOURCE_FILENAME")" | ||
CHECKSUM_SHA256=${_::64} | ||
fi | ||
|
||
: "$(tar --exclude=*/* -tf download/"$SOURCE_FILENAME")" | ||
SOURCE_DIR=${_%/} | ||
if test "$SOURCE_FILENAME" != "$SOURCE_DIR.tar.${source_file##*.}"; then | ||
mv download/{"$SOURCE_FILENAME","$_"} | ||
SOURCE_FILENAME=${_##*/} | ||
fi | ||
|
||
tempdir=$(mktemp -d -t "$SOURCE_DIR".XXXXXX) | ||
trap temp 0 | ||
tar --transform "s|$SOURCE_DIR|${tempdir##*/}|" -C /tmp \ | ||
-xf download/"$SOURCE_FILENAME" --wildcards "$SOURCE_DIR/Cargo.*" || | ||
die "Failed to extract the necessary files." | ||
|
||
info=$( | ||
if grep -q '\[metadata\]' "$tempdir"/Cargo.lock; then | ||
sed -e '0,/\[metadata\]/d | ||
s/checksum // | ||
s/(.*)// | ||
s/ /-/ | ||
s/ = // | ||
s/"//g' "$tempdir"/Cargo.lock | ||
else | ||
awk -F \" ' | ||
/name|version|checksum/ { | ||
if ($1 ~ "name") | ||
i += 1 | ||
data[i] = data[i] "," $2 | ||
} | ||
END { | ||
for (j in data) { | ||
split(data[j], f, ",") | ||
if (length(f[4]) == 64) | ||
print f[2] " " f[3] " " f[4] | ||
} | ||
}' "$tempdir"/Cargo.lock | sort | sed 's/ /-/' | ||
fi | ||
) | ||
|
||
mapfile -t crates < <(awk '{ print $1".crate" }' <<< "$info") | ||
mapfile -t checksums < <(awk '{ print $2 }' <<< "$info") | ||
echo "$info" | ||
for crate in "${crates[@]}"; do | ||
uris+=("https://static.crates.io/crates/${crate%%-[0-9]*.[0-9]*.[0-9]*}/$crate") | ||
(( psd == 3 )) && dirs+=("${crate%.crate}") | ||
done | ||
|
||
for (( i = 0; j = i+2, i < ${#crates[@]}; i++ )); do | ||
source_uris+=("SOURCE_URI_$j=\"${uris[i]}\"") | ||
checksums_sha256+=("CHECKSUM_SHA256_$j=\"${checksums[i]}\"") | ||
merged+=("${source_uris[i]}" "${checksums_sha256[i]}") | ||
(( psd == 3 )) && { | ||
source_dirs+=("SOURCE_DIR_$j=\"${dirs[i]}\"") | ||
merged+=("${source_dirs[i]}") | ||
} | ||
done | ||
|
||
if (( bump )); then | ||
sed -i \ | ||
-e '/SOURCE_URI_2/,/ARCHITECTURES/ { /^A/!d }' \ | ||
-e "/^ARCHITECTURES/i $(printf '%s\n' "${merged[@]}" | | ||
sed '0~'"$psd"' a\\' | head -n -1 | | ||
sed -z 's/\n/\\n/g')" \ | ||
-e "s/{2\.\.[0-9]\+}/{2..$(( "${#crates[@]}" + 1 ))}/" \ | ||
"$recipe" | ||
exit | ||
fi | ||
|
||
eval "$( | ||
sed -n '/\[package\]/,/^$/ { | ||
/"""\|\[/d | ||
s/-\(.*=\)/_\1/ | ||
s/ = /=/p | ||
}' "$tempdir"/Cargo.toml | ||
)" | ||
cat <<EOF >"$tempdir/$portName-$version.recipe" | ||
SUMMARY="${description%.}" | ||
DESCRIPTION="$( | ||
extended=$( | ||
grep -q extended-description "$tempdir"/Cargo.toml && | ||
printf "extended-" | ||
) | ||
sed -n "/${extended}description"' = """/,/"""/ { | ||
s/.*description = // | ||
s/"""//g | ||
/^$/d | ||
p | ||
}' "$tempdir"/Cargo.toml | ||
)" | ||
HOMEPAGE="$homepage" | ||
COPYRIGHT="" | ||
LICENSE="$( | ||
IFS=$'\n' read -d "" -ra licenses <<< "$( | ||
sed -e 's,/\| AND \| OR ,\n,g | ||
s,-\([0-9]\)\.0, v\1,g' <<< "$license" | sort | ||
)" | ||
printf %s "${licenses[0]}" | ||
(( ${#licenses[@]} > 1 )) && printf '\n\t%s' "${licenses[@]:1}" | ||
)" | ||
REVISION="1" | ||
SOURCE_URI="$( | ||
: "${SOURCE_URI//$version/\$portVersion}" | ||
printf '%s\n' "${_/$homepage/\$HOMEPAGE}" | ||
)" | ||
CHECKSUM_SHA256="$CHECKSUM_SHA256" | ||
$( | ||
if [[ $source_file != "$SOURCE_FILENAME" ]]; then | ||
: "${SOURCE_FILENAME/$version/\$portVersion}" | ||
printf '%s\n' "SOURCE_FILENAME=\"$_\"" | ||
fi | ||
printf '\n' | ||
printf '%s\n' "${merged[@]}" | sed '0~'"$psd"' a\\' | ||
) | ||
ARCHITECTURES="!x86_gcc2 ?x86 ?x86_64" | ||
commandBinDir=\$binDir | ||
if [ "\$targetArchitecture" = x86_gcc2 ]; then | ||
SECONDARY_ARCHITECTURES="?x86" | ||
commandBinDir=\$prefix/bin | ||
fi | ||
PROVIDES=" | ||
$portName\$secondaryArchSuffix = \$portVersion | ||
cmd:$cmd | ||
" | ||
REQUIRES=" | ||
haiku\$secondaryArchSuffix | ||
" | ||
BUILD_REQUIRES=" | ||
haiku\${secondaryArchSuffix}_devel | ||
" | ||
BUILD_PREREQUIRES=" | ||
cmd:cargo\$secondaryArchSuffix | ||
cmd:gcc\$secondaryArchSuffix | ||
" | ||
defineDebugInfoPackage $portName\$secondaryArchSuffix \\ | ||
"\$commandBinDir"/$cmd | ||
BUILD() | ||
{ | ||
export CARGO_HOME=\$sourceDir/../cargo | ||
vendor=\$CARGO_HOME/haiku | ||
mkdir -p "\$vendor" | ||
for i in \$(seq 2 $(( ${#crates[@]} + 1 ))); do | ||
eval "srcDir=\\\$sourceDir\$i" | ||
eval "sha256sum=\\\$CHECKSUM_SHA256_\$i" | ||
set -- "\$srcDir"$( | ||
(( psd == 2 )) && printf "/*" | ||
) | ||
ln -sf "\$1" "\$vendor" | ||
cat <<-EOF >"\$vendor/\${1##*/}/.cargo-checksum.json" | ||
{ | ||
"package": "\$sha256sum", | ||
"files": {} | ||
} | ||
EOF | ||
done | ||
cat <<-EOF >"\$CARGO_HOME"/config | ||
[source.haiku] | ||
directory = "\$vendor" | ||
[source.crates-io] | ||
replace-with = "haiku" | ||
EOF | ||
cargo build --release --frozen | ||
} | ||
INSTALL() | ||
{ | ||
install -m 755 -d "\$commandBinDir" "\$docDir" | ||
install -m 755 target/release/$cmd "\$commandBinDir" | ||
install -m 644 README.md "\$docDir" | ||
} | ||
TEST() | ||
{ | ||
export CARGO_HOME=\$sourceDir/../cargo | ||
cargo test --release --frozen | ||
} | ||
EOF | ||
cp "$tempdir/$portName-$version.recipe" . | ||
|
||
if [[ -v license_file ]]; then | ||
cat <<-EOF | ||
----------------------------------------------------------------------- | ||
This port uses a custom license file. | ||
It will be installed to the port's license directory; please rename it | ||
as appropriate and add it to the recipe. | ||
EOF | ||
tar --transform "s|$SOURCE_DIR|${tempdir##*/}|" -C /tmp \ | ||
-xf download/"$SOURCE_FILENAME" "${license_file/#./$SOURCE_DIR}" | ||
mkdir -p licenses | ||
cp "$tempdir"/"$license_file" licenses | ||
fi |