aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile15
-rw-r--r--README.md113
-rw-r--r--appveyor.yml7
-rw-r--r--build.config1
-rw-r--r--circle.yml4
-rw-r--r--core/compat.mk25
-rw-r--r--core/core.mk24
-rw-r--r--core/erlc.mk74
-rw-r--r--core/test.mk4
-rw-r--r--doc/src/guide/app.asciidoc146
-rw-r--r--doc/src/guide/book.asciidoc14
-rw-r--r--doc/src/guide/compat.asciidoc88
-rw-r--r--doc/src/guide/external_plugins.asciidoc7
-rw-r--r--doc/src/guide/installation.asciidoc112
-rw-r--r--doc/src/guide/limitations.asciidoc45
-rw-r--r--doc/src/guide/why.asciidoc80
-rw-r--r--plugins/bootstrap.mk6
-rw-r--r--plugins/relx.mk6
-rw-r--r--test/Makefile173
-rw-r--r--test/core_app.mk1398
-rw-r--r--test/core_compat.mk221
-rw-r--r--test/core_plugins.mk86
-rw-r--r--test/core_upgrade.mk124
-rw-r--r--test/plugin_bootstrap.mk155
24 files changed, 2730 insertions, 198 deletions
diff --git a/Makefile b/Makefile
index 625a38e..5eff5d6 100644
--- a/Makefile
+++ b/Makefile
@@ -24,13 +24,22 @@ all:
awk 'FNR==1 && NR!=1{print ""}1' $(patsubst %,%.mk,$(BUILD_CONFIG)) \
| sed 's/^ERLANG_MK_VERSION = .*/ERLANG_MK_VERSION = $(ERLANG_MK_VERSION)/' > $(ERLANG_MK)
-ifeq ($(p),)
+ifdef p
check:
- $(MAKE) -C test
+ $(MAKE) -C test pkg-$p
else
+ifdef c
check:
- $(MAKE) -C test pkg-$(p)
+ $(MAKE) -C test $c LEGACY=$(LEGACY)
+else
+check:
+ $(MAKE) -C test LEGACY=$(LEGACY)
endif
+endif
+
+clean:
+ $(MAKE) -C test clean
+ rm -rf doc/guide.pdf doc/html
docs:
$(MAKE) -f core/core.mk -f core/docs.mk -f plugins/asciidoc.mk asciidoc
diff --git a/README.md b/README.md
index 5557282..5a00371 100644
--- a/README.md
+++ b/README.md
@@ -1,23 +1,12 @@
erlang.mk
=========
-Common Makefile rules for building and testing Erlang applications.
+A build tool for Erlang that just works.
-Also features support for dependencies and a package index.
+[Check out our upcoming user guide!](doc/src/guide/book.asciidoc)
-[Check out our upcoming documentation!](doc/src/guide/book.asciidoc)
-
-Why erlang.mk?
---------------
-
-A number of reasons might push someone to use erlang.mk instead of
-an Erlang-based build tool, including but not limited to the following:
-
- * You want a very fast compilation and test cycle
- * You want the full power of Unix at your disposal when hooking into your build tool
- * You want to be able to easily edit the damn build tool and fix it when it fails
- * You want to use the deps mechanism with non-Erlang Makefile-based projects
- * Your project will be part of a larger make or automake based environment
+The README only contains legacy documentation that was not moved to
+the guide yet. Check there if you don't find what you're looking for.
Requirements
------------
@@ -25,41 +14,8 @@ Requirements
`erlang.mk` requires GNU Make and expects to be ran in a standard
unix environment with Erlang installed and in the `$PATH`.
-`erlang.mk` uses `wget` for downloading the package index file.
-
-`erlang.mk` will NOT work if the path contains spaces. This is a
-limitation of POSIX compatible make build tools.
-
-Usage
------
-
-Add the file `erlang.mk` to your project, then use the following base
-Makefile:
-
-``` Makefile
-PROJECT = my_project
-include erlang.mk
-```
-
-Alternatively you can use the following command to generate a skeleton
-of an OTP application:
-
-``` bash
-$ make -f erlang.mk bootstrap
-```
-
-To generate a skeleton of an OTP library:
-
-``` bash
-$ make -f erlang.mk bootstrap-lib
-```
-
-Finally if you are going to create a release of this project you may
-want to also use the `bootstrap-rel` target.
-
-You can combine targets to perform many operations. For example, the
-shell command `make clean app` will have the effect of recompiling
-the application fully, without touching the dependencies.
+Common workflow
+---------------
A common workflow when editing a file would be to run `make` regularly
to see if it compiles (or less often `make clean app` if you want to
@@ -67,11 +23,6 @@ recompile everything), followed by `make dialyze` to see if there are
any type errors and then `make tests` to run the test suites. The
result of the test runs can be browsed from the `logs/index.html` file.
-Getting help
-------------
-
-You can use `make help` to get help about erlang.mk or its plugins.
-
Packages
--------
@@ -152,11 +103,11 @@ when used as dependency.
The patching occurs only once, immediately after the package has
been fetched.
-erlang.mk defines a number of packages to be patched. You can add
-more packages to the list by appending the `AUTOPATCH` variable.
+The autopatch feature is applied to all dependencies. To disable
+it for a dependency, use the `NO_AUTOPATCH` variable:
``` Makefile
-AUTOPATCH += gproc
+NO_AUTOPATCH += gproc
```
Releases
@@ -226,6 +177,11 @@ You can enable verbose mode by calling Make with the variable
$ V=1 make
```
+Parallel execution
+------------------
+
+*Parallel execution is currently disabled.*
+
Parallel execution can be enabled through the use of the
`-j` option. The following output showcases concurrent
downloading of dependencies.
@@ -255,15 +211,12 @@ Core package functionality
The following targets are specific to packages:
-`pkg-list` lists all packages in the index.
+`search` lists all packages in the index.
-`pkg-search q=STRING` searches the index for STRING.
+`search q=STRING` searches the index for STRING.
Packages are downloaded into `DEPS_DIR` (`./deps/` by default).
-The package index file is downloaded from `PKG_FILE_URL`
-and saved in `PKG_FILE2`.
-
Core compiler functionality
---------------------------
@@ -294,39 +247,6 @@ If `{id, "git"},` is found in your project's `.app.src`, the
extended output of `git describe ...` will replace it. This
can be retrieved at runtime via `application:get_key/2`.
-Updating erlang.mk
-------------------
-
-You can update erlang.mk by running `make erlang-mk`. This automated
-update will always take the latest erlang.mk version, compile it and
-replace the erlang.mk of your project with the updated version.
-
-If your project includes a `build.config`, erlang.mk will use it
-when building the updated version.
-
-The `ERLANG_MK_BUILD_CONFIG` variable can be used to rename the
-`build.config` file.
-
-The `ERLANG_MK_BUILD_DIR` variable contains the path to the
-temporary directory used to build the updated erlang.mk.
-
-Bootstrap plugin
-----------------
-
-This plugin is available by default. It adds the following
-targets:
-
-`bootstrap` generates a skeleton of an OTP application.
-
-`bootstrap-lib` generates a skeleton of an OTP library.
-
-`bootstrap-rel` generates the files needed to build a release.
-
-`new` generate a skeleton module based on one of the available
-templates.
-
-`list-templates` lists the available templates.
-
C/C++ compiler plugin
---------------------
@@ -437,7 +357,6 @@ subdirectories names in the compiled module name add
`DTL_FULL_PATH=1` into your Makefile - `a/b/templatename.dtl`
will be compiled into `a_b_templatename_dtl.beam`.
-
Escript plugin
--------------
diff --git a/appveyor.yml b/appveyor.yml
new file mode 100644
index 0000000..5e8e5af
--- /dev/null
+++ b/appveyor.yml
@@ -0,0 +1,7 @@
+build_script:
+- C:\msys64\usr\bin\bash -lc "pacman --needed --noconfirm -Sy pacman-mirrors"
+- C:\msys64\usr\bin\bash -lc "pacman --noconfirm -Sy"
+- C:\msys64\usr\bin\bash -lc "pacman --noconfirm -S git make"
+test_script:
+- C:\msys64\usr\bin\bash -lc "cd $APPVEYOR_BUILD_FOLDER && make -j 32 -k check"
+- C:\msys64\usr\bin\bash -lc "cd $APPVEYOR_BUILD_FOLDER && make -j 32 -k check LEGACY=1"
diff --git a/build.config b/build.config
index 31090df..2c2fba6 100644
--- a/build.config
+++ b/build.config
@@ -16,6 +16,7 @@ plugins/protobuffs
core/erlc
core/docs
core/test
+core/compat
# Plugins.
plugins/asciidoc
diff --git a/circle.yml b/circle.yml
index 73b087e..b5a8efe 100644
--- a/circle.yml
+++ b/circle.yml
@@ -17,5 +17,5 @@ dependencies:
test:
override:
- - source $HOME/erlang/OTP-18.0.2/activate && make check:
- timeout: 7200
+ - source $HOME/erlang/OTP-18.0.2/activate && make -j 32 -k check
+ - source $HOME/erlang/OTP-18.0.2/activate && make -j 32 -k check LEGACY=1
diff --git a/core/compat.mk b/core/compat.mk
new file mode 100644
index 0000000..bdf9a90
--- /dev/null
+++ b/core/compat.mk
@@ -0,0 +1,25 @@
+# Copyright (c) 2015, Loïc Hoguin <[email protected]>
+# This file is part of erlang.mk and subject to the terms of the ISC License.
+
+.PHONY: rebar.config
+
+# We strip out -Werror because we don't want to fail due to
+# warnings when used as a dependency.
+
+define compat_convert_erlc_opt
+$(if $(filter-out -Werror,$1),\
+ $(if $(findstring +,$1),\
+ $(shell echo $1 | cut -b 2-)))
+endef
+
+define compat_rebar_config
+{deps, [$(call comma_list,$(foreach d,$(DEPS),\
+ {$(call dep_name,$d),".*",{git,"$(call dep_repo,$d)","$(call dep_commit,$d)"}}))]}.
+{erl_opts, [$(call comma_list,$(foreach o,$(ERLC_OPTS),$(call compat_convert_erlc_opt,$o)))]}.
+endef
+
+$(eval _compat_rebar_config = $$(compat_rebar_config))
+$(eval export _compat_rebar_config)
+
+rebar.config:
+ $(gen_verbose) echo "$${_compat_rebar_config}" > rebar.config
diff --git a/core/core.mk b/core/core.mk
index 4b292ee..a9cb808 100644
--- a/core/core.mk
+++ b/core/core.mk
@@ -45,7 +45,6 @@ export ERLANG_MK_TMP
ERL = erl +A0 -noinput -boot start_clean
# Platform detection.
-# @todo Add Windows/Cygwin detection eventually.
ifeq ($(PLATFORM),)
UNAME_S := $(shell uname -s)
@@ -64,6 +63,10 @@ else ifeq ($(UNAME_S),NetBSD)
PLATFORM = netbsd
else ifeq ($(UNAME_S),OpenBSD)
PLATFORM = openbsd
+else ifeq ($(UNAME_S),DragonFly)
+PLATFORM = dragonfly
+else ifeq ($(shell uname -o),Msys)
+PLATFORM = msys2
else
$(error Unable to detect platform. Please open a ticket with the output of uname -a.)
endif
@@ -90,7 +93,10 @@ ifneq ($(wildcard erl_crash.dump),)
$(gen_verbose) rm -f erl_crash.dump
endif
-distclean:: clean
+distclean:: clean distclean-tmp
+
+distclean-tmp:
+ $(gen_verbose) rm -rf $(ERLANG_MK_TMP)
help::
$(verbose) printf "%s\n" \
@@ -135,6 +141,12 @@ define erlang
$(ERL) $(2) -pz $(ERLANG_MK_TMP)/rebar/ebin -eval "$(subst $(newline),,$(subst ",\",$(1)))" -- erlang.mk
endef
+ifeq ($(PLATFORM),msys2)
+core_native_path = $(subst \,\\\\,$(shell cygpath -w $1))
+else
+core_native_path = $1
+endif
+
ifeq ($(shell which wget 2>/dev/null | wc -l), 1)
define core_http_get
wget --no-check-certificate -O $(1) $(2)|| rm $(1)
@@ -156,7 +168,7 @@ define core_http_get.erl
endef
define core_http_get
- $(call erlang,$(call core_http_get.erl,$(1),$(2)))
+ $(call erlang,$(call core_http_get.erl,$(call core_native_path,$1),$2))
endef
endif
@@ -171,13 +183,15 @@ core_ls = $(filter-out $(1),$(shell echo $(1)))
# Automated update.
+ERLANG_MK_REPO ?= https://github.com/ninenines/erlang.mk
+ERLANG_MK_COMMIT ?=
ERLANG_MK_BUILD_CONFIG ?= build.config
ERLANG_MK_BUILD_DIR ?= .erlang.mk.build
erlang-mk:
- git clone https://github.com/ninenines/erlang.mk $(ERLANG_MK_BUILD_DIR)
+ git clone $(ERLANG_MK_REPO) $(ERLANG_MK_BUILD_DIR)
if [ -f $(ERLANG_MK_BUILD_CONFIG) ]; then cp $(ERLANG_MK_BUILD_CONFIG) $(ERLANG_MK_BUILD_DIR)/build.config; fi
- cd $(ERLANG_MK_BUILD_DIR) && $(MAKE)
+ cd $(ERLANG_MK_BUILD_DIR) && $(if $(ERLANG_MK_COMMIT),git checkout $(ERLANG_MK_COMMIT) &&) $(MAKE)
cp $(ERLANG_MK_BUILD_DIR)/erlang.mk ./erlang.mk
rm -rf $(ERLANG_MK_BUILD_DIR)
diff --git a/core/erlc.mk b/core/erlc.mk
index 80affca..fbba7ae 100644
--- a/core/erlc.mk
+++ b/core/erlc.mk
@@ -40,13 +40,15 @@ asn1_verbose = $(asn1_verbose_$(V))
mib_verbose_0 = @echo " MIB " $(filter %.bin %.mib,$(?F));
mib_verbose = $(mib_verbose_$(V))
+ifneq ($(wildcard src/),)
+
# Targets.
ifeq ($(wildcard ebin/test),)
-app::
+app:: $(PROJECT).d
$(verbose) $(MAKE) --no-print-directory app-build
else
-app:: clean
+app:: clean $(PROJECT).d
$(verbose) $(MAKE) --no-print-directory app-build
endif
@@ -55,7 +57,7 @@ define app_file
{application, $(PROJECT), [
{description, "$(PROJECT_DESCRIPTION)"},
{vsn, "$(PROJECT_VERSION)"},
- {id, "$(1)"},
+ $(if $(IS_DEP),{id$(comma)$(space)"$(1)"}$(comma))
{modules, [$(call comma_list,$(2))]},
{registered, []},
{applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(DEPS))]}
@@ -66,7 +68,7 @@ define app_file
{application, $(PROJECT), [
{description, "$(PROJECT_DESCRIPTION)"},
{vsn, "$(PROJECT_VERSION)"},
- {id, "$(1)"},
+ $(if $(IS_DEP),{id$(comma)$(space)"$(1)"}$(comma))
{modules, [$(call comma_list,$(2))]},
{registered, [$(call comma_list,$(PROJECT)_sup $(PROJECT_REGISTERED))]},
{applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(DEPS))]},
@@ -76,25 +78,10 @@ endef
endif
app-build: ebin/$(PROJECT).app
- $(eval GITDESCRIBE := $(shell git describe --dirty --abbrev=7 --tags --always --first-parent 2>/dev/null || true))
- $(eval MODULES := $(patsubst %,'%',$(sort $(notdir $(basename $(shell find ebin -type f -name *.beam))))))
-ifeq ($(wildcard src/$(PROJECT).app.src),)
- $(app_verbose) echo $(subst $(newline),,$(subst ",\",$(call app_file,$(GITDESCRIBE),$(MODULES)))) \
- > ebin/$(PROJECT).app
-else
- $(verbose) if [ -z "$$(grep -E '^[^%]*{\s*modules\s*,' src/$(PROJECT).app.src)" ]; then \
- echo "Empty modules entry not found in $(PROJECT).app.src. Please consult the erlang.mk README for instructions." >&2; \
- exit 1; \
- fi
- $(appsrc_verbose) cat src/$(PROJECT).app.src \
- | sed "s/{[[:space:]]*modules[[:space:]]*,[[:space:]]*\[\]}/{modules, \[$(call comma_list,$(MODULES))\]}/" \
- | sed "s/{id,[[:space:]]*\"git\"}/{id, \"$(GITDESCRIBE)\"}/" \
- > ebin/$(PROJECT).app
-endif
+ $(verbose) echo -n
# Source files.
-ifneq ($(wildcard src/),)
ERL_FILES = $(sort $(call core_find,src/,*.erl))
CORE_FILES = $(sort $(call core_find,src/,*.core))
@@ -121,10 +108,10 @@ endif
ifneq ($(wildcard mibs/),)
MIB_FILES = $(sort $(call core_find,mibs/,*.mib))
-$(PROJECT).d:: $(MIB_FILES)
+$(PROJECT).d:: $(COMPILE_MIB_FIRST_PATHS) $(MIB_FILES)
$(verbose) mkdir -p include/ priv/mibs/
- $(mib_verbose) erlc -v $(ERLC_MIB_OPTS) -o priv/mibs/ -I priv/mibs/ $(COMPILE_MIB_FIRST_PATHS) $(MIB_FILES)
- $(mib_verbose) erlc -o include/ -- priv/mibs/*.bin
+ $(mib_verbose) erlc -v $(ERLC_MIB_OPTS) -o priv/mibs/ -I priv/mibs/ $?
+ $(mib_verbose) erlc -o include/ -- $(addprefix priv/mibs/,$(patsubst %.mib,%.bin,$(notdir $?)))
endif
# Leex and Yecc files.
@@ -170,7 +157,7 @@ define makedep.erl
end
end,
Depend = [begin
- case epp:parse_file(F, [{includes, ["include/"]}]) of
+ case epp:parse_file(F, ["include/"], []) of
{ok, Forms} ->
Deps = lists:usort(lists:foldl(fun
({attribute, _, behavior, Dep}, Acc) -> Add(Dep, Acc);
@@ -179,7 +166,10 @@ define makedep.erl
({attribute, _, file, {Dep, _}}, Acc) -> AddHd(Dep, Acc);
(_, Acc) -> Acc
end, [], Forms)),
- [F, ":", [[" ", D] || D <- Deps], "\n", CompileFirst(Deps)];
+ case Deps of
+ [] -> "";
+ _ -> [F, "::", [[" ", D] || D <- Deps], "; @touch \$$@\n", CompileFirst(Deps)]
+ end;
{error, enoent} ->
[]
end
@@ -188,12 +178,16 @@ define makedep.erl
halt()
endef
+ifeq ($(if $(NO_MAKEDEP),$(wildcard $(PROJECT).d),),)
$(PROJECT).d:: $(ERL_FILES) $(call core_find,include/,*.hrl)
$(makedep_verbose) $(call erlang,$(call makedep.erl,$@))
+endif
-include $(PROJECT).d
-ebin/$(PROJECT).app:: $(PROJECT).d
+ebin/$(PROJECT).app:: ebin/
+
+ebin/:
$(verbose) mkdir -p ebin/
define compile_erl
@@ -203,16 +197,30 @@ endef
ebin/$(PROJECT).app:: $(ERL_FILES) $(CORE_FILES)
$(if $(strip $?),$(call compile_erl,$?))
-
-$(sort $(ERL_FILES) $(CORE_FILES)):
- @touch $@
+ $(eval GITDESCRIBE := $(shell git describe --dirty --abbrev=7 --tags --always --first-parent 2>/dev/null || true))
+ $(eval MODULES := $(patsubst %,'%',$(sort $(notdir $(basename \
+ $(filter-out $(ERLC_EXCLUDE_PATHS),$(ERL_FILES) $(CORE_FILES)))))))
+ifeq ($(wildcard src/$(PROJECT).app.src),)
+ $(app_verbose) echo "$(subst $(newline),,$(subst ",\",$(call app_file,$(GITDESCRIBE),$(MODULES))))" \
+ > ebin/$(PROJECT).app
+else
+ $(verbose) if [ -z "$$(grep -E '^[^%]*{\s*modules\s*,' src/$(PROJECT).app.src)" ]; then \
+ echo "Empty modules entry not found in $(PROJECT).app.src. Please consult the erlang.mk README for instructions." >&2; \
+ exit 1; \
+ fi
+ $(appsrc_verbose) cat src/$(PROJECT).app.src \
+ | sed "s/{[[:space:]]*modules[[:space:]]*,[[:space:]]*\[\]}/{modules, \[$(call comma_list,$(MODULES))\]}/" \
+ | sed "s/{id,[[:space:]]*\"git\"}/{id, \"$(GITDESCRIBE)\"}/" \
+ > ebin/$(PROJECT).app
endif
clean:: clean-app
clean-app:
$(gen_verbose) rm -rf $(PROJECT).d ebin/ priv/mibs/ $(XRL_ERL_FILES) $(YRL_ERL_FILES) \
- $(addprefix include/,$(patsubst %.mib,.hrl,$(notdir $(MIB_FILES)))) \
- $(addprefix include/,$(patsubst %.asn1,.hrl,$(notdir $(ASN1_FILES)))) \
- $(addprefix include/,$(patsubst %.asn1,.asn1db,$(notdir $(ASN1_FILES)))) \
- $(addprefix src/,$(patsubst %.erl,.asn1db,$(notdir $(ASN1_FILES))))
+ $(addprefix include/,$(patsubst %.mib,%.hrl,$(notdir $(MIB_FILES)))) \
+ $(addprefix include/,$(patsubst %.asn1,%.hrl,$(notdir $(ASN1_FILES)))) \
+ $(addprefix include/,$(patsubst %.asn1,%.asn1db,$(notdir $(ASN1_FILES)))) \
+ $(addprefix src/,$(patsubst %.asn1,%.erl,$(notdir $(ASN1_FILES))))
+
+endif
diff --git a/core/test.mk b/core/test.mk
index 83bbecb..d7b0bfe 100644
--- a/core/test.mk
+++ b/core/test.mk
@@ -31,12 +31,12 @@ endif
ifeq ($(wildcard ebin/test),)
test-build:: ERLC_OPTS=$(TEST_ERLC_OPTS)
-test-build:: clean deps test-deps
+test-build:: clean deps test-deps $(PROJECT).d
$(verbose) $(MAKE) --no-print-directory app-build test-dir ERLC_OPTS="$(TEST_ERLC_OPTS)"
$(gen_verbose) touch ebin/test
else
test-build:: ERLC_OPTS=$(TEST_ERLC_OPTS)
-test-build:: deps test-deps
+test-build:: deps test-deps $(PROJECT).d
$(verbose) $(MAKE) --no-print-directory app-build test-dir ERLC_OPTS="$(TEST_ERLC_OPTS)"
endif
diff --git a/doc/src/guide/app.asciidoc b/doc/src/guide/app.asciidoc
index a04c5a9..5c097f6 100644
--- a/doc/src/guide/app.asciidoc
+++ b/doc/src/guide/app.asciidoc
@@ -165,6 +165,36 @@ key from dependencies automatically, which means you need to
add them to Erlang.mk and to the '.app.src' at the same time,
duplicating the work.
+If you really can't live without the legacy method, for one
+reason or another, worry not; Erlang.mk will support it. And
+if you need to create a new project that uses this method, you
+just have to say so when bootstrapping:
+
+[source,bash]
+$ make -f erlang.mk bootstrap-lib LEGACY=1
+
+=== Automatic application resource file values
+
+When building the application resource file, Erlang.mk may
+automatically add an `id` key with information about the
+Git commit (if using Git), or an empty string otherwise.
+It will only do this under specific conditions:
+
+* The application was built as a dependency of another, or
+* The legacy method was used, and the '.app.src' file contained `{id, "git"}`
+
+This value is most useful when you need to help your users,
+as it allows you to know which version they run exactly by
+asking them to look in the file, or by running a simple
+command on their production server:
+
+[source,erlang]
+----
+1> application:get_all_key(cowboy).
+{ok,[{description,"Small, fast, modular HTTP server."},
+ {id,"2.0.0-pre.2-25-g0ffde50-dirty"},
+----
+
=== File formats
Erlang.mk supports a variety of different source file formats.
@@ -201,6 +231,56 @@ Erlang.mk also comes with plugins for the following formats:
| .proto | src/ | Protocol buffers | ebin/*.beam
|===
+=== Compilation options
+
+Erlang.mk provides a few variables that you can use to customize
+the build process and the resulting files.
+
+==== ERLC_OPTS
+
+`ERLC_OPTS` can be used to pass some options to `erlc`, the Erlang
+compiler. Erlang.mk does not restrict any option. Please refer to
+the http://www.erlang.org/doc/man/erlc.html[erlc Manual] for the
+full list.
+
+By default, Erlang.mk will set the following options:
+
+[source,make]
+ERLC_OPTS = -Werror +debug_info +warn_export_vars +warn_shadow_vars +warn_obsolete_guard
+
+In other words: warnings as errors, debug info (recommended) and
+enable warnings for exported variables, shadow variables and
+obsolete guard functions.
+
+You can redefine this variable in your Makefile to change it
+completely, either before or after including Erlang.mk:
+
+[source,make]
+ERLC_OPTS = +debug_info
+
+You can also filter out some options from the defaults Erlang.mk
+sets, by defining ERLC_OPTS after including Erlang.mk using the
+`:=` operator.
+
+[source,make]
+----
+include erlang.mk
+
+ERLC_OPTS := $(filter-out -Werror,$(ERLC_OPTS))
+----
+
+==== ERLC_EXCLUDE
+
+`ERLC_EXCLUDE` can be used to exclude some modules from the
+compilation. It's there for handling special cases, you should
+not normally need it.
+
+To exclude a module, simply list it in the variable, either
+before or after including Erlang.mk:
+
+[source,make]
+ERLC_EXCLUDE = cowboy_http2
+
=== Cold and hot builds
The first time you run `make`, Erlang.mk will build everything.
@@ -235,10 +315,64 @@ transforms have changed. Erlang.mk also automatically keeps
track of which files should be compiled first, for example
when you have behaviors used by other modules in your project.
-=== Generated source files ===
+If your project is stable, you may want to disable generating
+the dependency tracking file every time you compile. You can
+do this by adding the following line to your 'Makefile':
+
+[source,make]
+NO_MAKEDEP ?= 1
+
+As you can see, the snippet above uses `?=` instead of a
+simple equal sign. This is to allow you to temporarily override
+this value when you do make substantial changes to your project
+(including a new header file, new module with dependencies, etc.)
+and want to rebuild the dependency tracking file. You'll be
+able to use the following command:
+
+[source,bash]
+$ NO_MAKEDEP= make
+
+Otherwise, `make clean app` will of course force the
+recompilation of your project.
+
+Erlang.mk can also keep track of the source files generated
+by other means, for example if you generate code from a data
+file in your repository.
+
+=== Generating Erlang source
+
+Erlang.mk provides hooks at different stages of the build process.
+When your goal is to generate Erlang source files, you can
+add your own rules before or after the dependency tracking
+file is generated. To do this, you would add your hook before
+or after including the 'erlang.mk' file.
+
+The easiest way is after:
-Generated source files are supported: they should be listed as
-dependencies to `$(PROJECT).d`:
+[source,make]
+----
+PROJECT = example
+
+include erlang.mk
+
+$(PROJECT).d:: src/generated_mod.erl
+
+src/generated_mod.erl:: gen-mod.sh
+ $(gen_verbose) ./gen-mod.sh $@
+----
+
+In this case we use `$(gen_verbose)` to hide the details of
+the build by default. Erlang.mk will simply say what file
+is it currently generating.
+
+When using an external script to generate the Erlang source
+file, it is recommended to depend on that script, so that
+the source file gets generated again when the script gets
+modified.
+
+If for whatever reason you prefer to hook before including
+Erlang.mk, don't forget to set the `.DEFAULT_GOAL` variable,
+otherwise nothing will get built:
[source,make]
----
@@ -250,14 +384,10 @@ $(PROJECT).d:: src/generated_mod.erl
include erlang.mk
-src/generated_mod.erl::
+src/generated_mod.erl:: gen-mod.sh
$(gen_verbose) ./gen-mod.sh $@
----
-Note how `.DEFAULT_GOAL` is set to `all` near the beginning. Without
-this, `$(PROJECT).d` would become the default target, changing the
-expected behavior of this `Makefile`.
-
=== Cleaning
Building typically involves creating a lot of new files. Some
diff --git a/doc/src/guide/book.asciidoc b/doc/src/guide/book.asciidoc
index e42e5bf..2056dad 100644
--- a/doc/src/guide/book.asciidoc
+++ b/doc/src/guide/book.asciidoc
@@ -4,12 +4,26 @@
= Erlang.mk User Guide
Loïc Hoguin <[email protected]>
+include::installation.asciidoc[Installation]
+
include::getting_started.asciidoc[Getting started]
include::overview.asciidoc[Overview]
include::updating.asciidoc[Updating Erlang.mk]
+include::limitations.asciidoc[Limitations]
+
+= Code
+
+include::app.asciidoc[Building]
+
+include::compat.asciidoc[Compatibility with other build tools]
+
= Advanced
include::external_plugins.asciidoc[External plugins]
+
+= About Erlang.mk
+
+include::why.asciidoc[Why erlang.mk?]
diff --git a/doc/src/guide/compat.asciidoc b/doc/src/guide/compat.asciidoc
new file mode 100644
index 0000000..61386e7
--- /dev/null
+++ b/doc/src/guide/compat.asciidoc
@@ -0,0 +1,88 @@
+== Compatibility with other build tools
+
+Erlang.mk tries its best to be compatible with the other Erlang
+build tools. It can use dependencies written with other build
+tools in mind, and can also make your projects usable by those
+build tools as well. Erlang.mk is like the cool kid that gets
+along with everybody.
+
+In this chapter I will use the term _Rebar project_ to refer
+to a project built using Rebar 2, Rebar 3 or Mad. These three
+build tools are very similar and share the same configuration
+file.
+
+=== Rebar projects as Erlang.mk dependencies
+
+Erlang.mk comes with a feature called _Autoload_ which will
+use Rebar 2 to patch any Rebar project and make it compatible
+with Erlang.mk. This feature essentially patches Rebar out
+and adds a Makefile to the project that Erlang.mk can then
+use for building:
+
+_Autoload_ is documented in more details in the
+link:deps.asciidoc[Packages and dependencies] chapter.
+
+=== Erlang.mk projects as Rebar dependencies
+
+Erlang.mk projects can be made compatible with the Rebar family
+of build tools pretty easily, as Erlang.mk will generate
+all the files they require for building.
+
+The Rebar family requires two files: a 'rebar.config' file
+containing compilation options and the list of dependencies,
+and the application resource file, found either at
+'ebin/$(PROJECT).app' or at 'src/$(PROJECT).app.src'.
+
+==== Rebar configuration
+
+Erlang.mk comes with a target that generates a 'rebar.config'
+file when invoked:
+
+[source,bash]
+$ make rebar.config
+
+Careful! This will build the file even if it already existed
+before.
+
+To build this file, Erlang.mk uses information it finds in
+the `DEPS` and `ERLC_OPTS` variables, among others. This
+means that the Rebar family builds your project much the
+same way as Erlang.mk.
+
+Careful though! Different build tools have different fetching
+strategies. If some applications provide differing dependencies,
+they might be fetched differently by other build tools. Check
+the link:sanity_check.asciidoc[Sanity check] chapter to find
+out how to detect such issues.
+
+You can automatically generate this file when you build
+your application, by making it a dependency of the `app`
+target:
+
+[source,make]
+----
+app:: rebar.config
+----
+
+Don't forget to commit the file when it changes!
+
+If you run into other issues, it's probably because you use a
+feature specific to Erlang.mk, like the `cp` fetch method.
+It could also be that we forgot to handle something! Sorry.
+We are of course interested to hear about any compatibility
+problems you may have, just open a ticket!
+
+==== Application resource file
+
+Erlang.mk has two ways to generate an application resource
+file: from the information found in the Makefile, or from
+the information found in the 'src/$(PROJECT).app.src' file.
+Needless to say, if you have this file in your repository,
+then you don't need to worry about compatibility with other
+build tools.
+
+If you don't, however, it's not much harder. Every time
+Erlang.mk will compile your application, it will produce
+a new 'ebin/$(PROJECT).app' file. Simply commit this file
+when it changes. It will only change when you modify the
+configuration, add or remove modules.
diff --git a/doc/src/guide/external_plugins.asciidoc b/doc/src/guide/external_plugins.asciidoc
index e5fdbd7..027b1b9 100644
--- a/doc/src/guide/external_plugins.asciidoc
+++ b/doc/src/guide/external_plugins.asciidoc
@@ -65,8 +65,11 @@ For eaxmple, if you have two plugins 'mk/dist.mk' and
file:
[source,make]
-include mk/dist.mk
-include mk/templates.mk
+THIS := $(dir $(realpath $(lastword $(MAKEFILE_LIST))))
+include $(THIS)/mk/dist.mk
+include $(THIS)/mk/templates.mk
+
+The `THIS` variable is required to relatively include files.
This allows users to not only be able to select individual
plugins, but also select all plugins from the dependency
diff --git a/doc/src/guide/installation.asciidoc b/doc/src/guide/installation.asciidoc
new file mode 100644
index 0000000..94232b8
--- /dev/null
+++ b/doc/src/guide/installation.asciidoc
@@ -0,0 +1,112 @@
+== Installation
+
+This chapter explains how to setup your system in
+order to use Erlang.mk.
+
+=== On Unix
+
+Erlang.mk requires GNU Make to be installed. GNU Make 3.81
+or later is required. GNU Make 4.1 or later is recommended,
+as this is the version Erlang.mk is developed on.
+
+Some functionality requires that Autoconf 2.59 or later be
+installed, in order to compile Erlang/OTP. Erlang/OTP may
+have further requirements depending on your needs.
+
+Erlang.mk currently requires Erlang/OTP to be installed in
+order to compile Erlang projects.
+
+Some packages may require additional libraries.
+
+=== On Windows
+
+Erlang.mk can be used on Windows inside an MSYS2 environment.
+Cygwin, MSYS (the original) and native Windows (both Batch
+and PowerShell) are currently not supported.
+
+The rest of this section details how to setup Erlang/OTP and
+MSYS2 in order to use Erlang.mk.
+
+==== Installing Erlang/OTP
+
+Erlang.mk requires Erlang/OTP to be installed. The OTP team
+provides binaries of Erlang/OTP for all major and minor releases,
+available from the http://www.erlang.org/download.html[official download page].
+It is recommended that you use the 64-bit installer unless
+technically impossible. Please follow the instructions from
+the installer to complete the installation.
+
+The OTP team also provides a short guide to
+http://www.erlang.org/download.html[installing Erlang/OTP on Windows]
+if you need additional references.
+
+You can install Erlang/OTP silently using the `/S` switch
+on the command line:
+
+[source,batch]
+C:\Users\essen\Downloads> otp_win64_18.0.exe /S
+
+==== Installing MSYS2
+
+The only supported environment on Windows is MSYS2. MSYS2 is
+a lightweight Unix-like environment for Windows that comes
+with the Arch Linux package manager, `pacman`.
+
+The MSYS2 project provides a http://msys2.github.io[one click installer]
+and instructions to set things up post-installation.
+
+It is currently not possible to use the installer silently.
+Thankfully, the MSYS2 project provides an archive that can
+be used in lieu of the installer. The archive however requires
+_7zip_ to decompress it.
+
+First, download the
+http://sourceforge.net/projects/msys2/files/Base/x86_64/msys2-base-x86_64-20150512.tar.xz/download[MSYS2 base archive]
+and extract it under 'C:\'. Assuming you downloaded the
+archive as 'msys2.tar.xz' and put it in 'C:\', you can
+use the following commands to extract it:
+
+[source,batch]
+C:\> 7z x msys2.tar.xz
+C:\> 7z x msys2.tar > NUL
+
+Then you can run the two commands needed to perform the
+post-installation setup:
+
+[source,batch]
+C:\> C:\msys64\usr\bin\bash -lc "pacman --needed --noconfirm -Sy bash pacman pacman-mirrors msys2-runtime"
+C:\> C:\msys64\usr\bin\bash -lc "pacman --noconfirm -Syu"
+
+==== Installing the required MSYS2 packages
+
+After following these instructions, you can install GNU Make,
+Git and any other required softwares. From an MSYS2 shell,
+you can call `pacman` directly:
+
+[source,bash]
+$ pacman -S git make
+
+You can use `pacman -Ss` to search packages. For example,
+to find all packages related to GCC:
+
+[source,bash]
+$ pacman -Ss gcc
+
+You can also run commands under the MSYS2 environment from
+the Windows command line or batch files. This command will
+install GNU Make and Git:
+
+[source,batch]
+C:\> C:\msys64\usr\bin\bash -lc "pacman --noconfirm -S git make"
+
+You can use similar `bash` commands if you need to run programs
+inside the MSYS2 environment from a batch file.
+
+==== Gotchas
+
+While most of the basic functionality will just work, there are
+still some issues. Erlang.mk needs to be fixed to pass the
+right paths when running Erlang scripts. We are working on it.
+Erlang.mk is fully tested on both Linux and Windows, but is
+lacking tests in the areas not yet covered by this guide,
+so expect bugs to be fixed as more tests are added.
diff --git a/doc/src/guide/limitations.asciidoc b/doc/src/guide/limitations.asciidoc
new file mode 100644
index 0000000..86ca918
--- /dev/null
+++ b/doc/src/guide/limitations.asciidoc
@@ -0,0 +1,45 @@
+== Limitations
+
+No software is perfect.
+
+It's very important, when evaluating and when using a tool,
+to understand its limitations, so as to avoid making mistakes
+and wasting valuable time.
+
+This chapter lists all known limitations of Erlang.mk.
+
+=== Erlang must be available
+
+Currently Erlang.mk requires you to install Erlang beforehand.
+Installing Erlang is not always easy, particularly if you need
+a specific version of Erlang for a specific project.
+
+In addition, the Erlang being used must be in your `$PATH`
+before you use Erlang.mk.
+
+In the future we envision, Erlang.mk could manage the Erlang
+version you need to use a project. Erlang.mk already does this
+for running tests when using `make ci`, so doing this during
+development is just a step away.
+
+=== Spaces in path
+
+Erlang.mk will currently not work properly if the path to the
+project contains spaces. To check if that is the case, use the
+command `pwd`.
+
+This issue is due to how Makefiles work. There might be ways
+to solve it, we have not given up on it, but it's very low
+priority considering how simple the workaround is.
+
+=== Dependency tracking and modification times
+
+Erlang source files that depend on other files will have their
+modification time updated when they need to be recompiled due
+to a dependency having changed. This could cause some editors to
+think the file changed when it didn't.
+
+Erlang.mk must use this method in order to be able to compile
+files in one `erlc` invocation. The benefits greatly outweigh
+the issue in this case and so there are currently no plans to
+fix this behavior.
diff --git a/doc/src/guide/why.asciidoc b/doc/src/guide/why.asciidoc
new file mode 100644
index 0000000..37aa676
--- /dev/null
+++ b/doc/src/guide/why.asciidoc
@@ -0,0 +1,80 @@
+== Why Erlang.mk
+
+Why would you choose Erlang.mk, if not for its
+link:overview.asciidoc[many features]? This chapter will
+attempt to answer that.
+
+=== Erlang.mk is fast
+
+Erlang.mk is as fast as it gets.
+
+Erlang.mk will group the compilation of files so as to avoid
+running the BEAM more than necessary. This saves many seconds
+compared to traditional Makefiles, even on small projects.
+
+Erlang.mk will not try to be too smart. It provides a simple
+solution that works for most people, and gives additional
+options for projects that run into edge cases, often in the
+form of extra variables or rules to be defined.
+
+=== Erlang.mk gives you the full power of Unix
+
+Erlang.mk is a Makefile.
+
+You could use Erlang.mk directly without configuring anything
+and it would just work. But you can also extend it greatly
+either through configuration or hooks, and you can of course
+add your own rules to the Makefile.
+
+In all cases: for configuration, hooks or custom rules, you
+have all the power of Unix at your disposal, and can call
+any utility _or even any language interpreter_ you want,
+every time you need to. Erlang.mk also allows you to write
+scripts in this small language called Erlang directly inside
+your Makefile if you ever need to...
+
+=== Erlang.mk is a text file
+
+Erlang.mk is a Makefile.
+
+Which means Erlang.mk is a simple text file. You can edit a
+text file. Nothing stops you. If you run into any bug, or
+behavior that does not suit you, you can just open the
+'erlang.mk' file in your favorite editor, fix and/or comment
+a few lines, save, and try again. It's as simple as it gets.
+
+Currently using a binary build tool? Good luck with that.
+
+=== Erlang.mk can manage Erlang itself
+
+Erlang.mk isn't written in Erlang.
+
+That's not a good thing, you say? Well, here's one thing
+that Erlang.mk and Makefiles can do for you that Erlang
+build tool can't easily: choose what version of Erlang is
+to be used for compiling the project.
+
+This really is a one-liner in Erlang.mk (a few more lines
+if you also let it download about build Erlang directly)
+and allows for even greater things, like testing your
+project across all supported Erlang versions in one small
+command: `make -k ci`.
+
+=== Erlang.mk can do more than Erlang
+
+Erlang.mk doesn't care what your dependencies are written in.
+
+Erlang.mk will happily compile any dependency, as long as
+they come with a Makefile. The dependency can be written
+in C, C++ or even Javascript... Who cares, really? If you
+need Erlang.mk to fetch it, then Erlang.mk will fetch it
+and compile it as needed.
+
+=== Erlang.mk integrates nicely in Make and Automake projects
+
+If you are planning to put your project in the middle of
+a Make or Automake-based build environment, then the most
+logical thing to do is to use a Makefile.
+
+Erlang.mk will happily sit in such an environment and behave
+as you expect it to.
diff --git a/plugins/bootstrap.mk b/plugins/bootstrap.mk
index 7f8331e..44f10ea 100644
--- a/plugins/bootstrap.mk
+++ b/plugins/bootstrap.mk
@@ -49,6 +49,8 @@ endef
ifdef SP
define bs_Makefile
PROJECT = $(PROJECT)
+PROJECT_DESCRIPTION = New project
+PROJECT_VERSION = 0.0.1
# Whitespace to be used when creating files from templates.
SP = $(SP)
@@ -355,7 +357,9 @@ ifneq ($(wildcard src/),)
endif
$(call render_template,bs_Makefile,Makefile)
$(verbose) mkdir src/
+ifdef LEGACY
$(call render_template,bs_appsrc,src/$(PROJECT).app.src)
+endif
$(call render_template,bs_app,src/$(PROJECT)_app.erl)
$(eval n := $(PROJECT)_sup)
$(call render_template,tpl_supervisor,src/$(PROJECT)_sup.erl)
@@ -366,7 +370,9 @@ ifneq ($(wildcard src/),)
endif
$(call render_template,bs_Makefile,Makefile)
$(verbose) mkdir src/
+ifdef LEGACY
$(call render_template,bs_appsrc_lib,src/$(PROJECT).app.src)
+endif
bootstrap-rel:
ifneq ($(wildcard relx.config),)
diff --git a/plugins/relx.mk b/plugins/relx.mk
index 063a475..0fecda0 100644
--- a/plugins/relx.mk
+++ b/plugins/relx.mk
@@ -5,12 +5,10 @@
# Configuration.
-RELX_CONFIG ?= $(CURDIR)/relx.config
-
RELX ?= $(CURDIR)/relx
-export RELX
+RELX_CONFIG ?= $(CURDIR)/relx.config
-RELX_URL ?= https://github.com/erlware/relx/releases/download/v2.0.0/relx
+RELX_URL ?= https://github.com/erlware/relx/releases/download/v3.5.0/relx
RELX_OPTS ?=
RELX_OUTPUT_DIR ?= _rel
diff --git a/test/Makefile b/test/Makefile
index 59063e6..ba612fa 100644
--- a/test/Makefile
+++ b/test/Makefile
@@ -1,72 +1,161 @@
+# Copyright (c) 2015, Loïc Hoguin <[email protected]>
# Copyright (c) 2014, Viktor Söderqvist <[email protected]>
# This file is part of erlang.mk and subject to the terms of the ISC License.
-# Tests for erlang.mk targets. If any test fails or if you run a target other
-# than 'all', you must probably do 'make clean' before you can test again.
+# Temporary application name, taken from rule name.
+
+APP = $(subst -,_,$@)
+APP_TO_CLEAN = $(subst -,_,$(patsubst clean-%,%,$@))
+
+# Erlang, quickly!
+
+ERL = erl +A0 -noinput -boot start_clean
+
+# Platform, condensed version.
+
+ifeq ($(shell uname -o),Msys)
+ PLATFORM = msys2
+else
+ PLATFORM = unix
+endif
+
+# OTP master, for downloading files for testing.
+
+OTP_MASTER = https://raw.githubusercontent.com/erlang/otp/master
# Verbosity.
+#
+# V=0: Show info messages only.
+# V=1: Show test commands.
+# V=2: Also show normal Erlang.mk output.
+# V=3: Also show verbose Erlang.mk output.
V ?= 0
-# Temporary files directory.
+# t: Verbosity control for tests.
+# v: Verbosity control for erlang.mk.
+# i: Command to display (or suppress) info messages.
-ERLANG_MK_TMP=$(CURDIR)/tmp
-export ERLANG_MK_TMP
-
-# t = Verbosity control for tests
-# v = Verbosity control for erlang.mk
-# i = Command to display (or suppress) info messages
ifeq ($V,0)
- # Show info messages only
t = @
v = V=0 >/dev/null 2>&1
- i = @echo
+ i = @echo $@:
else ifeq ($V,1)
- # Show test commands
t =
v = V=0 >/dev/null 2>&1
- i = @echo ==
+ i = @echo == $@:
else ifeq ($V,2)
- # Show briefly what erlang.mk is doing
t = @echo " TEST " $@;
v = V=0
- i = @echo ==
+ i = @echo == $@:
else
- # Show all commands with maximum verbosity
t =
v = V=1
- i = @echo ==
+ i = @echo == $@:
endif
-.PHONY: all clean app ct eunit tests-cover docs
+# Main targets.
-.NOTPARALLEL:
+.PHONY: all clean build
-all: clean app ct eunit tests-cover docs pkgs
- $i '+---------------------+'
- $i '| All tests passed. |'
- $i '+---------------------+'
+all:: core
-clean:
- $t rm -rf app1 pkgs.log $(ERLANG_MK_TMP)
+clean:: clean-core
+ $t rm -f erl_crash.dump
-app: app1
- $i "app: Testing the 'app' target."
- $t $(MAKE) -C app1 app $v
- $i "Checking the modules line in the generated .app file."
- $t [ `grep -E "{modules, *\['m'\]}" app1/ebin/app1.app | wc -l` -eq 1 ]
- $t [ -e app1/ebin/m.beam ]
- $i "Checking that '$(MAKE) clean-app' deletes ebin."
- $t $(MAKE) -C app1 clean-app $v
- $t [ ! -e app1/ebin ]
- $i "Checking that '$(MAKE) app' returns non-zero on compile errors."
- $t printf "%s\n" \
- "-module(syntax_error)." \
- "foo lorem_ipsum dolor sit amet." \
- > app1/src/syntax_error.erl
- $t if $(MAKE) -C app1 app $v ; then false ; fi
- $t rm app1/src/syntax_error.erl
- $i "Test 'app' passed."
+build:
+ $i "Generate a bleeding edge Erlang.mk"
+ $t cd .. && $(MAKE) $v
+
+# Core.
+
+.PHONY: core clean-core
+
+define include_core
+core:: core-$1
+clean-core:: clean-core-$1
+
+include core_$1.mk
+
+endef
+
+$(eval $(foreach t,$(patsubst %.mk,%,$(patsubst core_%,%,$(wildcard core_*.mk))),$(call include_core,$t)))
+
+# Plugins.
+
+define include_plugin
+all:: $1
+clean:: clean-$1
+
+include plugin_$1.mk
+
+endef
+
+$(eval $(foreach t,$(patsubst %.mk,%,$(patsubst plugin_%,%,$(wildcard plugin_*.mk))),$(call include_plugin,$t)))
+
+# Tests that don't easily fit into other categories.
+
+core:: core-clean-crash-dump core-distclean-tmp core-help
+clean-core:: clean-core-clean-crash-dump clean-core-distclean-tmp clean-core-help
+
+.PHONY: core-clean-crash-dump core-distclean-tmp core-help clean-core-clean-crash-dump clean-core-distclean-tmp clean-core-help
+
+clean-core-clean-crash-dump clean-core-distclean-tmp clean-core-help:
+ $t rm -rf $(APP_TO_CLEAN)/
+
+core-clean-crash-dump: build clean-core-clean-crash-dump
+
+ $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 fake erl_crash.dump file"
+ $t touch $(APP)/erl_crash.dump
+
+ $i "Clean the application"
+ $t $(MAKE) -C $(APP) clean $v
+
+ $i "Check that the crash dump is removed"
+ $t test ! -e $(APP)/erl_crash.dump
+
+core-distclean-tmp: build clean-core-distclean-tmp
+
+ $i "Bootstrap a new OTP application named $(APP)"
+ $t mkdir $(APP)/
+ $t cp ../erlang.mk $(APP)/
+ $t $(MAKE) -C $(APP) -f erlang.mk bootstrap all $v
+
+ $i "Check that a .erlang.mk directory exists"
+ $t test -d $(APP)/.erlang.mk
+
+ $i "Distclean the application"
+ $t $(MAKE) -C $(APP) distclean $v
+
+ $i "Check if .erlang.mk directory got removed"
+ $t test ! -e $(APP)/.erlang.mk
+
+core-help: build clean-core-help
+
+ $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 "Run 'make help' and check that it prints help"
+ $t test -n "`$(MAKE) -C $(APP) help` | grep Usage"
+
+# Legacy tests.
+#
+# The following tests are slowly being converted.
+# Do NOT use -j with legacy tests.
+
+.PHONY: legacy clean-legacy ct eunit tests-cover docs
+
+legacy: clean-legacy ct eunit tests-cover docs pkgs
+
+clean-legacy:
+ $t rm -rf app1 pkgs.log
ct: app1
$i "ct: Testing ct and related targets."
diff --git a/test/core_app.mk b/test/core_app.mk
new file mode 100644
index 0000000..55bf7b2
--- /dev/null
+++ b/test/core_app.mk
@@ -0,0 +1,1398 @@
+# Core: Building applications.
+
+CORE_APP_CASES = asn1 auto-git-id erlc-exclude erlc-opts erlc-opts-filter error generate-erl generate-erl-include generate-erl-prepend hrl hrl-recursive mib no-app no-makedep xrl xrl-include yrl yrl-include
+CORE_APP_TARGETS = $(addprefix core-app-,$(CORE_APP_CASES))
+CORE_APP_CLEAN_TARGETS = $(addprefix clean-,$(CORE_APP_TARGETS))
+
+.PHONY: core-app $(CORE_APP_TARGETS) clean-core-app $(CORE_APP_CLEAN_TARGETS)
+
+clean-core-app: $(CORE_APP_CLEAN_TARGETS)
+
+$(CORE_APP_CLEAN_TARGETS):
+ $t rm -rf $(APP_TO_CLEAN)/
+
+core-app: $(CORE_APP_TARGETS)
+
+core-app-asn1: build clean-core-app-asn1
+
+ $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 "Download .asn1 files from Erlang/OTP"
+ $t mkdir $(APP)/asn1/
+ $t curl -s -o $(APP)/asn1/CAP.asn1 $(OTP_MASTER)/lib/asn1/test/asn1_SUITE_data/CAP.asn1
+ $t curl -s -o $(APP)/asn1/Def.asn1 $(OTP_MASTER)/lib/asn1/test/asn1_SUITE_data/Def.asn1
+
+ $i "Generate .erl files dependent from headers generated by .asn1 files"
+ $t printf "%s\n" "-module(use_cap)." "-include(\"CAP.hrl\")." > $(APP)/src/use_cap.erl
+ $t printf "%s\n" "-module(use_def)." "-include(\"Def.hrl\")." > $(APP)/src/use_def.erl
+
+ $i "Generate an unrelated .hrl file"
+ $t mkdir $(APP)/include/
+ $t touch $(APP)/include/unrelated.hrl
+
+ $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)/ebin/CAP.beam
+ $t test -f $(APP)/ebin/Def.beam
+ $t test -f $(APP)/ebin/use_cap.beam
+ $t test -f $(APP)/ebin/use_def.beam
+ $t test -f $(APP)/include/CAP.asn1db
+ $t test -f $(APP)/include/CAP.hrl
+ $t test -f $(APP)/include/Def.asn1db
+ $t test -f $(APP)/include/Def.hrl
+ $t test -f $(APP)/src/CAP.erl
+ $t test -f $(APP)/src/Def.erl
+
+ $i "Check that the application was compiled correctly"
+ $t $(ERL) -pa $(APP)/ebin/ -eval " \
+ ok = application:start($(APP)), \
+ {ok, Mods = ['CAP', 'Def', use_cap, use_def]} \
+ = application:get_key($(APP), modules), \
+ [{module, M} = code:load_file(M) || M <- Mods], \
+ halt()"
+
+ $i "Touch one .asn1 file; check that only required files are rebuilt"
+# The use_cap.erl gets touched because of its dependency to CAP.hrl.
+ $t printf "%s\n" \
+ $(APP)/$(APP).d \
+ $(APP)/ebin/$(APP).app \
+ $(APP)/ebin/CAP.beam \
+ $(APP)/ebin/use_cap.beam \
+ $(APP)/include/CAP.asn1db \
+ $(APP)/include/CAP.hrl \
+ $(APP)/src/CAP.erl \
+ $(APP)/src/use_cap.erl | sort > $(APP)/EXPECT
+ $t touch $(APP)/asn1/CAP.asn1
+ $t $(MAKE) -C $(APP) $v
+ $t find $(APP) -type f -newer $(APP)/asn1/CAP.asn1 | sort | diff $(APP)/EXPECT -
+ $t rm $(APP)/EXPECT
+
+ $i "Check that the application was compiled correctly"
+ $t $(ERL) -pa $(APP)/ebin/ -eval " \
+ ok = application:start($(APP)), \
+ {ok, Mods = ['CAP', 'Def', use_cap, use_def]} \
+ = application:get_key($(APP), modules), \
+ [{module, M} = code:load_file(M) || M <- Mods], \
+ halt()"
+
+ $i "Clean the application"
+ $t $(MAKE) -C $(APP) clean $v
+
+ $i "Check that source files still exist"
+ $t test -f $(APP)/Makefile
+ $t test -f $(APP)/erlang.mk
+ $t test -f $(APP)/asn1/CAP.asn1
+ $t test -f $(APP)/asn1/Def.asn1
+ $t test -f $(APP)/include/unrelated.hrl
+ifdef LEGACY
+ $t test -f $(APP)/src/$(APP).app.src
+endif
+ $t test -f $(APP)/src/use_cap.erl
+ $t test -f $(APP)/src/use_def.erl
+
+ $i "Check that all build artifacts are removed, including intermediates"
+ $t test ! -e $(APP)/$(APP).d
+ $t test ! -e $(APP)/ebin/
+ $t test ! -e $(APP)/include/CAP.asn1db
+ $t test ! -e $(APP)/include/CAP.hrl
+ $t test ! -e $(APP)/include/Def.asn1db
+ $t test ! -e $(APP)/include/Def.hrl
+ $t test ! -e $(APP)/src/CAP.erl
+ $t test ! -e $(APP)/src/Def.erl
+
+ $i "Build the application again"
+ $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)/ebin/CAP.beam
+ $t test -f $(APP)/ebin/Def.beam
+ $t test -f $(APP)/ebin/use_cap.beam
+ $t test -f $(APP)/ebin/use_def.beam
+ $t test -f $(APP)/include/CAP.asn1db
+ $t test -f $(APP)/include/CAP.hrl
+ $t test -f $(APP)/include/Def.asn1db
+ $t test -f $(APP)/include/Def.hrl
+ $t test -f $(APP)/src/CAP.erl
+ $t test -f $(APP)/src/Def.erl
+
+ $i "Check that the application was compiled correctly"
+ $t $(ERL) -pa $(APP)/ebin/ -eval " \
+ ok = application:start($(APP)), \
+ {ok, Mods = ['CAP', 'Def', use_cap, use_def]} \
+ = application:get_key($(APP), modules), \
+ [{module, M} = code:load_file(M) || M <- Mods], \
+ halt()"
+
+core-app-auto-git-id: build clean-core-app-auto-git-id
+
+ $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 "Make it a git repository"
+ $t cd $(APP) && \
+ git init -q && \
+ git config user.email "[email protected]" && \
+ git config user.name "test suite" && \
+ git add . && \
+ git commit -q -m "Tests"
+
+ $i "Build the application"
+ $t $(MAKE) -C $(APP) $v
+
+ifdef LEGACY
+# Legacy replaces {id, "git"} always regardless of built as a dependency.
+ $i "Check that the generated .app file has an id key"
+ $t $(ERL) -pa $(APP)/ebin/ -eval " \
+ ok = application:start($(APP)), \
+ {ok, ID} = application:get_key($(APP), id), \
+ true = ID =/= [], \
+ halt()"
+else
+# If there is no .app.src though, only fill in id when built as a dependency.
+ $i "Check that the generated .app file has no id key"
+ $t $(ERL) -pa $(APP)/ebin/ -eval " \
+ ok = application:start($(APP)), \
+ {ok, []} = application:get_key($(APP), id), \
+ halt()"
+endif
+
+ $i "Clean the application"
+ $t $(MAKE) -C $(APP) clean $v
+
+ $i "Build the application with IS_DEP=1"
+ $t $(MAKE) -C $(APP) IS_DEP=1 $v
+
+ $i "Check that the generated .app file has an id key"
+ $t $(ERL) -pa $(APP)/ebin/ -eval " \
+ ok = application:start($(APP)), \
+ {ok, ID} = application:get_key($(APP), id), \
+ true = ID =/= [], \
+ halt()"
+
+core-app-erlc-exclude: build clean-core-app-erlc-exclude
+
+ $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 "Generate .erl files"
+ $t echo "-module(boy)." > $(APP)/src/boy.erl
+ $t echo "-module(girl)." > $(APP)/src/girl.erl
+
+ $i "Exclude boy.erl from the compilation"
+ $t echo "ERLC_EXCLUDE = boy" >> $(APP)/Makefile
+
+ $i "Build the application"
+ $t $(MAKE) -C $(APP) $v
+
+ $i "Check that boy.erl was not compiled"
+ $t test ! -e $(APP)/ebin/boy.beam
+
+ $i "Check that the application was compiled correctly (without boy.erl)"
+ $t $(ERL) -pa $(APP)/ebin/ -eval " \
+ ok = application:start($(APP)), \
+ {ok, Mods = [girl]} \
+ = application:get_key($(APP), modules), \
+ [{module, M} = code:load_file(M) || M <- Mods], \
+ halt()"
+
+core-app-erlc-opts: build clean-core-app-erlc-opts
+
+ $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 "Define an empty ERLC_OPTS (without debug_info)"
+ $t echo "ERLC_OPTS =" >> $(APP)/Makefile
+
+ $i "Generate .erl files"
+ $t echo "-module(boy)." > $(APP)/src/boy.erl
+ $t echo "-module(girl)." > $(APP)/src/girl.erl
+
+ $i "Build the application"
+ $t $(MAKE) -C $(APP) $v
+
+ $i "Check that the application was compiled correctly (without debug_info)"
+ $t $(ERL) -pa $(APP)/ebin/ -eval " \
+ ok = application:start($(APP)), \
+ {ok, Mods = [boy, girl]} \
+ = application:get_key($(APP), modules), \
+ [{module, M} = code:load_file(M) || M <- Mods], \
+ false = proplists:is_defined(debug_info, proplists:get_value(options, boy:module_info(compile))), \
+ false = proplists:is_defined(debug_info, proplists:get_value(options, girl:module_info(compile))), \
+ halt()"
+
+core-app-erlc-opts-filter: build clean-core-app-erlc-opts-filter
+
+ $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 "Define ERLC_OPTS filtering out debug_info"
+ $t echo "ERLC_OPTS := \$$(filter-out +debug_info,\$$(ERLC_OPTS))" >> $(APP)/Makefile
+
+ $i "Generate .erl files"
+ $t echo "-module(boy)." > $(APP)/src/boy.erl
+ $t echo "-module(girl)." > $(APP)/src/girl.erl
+
+ $i "Build the application"
+ $t $(MAKE) -C $(APP) $v
+
+ $i "Check that the application was compiled correctly (without debug_info)"
+ $t $(ERL) -pa $(APP)/ebin/ -eval " \
+ ok = application:start($(APP)), \
+ {ok, Mods = [boy, girl]} \
+ = application:get_key($(APP), modules), \
+ [{module, M} = code:load_file(M) || M <- Mods], \
+ false = proplists:is_defined(debug_info, proplists:get_value(options, boy:module_info(compile))), \
+ false = proplists:is_defined(debug_info, proplists:get_value(options, girl:module_info(compile))), \
+ halt()"
+
+core-app-error: build clean-core-app-error
+
+ $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 "Generate a bad .erl files"
+ $t touch $(APP)/src/breaking.erl
+
+ $i "Generate unrelated .erl files"
+ $t echo "-module(boy)." > $(APP)/src/boy.erl
+ $t echo "-module(girl)." > $(APP)/src/girl.erl
+
+ $i "Check that trying to build returns non-zero"
+ $t if $(MAKE) -C $(APP) $v; then false; fi
+
+core-app-generate-erl: build clean-core-app-generate-erl
+
+ $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 fake script file to be used as dependency"
+ $t touch $(APP)/script.sh
+
+ $i "Append rules to the Makefile to generate a .erl module"
+ $t echo "\$$(PROJECT).d:: src/generated.erl" >> $(APP)/Makefile
+ $t echo "src/generated.erl:: script.sh; echo \"-module(generated).\" > \$$@" >> $(APP)/Makefile
+
+ $i "Generate unrelated .erl files"
+ $t echo "-module(boy)." > $(APP)/src/boy.erl
+ $t echo "-module(girl)." > $(APP)/src/girl.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)/ebin/boy.beam
+ $t test -f $(APP)/ebin/generated.beam
+ $t test -f $(APP)/ebin/girl.beam
+ $t test -f $(APP)/src/generated.erl
+
+ $i "Check that the application was compiled correctly"
+ $t $(ERL) -pa $(APP)/ebin/ -eval " \
+ ok = application:start($(APP)), \
+ {ok, Mods = [boy, generated, girl]} \
+ = application:get_key($(APP), modules), \
+ [{module, M} = code:load_file(M) || M <- Mods], \
+ halt()"
+
+ $i "Touch the script file; check that only required files are rebuilt"
+ $t printf "%s\n" \
+ $(APP)/$(APP).d \
+ $(APP)/ebin/$(APP).app \
+ $(APP)/ebin/generated.beam \
+ $(APP)/src/generated.erl | sort > $(APP)/EXPECT
+ $t touch $(APP)/script.sh
+ $t $(MAKE) -C $(APP) $v
+ $t find $(APP) -type f -newer $(APP)/script.sh | sort | diff $(APP)/EXPECT -
+ $t rm $(APP)/EXPECT
+
+ $i "Check that the application was compiled correctly"
+ $t $(ERL) -pa $(APP)/ebin/ -eval " \
+ ok = application:start($(APP)), \
+ {ok, Mods = [boy, generated, girl]} \
+ = application:get_key($(APP), modules), \
+ [{module, M} = code:load_file(M) || M <- Mods], \
+ halt()"
+
+ $i "Clean the application"
+ $t $(MAKE) -C $(APP) clean $v
+
+ $i "Check that source files still exist"
+ $t test -f $(APP)/Makefile
+ $t test -f $(APP)/erlang.mk
+ $t test -f $(APP)/script.sh
+ifdef LEGACY
+ $t test -f $(APP)/src/$(APP).app.src
+endif
+ $t test -f $(APP)/src/boy.erl
+ $t test -f $(APP)/src/girl.erl
+
+ $i "Check that the generated .erl file still exists"
+ $t test -f $(APP)/src/generated.erl
+
+ $i "Check that all build artifacts are removed"
+ $t test ! -e $(APP)/$(APP).d
+ $t test ! -e $(APP)/ebin/
+
+ $i "Add a rule to remove the generated .erl file on clean"
+ $t echo "clean:: ; rm src/generated.erl" >> $(APP)/Makefile
+
+ $i "Clean the application again"
+ $t $(MAKE) -C $(APP) clean $v
+
+ $i "Check that the generated .erl file was removed"
+ $t test ! -e $(APP)/src/generated.erl
+
+ $i "Build the application again"
+ $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)/ebin/boy.beam
+ $t test -f $(APP)/ebin/generated.beam
+ $t test -f $(APP)/ebin/girl.beam
+ $t test -f $(APP)/src/generated.erl
+
+ $i "Check that the application was compiled correctly"
+ $t $(ERL) -pa $(APP)/ebin/ -eval " \
+ ok = application:start($(APP)), \
+ {ok, Mods = [boy, generated, girl]} \
+ = application:get_key($(APP), modules), \
+ [{module, M} = code:load_file(M) || M <- Mods], \
+ halt()"
+
+core-app-generate-erl-include: build clean-core-app-generate-erl-include
+
+ $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 fake script file to be used as dependency"
+ $t touch $(APP)/script.sh
+
+ $i "Append rules to the Makefile to generate a .erl module"
+ $t echo "\$$(PROJECT).d:: src/generated.erl" >> $(APP)/Makefile
+ $t echo "src/generated.erl:: script.sh; echo \"-module(generated).\" > \$$@; echo \"-include(\\\"included.hrl\\\").\" >> \$$@" >> $(APP)/Makefile
+
+ $i "Generate the .hrl file"
+ $t mkdir $(APP)/include/
+ $t touch $(APP)/include/included.hrl
+
+ $i "Generate unrelated .erl files"
+ $t echo "-module(boy)." > $(APP)/src/boy.erl
+ $t echo "-module(girl)." > $(APP)/src/girl.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)/ebin/boy.beam
+ $t test -f $(APP)/ebin/generated.beam
+ $t test -f $(APP)/ebin/girl.beam
+ $t test -f $(APP)/src/generated.erl
+
+ $i "Check that the application was compiled correctly"
+ $t $(ERL) -pa $(APP)/ebin/ -eval " \
+ ok = application:start($(APP)), \
+ {ok, Mods = [boy, generated, girl]} \
+ = application:get_key($(APP), modules), \
+ [{module, M} = code:load_file(M) || M <- Mods], \
+ halt()"
+
+ $i "Touch the .hrl file; check that only required files are rebuilt"
+ $t printf "%s\n" \
+ $(APP)/$(APP).d \
+ $(APP)/ebin/$(APP).app \
+ $(APP)/ebin/generated.beam \
+ $(APP)/src/generated.erl | sort > $(APP)/EXPECT
+ $t touch $(APP)/include/included.hrl
+ $t $(MAKE) -C $(APP) $v
+ $t find $(APP) -type f -newer $(APP)/include/included.hrl | sort | diff $(APP)/EXPECT -
+ $t rm $(APP)/EXPECT
+
+ $i "Check that the application was compiled correctly"
+ $t $(ERL) -pa $(APP)/ebin/ -eval " \
+ ok = application:start($(APP)), \
+ {ok, Mods = [boy, generated, girl]} \
+ = application:get_key($(APP), modules), \
+ [{module, M} = code:load_file(M) || M <- Mods], \
+ halt()"
+
+core-app-generate-erl-prepend: build clean-core-app-generate-erl-prepend
+
+ $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 fake script file to be used as dependency"
+ $t touch $(APP)/script.sh
+
+ $i "Generate a Makefile and prepend rules that generate a .erl module"
+ $t echo "PROJECT = $(APP)" > $(APP)/Makefile
+ $t echo ".DEFAULT_GOAL = all" >> $(APP)/Makefile
+ $t echo "\$$(PROJECT).d:: src/generated.erl" >> $(APP)/Makefile
+ $t echo "src/generated.erl:: script.sh; echo \"-module(generated).\" > \$$@" >> $(APP)/Makefile
+ $t echo "include erlang.mk" >> $(APP)/Makefile
+
+ $i "Generate unrelated .erl files"
+ $t echo "-module(boy)." > $(APP)/src/boy.erl
+ $t echo "-module(girl)." > $(APP)/src/girl.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)/ebin/boy.beam
+ $t test -f $(APP)/ebin/generated.beam
+ $t test -f $(APP)/ebin/girl.beam
+ $t test -f $(APP)/src/generated.erl
+
+ $i "Check that the application was compiled correctly"
+ $t $(ERL) -pa $(APP)/ebin/ -eval " \
+ ok = application:start($(APP)), \
+ {ok, Mods = [boy, generated, girl]} \
+ = application:get_key($(APP), modules), \
+ [{module, M} = code:load_file(M) || M <- Mods], \
+ halt()"
+
+ $i "Touch the script file; check that only required files are rebuilt"
+ $t printf "%s\n" \
+ $(APP)/$(APP).d \
+ $(APP)/ebin/$(APP).app \
+ $(APP)/ebin/generated.beam \
+ $(APP)/src/generated.erl | sort > $(APP)/EXPECT
+ $t touch $(APP)/script.sh
+ $t $(MAKE) -C $(APP) $v
+ $t find $(APP) -type f -newer $(APP)/script.sh | sort | diff $(APP)/EXPECT -
+ $t rm $(APP)/EXPECT
+
+ $i "Check that the application was compiled correctly"
+ $t $(ERL) -pa $(APP)/ebin/ -eval " \
+ ok = application:start($(APP)), \
+ {ok, Mods = [boy, generated, girl]} \
+ = application:get_key($(APP), modules), \
+ [{module, M} = code:load_file(M) || M <- Mods], \
+ halt()"
+
+ $i "Clean the application"
+ $t $(MAKE) -C $(APP) clean $v
+
+ $i "Check that source files still exist"
+ $t test -f $(APP)/Makefile
+ $t test -f $(APP)/erlang.mk
+ $t test -f $(APP)/script.sh
+ifdef LEGACY
+ $t test -f $(APP)/src/$(APP).app.src
+endif
+ $t test -f $(APP)/src/boy.erl
+ $t test -f $(APP)/src/girl.erl
+
+ $i "Check that the generated .erl file still exists"
+ $t test -f $(APP)/src/generated.erl
+
+ $i "Check that all build artifacts are removed"
+ $t test ! -e $(APP)/$(APP).d
+ $t test ! -e $(APP)/ebin/
+
+ $i "Add a rule to remove the generated .erl file on clean"
+ $t echo "clean:: ; rm src/generated.erl" >> $(APP)/Makefile
+
+ $i "Clean the application again"
+ $t $(MAKE) -C $(APP) clean $v
+
+ $i "Check that the generated .erl file was removed"
+ $t test ! -e $(APP)/src/generated.erl
+
+ $i "Build the application again"
+ $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)/ebin/boy.beam
+ $t test -f $(APP)/ebin/generated.beam
+ $t test -f $(APP)/ebin/girl.beam
+ $t test -f $(APP)/src/generated.erl
+
+ $i "Check that the application was compiled correctly"
+ $t $(ERL) -pa $(APP)/ebin/ -eval " \
+ ok = application:start($(APP)), \
+ {ok, Mods = [boy, generated, girl]} \
+ = application:get_key($(APP), modules), \
+ [{module, M} = code:load_file(M) || M <- Mods], \
+ halt()"
+
+core-app-hrl: build clean-core-app-hrl
+
+ $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 "Generate .hrl files"
+ $t mkdir $(APP)/include/
+ $t touch $(APP)/include/blue.hrl $(APP)/include/red.hrl
+
+ $i "Generate .erl files dependent from headers"
+ $t printf "%s\n" "-module(use_blue)." "-include(\"blue.hrl\")." > $(APP)/src/use_blue.erl
+ $t printf "%s\n" "-module(use_red)." "-include(\"red.hrl\")." > $(APP)/src/use_red.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)/ebin/use_blue.beam
+ $t test -f $(APP)/ebin/use_red.beam
+
+ $i "Check that the application was compiled correctly"
+ $t $(ERL) -pa $(APP)/ebin/ -eval " \
+ ok = application:start($(APP)), \
+ {ok, Mods = [use_blue, use_red]} \
+ = application:get_key($(APP), modules), \
+ [{module, M} = code:load_file(M) || M <- Mods], \
+ halt()"
+
+ $i "Touch one .hrl file; check that only required files are rebuilt"
+# The use_red.erl gets touched because of its dependency to red.hrl.
+ $t printf "%s\n" \
+ $(APP)/$(APP).d \
+ $(APP)/ebin/$(APP).app \
+ $(APP)/ebin/use_red.beam \
+ $(APP)/src/use_red.erl | sort > $(APP)/EXPECT
+ $t touch $(APP)/include/red.hrl
+ $t $(MAKE) -C $(APP) $v
+ $t find $(APP) -type f -newer $(APP)/include/red.hrl | sort | diff $(APP)/EXPECT -
+ $t rm $(APP)/EXPECT
+
+ $i "Check that the application was compiled correctly"
+ $t $(ERL) -pa $(APP)/ebin/ -eval " \
+ ok = application:start($(APP)), \
+ {ok, Mods = [use_blue, use_red]} \
+ = application:get_key($(APP), modules), \
+ [{module, M} = code:load_file(M) || M <- Mods], \
+ halt()"
+
+ $i "Clean the application"
+ $t $(MAKE) -C $(APP) clean $v
+
+ $i "Check that source files still exist"
+ $t test -f $(APP)/Makefile
+ $t test -f $(APP)/erlang.mk
+ $t test -f $(APP)/include/blue.hrl
+ $t test -f $(APP)/include/red.hrl
+ifdef LEGACY
+ $t test -f $(APP)/src/$(APP).app.src
+endif
+ $t test -f $(APP)/src/use_blue.erl
+ $t test -f $(APP)/src/use_red.erl
+
+ $i "Check that all build artifacts are removed"
+ $t test ! -e $(APP)/$(APP).d
+ $t test ! -e $(APP)/ebin/
+
+ $i "Build the application again"
+ $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)/ebin/use_blue.beam
+ $t test -f $(APP)/ebin/use_red.beam
+
+ $i "Check that the application was compiled correctly"
+ $t $(ERL) -pa $(APP)/ebin/ -eval " \
+ ok = application:start($(APP)), \
+ {ok, Mods = [use_blue, use_red]} \
+ = application:get_key($(APP), modules), \
+ [{module, M} = code:load_file(M) || M <- Mods], \
+ halt()"
+
+core-app-hrl-recursive: build clean-core-app-hrl-recursive
+
+ $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 "Generate .hrl files"
+ $t mkdir $(APP)/include/
+ $t touch $(APP)/include/blue.hrl $(APP)/include/pill.hrl
+ $t echo "-include(\"pill.hrl\")." > $(APP)/include/red.hrl
+
+ $i "Generate .erl files dependent from headers"
+ $t printf "%s\n" "-module(use_blue)." "-include(\"blue.hrl\")." > $(APP)/src/use_blue.erl
+ $t printf "%s\n" "-module(use_red)." "-include(\"red.hrl\")." > $(APP)/src/use_red.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)/ebin/use_blue.beam
+ $t test -f $(APP)/ebin/use_red.beam
+
+ $i "Check that the application was compiled correctly"
+ $t $(ERL) -pa $(APP)/ebin/ -eval " \
+ ok = application:start($(APP)), \
+ {ok, Mods = [use_blue, use_red]} \
+ = application:get_key($(APP), modules), \
+ [{module, M} = code:load_file(M) || M <- Mods], \
+ halt()"
+
+ $i "Touch the deepest .hrl file; check that only required files are rebuilt"
+# The use_red.erl gets touched because of its dependency to red.hrl and pill.hrl.
+ $t printf "%s\n" \
+ $(APP)/$(APP).d \
+ $(APP)/ebin/$(APP).app \
+ $(APP)/ebin/use_red.beam \
+ $(APP)/src/use_red.erl | sort > $(APP)/EXPECT
+ $t touch $(APP)/include/pill.hrl
+ $t $(MAKE) -C $(APP) $v
+ $t find $(APP) -type f -newer $(APP)/include/pill.hrl | sort | diff $(APP)/EXPECT -
+ $t rm $(APP)/EXPECT
+
+ $i "Check that the application was compiled correctly"
+ $t $(ERL) -pa $(APP)/ebin/ -eval " \
+ ok = application:start($(APP)), \
+ {ok, Mods = [use_blue, use_red]} \
+ = application:get_key($(APP), modules), \
+ [{module, M} = code:load_file(M) || M <- Mods], \
+ halt()"
+
+ $i "Clean the application"
+ $t $(MAKE) -C $(APP) clean $v
+
+ $i "Check that source files still exist"
+ $t test -f $(APP)/Makefile
+ $t test -f $(APP)/erlang.mk
+ $t test -f $(APP)/include/blue.hrl
+ $t test -f $(APP)/include/pill.hrl
+ $t test -f $(APP)/include/red.hrl
+ifdef LEGACY
+ $t test -f $(APP)/src/$(APP).app.src
+endif
+ $t test -f $(APP)/src/use_blue.erl
+ $t test -f $(APP)/src/use_red.erl
+
+ $i "Check that all build artifacts are removed"
+ $t test ! -e $(APP)/$(APP).d
+ $t test ! -e $(APP)/ebin/
+
+ $i "Build the application again"
+ $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)/ebin/use_blue.beam
+ $t test -f $(APP)/ebin/use_red.beam
+
+ $i "Check that the application was compiled correctly"
+ $t $(ERL) -pa $(APP)/ebin/ -eval " \
+ ok = application:start($(APP)), \
+ {ok, Mods = [use_blue, use_red]} \
+ = application:get_key($(APP), modules), \
+ [{module, M} = code:load_file(M) || M <- Mods], \
+ halt()"
+
+core-app-mib: build clean-core-app-mib
+
+ $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 "Download .mib files from Erlang/OTP"
+ $t mkdir $(APP)/mibs/
+ $t curl -s -o $(APP)/mibs/EX1-MIB.mib $(OTP_MASTER)/lib/snmp/examples/ex1/EX1-MIB.mib
+ $t curl -s -o $(APP)/mibs/OTP-REG.mib $(OTP_MASTER)/lib/otp_mibs/mibs/OTP-REG.mib
+
+ $i "Generate .erl files dependent from headers generated by .mib files"
+ $t printf "%s\n" "-module(use_v1)." "-include(\"EX1-MIB.hrl\")." > $(APP)/src/use_v1.erl
+ $t printf "%s\n" "-module(use_v2)." "-include(\"OTP-REG.hrl\")." > $(APP)/src/use_v2.erl
+
+ $i "Generate an unrelated .hrl file"
+ $t mkdir $(APP)/include/
+ $t touch $(APP)/include/unrelated.hrl
+
+ $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)/ebin/use_v1.beam
+ $t test -f $(APP)/ebin/use_v2.beam
+ $t test -f $(APP)/include/EX1-MIB.hrl
+ $t test -f $(APP)/include/OTP-REG.hrl
+ $t test -f $(APP)/priv/mibs/EX1-MIB.bin
+ $t test -f $(APP)/priv/mibs/OTP-REG.bin
+
+ $i "Check that the application was compiled correctly"
+ $t $(ERL) -pa $(APP)/ebin/ -eval " \
+ ok = application:start($(APP)), \
+ {ok, Mods = [use_v1, use_v2]} \
+ = application:get_key($(APP), modules), \
+ [{module, M} = code:load_file(M) || M <- Mods], \
+ halt()"
+
+ $i "Touch one .mib file; check that only required files are rebuilt"
+# The use_v1.erl gets touched because of its dependency to EX1-MIB.hrl.
+ $t printf "%s\n" \
+ $(APP)/$(APP).d \
+ $(APP)/ebin/$(APP).app \
+ $(APP)/ebin/use_v1.beam \
+ $(APP)/include/EX1-MIB.hrl \
+ $(APP)/priv/mibs/EX1-MIB.bin \
+ $(APP)/src/use_v1.erl | sort > $(APP)/EXPECT
+ $t touch $(APP)/mibs/EX1-MIB.mib
+ $t $(MAKE) -C $(APP) $v
+ $t find $(APP) -type f -newer $(APP)/mibs/EX1-MIB.mib | sort | diff $(APP)/EXPECT -
+ $t rm $(APP)/EXPECT
+
+ $i "Check that the application was compiled correctly"
+ $t $(ERL) -pa $(APP)/ebin/ -eval " \
+ ok = application:start($(APP)), \
+ {ok, Mods = [use_v1, use_v2]} \
+ = application:get_key($(APP), modules), \
+ [{module, M} = code:load_file(M) || M <- Mods], \
+ halt()"
+
+ $i "Clean the application"
+ $t $(MAKE) -C $(APP) clean $v
+
+ $i "Check that source files still exist"
+ $t test -f $(APP)/Makefile
+ $t test -f $(APP)/erlang.mk
+ $t test -f $(APP)/include/unrelated.hrl
+ $t test -f $(APP)/mibs/EX1-MIB.mib
+ $t test -f $(APP)/mibs/OTP-REG.mib
+ifdef LEGACY
+ $t test -f $(APP)/src/$(APP).app.src
+endif
+ $t test -f $(APP)/src/use_v1.erl
+ $t test -f $(APP)/src/use_v2.erl
+
+ $i "Check that all build artifacts are removed, including intermediates"
+ $t test ! -e $(APP)/$(APP).d
+ $t test ! -e $(APP)/ebin/
+ $t test ! -e $(APP)/include/EX1-MIB.hrl
+ $t test ! -e $(APP)/include/OTP-REG.hrl
+ $t test ! -e $(APP)/priv/mibs/
+
+ $i "Build the application again"
+ $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)/ebin/use_v1.beam
+ $t test -f $(APP)/ebin/use_v2.beam
+ $t test -f $(APP)/include/EX1-MIB.hrl
+ $t test -f $(APP)/include/OTP-REG.hrl
+ $t test -f $(APP)/priv/mibs/EX1-MIB.bin
+ $t test -f $(APP)/priv/mibs/OTP-REG.bin
+
+ $i "Check that the application was compiled correctly"
+ $t $(ERL) -pa $(APP)/ebin/ -eval " \
+ ok = application:start($(APP)), \
+ {ok, Mods = [use_v1, use_v2]} \
+ = application:get_key($(APP), modules), \
+ [{module, M} = code:load_file(M) || M <- Mods], \
+ halt()"
+
+core-app-no-app: build clean-core-app-no-app
+
+ $i "Bootstrap a project without an OTP library"
+ $t mkdir $(APP)/
+ $t cp ../erlang.mk $(APP)/
+ $t $(MAKE) -C $(APP) -f erlang.mk bootstrap-lib $v
+ $t rm -rf $(APP)/src
+
+ $i "Build the project"
+ $t $(MAKE) -C $(APP) $v
+
+core-app-no-makedep: build clean-core-app-no-makedep
+
+ $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 "Set NO_MAKEDEP ?= 1 in the Makefile"
+ $t sed -i.bak '2i\
+NO_MAKEDEP ?= 1\
+' $(APP)/Makefile
+
+ $i "Generate .hrl files"
+ $t mkdir $(APP)/include/
+ $t touch $(APP)/include/blue.hrl $(APP)/include/red.hrl
+
+ $i "Generate .erl files dependent from headers"
+ $t printf "%s\n" "-module(use_blue)." "-include(\"blue.hrl\")." > $(APP)/src/use_blue.erl
+ $t printf "%s\n" "-module(use_red)." "-include(\"red.hrl\")." > $(APP)/src/use_red.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)/ebin/use_blue.beam
+ $t test -f $(APP)/ebin/use_red.beam
+
+ $i "Check that the application was compiled correctly"
+ $t $(ERL) -pa $(APP)/ebin/ -eval " \
+ ok = application:start($(APP)), \
+ {ok, Mods = [use_blue, use_red]} \
+ = application:get_key($(APP), modules), \
+ [{module, M} = code:load_file(M) || M <- Mods], \
+ halt()"
+
+ $i "Touch one .hrl file; check that only required files are rebuilt"
+# The use_red.erl gets touched because of its dependency to red.hrl.
+ $t printf "%s\n" \
+ $(APP)/ebin/$(APP).app \
+ $(APP)/ebin/use_red.beam \
+ $(APP)/src/use_red.erl | sort > $(APP)/EXPECT
+ $t touch $(APP)/include/red.hrl
+ $t $(MAKE) -C $(APP) $v
+ $t find $(APP) -type f -newer $(APP)/include/red.hrl | sort | diff $(APP)/EXPECT -
+ $t rm $(APP)/EXPECT
+
+ $i "Check that the application was compiled correctly"
+ $t $(ERL) -pa $(APP)/ebin/ -eval " \
+ ok = application:start($(APP)), \
+ {ok, Mods = [use_blue, use_red]} \
+ = application:get_key($(APP), modules), \
+ [{module, M} = code:load_file(M) || M <- Mods], \
+ halt()"
+
+ $i "Touch one .hrl file; disable NO_MAKEDEP and check that only required files are rebuilt"
+# The use_red.erl gets touched because of its dependency to red.hrl.
+ $t printf "%s\n" \
+ $(APP)/$(APP).d \
+ $(APP)/ebin/$(APP).app \
+ $(APP)/ebin/use_red.beam \
+ $(APP)/src/use_red.erl | sort > $(APP)/EXPECT
+ $t touch $(APP)/include/red.hrl
+ $t NO_MAKEDEP= $(MAKE) -C $(APP) $v
+ $t find $(APP) -type f -newer $(APP)/include/red.hrl | sort | diff $(APP)/EXPECT -
+ $t rm $(APP)/EXPECT
+
+ $i "Check that the application was compiled correctly"
+ $t $(ERL) -pa $(APP)/ebin/ -eval " \
+ ok = application:start($(APP)), \
+ {ok, Mods = [use_blue, use_red]} \
+ = application:get_key($(APP), modules), \
+ [{module, M} = code:load_file(M) || M <- Mods], \
+ halt()"
+
+ $i "Clean the application"
+ $t $(MAKE) -C $(APP) clean $v
+
+ $i "Check that source files still exist"
+ $t test -f $(APP)/Makefile
+ $t test -f $(APP)/erlang.mk
+ $t test -f $(APP)/include/blue.hrl
+ $t test -f $(APP)/include/red.hrl
+ifdef LEGACY
+ $t test -f $(APP)/src/$(APP).app.src
+endif
+ $t test -f $(APP)/src/use_blue.erl
+ $t test -f $(APP)/src/use_red.erl
+
+ $i "Check that all build artifacts are removed"
+ $t test ! -e $(APP)/$(APP).d
+ $t test ! -e $(APP)/ebin/
+
+ $i "Build the application again"
+ $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)/ebin/use_blue.beam
+ $t test -f $(APP)/ebin/use_red.beam
+
+ $i "Check that the application was compiled correctly"
+ $t $(ERL) -pa $(APP)/ebin/ -eval " \
+ ok = application:start($(APP)), \
+ {ok, Mods = [use_blue, use_red]} \
+ = application:get_key($(APP), modules), \
+ [{module, M} = code:load_file(M) || M <- Mods], \
+ halt()"
+
+core-app-xrl: build clean-core-app-xrl
+
+ $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 "Download .xrl files from Robert"
+ $t curl -s -o $(APP)/src/erlang_scan.xrl https://raw.githubusercontent.com/rvirding/leex/master/examples/erlang_scan.xrl
+ $t curl -s -o $(APP)/src/lfe_scan.xrl https://raw.githubusercontent.com/rvirding/leex/master/examples/lfe_scan.xrl
+
+ $i "Generate unrelated .erl files"
+ $t echo "-module(boy)." > $(APP)/src/boy.erl
+ $t echo "-module(girl)." > $(APP)/src/girl.erl
+
+ $i "Disable warnings; our test .xrl files aren't perfect"
+ $t echo "ERLC_OPTS=+debug_info" >> $(APP)/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)/ebin/boy.beam
+ $t test -f $(APP)/ebin/erlang_scan.beam
+ $t test -f $(APP)/ebin/girl.beam
+ $t test -f $(APP)/ebin/lfe_scan.beam
+ $t test -f $(APP)/src/erlang_scan.erl
+ $t test -f $(APP)/src/lfe_scan.erl
+
+ $i "Check that the application was compiled correctly"
+ $t $(ERL) -pa $(APP)/ebin/ -eval " \
+ ok = application:start($(APP)), \
+ {ok, Mods = [boy, erlang_scan, girl, lfe_scan]} \
+ = application:get_key($(APP), modules), \
+ [{module, M} = code:load_file(M) || M <- Mods], \
+ halt()"
+
+ $i "Touch one .xrl file; check that only required files are rebuilt"
+ $t printf "%s\n" \
+ $(APP)/$(APP).d \
+ $(APP)/ebin/$(APP).app \
+ $(APP)/ebin/erlang_scan.beam \
+ $(APP)/src/erlang_scan.erl | sort > $(APP)/EXPECT
+ $t touch $(APP)/src/erlang_scan.xrl
+ $t $(MAKE) -C $(APP) $v
+ $t find $(APP) -type f -newer $(APP)/src/erlang_scan.xrl | sort | diff $(APP)/EXPECT -
+ $t rm $(APP)/EXPECT
+
+ $i "Check that the application was compiled correctly"
+ $t $(ERL) -pa $(APP)/ebin/ -eval " \
+ ok = application:start($(APP)), \
+ {ok, Mods = [boy, erlang_scan, girl, lfe_scan]} \
+ = application:get_key($(APP), modules), \
+ [{module, M} = code:load_file(M) || M <- Mods], \
+ halt()"
+
+ $i "Clean the application"
+ $t $(MAKE) -C $(APP) clean $v
+
+ $i "Check that source files still exist"
+ $t test -f $(APP)/Makefile
+ $t test -f $(APP)/erlang.mk
+ifdef LEGACY
+ $t test -f $(APP)/src/$(APP).app.src
+endif
+ $t test -f $(APP)/src/boy.erl
+ $t test -f $(APP)/src/erlang_scan.xrl
+ $t test -f $(APP)/src/girl.erl
+ $t test -f $(APP)/src/lfe_scan.xrl
+
+ $i "Check that all build artifacts are removed, including intermediates"
+ $t test ! -e $(APP)/$(APP).d
+ $t test ! -e $(APP)/ebin/
+ $t test ! -e $(APP)/src/erlang_scan.erl
+ $t test ! -e $(APP)/src/lfe_scan.erl
+
+ $i "Build the application again"
+ $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)/ebin/boy.beam
+ $t test -f $(APP)/ebin/erlang_scan.beam
+ $t test -f $(APP)/ebin/girl.beam
+ $t test -f $(APP)/ebin/lfe_scan.beam
+ $t test -f $(APP)/src/erlang_scan.erl
+ $t test -f $(APP)/src/lfe_scan.erl
+
+ $i "Check that the application was compiled correctly"
+ $t $(ERL) -pa $(APP)/ebin/ -eval " \
+ ok = application:start($(APP)), \
+ {ok, Mods = [boy, erlang_scan, girl, lfe_scan]} \
+ = application:get_key($(APP), modules), \
+ [{module, M} = code:load_file(M) || M <- Mods], \
+ halt()"
+
+core-app-xrl-include: build clean-core-app-xrl-include
+
+ $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 "Download a .xrl file with numerous includes from Gordon"
+ $t curl -s -o $(APP)/src/xfl_lexer.xrl https://raw.githubusercontent.com/hypernumbers/hypernumbers/master/lib/formula_engine-1.0/priv/xfl_lexer.xrl
+ $t curl -s -o $(APP)/src/errvals.hrl https://raw.githubusercontent.com/hypernumbers/hypernumbers/master/lib/hypernumbers-1.0/include/errvals.hrl
+ $t curl -s -o $(APP)/src/muin_proc_dict.hrl https://raw.githubusercontent.com/hypernumbers/hypernumbers/master/lib/hypernumbers-1.0/include/muin_proc_dict.hrl
+ $t curl -s -o $(APP)/src/muin_records.hrl https://raw.githubusercontent.com/hypernumbers/hypernumbers/master/lib/hypernumbers-1.0/include/muin_records.hrl
+ $t curl -s -o $(APP)/src/typechecks.hrl https://raw.githubusercontent.com/hypernumbers/hypernumbers/master/lib/hypernumbers-1.0/include/typechecks.hrl
+
+ $i "Generate unrelated .erl files"
+ $t echo "-module(boy)." > $(APP)/src/boy.erl
+ $t echo "-module(girl)." > $(APP)/src/girl.erl
+
+ $i "Disable warnings; our test .xrl files aren't perfect"
+ $t echo "ERLC_OPTS=+debug_info" >> $(APP)/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)/ebin/boy.beam
+ $t test -f $(APP)/ebin/girl.beam
+ $t test -f $(APP)/ebin/xfl_lexer.beam
+ $t test -f $(APP)/src/xfl_lexer.erl
+
+ $i "Check that the application was compiled correctly"
+ $t $(ERL) -pa $(APP)/ebin/ -eval " \
+ ok = application:start($(APP)), \
+ {ok, Mods = [boy, girl, xfl_lexer]} \
+ = application:get_key($(APP), modules), \
+ [{module, M} = code:load_file(M) || M <- Mods], \
+ halt()"
+
+ $i "Touch the .xrl file; check that only required files are rebuilt"
+ $t printf "%s\n" \
+ $(APP)/$(APP).d \
+ $(APP)/ebin/$(APP).app \
+ $(APP)/ebin/xfl_lexer.beam \
+ $(APP)/src/xfl_lexer.erl | sort > $(APP)/EXPECT
+ $t touch $(APP)/src/xfl_lexer.xrl
+ $t $(MAKE) -C $(APP) $v
+ $t find $(APP) -type f -newer $(APP)/src/xfl_lexer.xrl | sort | diff $(APP)/EXPECT -
+ $t rm $(APP)/EXPECT
+
+ $i "Check that the application was compiled correctly"
+ $t $(ERL) -pa $(APP)/ebin/ -eval " \
+ ok = application:start($(APP)), \
+ {ok, Mods = [boy, girl, xfl_lexer]} \
+ = application:get_key($(APP), modules), \
+ [{module, M} = code:load_file(M) || M <- Mods], \
+ halt()"
+
+ $i "Touch a .hrl file included directly; check that only required files are rebuilt"
+ $t printf "%s\n" \
+ $(APP)/$(APP).d \
+ $(APP)/ebin/$(APP).app \
+ $(APP)/ebin/xfl_lexer.beam \
+ $(APP)/src/xfl_lexer.erl | sort > $(APP)/EXPECT
+ $t touch $(APP)/src/typechecks.hrl
+ $t $(MAKE) -C $(APP) $v
+ $t find $(APP) -type f -newer $(APP)/src/typechecks.hrl | sort | diff $(APP)/EXPECT -
+ $t rm $(APP)/EXPECT
+
+ $i "Check that the application was compiled correctly"
+ $t $(ERL) -pa $(APP)/ebin/ -eval " \
+ ok = application:start($(APP)), \
+ {ok, Mods = [boy, girl, xfl_lexer]} \
+ = application:get_key($(APP), modules), \
+ [{module, M} = code:load_file(M) || M <- Mods], \
+ halt()"
+
+ $i "Touch a .hrl file included indirectly; check that only required files are rebuilt"
+ $t printf "%s\n" \
+ $(APP)/$(APP).d \
+ $(APP)/ebin/$(APP).app \
+ $(APP)/ebin/xfl_lexer.beam \
+ $(APP)/src/xfl_lexer.erl | sort > $(APP)/EXPECT
+ $t touch $(APP)/src/errvals.hrl
+ $t $(MAKE) -C $(APP) $v
+ $t find $(APP) -type f -newer $(APP)/src/errvals.hrl | sort | diff $(APP)/EXPECT -
+ $t rm $(APP)/EXPECT
+
+ $i "Check that the application was compiled correctly"
+ $t $(ERL) -pa $(APP)/ebin/ -eval " \
+ ok = application:start($(APP)), \
+ {ok, Mods = [boy, girl, xfl_lexer]} \
+ = application:get_key($(APP), modules), \
+ [{module, M} = code:load_file(M) || M <- Mods], \
+ halt()"
+
+ $i "Clean the application"
+ $t $(MAKE) -C $(APP) clean $v
+
+ $i "Check that source files still exist"
+ $t test -f $(APP)/Makefile
+ $t test -f $(APP)/erlang.mk
+ifdef LEGACY
+ $t test -f $(APP)/src/$(APP).app.src
+endif
+ $t test -f $(APP)/src/boy.erl
+ $t test -f $(APP)/src/girl.erl
+ $t test -f $(APP)/src/errvals.hrl
+ $t test -f $(APP)/src/muin_proc_dict.hrl
+ $t test -f $(APP)/src/muin_records.hrl
+ $t test -f $(APP)/src/typechecks.hrl
+ $t test -f $(APP)/src/xfl_lexer.xrl
+
+ $i "Check that all build artifacts are removed, including intermediates"
+ $t test ! -e $(APP)/$(APP).d
+ $t test ! -e $(APP)/ebin/
+ $t test ! -e $(APP)/src/xfl_lexer.erl
+
+ $i "Build the application again"
+ $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)/ebin/boy.beam
+ $t test -f $(APP)/ebin/girl.beam
+ $t test -f $(APP)/ebin/xfl_lexer.beam
+ $t test -f $(APP)/src/xfl_lexer.erl
+
+ $i "Check that the application was compiled correctly"
+ $t $(ERL) -pa $(APP)/ebin/ -eval " \
+ ok = application:start($(APP)), \
+ {ok, Mods = [boy, girl, xfl_lexer]} \
+ = application:get_key($(APP), modules), \
+ [{module, M} = code:load_file(M) || M <- Mods], \
+ halt()"
+
+core-app-yrl: build clean-core-app-yrl
+
+ $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 "Download .yrl files from Erlang/OTP"
+ $t curl -s -o $(APP)/src/xmerl_xpath_parse.yrl $(OTP_MASTER)/lib/xmerl/src/xmerl_xpath_parse.yrl
+ $t curl -s -o $(APP)/src/xref_parser.yrl $(OTP_MASTER)/lib/tools/src/xref_parser.yrl
+
+ $i "Generate unrelated .erl files"
+ $t echo "-module(boy)." > $(APP)/src/boy.erl
+ $t echo "-module(girl)." > $(APP)/src/girl.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)/ebin/boy.beam
+ $t test -f $(APP)/ebin/girl.beam
+ $t test -f $(APP)/ebin/xmerl_xpath_parse.beam
+ $t test -f $(APP)/ebin/xref_parser.beam
+ $t test -f $(APP)/src/xmerl_xpath_parse.erl
+ $t test -f $(APP)/src/xref_parser.erl
+
+ $i "Check that the application was compiled correctly"
+ $t $(ERL) -pa $(APP)/ebin/ -eval " \
+ ok = application:start($(APP)), \
+ {ok, Mods = [boy, girl, xmerl_xpath_parse, xref_parser]} \
+ = application:get_key($(APP), modules), \
+ [{module, M} = code:load_file(M) || M <- Mods], \
+ halt()"
+
+ $i "Touch one .yrl file; check that only required files are rebuilt"
+ $t printf "%s\n" \
+ $(APP)/$(APP).d \
+ $(APP)/ebin/$(APP).app \
+ $(APP)/ebin/xref_parser.beam \
+ $(APP)/src/xref_parser.erl | sort > $(APP)/EXPECT
+ $t touch $(APP)/src/xref_parser.yrl
+ $t $(MAKE) -C $(APP) $v
+ $t find $(APP) -type f -newer $(APP)/src/xref_parser.yrl | sort | diff $(APP)/EXPECT -
+ $t rm $(APP)/EXPECT
+
+ $i "Check that the application was compiled correctly"
+ $t $(ERL) -pa $(APP)/ebin/ -eval " \
+ ok = application:start($(APP)), \
+ {ok, Mods = [boy, girl, xmerl_xpath_parse, xref_parser]} \
+ = application:get_key($(APP), modules), \
+ [{module, M} = code:load_file(M) || M <- Mods], \
+ halt()"
+
+ $i "Clean the application"
+ $t $(MAKE) -C $(APP) clean $v
+
+ $i "Check that source files still exist"
+ $t test -f $(APP)/Makefile
+ $t test -f $(APP)/erlang.mk
+ifdef LEGACY
+ $t test -f $(APP)/src/$(APP).app.src
+endif
+ $t test -f $(APP)/src/boy.erl
+ $t test -f $(APP)/src/girl.erl
+ $t test -f $(APP)/src/xmerl_xpath_parse.yrl
+ $t test -f $(APP)/src/xref_parser.yrl
+
+ $i "Check that all build artifacts are removed, including intermediates"
+ $t test ! -e $(APP)/$(APP).d
+ $t test ! -e $(APP)/ebin/
+ $t test ! -e $(APP)/src/xmerl_xpath_parse.erl
+ $t test ! -e $(APP)/src/xref_parser.erl
+
+ $i "Build the application again"
+ $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)/ebin/boy.beam
+ $t test -f $(APP)/ebin/girl.beam
+ $t test -f $(APP)/ebin/xmerl_xpath_parse.beam
+ $t test -f $(APP)/ebin/xref_parser.beam
+ $t test -f $(APP)/src/xmerl_xpath_parse.erl
+ $t test -f $(APP)/src/xref_parser.erl
+
+ $i "Check that the application was compiled correctly"
+ $t $(ERL) -pa $(APP)/ebin/ -eval " \
+ ok = application:start($(APP)), \
+ {ok, Mods = [boy, girl, xmerl_xpath_parse, xref_parser]} \
+ = application:get_key($(APP), modules), \
+ [{module, M} = code:load_file(M) || M <- Mods], \
+ halt()"
+
+core-app-yrl-include: build clean-core-app-yrl-include
+
+ $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 "Download a .yrl file with includes from Erlang/OTP"
+ $t curl -s -o $(APP)/src/core_parse.yrl $(OTP_MASTER)/lib/compiler/src/core_parse.yrl
+ $t curl -s -o $(APP)/src/core_parse.hrl $(OTP_MASTER)/lib/compiler/src/core_parse.hrl
+
+ $i "Generate unrelated .erl files"
+ $t echo "-module(boy)." > $(APP)/src/boy.erl
+ $t echo "-module(girl)." > $(APP)/src/girl.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)/ebin/boy.beam
+ $t test -f $(APP)/ebin/core_parse.beam
+ $t test -f $(APP)/ebin/girl.beam
+ $t test -f $(APP)/src/core_parse.erl
+
+ $i "Check that the application was compiled correctly"
+ $t $(ERL) -pa $(APP)/ebin/ -eval " \
+ ok = application:start($(APP)), \
+ {ok, Mods = [boy, core_parse, girl]} \
+ = application:get_key($(APP), modules), \
+ [{module, M} = code:load_file(M) || M <- Mods], \
+ halt()"
+
+ $i "Touch the .yrl file; check that only required files are rebuilt"
+ $t printf "%s\n" \
+ $(APP)/$(APP).d \
+ $(APP)/ebin/$(APP).app \
+ $(APP)/ebin/core_parse.beam \
+ $(APP)/src/core_parse.erl | sort > $(APP)/EXPECT
+ $t touch $(APP)/src/core_parse.yrl
+ $t $(MAKE) -C $(APP) $v
+ $t find $(APP) -type f -newer $(APP)/src/core_parse.yrl | sort | diff $(APP)/EXPECT -
+ $t rm $(APP)/EXPECT
+
+ $i "Check that the application was compiled correctly"
+ $t $(ERL) -pa $(APP)/ebin/ -eval " \
+ ok = application:start($(APP)), \
+ {ok, Mods = [boy, core_parse, girl]} \
+ = application:get_key($(APP), modules), \
+ [{module, M} = code:load_file(M) || M <- Mods], \
+ halt()"
+
+ $i "Touch the .hrl file included; check that only required files are rebuilt"
+ $t printf "%s\n" \
+ $(APP)/$(APP).d \
+ $(APP)/ebin/$(APP).app \
+ $(APP)/ebin/core_parse.beam \
+ $(APP)/src/core_parse.erl | sort > $(APP)/EXPECT
+ $t touch $(APP)/src/core_parse.hrl
+ $t $(MAKE) -C $(APP) $v
+ $t find $(APP) -type f -newer $(APP)/src/core_parse.hrl | sort | diff $(APP)/EXPECT -
+ $t rm $(APP)/EXPECT
+
+ $i "Check that the application was compiled correctly"
+ $t $(ERL) -pa $(APP)/ebin/ -eval " \
+ ok = application:start($(APP)), \
+ {ok, Mods = [boy, core_parse, girl]} \
+ = application:get_key($(APP), modules), \
+ [{module, M} = code:load_file(M) || M <- Mods], \
+ halt()"
+
+ $i "Clean the application"
+ $t $(MAKE) -C $(APP) clean $v
+
+ $i "Check that source files still exist"
+ $t test -f $(APP)/Makefile
+ $t test -f $(APP)/erlang.mk
+ifdef LEGACY
+ $t test -f $(APP)/src/$(APP).app.src
+endif
+ $t test -f $(APP)/src/boy.erl
+ $t test -f $(APP)/src/core_parse.hrl
+ $t test -f $(APP)/src/core_parse.yrl
+ $t test -f $(APP)/src/girl.erl
+
+ $i "Check that all build artifacts are removed, including intermediates"
+ $t test ! -e $(APP)/$(APP).d
+ $t test ! -e $(APP)/ebin/
+ $t test ! -e $(APP)/src/core_parse.erl
+
+ $i "Build the application again"
+ $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)/ebin/boy.beam
+ $t test -f $(APP)/ebin/core_parse.beam
+ $t test -f $(APP)/ebin/girl.beam
+ $t test -f $(APP)/src/core_parse.erl
+
+ $i "Check that the application was compiled correctly"
+ $t $(ERL) -pa $(APP)/ebin/ -eval " \
+ ok = application:start($(APP)), \
+ {ok, Mods = [boy, core_parse, girl]} \
+ = application:get_key($(APP), modules), \
+ [{module, M} = code:load_file(M) || M <- Mods], \
+ halt()"
diff --git a/test/core_compat.mk b/test/core_compat.mk
new file mode 100644
index 0000000..9b6516f
--- /dev/null
+++ b/test/core_compat.mk
@@ -0,0 +1,221 @@
+# Core: Compatibility with other build tools.
+#
+# Note: autopatch functionality is covered separately.
+
+CORE_COMPAT_CASES = auto-rebar rebar rebar-deps rebar-deps-pkg rebar-erlc-opts
+CORE_COMPAT_TARGETS = $(addprefix core-compat-,$(CORE_COMPAT_CASES))
+CORE_COMPAT_CLEAN_TARGETS = $(addprefix clean-,$(CORE_COMPAT_TARGETS))
+
+REBAR_BINARY = https://github.com/rebar/rebar/releases/download/2.6.0/rebar
+
+.PHONY: core-compat $(CORE_COMPAT_TARGETS) clean-core-compat $(CORE_COMPAT_CLEAN_TARGETS)
+
+clean-core-compat: $(CORE_COMPAT_CLEAN_TARGETS)
+
+$(CORE_COMPAT_CLEAN_TARGETS):
+ $t rm -rf $(APP_TO_CLEAN)/
+
+core-compat: $(CORE_COMPAT_TARGETS)
+
+core-compat-auto-rebar: build clean-core-compat-auto-rebar
+
+ $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 "Add rebar.config as a dependency of 'app' target"
+ $t echo "app:: rebar.config" >> $(APP)/Makefile
+
+ $i "Build the application"
+ $t $(MAKE) -C $(APP) $v
+
+ $i "Check that rebar.config was created"
+ $t test -f $(APP)/rebar.config
+
+ $i "Check that rebar.config can be loaded"
+ $t $(ERL) -eval "{ok, _} = file:consult(\"$(APP)/rebar.config\"), halt()"
+
+ $i "Create a temporary file"
+ $t touch $(APP)/older_file
+
+ $i "Wait a second"
+ $t sleep 1
+
+ $i "Build the application again"
+ $t $(MAKE) -C $(APP) $v
+
+ $i "Check that rebar.config is newer than the temporary file"
+ $t test $(APP)/rebar.config -nt $(APP)/older_file
+
+ $i "Check that rebar.config can be loaded"
+ $t $(ERL) -eval "{ok, _} = file:consult(\"$(APP)/rebar.config\"), halt()"
+
+ $i "Distclean the application"
+ $t $(MAKE) -C $(APP) distclean $v
+
+ $i "Download rebar"
+ $t curl -s -L -o $(APP)/rebar $(REBAR_BINARY)
+ $t chmod +x $(APP)/rebar
+
+ $i "Use rebar to build the application"
+ $t cd $(APP) && ./rebar compile $v
+
+core-compat-rebar: build clean-core-compat-rebar
+
+ $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 "Run 'make rebar.config'"
+ $t $(MAKE) -C $(APP) rebar.config $v
+
+ $i "Check that rebar.config was created"
+ $t test -f $(APP)/rebar.config
+
+ $i "Check that rebar.config can be loaded"
+ $t $(ERL) -eval "{ok, _} = file:consult(\"$(APP)/rebar.config\"), halt()"
+
+ $i "Create a temporary file"
+ $t touch $(APP)/older_file
+
+ $i "Wait a second"
+ $t sleep 1
+
+ $i "Run 'make rebar.config' again"
+ $t $(MAKE) -C $(APP) rebar.config $v
+
+ $i "Check that rebar.config is newer than the temporary file"
+ $t test $(APP)/rebar.config -nt $(APP)/older_file
+
+ $i "Check that rebar.config can be loaded"
+ $t $(ERL) -eval "{ok, _} = file:consult(\"$(APP)/rebar.config\"), halt()"
+
+ $i "Distclean the application"
+ $t $(MAKE) -C $(APP) distclean $v
+
+ $i "Download rebar"
+ $t curl -s -L -o $(APP)/rebar $(REBAR_BINARY)
+ $t chmod +x $(APP)/rebar
+
+ $i "Use rebar to build the application"
+ $t cd $(APP) && ./rebar compile $v
+
+core-compat-rebar-deps: build clean-core-compat-rebar-deps
+
+ $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 "Add Cowboy as a dependency"
+ $t sed -i.bak '2i\
+DEPS = cowboy\
+dep_cowboy = git https://github.com/ninenines/cowboy 1.0.0\
+' $(APP)/Makefile
+
+ $i "Run 'make rebar.config'"
+ $t $(MAKE) -C $(APP) rebar.config $v
+
+ $i "Check that rebar.config was created"
+ $t test -f $(APP)/rebar.config
+
+ $i "Check that Cowboy is listed in rebar.config"
+ $t $(ERL) -eval " \
+ {ok, C} = file:consult(\"$(APP)/rebar.config\"), \
+ {_, [{cowboy, _, {git, _, \"1.0.0\"}}]} = lists:keyfind(deps, 1, C), \
+ halt()"
+
+ $i "Distclean the application"
+ $t $(MAKE) -C $(APP) distclean $v
+
+ $i "Download rebar"
+ $t curl -s -L -o $(APP)/rebar $(REBAR_BINARY)
+ $t chmod +x $(APP)/rebar
+
+ $i "Use rebar to build the application"
+ $t cd $(APP) && ./rebar get-deps compile $v
+
+core-compat-rebar-deps-pkg: build clean-core-compat-rebar-deps-pkg
+
+ $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 "Add Cowboy package as a dependency"
+ $t sed -i.bak '2i\
+DEPS = cowboy\
+' $(APP)/Makefile
+
+ $i "Run 'make rebar.config'"
+ $t $(MAKE) -C $(APP) rebar.config $v
+
+ $i "Check that rebar.config was created"
+ $t test -f $(APP)/rebar.config
+
+ $i "Check that Cowboy is listed in rebar.config"
+ $t $(ERL) -eval " \
+ {ok, C} = file:consult(\"$(APP)/rebar.config\"), \
+ {_, [{cowboy, _, {git, \"https://github.com/\" ++ _, _}}]} = lists:keyfind(deps, 1, C), \
+ halt()"
+
+ $i "Distclean the application"
+ $t $(MAKE) -C $(APP) distclean $v
+
+ $i "Download rebar"
+ $t curl -s -L -o $(APP)/rebar $(REBAR_BINARY)
+ $t chmod +x $(APP)/rebar
+
+ $i "Use rebar to build the application"
+ $t cd $(APP) && ./rebar get-deps compile $v
+
+core-compat-rebar-erlc-opts: build clean-core-compat-rebar-erlc-opts
+
+ $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 "Add extra options to ERLC_OPTS"
+ $t echo "ERLC_OPTS += +warn_export_all +warn_missing_spec +warn_untyped_record" >> $(APP)/Makefile
+
+ $i "Run 'make rebar.config'"
+ $t $(MAKE) -C $(APP) rebar.config $v
+
+ $i "Check that rebar.config was created"
+ $t test -f $(APP)/rebar.config
+
+ $i "Check that -Werror is not listed in rebar.config"
+ $t $(ERL) -eval " \
+ {ok, C} = file:consult(\"$(APP)/rebar.config\"), \
+ {_, Opts} = lists:keyfind(erl_opts, 1, C), \
+ false = lists:member(warning_as_errors, Opts), \
+ halt()"
+
+ $i "Check that debug_info is listed in rebar.config"
+ $t $(ERL) -eval " \
+ {ok, C} = file:consult(\"$(APP)/rebar.config\"), \
+ {_, Opts} = lists:keyfind(erl_opts, 1, C), \
+ true = lists:member(debug_info, Opts), \
+ halt()"
+
+ $i "Check that extra options are listed in rebar.config"
+ $t $(ERL) -eval " \
+ {ok, C} = file:consult(\"$(APP)/rebar.config\"), \
+ {_, Opts} = lists:keyfind(erl_opts, 1, C), \
+ true = lists:member(warn_export_all, Opts), \
+ true = lists:member(warn_missing_spec, Opts), \
+ true = lists:member(warn_untyped_record, Opts), \
+ halt()"
+
+ $i "Distclean the application"
+ $t $(MAKE) -C $(APP) distclean $v
+
+ $i "Download rebar"
+ $t curl -s -L -o $(APP)/rebar $(REBAR_BINARY)
+ $t chmod +x $(APP)/rebar
+
+ $i "Use rebar to build the application"
+ $t cd $(APP) && ./rebar compile $v
diff --git a/test/core_plugins.mk b/test/core_plugins.mk
new file mode 100644
index 0000000..5bbee3f
--- /dev/null
+++ b/test/core_plugins.mk
@@ -0,0 +1,86 @@
+# Core: External plugins.
+
+CORE_PLUGINS_CASES = all one
+CORE_PLUGINS_TARGETS = $(addprefix core-plugins-,$(CORE_PLUGINS_CASES))
+CORE_PLUGINS_CLEAN_TARGETS = $(addprefix clean-,$(CORE_PLUGINS_TARGETS))
+
+.PHONY: core-plugins $(CORE_PLUGINS_TARGETS) clean-core-plugins $(CORE_PLUGINS_CLEAN_TARGETS)
+
+clean-core-plugins: $(CORE_PLUGINS_CLEAN_TARGETS)
+
+$(CORE_PLUGINS_CLEAN_TARGETS):
+ $t rm -rf $(APP_TO_CLEAN)/
+
+core-plugins: $(CORE_PLUGINS_TARGETS)
+
+core-plugins-all: build clean-core-plugins-all
+
+ $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 local git repository with two plugins"
+ $t mkdir -p $(APP)/plugin_dep/mk
+ $t echo "plugin1: ; @echo \$$@" > $(APP)/plugin_dep/mk/plugin1.mk
+ $t echo "plugin2: ; @echo \$$@" > $(APP)/plugin_dep/mk/plugin2.mk
+ $t echo "THIS := \$$(dir \$$(realpath \$$(lastword \$$(MAKEFILE_LIST))))" > $(APP)/plugin_dep/plugins.mk
+ $t printf "%s\n" "include \$$(THIS)/mk/plugin1.mk" >> $(APP)/plugin_dep/plugins.mk
+ $t printf "%s\n" "include \$$(THIS)/mk/plugin2.mk" >> $(APP)/plugin_dep/plugins.mk
+# We check that overriding THIS doesn't cause an error.
+ $t echo "THIS :=" >> $(APP)/plugin_dep/plugins.mk
+ $t cd $(APP)/plugin_dep && \
+ git init -q && \
+ git config user.email "[email protected]" && \
+ git config user.name "test suite" && \
+ git add . && \
+ git commit -q -m "Tests"
+
+ $i "Add dependency and plugins to the Makefile"
+ $t sed -i.bak '2i\
+DEPS = plugin_dep\
+dep_plugin_dep = git file://$(abspath $(APP)/plugin_dep) master\
+DEP_PLUGINS = plugin_dep\
+' $(APP)/Makefile
+
+ $i "Run 'make plugin1' and check that it prints plugin1"
+ $t test -n "`$(MAKE) -C $(APP) plugin1 | grep plugin1`"
+
+ $i "Run 'make plugin2' and check that it prints plugin2"
+ $t test -n "`$(MAKE) -C $(APP) plugin2 | grep plugin2`"
+
+core-plugins-one: build clean-core-plugins-one
+
+ $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 local git repository with two plugins"
+ $t mkdir -p $(APP)/plugin_dep/mk
+ $t echo "plugin1: ; @echo \$$@" > $(APP)/plugin_dep/mk/plugin1.mk
+ $t echo "plugin2: ; @echo \$$@" > $(APP)/plugin_dep/mk/plugin2.mk
+ $t echo "THIS := \$$(dir \$$(realpath \$$(lastword \$$(MAKEFILE_LIST))))" > $(APP)/plugin_dep/plugins.mk
+ $t printf "%s\n" "include \$$(THIS)/mk/plugin1.mk" >> $(APP)/plugin_dep/plugins.mk
+ $t printf "%s\n" "include \$$(THIS)/mk/plugin2.mk" >> $(APP)/plugin_dep/plugins.mk
+# We check that overriding THIS doesn't cause an error.
+ $t echo "THIS :=" >> $(APP)/plugin_dep/plugins.mk
+ $t cd $(APP)/plugin_dep && \
+ git init -q && \
+ git config user.email "[email protected]" && \
+ git config user.name "test suite" && \
+ git add . && \
+ git commit -q -m "Tests"
+
+ $i "Add dependency and plugins to the Makefile"
+ $t sed -i.bak '2i\
+DEPS = plugin_dep\
+dep_plugin_dep = git file://$(abspath $(APP)/plugin_dep) master\
+DEP_PLUGINS = plugin_dep/mk/plugin1.mk\
+' $(APP)/Makefile
+
+ $i "Run 'make plugin1' and check that it prints plugin1"
+ $t test -n "`$(MAKE) -C $(APP) plugin1 | grep plugin1`"
+
+ $i "Run 'make plugin2' and confirm the target doesn't exist"
+ $t if `$(MAKE) -C $(APP) plugin2`; then false; fi
diff --git a/test/core_upgrade.mk b/test/core_upgrade.mk
new file mode 100644
index 0000000..ec49a9f
--- /dev/null
+++ b/test/core_upgrade.mk
@@ -0,0 +1,124 @@
+# Core: Erlang.mk upgrade.
+
+CORE_UPGRADE_CASES = custom-build-dir custom-config custom-repo no-config renamed-config
+CORE_UPGRADE_TARGETS = $(addprefix core-upgrade-,$(CORE_UPGRADE_CASES))
+CORE_UPGRADE_CLEAN_TARGETS = $(addprefix clean-,$(CORE_UPGRADE_TARGETS))
+
+.PHONY: core-upgrade $(CORE_UPGRADE_TARGETS) clean-core-upgrade $(CORE_UPGRADE_CLEAN_TARGETS)
+
+clean-core-upgrade: $(CORE_UPGRADE_CLEAN_TARGETS)
+
+$(CORE_UPGRADE_CLEAN_TARGETS):
+ $t rm -rf $(APP_TO_CLEAN)/
+
+core-upgrade: $(CORE_UPGRADE_TARGETS)
+
+core-upgrade-custom-build-dir: build clean-core-upgrade-custom-build-dir
+
+ $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 "Append a rule to the Erlang.mk file for testing purposes"
+ $t echo "erlang_mk_upgrade_test_rule: ; @echo FAIL" >> $(APP)/erlang.mk
+
+ $i "Check that the test rule works as intended"
+ $t test "FAIL" = "`$(MAKE) -C $(APP) --no-print-directory erlang_mk_upgrade_test_rule V=0`"
+
+ $i "Create the custom build directory"
+ $t mkdir $(APP)/custom/
+ $t test -d $(APP)/custom/
+
+ $i "Upgrade Erlang.mk with a custom build directory"
+ $t ERLANG_MK_BUILD_DIR=custom $(MAKE) -C $(APP) erlang-mk $v
+
+ $i "Check that the rule is gone"
+ $t if $(MAKE) -C $(APP) erlang_mk_upgrade_test_rule $v; then false; fi
+
+ $i "Check that the custom build directory is gone"
+ $t test ! -d $(APP)/custom/
+
+core-upgrade-custom-config: build clean-core-upgrade-custom-config
+
+ $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 custom build.config file without plugins"
+ $t echo "core/*" > $(APP)/build.config
+
+ $i "Upgrade Erlang.mk"
+ $t $(MAKE) -C $(APP) erlang-mk $v
+
+ $i "Check that the bootstrap plugin is gone"
+ $t if $(MAKE) -C $(APP) list-templates $v; then false; fi
+
+core-upgrade-custom-repo: build clean-core-upgrade-custom-repo
+
+ $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 "Fork erlang.mk locally and modify it"
+ $t git clone -q https://github.com/ninenines/erlang.mk $(APP)/alt-erlangmk-repo
+ $t sed -i.bak '1i\
+# Copyright (c) erlang.mk Testsuite!\
+' $(APP)/alt-erlangmk-repo/core/core.mk
+ $t (cd $(APP)/alt-erlangmk-repo && \
+ git checkout -q -b test-copyright && \
+ git config user.email "[email protected]" && \
+ git config user.name "test suite" && \
+ git commit -q -a -m 'Add Testsuite copyright')
+
+ $i "Point application to an alternate erlang.mk repository"
+ $t sed -i.bak '2i\
+ERLANG_MK_REPO = file://$(abspath $(APP)/alt-erlangmk-repo)\
+ERLANG_MK_COMMIT = test-copyright\
+' $(APP)/Makefile
+
+ $i "Update erlang.mk"
+ $t $(MAKE) -C $(APP) erlang-mk $v
+
+ $i "Check our modification is there"
+ $t grep -q "# Copyright (c) erlang.mk Testsuite!" $(APP)/erlang.mk
+
+core-upgrade-no-config: build clean-core-upgrade-no-config
+
+ $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 "Append a rule to the Erlang.mk file for testing purposes"
+ $t echo "erlang_mk_upgrade_test_rule: ; @echo FAIL" >> $(APP)/erlang.mk
+
+ $i "Check that the test rule works as intended"
+ $t test "FAIL" = "`$(MAKE) -C $(APP) --no-print-directory erlang_mk_upgrade_test_rule V=0`"
+
+ $i "Upgrade Erlang.mk"
+ $t $(MAKE) -C $(APP) erlang-mk $v
+
+ $i "Check that the rule is gone"
+ $t if $(MAKE) -C $(APP) erlang_mk_upgrade_test_rule $v; then false; fi
+
+core-upgrade-renamed-config: build clean-core-upgrade-renamed-config
+
+ $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 custom build.config file without plugins; name it my.build.config"
+ $t echo "core/*" > $(APP)/my.build.config
+
+ $i "Set ERLANG_MK_BUILD_CONFIG=my.build.config in the Makefile"
+ $t echo "ERLANG_MK_BUILD_CONFIG = my.build.config" >> $(APP)/Makefile
+
+ $i "Upgrade Erlang.mk"
+ $t $(MAKE) -C $(APP) erlang-mk $v
+
+ $i "Check that the bootstrap plugin is gone"
+ $t if $(MAKE) -C $(APP) list-templates $v; then false; fi
diff --git a/test/plugin_bootstrap.mk b/test/plugin_bootstrap.mk
new file mode 100644
index 0000000..16bfb58
--- /dev/null
+++ b/test/plugin_bootstrap.mk
@@ -0,0 +1,155 @@
+# Bootstrap plugin.
+
+BOOTSTRAP_CASES = app lib rel templates
+BOOTSTRAP_TARGETS = $(addprefix bootstrap-,$(BOOTSTRAP_CASES))
+BOOTSTRAP_CLEAN_TARGETS = $(addprefix clean-,$(BOOTSTRAP_TARGETS))
+
+.PHONY: bootstrap $(BOOTSTRAP_TARGETS) clean-bootstrap $(BOOTSTRAP_CLEAN_TARGETS)
+
+clean-bootstrap: $(BOOTSTRAP_CLEAN_TARGETS)
+
+$(BOOTSTRAP_CLEAN_TARGETS):
+ $t rm -rf $(APP_TO_CLEAN)/
+
+bootstrap: $(BOOTSTRAP_TARGETS)
+
+bootstrap-app: build clean-bootstrap-app
+
+ $i "Bootstrap a new OTP application named $(APP)"
+ $t mkdir $(APP)/
+ $t cp ../erlang.mk $(APP)/
+ $t $(MAKE) -C $(APP) -f erlang.mk bootstrap $v
+
+ $i "Check that all bootstrapped files exist"
+ $t test -f $(APP)/Makefile
+ifdef LEGACY
+ $t test -f $(APP)/src/$(APP).app.src
+endif
+ $t test -f $(APP)/src/$(APP)_app.erl
+ $t test -f $(APP)/src/$(APP)_sup.erl
+
+ $i "Build the application"
+ $t $(MAKE) -C $(APP) $v
+
+ $i "Check that all compiled files exist"
+ $t test -f $(APP)/ebin/$(APP).app
+ $t test -f $(APP)/ebin/$(APP)_app.beam
+ $t test -f $(APP)/ebin/$(APP)_sup.beam
+
+ $i "Check that the application was compiled correctly"
+ $t $(ERL) -pa $(APP)/ebin/ -eval " \
+ ok = application:start($(APP)), \
+ {ok, [$(APP)_app, $(APP)_sup]} = application:get_key($(APP), modules), \
+ {module, $(APP)_app} = code:load_file($(APP)_app), \
+ {module, $(APP)_sup} = code:load_file($(APP)_sup), \
+ halt()"
+
+bootstrap-lib: build clean-bootstrap-lib
+
+ $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 "Check that all bootstrapped files exist"
+ $t test -f $(APP)/Makefile
+ifdef LEGACY
+ $t test -f $(APP)/src/$(APP).app.src
+endif
+
+ $i "Build the application"
+ $t $(MAKE) -C $(APP) $v
+
+ $i "Check that all compiled files exist"
+ $t test -f $(APP)/ebin/$(APP).app
+
+ $i "Check that the application was compiled correctly"
+ $t $(ERL) -pa $(APP)/ebin/ -eval " \
+ ok = application:start($(APP)), \
+ {ok, []} = application:get_key($(APP), modules), \
+ halt()"
+
+bootstrap-rel: build clean-bootstrap-rel
+
+ $i "Bootstrap a new release-enabled OTP application named $(APP)"
+ $t mkdir $(APP)/
+ $t cp ../erlang.mk $(APP)/
+ $t $(MAKE) -C $(APP) -f erlang.mk bootstrap bootstrap-rel $v
+
+ $i "Check that all bootstrapped files exist"
+ $t test -f $(APP)/Makefile
+ $t test -f $(APP)/relx.config
+ $t test -f $(APP)/rel/sys.config
+ $t test -f $(APP)/rel/vm.args
+ifdef LEGACY
+ $t test -f $(APP)/src/$(APP).app.src
+endif
+ $t test -f $(APP)/src/$(APP)_app.erl
+ $t test -f $(APP)/src/$(APP)_sup.erl
+
+ $i "Build the application and the release"
+ $t $(MAKE) -C $(APP) $v
+
+ $i "Check that all compiled files exist"
+ $t test -f $(APP)/ebin/$(APP).app
+ $t test -f $(APP)/ebin/$(APP)_app.beam
+ $t test -f $(APP)/ebin/$(APP)_sup.beam
+
+ $i "Check that the release was generated"
+ifeq ($(PLATFORM),msys2)
+ $t test -f $(APP)/_rel/$(APP)_release/bin/$(APP)_release.cmd
+else
+ $t test -f $(APP)/_rel/$(APP)_release/bin/$(APP)_release
+endif
+
+ $i "Check that the release can be started and stopped"
+ifeq ($(PLATFORM),msys2)
+ $t $(APP)/_rel/$(APP)_release/bin/$(APP)_release.cmd install $v
+ $t $(APP)/_rel/$(APP)_release/bin/$(APP)_release.cmd start $v
+ $t $(APP)/_rel/$(APP)_release/bin/$(APP)_release.cmd stop $v
+ $t $(APP)/_rel/$(APP)_release/bin/$(APP)_release.cmd uninstall $v
+else
+ $t $(APP)/_rel/$(APP)_release/bin/$(APP)_release start $v
+ $t $(APP)/_rel/$(APP)_release/bin/$(APP)_release stop $v
+endif
+
+ $i "Check that there's no erl_crash.dump file"
+ $t test ! -f $(APP)/_rel/$(APP)_release/erl_crash.dump
+
+bootstrap-templates: build clean-bootstrap-templates
+
+ $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 "Check that we can get the list of templates"
+ $t test `$(MAKE) -C $(APP) --no-print-directory list-templates V=0 | wc -l` -eq 1
+
+ $i "Generate one of each template"
+ $t $(MAKE) -C $(APP) --no-print-directory new t=gen_fsm n=my_fsm
+ $t $(MAKE) -C $(APP) --no-print-directory new t=gen_server n=my_server
+ $t $(MAKE) -C $(APP) --no-print-directory new t=supervisor n=my_sup
+ $t $(MAKE) -C $(APP) --no-print-directory new t=cowboy_http n=my_http
+ $t $(MAKE) -C $(APP) --no-print-directory new t=cowboy_loop n=my_loop
+ $t $(MAKE) -C $(APP) --no-print-directory new t=cowboy_rest n=my_rest
+ $t $(MAKE) -C $(APP) --no-print-directory new t=cowboy_ws n=my_ws
+ $t $(MAKE) -C $(APP) --no-print-directory new t=ranch_protocol n=my_protocol
+
+# Here we disable warnings because templates contain missing behaviors.
+ $i "Build the application"
+ $t $(MAKE) -C $(APP) ERLC_OPTS=+debug_info $v
+
+ $i "Check that all compiled files exist"
+ $t test -f $(APP)/ebin/$(APP).app
+ $t test -f $(APP)/ebin/my_fsm.beam
+ $t test -f $(APP)/ebin/my_server.beam
+ $t test -f $(APP)/ebin/my_sup.beam
+
+ $i "Check that all the modules can be loaded"
+ $t $(ERL) -pa $(APP)/ebin/ -eval " \
+ ok = application:start($(APP)), \
+ {ok, Mods = [my_fsm, my_http, my_loop, my_protocol, my_rest, my_server, my_sup, my_ws]} \
+ = application:get_key($(APP), modules), \
+ [{module, M} = code:load_file(M) || M <- Mods], \
+ halt()"