Browse Source

compile apps in the right order by looking at their LOCAL_DEPS

we also use LOCAL_DEPS at the top-level, if it exists, to determine
which local apps to compile, in the interest of saving compile time
for projects that have many apps and multiple release configurations
Jeffrey Griffin 8 years ago
parent
commit
750bdd359b
3 changed files with 170 additions and 8 deletions
  1. 7 5
      core/deps.mk
  2. 14 3
      doc/src/guide/deps.asciidoc
  3. 149 0
      test/core_deps.mk

+ 7 - 5
core/deps.mk

@@ -26,6 +26,7 @@ dep_repo = $(patsubst git://github.com/%,https://github.com/%, \
 	$(if $(dep_$(1)),$(word 2,$(dep_$(1))),$(pkg_$(1)_repo)))
 dep_commit = $(if $(dep_$(1)_commit),$(dep_$(1)_commit),$(if $(dep_$(1)),$(word 3,$(dep_$(1))),$(pkg_$(1)_commit)))
 
+LOCAL_DEPS_DIRS = $(foreach a,$(LOCAL_DEPS),$(if $(wildcard $(APPS_DIR)/$(a)),$(APPS_DIR)/$(a)))
 ALL_APPS_DIRS = $(if $(wildcard $(APPS_DIR)/),$(filter-out $(APPS_DIR),$(shell find $(APPS_DIR) -maxdepth 1 -type d)))
 ALL_DEPS_DIRS = $(addprefix $(DEPS_DIR)/,$(foreach dep,$(filter-out $(IGNORE_DEPS),$(BUILD_DEPS) $(DEPS)),$(call dep_name,$(dep))))
 
@@ -48,9 +49,6 @@ dep_verbose = $(dep_verbose_$(V))
 
 # Core targets.
 
-ifdef IS_APP
-apps::
-else
 apps:: $(ALL_APPS_DIRS) clean-tmp-deps.log
 ifeq ($(IS_APP)$(IS_DEP),)
 	$(verbose) rm -f $(ERLANG_MK_TMP)/apps.log
@@ -59,10 +57,15 @@ endif
 # Create ebin directory for all apps to make sure Erlang recognizes them
 # as proper OTP applications when using -include_lib. This is a temporary
 # fix, a proper fix would be to compile apps/* in the right order.
+ifndef IS_APP
 	$(verbose) for dep in $(ALL_APPS_DIRS) ; do \
 		mkdir -p $$dep/ebin || exit $$?; \
 	done
-	$(verbose) for dep in $(ALL_APPS_DIRS) ; do \
+endif
+# at the toplevel: if LOCAL_DEPS is defined with at least one local app, only
+# compile that list of apps. otherwise, compile everything.
+# within an app: compile all LOCAL_DEPS that are (uncompiled) local apps
+	$(verbose) for dep in $(if $(LOCAL_DEPS_DIRS)$(IS_APP),$(LOCAL_DEPS_DIRS),$(ALL_APPS_DIRS)) ; do \
 		if grep -qs ^$$dep$$ $(ERLANG_MK_TMP)/apps.log; then \
 			:; \
 		else \
@@ -70,7 +73,6 @@ endif
 			$(MAKE) -C $$dep IS_APP=1 || exit $$?; \
 		fi \
 	done
-endif
 
 clean-tmp-deps.log:
 ifeq ($(IS_APP)$(IS_DEP),)

+ 14 - 3
doc/src/guide/deps.asciidoc

@@ -397,9 +397,20 @@ They work exactly the same as remote dependencies, except:
 * They are not deleted on `make distclean`
 * They are not automatically added to the application resource file
 
-To properly fill the application resource file, you will
-need to define the `LOCAL_DEPS` variable for each relevant
-application, the same as for OTP applications.
+To properly fill the application resource file and compile apps in
+the right order, you will need to define the `LOCAL_DEPS` variable
+for each relevant application, the same as for OTP applications. Apps
+can depend on each other in this way, and their compilation order
+will follow the same rules as regular dependencies in `DEPS`.
+
+The top-level `LOCAL_DEPS` variable, if defined, will determine which
+apps (along with their dependencies) to build, and also which apps
+should be added to the top-level application resource file, if there
+is one. This may be useful, for example, for specifying a different
+set of apps to build for different releases. If `LOCAL_DEPS` is not
+defined, then all apps in the '$(APPS_DIR)' will be built, but none
+will be automatically added to the top-level application resource
+file.
 
 If there is a conflict between a local dependency and a
 remote dependency, then the local dependency always wins;

+ 149 - 0
test/core_deps.mk

@@ -479,6 +479,155 @@ core-deps-apps-only: build clean
 	$i "Check that all relevant files were removed"
 	$t test ! -e $(APP)/deps
 
+core-deps-apps-local-deps: build clean
+
+	$i "Bootstrap a new OTP library named $(APP)"
+	$t mkdir $(APP)/
+	$t cp ../erlang.mk $(APP)/
+	$t $(MAKE) -C $(APP) -f erlang.mk bootstrap-lib $v
+
+	$i "Create a new application my_app_1"
+	$t $(MAKE) -C $(APP) new-app in=my_app_1 $v
+
+	$i "Add Lager to the list of dependencies of my_app_1, and add the lager parse_transform"
+	$t perl -ni.bak -e 'print;if ($$.==1) {print "DEPS = lager\nERLC_OPTS+=+{parse_transform,lager_transform}\n"}' $(APP)/apps/my_app_1/Makefile
+
+	$i "Add a lager:error/2 call to my_app_1_app.erl that will fail if the parse_transform doesn't run"
+	$t perl -ni.bak -e 'print;if (/^-export/){print "\n-export([log/0]).\n"} if (eof) {print "\nlog() -> lager:error(\"test\", []).\n"}' $(APP)/apps/my_app_1/src/my_app_1_app.erl
+
+	$i "Create a new application my_app_2"
+	$t $(MAKE) -C $(APP) new-app in=my_app_2 $v
+
+	$i "Add my_app_1 to the list of local dependencies of my_app_2, don't add lager, but add the lager parse_transform (this will fail unless my_app_1 was indeed built first)"
+	$t perl -ni.bak -e 'print;if ($$.==1) {print "LOCAL_DEPS = my_app_1\nERLC_OPTS+=+{parse_transform,lager_transform}\n"}' $(APP)/apps/my_app_2/Makefile
+
+	$i "Add a lager:error/2 call to my_app_2_app.erl that will fail if the parse_transform doesn't run"
+	$t perl -ni.bak -e 'print;if (/^-export/){print "\n-export([log/0]).\n"} if (eof) {print "\nlog() -> lager:error(\"test\", []).\n"}' $(APP)/apps/my_app_2/src/my_app_2_app.erl
+
+	$i "Build the application"
+	$t $(MAKE) -C $(APP) $v
+
+	$i "Check that all compiled files exist"
+	$t test -f $(APP)/$(APP).d
+	$t test -f $(APP)/ebin/$(APP).app
+	$t test -f $(APP)/apps/my_app_1/my_app_1.d
+	$t test -f $(APP)/apps/my_app_1/ebin/my_app_1.app
+	$t test -f $(APP)/apps/my_app_1/ebin/my_app_1_app.beam
+	$t test -f $(APP)/apps/my_app_1/ebin/my_app_1_sup.beam
+	$t test -f $(APP)/apps/my_app_2/my_app_2.d
+	$t test -f $(APP)/apps/my_app_2/ebin/my_app_2.app
+	$t test -f $(APP)/apps/my_app_2/ebin/my_app_2_app.beam
+	$t test -f $(APP)/apps/my_app_2/ebin/my_app_2_sup.beam
+	$t test -f $(APP)/deps/lager/ebin/lager.app
+
+	$i "Distclean the application"
+	$t $(MAKE) -C $(APP) distclean $v
+
+	$i "Test after swapping my_app_1 and my_app_2 to make sure lexical ordering didnt incidentally build the correct app first"
+
+	$i "Add my_app_2 to the list of local dependencies of my_app_1, don't add lager, but add the lager parse_transform (this will fail unless my_app_2 was indeed built first)"
+	$t mv $(APP)/apps/my_app_1/Makefile{.bak,}
+	$t perl -ni.bak -e 'print;if ($$.==1) {print "LOCAL_DEPS = my_app_2\nERLC_OPTS+=+{parse_transform,lager_transform}\n"}' $(APP)/apps/my_app_1/Makefile
+
+	$i "Add Lager to the list of dependencies of my_app_2, and add the lager parse_transform"
+	$t mv $(APP)/apps/my_app_2/Makefile{.bak,}
+	$t perl -ni.bak -e 'print;if ($$.==1) {print "DEPS = lager\nERLC_OPTS+=+{parse_transform,lager_transform}\n"}' $(APP)/apps/my_app_2/Makefile
+
+	$i "Build the application"
+	$t $(MAKE) -C $(APP) $v
+
+	$i "Check that all compiled files exist"
+	$t test -f $(APP)/$(APP).d
+	$t test -f $(APP)/ebin/$(APP).app
+	$t test -f $(APP)/apps/my_app_1/my_app_1.d
+	$t test -f $(APP)/apps/my_app_1/ebin/my_app_1.app
+	$t test -f $(APP)/apps/my_app_1/ebin/my_app_1_app.beam
+	$t test -f $(APP)/apps/my_app_1/ebin/my_app_1_sup.beam
+	$t test -f $(APP)/apps/my_app_2/my_app_2.d
+	$t test -f $(APP)/apps/my_app_2/ebin/my_app_2.app
+	$t test -f $(APP)/apps/my_app_2/ebin/my_app_2_app.beam
+	$t test -f $(APP)/apps/my_app_2/ebin/my_app_2_sup.beam
+	$t test -f $(APP)/deps/lager/ebin/lager.app
+
+core-deps-apps-local-deps-circular: build clean
+
+	$i "Bootstrap a new OTP library named $(APP)"
+	$t mkdir $(APP)/
+	$t cp ../erlang.mk $(APP)/
+	$t $(MAKE) -C $(APP) -f erlang.mk bootstrap-lib $v
+
+	$i "Create a new application my_app_1"
+	$t $(MAKE) -C $(APP) new-app in=my_app_1 $v
+
+	$i "Create a new application my_app_2"
+	$t $(MAKE) -C $(APP) new-app in=my_app_2 $v
+
+	$i "Add my_app_1 to the list of local dependencies of my_app_2"
+	$t perl -ni.bak -e 'print;if ($$.==1) {print "LOCAL_DEPS = my_app_1\n"}' $(APP)/apps/my_app_2/Makefile
+
+	$i "Add my_app_2 to the list of local dependencies of my_app_1"
+	$t perl -ni.bak -e 'print;if ($$.==1) {print "LOCAL_DEPS = my_app_2\n"}' $(APP)/apps/my_app_1/Makefile
+
+	$i "Build the application"
+	$t $(MAKE) -C $(APP) $v
+
+	$i "Check that all compiled files exist"
+	$t test -f $(APP)/$(APP).d
+	$t test -f $(APP)/ebin/$(APP).app
+	$t test -f $(APP)/apps/my_app_1/my_app_1.d
+	$t test -f $(APP)/apps/my_app_1/ebin/my_app_1.app
+	$t test -f $(APP)/apps/my_app_1/ebin/my_app_1_app.beam
+	$t test -f $(APP)/apps/my_app_1/ebin/my_app_1_sup.beam
+	$t test -f $(APP)/apps/my_app_2/my_app_2.d
+	$t test -f $(APP)/apps/my_app_2/ebin/my_app_2.app
+	$t test -f $(APP)/apps/my_app_2/ebin/my_app_2_app.beam
+	$t test -f $(APP)/apps/my_app_2/ebin/my_app_2_sup.beam
+
+core-deps-apps-toplevel-local-deps: build clean
+
+	$i "Bootstrap a new OTP library named $(APP)"
+	$t mkdir $(APP)/
+	$t cp ../erlang.mk $(APP)/
+	$t $(MAKE) -C $(APP) -f erlang.mk bootstrap-lib $v
+
+	$i "Create a new application my_app_1"
+	$t $(MAKE) -C $(APP) new-app in=my_app_1 $v
+
+	$i "Add my_app_1 to the list of toplevel local dependencies"
+	$t perl -ni.bak -e 'print;if ($$.==1) {print "LOCAL_DEPS = my_app_1\n"}' $(APP)/Makefile
+
+	$i "Create a new application my_app_2"
+	$t $(MAKE) -C $(APP) new-app in=my_app_2 $v
+
+	$i "Build the application"
+	$t $(MAKE) -C $(APP) $v
+
+	$i "Check that all compiled files exist"
+	$t test -f $(APP)/$(APP).d
+	$t test -f $(APP)/ebin/$(APP).app
+	$t test -f $(APP)/apps/my_app_1/my_app_1.d
+	$t test -f $(APP)/apps/my_app_1/ebin/my_app_1.app
+	$t test -f $(APP)/apps/my_app_1/ebin/my_app_1_app.beam
+	$t test -f $(APP)/apps/my_app_1/ebin/my_app_1_sup.beam
+
+	$i "Check that my_app_2 compiled files DO NOT exist"
+	$t test ! -e $(APP)/apps/my_app_2/my_app_2.d
+	$t test ! -e $(APP)/apps/my_app_2/ebin/my_app_2.app
+	$t test ! -e $(APP)/apps/my_app_2/ebin/my_app_2_app.beam
+	$t test ! -e $(APP)/apps/my_app_2/ebin/my_app_2_sup.beam
+
+	$i "Add my_app_2 to the list of local dependencies of my_app_1"
+	$t perl -ni.bak -e 'print;if ($$.==1) {print "LOCAL_DEPS = my_app_2\n"}' $(APP)/apps/my_app_1/Makefile
+
+	$i "Rebuild the application"
+	$t $(MAKE) -C $(APP) $v
+
+	$i "Check that my_app_2 compiled files exist"
+	$t test -f $(APP)/apps/my_app_2/my_app_2.d
+	$t test -f $(APP)/apps/my_app_2/ebin/my_app_2.app
+	$t test -f $(APP)/apps/my_app_2/ebin/my_app_2_app.beam
+	$t test -f $(APP)/apps/my_app_2/ebin/my_app_2_sup.beam
+
 core-deps-autopatch-rebar: build clean
 
 	$i "Bootstrap a new OTP library named $(APP)"