diff options
-rw-r--r-- | Makefile | 15 | ||||
-rw-r--r-- | README.md | 113 | ||||
-rw-r--r-- | appveyor.yml | 7 | ||||
-rw-r--r-- | build.config | 1 | ||||
-rw-r--r-- | circle.yml | 4 | ||||
-rw-r--r-- | core/compat.mk | 25 | ||||
-rw-r--r-- | core/core.mk | 24 | ||||
-rw-r--r-- | core/erlc.mk | 74 | ||||
-rw-r--r-- | core/test.mk | 4 | ||||
-rw-r--r-- | doc/src/guide/app.asciidoc | 146 | ||||
-rw-r--r-- | doc/src/guide/book.asciidoc | 14 | ||||
-rw-r--r-- | doc/src/guide/compat.asciidoc | 88 | ||||
-rw-r--r-- | doc/src/guide/external_plugins.asciidoc | 7 | ||||
-rw-r--r-- | doc/src/guide/installation.asciidoc | 112 | ||||
-rw-r--r-- | doc/src/guide/limitations.asciidoc | 45 | ||||
-rw-r--r-- | doc/src/guide/why.asciidoc | 80 | ||||
-rw-r--r-- | plugins/bootstrap.mk | 6 | ||||
-rw-r--r-- | plugins/relx.mk | 6 | ||||
-rw-r--r-- | test/Makefile | 173 | ||||
-rw-r--r-- | test/core_app.mk | 1398 | ||||
-rw-r--r-- | test/core_compat.mk | 221 | ||||
-rw-r--r-- | test/core_plugins.mk | 86 | ||||
-rw-r--r-- | test/core_upgrade.mk | 124 | ||||
-rw-r--r-- | test/plugin_bootstrap.mk | 155 |
24 files changed, 2730 insertions, 198 deletions
@@ -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 @@ -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 @@ -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()" |