| b.liu | e958203 | 2025-04-17 19:18:16 +0800 | [diff] [blame] | 1 | #!/bin/sh | 
 | 2 |  | 
 | 3 | # ipkg-build -- construct a .ipk from a directory | 
 | 4 | # Carl Worth <cworth@east.isi.edu> | 
 | 5 | # based on a script by Steve Redler IV, steve@sr-tech.com 5-21-2001 | 
 | 6 | # 2003-04-25 rea@sr.unh.edu | 
 | 7 | #   Updated to work on Familiar Pre0.7rc1, with busybox tar. | 
 | 8 | #   Note it Requires: binutils-ar (since the busybox ar can't create) | 
 | 9 | #   For UID debugging it needs a better "find". | 
 | 10 | set -e | 
 | 11 |  | 
 | 12 | version=1.0 | 
 | 13 | FIND="$(command -v find)" | 
 | 14 | FIND="${FIND:-$(command -v gfind)}" | 
 | 15 | TAR="${TAR:-$(command -v tar)}" | 
 | 16 |  | 
 | 17 | # try to use fixed source epoch | 
 | 18 | if [ -n "$PKG_SOURCE_DATE_EPOCH" ]; then | 
 | 19 | 	TIMESTAMP=$(date --date="@$PKG_SOURCE_DATE_EPOCH") | 
 | 20 | elif [ -n "$SOURCE_DATE_EPOCH" ]; then | 
 | 21 | 	TIMESTAMP=$(date --date="@$SOURCE_DATE_EPOCH") | 
 | 22 | else | 
 | 23 | 	TIMESTAMP=$(date) | 
 | 24 | fi | 
 | 25 |  | 
 | 26 | ipkg_extract_value() { | 
 | 27 | 	sed -e "s/^[^:]*:[[:space:]]*//" | 
 | 28 | } | 
 | 29 |  | 
 | 30 | required_field() { | 
 | 31 | 	field=$1 | 
 | 32 |  | 
 | 33 | 	grep "^$field:" < "$CONTROL/control" | ipkg_extract_value | 
 | 34 | } | 
 | 35 |  | 
 | 36 | pkg_appears_sane() { | 
 | 37 | 	local pkg_dir="$1" | 
 | 38 |  | 
 | 39 | 	local owd="$PWD" | 
 | 40 | 	cd "$pkg_dir" | 
 | 41 |  | 
 | 42 | 	PKG_ERROR=0 | 
 | 43 | 	pkg="$(required_field Package)" | 
 | 44 | 	version="$(required_field Version | sed 's/Version://; s/^.://g;')" | 
 | 45 | 	arch="$(required_field Architecture)" | 
 | 46 |  | 
 | 47 | 	if echo "$pkg" | grep '[^a-zA-Z0-9_.+-]'; then | 
 | 48 | 		echo "*** Error: Package name $name contains illegal characters, (other than [a-z0-9.+-])" >&2 | 
 | 49 | 		PKG_ERROR=1; | 
 | 50 | 	fi | 
 | 51 |  | 
 | 52 | 	if [ -f "$CONTROL/conffiles" ]; then | 
 | 53 | 		rm -f "$CONTROL/conffiles.resolved" | 
 | 54 |  | 
 | 55 | 		for cf in $($FIND $(sed -e "s!^/!$pkg_dir/!" "$CONTROL/conffiles") -type f); do | 
 | 56 | 			echo "${cf#$pkg_dir}" >> "$CONTROL/conffiles.resolved" | 
 | 57 | 		done | 
 | 58 |  | 
 | 59 | 		rm "$CONTROL"/conffiles | 
 | 60 | 		if [ -f "$CONTROL"/conffiles.resolved ]; then | 
 | 61 | 			LC_ALL=C sort -o "$CONTROL"/conffiles "$CONTROL"/conffiles.resolved | 
 | 62 | 			rm "$CONTROL"/conffiles.resolved | 
 | 63 | 			chmod 0644 "$CONTROL"/conffiles | 
 | 64 | 		fi | 
 | 65 | 	fi | 
 | 66 |  | 
 | 67 | 	cd "$owd" | 
 | 68 | 	return $PKG_ERROR | 
 | 69 | } | 
 | 70 |  | 
 | 71 | resolve_file_mode_id() { | 
 | 72 | 	local var=$1 type=$2 name=$3 id | 
 | 73 |  | 
 | 74 | 	case "$name" in | 
 | 75 | 		root) | 
 | 76 | 			id=0 | 
 | 77 | 		;; | 
 | 78 | 		*[!0-9]*) | 
 | 79 | 			id=$(sed -ne "s#^$type $name \\([0-9]\\+\\)\\b.*\$#\\1#p" "$TOPDIR/tmp/.packageusergroup" 2>/dev/null) | 
 | 80 | 		;; | 
 | 81 | 		*) | 
 | 82 | 			id=$name | 
 | 83 | 		;; | 
 | 84 | 	esac | 
 | 85 |  | 
 | 86 | 	export "$var=$id" | 
 | 87 |  | 
 | 88 | 	[ -n "$id" ] | 
 | 89 | } | 
 | 90 |  | 
 | 91 | ### | 
 | 92 | # ipkg-build "main" | 
 | 93 | ### | 
 | 94 | file_modes="" | 
 | 95 | usage="Usage: $0 [-v] [-h] [-m] <pkg_directory> [<destination_directory>]" | 
 | 96 | while getopts "hvm:" opt; do | 
 | 97 |     case $opt in | 
 | 98 | 	v ) echo "$version" | 
 | 99 | 	    exit 0 | 
 | 100 | 	    ;; | 
 | 101 | 	h ) 	echo "$usage"  >&2 ;; | 
 | 102 | 	m )	file_modes=$OPTARG ;; | 
 | 103 | 	\? ) 	echo "$usage"  >&2 | 
 | 104 | 	esac | 
 | 105 | done | 
 | 106 |  | 
 | 107 |  | 
 | 108 | shift $((OPTIND - 1)) | 
 | 109 |  | 
 | 110 | # continue on to process additional arguments | 
 | 111 |  | 
 | 112 | case $# in | 
 | 113 | 1) | 
 | 114 | 	dest_dir=$PWD | 
 | 115 | 	;; | 
 | 116 | 2) | 
 | 117 | 	dest_dir=$2 | 
 | 118 | 	if [ "$dest_dir" = "." ] || [ "$dest_dir" = "./" ] ; then | 
 | 119 | 	    dest_dir=$PWD | 
 | 120 | 	fi | 
 | 121 | 	;; | 
 | 122 | *) | 
 | 123 | 	echo "$usage" >&2 | 
 | 124 | 	exit 1 | 
 | 125 | 	;; | 
 | 126 | esac | 
 | 127 |  | 
 | 128 | pkg_dir="$(realpath "$1")" | 
 | 129 |  | 
 | 130 | if [ ! -d "$pkg_dir" ]; then | 
 | 131 | 	echo "*** Error: Directory $pkg_dir does not exist" >&2 | 
 | 132 | 	exit 1 | 
 | 133 | fi | 
 | 134 |  | 
 | 135 | # CONTROL is second so that it takes precedence | 
 | 136 | CONTROL= | 
 | 137 | [ -d "$pkg_dir"/CONTROL ] && CONTROL=CONTROL | 
 | 138 | if [ -z "$CONTROL" ]; then | 
 | 139 | 	echo "*** Error: Directory $pkg_dir has no CONTROL subdirectory." >&2 | 
 | 140 | 	exit 1 | 
 | 141 | fi | 
 | 142 |  | 
 | 143 | if ! pkg_appears_sane "$pkg_dir"; then | 
 | 144 | 	echo >&2 | 
 | 145 | 	echo "ipkg-build: Please fix the above errors and try again." >&2 | 
 | 146 | 	exit 1 | 
 | 147 | fi | 
 | 148 |  | 
 | 149 | tmp_dir=$dest_dir/IPKG_BUILD.$$ | 
 | 150 | mkdir "$tmp_dir" | 
 | 151 |  | 
 | 152 | echo $CONTROL > "$tmp_dir"/tarX | 
 | 153 | cd "$pkg_dir" | 
 | 154 | for file_mode in $file_modes; do | 
 | 155 | 	case $file_mode in | 
 | 156 | 	/*:*:*:*) | 
 | 157 | 	    ;; | 
 | 158 | 	*) | 
 | 159 | 	    echo "ERROR: file modes must use absolute path and contain user:group:mode" | 
 | 160 | 	    echo "$file_mode" | 
 | 161 | 	    exit 1 | 
 | 162 | 	    ;; | 
 | 163 | 	esac | 
 | 164 |  | 
 | 165 | 	mode=${file_mode##*:}; path=${file_mode%:*} | 
 | 166 | 	group=${path##*:};     path=${path%:*} | 
 | 167 | 	user=${path##*:};      path=${path%:*} | 
 | 168 |  | 
 | 169 | 	if ! resolve_file_mode_id uid user "$user"; then | 
 | 170 | 		echo "ERROR: unable to resolve uid of $user" >&2 | 
 | 171 | 		exit 1 | 
 | 172 | 	fi | 
 | 173 |  | 
 | 174 | 	if ! resolve_file_mode_id gid group "$group"; then | 
 | 175 | 		echo "ERROR: unable to resolve gid of $group" >&2 | 
 | 176 | 		exit 1 | 
 | 177 | 	fi | 
 | 178 |  | 
 | 179 | 	chown "$uid:$gid" "$pkg_dir/$path" | 
 | 180 | 	chmod  "$mode" "$pkg_dir/$path" | 
 | 181 | done | 
 | 182 | $TAR -X "$tmp_dir"/tarX --format=gnu --numeric-owner --sort=name -cpf - --mtime="$TIMESTAMP" . | gzip -n - > "$tmp_dir"/data.tar.gz | 
 | 183 |  | 
 | 184 | installed_size=$(zcat < "$tmp_dir"/data.tar.gz | wc -c) | 
 | 185 | sed -i -e "s/^Installed-Size: .*/Installed-Size: $installed_size/" \ | 
 | 186 | 	"$pkg_dir"/$CONTROL/control | 
 | 187 |  | 
 | 188 | ( cd "$pkg_dir"/$CONTROL && $TAR --format=gnu --numeric-owner --sort=name -cf -  --mtime="$TIMESTAMP" . | gzip -n - > "$tmp_dir"/control.tar.gz ) | 
 | 189 | rm "$tmp_dir"/tarX | 
 | 190 |  | 
 | 191 | echo "2.0" > "$tmp_dir"/debian-binary | 
 | 192 |  | 
 | 193 | pkg_file=$dest_dir/${pkg}_${version}_${arch}.ipk | 
 | 194 | rm -f "$pkg_file" | 
 | 195 | ( cd "$tmp_dir" && $TAR --format=gnu --numeric-owner --sort=name -cf -  --mtime="$TIMESTAMP" ./debian-binary ./data.tar.gz ./control.tar.gz | gzip -n - > "$pkg_file" ) | 
 | 196 |  | 
 | 197 | rm "$tmp_dir"/debian-binary "$tmp_dir"/data.tar.gz "$tmp_dir"/control.tar.gz | 
 | 198 | rmdir "$tmp_dir" | 
 | 199 |  | 
 | 200 | echo "Packaged contents of $pkg_dir into $pkg_file" |