aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rw-r--r--Makefile3
-rw-r--r--README.md2
-rw-r--r--core/core.mk12
-rw-r--r--core/deps.mk18
-rw-r--r--core/erlc.mk155
-rw-r--r--doc/src/guide/app.asciidoc290
-rw-r--r--doc/src/guide/book.asciidoc15
-rw-r--r--doc/src/guide/external_plugins.asciidoc73
-rw-r--r--doc/src/guide/getting_started.asciidoc262
-rw-r--r--doc/src/guide/overview.asciidoc88
-rw-r--r--doc/src/guide/updating.asciidoc62
-rw-r--r--index/riak_control.mk7
-rw-r--r--plugins/elvis.mk4
-rw-r--r--plugins/erlydtl.mk34
-rw-r--r--plugins/triq.mk2
16 files changed, 962 insertions, 67 deletions
diff --git a/.gitignore b/.gitignore
index 30d0d8e..3d5a519 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,5 @@
+doc/guide.pdf
+doc/html
test/app1/
test/pkgs.log
test/temp-python/
diff --git a/Makefile b/Makefile
index 9e71e35..625a38e 100644
--- a/Makefile
+++ b/Makefile
@@ -31,3 +31,6 @@ else
check:
$(MAKE) -C test pkg-$(p)
endif
+
+docs:
+ $(MAKE) -f core/core.mk -f core/docs.mk -f plugins/asciidoc.mk asciidoc
diff --git a/README.md b/README.md
index cf0551d..5557282 100644
--- a/README.md
+++ b/README.md
@@ -5,6 +5,8 @@ Common Makefile rules for building and testing Erlang applications.
Also features support for dependencies and a package index.
+[Check out our upcoming documentation!](doc/src/guide/book.asciidoc)
+
Why erlang.mk?
--------------
diff --git a/core/core.mk b/core/core.mk
index a6f55b6..4b292ee 100644
--- a/core/core.mk
+++ b/core/core.mk
@@ -73,13 +73,9 @@ endif
# Core targets.
-ifneq ($(words $(MAKECMDGOALS)),1)
.NOTPARALLEL:
-endif
-all:: deps
- $(verbose) $(MAKE) --no-print-directory app
- $(verbose) $(MAKE) --no-print-directory rel
+all:: deps app rel
# Noop to avoid a Make warning when there's nothing to do.
rel::
@@ -101,7 +97,7 @@ help::
"erlang.mk (version $(ERLANG_MK_VERSION)) is distributed under the terms of the ISC License." \
"Copyright (c) 2013-2015 Loïc Hoguin <[email protected]>" \
"" \
- "Usage: [V=1] $(MAKE) [-jNUM] [target]..." \
+ "Usage: [V=1] $(MAKE) [target]..." \
"" \
"Core targets:" \
" all Run deps, app and rel targets in that order" \
@@ -136,7 +132,7 @@ endef
# Adding erlang.mk to make Erlang scripts who call init:get_plain_arguments() happy.
define erlang
-$(ERL) -pz $(ERLANG_MK_TMP)/rebar/ebin -eval "$(subst $(newline),,$(subst ",\",$(1)))" -- erlang.mk
+$(ERL) $(2) -pz $(ERLANG_MK_TMP)/rebar/ebin -eval "$(subst $(newline),,$(subst ",\",$(1)))" -- erlang.mk
endef
ifeq ($(shell which wget 2>/dev/null | wc -l), 1)
@@ -166,7 +162,7 @@ endif
core_eq = $(and $(findstring $(1),$(2)),$(findstring $(2),$(1)))
-core_find = $(foreach d,$(call core_ls,$1*),$(call core_find,$d/,$2) $(filter $(subst *,%,$2),$d))
+core_find = $(if $(wildcard $1),$(shell find $1 -type f -name $(subst *,\*,$2)))
core_lc = $(subst A,a,$(subst B,b,$(subst C,c,$(subst D,d,$(subst E,e,$(subst F,f,$(subst G,g,$(subst H,h,$(subst I,i,$(subst J,j,$(subst K,k,$(subst L,l,$(subst M,m,$(subst N,n,$(subst O,o,$(subst P,p,$(subst Q,q,$(subst R,r,$(subst S,s,$(subst T,t,$(subst U,u,$(subst V,v,$(subst W,w,$(subst X,x,$(subst Y,y,$(subst Z,z,$(1)))))))))))))))))))))))))))
diff --git a/core/deps.mk b/core/deps.mk
index c3842d1..4ac58ca 100644
--- a/core/deps.mk
+++ b/core/deps.mk
@@ -434,7 +434,8 @@ define dep_autopatch_rebar.erl
end
end || P <- Plugins],
[RunPlugin(P, preprocess) || P <- Plugins],
- [RunPlugin(P, pre_compile) || P <- Plugins]
+ [RunPlugin(P, pre_compile) || P <- Plugins],
+ [RunPlugin(P, compile) || P <- Plugins]
end
end(),
halt()
@@ -572,3 +573,18 @@ $(foreach dep,$(DEPS),$(eval $(call dep_target,$(dep))))
distclean-deps:
$(gen_verbose) rm -rf $(DEPS_DIR)
+
+# External plugins.
+
+DEP_PLUGINS ?=
+
+define core_dep_plugin
+-include $(DEPS_DIR)/$(1)
+
+$(DEPS_DIR)/$(1): $(DEPS_DIR)/$(2) ;
+endef
+
+$(foreach p,$(DEP_PLUGINS),\
+ $(eval $(if $(findstring /,$p),\
+ $(call core_dep_plugin,$p,$(firstword $(subst /, ,$p))),\
+ $(call core_dep_plugin,$p/plugins.mk,$p))))
diff --git a/core/erlc.mk b/core/erlc.mk
index 88895a2..80affca 100644
--- a/core/erlc.mk
+++ b/core/erlc.mk
@@ -24,6 +24,9 @@ app_verbose = $(app_verbose_$(V))
appsrc_verbose_0 = @echo " APP " $(PROJECT).app.src;
appsrc_verbose = $(appsrc_verbose_$(V))
+makedep_verbose_0 = @echo " DEPEND" $(PROJECT).d;
+makedep_verbose = $(makedep_verbose_$(V))
+
erlc_verbose_0 = @echo " ERLC " $(filter-out $(patsubst %,%.erl,$(ERLC_EXCLUDE)),\
$(filter %.erl %.core,$(?F)));
erlc_verbose = $(erlc_verbose_$(V))
@@ -40,9 +43,11 @@ mib_verbose = $(mib_verbose_$(V))
# Targets.
ifeq ($(wildcard ebin/test),)
-app:: app-build
+app::
+ $(verbose) $(MAKE) --no-print-directory app-build
else
-app:: clean app-build
+app:: clean
+ $(verbose) $(MAKE) --no-print-directory app-build
endif
ifeq ($(wildcard src/$(PROJECT)_app.erl),)
@@ -70,7 +75,7 @@ define app_file
endef
endif
-app-build: erlc-include ebin/$(PROJECT).app
+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),)
@@ -87,61 +92,127 @@ else
> ebin/$(PROJECT).app
endif
-erlc-include:
- - $(verbose) if [ -d ebin/ ]; then \
- find include/ src/ -type f -name \*.hrl -newer ebin -exec touch $(shell find src/ -type f -name "*.erl") \; 2>/dev/null || printf ''; \
- fi
+# Source files.
-define compile_erl
- $(erlc_verbose) erlc -v $(if $(IS_DEP),$(filter-out -Werror,$(ERLC_OPTS)),$(ERLC_OPTS)) -o ebin/ \
- -pa ebin/ -I include/ $(filter-out $(ERLC_EXCLUDE_PATHS),\
- $(COMPILE_FIRST_PATHS) $(1))
-endef
+ifneq ($(wildcard src/),)
+ERL_FILES = $(sort $(call core_find,src/,*.erl))
+CORE_FILES = $(sort $(call core_find,src/,*.core))
-define compile_xyrl
- $(xyrl_verbose) erlc -v -o ebin/ $(1)
- $(xyrl_verbose) erlc $(ERLC_OPTS) -o ebin/ ebin/*.erl
- $(verbose) rm ebin/*.erl
-endef
+# ASN.1 files.
-define compile_asn1
- $(asn1_verbose) erlc -v -I include/ -o ebin/ $(1)
- $(verbose) mv ebin/*.hrl include/
- $(verbose) mv ebin/*.asn1db include/
- $(verbose) rm ebin/*.erl
-endef
+ifneq ($(wildcard asn1/),)
+ASN1_FILES = $(sort $(call core_find,asn1/,*.asn1))
+ERL_FILES += $(addprefix src/,$(patsubst %.asn1,%.erl,$(notdir $(ASN1_FILES))))
-define compile_mib
- $(mib_verbose) erlc -v $(ERLC_MIB_OPTS) -o priv/mibs/ \
- -I priv/mibs/ $(COMPILE_MIB_FIRST_PATHS) $(1)
- $(mib_verbose) erlc -o include/ -- priv/mibs/*.bin
+define compile_asn1
+ $(verbose) mkdir -p include/
+ $(asn1_verbose) erlc -v -I include/ -o asn1/ +noobj $(1)
+ $(verbose) mv asn1/*.erl src/
+ $(verbose) mv asn1/*.hrl include/
+ $(verbose) mv asn1/*.asn1db include/
endef
-ifneq ($(wildcard src/),)
-ebin/$(PROJECT).app::
- $(verbose) mkdir -p ebin/
-
-ifneq ($(wildcard asn1/),)
-ebin/$(PROJECT).app:: $(sort $(call core_find,asn1/,*.asn1))
- $(verbose) mkdir -p include
+$(PROJECT).d:: $(ASN1_FILES)
$(if $(strip $?),$(call compile_asn1,$?))
endif
+# SNMP MIB files.
+
ifneq ($(wildcard mibs/),)
-ebin/$(PROJECT).app:: $(sort $(call core_find,mibs/,*.mib))
- $(verbose) mkdir -p priv/mibs/ include
- $(if $(strip $?),$(call compile_mib,$?))
+MIB_FILES = $(sort $(call core_find,mibs/,*.mib))
+
+$(PROJECT).d:: $(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
endif
-ebin/$(PROJECT).app:: $(sort $(call core_find,src/,*.erl *.core))
+# Leex and Yecc files.
+
+XRL_FILES = $(sort $(call core_find,src/,*.xrl))
+XRL_ERL_FILES = $(addprefix src/,$(patsubst %.xrl,%.erl,$(notdir $(XRL_FILES))))
+ERL_FILES += $(XRL_ERL_FILES)
+
+YRL_FILES = $(sort $(call core_find,src/,*.yrl))
+YRL_ERL_FILES = $(addprefix src/,$(patsubst %.yrl,%.erl,$(notdir $(YRL_FILES))))
+ERL_FILES += $(YRL_ERL_FILES)
+
+$(PROJECT).d:: $(XRL_FILES) $(YRL_FILES)
+ $(if $(strip $?),$(xyrl_verbose) erlc -v -o src/ $?)
+
+# Erlang and Core Erlang files.
+
+define makedep.erl
+ ErlFiles = lists:usort(string:tokens("$(ERL_FILES)", " ")),
+ Modules = [{filename:basename(F, ".erl"), F} || F <- ErlFiles],
+ Add = fun (Dep, Acc) ->
+ case lists:keyfind(atom_to_list(Dep), 1, Modules) of
+ {_, DepFile} -> [DepFile|Acc];
+ false -> Acc
+ end
+ end,
+ AddHd = fun (Dep, Acc) ->
+ case {Dep, lists:keymember(Dep, 2, Modules)} of
+ {"src/" ++ _, false} -> [Dep|Acc];
+ {"include/" ++ _, false} -> [Dep|Acc];
+ _ -> Acc
+ end
+ end,
+ CompileFirst = fun (Deps) ->
+ First0 = [case filename:extension(D) of
+ ".erl" -> filename:basename(D, ".erl");
+ _ -> []
+ end || D <- Deps],
+ case lists:usort(First0) of
+ [] -> [];
+ [[]] -> [];
+ First -> ["COMPILE_FIRST +=", [[" ", F] || F <- First], "\n"]
+ end
+ end,
+ Depend = [begin
+ case epp:parse_file(F, [{includes, ["include/"]}]) of
+ {ok, Forms} ->
+ Deps = lists:usort(lists:foldl(fun
+ ({attribute, _, behavior, Dep}, Acc) -> Add(Dep, Acc);
+ ({attribute, _, behaviour, Dep}, Acc) -> Add(Dep, Acc);
+ ({attribute, _, compile, {parse_transform, Dep}}, Acc) -> Add(Dep, Acc);
+ ({attribute, _, file, {Dep, _}}, Acc) -> AddHd(Dep, Acc);
+ (_, Acc) -> Acc
+ end, [], Forms)),
+ [F, ":", [[" ", D] || D <- Deps], "\n", CompileFirst(Deps)];
+ {error, enoent} ->
+ []
+ end
+ end || F <- ErlFiles],
+ ok = file:write_file("$(1)", Depend),
+ halt()
+endef
+
+$(PROJECT).d:: $(ERL_FILES) $(call core_find,include/,*.hrl)
+ $(makedep_verbose) $(call erlang,$(call makedep.erl,$@))
+
+-include $(PROJECT).d
+
+ebin/$(PROJECT).app:: $(PROJECT).d
+ $(verbose) mkdir -p ebin/
+
+define compile_erl
+ $(erlc_verbose) erlc -v $(if $(IS_DEP),$(filter-out -Werror,$(ERLC_OPTS)),$(ERLC_OPTS)) -o ebin/ \
+ -pa ebin/ -I include/ $(filter-out $(ERLC_EXCLUDE_PATHS),$(COMPILE_FIRST_PATHS) $(1))
+endef
+
+ebin/$(PROJECT).app:: $(ERL_FILES) $(CORE_FILES)
$(if $(strip $?),$(call compile_erl,$?))
-ebin/$(PROJECT).app:: $(sort $(call core_find,src/,*.xrl *.yrl))
- $(if $(strip $?),$(call compile_xyrl,$?))
+$(sort $(ERL_FILES) $(CORE_FILES)):
+ @touch $@
endif
clean:: clean-app
clean-app:
- $(gen_verbose) rm -rf ebin/ priv/mibs/ \
- $(addprefix include/,$(addsuffix .hrl,$(notdir $(basename $(call core_find,mibs/,*.mib)))))
+ $(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))))
diff --git a/doc/src/guide/app.asciidoc b/doc/src/guide/app.asciidoc
new file mode 100644
index 0000000..a04c5a9
--- /dev/null
+++ b/doc/src/guide/app.asciidoc
@@ -0,0 +1,290 @@
+== Building
+
+Erlang.mk can do a lot of things, but it is, first and
+foremost, a build tool. In this chapter we will cover
+the basics of building a project with Erlang.mk.
+
+For most of this chapter, we will assume that you are
+using a project link:getting_started.asciidoc[generated by Erlang.mk].
+
+=== How to build
+
+To build a project, all you have to do is type `make`:
+
+[source,bash]
+$ make
+
+It will work regardless of your project: OTP applications,
+library applications, NIFs, port drivers or even releases.
+Erlang.mk also automatically downloads and compiles the
+dependencies for your project.
+
+All this is possible thanks to a combination of configuration
+and conventions. Most of the conventions come from Erlang/OTP
+itself so any seasoned Erlang developers should feel right at
+home.
+
+=== What to build
+
+Erlang.mk gives you control over three steps of the build
+process, allowing you to do a partial build if needed.
+
+A build has three phases: first any dependency is fetched
+and built, then the project itself is built and finally a
+release may be generated when applicable. A release is only
+generated for projects specifically configured to do so.
+
+Erlang.mk handles those three phases automatically when you
+type `make`. But sometimes you just want to repeat one or
+two of them.
+
+The commands detailed in this section are most useful after
+you have a successful build as they allow you to quickly
+redo a step instead of going through everything. This is
+especially useful for large projects or projects that end
+up generating releases.
+
+==== Application
+
+You can build your application specifically, without
+looking at handling dependencies or generating a release,
+by running the following command:
+
+[source,bash]
+$ make app
+
+This command is very useful if you have a lot of dependencies
+and develop on a machine with slow file access, like the
+Raspberry Pi and many other embedded devices.
+
+Note that this command may fail if a required dependency
+is missing.
+
+==== Dependencies
+
+You can build all dependencies, and nothing else, by
+running the following command:
+
+[source,bash]
+$ make deps
+
+This will fetch and compile all dependencies and their
+dependencies, recursively.
+
+link:deps.asciidoc[Packages and dependencies] are covered
+in the next chapter.
+
+==== Release
+
+You can generate the release, skipping the steps for building
+the application and dependencies, by running the following
+command:
+
+[source,bash]
+$ make rel
+
+This command can be useful if nothing changed except the
+release configuration files.
+
+Consult the link:relx.asciidoc[Releases] chapter for more
+information about what releases are and how they are generated.
+
+Note that this command may fail if a required dependency
+is missing.
+
+=== Application resource file
+
+When building your application, Erlang.mk will generate the
+http://www.erlang.org/doc/man/app.html[application resource file].
+This file is mandatory for all Erlang applications and is
+found in 'ebin/$(PROJECT).app'.
+
+`PROJECT` is a variable defined in your Makefile and taken
+from the name of the directory when Erlang.mk bootstraps
+your project.
+
+Erlang.mk can build the 'ebin/$(PROJECT).app' in two different
+ways: from the configuration found in the Makefile, or from
+the 'src/$(PROJECT).app.src' file.
+
+==== Application configuration
+
+Erlang.mk automatically fills the `PROJECT` variable when
+bootstrapping a new project, but everything else is up to
+you. None of the values are required to build your project,
+although it is recommended to fill everything relevant to
+your situation.
+
+`PROJECT`::
+ The name of the OTP application or library.
+`PROJECT_DESCRIPTION`::
+ Short description of the project.
+`PROJECT_VERSION`::
+ Current version of the project.
+`PROJECT_REGISTERED`::
+ List of the names of all registered processes.
+`OTP_DEPS`::
+ List of Erlang/OTP applications this project depends on,
+ excluding `erts`, `kernel` and `stdlib`.
+`DEPS`::
+ List of applications this project depends on that need
+ to be fetched by Erlang.mk.
+
+There's no need for quotes or anything. The relevant part of
+the Cowboy Makefile follows, if you need an example:
+
+[source,make]
+----
+PROJECT = cowboy
+PROJECT_DESCRIPTION = Small, fast, modular HTTP server.
+PROJECT_VERSION = 2.0.0-pre.2
+PROJECT_REGISTERED = cowboy_clock
+
+OTP_DEPS = crypto
+DEPS = cowlib ranch
+----
+
+Any space before and after the value is dropped.
+
+link:deps.asciidoc[Dependencies] are covered in details in
+the next chapter.
+
+==== Legacy method
+
+The 'src/$(PROJECT).app.src' file is a legacy method of
+building Erlang applications. It was introduced by the original
+`rebar` build tool, of which Erlang.mk owes a great deal as it
+is its main inspiration.
+
+The '.app.src' file serves as a template to generate the '.app'
+file. Erlang.mk will take it, fill in the `modules` value
+dynamically, and save the result in 'ebin/$(PROJECT).app'.
+
+When using this method, Erlang.mk cannot fill the `applications`
+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.
+
+=== File formats
+
+Erlang.mk supports a variety of different source file formats.
+The following formats are supported natively:
+
+[cols="<,3*^",options="header"]
+|===
+| Extension | Location | Description | Output
+| .erl | src/ | Erlang source | ebin/*.beam
+| .core | src/ | Core Erlang source | ebin/*.beam
+| .xrl | src/ | Leex source | src/*.erl
+| .yrl | src/ | Yecc source | src/*.erl
+| .asn1 | asn1/ | ASN.1 files | include/*.hrl include/*.asn1db src/*.erl
+| .mib | mibs/ | SNMP MIB files | include/*.hrl priv/mibs/*.bin
+|===
+
+Files are always searched recursively.
+
+The build is ordered, so that files that generate Erlang source
+files are run before, and the resulting Erlang source files are
+then built normally.
+
+In addition, Erlang.mk keeps track of header files (`.hrl`)
+as described at the end of this chapter. It can also compile
+C code, as described in the link:ports.asciidoc[NIFs and port drivers]
+chapter.
+
+Erlang.mk also comes with plugins for the following formats:
+
+[cols="<,3*^",options="header"]
+|===
+| Extension | Location | Description | Output
+| .dtl | templates/ | Django templates | ebin/*.beam
+| .proto | src/ | Protocol buffers | ebin/*.beam
+|===
+
+=== Cold and hot builds
+
+The first time you run `make`, Erlang.mk will build everything.
+
+The second time you run `make`, and all subsequent times, Erlang.mk
+will only rebuild what changed. Erlang.mk has been optimized for
+this use case, as it is the most common during development.
+
+Erlang.mk figures out what changed by using the dependency tracking
+feature of Make. Make automatically rebuilds a target if one of its
+dependency has changed (for example if a header file has changed,
+all the source files that include it will be rebuilt), and Erlang.mk
+leverages this feature to cut down on rebuild times.
+
+Note that this applies only to building; some other features of
+Erlang.mk will run every time they are called regardless of files
+changed.
+
+=== Dependency tracking
+
+NOTE: This section is about the dependency tracking between files
+inside your project, not application dependencies.
+
+Erlang.mk keeps track of the dependencies between the different
+files in your project. This information is kept in the '$(PROJECT).d'
+file in your directory. It is generated if missing, and will be
+generated again after every file change, by default.
+
+Dependency tracking is what allows Erlang.mk to know when to
+rebuild Erlang files when header files, behaviors or parse
+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 ===
+
+Generated source files are supported: they should be listed as
+dependencies to `$(PROJECT).d`:
+
+[source,make]
+----
+PROJECT = example
+
+.DEFAULT_GOAL = all
+
+$(PROJECT).d:: src/generated_mod.erl
+
+include erlang.mk
+
+src/generated_mod.erl::
+ $(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
+are reused in rebuilds, some are simply replaced. All can be
+removed safely.
+
+Erlang.mk provides two commands to remove them: `clean` and
+`distclean`. `clean` removes all the intermediate files that
+were created as a result of building, including the BEAM files,
+the dependency tracking file and the generated documentation.
+`distclean` removes these and more, including the downloaded
+dependencies, Dialyzer's PLT file and the generated release,
+putting your directory back to the state it was before you
+started working on it.
+
+To clean:
+
+[source,bash]
+$ make clean
+
+Or distclean:
+
+[source,bash]
+$ make distclean
+
+That is the question.
+
+Note that Erlang.mk will automatically clean some files as
+part of other targets, but it will never run `distclean` if
+you don't explicitly use it.
diff --git a/doc/src/guide/book.asciidoc b/doc/src/guide/book.asciidoc
new file mode 100644
index 0000000..e42e5bf
--- /dev/null
+++ b/doc/src/guide/book.asciidoc
@@ -0,0 +1,15 @@
+// a2x: --dblatex-opts "-P latex.output.revhistory=0 -P doc.publisher.show=0 -P index.numbered=0"
+// a2x: -d book --attribute tabsize=4
+
+= Erlang.mk User Guide
+Loïc Hoguin <[email protected]>
+
+include::getting_started.asciidoc[Getting started]
+
+include::overview.asciidoc[Overview]
+
+include::updating.asciidoc[Updating Erlang.mk]
+
+= Advanced
+
+include::external_plugins.asciidoc[External plugins]
diff --git a/doc/src/guide/external_plugins.asciidoc b/doc/src/guide/external_plugins.asciidoc
new file mode 100644
index 0000000..e5fdbd7
--- /dev/null
+++ b/doc/src/guide/external_plugins.asciidoc
@@ -0,0 +1,73 @@
+== External plugins
+
+It is often convenient to be able to keep the build files
+used by all your projects in one place. Those files could
+be Makefiles, configuration files, templates and more.
+
+Erlang.mk allows you to automatically load plugins from
+dependencies. Plugins can do anything, including defining
+new variables, defining file templates, hooking themselves
+inside the normal Erlang.mk processing or even adding new
+rules.
+
+You can load plugins using one of two methods. You can
+either load all plugins from a dependency, or just one.
+We will also cover conventions about writing external
+plugins.
+
+=== Loading all plugins from a dependency
+
+To load plugins from a dependency, all you need to do is add
+the dependency name to `DEP_PLUGINS` in addition to the list
+of dependencies.
+
+For example, if you have `cowboy` in `DEPS`, add `cowboy` in
+`DEP_PLUGINS` also:
+
+[source,make]
+DEPS = cowboy
+DEP_PLUGINS = cowboy
+
+This will load the file 'plugins.mk' in the root folder of
+the Cowboy repository.
+
+=== Loading one plugin from a dependency
+
+Now that we know how to load all plugins, let's take a look
+at how to load one specific plugin from a dependency.
+
+To do this, instead of writing only the name of the dependency,
+we will write its name and the path to the plugin file. This
+means that writing `DEP_PLUGINS = cowboy` is equivalent to
+writing `DEP_PLUGINS = cowboy/plugins.mk`.
+
+Knowing this, if we were to load the plugin 'mk/dist.mk'
+from Cowboy and no other, we would write the following in
+our Makefile:
+
+[source,make]
+DEPS = cowboy
+DEP_PLUGINS = cowboy/mk/dist.mk
+
+=== Writing external plugins
+
+The 'plugins.mk' file is a convention. It is meant to load
+all the plugins from the dependency. The code for the plugin
+can be written directly in 'plugins.mk' or be separate.
+
+If you are providing more than one plugin with your repository,
+the recommended way is to create one file per plugin in the
+'mk/' folder in your repository, and then include those
+individual plugins in 'plugins.mk'.
+
+For eaxmple, if you have two plugins 'mk/dist.mk' and
+'mk/templates.mk', you could write the following 'plugins.mk'
+file:
+
+[source,make]
+include mk/dist.mk
+include mk/templates.mk
+
+This allows users to not only be able to select individual
+plugins, but also select all plugins from the dependency
+in one go if they wish to do so.
diff --git a/doc/src/guide/getting_started.asciidoc b/doc/src/guide/getting_started.asciidoc
new file mode 100644
index 0000000..34280d1
--- /dev/null
+++ b/doc/src/guide/getting_started.asciidoc
@@ -0,0 +1,262 @@
+== Getting started
+
+This chapter explains how to get started using Erlang.mk.
+
+=== Creating a folder for your project
+
+The first step is always to create a new folder that will
+contain your project.
+
+[source,bash]
+$ mkdir hello_joe
+$ cd hello_joe
+
+Most people tend to put all their projects side by side in
+a common folder. We recommend keeping an organization similar
+to your remote repositories. For example, for GitHub users,
+put all your projects in a common folder with the same name
+as your username. For example '$HOME/ninenines/cowboy' for
+the Cowboy project.
+
+=== Downloading Erlang.mk
+
+At the time of writing, Erlang.mk is unlikely to be present
+in your Erlang distribution, or even in your OS packages.
+
+The next step is therefore to download it:
+
+[source,bash]
+$ wget https://raw.githubusercontent.com/ninenines/erlang.mk/master/erlang.mk
+
+Or:
+
+[source,bash]
+$ curl https://raw.githubusercontent.com/ninenines/erlang.mk/master/erlang.mk
+
+Alternatively, just https://raw.githubusercontent.com/ninenines/erlang.mk/master/erlang.mk[click on this link].
+
+Make sure you put the file inside the folder we created previously.
+
+=== Getting started with OTP applications
+
+An OTP application is an Erlang application that has a supervision
+tree. In other words, it will always have processes running.
+
+This kind of project can be automatically generated by Erlang.mk.
+All you need to do is use the `bootstrap` target:
+
+[source,bash]
+$ make -f erlang.mk bootstrap
+
+Something similar to the following snippet will then appear
+on your screen:
+
+[source,bash]
+----
+git clone https://github.com/ninenines/erlang.mk .erlang.mk.build
+Cloning into '.erlang.mk.build'...
+remote: Counting objects: 4035, done.
+remote: Compressing objects: 100% (12/12), done.
+remote: Total 4035 (delta 8), reused 4 (delta 4), pack-reused 4019
+Receiving objects: 100% (4035/4035), 1.10 MiB | 784.00 KiB/s, done.
+Resolving deltas: 100% (2442/2442), done.
+Checking connectivity... done.
+if [ -f build.config ]; then cp build.config .erlang.mk.build; fi
+cd .erlang.mk.build && make
+make[1]: Entering directory '/home/essen/tmp/hello_joe/.erlang.mk.build'
+awk 'FNR==1 && NR!=1{print ""}1' core/core.mk index/*.mk core/index.mk core/deps.mk plugins/protobuffs.mk core/erlc.mk core/docs.mk core/test.mk plugins/asciidoc.mk plugins/bootstrap.mk plugins/c_src.mk plugins/ci.mk plugins/ct.mk plugins/dialyzer.mk plugins/edoc.mk plugins/elvis.mk plugins/erlydtl.mk plugins/escript.mk plugins/eunit.mk plugins/relx.mk plugins/shell.mk plugins/triq.mk plugins/xref.mk plugins/cover.mk \
+ | sed 's/^ERLANG_MK_VERSION = .*/ERLANG_MK_VERSION = 1.2.0-642-gccd2b9f/' > erlang.mk
+make[1]: Leaving directory '/home/essen/tmp/hello_joe/.erlang.mk.build'
+cp .erlang.mk.build/erlang.mk ./erlang.mk
+rm -rf .erlang.mk.build
+----
+
+This is Erlang.mk bootstrapping itself. Indeed, the file you
+initially downloaded contains nothing more than the code needed
+to bootstrap. This operation is done only once. Consult the
+link:updating.asciidoc[Updating Erlang.mk] chapter for more
+information.
+
+Of course, the generated project can now be compiled:
+
+[source,bash]
+$ make
+
+Cheers!
+
+=== Getting started with OTP libraries
+
+An OTP library is an Erlang application that has no supervision
+tree. In other words, it is nothing but modules.
+
+This kind of project can also be generated by Erlang.mk, using
+the `bootstrap-lib` target:
+
+[source,bash]
+$ make -f erlang.mk bootstrap-lib
+
+Erlang.mk will once again bootstrap itself and generate all
+the files for your project. You can now compile it:
+
+[source,bash]
+$ make
+
+Enjoy!
+
+=== Getting started with OTP releases
+
+An OTP release is the combination of the Erlang RunTime System (ERTS)
+along with all the libraries and files that your node will need
+to run. It is entirely self contained, and can often be sent as-is
+to your production system and run without any extra setup.
+
+Erlang.mk can of course bootstrap your project to generate releases.
+You can use the `bootstrap-rel` target for this purpose:
+
+[source,bash]
+$ make bootstrap-rel
+
+This target can be combined with `bootstrap` or `bootstrap-lib` to
+create a project that will build a release:
+
+[source,bash]
+$ make -f erlang.mk bootstrap-lib bootstrap-rel
+
+It is often very useful to keep the top-level project for
+commands useful during operations, and put the components
+of the system in separate applications that you will then
+depend on. Consult the link:deps.asciidoc[Packages and dependencies]
+chapter for more information.
+
+When you run `make` from now on, Erlang.mk will compile your
+project and build the release:
+
+[source,bash]
+$ make
+ APP hello_joe.app.src
+ GEN distclean-relx-rel
+ GEN /home/essen/tmp/hello_joe/relx
+===> Starting relx build process ...
+===> Resolving OTP Applications from directories:
+ /home/essen/tmp/hello_joe/ebin
+ /usr/lib/erlang/lib
+ /home/essen/tmp/hello_joe/deps
+===> Resolved hello_joe_release-1
+===> Including Erts from /usr/lib/erlang
+===> release successfully created!
+
+The first time you run this command, Erlang.mk will download
+_relx_, the release building tool. So don't worry if you see
+more output than above.
+
+If building the release is slow, no need to upgrade your
+hardware just yet. Just consult the link:relx.asciidoc[Releases]
+chapter for various tips to speed up build time during
+development.
+
+You can start the release using the './_rel/hello_joe_release/bin/hello_joe_release'
+script, or simply run `make run`. The latter will also compile
+your project and build the release if it wasn't already:
+
+[source,bash]
+----
+$ make run
+ APP hello_joe.app.src
+ GEN distclean-relx-rel
+===> Starting relx build process ...
+===> Resolving OTP Applications from directories:
+ /home/essen/tmp/hello_joe/ebin
+ /usr/lib/erlang/lib
+ /home/essen/tmp/hello_joe/deps
+===> Resolved hello_joe_release-1
+===> Including Erts from /usr/lib/erlang
+===> release successfully created!
+Exec: /home/essen/tmp/hello_joe/_rel/hello_joe_release/erts-7.0/bin/erlexec -boot /home/essen/tmp/hello_joe/_rel/hello_joe_release/releases/1/hello_joe_release -boot_var ERTS_LIB_DIR /home/essen/tmp/hello_joe/_rel/hello_joe_release/erts-7.0/../lib -env ERL_LIBS /home/essen/tmp/hello_joe/_rel/hello_joe_release/releases/1/lib -config /home/essen/tmp/hello_joe/_rel/hello_joe_release/releases/1/sys.config -args_file /home/essen/tmp/hello_joe/_rel/hello_joe_release/releases/1/vm.args -- console
+Root: /home/essen/tmp/hello_joe/_rel/hello_joe_release
+/home/essen/tmp/hello_joe/_rel/hello_joe_release
+heart_beat_kill_pid = 16389
+Erlang/OTP 18 [erts-7.0] [source] [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false]
+
+Eshell V7.0 (abort with ^G)
+----
+
+Simple as that!
+
+=== Using templates
+
+It is no secret that Erlang's OTP behaviors tend to have some
+boilerplate. It is rarely an issue of course, except when
+creating new modules. That's why Erlang.mk not only comes with
+templates for generating projects, but also individual modules!
+
+You can list all available templates with the `list-templates`
+target:
+
+[source,bash]
+$ make list-templates
+Available templates: cowboy_http cowboy_loop cowboy_rest cowboy_ws gen_fsm gen_server ranch_protocol supervisor
+
+To generate a module, let's say a `gen_server`, all you need to
+do is to call `make new` with the appropriate arguments:
+
+[source,bash]
+$ make new t=gen_server n=my_server
+
+This will create a module located in 'src/my_server.erl'
+using the `gen_server` template.
+
+This module is automatically compiled the next time you run
+`make`:
+
+[source,bash]
+$ make
+ ERLC my_server.erl
+ APP hello_joe.app.src
+
+All that's left to do is to open it in your favorite editor
+and make it do something!
+
+=== Getting help
+
+During development, if you don't remember the name of a target,
+you can always run `make help`:
+
+[source,bash]
+----
+$ make help
+erlang.mk (version 1.2.0-642-gccd2b9f) is distributed under the terms of the ISC License.
+Copyright (c) 2013-2015 Loïc Hoguin <[email protected]>
+
+Usage: [V=1] make [target]...
+
+Core targets:
+ all Run deps, app and rel targets in that order
+ app Compile the project
+ deps Fetch dependencies (if needed) and compile them
+ search q=... Search for a package in the built-in index
+ rel Build a release for this project, if applicable
+ docs Build the documentation for this project
+ install-docs Install the man pages for this project
+ check Compile and run all tests and analysis for this project
+ tests Run the tests for this project
+ clean Delete temporary and output files from most targets
+ distclean Delete all temporary and output files
+ help Display this help and exit
+ erlang-mk Update erlang.mk to the latest version
+
+Bootstrap targets:
+ bootstrap Generate a skeleton of an OTP application
+ bootstrap-lib Generate a skeleton of an OTP library
+ bootstrap-rel Generate the files needed to build a release
+ new t=TPL n=NAME Generate a module NAME based on the template TPL
+ list-templates List available templates
+...
+----
+
+This guide should provide any other answer. If not, please
+open a ticket on https://github.com/ninenines/erlang.mk/issues[the official repository]
+and we will work on improving the guide.
+
+Commercial support is available through Nine Nines. Please contact
+Loïc Hoguin by sending an email to mailto:[email protected][].
diff --git a/doc/src/guide/overview.asciidoc b/doc/src/guide/overview.asciidoc
new file mode 100644
index 0000000..a81dd7a
--- /dev/null
+++ b/doc/src/guide/overview.asciidoc
@@ -0,0 +1,88 @@
+== Overview
+
+Now that you know how to get started, let's take a look at
+what Erlang.mk can do for you.
+
+=== Building your project
+
+Erlang.mk is first and foremost a build tool. It is especially
+tailored for Erlang developers and follows widely accepted
+practices in the Erlang community.
+
+Erlang.mk will happily build all link:app.asciidoc[Erlang-specific files]
+you throw at it. Other kinds of files too, like C or C++ code
+when you are working on link:ports.asciidoc[a NIF or a port driver].
+
+Erlang.mk embraces the concept of link:deps.asciidoc[source dependencies].
+It can fetch dependency source code using a variety of mechanisms,
+including fetching from Git, Mercurial or SVN.
+
+Erlang.mk will automatically link:relx.asciidoc[generate releases]
+when applicable. It can also link:escripts.asciidoc[generate escripts].
+
+=== Exploring the package index
+
+Erlang.mk comes with a link:deps.asciidoc[built-in package index].
+It is built as an extension of the dependency system and is
+meant to be used for discovery purposes.
+
+No package is ever installed, they are only used as dependencies
+and are always project-specific. They can be thought of as a
+shortcut over plain dependencies.
+
+You can get a list of all packages known to Erlang.mk by using
+the `search` target:
+
+[source,bash]
+$ make search
+
+You can also use this target to search across all packages, for
+example to find all packages related to Cowboy:
+
+[source,bash]
+$ make search q=cowboy
+
+=== Generating documentation
+
+Erlang.mk supports _EDoc_ and _Asciidoc_.
+
+link:edoc.asciidoc[EDoc] generates HTML documentation directly from
+your source code.
+
+While it is convenient, ask yourself: if all the documentation is
+inside the source code, why not just open the source code directly?
+That's where _Asciidoc_ comes in.
+
+The link:asciidoc.asciidoc[Asciidoc] plugin expects all documentation
+to be separate from source. It will generate HTML, PDF, man pages and
+more from the documentation you write in the 'doc/src/' folder in
+your repository.
+
+=== Running tests
+
+Erlang.mk supports a lot of different testing and static
+analysis tools.
+
+The link:shell.asciidoc[make shell] command allows you
+to test your project manually. You can automate these
+unit tests with link:eunit.asciidoc[EUnit] and test
+your entire system with link:common_test.asciidoc[Common Test].
+link:property_based_testing.asciidoc[Property based testing]
+with Triq is a strong alternative to writing unit tests
+manually. link:coverage.asciidoc[Code coverage] can of course
+be enabled during tests.
+
+Erlang.mk comes with features to make your life easier when
+setting up and using link:ci.asciidoc[Continuous integration].
+
+On the static analysis side of things, Erlang.mk comes with
+support for link:dialyzer.asciidoc[Dialyzer], link:xref.asciidoc[Xref]
+and link:elvis.asciidoc[Elvis], performing success typing
+analysis, cross reference and style reviewing.
+
+=== Need more?
+
+Not convinced yet? You can read about link:why.asciidoc[why you should use Erlang.mk]
+and its link:history.asciidoc[history]. And if you're still not
+convinced after that, it's OK! The world would be boring if
+everyone agreed on everything all the time.
diff --git a/doc/src/guide/updating.asciidoc b/doc/src/guide/updating.asciidoc
new file mode 100644
index 0000000..ecd4a70
--- /dev/null
+++ b/doc/src/guide/updating.asciidoc
@@ -0,0 +1,62 @@
+== Updating Erlang.mk
+
+This chapter describes how to update the 'erlang.mk' file
+in your repository.
+
+=== Initial bootstrap
+
+The first time you use Erlang.mk, it will bootstrap itself.
+It always uses the most recent version for this, so you don't
+have to update after creating your project.
+
+=== Updating
+
+Later on though, updating becomes a necessity. Erlang.mk
+developers and contributors relentlessly improve the project
+and add new features; it would be a waste not to benefit
+from this.
+
+That's why updating Erlang.mk is so simple. All you need
+to do is to call `make erlang-mk`:
+
+[source,bash]
+----
+$ make erlang-mk
+git clone https://github.com/ninenines/erlang.mk .erlang.mk.build
+Cloning into '.erlang.mk.build'...
+remote: Counting objects: 4035, done.
+remote: Compressing objects: 100% (12/12), done.
+remote: Total 4035 (delta 8), reused 4 (delta 4), pack-reused 4019
+Receiving objects: 100% (4035/4035), 1.10 MiB | 1000.00 KiB/s, done.
+Resolving deltas: 100% (2442/2442), done.
+Checking connectivity... done.
+if [ -f build.config ]; then cp build.config .erlang.mk.build; fi
+cd .erlang.mk.build && make
+make[1]: Entering directory '/home/essen/tmp/emkg/hello_joe/.erlang.mk.build'
+awk 'FNR==1 && NR!=1{print ""}1' core/core.mk index/*.mk core/index.mk core/deps.mk plugins/protobuffs.mk core/erlc.mk core/docs.mk core/test.mk plugins/asciidoc.mk plugins/bootstrap.mk plugins/c_src.mk plugins/ci.mk plugins/ct.mk plugins/dialyzer.mk plugins/edoc.mk plugins/elvis.mk plugins/erlydtl.mk plugins/escript.mk plugins/eunit.mk plugins/relx.mk plugins/shell.mk plugins/triq.mk plugins/xref.mk plugins/cover.mk \
+ | sed 's/^ERLANG_MK_VERSION = .*/ERLANG_MK_VERSION = 1.2.0-642-gccd2b9f/' > erlang.mk
+make[1]: Leaving directory '/home/essen/tmp/emkg/hello_joe/.erlang.mk.build'
+cp .erlang.mk.build/erlang.mk ./erlang.mk
+rm -rf .erlang.mk.build
+----
+
+All that's left to do is to commit the file!
+
+Yep, it's that easy.
+
+=== Customizing the build
+
+Erlang.mk allows you to customize which plugins are to be included
+in the 'erlang.mk' file. You can do so by maintaining your own
+'build.config' file in your repository. Erlang.mk will automatically
+use it the next time you run `make erlang-mk`.
+
+The 'build.config' file contains the list of all files that will
+be built into the resulting 'erlang.mk' file. You can start from
+the https://github.com/ninenines/erlang.mk/blob/master/build.config[most recent version]
+and customize to your needs.
+
+You can also name the file differently or put it in a separate folder
+by modifying the value for `ERLANG_MK_BUILD_CONFIG`. You can also
+tell Erlang.mk to use a different temporary directory by changing
+the `ERLANG_MK_BUILD_DIR` variable.
diff --git a/index/riak_control.mk b/index/riak_control.mk
new file mode 100644
index 0000000..49d11a3
--- /dev/null
+++ b/index/riak_control.mk
@@ -0,0 +1,7 @@
+PACKAGES += riak_control
+pkg_riak_control_name = riak_control
+pkg_riak_control_description = Webmachine-based administration interface for Riak.
+pkg_riak_control_homepage = https://github.com/basho/riak_control
+pkg_riak_control_fetch = git
+pkg_riak_control_repo = https://github.com/basho/riak_control
+pkg_riak_control_commit = master
diff --git a/plugins/elvis.mk b/plugins/elvis.mk
index 3f4419a..58823b5 100644
--- a/plugins/elvis.mk
+++ b/plugins/elvis.mk
@@ -10,8 +10,8 @@ ELVIS_CONFIG ?= $(CURDIR)/elvis.config
ELVIS ?= $(CURDIR)/elvis
export ELVIS
-ELVIS_URL ?= https://github.com/inaka/elvis/releases/download/0.2.5-beta2/elvis
-ELVIS_CONFIG_URL ?= https://github.com/inaka/elvis/releases/download/0.2.5-beta2/elvis.config
+ELVIS_URL ?= https://github.com/inaka/elvis/releases/download/0.2.5/elvis
+ELVIS_CONFIG_URL ?= https://github.com/inaka/elvis/releases/download/0.2.5/elvis.config
ELVIS_OPTS ?=
# Core targets.
diff --git a/plugins/erlydtl.mk b/plugins/erlydtl.mk
index 46a3080..1f33bc6 100644
--- a/plugins/erlydtl.mk
+++ b/plugins/erlydtl.mk
@@ -4,6 +4,8 @@
# Configuration.
DTL_FULL_PATH ?= 0
+DTL_PATH ?= templates/
+DTL_SUFFIX ?= _dtl
# Verbosity.
@@ -12,20 +14,26 @@ dtl_verbose = $(dtl_verbose_$(V))
# Core targets.
-define compile_erlydtl
- $(dtl_verbose) $(ERL) -pa ebin/ $(DEPS_DIR)/erlydtl/ebin/ -eval ' \
- Compile = fun(F) -> \
- S = fun (1) -> re:replace(filename:rootname(string:sub_string(F, 11), ".dtl"), "/", "_", [{return, list}, global]); \
- (0) -> filename:basename(F, ".dtl") \
- end, \
- Module = list_to_atom(string:to_lower(S($(DTL_FULL_PATH))) ++ "_dtl"), \
- {ok, _} = erlydtl:compile(F, Module, [{out_dir, "ebin/"}, return_errors, {doc_root, "templates"}]) \
- end, \
- _ = [Compile(F) || F <- string:tokens("$(1)", " ")], \
- halt().'
+define erlydtl_compile.erl
+ [begin
+ Module0 = case $(DTL_FULL_PATH) of
+ 0 ->
+ filename:basename(F, ".dtl");
+ 1 ->
+ "$(DTL_PATH)" ++ F2 = filename:rootname(F, ".dtl"),
+ re:replace(F2, "/", "_", [{return, list}, global])
+ end,
+ Module = list_to_atom(string:to_lower(Module0) ++ "$(DTL_SUFFIX)"),
+ case erlydtl:compile(F, Module, [{out_dir, "ebin/"}, return_errors, {doc_root, "templates"}]) of
+ ok -> ok;
+ {ok, _} -> ok
+ end
+ end || F <- string:tokens("$(1)", " ")],
+ halt().
endef
ifneq ($(wildcard src/),)
-ebin/$(PROJECT).app:: $(sort $(call core_find,templates/,*.dtl))
- $(if $(strip $?),$(call compile_erlydtl,$?))
+ebin/$(PROJECT).app:: $(sort $(call core_find,$(DTL_PATH),*.dtl))
+ $(if $(strip $?),\
+ $(dtl_verbose) $(call erlang,$(call erlydtl_compile.erl,$?,-pa ebin/ $(DEPS_DIR)/erlydtl/ebin/)))
endif
diff --git a/plugins/triq.mk b/plugins/triq.mk
index 77775f6..f316c1b 100644
--- a/plugins/triq.mk
+++ b/plugins/triq.mk
@@ -1,7 +1,7 @@
# Copyright (c) 2015, Loïc Hoguin <[email protected]>
# This file is part of erlang.mk and subject to the terms of the ISC License.
-ifneq ($(wildcard $(DEPS_DIR)/triq),)
+ifeq ($(filter triq,$(DEPS) $(TEST_DEPS)),triq)
.PHONY: triq
# Targets.