123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341 |
- #!/bin/sh
- # Copyright (C) 2018 Alister Sanders
- # This program is free software: you can redistribute it and/or modify
- # it under the terms of the GNU General Public License as published by
- # the Free Software Foundation, either version 3 of the License, or
- # (at your option) any later version.
- # This program is distributed in the hope that it will be useful,
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- # GNU General Public License for more details.
- # You should have received a copy of the GNU General Public License
- # along with this program. If not, see <http://www.gnu.org/licenses/>.
- SP_MACRO_PATH="$SP_MACRO_PATH:/usr/share/spotfile"
- SP_PKG_PATH="$SP_PKG_PATH:$PWD"
- RED="\\x1b[38;5;88m"
- BLUE="\\x1b[38;5;69m"
- GREEN="\\x1b[38;5;107m"
- RESET="\\x1b[0m"
- # Utility Functions
- # ------------------------------------------------------------------------------
- highlight() {
- echo -e $(echo "$@" |
- sed -e "s,//\([^\(//\)]*\)//,$BLUE\1$RESET,g" \
- -e "s,##\([^\(##\)]*\)##,$RED\1$RESET,g")
- }
- log() {
- case "$1" in
- error) shift; highlight " $RED!$RESET $@" >&2 ;;
- info) shift; highlight " $GREEN*$RESET $@" ;;
- verb) shift; [ "$verbose" = 1 ] && highlight "$@" || : ;;
- *) ;;
- esac
- }
- die() {
- log error "$@"
- exit 1
- }
- find_macro_file() {
- fullpath=""
- # Check each directory specified in SP_MACRO_PATH for the file
- for path in `echo "$SP_MACRO_PATH" | sed -e 's/:/\n/g'`; do
- if [ ! -z "$path" -a -f "$path/$1" ]; then
- fullpath="$path/$1"
- break
- fi
- done
- if [ ! -z "$fullpath" ]; then
- echo "$fullpath"
- return 0
- else
- return 1
- fi
- }
- require() {
- which "$1" &>/dev/null ||
- die "Could not find //$1// in //\$PATH// (is it installed?)"
- }
- strip_json_str() {
- # Remove leading/trailing quotation marks
- echo -n "$1" | sed -e 's/^"\(.*\)"$/\1/'
- }
- run_m4() {
- working_dir="$1"
- target_host="$2"
-
- # TODO: Include spotfile version info
- (cd "$working_dir"; while read -r line; do
- echo "$line"
- done | M4PATH="$SP_MACRO_PATH" m4 -P \
- -D SP_BUILDHOST="$(hostname)" \
- -D SP_TARGETHOST="$target_host")
- }
- help() {
- echo "$(basename $0) [ARGS] PACKAGE1 PACKAGE2 ..."
- echo " -t, --target HOST Add a target host"
- echo " -i, --install Install the specified packages"
- echo " -S, --no-symlink Don't create symlinks during package"
- echo " installation; copy files directly"
- echo " -r, --relative Use relative symlinks for installation"
- echo " -V, --verbose Print verbose messages"
- echo " -h, --help Print help message"
- }
- # Package operations
- # ------------------------------------------------------------------------------
- find_package() {
- for path in `echo "$SP_PKG_PATH" | sed -e 's/:/\n/g'`; do
- if [ ! -z "$path" -a -d "$path/$1" ]; then
- fullpath="$path/$1"
- break
- fi
- done
- if [ ! -z "$fullpath" ]; then
- echo "$fullpath"
- return 0
- else
- return 1
- fi
- }
- build_package() {
- pkg="$1"
- target_host="$2"
- sp_macros="$3"
-
- log verb "Check for existence of //$pkg/// in //\$SP_PKG_PATH//"
- pkg_path=$(find_package "$pkg")
- [ $? -ne 0 ] &&
- die "Could not find //$pkg// in //\$SP_PKG_PATH//" ||
- log verb "Found //$pkg//: $pkg_path"
- manifest_file="$pkg_path/pkg.json"
- [ ! -f "$manifest_file" ] &&
- die "//$pkg// doesn't have a package manifest ($manifest_file)!" ||
- log verb "Found manifest for //$pkg//: $manifest_file"
- manifest=$(cat "$manifest_file" | jq -c)
- build_targets=$(echo "$manifest" | jq ".targets")
- n_targets=$(echo "$build_targets" | jq length)
- log verb "$n_targets target(s) total to build"
- if [ "$n_targets" = 0 ]; then
- log verb "//$pkg// has no targets --- nothing to do"
- return 0
- fi
- for i in `seq 0 $((n_targets - 1))`; do
- # Determine the input and output file for this target
- in_file=$(strip_json_str "$(echo "$build_targets" | jq ".[$i].in")")
- out_file=$(strip_json_str "$(echo "$build_targets" | jq ".[$i].out")")
- log verb "Target wants $in_file -> $out_file"
- input_file_path="$pkg_path/$in_file"
- output_file_path="$pkg_path/$target_host/$out_file"
- output_dir=$(dirname "$output_file_path")
- log verb "Check whether $output_dir exists..."
- if [ ! -d "$output_dir" ]; then
- mkdir -p "$output_dir" &&
- log verb "Created directory $output_dir" ||
- die "Failed to create $output_dir for //$pkg//"
- fi
- # Finally, build the target
- cat "$sp_macros" "$input_file_path" |
- run_m4 "$pkg_path" "$target_host" > "$output_file_path"
- done
- }
- install_package() {
- pkg="$1"
- target_host="$2"
- no_symlink="$3"
- relative_symlink="$4"
-
- log verb "Check for existence of //$pkg/// in //\$SP_PKG_PATH//"
- pkg_path=$(find_package "$pkg")
- [ $? -ne 0 ] &&
- die "Could not find //$pkg// in //\$SP_PKG_PATH//" ||
- log verb "Found //$pkg//: $pkg_path"
- manifest_file="$pkg_path/pkg.json"
- [ ! -f "$manifest_file" ] &&
- die "//$pkg// doesn't have a package manifest ($manifest_file)!" ||
- log verb "Found manifest for //$pkg//: $manifest_file"
- manifest=$(cat "$manifest_file" | jq -c)
- install_targets=$(echo "$manifest" | jq ".install")
- n_targets=$(echo "$install_targets" | jq length)
- if [ "$n_targets" = 0 ]; then
- log info "//$pkg// has no installation targets... eh?"
- return 0
- fi
- log verb "Check for host directory in $pkg_path"
- [ ! -d "$pkg_path/$target_host" ] && die "//$pkg// does not include a configuration for host //$target_host//"
-
- for i in `seq 0 $((n_targets - 1))`; do
- source_file=$(strip_json_str $(echo "$install_targets" | jq ".[$i].source"))
- dest_file=$(strip_json_str $(echo "$install_targets" | jq ".[$i].destination") | sed -e 's,/$,,')
- source_file_path="$pkg_path/$target_host/$source_file"
- dest_file_path="${dest_file/#\~/$HOME}"
- dest_parent="$(dirname $dest_file_path)"
- # Make sure the parent of the destination path actually exists
- if [ ! -d "$dest_parent" ]; then
- mkdir -p "$dest_parent" &&
- log verb "Created $dest_parent" ||
- die "Could not create $dest_parent"
- fi
- log verb "Installation target wants $source_file_path -> $dest_file_path"
- # Any trailing slashes were removed from the path, so it's
- # safe to run rm -rf without fear of deleting contents of a
- # symlinked directory
- if [ -e "$dest_file_path" -o -h "$dest_file_path" ]; then
- log info "Removing old $dest_file_path"
- rm -rf "$dest_file_path"
- fi
-
- if [ "$no_symlink" = 1 ]; then
- cp -r "$source_file_path" "$dest_file_path" &&
- log info "Copied $source_file_path -> $dest_file_path" ||
- die "Failed to copy $source_file_path -> $dest_file_path"
- elif [ "$relative_symlink" = 1 ]; then
- ln -rs "$source_file_path" "$dest_file_path" &&
- log info "Linked $dest_file_path -> $source_file_path" ||
- die "Failed to link $dest_file_path -> $source_file_path"
- else
- ln -s "$source_file_path" "$dest_file_path" &&
- log info "Linked $dest_file_path -> $source_file_path" ||
- die "Failed to link $dest_file_path -> $source_file_path"
-
- fi
- done
- }
- # Main
- # -------------------------------------------------------------------------------
- require getopt
- require m4
- require jq
- # Make sure getopt long arguments are supported
- getopt -T; [ $? = 4 ] || die "Long getopt arguments are not supported"
- # Process command line options
- GETOPT=`getopt -o "t:iSrVh" --long "target:,install,no-symlink,relative,verbose,help" -- $*`
- eval set -- "$GETOPT"
- while :; do
- case "$1" in
- -t|--target)
- # Append this host to the list of target hosts
- [ -z "$target_hosts" ] \
- && target_hosts="$2" \
- || target_hosts="$target_hosts $2"
- shift 2
- ;;
- -i|--install)
- do_install=1
- shift
- ;;
- -S|--no-symlink)
- no_symlink=1
- shift
- ;;
-
- -r|--relative)
- relative_symlink=1
- shift
- ;;
- -V|--verbose)
- verbose=1
- shift
- ;;
- -h|--help)
- help
- exit 0
- ;;
-
- --)
- shift
- break
- ;;
-
- *) ;;
- esac
- done
- # Find the location of the default macro set
- spotfile_macros=$(find_macro_file "spotfile.m4")
- [ $? -ne 0 ] && die "Could not find //spotfile.m4// in //\$SP_MACRO_PATH//"
- [ -z "$do_install" -a "$no_symlink" = 1 ] &&
- log error "Warning: //--no-symlink// specified without //--install// (nonsensical)"
- [ "$relative_symlink" = 1 -a "$no_symlink" = 1 ] &&
- log error "Warning: //--relative// and //--no-symlink// specified (nonsensical)"
- if [ -z "$target_hosts" ]; then
- log verb "No hosts specified, using local hostname"
- target_hosts="$(hostname)"
- fi
- log verb "Using build host(s) //$target_hosts//"
- if [ -z "$do_install" ]; then # Build
- # The remaining unshifted arguments are package names
- for host in `echo $target_hosts | sed -e 's/:/\n/g'`; do
- for pkg in "$@"; do
- log info "Working on package //$pkg// for //$host//"
-
- build_package "$pkg" "$host" "$spotfile_macros"
- done
- done
- else # Install configurations
- if [ "$(echo $target_hosts | awk '{print $1}')" != "$(echo $target_hosts)" ]; then
- die "More than one target host was specified\nOnly a single host should be specified, or none at all"
- fi
- # Strip whitespace
- host="$(echo $target_hosts)"
- log info "Using host //$host//"
-
- for pkg in "$@"; do
- log info "Installing package //$pkg//"
- install_package "$pkg" "$host" "$no_symlink" "$relative_symlink"
- done
- fi
|