Skip to content

Instantly share code, notes, and snippets.

@calebccff
Created January 19, 2025 03:27
Show Gist options
  • Save calebccff/fb0ed0b251f487c91e1a2037cb524697 to your computer and use it in GitHub Desktop.
Save calebccff/fb0ed0b251f487c91e1a2037cb524697 to your computer and use it in GitHub Desktop.
APKBUILD to JSON parser
#!/bin/busybox sh
# parse-pkg.sh: APKBUILD -> JSON parser
# This parses APKBUILD files to determine info
# ahead of time about the package that WOULD be
# built if abuild were invoked.
#
# Since APKBUILD files are literally shell scripts
# they can be fully dynamic, producing different
# output depending on the input.
#
# This script parses them by sourcing them just
# like abuild does, so it's also necessary to
# set any appropriate environment variables
# Print ALL variables when we run set
# not just environment variables
set -a
APKBUILD=$(realpath $1)
cleanup() {
/usr/bin/rm $ignore_file
/usr/bin/rmdir $tmpdir
}
trap cleanup EXIT
# Set up a temporary directory to run in
# to reduce the chance of the APKBUILD
# creating random files...
tmpdir=$(mktemp -d)
cd $tmpdir
chmod -w .
# We need to declare variables here that we don't want
# to be exported so they get included in the ignore file
is_first_line=true
inside_var=
ignore_file=$(mktemp)
subpkgname=
# the name of the subpkg function
subpkgsplit=
i=
# Try to minimise the chance of a subpackage messing with your filesystem
srcdir="$(pwd)"
basedir="$(pwd)"
# Record the variables used by this script and environment variables
# so we can filter them out
set > $ignore_file
subpkg_set() {
subpkgname=${1%%:*}
local _splitarch=${1#*:}
[ "$_splitarch" = "$1" ] && _splitarch=""
subpkgsplit=${_splitarch%%:*}
if [ -z "$subpkgsplit" ]; then
case $subpkgname in
*-bash-completion) subpkgsplit=bashcomp ;;
*-zsh-completion) subpkgsplit=zshcomp ;;
*-fish-completion) subpkgsplit=fishcomp ;;
*) subpkgsplit="${subpkgname##*-}" ;;
esac
fi
subpkgarch=${_splitarch#*:}
if [ "$subpkgarch" = "$_splitarch" -o -z "$subpkgarch" ]; then
case "$subpkgname" in
*-doc | *-openrc | *-lang | *sh-completion | *-pyc) subpkgarch="noarch" ;;
*) subpkgarch="$pkgarch" ;;
esac
fi
}
# Source the APKBUILD itself
. $APKBUILD
function dump_vars() {
# stdin: output of "set" command
is_first_line=true
while read -r var; do
name=${var%%=*}
# Check that $name isn't an ignored variable and that the line we
# read in matches the format "MY_VAR='"
if grep -q "^${name}=" $ignore_file || echo "$var" | grep -qv "^${name}='"; then
continue
fi
# Rewrite the subpackages variable so we can use
# the "subpackages" key to refer to the actual
# subpackage objects
if [ "$name" = "subpackages" ]; then
key="subpackage_names"
else
key="$name"
fi
val="$(eval echo \$$name)"
# Add a comma for the previous property
if ! $is_first_line; then
printf ",\n"
fi
printf "\"$key\": \"$val\""
is_first_line=false
done
printf "$1\n"
}
# Now handle the subpackages.
# The only way to figure out what variables
# they set is to run the function... but we
# REALLY don't want it to do random stuff in our
# environment so override some builtins
install() {
:
}
mv() {
:
}
touch() {
:
}
cp() {
:
}
amove() {
:
}
mkdir() {
:
}
ln() {
:
}
rm() {
:
}
rmdir() {
:
}
printf "{\n"
set | dump_vars ,
printf "\"subpackages\": {\n"
is_first_line=true
for i in $subpackages; do
if ! $is_first_line; then
printf ",\n"
fi
subpkg_set $i
printf "\"$subpkgname\": {\n"
cmd=$(cat <<-END
(unset \$(set | grep "=" | cut -d= -f1);
pkgname=${pkgname};
pkgver=${pkgver};
pkgrel=${pkgrel};
subpkg_set $i;
$subpkgsplit >/dev/null 2>&1
unset pkgname pkgver pkgrel;
set | dump_vars)
END
)
eval "$cmd"
printf "\n}"
is_first_line=false
done
printf "\n}\n"
printf "\n}\n"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment