diff options
-rw-r--r-- | .gitignore | 2 | ||||
-rw-r--r-- | Makefile | 3 | ||||
-rw-r--r-- | README.md | 2 | ||||
-rw-r--r-- | core/core.mk | 12 | ||||
-rw-r--r-- | core/deps.mk | 18 | ||||
-rw-r--r-- | core/erlc.mk | 155 | ||||
-rw-r--r-- | doc/src/guide/app.asciidoc | 290 | ||||
-rw-r--r-- | doc/src/guide/book.asciidoc | 15 | ||||
-rw-r--r-- | doc/src/guide/external_plugins.asciidoc | 73 | ||||
-rw-r--r-- | doc/src/guide/getting_started.asciidoc | 262 | ||||
-rw-r--r-- | doc/src/guide/overview.asciidoc | 88 | ||||
-rw-r--r-- | doc/src/guide/updating.asciidoc | 62 | ||||
-rw-r--r-- | index/riak_control.mk | 7 | ||||
-rw-r--r-- | plugins/elvis.mk | 4 | ||||
-rw-r--r-- | plugins/erlydtl.mk | 34 | ||||
-rw-r--r-- | plugins/triq.mk | 2 |
16 files changed, 962 insertions, 67 deletions
@@ -1,3 +1,5 @@ +doc/guide.pdf +doc/html test/app1/ test/pkgs.log test/temp-python/ @@ -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 @@ -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) +([email protected])1> +---- + +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. |