add script for managing 'build environments' (.config+files/), including documentation
authorFelix Fietkau <nbd@openwrt.org>
Wed, 6 Aug 2008 14:30:46 +0000 (14:30 +0000)
committerFelix Fietkau <nbd@openwrt.org>
Wed, 6 Aug 2008 14:30:46 +0000 (14:30 +0000)
SVN-Revision: 12212

docs/build.tex
scripts/env [new file with mode: 0755]

index 54d31c582b5c1b3e01aa23c206f8404eb566b529..cc7f5fd2c89b37110efa872816f515f8a5cb6551 100644 (file)
@@ -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 (executable)
index 0000000..d783e20
--- /dev/null
@@ -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