# Copyright (c) 2013-2014, Loïc Hoguin # # Permission to use, copy, modify, and/or distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .PHONY: all deps app rel docs tests clean distclean help ERLANG_MK_VERSION = 1 # Core configuration. PROJECT ?= $(notdir $(CURDIR)) # Verbosity. V ?= 0 gen_verbose_0 = @echo " GEN " $@; gen_verbose = $(gen_verbose_$(V)) # Core targets. all:: deps app rel clean:: $(gen_verbose) rm -f erl_crash.dump distclean:: clean help:: @printf "%s\n" \ "erlang.mk (version $(ERLANG_MK_VERSION)) is distributed under the terms of the ISC License." \ "Copyright (c) 2013-2014 Loïc Hoguin " \ "" \ "Usage: [V=1] make [target]" \ "" \ "Core targets:" \ " all Run deps, app and rel targets in that order" \ " deps Fetch dependencies (if needed) and compile them" \ " app Compile the project" \ " rel Build a release for this project, if applicable" \ " docs Build the documentation 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" \ "" \ "The target clean only removes files that are commonly removed." \ "Dependencies and releases are left untouched." \ "" \ "Setting V=1 when calling make enables verbose mode." # Core functions. define core_http_get wget --no-check-certificate -O $(1) $(2)|| rm $(1) endef # Copyright (c) 2013-2014, Loïc Hoguin # This file is part of erlang.mk and subject to the terms of the ISC License. .PHONY: distclean-deps distclean-pkg pkg-list pkg-search # Configuration. DEPS_DIR ?= $(CURDIR)/deps export DEPS_DIR REBAR_DEPS_DIR = $(DEPS_DIR) export REBAR_DEPS_DIR ALL_DEPS_DIRS = $(addprefix $(DEPS_DIR)/,$(DEPS)) ifeq ($(filter $(DEPS_DIR),$(subst :, ,$(ERL_LIBS))),) ifeq ($(ERL_LIBS),) ERL_LIBS = $(DEPS_DIR) else ERL_LIBS := $(ERL_LIBS):$(DEPS_DIR) endif endif export ERL_LIBS PKG_FILE ?= $(CURDIR)/.erlang.mk.packages.v1 export PKG_FILE PKG_FILE_URL ?= https://raw.githubusercontent.com/extend/erlang.mk/master/packages.v1.tsv # Core targets. deps:: $(ALL_DEPS_DIRS) @for dep in $(ALL_DEPS_DIRS) ; do \ if [ -f $$dep/Makefile ] ; then \ $(MAKE) -C $$dep ; \ else \ echo "include $(CURDIR)/erlang.mk" | $(MAKE) -f - -C $$dep ; \ fi ; \ done distclean:: distclean-deps distclean-pkg # Deps related targets. define dep_fetch @mkdir -p $(DEPS_DIR) ifeq (,$(findstring pkg://,$(word 1,$(dep_$(1))))) git clone -n -- $(word 1,$(dep_$(1))) $(DEPS_DIR)/$(1) else @if [ ! -f $(PKG_FILE) ]; then $(call get_pkg_file); fi git clone -n -- `awk 'BEGIN { FS = "\t" }; \ $$$$1 == "$(subst pkg://,,$(word 1,$(dep_$(1))))" { print $$$$2 }' \ $(PKG_FILE)` $(DEPS_DIR)/$(1) endif cd $(DEPS_DIR)/$(1) ; git checkout -q $(word 2,$(dep_$(1))) endef define dep_target $(DEPS_DIR)/$(1): $(call dep_fetch,$(1)) endef $(foreach dep,$(DEPS),$(eval $(call dep_target,$(dep)))) distclean-deps: $(gen_verbose) rm -rf $(DEPS_DIR) # Packages related targets. $(PKG_FILE): $(call core_http_get,$(PKG_FILE),$(PKG_FILE_URL)) pkg-list: $(PKG_FILE) @cat $(PKG_FILE) | awk 'BEGIN { FS = "\t" }; { print \ "Name:\t\t" $$1 "\n" \ "Repository:\t" $$2 "\n" \ "Website:\t" $$3 "\n" \ "Description:\t" $$4 "\n" }' ifdef q pkg-search: $(PKG_FILE) @cat $(PKG_FILE) | grep -i ${q} | awk 'BEGIN { FS = "\t" }; { print \ "Name:\t\t" $$1 "\n" \ "Repository:\t" $$2 "\n" \ "Website:\t" $$3 "\n" \ "Description:\t" $$4 "\n" }' else pkg-search: @echo "Usage: make pkg-search q=STRING" endif distclean-pkg: $(gen_verbose) rm -f $(PKG_FILE) help:: @printf "%s\n" "" \ "Package-related targets:" \ " pkg-list List all known packages" \ " pkg-search q=STRING Search for STRING in the package index" # Copyright (c) 2013-2014, Loïc Hoguin # This file is part of erlang.mk and subject to the terms of the ISC License. .PHONY: clean-app # Configuration. ERLC_OPTS ?= -Werror +debug_info +warn_export_all +warn_export_vars \ +warn_shadow_vars +warn_obsolete_guard # +bin_opt_info +warn_missing_spec COMPILE_FIRST ?= COMPILE_FIRST_PATHS = $(addprefix src/,$(addsuffix .erl,$(COMPILE_FIRST))) # Verbosity. appsrc_verbose_0 = @echo " APP " $(PROJECT).app.src; appsrc_verbose = $(appsrc_verbose_$(V)) erlc_verbose_0 = @echo " ERLC " $(filter %.erl %.core,$(?F)); erlc_verbose = $(erlc_verbose_$(V)) xyrl_verbose_0 = @echo " XYRL " $(filter %.xrl %.yrl,$(?F)); xyrl_verbose = $(xyrl_verbose_$(V)) # Core targets. app:: ebin/$(PROJECT).app $(eval MODULES := $(shell find ebin -type f -name \*.beam \ | sed "s/ebin\//'/;s/\.beam/',/" | sed '$$s/.$$//')) $(appsrc_verbose) cat src/$(PROJECT).app.src \ | sed "s/{modules,[[:space:]]*\[\]}/{modules, \[$(MODULES)\]}/" \ > ebin/$(PROJECT).app define compile_erl $(erlc_verbose) erlc -v $(ERLC_OPTS) -o ebin/ \ -pa ebin/ -I include/ $(COMPILE_FIRST_PATHS) $(1) endef define compile_xyrl $(xyrl_verbose) erlc -v -o ebin/ $(1) $(xyrl_verbose) erlc $(ERLC_OPTS) -o ebin/ ebin/*.erl @rm ebin/*.erl endef ifneq ($(wildcard src/),) ebin/$(PROJECT).app:: @mkdir -p ebin/ ebin/$(PROJECT).app:: $(shell find src -type f -name \*.erl) \ $(shell find src -type f -name \*.core) $(if $(strip $?),$(call compile_erl,$?)) ebin/$(PROJECT).app:: $(shell find src -type f -name \*.xrl) \ $(shell find src -type f -name \*.yrl) $(if $(strip $?),$(call compile_xyrl,$?)) endif clean:: clean-app # Extra targets. clean-app: $(gen_verbose) rm -rf ebin/ # Copyright (c) 2014, Loïc Hoguin # This file is part of erlang.mk and subject to the terms of the ISC License. .PHONY: clean-c_src # todo # Configuration. C_SRC_DIR = $(CURDIR)/c_src C_SRC_ENV ?= $(C_SRC_DIR)/env.mk C_SRC_OPTS ?= C_SRC_OUTPUT ?= $(CURDIR)/priv/$(PROJECT).so # Verbosity. c_src_verbose_0 = @echo " C_SRC " $(?F); c_src_verbose = $(appsrc_verbose_$(V)) # Targets. ifeq ($(wildcard $(C_SRC_DIR)/Makefile),) app:: $(C_SRC_ENV) @mkdir -p priv/ $(c_src_verbose) gcc $(C_SRC_DIR)/*.c -fPIC -shared -o $(C_SRC_OUTPUT) \ -I $(ERTS_INCLUDE_DIR) $(C_SRC_OPTS) $(C_SRC_ENV): erl -noshell -noinput -eval "file:write_file(\"$(C_SRC_ENV)\", \ io_lib:format(\"ERTS_INCLUDE_DIR ?= ~s/erts-~s/include/\", \ [code:root_dir(), erlang:system_info(version)])), \ init:stop()." -include $(C_SRC_ENV) else ifneq ($(wildcard $(C_SRC_DIR),)) app:: $(MAKE) -C $(C_SRC_DIR) clean:: $(MAKE) -C $(C_SRC_DIR) clean endif endif clean:: clean-c_src clean-c_src: $(gen_verbose) rm -f $(C_SRC_ENV) $(C_SRC_OUTPUT) # Copyright (c) 2013-2014, Loïc Hoguin # This file is part of erlang.mk and subject to the terms of the ISC License. .PHONY: build-ct-deps build-ct-suites tests-ct clean-ct distclean-ct # Configuration. CT_OPTS ?= CT_SUITES ?= TEST_ERLC_OPTS ?= +debug_info +warn_export_vars +warn_shadow_vars +warn_obsolete_guard TEST_ERLC_OPTS += -DTEST=1 -DEXTRA=1 +'{parse_transform, eunit_autoexport}' # Core targets. tests:: tests-ct clean:: clean-ct distclean:: distclean-ct help:: @printf "%s\n" "" \ "All your common_test suites have their associated targets." \ "A suite named http_SUITE can be ran using the ct-http target." # Plugin-specific targets. ALL_TEST_DEPS_DIRS = $(addprefix $(DEPS_DIR)/,$(TEST_DEPS)) CT_RUN = ct_run \ -no_auto_compile \ -noshell \ -pa $(realpath ebin) $(DEPS_DIR)/*/ebin \ -dir test \ -logdir logs $(foreach dep,$(TEST_DEPS),$(eval $(call dep_target,$(dep)))) build-ct-deps: $(ALL_TEST_DEPS_DIRS) @for dep in $(ALL_TEST_DEPS_DIRS) ; do $(MAKE) -C $$dep; done build-ct-suites: build-ct-deps $(gen_verbose) erlc -v $(TEST_ERLC_OPTS) -o test/ \ $(wildcard test/*.erl test/*/*.erl) -pa ebin/ tests-ct: ERLC_OPTS = $(TEST_ERLC_OPTS) tests-ct: clean deps app build-ct-suites @if [ -d "test" ] ; \ then \ mkdir -p logs/ ; \ $(CT_RUN) -suite $(addsuffix _SUITE,$(CT_SUITES)) $(CT_OPTS) ; \ fi $(gen_verbose) rm -f test/*.beam define ct_suite_target ct-$(1): ERLC_OPTS = $(TEST_ERLC_OPTS) ct-$(1): clean deps app build-tests @if [ -d "test" ] ; \ then \ mkdir -p logs/ ; \ $(CT_RUN) -suite $(addsuffix _SUITE,$(1)) $(CT_OPTS) ; \ fi $(gen_verbose) rm -f test/*.beam endef $(foreach test,$(CT_SUITES),$(eval $(call ct_suite_target,$(test)))) clean-ct: $(gen_verbose) rm -rf test/*.beam distclean-ct: $(gen_verbose) rm -rf logs/ # Copyright (c) 2013-2014, Loïc Hoguin # This file is part of erlang.mk and subject to the terms of the ISC License. .PHONY: plt distclean-plt dialyze # Configuration. DIALYZER_PLT ?= $(CURDIR)/.$(PROJECT).plt export DIALYZER_PLT PLT_APPS ?= DIALYZER_OPTS ?= -Werror_handling -Wrace_conditions \ -Wunmatched_returns # -Wunderspecs # Core targets. distclean:: distclean-plt help:: @printf "%s\n" "" \ "Dialyzer targets:" \ " plt Build a PLT file for this project" \ " dialyze Analyze the project using Dialyzer" # Plugin-specific targets. plt: deps app @dialyzer --build_plt --apps erts kernel stdlib $(PLT_APPS) $(ALL_DEPS_DIRS) distclean-plt: $(gen_verbose) rm -f $(DIALYZER_PLT) dialyze: @dialyzer --src src --no_native $(DIALYZER_OPTS) # Copyright (c) 2013-2014, Loïc Hoguin # This file is part of erlang.mk and subject to the terms of the ISC License. # Verbosity. dtl_verbose_0 = @echo " DTL " $(filter %.dtl,$(?F)); dtl_verbose = $(dtl_verbose_$(V)) # Core targets. define compile_erlydtl $(dtl_verbose) erl -noshell -pa ebin/ $(DEPS_DIR)/erlydtl/ebin/ -eval ' \ Compile = fun(F) -> \ Module = list_to_atom( \ string:to_lower(filename:basename(F, ".dtl")) ++ "_dtl"), \ erlydtl:compile(F, Module, [{out_dir, "ebin/"}]) \ end, \ _ = [Compile(F) || F <- string:tokens("$(1)", " ")], \ init:stop()' endef ifneq ($(wildcard src/),) ebin/$(PROJECT).app:: $(shell find templates -type f -name \*.dtl 2>/dev/null) $(if $(strip $?),$(call compile_erlydtl,$?)) endif # Copyright (c) 2013-2014, Loïc Hoguin # This file is part of erlang.mk and subject to the terms of the ISC License. .PHONY: distclean-edoc # Configuration. EDOC_OPTS ?= # Core targets. docs:: distclean-edoc $(gen_verbose) erl -noshell \ -eval 'edoc:application($(PROJECT), ".", [$(EDOC_OPTS)]), init:stop().' distclean:: distclean-edoc # Plugin-specific targets. distclean-edoc: $(gen_verbose) rm -f doc/*.css doc/*.html doc/*.png doc/edoc-info # Copyright (c) 2013-2014, Loïc Hoguin # This file is part of erlang.mk and subject to the terms of the ISC License. .PHONY: distclean-rel # Configuration. RELX_CONFIG ?= $(CURDIR)/relx.config ifneq ($(wildcard $(RELX_CONFIG)),) RELX ?= $(CURDIR)/relx export RELX RELX_URL ?= https://github.com/erlware/relx/releases/download/v1.0.2/relx RELX_OPTS ?= RELX_OUTPUT_DIR ?= _rel ifneq ($(firstword $(subst -o,,$(RELX_OPTS))),) RELX_OUTPUT_DIR = $(firstword $(subst -o,,$(RELX_OPTS))) endif # Core targets. rel:: distclean-rel $(RELX) @$(RELX) -c $(RELX_CONFIG) $(RELX_OPTS) distclean:: distclean-rel # Plugin-specific targets. define relx_fetch $(call core_http_get,$(RELX),$(RELX_URL)) chmod +x $(RELX) endef $(RELX): @$(call relx_fetch) distclean-rel: $(gen_verbose) rm -rf $(RELX_OUTPUT_DIR) endif