diff --git a/Makefile b/Makefile
index b7d952db97f94c665b503f834d5ef44ed93fa636..d09463fba75368209c0b0b72db4a89a3a7a5dc82 100644
--- a/Makefile
+++ b/Makefile
@@ -4,6 +4,8 @@ LC_ALL:=C
 LANG:=C
 export LC_ALL LANG
 
+.SHELLFLAGS = -ec
+
 # check for spaces & resolve possibly relative paths
 define mkabspath
    ifneq (1,$(words [$($(1))]))
@@ -50,14 +52,16 @@ show-release:
 
 
 update: FORCE
-	@GLUON_SITEDIR='$(GLUON_SITEDIR)' scripts/update.sh
-	@GLUON_SITEDIR='$(GLUON_SITEDIR)' scripts/patch.sh
-	@GLUON_SITEDIR='$(GLUON_SITEDIR)' scripts/feeds.sh
+	@
+	GLUON_SITEDIR='$(GLUON_SITEDIR)' scripts/update.sh
+	GLUON_SITEDIR='$(GLUON_SITEDIR)' scripts/patch.sh
+	GLUON_SITEDIR='$(GLUON_SITEDIR)' scripts/feeds.sh
 
 update-patches: FORCE
-	@GLUON_SITEDIR='$(GLUON_SITEDIR)' scripts/update.sh
-	@GLUON_SITEDIR='$(GLUON_SITEDIR)' scripts/update-patches.sh
-	@GLUON_SITEDIR='$(GLUON_SITEDIR)' scripts/patch.sh
+	@
+	GLUON_SITEDIR='$(GLUON_SITEDIR)' scripts/update.sh
+	GLUON_SITEDIR='$(GLUON_SITEDIR)' scripts/update-patches.sh
+	GLUON_SITEDIR='$(GLUON_SITEDIR)' scripts/patch.sh
 
 update-feeds: FORCE
 	@GLUON_SITEDIR='$(GLUON_SITEDIR)' scripts/feeds.sh
@@ -89,19 +93,34 @@ GLUON_CONFIG_VARS := \
 	SUBTARGET='$(SUBTARGET)'
 
 
-CheckTarget := [ '$(BOARD)' ] \
-	|| (echo 'Please set GLUON_TARGET to a valid target. Gluon supports the following targets:'; $(foreach target,$(GLUON_TARGETS),echo ' * $(target)';) false)
+define CheckTarget
+	if [ -z '$(BOARD)' ]; then
+		echo 'Please set GLUON_TARGET to a valid target. Gluon supports the following targets:'
+		for target in $(GLUON_TARGETS); do
+			echo " * $$target"
+		done
+		exit 1
+	fi
+endef
 
-CheckExternal := test -d openwrt || (echo 'You don'"'"'t seem to have obtained the external repositories needed by Gluon; please call `make update` first!'; false)
+define CheckExternal
+	if [ ! -d openwrt ]; then
+		echo "You don't seem to have obtained the external repositories needed by Gluon; please call \`make update\` first!"
+		exit 1
+	fi
+endef
 
 define CheckSite
-	@GLUON_SITEDIR='$(GLUON_SITEDIR)' GLUON_SITE_CONFIG='$(1).conf' $(LUA) -e 'assert(dofile("scripts/site_config.lua")(os.getenv("GLUON_SITE_CONFIG")))' \
-		|| (echo 'Your site configuration ($(1).conf) did not pass validation.'; false)
-
+	if ! GLUON_SITEDIR='$(GLUON_SITEDIR)' GLUON_SITE_CONFIG='$(1).conf' $(LUA) -e 'assert(dofile("scripts/site_config.lua")(os.getenv("GLUON_SITE_CONFIG")))'; then
+		echo 'Your site configuration ($(1).conf) did not pass validation
+		exit 1
+	fi
 endef
 
 list-targets: FORCE
-	@$(foreach target,$(GLUON_TARGETS),echo '$(target)';)
+	@for target in $(GLUON_TARGETS); do
+		echo "$$target"
+	done
 
 lint: lint-lua lint-sh
 
@@ -140,61 +159,71 @@ $(eval $(call merge_lists,GLUON_CLASS_PACKAGES_tiny,$(GLUON_FEATURE_PACKAGES_tin
 LUA := openwrt/staging_dir/hostpkg/bin/lua
 
 $(LUA):
-	@$(CheckExternal)
+	+@
+
+	$(CheckExternal)
 
-	+@[ -e openwrt/.config ] || $(OPENWRTMAKE) defconfig
-	+@$(OPENWRTMAKE) tools/install
-	+@$(OPENWRTMAKE) package/lua/host/compile
+	[ -e openwrt/.config ] || $(OPENWRTMAKE) defconfig
+	$(OPENWRTMAKE) tools/install
+	$(OPENWRTMAKE) package/lua/host/compile
 
 
 config: $(LUA) FORCE
-	@$(CheckExternal)
-	@$(CheckTarget)
-	$(foreach conf,site $(patsubst $(GLUON_SITEDIR)/%.conf,%,$(wildcard $(GLUON_SITEDIR)/domains/*.conf)),$(call CheckSite,$(conf)))
+	+@
+
+	$(CheckExternal)
+	$(CheckTarget)
+	$(foreach conf,site $(patsubst $(GLUON_SITEDIR)/%.conf,%,$(wildcard $(GLUON_SITEDIR)/domains/*.conf)),\
+		$(call CheckSite,$(conf)); \
+	)
 
-	@$(GLUON_CONFIG_VARS) \
+	$(GLUON_CONFIG_VARS) \
 		$(LUA) scripts/target_config.lua '$(GLUON_TARGET)' '$(GLUON_DEFAULT_PACKAGES)' '$(GLUON_CLASS_PACKAGES_standard)' '$(GLUON_CLASS_PACKAGES_tiny)' \
 		> openwrt/.config
-	+@$(OPENWRTMAKE) defconfig
+	$(OPENWRTMAKE) defconfig
 
-	@$(GLUON_CONFIG_VARS) \
+	$(GLUON_CONFIG_VARS) \
 		$(LUA) scripts/target_config_check.lua '$(GLUON_TARGET)' '$(GLUON_DEFAULT_PACKAGES)' '$(GLUON_CLASS_PACKAGES_standard)' '$(GLUON_CLASS_PACKAGES_tiny)'
 
 
 all: config
-	@$(GLUON_CONFIG_VARS) \
+	+@
+	$(GLUON_CONFIG_VARS) \
 		$(LUA) scripts/clean_output.lua
-	+@$(OPENWRTMAKE)
-	@$(GLUON_CONFIG_VARS) \
+	$(OPENWRTMAKE)
+	$(GLUON_CONFIG_VARS) \
 		$(LUA) scripts/copy_output.lua '$(GLUON_TARGET)'
 
 clean download: config
 	+@$(OPENWRTMAKE) $@
 
 dirclean: FORCE
-	+@[ -e openwrt/.config ] || $(OPENWRTMAKE) defconfig
-	+@$(OPENWRTMAKE) dirclean
-	@rm -rf $(GLUON_TMPDIR) $(GLUON_OUTPUTDIR)
+	+@
+	[ -e openwrt/.config ] || $(OPENWRTMAKE) defconfig
+	$(OPENWRTMAKE) dirclean
+	rm -rf $(GLUON_TMPDIR) $(GLUON_OUTPUTDIR)
 
 manifest: $(LUA) FORCE
-	@[ '$(GLUON_BRANCH)' ] || (echo 'Please set GLUON_BRANCH to create a manifest.'; false)
-	@echo '$(GLUON_PRIORITY)' | grep -qE '^([0-9]*\.)?[0-9]+$$' || (echo 'Please specify a numeric value for GLUON_PRIORITY to create a manifest.'; false)
-	@$(CheckExternal)
-
-	@( \
-		echo 'BRANCH=$(GLUON_BRANCH)' && \
-		echo "DATE=$$($(LUA) scripts/rfc3339date.lua)" && \
-		echo 'PRIORITY=$(GLUON_PRIORITY)' && \
-		echo && \
-		$(foreach GLUON_TARGET,$(GLUON_TARGETS), \
-			GLUON_SITEDIR='$(GLUON_SITEDIR)' $(LUA) scripts/generate_manifest.lua '$(GLUON_TARGET)' && \
-		) : \
+	@
+	[ '$(GLUON_BRANCH)' ] || (echo 'Please set GLUON_BRANCH to create a manifest.'; false)
+	echo '$(GLUON_PRIORITY)' | grep -qE '^([0-9]*\.)?[0-9]+$$' || (echo 'Please specify a numeric value for GLUON_PRIORITY to create a manifest.'; false)
+	$(CheckExternal)
+
+	(
+		echo 'BRANCH=$(GLUON_BRANCH)'
+		echo "DATE=$$($(LUA) scripts/rfc3339date.lua)"
+		echo 'PRIORITY=$(GLUON_PRIORITY)'
+		echo
+		for target in $(GLUON_TARGETS); do
+			GLUON_SITEDIR='$(GLUON_SITEDIR)' $(LUA) scripts/generate_manifest.lua "$$target"
+		done
 	) > 'tmp/$(GLUON_BRANCH).manifest.tmp'
 
-	@mkdir -p '$(GLUON_IMAGEDIR)/sysupgrade'
-	@mv 'tmp/$(GLUON_BRANCH).manifest.tmp' '$(GLUON_IMAGEDIR)/sysupgrade/$(GLUON_BRANCH).manifest'
+	mkdir -p '$(GLUON_IMAGEDIR)/sysupgrade'
+	mv 'tmp/$(GLUON_BRANCH).manifest.tmp' '$(GLUON_IMAGEDIR)/sysupgrade/$(GLUON_BRANCH).manifest'
 
 FORCE: ;
 
 .PHONY: FORCE
 .NOTPARALLEL:
+.ONESHELL: