Browse Source

Maintenance

* Update copyright years.
* Update erlang.mk.
* Fix triq testing.
Loïc Hoguin 10 years ago
parent
commit
6f9b4bbc41
15 changed files with 628 additions and 214 deletions
  1. 1 1
      LICENSE
  2. 11 3
      Makefile
  3. 605 73
      erlang.mk
  4. 1 1
      include/cow_inline.hrl
  5. 1 1
      src/cow_cookie.erl
  6. 1 5
      src/cow_date.erl
  7. 1 5
      src/cow_http.erl
  8. 1 65
      src/cow_http_hd.erl
  9. 1 6
      src/cow_http_te.erl
  10. 1 1
      src/cow_mimetypes.erl
  11. 1 11
      src/cow_multipart.erl
  12. 1 9
      src/cow_qs.erl
  13. 1 1
      src/cow_spdy.erl
  14. 1 1
      src/cowlib.app.src
  15. 0 31
      test/eunit_SUITE.erl

+ 1 - 1
LICENSE

@@ -1,4 +1,4 @@
-Copyright (c) 2013-2014, Loïc Hoguin <essen@ninenines.eu>
+Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
 
 Permission to use, copy, modify, and/or distribute this software for any
 purpose with or without fee is hereby granted, provided that the above

+ 11 - 3
Makefile

@@ -1,8 +1,13 @@
 # See LICENSE for licensing information.
 
 PROJECT = cowlib
+#ERLC_OPTS += +bin_opt_info
+TEST_ERLC_OPTS += +'{parse_transform, eunit_autoexport}' +'{parse_transform, horse_autoexport}'
 PLT_APPS = crypto
 
+TEST_DEPS = triq
+dep_triq = git https://github.com/krestenkrab/triq master
+
 include erlang.mk
 
 .PHONY: gen perfs
@@ -32,12 +37,15 @@ gen:
 
 # Performance testing.
 
+ifeq ($(MAKECMDGOALS),perfs)
+.NOTPARALLEL:
+endif
+
 deps/horse:
 	git clone -n -- https://github.com/extend/horse $(DEPS_DIR)/horse
 	cd $(DEPS_DIR)/horse ; git checkout -q master
 	$(MAKE) -C $(DEPS_DIR)/horse
 
-perfs: ERLC_OPTS += -DPERF=1 +'{parse_transform, horse_autoexport}' -DEXTRA=1
-perfs: clean deps deps/horse app
+perfs: test-build
 	$(gen_verbose) erl -noshell -pa ebin deps/horse/ebin \
-		-eval 'horse:app_perf($(PROJECT)), init:stop().'
+		-eval 'horse:app_perf($(PROJECT)), erlang:halt().'

+ 605 - 73
erlang.mk

@@ -1,4 +1,4 @@
-# Copyright (c) 2013-2014, Loïc Hoguin <essen@ninenines.eu>
+# Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
 #
 # Permission to use, copy, modify, and/or distribute this software for any
 # purpose with or without fee is hereby granted, provided that the above
@@ -28,12 +28,30 @@ V ?= 0
 gen_verbose_0 = @echo " GEN   " $@;
 gen_verbose = $(gen_verbose_$(V))
 
+# "erl" command.
+
+ERL = erl +A0 -noinput -boot start_clean
+
 # Core targets.
 
-all:: deps app rel
+ifneq ($(words $(MAKECMDGOALS)),1)
+.NOTPARALLEL:
+endif
 
-clean::
+all:: deps
+	@$(MAKE) --no-print-directory app
+	@$(MAKE) --no-print-directory rel
+
+# Noop to avoid a Make warning when there's nothing to do.
+rel::
+	@echo -n
+
+clean:: clean-crashdump
+
+clean-crashdump:
+ifneq ($(wildcard erl_crash.dump),)
 	$(gen_verbose) rm -f erl_crash.dump
+endif
 
 distclean:: clean
 
@@ -42,7 +60,7 @@ help::
 		"erlang.mk (version $(ERLANG_MK_VERSION)) is distributed under the terms of the ISC License." \
 		"Copyright (c) 2013-2014 Loïc Hoguin <essen@ninenines.eu>" \
 		"" \
-		"Usage: [V=1] make [target]" \
+		"Usage: [V=1] make [-jNUM] [target]" \
 		"" \
 		"Core targets:" \
 		"  all         Run deps, app and rel targets in that order" \
@@ -58,7 +76,8 @@ help::
 		"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."
+		"Setting V=1 when calling make enables verbose mode." \
+		"Parallel execution is supported through the -j Make flag."
 
 # Core functions.
 
@@ -68,7 +87,7 @@ define core_http_get
 endef
 else
 define core_http_get
-	erl -noshell -eval 'ssl:start(), inets:start(), case httpc:request(get, {"$(2)", []}, [{autoredirect, true}], []) of {ok, {{_, 200, _}, _, Body}} -> case file:write_file("$(1)", Body) of ok -> ok; {error, R1} -> halt(R1) end; {error, R2} -> halt(R2) end, halt(0).'
+	$(ERL) -eval 'ssl:start(), inets:start(), case httpc:request(get, {"$(2)", []}, [{autoredirect, true}], []) of {ok, {{_, 200, _}, _, Body}} -> case file:write_file("$(1)", Body) of ok -> ok; {error, R1} -> halt(R1) end; {error, R2} -> halt(R2) end, halt(0).'
 endef
 endif
 
@@ -84,13 +103,16 @@ erlang-mk:
 	cp $(ERLANG_MK_BUILD_DIR)/erlang.mk ./erlang.mk
 	rm -rf $(ERLANG_MK_BUILD_DIR)
 
-# Copyright (c) 2013-2014, Loïc Hoguin <essen@ninenines.eu>
+# Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
 # 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.
 
+AUTOPATCH ?= edown gen_leader gproc
+export AUTOPATCH
+
 DEPS_DIR ?= $(CURDIR)/deps
 export DEPS_DIR
 
@@ -128,6 +150,41 @@ distclean:: distclean-deps distclean-pkg
 
 # Deps related targets.
 
+define dep_autopatch
+	$(ERL) -eval " \
+DepDir = \"$(DEPS_DIR)/$(1)/\", \
+fun() -> \
+	{ok, Conf} = file:consult(DepDir ++ \"rebar.config\"), \
+	File = case lists:keyfind(deps, 1, Conf) of false -> []; {_, Deps} -> \
+		[begin {Method, Repo, Commit} = case Repos of \
+			{git, R} -> {git, R, master}; \
+			{M, R, {branch, C}} -> {M, R, C}; \
+			{M, R, {tag, C}} -> {M, R, C}; \
+			{M, R, C} -> {M, R, C} \
+		end, \
+		io_lib:format(\"DEPS += ~s\ndep_~s = ~s ~s ~s~n\", [Name, Name, Method, Repo, Commit]) \
+		end || {Name, _, Repos} <- Deps] \
+	end, \
+	ok = file:write_file(\"$(DEPS_DIR)/$(1)/Makefile\", [\"ERLC_OPTS = +debug_info\n\n\", File, \"\ninclude erlang.mk\"]) \
+end(), \
+AppSrcOut = \"$(DEPS_DIR)/$(1)/src/$(1).app.src\", \
+AppSrcIn = case filelib:is_regular(AppSrcOut) of false -> \"$(DEPS_DIR)/$(1)/ebin/$(1).app\"; true -> AppSrcOut end, \
+fun() -> \
+	{ok, [{application, $(1), L}]} = file:consult(AppSrcIn), \
+	L2 = case lists:keyfind(modules, 1, L) of {_, _} -> L; false -> [{modules, []}|L] end, \
+	L3 = case lists:keyfind(vsn, 1, L2) of {vsn, git} -> lists:keyreplace(vsn, 1, L2, {vsn, \"git\"}); _ -> L2 end, \
+	ok = file:write_file(AppSrcOut, io_lib:format(\"~p.~n\", [{application, $(1), L3}])) \
+end(), \
+case AppSrcOut of AppSrcIn -> ok; _ -> ok = file:delete(AppSrcIn) end, \
+halt()."
+endef
+
+ifeq ($(V),0)
+define dep_autopatch_verbose
+	@echo " PATCH " $(1);
+endef
+endif
+
 define dep_fetch
 	if [ "$$$$VS" = "git" ]; then \
 		git clone -n -- $$$$REPO $(DEPS_DIR)/$(1); \
@@ -135,6 +192,8 @@ define dep_fetch
 	elif [ "$$$$VS" = "hg" ]; then \
 		hg clone -U $$$$REPO $(DEPS_DIR)/$(1); \
 		cd $(DEPS_DIR)/$(1) && hg update -q $$$$COMMIT; \
+	elif [ "$$$$VS" = "svn" ]; then \
+		svn checkout $$$$REPO $(DEPS_DIR)/$(1); \
 	else \
 		echo "Unknown or invalid dependency: $(1). Please consult the erlang.mk README for instructions." >&2; \
 		exit 78; \
@@ -157,6 +216,15 @@ else
 	COMMIT=$(word 3,$(dep_$(1))); \
 	$(call dep_fetch,$(1))
 endif
+ifneq ($(filter $(1),$(AUTOPATCH)),)
+	$(call dep_autopatch_verbose,$(1)) if [ -f $(DEPS_DIR)/$(1)/rebar.config ]; then \
+		$(call dep_autopatch,$(1)); \
+		cd $(DEPS_DIR)/$(1)/ && ln -s ../../erlang.mk; \
+	elif [ ! -f $(DEPS_DIR)/$(1)/Makefile ]; then \
+		echo "ERLC_OPTS = +debug_info\ninclude erlang.mk" > $(DEPS_DIR)/$(1)/Makefile; \
+		cd $(DEPS_DIR)/$(1)/ && ln -s ../../erlang.mk; \
+	fi
+endif
 endef
 
 $(foreach dep,$(DEPS),$(eval $(call dep_target,$(dep))))
@@ -199,32 +267,48 @@ help::
 		"  pkg-list              List all known packages" \
 		"  pkg-search q=STRING   Search for STRING in the package index"
 
-# Copyright (c) 2013-2014, Loïc Hoguin <essen@ninenines.eu>
+# Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
 # 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
+ERLC_OPTS ?= -Werror +debug_info +warn_export_vars +warn_shadow_vars \
+	+warn_obsolete_guard # +bin_opt_info +warn_export_all +warn_missing_spec
 COMPILE_FIRST ?=
 COMPILE_FIRST_PATHS = $(addprefix src/,$(addsuffix .erl,$(COMPILE_FIRST)))
+ERLC_EXCLUDE ?=
+ERLC_EXCLUDE_PATHS = $(addprefix src/,$(addsuffix .erl,$(ERLC_EXCLUDE)))
+
+ERLC_MIB_OPTS ?=
+COMPILE_MIB_FIRST ?=
+COMPILE_MIB_FIRST_PATHS = $(addprefix mibs/,$(addsuffix .mib,$(COMPILE_MIB_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_0 = @echo " ERLC  " $(filter-out $(patsubst %,%.erl,$(ERLC_EXCLUDE)),\
+	$(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.
+mib_verbose_0 = @echo " MIB   " $(filter %.bin %.mib,$(?F));
+mib_verbose = $(mib_verbose_$(V))
+
+# Targets.
+
+ifeq ($(wildcard ebin/test),)
+app:: app-build
+else
+app:: clean app-build
+endif
 
-app:: erlc-include ebin/$(PROJECT).app
+app-build: erlc-include ebin/$(PROJECT).app
 	$(eval MODULES := $(shell find ebin -type f -name \*.beam \
 		| sed "s/ebin\//'/;s/\.beam/',/" | sed '$$s/.$$//'))
 	@if [ -z "$$(grep -E '^[^%]*{modules,' src/$(PROJECT).app.src)" ]; then \
@@ -237,9 +321,15 @@ app:: erlc-include ebin/$(PROJECT).app
 		| sed "s/{id,[[:space:]]*\"git\"}/{id, \"$(GITDESCRIBE)\"}/" \
 		> ebin/$(PROJECT).app
 
+erlc-include:
+	-@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
+
 define compile_erl
 	$(erlc_verbose) erlc -v $(ERLC_OPTS) -o ebin/ \
-		-pa ebin/ -I include/ $(COMPILE_FIRST_PATHS) $(1)
+		-pa ebin/ -I include/ $(filter-out $(ERLC_EXCLUDE_PATHS),\
+		$(COMPILE_FIRST_PATHS) $(1))
 endef
 
 define compile_xyrl
@@ -248,10 +338,22 @@ define compile_xyrl
 	@rm ebin/*.erl
 endef
 
+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
+endef
+
 ifneq ($(wildcard src/),)
 ebin/$(PROJECT).app::
 	@mkdir -p ebin/
 
+ifneq ($(wildcard mibs/),)
+ebin/$(PROJECT).app:: $(shell find mibs -type f -name \*.mib)
+	@mkdir -p priv/mibs/ include
+	$(if $(strip $?),$(call compile_mib,$?))
+endif
+
 ebin/$(PROJECT).app:: $(shell find src -type f -name \*.erl) \
 		$(shell find src -type f -name \*.core)
 	$(if $(strip $?),$(call compile_erl,$?))
@@ -263,17 +365,56 @@ endif
 
 clean:: clean-app
 
-# Extra targets.
+clean-app:
+	$(gen_verbose) rm -rf ebin/ priv/mibs/ \
+		$(addprefix include/,$(addsuffix .hrl,$(notdir $(basename $(wildcard mibs/*.mib)))))
 
-erlc-include:
-	-@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
+# Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
+# This file is part of erlang.mk and subject to the terms of the ISC License.
 
-clean-app:
-	$(gen_verbose) rm -rf ebin/
+.PHONY: test-deps test-dir test-build clean-test-dir
+
+# Configuration.
+
+TEST_DIR ?= test
+
+ALL_TEST_DEPS_DIRS = $(addprefix $(DEPS_DIR)/,$(TEST_DEPS))
+
+TEST_ERLC_OPTS ?= +debug_info +warn_export_vars +warn_shadow_vars +warn_obsolete_guard
+TEST_ERLC_OPTS += -DTEST=1
+
+# Targets.
+
+$(foreach dep,$(TEST_DEPS),$(eval $(call dep_target,$(dep))))
 
-# Copyright (c) 2014, Loïc Hoguin <essen@ninenines.eu>
+test-deps: $(ALL_TEST_DEPS_DIRS)
+	@for dep in $(ALL_TEST_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
+
+ifneq ($(strip $(TEST_DIR)),)
+test-dir:
+	$(gen_verbose) erlc -v $(TEST_ERLC_OPTS) -I include/ -o $(TEST_DIR) \
+		$(wildcard $(TEST_DIR)/*.erl $(TEST_DIR)/*/*.erl) -pa ebin/
+endif
+
+ifeq ($(wildcard ebin/test),)
+test-build: ERLC_OPTS=$(TEST_ERLC_OPTS)
+test-build: clean deps test-deps
+	@$(MAKE) --no-print-directory app-build test-dir ERLC_OPTS="$(TEST_ERLC_OPTS)"
+	$(gen_verbose) touch ebin/test
+else
+test-build: ERLC_OPTS=$(TEST_ERLC_OPTS)
+test-build: deps test-deps
+	@$(MAKE) --no-print-directory app-build test-dir ERLC_OPTS="$(TEST_ERLC_OPTS)"
+endif
+
+clean:: clean-test-dir
+
+clean-test-dir:
+ifneq ($(wildcard $(TEST_DIR)/*.beam),)
+	$(gen_verbose) rm -f $(TEST_DIR)/*.beam
+endif
+
+# Copyright (c) 2014-2015, Loïc Hoguin <essen@ninenines.eu>
 # This file is part of erlang.mk and subject to the terms of the ISC License.
 
 .PHONY: bootstrap bootstrap-lib bootstrap-rel new list-templates
@@ -392,6 +533,56 @@ tpl_gen_server = "-module($(n))." \
 	"" \
 	"code_change(_OldVsn, State, _Extra) ->" \
 	"	{ok, State}."
+tpl_gen_fsm = "-module($(n))." \
+	"-behaviour(gen_fsm)." \
+	"" \
+	"%% API." \
+	"-export([start_link/0])." \
+	"" \
+	"%% gen_fsm." \
+	"-export([init/1])." \
+	"-export([state_name/2])." \
+	"-export([handle_event/3])." \
+	"-export([state_name/3])." \
+	"-export([handle_sync_event/4])." \
+	"-export([handle_info/3])." \
+	"-export([terminate/3])." \
+	"-export([code_change/4])." \
+	"" \
+	"-record(state, {" \
+	"})." \
+	"" \
+	"%% API." \
+	"" \
+	"-spec start_link() -> {ok, pid()}." \
+	"start_link() ->" \
+	"	gen_fsm:start_link(?MODULE, [], [])." \
+	"" \
+	"%% gen_fsm." \
+	"" \
+	"init([]) ->" \
+	"	{ok, state_name, \#state{}}." \
+	"" \
+	"state_name(_Event, StateData) ->" \
+	"	{next_state, state_name, StateData}." \
+	"" \
+	"handle_event(_Event, StateName, StateData) ->" \
+	"	{next_state, StateName, StateData}." \
+	"" \
+	"state_name(_Event, _From, StateData) ->" \
+	"	{reply, ignored, state_name, StateData}." \
+	"" \
+	"handle_sync_event(_Event, _From, StateName, StateData) ->" \
+	"	{reply, ignored, StateName, StateData}." \
+	"" \
+	"handle_info(_Info, StateName, StateData) ->" \
+	"	{next_state, StateName, StateData}." \
+	"" \
+	"terminate(_Reason, _StateName, _StateData) ->" \
+	"	ok." \
+	"" \
+	"code_change(_OldVsn, StateName, StateData, _Extra) ->" \
+	"	{ok, StateName, StateData}."
 tpl_cowboy_http = "-module($(n))." \
 	"-behaviour(cowboy_http_handler)." \
 	"" \
@@ -551,85 +742,169 @@ endif
 list-templates:
 	@echo Available templates: $(sort $(patsubst tpl_%,%,$(filter tpl_%,$(.VARIABLES))))
 
-# Copyright (c) 2013-2014, Loïc Hoguin <essen@ninenines.eu>
+# Copyright (c) 2014-2015, Loïc Hoguin <essen@ninenines.eu>
 # 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
+.PHONY: clean-c_src distclean-c_src-env
+# todo
+
+# Configuration.
+
+C_SRC_DIR = $(CURDIR)/c_src
+C_SRC_ENV ?= $(C_SRC_DIR)/env.mk
+C_SRC_OUTPUT ?= $(CURDIR)/priv/$(PROJECT).so
+
+# System type and C compiler/flags.
+
+UNAME_SYS := $(shell uname -s)
+ifeq ($(UNAME_SYS), Darwin)
+	CC ?= cc
+	CFLAGS ?= -O3 -std=c99 -arch x86_64 -finline-functions -Wall -Wmissing-prototypes
+	CXXFLAGS ?= -O3 -arch x86_64 -finline-functions -Wall
+	LDFLAGS ?= -arch x86_64 -flat_namespace -undefined suppress
+else ifeq ($(UNAME_SYS), FreeBSD)
+	CC ?= cc
+	CFLAGS ?= -O3 -std=c99 -finline-functions -Wall -Wmissing-prototypes
+	CXXFLAGS ?= -O3 -finline-functions -Wall
+else ifeq ($(UNAME_SYS), Linux)
+	CC ?= gcc
+	CFLAGS ?= -O3 -std=c99 -finline-functions -Wall -Wmissing-prototypes
+	CXXFLAGS ?= -O3 -finline-functions -Wall
+endif
+
+CFLAGS += -fPIC -I $(ERTS_INCLUDE_DIR) -I $(ERL_INTERFACE_INCLUDE_DIR)
+CXXFLAGS += -fPIC -I $(ERTS_INCLUDE_DIR) -I $(ERL_INTERFACE_INCLUDE_DIR)
+
+LDLIBS += -L $(ERL_INTERFACE_LIB_DIR) -lerl_interface -lei
+LDFLAGS += -shared
+
+# Verbosity.
+
+c_verbose_0 = @echo " C     " $(?F);
+c_verbose = $(c_verbose_$(V))
+
+cpp_verbose_0 = @echo " CPP   " $(?F);
+cpp_verbose = $(cpp_verbose_$(V))
+
+link_verbose_0 = @echo " LD    " $(@F);
+link_verbose = $(link_verbose_$(V))
+
+# Targets.
+
+ifeq ($(wildcard $(C_SRC_DIR)),)
+else ifneq ($(wildcard $(C_SRC_DIR)/Makefile),)
+app::
+	$(MAKE) -C $(C_SRC_DIR)
+
+clean::
+	$(MAKE) -C $(C_SRC_DIR) clean
+
+else
+SOURCES := $(shell find $(C_SRC_DIR) -type f \( -name "*.c" -o -name "*.C" -o -name "*.cc" -o -name "*.cpp" \))
+OBJECTS = $(addsuffix .o, $(basename $(SOURCES)))
+
+COMPILE_C = $(c_verbose) $(CC) $(CFLAGS) $(CPPFLAGS) -c
+COMPILE_CPP = $(cpp_verbose) $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c
+
+app:: $(C_SRC_ENV) $(C_SRC_OUTPUT)
+
+$(C_SRC_OUTPUT): $(OBJECTS)
+	@mkdir -p priv/
+	$(link_verbose) $(CC) $(OBJECTS) $(LDFLAGS) $(LDLIBS) -o $(C_SRC_OUTPUT)
+
+%.o: %.c
+	$(COMPILE_C) $(OUTPUT_OPTION) $<
+
+%.o: %.cc
+	$(COMPILE_CPP) $(OUTPUT_OPTION) $<
+
+%.o: %.C
+	$(COMPILE_CPP) $(OUTPUT_OPTION) $<
+
+%.o: %.cpp
+	$(COMPILE_CPP) $(OUTPUT_OPTION) $<
+
+$(C_SRC_ENV):
+	@$(ERL) -eval "file:write_file(\"$(C_SRC_ENV)\", \
+		io_lib:format( \
+			\"ERTS_INCLUDE_DIR ?= ~s/erts-~s/include/~n\" \
+			\"ERL_INTERFACE_INCLUDE_DIR ?= ~s~n\" \
+			\"ERL_INTERFACE_LIB_DIR ?= ~s~n\", \
+			[code:root_dir(), erlang:system_info(version), \
+			code:lib_dir(erl_interface, include), \
+			code:lib_dir(erl_interface, lib)])), \
+		halt()."
+
+clean:: clean-c_src
+
+clean-c_src:
+	$(gen_verbose) rm -f $(C_SRC_OUTPUT) $(OBJECTS)
+
+distclean:: distclean-c_src-env
+
+distclean-c_src-env:
+	$(gen_verbose) rm -f $(C_SRC_ENV)
+
+-include $(C_SRC_ENV)
+endif
+
+# Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
+# This file is part of erlang.mk and subject to the terms of the ISC License.
+
+.PHONY: ct distclean-ct
 
 # Configuration.
 
 CT_OPTS ?=
-ifneq ($(wildcard test/),)
-	CT_SUITES ?= $(sort $(subst _SUITE.erl,,$(shell find test -type f -name \*_SUITE.erl -exec basename {} \;)))
+ifneq ($(wildcard $(TEST_DIR)),)
+	CT_SUITES ?= $(sort $(subst _SUITE.erl,,$(shell find $(TEST_DIR) -type f -name \*_SUITE.erl -exec basename {} \;)))
 else
 	CT_SUITES ?=
 endif
 
-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
+tests:: ct
 
 distclean:: distclean-ct
 
 help::
 	@printf "%s\n" "" \
+		"Common_test targets:" \
+		"  ct          Run all the common_test suites for this project" \
+		"" \
 		"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 \
+	-noinput \
+	-pa ebin $(DEPS_DIR)/*/ebin \
+	-dir $(TEST_DIR) \
 	-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) -I include/ -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
+ifeq ($(CT_SUITES),)
+ct:
+else
+ct: test-build
+	@mkdir -p logs/
+	$(gen_verbose) $(CT_RUN) -suite $(addsuffix _SUITE,$(CT_SUITES)) $(CT_OPTS)
+endif
 
 define ct_suite_target
-ct-$(1): ERLC_OPTS = $(TEST_ERLC_OPTS)
-ct-$(1): clean deps app build-ct-suites
-	@if [ -d "test" ] ; \
-	then \
-		mkdir -p logs/ ; \
-		$(CT_RUN) -suite $(addsuffix _SUITE,$(1)) $(CT_OPTS) ; \
-	fi
-	$(gen_verbose) rm -f test/*.beam
+ct-$(1): test-build
+	@mkdir -p logs/
+	$(gen_verbose) $(CT_RUN) -suite $(addsuffix _SUITE,$(1)) $(CT_OPTS)
 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 <essen@ninenines.eu>
+# Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
 # This file is part of erlang.mk and subject to the terms of the ISC License.
 
 .PHONY: plt distclean-plt dialyze
@@ -671,6 +946,35 @@ dialyze: $(DIALYZER_PLT)
 endif
 	@dialyzer --no_native $(DIALYZER_DIRS) $(DIALYZER_OPTS)
 
+# Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
+# Copyright (c) 2015, Viktor Söderqvist <viktor@zuiderkwast.se>
+# This file is part of erlang.mk and subject to the terms of the ISC License.
+
+.PHONY: distclean-edoc build-doc-deps
+
+# Configuration.
+
+EDOC_OPTS ?=
+
+# Core targets.
+
+docs:: distclean-edoc build-doc-deps
+	$(gen_verbose) $(ERL) -eval 'edoc:application($(PROJECT), ".", [$(EDOC_OPTS)]), halt().'
+
+distclean:: distclean-edoc
+
+# Plugin-specific targets.
+
+DOC_DEPS_DIRS = $(addprefix $(DEPS_DIR)/,$(DOC_DEPS))
+
+$(foreach dep,$(DOC_DEPS),$(eval $(call dep_target,$(dep))))
+
+build-doc-deps: $(DOC_DEPS_DIRS)
+	@for dep in $(DOC_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
+
+distclean-edoc:
+	$(gen_verbose) rm -f doc/*.css doc/*.html doc/*.png doc/edoc-info
+
 # Copyright (c) 2014, Juan Facorro <juan@inaka.net>
 # This file is part of erlang.mk and subject to the terms of the ISC License.
 
@@ -694,25 +998,221 @@ help::
 		"Elvis targets:" \
 		"  elvis       Run Elvis using the local elvis.config or download the default otherwise"
 
-ifneq ($(wildcard $(ELVIS_CONFIG)),)
-rel:: distclean-elvis
-endif
-
 distclean:: distclean-elvis
 
 # Plugin-specific targets.
 
 $(ELVIS):
-	@$(call core_http_get,$(ELVIS_CONFIG),$(ELVIS_CONFIG_URL))
 	@$(call core_http_get,$(ELVIS),$(ELVIS_URL))
 	@chmod +x $(ELVIS)
 
-elvis: $(ELVIS)
+$(ELVIS_CONFIG):
+	@$(call core_http_get,$(ELVIS_CONFIG),$(ELVIS_CONFIG_URL))
+
+elvis: $(ELVIS) $(ELVIS_CONFIG)
 	@$(ELVIS) rock -c $(ELVIS_CONFIG) $(ELVIS_OPTS)
 
 distclean-elvis:
 	$(gen_verbose) rm -rf $(ELVIS)
 
+# Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
+# This file is part of erlang.mk and subject to the terms of the ISC License.
+
+# Configuration.
+
+DTL_FULL_PATH ?= 0
+
+# Verbosity.
+
+dtl_verbose_0 = @echo " DTL   " $(filter %.dtl,$(?F));
+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().'
+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) 2014 Dave Cottlehuber <dch@skunkwerks.at>
+# This file is part of erlang.mk and subject to the terms of the ISC License.
+
+.PHONY: distclean-escript escript
+
+# Configuration.
+
+ESCRIPT_NAME ?= $(PROJECT)
+ESCRIPT_COMMENT ?= This is an -*- erlang -*- file
+
+ESCRIPT_BEAMS ?= "ebin/*", "deps/*/ebin/*"
+ESCRIPT_SYS_CONFIG ?= "rel/sys.config"
+ESCRIPT_EMU_ARGS ?= -pa . \
+	-sasl errlog_type error \
+	-escript main $(ESCRIPT_NAME)
+ESCRIPT_SHEBANG ?= /usr/bin/env escript
+ESCRIPT_STATIC ?= "deps/*/priv/**", "priv/**"
+
+# Core targets.
+
+distclean:: distclean-escript
+
+help::
+	@printf "%s\n" "" \
+		"Escript targets:" \
+		"  escript     Build an executable escript archive" \
+
+# Plugin-specific targets.
+
+# Based on https://github.com/synrc/mad/blob/master/src/mad_bundle.erl
+# Copyright (c) 2013 Maxim Sokhatsky, Synrc Research Center
+# Modified MIT License, https://github.com/synrc/mad/blob/master/LICENSE :
+# Software may only be used for the great good and the true happiness of all
+# sentient beings.
+
+define ESCRIPT_RAW
+'Read = fun(F) -> {ok, B} = file:read_file(filename:absname(F)), B end,'\
+'Files = fun(L) -> A = lists:concat([filelib:wildcard(X)||X<- L ]),'\
+'  [F || F <- A, not filelib:is_dir(F) ] end,'\
+'Squash = fun(L) -> [{filename:basename(F), Read(F) } || F <- L ] end,'\
+'Zip = fun(A, L) -> {ok,{_,Z}} = zip:create(A, L, [{compress,all},memory]), Z end,'\
+'Ez = fun(Escript) ->'\
+'  Static = Files([$(ESCRIPT_STATIC)]),'\
+'  Beams = Squash(Files([$(ESCRIPT_BEAMS), $(ESCRIPT_SYS_CONFIG)])),'\
+'  Archive = Beams ++ [{ "static.gz", Zip("static.gz", Static)}],'\
+'  escript:create(Escript, [ $(ESCRIPT_OPTIONS)'\
+'    {archive, Archive, [memory]},'\
+'    {shebang, "$(ESCRIPT_SHEBANG)"},'\
+'    {comment, "$(ESCRIPT_COMMENT)"},'\
+'    {emu_args, " $(ESCRIPT_EMU_ARGS)"}'\
+'  ]),'\
+'  file:change_mode(Escript, 8#755)'\
+'end,'\
+'Ez("$(ESCRIPT_NAME)"),'\
+'halt().'
+endef
+
+ESCRIPT_COMMAND = $(subst ' ',,$(ESCRIPT_RAW))
+
+escript:: distclean-escript deps app
+	$(gen_verbose) $(ERL) -eval $(ESCRIPT_COMMAND)
+
+distclean-escript:
+	$(gen_verbose) rm -f $(ESCRIPT_NAME)
+
+# Copyright (c) 2014, Enrique Fernandez <enrique.fernandez@erlang-solutions.com>
+# Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
+# This file is contributed to erlang.mk and subject to the terms of the ISC License.
+
+.PHONY: eunit
+
+# Configuration
+
+ifeq ($(strip $(TEST_DIR)),)
+TAGGED_EUNIT_TESTS = {dir,"ebin"}
+else
+ifeq ($(wildcard $(TEST_DIR)),)
+TAGGED_EUNIT_TESTS = {dir,"ebin"}
+else
+# All modules in TEST_DIR
+TEST_DIR_MODS = $(notdir $(basename $(shell find $(TEST_DIR) -type f -name *.beam)))
+# All modules in 'ebin'
+EUNIT_EBIN_MODS = $(notdir $(basename $(shell find ebin -type f -name *.beam)))
+# Only those modules in TEST_DIR with no matching module in 'ebin'.
+# This is done to avoid some tests being executed twice.
+EUNIT_MODS = $(filter-out $(patsubst %,%_tests,$(EUNIT_EBIN_MODS)),$(TEST_DIR_MODS))
+TAGGED_EUNIT_TESTS = {dir,"ebin"} $(foreach mod,$(EUNIT_MODS),$(shell echo $(mod) | sed -e 's/\(.*\)/{module,\1}/g'))
+endif
+endif
+
+EUNIT_OPTS ?= verbose
+
+# Utility functions
+
+define str-join
+	$(shell echo '$(strip $(1))' | sed -e "s/ /,/g")
+endef
+
+# Core targets.
+
+tests:: eunit
+
+help::
+	@printf "%s\n" "" \
+		"EUnit targets:" \
+		"  eunit       Run all the EUnit tests for this project"
+
+# Plugin-specific targets.
+
+EUNIT_RUN = $(ERL) \
+	-pa $(TEST_DIR) $(DEPS_DIR)/*/ebin \
+	-pz ebin \
+	-eval 'case eunit:test([$(call str-join,$(TAGGED_EUNIT_TESTS))], [$(EUNIT_OPTS)]) of ok -> halt(0); error -> halt(1) end.'
+
+eunit: test-build
+	$(gen_verbose) $(EUNIT_RUN)
+
+# Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
+# This file is part of erlang.mk and subject to the terms of the ISC License.
+
+.PHONY: relx-rel distclean-relx-rel distclean-relx
+
+# Configuration.
+
+RELX_CONFIG ?= $(CURDIR)/relx.config
+
+RELX ?= $(CURDIR)/relx
+export RELX
+
+RELX_URL ?= https://github.com/erlware/relx/releases/download/v1.2.0/relx
+RELX_OPTS ?=
+RELX_OUTPUT_DIR ?= _rel
+
+ifeq ($(firstword $(RELX_OPTS)),-o)
+	RELX_OUTPUT_DIR = $(word 2,$(RELX_OPTS))
+else
+	RELX_OPTS += -o $(RELX_OUTPUT_DIR)
+endif
+
+# Core targets.
+
+ifneq ($(wildcard $(RELX_CONFIG)),)
+rel:: distclean-relx-rel relx-rel
+endif
+
+distclean:: distclean-relx-rel distclean-relx
+
+# Plugin-specific targets.
+
+define relx_fetch
+	$(call core_http_get,$(RELX),$(RELX_URL))
+	chmod +x $(RELX)
+endef
+
+$(RELX):
+	@$(call relx_fetch)
+
+relx-rel: $(RELX)
+	@$(RELX) -c $(RELX_CONFIG) $(RELX_OPTS)
+
+distclean-relx-rel:
+	$(gen_verbose) rm -rf $(RELX_OUTPUT_DIR)
+
+distclean-relx:
+	$(gen_verbose) rm -rf $(RELX)
+
 # Copyright (c) 2014, M Robert Martin <rob@version2beta.com>
 # This file is contributed to erlang.mk and subject to the terms of the ISC License.
 
@@ -720,7 +1220,7 @@ distclean-elvis:
 
 # Configuration.
 
-SHELL_PATH ?= -pa ../$(PROJECT)/ebin $(DEPS_DIR)/*/ebin
+SHELL_PATH ?= -pa $(CURDIR)/ebin $(DEPS_DIR)/*/ebin
 SHELL_OPTS ?=
 
 ALL_SHELL_DEPS_DIRS = $(addprefix $(DEPS_DIR)/,$(SHELL_DEPS))
@@ -741,3 +1241,35 @@ build-shell-deps: $(ALL_SHELL_DEPS_DIRS)
 
 shell: build-shell-deps
 	$(gen_verbose) erl $(SHELL_PATH) $(SHELL_OPTS)
+
+# Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
+# This file is part of erlang.mk and subject to the terms of the ISC License.
+
+ifneq ($(wildcard $(DEPS_DIR)/triq),)
+.PHONY: triq
+
+# Targets.
+
+tests:: triq
+
+define triq_run
+$(ERL) -pa $(CURDIR)/ebin $(DEPS_DIR)/*/ebin \
+	-eval "try $(1) of true -> halt(0); _ -> halt(1) catch error:undef -> io:format(\"Undefined property or module~n\"), halt() end."
+endef
+
+ifdef t
+ifeq (,$(findstring :,$(t)))
+triq: test-build
+	@$(call triq_run,triq:check($(t)))
+else
+triq: test-build
+	@echo Testing $(t)/0
+	@$(call triq_run,triq:check($(t)()))
+endif
+else
+triq: test-build
+	$(eval MODULES := $(shell find ebin -type f -name \*.beam \
+		| sed "s/ebin\//'/;s/\.beam/',/" | sed '$$s/.$$//'))
+	$(gen_verbose) $(call triq_run,[true] =:= lists:usort([triq:check(M) || M <- [$(MODULES)]]))
+endif
+endif

+ 1 - 1
include/cow_inline.hrl

@@ -1,4 +1,4 @@
-%% Copyright (c) 2014, Loïc Hoguin <essen@ninenines.eu>
+%% Copyright (c) 2014-2015, Loïc Hoguin <essen@ninenines.eu>
 %%
 %% Permission to use, copy, modify, and/or distribute this software for any
 %% purpose with or without fee is hereby granted, provided that the above

+ 1 - 1
src/cow_cookie.erl

@@ -1,4 +1,4 @@
-%% Copyright (c) 2013-2014, Loïc Hoguin <essen@ninenines.eu>
+%% Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
 %%
 %% Permission to use, copy, modify, and/or distribute this software for any
 %% purpose with or without fee is hereby granted, provided that the above

+ 1 - 5
src/cow_date.erl

@@ -1,4 +1,4 @@
-%% Copyright (c) 2013-2014, Loïc Hoguin <essen@ninenines.eu>
+%% Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
 %%
 %% Permission to use, copy, modify, and/or distribute this software for any
 %% purpose with or without fee is hereby granted, provided that the above
@@ -182,9 +182,7 @@ http_date_test_() ->
 		{<<"Sun Nov  6 08:49:37 1994">>, {{1994, 11, 6}, {8, 49, 37}}}
 	],
 	[{V, fun() -> R = http_date(V) end} || {V, R} <- Tests].
--endif.
 
--ifdef(PERF).
 horse_http_date_fixdate() ->
 	horse:repeat(200000,
 		http_date(<<"Sun, 06 Nov 1994 08:49:37 GMT">>)
@@ -221,9 +219,7 @@ rfc2109_test_() ->
 		{<<"Sun, 01-Jan-2012 00:00:00 GMT">>, {{2012, 1,  1}, { 0,  0,  0}}}
 	],
 	[{R, fun() -> R = rfc2109(D) end} || {R, D} <- Tests].
--endif.
 
--ifdef(PERF).
 horse_rfc2019_20130101_000000() ->
 	horse:repeat(100000,
 		rfc2109({{2013, 1, 1}, {0, 0, 0}})

+ 1 - 5
src/cow_http.erl

@@ -1,4 +1,4 @@
-%% Copyright (c) 2013-2014, Loïc Hoguin <essen@ninenines.eu>
+%% Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
 %%
 %% Permission to use, copy, modify, and/or distribute this software for any
 %% purpose with or without fee is hereby granted, provided that the above
@@ -82,9 +82,7 @@ parse_status_line_error_test_() ->
 	],
 	[{V, fun() -> {'EXIT', _} = (catch parse_status_line(V)) end}
 		|| V <- Tests].
--endif.
 
--ifdef(PERF).
 horse_parse_status_line_200() ->
 	horse:repeat(200000,
 		parse_status_line(<<"HTTP/1.1 200 OK\r\n">>)
@@ -184,9 +182,7 @@ parse_headers_error_test_() ->
 	],
 	[{V, fun() -> {'EXIT', _} = (catch parse_headers(V)) end}
 		|| V <- Tests].
--endif.
 
--ifdef(PERF).
 horse_parse_headers() ->
 	horse:repeat(50000,
 		parse_headers(<<"Server: Erlang/R17\r\n"

+ 1 - 65
src/cow_http_hd.erl

@@ -1,4 +1,4 @@
-%% Copyright (c) 2014, Loïc Hoguin <essen@ninenines.eu>
+%% Copyright (c) 2014-2015, Loïc Hoguin <essen@ninenines.eu>
 %%
 %% Permission to use, copy, modify, and/or distribute this software for any
 %% purpose with or without fee is hereby granted, provided that the above
@@ -386,9 +386,7 @@ parse_accept_error_test_() ->
 		<<"audio/basic;t=\"zero \\", 0, " woo\"">>
 	],
 	[{V, fun() -> {'EXIT', _} = (catch parse_accept(V)) end} || V <- Tests].
--endif.
 
--ifdef(PERF).
 horse_parse_accept() ->
 	horse:repeat(20000,
 		parse_accept(<<"text/*;q=0.3, text/html;q=0.7, text/html;level=1, "
@@ -480,9 +478,7 @@ parse_accept_charset_error_test_() ->
 		<<>>
 	],
 	[{V, fun() -> {'EXIT', _} = (catch parse_accept_charset(V)) end} || V <- Tests].
--endif.
 
--ifdef(PERF).
 horse_parse_accept_charset() ->
 	horse:repeat(20000,
 		parse_accept_charset(<<"iso-8859-5, unicode-1-1;q=0.8">>)
@@ -538,9 +534,7 @@ parse_accept_encoding_test_() ->
 		]}
 	],
 	[{V, fun() -> R = parse_accept_encoding(V) end} || {V, R} <- Tests].
--endif.
 
--ifdef(PERF).
 horse_parse_accept_encoding() ->
 	horse:repeat(20000,
 		parse_accept_encoding(<<"gzip;q=1.0, identity; q=0.5, *;q=0">>)
@@ -657,9 +651,7 @@ parse_accept_language_error_test_() ->
 		<<"419-en-us">>
 	],
 	[{V, fun() -> {'EXIT', _} = (catch parse_accept_language(V)) end} || V <- Tests].
--endif.
 
--ifdef(PERF).
 horse_parse_accept_language() ->
 	horse:repeat(20000,
 		parse_accept_language(<<"da, en-gb;q=0.8, en;q=0.7">>)
@@ -688,9 +680,7 @@ parse_accept_ranges_error_test_() ->
 		<<>>
 	],
 	[{V, fun() -> {'EXIT', _} = (catch parse_accept_ranges(V)) end} || V <- Tests].
--endif.
 
--ifdef(PERF).
 horse_parse_accept_ranges_none() ->
 	horse:repeat(200000,
 		parse_accept_ranges(<<"none">>)
@@ -764,9 +754,7 @@ parse_allow_test_() ->
 		{<<"GET, HEAD, PUT">>, [<<"GET">>, <<"HEAD">>, <<"PUT">>]}
 	],
 	[{V, fun() -> R = parse_allow(V) end} || {V, R} <- Tests].
--endif.
 
--ifdef(PERF).
 horse_parse_allow() ->
 	horse:repeat(200000,
 		parse_allow(<<"GET, HEAD, PUT">>)
@@ -863,9 +851,7 @@ parse_authorization_test_() ->
 				{<<"opaque">>, <<"5ccc069c403ebaf9f0171e9517f40e41">>}]}}
 	],
 	[{V, fun() -> R = parse_authorization(V) end} || {V, R} <- Tests].
--endif.
 
--ifdef(PERF).
 horse_parse_authorization_basic() ->
 	horse:repeat(20000,
 		parse_authorization(<<"Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==">>)
@@ -1014,9 +1000,7 @@ parse_cache_control_error_test_() ->
 		<<>>
 	],
 	[{V, fun() -> {'EXIT', _} = (catch parse_cache_control(V)) end} || V <- Tests].
--endif.
 
--ifdef(PERF).
 horse_parse_cache_control_no_cache() ->
 	horse:repeat(200000,
 		parse_cache_control(<<"no-cache">>)
@@ -1078,9 +1062,7 @@ parse_connection_error_test_() ->
 		<<>>
 	],
 	[{V, fun() -> {'EXIT', _} = (catch parse_connection(V)) end} || V <- Tests].
--endif.
 
--ifdef(PERF).
 horse_parse_connection_close() ->
 	horse:repeat(200000,
 		parse_connection(<<"close">>)
@@ -1115,9 +1097,7 @@ parse_content_encoding_error_test_() ->
 		<<>>
 	],
 	[{V, fun() -> {'EXIT', _} = (catch parse_content_encoding(V)) end} || V <- Tests].
--endif.
 
--ifdef(PERF).
 horse_parse_content_encoding() ->
 	horse:repeat(200000,
 		parse_content_encoding(<<"gzip">>)
@@ -1403,9 +1383,7 @@ parse_content_language_error_test_() ->
 		<<>>
 	],
 	[{V, fun() -> {'EXIT', _} = (catch parse_content_language(V)) end} || V <- Tests].
--endif.
 
--ifdef(PERF).
 horse_parse_content_language() ->
 	horse:repeat(100000,
 		parse_content_language(<<"fr, en-US, es-419, az-Arab, x-pig-latin, man-Nkoo-GN">>)
@@ -1449,9 +1427,7 @@ parse_content_length_error_test_() ->
 		<<"4.17">>
 	],
 	[{V, fun() -> {'EXIT', _} = (catch parse_content_length(V)) end} || V <- Tests].
--endif.
 
--ifdef(PERF).
 horse_parse_content_length_zero() ->
 	horse:repeat(100000,
 		parse_content_length(<<"0">>)
@@ -1551,9 +1527,7 @@ parse_content_range_error_test_() ->
 		<<>>
 	],
 	[{V, fun() -> {'EXIT', _} = (catch parse_content_range(V)) end} || V <- Tests].
--endif.
 
--ifdef(PERF).
 horse_parse_content_range_bytes() ->
 	horse:repeat(200000,
 		parse_content_range(<<"bytes 21010-47021/47022">>)
@@ -1666,9 +1640,7 @@ parse_content_type_test_() ->
 			]}}
 	],
 	[{V, fun() -> R = parse_content_type(V) end} || {V, R} <- Tests].
--endif.
 
--ifdef(PERF).
 horse_parse_content_type() ->
 	horse:repeat(200000,
 		parse_content_type(<<"text/html;charset=utf-8">>)
@@ -1739,9 +1711,7 @@ parse_etag_error_test_() ->
 		<<"W/">>
 	],
 	[{V, fun() -> {'EXIT', _} = (catch parse_etag(V)) end} || V <- Tests].
--endif.
 
--ifdef(PERF).
 horse_parse_etag() ->
 	horse:repeat(200000,
 		parse_etag(<<"W/\"xyzzy\"">>)
@@ -1789,9 +1759,7 @@ parse_expect_error_test_() ->
 		<<"Cookies">>
 	],
 	[{V, fun() -> {'EXIT', _} = (catch parse_expect(V)) end} || V <- Tests].
--endif.
 
--ifdef(PERF).
 horse_parse_expect() ->
 	horse:repeat(200000,
 		parse_expect(<<"100-continue">>)
@@ -1821,9 +1789,7 @@ parse_expires_test_() ->
 		{<<"Thu, 01 Dec 1994 16:00:00 GMT">>, {{1994, 12, 1}, {16, 0, 0}}}
 	],
 	[{V, fun() -> R = parse_expires(V) end} || {V, R} <- Tests].
--endif.
 
--ifdef(PERF).
 horse_parse_expires_0() ->
 	horse:repeat(200000,
 		parse_expires(<<"0">>)
@@ -1897,9 +1863,7 @@ parse_host_test_() ->
 		{<<"[::ffff:192.0.2.1]">>, {<<"[::ffff:192.0.2.1]">>, undefined}}
 	],
 	[{V, fun() -> R = parse_host(V) end} || {V, R} <- Tests].
--endif.
 
--ifdef(PERF).
 horse_parse_host_blue_example_org() ->
 	horse:repeat(200000,
 		parse_host(<<"blue.example.org:8080">>)
@@ -1966,9 +1930,7 @@ parse_if_match_error_test_() ->
 		<<>>
 	],
 	[{V, fun() -> {'EXIT', _} = (catch parse_if_match(V)) end} || V <- Tests].
--endif.
 
--ifdef(PERF).
 horse_parse_if_match() ->
 	horse:repeat(200000,
 		parse_if_match(<<"\"xyzzy\", \"r2d2xxxx\", \"c3piozzzz\"">>)
@@ -2015,9 +1977,7 @@ parse_if_none_match_error_test_() ->
 		<<>>
 	],
 	[{V, fun() -> {'EXIT', _} = (catch parse_if_none_match(V)) end} || V <- Tests].
--endif.
 
--ifdef(PERF).
 horse_parse_if_none_match() ->
 	horse:repeat(200000,
 		parse_if_none_match(<<"W/\"xyzzy\", W/\"r2d2xxxx\", W/\"c3piozzzz\"">>)
@@ -2048,9 +2008,7 @@ parse_if_range_error_test_() ->
 		<<>>
 	],
 	[{V, fun() -> {'EXIT', _} = (catch parse_if_range(V)) end} || V <- Tests].
--endif.
 
--ifdef(PERF).
 horse_parse_if_range_etag() ->
 	horse:repeat(200000,
 		parse_if_range(<<"\"xyzzy\"">>)
@@ -2255,9 +2213,7 @@ parse_range_error_test_() ->
 		<<>>
 	],
 	[{V, fun() -> {'EXIT', _} = (catch parse_range(V)) end} || V <- Tests].
--endif.
 
--ifdef(PERF).
 horse_parse_range_first_last() ->
 	horse:repeat(200000,
 		parse_range(<<"bytes=500-999">>)
@@ -2307,9 +2263,7 @@ parse_retry_after_error_test_() ->
 		<<>>
 	],
 	[{V, fun() -> {'EXIT', _} = (catch parse_retry_after(V)) end} || V <- Tests].
--endif.
 
--ifdef(PERF).
 horse_parse_retry_after_date() ->
 	horse:repeat(200000,
 		parse_retry_after(<<"Fri, 31 Dec 1999 23:59:59 GMT">>)
@@ -2417,9 +2371,7 @@ parse_sec_websocket_extensions_error_test_() ->
 	],
 	[{V, fun() -> {'EXIT', _} = (catch parse_sec_websocket_extensions(V)) end}
 		|| V <- Tests].
--endif.
 
--ifdef(PERF).
 horse_parse_sec_websocket_extensions() ->
 	horse:repeat(200000,
 		parse_sec_websocket_extensions(<<"mux; max-channels=4; flow-control, deflate-stream">>)
@@ -2456,9 +2408,7 @@ parse_sec_websocket_protocol_req_error_test_() ->
 	],
 	[{V, fun() -> {'EXIT', _} = (catch parse_sec_websocket_protocol_req(V)) end}
 		|| V <- Tests].
--endif.
 
--ifdef(PERF).
 horse_parse_sec_websocket_protocol_req() ->
 	horse:repeat(200000,
 		parse_sec_websocket_protocol_req(<<"chat, superchat">>)
@@ -2494,9 +2444,7 @@ parse_sec_websocket_protocol_resp_error_test_() ->
 	],
 	[{V, fun() -> {'EXIT', _} = (catch parse_sec_websocket_protocol_resp(V)) end}
 		|| V <- Tests].
--endif.
 
--ifdef(PERF).
 horse_parse_sec_websocket_protocol_resp() ->
 	horse:repeat(200000,
 		parse_sec_websocket_protocol_resp(<<"chat">>)
@@ -2533,9 +2481,7 @@ parse_sec_websocket_version_req_error_test_() ->
 	],
 	[{V, fun() -> {'EXIT', _} = (catch parse_sec_websocket_version_req(V)) end}
 		|| V <- Tests].
--endif.
 
--ifdef(PERF).
 horse_parse_sec_websocket_version_req_13() ->
 	horse:repeat(200000,
 		parse_sec_websocket_version_req(<<"13">>)
@@ -2591,9 +2537,7 @@ parse_sec_websocket_version_resp_error_test_() ->
 	],
 	[{V, fun() -> {'EXIT', _} = (catch parse_sec_websocket_version_resp(V)) end}
 		|| V <- Tests].
--endif.
 
--ifdef(PERF).
 horse_parse_sec_websocket_version_resp() ->
 	horse:repeat(200000,
 		parse_sec_websocket_version_resp(<<"13, 8, 7">>)
@@ -2691,9 +2635,7 @@ parse_te_test_() ->
 		{<<"trailers, deflate;q=0.5">>, {trailers, [{<<"deflate">>, 500}]}}
 	],
 	[{V, fun() -> R = parse_te(V) end} || {V, R} <- Tests].
--endif.
 
--ifdef(PERF).
 horse_parse_te() ->
 	horse:repeat(200000,
 		parse_te(<<"trailers, deflate;q=0.5">>)
@@ -2718,9 +2660,7 @@ parse_trailer_error_test_() ->
 		<<>>
 	],
 	[{V, fun() -> {'EXIT', _} = (catch parse_trailer(V)) end} || V <- Tests].
--endif.
 
--ifdef(PERF).
 horse_parse_trailer() ->
 	horse:repeat(200000,
 		parse_trailer(<<"Date, Content-MD5">>)
@@ -2769,9 +2709,7 @@ parse_transfer_encoding_error_test_() ->
 	],
 	[{V, fun() -> {'EXIT', _} = (catch parse_transfer_encoding(V)) end}
 		|| V <- Tests].
--endif.
 
--ifdef(PERF).
 horse_parse_transfer_encoding_chunked() ->
 	horse:repeat(200000,
 		parse_transfer_encoding(<<"chunked">>)
@@ -3018,9 +2956,7 @@ parse_www_authenticate_error_test_() ->
 		<<>>
 	],
 	[{V, fun() -> {'EXIT', _} = (catch parse_www_authenticate(V)) end} || V <- Tests].
--endif.
 
--ifdef(PERF).
 horse_parse_www_authenticate() ->
 	horse:repeat(200000,
 		parse_www_authenticate(<<"Newauth realm=\"apps\", type=1, title=\"Login to \\\"apps\\\"\", Basic realm=\"simple\"">>)

+ 1 - 6
src/cow_http_te.erl

@@ -34,7 +34,7 @@
 	| {done, Data::binary(), TotalLen::non_neg_integer(), Rest::binary()}.
 -export_type([decode_ret/0]).
 
--ifdef(EXTRA).
+-ifdef(TEST).
 dripfeed(<< C, Rest/bits >>, Acc, State, F) ->
 	case F(<< Acc/binary, C >>, State) of
 		more ->
@@ -92,11 +92,8 @@ stream_identity_parts_test() ->
 	{done, << 0:7992 >>, 2999, <<>>}
 		= stream_identity(<< 0:7992 >>, S2),
 	ok.
--endif.
 
--ifdef(PERF).
 %% Using the same data as the chunked one for comparison.
-
 horse_stream_identity() ->
 	horse:repeat(10000,
 		stream_identity(<<
@@ -296,9 +293,7 @@ stream_chunked_error_test_() ->
 	[{lists:flatten(io_lib:format("value ~p state ~p", [V, S])),
 		fun() -> {'EXIT', _} = (catch stream_chunked(V, S)) end}
 			|| {V, S} <- Tests].
--endif.
 
--ifdef(PERF).
 horse_stream_chunked() ->
 	horse:repeat(10000,
 		stream_chunked(<<

+ 1 - 1
src/cow_mimetypes.erl

@@ -1,4 +1,4 @@
-%% Copyright (c) 2013-2014, Loïc Hoguin <essen@ninenines.eu>
+%% Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
 %%
 %% Permission to use, copy, modify, and/or distribute this software for any
 %% purpose with or without fee is hereby granted, provided that the above

+ 1 - 11
src/cow_multipart.erl

@@ -1,4 +1,4 @@
-%% Copyright (c) 2014, Loïc Hoguin <essen@ninenines.eu>
+%% Copyright (c) 2014-2015, Loïc Hoguin <essen@ninenines.eu>
 %%
 %% Permission to use, copy, modify, and/or distribute this software for any
 %% purpose with or without fee is hereby granted, provided that the above
@@ -362,9 +362,7 @@ parse_partial_test() ->
 	{ok, <<"boundary">>, <<"\r\n--">>}
 		= parse_body(<<"boundary\r\n--">>, <<"boundary">>),
 	ok.
--endif.
 
--ifdef(PERF).
 perf_parse_multipart(Stream, Boundary) ->
 	case parse_headers(Stream, Boundary) of
 		{ok, _, Rest} ->
@@ -461,9 +459,7 @@ identity_test() ->
 	{done, Body2, M6} = parse_body(M5, B),
 	{done, Epilogue} = parse_headers(M6, B),
 	ok.
--endif.
 
--ifdef(PERF).
 perf_build_multipart() ->
 	B = boundary(),
 	[
@@ -575,9 +571,7 @@ parse_content_disposition_test_() ->
 			{<<"file">>, [{<<"filename">>, <<"file2.gif">>}]}}
 	],
 	[{V, fun() -> R = parse_content_disposition(V) end} || {V, R} <- Tests].
--endif.
 
--ifdef(PERF).
 horse_parse_content_disposition_attachment() ->
 	horse:repeat(100000,
 		parse_content_disposition(<<"attachment; filename=genome.jpeg;"
@@ -617,9 +611,7 @@ parse_content_transfer_encoding_test_() ->
 	],
 	[{V, fun() -> R = parse_content_transfer_encoding(V) end}
 		|| {V, R} <- Tests].
--endif.
 
--ifdef(PERF).
 horse_parse_content_transfer_encoding() ->
 	horse:repeat(100000,
 		parse_content_transfer_encoding(<<"QUOTED-PRINTABLE">>)
@@ -680,9 +672,7 @@ parse_content_type_test_() ->
 	],
 	[{V, fun() -> R = parse_content_type(V) end}
 		|| {V, R} <- Tests].
--endif.
 
--ifdef(PERF).
 horse_parse_content_type_zero() ->
 	horse:repeat(100000,
 		parse_content_type(<<"text/plain">>)

+ 1 - 9
src/cow_qs.erl

@@ -1,4 +1,4 @@
-%% Copyright (c) 2013-2014, Loïc Hoguin <essen@ninenines.eu>
+%% Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
 %%
 %% Permission to use, copy, modify, and/or distribute this software for any
 %% purpose with or without fee is hereby granted, provided that the above
@@ -126,9 +126,7 @@ parse_qs_identity_test_() ->
 			"b-sid=521732&ortb-xt=IAB3&ortb-ugc=">>
 	],
 	[{V, fun() -> V = qs(parse_qs(V)) end} || V <- Tests].
--endif.
 
--ifdef(PERF).
 horse_parse_qs_shorter() ->
 	horse:repeat(20000,
 		parse_qs(<<"hl=en&q=erlang%20cowboy">>)
@@ -310,9 +308,7 @@ qs_identity_test_() ->
 	[{lists:flatten(io_lib:format("~p", [V])), fun() ->
 		V = parse_qs(qs(V))
 	end} || V <- Tests].
--endif.
 
--ifdef(PERF).
 horse_qs_shorter() ->
 	horse:repeat(20000, qs(?QS_SHORTER)).
 
@@ -395,9 +391,7 @@ urldecode_identity_test_() ->
 			"%BE%8B%E3%80%9C">>
 	],
 	[{V, fun() -> V = urlencode(urldecode(V)) end} || V <- Tests].
--endif.
 
--ifdef(PERF).
 horse_urldecode() ->
 	horse:repeat(100000,
 		urldecode(<<"nothingnothingnothingnothing">>)
@@ -544,9 +538,7 @@ urlencode_identity_test_() ->
 			129,153,227,130,139,230,151,139,229,190,139,227,128,156>>
 	],
 	[{V, fun() -> V = urldecode(urlencode(V)) end} || V <- Tests].
--endif.
 
--ifdef(PERF).
 horse_urlencode() ->
 	horse:repeat(100000,
 		urlencode(<<"nothingnothingnothingnothing">>)

+ 1 - 1
src/cow_spdy.erl

@@ -1,4 +1,4 @@
-%% Copyright (c) 2013-2014, Loïc Hoguin <essen@ninenines.eu>
+%% Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
 %%
 %% Permission to use, copy, modify, and/or distribute this software for any
 %% purpose with or without fee is hereby granted, provided that the above

+ 1 - 1
src/cowlib.app.src

@@ -1,4 +1,4 @@
-%% Copyright (c) 2013-2014, Loïc Hoguin <essen@ninenines.eu>
+%% Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
 %%
 %% Permission to use, copy, modify, and/or distribute this software for any
 %% purpose with or without fee is hereby granted, provided that the above

+ 0 - 31
test/eunit_SUITE.erl

@@ -1,31 +0,0 @@
-%% Copyright (c) 2013-2014, Loïc Hoguin <essen@ninenines.eu>
-%%
-%% 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.
-
--module(eunit_SUITE).
-
--include_lib("common_test/include/ct.hrl").
-
-%% ct.
--export([all/0]).
-
-%% Tests.
--export([eunit/1]).
-
-%% ct.
-
-all() ->
-	[eunit].
-
-eunit(_) ->
-	ok = eunit:test({application, cowlib}).