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  | 
