diff options
-rw-r--r-- | docs/build.tex | 57 | ||||
-rwxr-xr-x | scripts/env | 219 |
2 files changed, 276 insertions, 0 deletions
diff --git a/docs/build.tex b/docs/build.tex index 54d31c582..cc7f5fd2c 100644 --- a/docs/build.tex +++ b/docs/build.tex @@ -483,3 +483,60 @@ Other useful targets include: \item \texttt{make package/\textit{<name>}/configure V=99} \end{itemize} + +\subsection{Using build environments} +OpenWrt provides a means of building images for multiple configurations +which can use multiple targets in one single checkout. These \emph{environments} +store a copy of the .config file generated by \texttt{make menuconfig} and the contents +of the \texttt{./files} folder. +The script \texttt{./scripts/env} is used to manage these environments, it uses +\texttt{git} (which needs to be installed on your system) as backend for version control. + +The command +\begin{Verbatim} + \texttt{./scripts/env help} +\end{Verbatim} +produces a short help text with a list of commands. + +To create a new environment named \texttt{current}, run the following command +\begin{Verbatim} + ./scripts/env new current +\end{Verbatim} +This will move your \texttt{.config} file and \texttt{./files} (if it exists) to +the \texttt{env/} subdirectory and create symlinks in the base folder. + +After running make menuconfig or changing things in files/, your current state will +differ from what has been saved before. To show these changes, use: +\begin{Verbatim} + ./scripts/env diff +\end{Verbatim} + +If you want to save these changes, run: +\begin{Verbatim} + ./scripts/env save +\end{Verbatim} +If you want to revert your changes to the previously saved copy, run: +\begin{Verbatim} + ./scripts/env revert +\end{Verbatim} + +If you want, you can now create a second environment using the \texttt{new} command. +It will ask you whether you want to make it a clone of the current environment (e.g. +for minor changes) or if you want to start with a clean version (e.g. for selecting +a new target). + +To switch to a different environment (e.g. \texttt{test1}), use: +\begin{Verbatim} + ./scripts/env switch test1 +\end{Verbatim} + +To rename the current branch to a new name (e.g. \texttt{test2}), use: +\begin{Verbatim} + ./scripts/env rename test2 +\end{Verbatim} + +If you want to get rid of environment switching and keep everything in the base directory +again, use: +\begin{Verbatim} + ./scripts/env clear +\end{Verbatim} diff --git a/scripts/env b/scripts/env new file mode 100755 index 000000000..d783e20ef --- /dev/null +++ b/scripts/env @@ -0,0 +1,219 @@ +#!/usr/bin/env bash +BASEDIR="$PWD" +ENVDIR="$PWD/env" + +usage() { + cat <<EOF +Usage: $0 [options] <command> [arguments] +Commands: + help This help text + list List environments + clear Delete all environment and revert to flat config/files + new <name> Create a new environment + switch <name> Switch to a different environment + delete <name> Delete an environment + rename <newname> Rename the current environment + diff Show differences between current state and environment + save Save your changes to the environment + revert Revert your changes since last save + +Options: + +EOF + exit ${1:-1} +} + +error() { + echo "$0: $*" + exit 1 +} + +ask_bool() { + local DEFAULT="$1"; shift + local def defstr val + case "$DEFAULT" in + 1) def=0; defstr="Y/n";; + 0) def=1; defstr="y/N";; + *) def=; defstr="y/n";; + esac + while [ -z "$val" ]; do + local VAL + + echo -n "$* ($defstr): " + read VAL + case "$VAL" in + y*|Y*) val=0;; + n*|N*) val=1;; + *) val="$def";; + esac + done + return "$val" +} + +env_init() { + local CREATE="$1" + if [ -z "$CREATE" ]; then + [ -d "$ENVDIR" ] || exit 0 + fi + [ -x "$(which git 2>/dev/null)" ] || error "Git is not installed" + mkdir -p "$ENVDIR" || error "Failed to create the environment directory" + cd "$ENVDIR" || error "Failed to switch to the environment directory" + [ -d .git ] || { + git init && + touch .config && + mkdir files && + git-add . && + git-commit -q -m "Initial import" + } || { + rm -rf .git + error "Failed to initialize the environment directory" + } +} + +env_sync_data() { + [ \! -L "$BASEDIR/.config" -a -f "$BASEDIR/.config" ] && mv "$BASEDIR/.config" "$ENVDIR" + git-add . + git-add -u +} + +env_sync() { + local STR="$1" + env_sync_data + git-commit -m "${STR:-Update} at $(date)" +} + +env_link_config() { + rm -f "$BASEDIR/.config" + ln -s env/.config "$BASEDIR/.config" + mkdir -p "$ENVDIR/files" + [ -L "$BASEDIR/files" ] || ln -s env/files "$BASEDIR/files" +} + +env_do_reset() { + git-reset --hard HEAD + git-clean -d -f +} + +env_list() { + env_init + git-branch | grep -vE '^. master$' +} + +env_diff() { + env_init + env_sync_data + git-diff --cached +} + +env_save() { + env_init + env_sync + env_link_config +} + +env_revert() { + env_init + env_do_reset + env_link_config +} + +env_ask_sync() { + LINES="$(env_diff | wc -l)" # implies env_init + [ "$LINES" -gt 0 ] && { + if ask_bool 1 "Do you want to save your changes"; then + env_sync + else + env_sync_data + env_do_reset + fi + } +} + +env_clear() { + env_init + [ -L "$BASEDIR/.config" ] && rm -f "$BASEDIR/.config" + [ -L "$BASEDIR/files" ] && rm -f "$BASEDIR/files" + [ -f "$ENVDIR/.config" ] || ( cd "$ENVDIR/files" && find | grep -vE '^\.$' > /dev/null ) + env_sync_data + if ask_bool 1 "Do you want to keep your current config and files"; then + mkdir -p "$BASEDIR/files" + cp -a "$ENVDIR/files/*" "$BASEDIR/files" 2>/dev/null >/dev/null + cp "$ENVDIR/.config" "$BASEDIR/" + else + rm -rf "$BASEDIR/files" "$BASEDIR/.config" + fi + cd "$BASEDIR" + rm -rf "$ENVDIR" +} + +env_delete() { + local name="${1##*/}" + [ -z "$name" ] && usage + [ -f "$envdir/.git/refs/heads/$name" ] || error "environment '$name' not found" + branch="$(git-branch | grep '^\* ' | awk '{print $2}')" + [ "$name" = "branch" ] && error "cannot delete the currently selected environment" + git-branch -D "$name" +} + +env_switch() { + local name="${1##*/}" + [ -z "$name" ] && usage + [ -f "$envdir/.git/refs/heads/$name" ] || error "environment '$name' not found" + + env_init + env_ask_sync + git-checkout "$NAME" + env_link_config +} + +env_rename() { + local NAME="${1##*/}" + env_init + git-branch -m "$NAME" +} + +env_new() { + local NAME="$1" + local branch + local from="master" + + [ -z "$NAME" ] && usage + env_init 1 + + branch="$(git-branch | grep '^\* ' | awk '{print $2}')" + if [ -n "$branch" -a "$branch" != "master" ]; then + env_ask_sync + if ask_bool 0 "Do you want to clone the current environment?"; then + from="$branch" + fi + rm -f "$BASEDIR/.config" "$BASEDIR/files" + fi + git-checkout -b "$1" "$from" + if [ -f "$BASEDIR/.config" -o -d "$BASEDIR/files" ]; then + if ask_bool 1 "Do you want to keep your current config and files?"; then + [ -d "$BASEDIR/files" -a \! -L "$BASEDIR/files" ] && { + mv "$BASEDIR/files/"* "$ENVDIR/" 2>/dev/null + rmdir "$BASEDIR/files" + } + env_sync + else + rm -rf "$BASEDIR/.config" "$BASEDIR/files" + fi + fi + env_link_config +} + +COMMAND="$1"; shift +case "$COMMAND" in + help) usage 0;; + new) env_new "$@";; + list) env_list "$@";; + clear) env_clear "$@";; + switch) env_switch "$@";; + delete) env_delete "$@";; + rename) env_rename "$@";; + diff) env_diff "$@";; + save) env_save "$@";; + revert) env_revert "$@";; + *) usage;; +esac |