416 lines
12 KiB
Bash
Executable File
416 lines
12 KiB
Bash
Executable File
#!/bin/bash
|
|
set -euo pipefail
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
PROJECT_DIR="$SCRIPT_DIR/sing-box-rr"
|
|
GO_BIN="${GO_BIN:-/usr/local/go/bin/go}"
|
|
GO_CACHE_DIR="${GO_CACHE_DIR:-/tmp/sing-box-rr-go-build}"
|
|
GO_MODCACHE_DIR="${GO_MODCACHE_DIR:-/tmp/sing-box-rr-go-mod}"
|
|
LD_LLD_BIN="${LD_LLD_BIN:-}"
|
|
SDK_ARM64="/home/fresa/sdk/openwrt-sdk-24.10.4-mediatek-filogic_gcc-13.3.0_musl.Linux-x86_64"
|
|
SDK_X86="/home/fresa/sdk/openwrt-sdk-24.10.4-x86-64_gcc-13.3.0_musl.Linux-x86_64"
|
|
PACKAGES_DIR="$SCRIPT_DIR/packages/24.10"
|
|
PKG_NAME="sing-box-rr"
|
|
FORCE_REBUILD=0
|
|
|
|
LOG_DIR="$SCRIPT_DIR/build-logs"
|
|
mkdir -p "$LOG_DIR"
|
|
LOG_FILE="$LOG_DIR/build-ipk-$(date +%Y%m%d-%H%M%S).log"
|
|
LOG_LATEST="$LOG_DIR/build-ipk-latest.log"
|
|
: > "$LOG_FILE"
|
|
ln -sf "$(basename "$LOG_FILE")" "$LOG_LATEST"
|
|
ls -1t "$LOG_DIR"/build-ipk-*.log 2>/dev/null | tail -n +11 | xargs -r rm -f
|
|
|
|
echo "=== Logging to $LOG_FILE ==="
|
|
|
|
run() { "$@" 2>&1 | tee -a "$LOG_FILE"; return "${PIPESTATUS[0]}"; }
|
|
|
|
project_version() {
|
|
if [ -f "$SCRIPT_DIR/VERSION" ]; then
|
|
cat "$SCRIPT_DIR/VERSION"
|
|
else
|
|
echo "0.0.0-unknown-r1"
|
|
fi
|
|
}
|
|
|
|
project_source_date_epoch() {
|
|
if [ -f "$SCRIPT_DIR/SOURCE_DATE_EPOCH" ]; then
|
|
cat "$SCRIPT_DIR/SOURCE_DATE_EPOCH"
|
|
else
|
|
date +%s
|
|
fi
|
|
}
|
|
|
|
project_version_base() {
|
|
project_version | sed -E 's/-r[0-9]+$//'
|
|
}
|
|
|
|
package_release() {
|
|
local release
|
|
release="$(project_version | sed -nE 's/^.*-r([0-9]+)$/\1/p' | head -n1)"
|
|
if [ -n "$release" ]; then
|
|
echo "$release"
|
|
else
|
|
echo "1"
|
|
fi
|
|
}
|
|
|
|
ipk_version() {
|
|
echo "$(project_version_base)-r$(package_release)"
|
|
}
|
|
|
|
build_tags() {
|
|
tr -d '\n' < "$PROJECT_DIR/release/DEFAULT_BUILD_TAGS_OTHERS"
|
|
}
|
|
|
|
has_build_tag() {
|
|
printf '%s' "$(build_tags)" | tr ',' '\n' | grep -qx "$1"
|
|
}
|
|
|
|
uses_musl_toolchain() {
|
|
has_build_tag "with_musl"
|
|
}
|
|
|
|
prefers_lld_linker() {
|
|
has_build_tag "with_naive_outbound"
|
|
}
|
|
|
|
goarch_to_sdk_cc_name() {
|
|
case "$1" in
|
|
arm64) echo "aarch64-openwrt-linux-musl-gcc" ;;
|
|
amd64) echo "x86_64-openwrt-linux-musl-gcc" ;;
|
|
*) return 1 ;;
|
|
esac
|
|
}
|
|
|
|
goarch_to_sdk_cxx_name() {
|
|
case "$1" in
|
|
arm64) echo "aarch64-openwrt-linux-musl-g++" ;;
|
|
amd64) echo "x86_64-openwrt-linux-musl-g++" ;;
|
|
*) return 1 ;;
|
|
esac
|
|
}
|
|
|
|
find_toolchain_binary() {
|
|
local sdk_dir="$1" binary_name="$2"
|
|
find "$sdk_dir/staging_dir" -type f -path "*/bin/$binary_name" | head -n1
|
|
}
|
|
|
|
goarch_to_sdk_ld_bfd_name() {
|
|
case "$1" in
|
|
arm64) echo "aarch64-openwrt-linux-musl-ld.bfd" ;;
|
|
amd64) echo "x86_64-openwrt-linux-musl-ld.bfd" ;;
|
|
*) return 1 ;;
|
|
esac
|
|
}
|
|
|
|
goarch_to_sdk_ld_name() {
|
|
case "$1" in
|
|
arm64) echo "aarch64-openwrt-linux-musl-ld" ;;
|
|
amd64) echo "x86_64-openwrt-linux-musl-ld" ;;
|
|
*) return 1 ;;
|
|
esac
|
|
}
|
|
|
|
find_linker_binary() {
|
|
local sdk_dir="$1" goarch="$2"
|
|
local ld_bfd_name ld_name ld_bin
|
|
|
|
if [ -n "$LD_LLD_BIN" ]; then
|
|
if [ -x "$LD_LLD_BIN" ]; then
|
|
echo "$LD_LLD_BIN"
|
|
return 0
|
|
fi
|
|
echo "configured LD_LLD_BIN is not executable: $LD_LLD_BIN" | tee -a "$LOG_FILE"
|
|
exit 1
|
|
fi
|
|
|
|
ld_bfd_name="$(goarch_to_sdk_ld_bfd_name "$goarch")" || return 1
|
|
ld_name="$(goarch_to_sdk_ld_name "$goarch")" || return 1
|
|
|
|
if prefers_lld_linker; then
|
|
ld_bin="$(find_lld_binary "$sdk_dir")"
|
|
if [ -n "$ld_bin" ]; then
|
|
echo "$ld_bin"
|
|
return 0
|
|
fi
|
|
else
|
|
ld_bin="$(find_toolchain_binary "$sdk_dir" "$ld_bfd_name")"
|
|
if [ -n "$ld_bin" ]; then
|
|
echo "$ld_bin"
|
|
return 0
|
|
fi
|
|
|
|
ld_bin="$(find_toolchain_binary "$sdk_dir" "$ld_name")"
|
|
if [ -n "$ld_bin" ]; then
|
|
echo "$ld_bin"
|
|
return 0
|
|
fi
|
|
fi
|
|
|
|
if ! prefers_lld_linker; then
|
|
find_lld_binary "$sdk_dir"
|
|
return 0
|
|
fi
|
|
|
|
ld_bin="$(find_toolchain_binary "$sdk_dir" "$ld_bfd_name")"
|
|
if [ -n "$ld_bin" ]; then
|
|
echo "$ld_bin"
|
|
return 0
|
|
fi
|
|
|
|
find_toolchain_binary "$sdk_dir" "$ld_name"
|
|
}
|
|
|
|
find_lld_binary() {
|
|
local sdk_dir="$1"
|
|
local lld_bin
|
|
lld_bin="$(find "$sdk_dir/staging_dir/host" -maxdepth 3 -name 'ld.lld' | head -n1)"
|
|
if [ -n "$lld_bin" ]; then
|
|
echo "$lld_bin"
|
|
return 0
|
|
fi
|
|
find "$sdk_dir/staging_dir/host" -maxdepth 3 -type f -name 'lld' | head -n1
|
|
}
|
|
|
|
prepare_musl_toolchain() {
|
|
local sdk_dir="$1" goarch="$2" toolchain_dir="$3"
|
|
local cc_name cxx_name cc_bin cxx_bin ld_bin staging_dir
|
|
|
|
staging_dir="$sdk_dir/staging_dir"
|
|
cc_name="$(goarch_to_sdk_cc_name "$goarch")" || { echo "unsupported GOARCH for musl toolchain: $goarch" | tee -a "$LOG_FILE"; exit 1; }
|
|
cxx_name="$(goarch_to_sdk_cxx_name "$goarch")" || { echo "unsupported GOARCH for musl toolchain: $goarch" | tee -a "$LOG_FILE"; exit 1; }
|
|
cc_bin="$(find_toolchain_binary "$sdk_dir" "$cc_name")"
|
|
cxx_bin="$(find_toolchain_binary "$sdk_dir" "$cxx_name")"
|
|
ld_bin="$(find_linker_binary "$sdk_dir" "$goarch")"
|
|
[ -n "$cc_bin" ] || { echo "$cc_name not found under $staging_dir" | tee -a "$LOG_FILE"; exit 1; }
|
|
[ -n "$cxx_bin" ] || { echo "$cxx_name not found under $staging_dir" | tee -a "$LOG_FILE"; exit 1; }
|
|
[ -n "$ld_bin" ] || { echo "no suitable linker found under $sdk_dir/staging_dir" | tee -a "$LOG_FILE"; exit 1; }
|
|
|
|
mkdir -p "$toolchain_dir"
|
|
cat > "$toolchain_dir/ld" <<EOF
|
|
#!/bin/sh
|
|
exec '$ld_bin' "\$@"
|
|
EOF
|
|
|
|
cat > "$toolchain_dir/cc" <<EOF
|
|
#!/bin/sh
|
|
export STAGING_DIR='$staging_dir'
|
|
exec '$cc_bin' -B'$toolchain_dir' "\$@"
|
|
EOF
|
|
cat > "$toolchain_dir/cxx" <<EOF
|
|
#!/bin/sh
|
|
export STAGING_DIR='$staging_dir'
|
|
exec '$cxx_bin' -B'$toolchain_dir' "\$@"
|
|
EOF
|
|
chmod +x "$toolchain_dir/ld" "$toolchain_dir/cc" "$toolchain_dir/cxx"
|
|
}
|
|
|
|
build_ldflags() {
|
|
local base_ldflags
|
|
base_ldflags="$(tr -d '\n' < "$PROJECT_DIR/release/LDFLAGS")"
|
|
echo "${base_ldflags} -X github.com/sagernet/sing-box/constant.Version=$(project_version) -s -w"
|
|
}
|
|
|
|
require_tools() {
|
|
[ -x "$GO_BIN" ] || { echo "go not found at $GO_BIN" | tee -a "$LOG_FILE"; exit 1; }
|
|
command -v fakeroot >/dev/null || { echo "fakeroot not found in PATH" | tee -a "$LOG_FILE"; exit 1; }
|
|
}
|
|
|
|
reset_go_cache_if_needed() {
|
|
if [ "$FORCE_REBUILD" = 1 ]; then
|
|
chmod -R u+w "$GO_CACHE_DIR" "$GO_MODCACHE_DIR" 2>/dev/null || true
|
|
rm -rf "$GO_CACHE_DIR" "$GO_MODCACHE_DIR"
|
|
fi
|
|
mkdir -p "$GO_CACHE_DIR" "$GO_MODCACHE_DIR"
|
|
}
|
|
|
|
build_binary() {
|
|
local goarch="$1" output="$2" sdk_dir="$3" toolchain_dir="$4"
|
|
local tags ldflags
|
|
local -a env_cmd
|
|
|
|
tags="$(build_tags)"
|
|
ldflags="$(build_ldflags)"
|
|
env_cmd=(
|
|
env
|
|
"PATH=$(dirname "$GO_BIN"):$PATH"
|
|
"GOTOOLCHAIN=local"
|
|
"GOCACHE=$GO_CACHE_DIR"
|
|
"GOMODCACHE=$GO_MODCACHE_DIR"
|
|
"GOOS=linux"
|
|
"GOARCH=$goarch"
|
|
)
|
|
if uses_musl_toolchain; then
|
|
prepare_musl_toolchain "$sdk_dir" "$goarch" "$toolchain_dir"
|
|
env_cmd+=(
|
|
"CGO_ENABLED=1"
|
|
"CC=$toolchain_dir/cc"
|
|
"CXX=$toolchain_dir/cxx"
|
|
)
|
|
else
|
|
env_cmd+=("CGO_ENABLED=0")
|
|
fi
|
|
if [ "$goarch" = "amd64" ]; then
|
|
env_cmd+=("GOAMD64=v1")
|
|
fi
|
|
|
|
echo "=== Building sing-box for GOARCH=$goarch ===" | tee -a "$LOG_FILE"
|
|
(
|
|
cd "$PROJECT_DIR"
|
|
run "${env_cmd[@]}" "$GO_BIN" build \
|
|
-buildvcs=false \
|
|
-trimpath \
|
|
-tags "$tags" \
|
|
-ldflags "$ldflags" \
|
|
-o "$output" \
|
|
./cmd/sing-box
|
|
)
|
|
}
|
|
|
|
stage_common_tree() {
|
|
local root_dir="$1" binary_path="$2"
|
|
install -d "$root_dir/usr/bin" \
|
|
"$root_dir/usr/share/sing-box" \
|
|
"$root_dir/etc/sing-box" \
|
|
"$root_dir/etc/config" \
|
|
"$root_dir/etc/init.d" \
|
|
"$root_dir/lib/upgrade/keep.d"
|
|
install -m755 "$binary_path" "$root_dir/usr/bin/sing-box"
|
|
install -m644 "$PROJECT_DIR/release/config/config.json" "$root_dir/etc/sing-box/config.json"
|
|
install -m644 "$PROJECT_DIR/release/config/openwrt.conf" "$root_dir/etc/config/sing-box"
|
|
install -m755 "$PROJECT_DIR/release/config/openwrt.init" "$root_dir/etc/init.d/sing-box"
|
|
install -m644 "$PROJECT_DIR/release/config/openwrt.keep" "$root_dir/lib/upgrade/keep.d/sing-box"
|
|
}
|
|
|
|
stage_ipk_metadata() {
|
|
local root_dir="$1" pkg_arch="$2"
|
|
local control_dir="$root_dir/CONTROL"
|
|
|
|
mkdir -p "$control_dir"
|
|
cat > "$control_dir/control" <<EOF
|
|
Package: $PKG_NAME
|
|
Version: $(ipk_version)
|
|
Depends: ca-bundle, kmod-inet-diag, kmod-tun
|
|
Provides: sing-box, sing-box-tiny
|
|
Conflicts: sing-box
|
|
Source: https://git.my-handbook.ru/fresa/sing-box-rr-edition
|
|
SourceName: sing-box-rr-edition
|
|
License: GPL-3.0-or-later
|
|
Section: net
|
|
Require-User: sing-box=5566:sing-box=5566
|
|
URL: https://git.my-handbook.ru/fresa/sing-box-rr-edition
|
|
Maintainer: RR Edition
|
|
Architecture: $pkg_arch
|
|
Installed-Size: 0
|
|
Description: RR Edition trimmed network-only sing-box fork for OpenWrt.
|
|
EOF
|
|
cat > "$control_dir/conffiles" <<'EOF'
|
|
/etc/config/sing-box
|
|
/etc/sing-box/config.json
|
|
EOF
|
|
install -m755 "$PROJECT_DIR/release/config/openwrt.prerm" "$control_dir/prerm"
|
|
}
|
|
|
|
package_ipk() {
|
|
local pkg_root="$1" sdk_dir="$2" out_dir="$3"
|
|
local source_epoch
|
|
|
|
source_epoch="$(project_source_date_epoch)"
|
|
mkdir -p "$out_dir"
|
|
rm -f "$out_dir"/${PKG_NAME}_*.ipk
|
|
|
|
run env "PKG_SOURCE_DATE_EPOCH=$source_epoch" fakeroot sh -c '
|
|
set -e
|
|
pkg_dir="$1"
|
|
packer="$2"
|
|
dest_dir="$3"
|
|
chown -R 0:0 "$pkg_dir"
|
|
"$packer" "$pkg_dir" "$dest_dir"
|
|
' sh "$pkg_root" "$sdk_dir/scripts/ipkg-build" "$out_dir"
|
|
}
|
|
|
|
build_target() {
|
|
local label="$1" goarch="$2" pkg_arch="$3" sdk_dir="$4" out_dir="$5"
|
|
local temp_dir binary_path pkg_root toolchain_dir
|
|
|
|
[ -x "$sdk_dir/scripts/ipkg-build" ] || {
|
|
echo "$label SDK ipkg-build not found at $sdk_dir/scripts/ipkg-build" | tee -a "$LOG_FILE"
|
|
exit 1
|
|
}
|
|
|
|
temp_dir="$(mktemp -d /tmp/sing-box-rr-ipk.XXXXXX)"
|
|
trap 'rm -rf "$temp_dir"' RETURN
|
|
binary_path="$temp_dir/sing-box"
|
|
pkg_root="$temp_dir/pkg"
|
|
toolchain_dir="$temp_dir/toolchain"
|
|
|
|
echo "=== Building $PKG_NAME $label for OpenWrt 24.10 ===" | tee -a "$LOG_FILE"
|
|
build_binary "$goarch" "$binary_path" "$sdk_dir" "$toolchain_dir"
|
|
stage_common_tree "$pkg_root" "$binary_path"
|
|
stage_ipk_metadata "$pkg_root" "$pkg_arch"
|
|
package_ipk "$pkg_root" "$sdk_dir" "$out_dir"
|
|
run ls -lh "$out_dir"/${PKG_NAME}_*.ipk
|
|
|
|
trap - RETURN
|
|
rm -rf "$temp_dir"
|
|
}
|
|
|
|
build_arm64() {
|
|
[ -d "$SDK_ARM64" ] || { echo "arm64 SDK not found at $SDK_ARM64" | tee -a "$LOG_FILE"; exit 1; }
|
|
build_target "arm64" "arm64" "aarch64_cortex-a53" "$SDK_ARM64" "$PACKAGES_DIR/arm64"
|
|
}
|
|
|
|
build_x86() {
|
|
[ -d "$SDK_X86" ] || { echo "x86_64 SDK not found at $SDK_X86" | tee -a "$LOG_FILE"; exit 1; }
|
|
build_target "x86_64" "amd64" "x86_64" "$SDK_X86" "$PACKAGES_DIR/x86_64"
|
|
}
|
|
|
|
for arg in "$@"; do
|
|
[ "$arg" = "--force" ] || [ "$arg" = "-f" ] && FORCE_REBUILD=1
|
|
done
|
|
args=()
|
|
for arg in "$@"; do
|
|
[ "$arg" != "--force" ] && [ "$arg" != "-f" ] && args+=("$arg")
|
|
done
|
|
set -- "${args[@]}"
|
|
|
|
require_tools
|
|
reset_go_cache_if_needed
|
|
|
|
case "${1:-arm64}" in
|
|
arm64)
|
|
build_arm64
|
|
;;
|
|
x86|x86_64)
|
|
build_x86
|
|
;;
|
|
all)
|
|
rm -rf "$PACKAGES_DIR/arm64" "$PACKAGES_DIR/x86_64" 2>/dev/null || true
|
|
build_arm64
|
|
build_x86
|
|
;;
|
|
*)
|
|
echo "Usage: $0 [arm64|x86|all] [--force|-f]"
|
|
echo " arm64 - build IPK package for arm64 (default)"
|
|
echo " x86 - build IPK package for x86_64"
|
|
echo " all - build IPK packages for arm64 and x86_64"
|
|
echo ""
|
|
echo " --force|-f Rebuild with a clean Go cache"
|
|
exit 1
|
|
;;
|
|
esac
|
|
|
|
echo ""
|
|
echo "=== $PKG_NAME $(ipk_version) ==="
|
|
ls -la "$PACKAGES_DIR"/arm64/*.ipk "$PACKAGES_DIR"/x86_64/*.ipk 2>/dev/null || true
|
|
|
|
echo ""
|
|
echo "=== Full log: $LOG_FILE ==="
|
|
ERRS=$(grep -E "error:|undefined reference|No such file|failed to build" "$LOG_FILE" \
|
|
| grep -v "ignored)" | head -20 || true)
|
|
if [ -n "$ERRS" ]; then
|
|
echo "=== Errors detected: ==="
|
|
echo "$ERRS"
|
|
fi
|