From c6749e61a9de25a241227ed9b7bd36caee236a98 Mon Sep 17 00:00:00 2001 From: Martin Sumner Date: Fri, 3 Nov 2017 11:04:31 +0000 Subject: [PATCH 01/13] Split out block serialisation To allow for alternate compression scenarios to be more easily tested --- src/leveled_sst.erl | 60 ++++++++++++++++++++++++++++++--------------- 1 file changed, 40 insertions(+), 20 deletions(-) diff --git a/src/leveled_sst.erl b/src/leveled_sst.erl index 7b8633b..4b251f9 100644 --- a/src/leveled_sst.erl +++ b/src/leveled_sst.erl @@ -740,6 +740,26 @@ generate_filenames(RootFilename) -> end. +-spec serialise_block(any()) -> binary(). +%% @doc +%% Convert term to binary +%% Function split out to make it easier to experiment with different +%% compression methods. Also, perhaps standardise applictaion of CRC +%% checks +serialise_block(Term) -> + term_to_binary(Term, ?BINARY_SETTINGS). + + +-spec deserialise_block(binary()) -> any(). +%% @doc +%% Convert binary to term +%% Function split out to make it easier to experiment with different +%% compression methods. Also, perhaps standardise applictaion of CRC +%% checks +deserialise_block(Bin) -> + binary_to_term(Bin). + + %%%============================================================================ %%% SlotIndex Implementation %%%============================================================================ @@ -865,45 +885,45 @@ generate_binary_slot(Lookup, KVL) -> {B1, B2, B3, B4, B5} = case length(KVL) of L when L =< SideBlockSize -> - {term_to_binary(KVL, ?BINARY_SETTINGS), + {serialise_block(KVL), <<0:0>>, <<0:0>>, <<0:0>>, <<0:0>>}; L when L =< 2 * SideBlockSize -> {KVLA, KVLB} = lists:split(SideBlockSize, KVL), - {term_to_binary(KVLA, ?BINARY_SETTINGS), - term_to_binary(KVLB, ?BINARY_SETTINGS), + {serialise_block(KVLA), + serialise_block(KVLB), <<0:0>>, <<0:0>>, <<0:0>>}; L when L =< (2 * SideBlockSize + MidBlockSize) -> {KVLA, KVLB_Rest} = lists:split(SideBlockSize, KVL), {KVLB, KVLC} = lists:split(SideBlockSize, KVLB_Rest), - {term_to_binary(KVLA, ?BINARY_SETTINGS), - term_to_binary(KVLB, ?BINARY_SETTINGS), - term_to_binary(KVLC, ?BINARY_SETTINGS), + {serialise_block(KVLA), + serialise_block(KVLB), + serialise_block(KVLC), <<0:0>>, <<0:0>>}; L when L =< (3 * SideBlockSize + MidBlockSize) -> {KVLA, KVLB_Rest} = lists:split(SideBlockSize, KVL), {KVLB, KVLC_Rest} = lists:split(SideBlockSize, KVLB_Rest), {KVLC, KVLD} = lists:split(MidBlockSize, KVLC_Rest), - {term_to_binary(KVLA, ?BINARY_SETTINGS), - term_to_binary(KVLB, ?BINARY_SETTINGS), - term_to_binary(KVLC, ?BINARY_SETTINGS), - term_to_binary(KVLD, ?BINARY_SETTINGS), + {serialise_block(KVLA), + serialise_block(KVLB), + serialise_block(KVLC), + serialise_block(KVLD), <<0:0>>}; L when L =< (4 * SideBlockSize + MidBlockSize) -> {KVLA, KVLB_Rest} = lists:split(SideBlockSize, KVL), {KVLB, KVLC_Rest} = lists:split(SideBlockSize, KVLB_Rest), {KVLC, KVLD_Rest} = lists:split(MidBlockSize, KVLC_Rest), {KVLD, KVLE} = lists:split(SideBlockSize, KVLD_Rest), - {term_to_binary(KVLA, ?BINARY_SETTINGS), - term_to_binary(KVLB, ?BINARY_SETTINGS), - term_to_binary(KVLC, ?BINARY_SETTINGS), - term_to_binary(KVLD, ?BINARY_SETTINGS), - term_to_binary(KVLE, ?BINARY_SETTINGS)} + {serialise_block(KVLA), + serialise_block(KVLB), + serialise_block(KVLC), + serialise_block(KVLD), + serialise_block(KVLE)} end, B1P = byte_size(PosBinIndex), @@ -934,7 +954,7 @@ check_blocks([], _Handle, _Slot, _BlockLengths, _LedgerKey) -> check_blocks([Pos|Rest], Handle, Slot, BlockLengths, LedgerKey) -> {BlockNumber, BlockPos} = revert_position(Pos), BlockBin = read_block(Handle, Slot, BlockLengths, BlockNumber), - BlockL = binary_to_term(BlockBin), + BlockL = deserialise_block(BlockBin), {K, V} = lists:nth(BlockPos, BlockL), case K of LedgerKey -> @@ -1022,7 +1042,7 @@ binaryslot_tolist(FullBin) -> {Acc, Bin}; _ -> <> = Bin, - {Acc ++ binary_to_term(Block), Rest} + {Acc ++ deserialise_block(Block), Rest} end end, @@ -1076,7 +1096,7 @@ binaryslot_trimmedlist(FullBin, StartKey, EndKey) -> 0 -> [Block1, Block2]; _ -> - MidBlockList = binary_to_term(MidBlock), + MidBlockList = deserialise_block(MidBlock), {MidFirst, _} = lists:nth(1, MidBlockList), {MidLast, _} = lists:last(MidBlockList), Split = {StartKey > MidLast, @@ -1114,7 +1134,7 @@ binaryslot_trimmedlist(FullBin, StartKey, EndKey) -> BlockList = case is_binary(Block) of true -> - binary_to_term(Block); + deserialise_block(Block); false -> Block end, @@ -1198,7 +1218,7 @@ fetch_value([Pos|Rest], BlockLengths, Blocks, Key) -> Offset, Length} = block_offsetandlength(BlockLengths, BlockNumber), <<_Pre:Offset/binary, Block:Length/binary, _Rest/binary>> = Blocks, - BlockL = binary_to_term(Block), + BlockL = deserialise_block(Block), {K, V} = lists:nth(BlockPos, BlockL), case K of Key -> From 912920a53cf6adf288679b6fce8d7dabdf368acf Mon Sep 17 00:00:00 2001 From: Martin Sumner Date: Fri, 3 Nov 2017 11:47:00 +0000 Subject: [PATCH 02/13] Failed attempt to hack in LZ4 --- .erlang.mk/deps.log | 2 + .erlang.mk/last-makefile-change | 0 Makefile | 41 + c_src/env.mk | 3 + c_src/lz4.c | 47 + c_src/lz4.o | Bin 0 -> 3272 bytes c_src/lz4_erlang.h | 58 + c_src/lz4f.c | 286 ++ c_src/lz4f.o | Bin 0 -> 5868 bytes ebin/lz4.app | 8 + erlang.mk | 6889 +++++++++++++++++++++++++++++++ lz4.d | 2 + priv/lz4_nif.so | Bin 0 -> 104132 bytes rebar.config | 7 + src/leveled_cdb.erl | 15 +- src/lz4_nif.erl | 64 + src/lz4f.erl | 107 + test/lz4f_SUITE.erl | 82 + 18 files changed, 7603 insertions(+), 8 deletions(-) create mode 100644 .erlang.mk/deps.log create mode 100644 .erlang.mk/last-makefile-change create mode 100644 Makefile create mode 100644 c_src/env.mk create mode 100644 c_src/lz4.c create mode 100644 c_src/lz4.o create mode 100644 c_src/lz4_erlang.h create mode 100644 c_src/lz4f.c create mode 100644 c_src/lz4f.o create mode 100644 ebin/lz4.app create mode 100644 erlang.mk create mode 100644 lz4.d create mode 100755 priv/lz4_nif.so create mode 100644 src/lz4_nif.erl create mode 100644 src/lz4f.erl create mode 100644 test/lz4f_SUITE.erl diff --git a/.erlang.mk/deps.log b/.erlang.mk/deps.log new file mode 100644 index 0000000..4d0d282 --- /dev/null +++ b/.erlang.mk/deps.log @@ -0,0 +1,2 @@ +/Users/martinsumner/dbroot/leveled/deps/lz4_src +/Users/martinsumner/dbroot/leveled/deps/nif_helpers diff --git a/.erlang.mk/last-makefile-change b/.erlang.mk/last-makefile-change new file mode 100644 index 0000000..e69de29 diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..340db88 --- /dev/null +++ b/Makefile @@ -0,0 +1,41 @@ +PROJECT = lz4 +PROJECT_DESCRIPTION = New project +PROJECT_VERSION = 0.1.0 + +BUILD_DEPS = lz4_src nif_helpers +dep_lz4_src = git https://github.com/lz4/lz4 v1.8.0 +dep_nif_helpers = git https://github.com/ninenines/nif_helpers master +DEP_PLUGINS = nif_helpers + +C_SRC_OUTPUT = $(CURDIR)/priv/lz4_nif + +TEST_DEPS = ct_helper +dep_ct_helper = git https://github.com/extend/ct_helper master + +include erlang.mk + +CFLAGS += -I $(DEPS_DIR)/lz4_src/lib +# This is required in order to build a liblz4.a that we can +# include in our shared library. +export CPPFLAGS += -shared -fPIC +LDLIBS += $(DEPS_DIR)/lz4_src/lib/liblz4.a + +-include c_src/env.mk + +cppcheck: + cppcheck -f --quiet --error-exitcode=2 --enable=all --inconclusive --std=posix \ + -Ideps/lz4_src/lib/ -Ideps/nif_helpers/ -I$(ERTS_INCLUDE_DIR) c_src/ + +scan-build: + make clean + scan-build make + +# Download a large file for use in compression tests. + +PDF_REFERENCE = http://www.adobe.com/content/dam/Adobe/en/devnet/acrobat/pdfs/pdf_reference_1-7.pdf + +test-build:: $(TEST_DIR)/lz4f_SUITE_data/pdf_reference_1-7.pdf + +$(TEST_DIR)/lz4f_SUITE_data/pdf_reference_1-7.pdf: + $(verbose) mkdir -p $(TEST_DIR)/lz4f_SUITE_data/ + $(gen_verbose) $(call core_http_get,$@,$(PDF_REFERENCE)) diff --git a/c_src/env.mk b/c_src/env.mk new file mode 100644 index 0000000..429d36e --- /dev/null +++ b/c_src/env.mk @@ -0,0 +1,3 @@ +ERTS_INCLUDE_DIR ?= /Users/martinsumner/erlang/18.3/erts-7.3/include/ +ERL_INTERFACE_INCLUDE_DIR ?= /Users/martinsumner/erlang/18.3/lib/erl_interface-3.8.2/include +ERL_INTERFACE_LIB_DIR ?= /Users/martinsumner/erlang/18.3/lib/erl_interface-3.8.2/lib diff --git a/c_src/lz4.c b/c_src/lz4.c new file mode 100644 index 0000000..80b0948 --- /dev/null +++ b/c_src/lz4.c @@ -0,0 +1,47 @@ +// Copyright (c) 2017-Present Pivotal Software, Inc. All rights reserved. +// +// This package, the LZ4 binding for Erlang, is double-licensed under the Mozilla +// Public License 1.1 ("MPL") and the Apache License version 2 +// ("ASL"). For the MPL, please see LICENSE-MPL-RabbitMQ. For the ASL, +// please see LICENSE-APACHE2. +// +// This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, +// either express or implied. See the LICENSE file for specific language governing +// rights and limitations of this software. +// +// If you have any questions regarding licensing, please contact us at +// info@rabbitmq.com. + +#include "lz4_erlang.h" + +NIF_ATOMS(NIF_ATOM_DECL) +NIF_RESOURCES(NIF_RES_DECL) + +int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info); +int upgrade(ErlNifEnv* env, void** priv_data, void** old_priv_data, ERL_NIF_TERM load_info); +void unload(ErlNifEnv* env, void* priv_data); + +int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) +{ + NIF_ATOMS(NIF_ATOM_INIT) + NIF_RESOURCES(NIF_RES_INIT) + + return 0; +} + +int upgrade(ErlNifEnv* env, void** priv_data, void** old_priv_data, ERL_NIF_TERM load_info) +{ + *priv_data = *old_priv_data; + + return 0; +} + +void unload(ErlNifEnv* env, void* priv_data) +{ +} + +static ErlNifFunc nif_funcs[] = { + NIF_FUNCTIONS(NIF_FUNCTION_ARRAY) +}; + +ERL_NIF_INIT(lz4_nif, nif_funcs, load, NULL, upgrade, unload) diff --git a/c_src/lz4.o b/c_src/lz4.o new file mode 100644 index 0000000000000000000000000000000000000000..913c5e50ba277e0092c05a32747899d726b18742 GIT binary patch literal 3272 zcmb7GUuauZ7(eN{v}$`BbyX)0%TSnul@ulpB$&E(L>U^kh=0rJU2>9!CHID#8|?-{ zp*jhpQo$$jVZQk1t)LG@_Yl~8@IfjFKJ3MU2*WAV7a5ycf8ROZP11W4>OOMvo!|HW z+}j{v8q`wnyL(V-Vxg0RAR0K>INc5$;1b$pO@2o9m1bQ_zT$5+tRs7JV}} zI5+!Bu7iWwC=~&scogb$1Q{p6pjaXnkd%41T6LWco|(=~2S|vZjzmW!XpI4JngF7{ zeBJlTPKhN_?yN7U>v4AczB?Fv!jDMGg6-Sw@QA;&zBD`3f zw)|vfW&E@MWxRYdw!d{_@A@_oW(B#;o?@%y7>&sjm$TZ_qEvQxrgYVR@ z=)`kZ+_R$KI!Y+Vt*WY!PPr)k1y9)p=_y}|7oIyIPtBf|xmUBZhfcnB^n}dk{lzDG z$O~FtIe(O5h%?HrPQGb7=L^@WgLIR%!OECm&m5Xn=*QuWT3WvA#Gua3EXs+~XDf3#!g_#9%p-Vv$b z{IZwFnBw-L3nBZk&8NHgf4z%-gZ2H=T-!zedp}1m)Z&HpTi?N5^uc<6#3UwSk!+hm z9>XL0E!J~iiRg6`gN^V789|=$k6aAqR{v25sbClbP~6>sXk59 zR^r0I@zMCR%lZpTb~6|Ye;Yf?SP2o>F($hTMwack-kx?J!tNN`aorKSA+yp0-@q29 z{lHh?V?rGH1ROr8kHAsj`1|04(7(reAAAJ*D(my$_|~T007vadax8xy9Ooc)oaIlm zdPV+wBV7$us1`Ni?i0KP(jaf$d$6y=WPw4p(-a<^G zU)X=kKyjJzCgVSW@!Lc75`7TDhePF)c&jXStV`X z#chE8Av9M54RBwGL@~`bB5nugMs1?IHRaF)cN<<_N&j3;aT + +// List of atoms used by this NIF. +// +// @todo We don't use threads so perhaps we should make nif_helpers +// better by splitting concerns into threads/not and have nif_helpers +// decide when to create the _nif_thread_ret atom or not. + +#define NIF_ATOMS(A) \ + A(ok) \ + A(done) \ + A(enomem) \ + A(_nif_thread_ret_) + +// List of resources used by this NIF. + +#define NIF_RESOURCES(R) \ + R(LZ4F_cctx) \ + R(LZ4F_dctx) + +// List of functions defined in this NIF. + +#define NIF_FUNCTION_NAME(f) lz4_erlang_ ## f +#define NIF_FUNCTIONS(F) \ + F(lz4f_compress_frame, 2) \ + F(lz4f_create_compression_context, 0) \ + F(lz4f_compress_begin, 2) \ + F(lz4f_compress_update, 2) \ + F(lz4f_flush, 1) \ + F(lz4f_compress_end, 1) \ + F(lz4f_create_decompression_context, 0) \ + F(lz4f_get_frame_info, 2) \ + F(lz4f_decompress, 2) + +#include "nif_helpers.h" + +NIF_ATOMS(NIF_ATOM_H_DECL) +NIF_RESOURCES(NIF_RES_H_DECL) +NIF_FUNCTIONS(NIF_FUNCTION_H_DECL) + +#endif diff --git a/c_src/lz4f.c b/c_src/lz4f.c new file mode 100644 index 0000000..c4dd41e --- /dev/null +++ b/c_src/lz4f.c @@ -0,0 +1,286 @@ +// Copyright (c) 2017-Present Pivotal Software, Inc. All rights reserved. +// +// This package, the LZ4 binding for Erlang, is double-licensed under the Mozilla +// Public License 1.1 ("MPL") and the Apache License version 2 +// ("ASL"). For the MPL, please see LICENSE-MPL-RabbitMQ. For the ASL, +// please see LICENSE-APACHE2. +// +// This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, +// either express or implied. See the LICENSE file for specific language governing +// rights and limitations of this software. +// +// If you have any questions regarding licensing, please contact us at +// info@rabbitmq.com. + +#include "lz4_erlang.h" +#include + +void dtor_LZ4F_cctx(ErlNifEnv* env, void* obj) +{ + LZ4F_freeCompressionContext(NIF_RES_GET(LZ4F_cctx, obj)); +} + +void dtor_LZ4F_dctx(ErlNifEnv* env, void* obj) +{ + LZ4F_freeDecompressionContext(NIF_RES_GET(LZ4F_dctx, obj)); +} + +NIF_FUNCTION(lz4f_compress_frame) +{ + LZ4F_preferences_t preferences; + size_t dstCapacity, dstSize; + ErlNifBinary srcBin, dstBin; + + BADARG_IF(!enif_inspect_binary(env, argv[0], &srcBin)); + + memset(&preferences, 0, sizeof(preferences)); + + // @todo prefs + + dstCapacity = LZ4F_compressFrameBound(srcBin.size, &preferences); + + if (!enif_alloc_binary(dstCapacity, &dstBin)) + return enif_raise_exception(env, atom_enomem); + + dstSize = LZ4F_compressFrame(dstBin.data, dstCapacity, srcBin.data, srcBin.size, &preferences); + + if (LZ4F_isError(dstSize)) { + enif_release_binary(&dstBin); + return enif_raise_exception(env, enif_make_atom(env, LZ4F_getErrorName(dstSize))); + } + + if (!enif_realloc_binary(&dstBin, dstSize)) { + enif_release_binary(&dstBin); + return enif_raise_exception(env, atom_enomem); + } + + return enif_make_binary(env, &dstBin); +} + +NIF_FUNCTION(lz4f_create_compression_context) +{ + LZ4F_cctx* cctx; + LZ4F_errorCode_t result; + ERL_NIF_TERM term; + + result = LZ4F_createCompressionContext(&cctx, LZ4F_VERSION); + + if (LZ4F_isError(result)) + return enif_raise_exception(env, enif_make_atom(env, LZ4F_getErrorName(result))); + + NIF_RES_TO_TERM(LZ4F_cctx, cctx, term); + + return term; +} + +NIF_FUNCTION(lz4f_compress_begin) +{ + void* cctx_res; + LZ4F_preferences_t preferences; + size_t dstSize; + ErlNifBinary dstBin; + + BADARG_IF(!enif_get_resource(env, argv[0], res_LZ4F_cctx, &cctx_res)); + + memset(&preferences, 0, sizeof(preferences)); + + // @todo prefs + + if (!enif_alloc_binary(LZ4F_HEADER_SIZE_MAX, &dstBin)) + return enif_raise_exception(env, atom_enomem); + + dstSize = LZ4F_compressBegin(NIF_RES_GET(LZ4F_cctx, cctx_res), + dstBin.data, LZ4F_HEADER_SIZE_MAX, &preferences); + + if (LZ4F_isError(dstSize)) { + enif_release_binary(&dstBin); + return enif_raise_exception(env, enif_make_atom(env, LZ4F_getErrorName(dstSize))); + } + + if (!enif_realloc_binary(&dstBin, dstSize)) { + enif_release_binary(&dstBin); + return enif_raise_exception(env, atom_enomem); + } + + return enif_make_binary(env, &dstBin); +} + +NIF_FUNCTION(lz4f_compress_update) +{ + void* cctx_res; + size_t dstCapacity, dstSize; + ErlNifBinary srcBin, dstBin; + + BADARG_IF(!enif_get_resource(env, argv[0], res_LZ4F_cctx, &cctx_res)); + BADARG_IF(!enif_inspect_binary(env, argv[1], &srcBin)); + + // @todo We pass NULL because we don't currently keep the preferences + // setup when the user began the compression. It might be done later + // as an optimization. + dstCapacity = LZ4F_compressBound(srcBin.size, NULL); + + if (!enif_alloc_binary(dstCapacity, &dstBin)) + return enif_raise_exception(env, atom_enomem); + + // We pass NULL because we can't guarantee that the source binary + // data will remain for future calls. It may be garbage collected. + dstSize = LZ4F_compressUpdate(NIF_RES_GET(LZ4F_cctx, cctx_res), + dstBin.data, dstCapacity, srcBin.data, srcBin.size, NULL); + + if (LZ4F_isError(dstSize)) { + enif_release_binary(&dstBin); + return enif_raise_exception(env, enif_make_atom(env, LZ4F_getErrorName(dstSize))); + } + + if (!enif_realloc_binary(&dstBin, dstSize)) { + enif_release_binary(&dstBin); + return enif_raise_exception(env, atom_enomem); + } + + return enif_make_binary(env, &dstBin); +} + +NIF_FUNCTION(lz4f_flush) +{ + void* cctx_res; + size_t dstCapacity, dstSize; + ErlNifBinary dstBin; + + BADARG_IF(!enif_get_resource(env, argv[0], res_LZ4F_cctx, &cctx_res)); + + // We pass 0 to get the upper bound for this operation. + // + // @todo We pass NULL because we don't currently keep the preferences + // setup when the user began the compression. It might be done later + // as an optimization. + dstCapacity = LZ4F_compressBound(0, NULL); + + if (!enif_alloc_binary(dstCapacity, &dstBin)) + return enif_raise_exception(env, atom_enomem); + + // We pass NULL because we can't guarantee that the source binary + // data will remain for future calls. It may be garbage collected. + dstSize = LZ4F_flush(NIF_RES_GET(LZ4F_cctx, cctx_res), + dstBin.data, dstCapacity, NULL); + + if (LZ4F_isError(dstSize)) { + enif_release_binary(&dstBin); + return enif_raise_exception(env, enif_make_atom(env, LZ4F_getErrorName(dstSize))); + } + + if (!enif_realloc_binary(&dstBin, dstSize)) { + enif_release_binary(&dstBin); + return enif_raise_exception(env, atom_enomem); + } + + return enif_make_binary(env, &dstBin); +} + +NIF_FUNCTION(lz4f_compress_end) +{ + void* cctx_res; + size_t dstCapacity, dstSize; + ErlNifBinary dstBin; + + BADARG_IF(!enif_get_resource(env, argv[0], res_LZ4F_cctx, &cctx_res)); + + // We pass 0 to get the upper bound for this operation. + // + // @todo We pass NULL because we don't currently keep the preferences + // setup when the user began the compression. It might be done later + // as an optimization. + dstCapacity = LZ4F_compressBound(0, NULL); + + if (!enif_alloc_binary(dstCapacity, &dstBin)) + return enif_raise_exception(env, atom_enomem); + + // We pass NULL because we can't guarantee that the source binary + // data will remain for future calls. It may be garbage collected. + dstSize = LZ4F_compressEnd(NIF_RES_GET(LZ4F_cctx, cctx_res), + dstBin.data, dstCapacity, NULL); + + if (LZ4F_isError(dstSize)) { + enif_release_binary(&dstBin); + return enif_raise_exception(env, enif_make_atom(env, LZ4F_getErrorName(dstSize))); + } + + if (!enif_realloc_binary(&dstBin, dstSize)) { + enif_release_binary(&dstBin); + return enif_raise_exception(env, atom_enomem); + } + + return enif_make_binary(env, &dstBin); +} + +NIF_FUNCTION(lz4f_create_decompression_context) +{ + LZ4F_dctx* dctx; + LZ4F_errorCode_t result; + ERL_NIF_TERM term; + + result = LZ4F_createDecompressionContext(&dctx, LZ4F_VERSION); + + if (LZ4F_isError(result)) + return enif_raise_exception(env, enif_make_atom(env, LZ4F_getErrorName(result))); + + NIF_RES_TO_TERM(LZ4F_dctx, dctx, term); + + return term; +} + +NIF_FUNCTION(lz4f_get_frame_info) +{ + // @todo + + return atom_ok; +} + +NIF_FUNCTION(lz4f_decompress) +{ + void* dctx_res; + ERL_NIF_TERM head, reversed; + size_t dstSize, srcRead, srcSize; + ErlNifBinary srcBin, dstBin; + + BADARG_IF(!enif_get_resource(env, argv[0], res_LZ4F_dctx, &dctx_res)); + BADARG_IF(!enif_inspect_binary(env, argv[1], &srcBin)); + + srcRead = 0; + srcSize = srcBin.size; + + head = enif_make_list(env, 0); + + while (srcSize) { + dstSize = 65536; // Arbitrary maximum size of chunk. + + if (!enif_alloc_binary(dstSize, &dstBin)) { + return enif_raise_exception(env, atom_enomem); + } + + LZ4F_decompress(NIF_RES_GET(LZ4F_dctx, dctx_res), + dstBin.data, &dstSize, + srcBin.data + srcRead, &srcSize, + NULL); + + if (LZ4F_isError(dstSize)) { + enif_release_binary(&dstBin); + return enif_raise_exception(env, atom_enomem); + } + + if (!enif_realloc_binary(&dstBin, dstSize)) { + enif_release_binary(&dstBin); + return enif_raise_exception(env, atom_enomem); + } + + head = enif_make_list_cell(env, enif_make_binary(env, &dstBin), head); + + srcRead += srcSize; + srcSize = srcBin.size - srcRead; + } + + // We don't check the return value because no error can occur + // according to the function's source code. + enif_make_reverse_list(env, head, &reversed); + + return reversed; +} diff --git a/c_src/lz4f.o b/c_src/lz4f.o new file mode 100644 index 0000000000000000000000000000000000000000..be8228e18fa3f82e3e7c17744bac147715ec54fa GIT binary patch literal 5868 zcmds*e{5S<6~|xNq)lm7oAG0evDIa5SqnzZ)`?Kjsc!4+Wpbu%ZPwB)yt?*tW7V-U z$7X4#pcE%XPo77BO)C|Gpmoxa_J=2as35voO0>EXKTl?v&g0@1Nw4d3tm z;UCXQhr}Nsp7Q*8_nvdlIrp4<-+Ot#eCzeA%MGJxsevDy%{Ys^@ZrR9BhC)C7deXW zlY(ibqua1)@T6THmX)!GGV)rscL(?GuD12n5C`F)J;pN9W*A9!G#ev?J}fJe>K_P4 zGFCSEXgnFUtk6y^Bek9VDlT({jODV&Fjh*%CAIpNW%pUJbhzJ^y_QuU-%P9QpHcRn z!@Tv}D1psBsUd&Y0$yIW$pq2aM{t_x{1n*w0mC>C1B~T9oV5r0c&@4K{5P+AOy`1` zzpM98)c3457HS6`++`lUGy;V#=c<{%-7NGJFH(Q3jJ?7tO*C*(FAp&ZJq$PMgl}m4VD-P?<6F-9;!a3AMh7@mDG3b~46vSuE)>T4NEO;B2S?sKO0*YP52Ed*W888aW8?Q)iP zow>Uey#@?~oU3|N$kCabF>_O`h%8|BPR2Z|N2x30Lw$0W_N(G(A6~Q2eNk9J2C}k5 zMU-fQr9yp59FD}4U&7Lou|V-Kg1KnsJIp+)ROlZ6Nkz!gRf|#nkBU*&`JQ7j%5ttc z@MtCR7r#GgrDk~M`P`JL%RVmFqnQ1sYt=j2KCdPycU)PQ4xY0$C2l`!IxB6?o+C7gKLZ>4dZ>P=V{2pO-mc(CDn6bk?}?KwsUon@kRAA!l>&h4wYZS2(0oRTe9OiWmf3zgx=*V@2!`B-i*>?J;b5y-IHs60=c5!Cvg^) zU(Q<`TCX$L@^vn8)L+jmLwRawZ%Sw-R5{K67s@iMAyvuJ&d#p#D zUX8uY&>MN{z4ac3UdP)~?^^R1gGkZ+Gv95Whu(3e$9mSq*PUDb__Bh?4f}1p z@2@uu|2`1^Yi!*G-iEdb`oJyVI?xN=3N8WHfPY6}KLP#`#Cx^>RS>T>{ujWTz#RB- zFbduRc7Zp8TftAl&kf*5!GB=gZb1K^LDb6sa}evvUjnK318_as-vd{J-v)00zYfyx zSHSDRJs|Z$AmZ3*0TR+Am?)$#J@uR zBFK511UJIZqaghzK>D>o`rQZ8?>!*>ZUgCe6G*@7K>EEIq`nuV-xd&i&3_4NoPK`? z((g1#zf&Oneg~xACqeoxfVA^vkbcu3{q}+MyBDP2ZjgS1AoV*y`t^fYQ~s48{l0_y z75%;rvVOk?>HjQ9|Hnc4FM#xa45YosK>9xf(tjAF|NBAu?*!@pE|B`|ApNfgSAr`* z`k!r*{$2s;?^%%R+a^m8vrKifgt{|xwka3x4R17zH98ZwT* zfNRkI8pyc0uYKSYNP9m9Y3~J)ai0Wf_nY7<@CeAb4}tg>*Z*0NdAkw(Ft`Hz5V#F~ z@wd#U$OhZ9kdr0F3B?h`{fgTZeTsk>_YKek1Nz9Qw!TkXkGxV|Qaay9o!K78dgF0P z$$g?_7(Y^N{oFc-e(L+w_-9mGKX<;1ej#5b4II_u;fKd>m0UlEZiif%67plvt8Jfi zwcA|n6R!4eT=urYSgpNx;ia~H+9l_Du9fqiT-(0I)%Lmk|H37I&}DDP)qcPg&m%5- zcevV&r8XY!quTaKR~z@vI{DXJ@kLzvy)J);UHb31+FM=rvBvBCb-L`0y5zR2ZCG|P z9<$=f!2uf|l@7#{;q+nEnYI&lc+jrz><=Gu^`z}^B9V&JtJQaQy9h$##2dr7E6jRZB$uEdqG_h zlQg%p*4hqjOenES-6tM>~NMz$EojL1(eJ~EWOq!bRcG*{* zOgNM3H!S{QWu^LUG^#?*$cosBgwjYIYIGM%WC#0{7j<1Va3CBFrw=v`s>GNbh+=KH zg6vV9?o?Jr$q%6QGwT!|MP(lnCsKMZzmv-OFqc?+scbr;yYzl>b*mcJsmkq9Rjkix XDw~YDrXZr(E(MED!)_QBM33<=#HL_z literal 0 HcmV?d00001 diff --git a/ebin/lz4.app b/ebin/lz4.app new file mode 100644 index 0000000..6afd72c --- /dev/null +++ b/ebin/lz4.app @@ -0,0 +1,8 @@ +{application, 'lz4', [ + {description, "New project"}, + {vsn, "0.1.0"}, + {modules, ['leveled_app','leveled_bookie','leveled_cdb','leveled_codec','leveled_iclerk','leveled_imanifest','leveled_inker','leveled_log','leveled_pclerk','leveled_penciller','leveled_pmanifest','leveled_pmem','leveled_rand','leveled_runner','leveled_sst','leveled_sup','leveled_tictac','leveled_tinybloom','leveled_tree','lz4_nif','lz4f']}, + {registered, []}, + {applications, [kernel,stdlib]}, + {env, []} +]}. \ No newline at end of file diff --git a/erlang.mk b/erlang.mk new file mode 100644 index 0000000..64aaa4e --- /dev/null +++ b/erlang.mk @@ -0,0 +1,6889 @@ +# Copyright (c) 2013-2016, 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 app apps deps search rel relup docs install-docs check tests clean distclean help erlang-mk + +ERLANG_MK_FILENAME := $(realpath $(lastword $(MAKEFILE_LIST))) + +ERLANG_MK_VERSION = 2017.04.25-6-g5922969 + +# Make 3.81 and 3.82 are deprecated. + +ifeq ($(MAKE_VERSION),3.81) +$(warning Please upgrade to GNU Make 4 or later: https://erlang.mk/guide/installation.html) +endif + +ifeq ($(MAKE_VERSION),3.82) +$(warning Please upgrade to GNU Make 4 or later: https://erlang.mk/guide/installation.html) +endif + +# Core configuration. + +PROJECT ?= $(notdir $(CURDIR)) +PROJECT := $(strip $(PROJECT)) + +PROJECT_VERSION ?= rolling +PROJECT_MOD ?= $(PROJECT)_app +PROJECT_ENV ?= [] + +# Verbosity. + +V ?= 0 + +verbose_0 = @ +verbose_2 = set -x; +verbose = $(verbose_$(V)) + +gen_verbose_0 = @echo " GEN " $@; +gen_verbose_2 = set -x; +gen_verbose = $(gen_verbose_$(V)) + +# Temporary files directory. + +ERLANG_MK_TMP ?= $(CURDIR)/.erlang.mk +export ERLANG_MK_TMP + +# "erl" command. + +ERL = erl +A0 -noinput -boot start_clean + +# Platform detection. + +ifeq ($(PLATFORM),) +UNAME_S := $(shell uname -s) + +ifeq ($(UNAME_S),Linux) +PLATFORM = linux +else ifeq ($(UNAME_S),Darwin) +PLATFORM = darwin +else ifeq ($(UNAME_S),SunOS) +PLATFORM = solaris +else ifeq ($(UNAME_S),GNU) +PLATFORM = gnu +else ifeq ($(UNAME_S),FreeBSD) +PLATFORM = freebsd +else ifeq ($(UNAME_S),NetBSD) +PLATFORM = netbsd +else ifeq ($(UNAME_S),OpenBSD) +PLATFORM = openbsd +else ifeq ($(UNAME_S),DragonFly) +PLATFORM = dragonfly +else ifeq ($(shell uname -o),Msys) +PLATFORM = msys2 +else +$(error Unable to detect platform. Please open a ticket with the output of uname -a.) +endif + +export PLATFORM +endif + +# Core targets. + +all:: deps app rel + +# Noop to avoid a Make warning when there's nothing to do. +rel:: + $(verbose) : + +relup:: deps app + +check:: tests + +clean:: clean-crashdump + +clean-crashdump: +ifneq ($(wildcard erl_crash.dump),) + $(gen_verbose) rm -f erl_crash.dump +endif + +distclean:: clean distclean-tmp + +distclean-tmp: + $(gen_verbose) rm -rf $(ERLANG_MK_TMP) + +help:: + $(verbose) printf "%s\n" \ + "erlang.mk (version $(ERLANG_MK_VERSION)) is distributed under the terms of the ISC License." \ + "Copyright (c) 2013-2016 Loïc Hoguin " \ + "" \ + "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" \ + " fetch-deps Fetch dependencies recursively (if needed) without compiling them" \ + " list-deps List dependencies recursively on stdout" \ + " 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" + +# Core functions. + +empty := +space := $(empty) $(empty) +tab := $(empty) $(empty) +comma := , + +define newline + + +endef + +define comma_list +$(subst $(space),$(comma),$(strip $(1))) +endef + +# Adding erlang.mk to make Erlang scripts who call init:get_plain_arguments() happy. +define erlang +$(ERL) $(2) -pz $(ERLANG_MK_TMP)/rebar/ebin -eval "$(subst $(newline),,$(subst ",\",$(1)))" -- erlang.mk +endef + +ifeq ($(PLATFORM),msys2) +core_native_path = $(subst \,\\\\,$(shell cygpath -w $1)) +else +core_native_path = $1 +endif + +core_http_get = curl -Lf$(if $(filter-out 0,$(V)),,s)o $(call core_native_path,$1) $2 + +core_eq = $(and $(findstring $(1),$(2)),$(findstring $(2),$(1))) + +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))))))))))))))))))))))))))) + +core_ls = $(filter-out $(1),$(shell echo $(1))) + +# @todo Use a solution that does not require using perl. +core_relpath = $(shell perl -e 'use File::Spec; print File::Spec->abs2rel(@ARGV) . "\n"' $1 $2) + +# Automated update. + +ERLANG_MK_REPO ?= https://github.com/ninenines/erlang.mk +ERLANG_MK_COMMIT ?= +ERLANG_MK_BUILD_CONFIG ?= build.config +ERLANG_MK_BUILD_DIR ?= .erlang.mk.build + +erlang-mk: + git clone $(ERLANG_MK_REPO) $(ERLANG_MK_BUILD_DIR) +ifdef ERLANG_MK_COMMIT + cd $(ERLANG_MK_BUILD_DIR) && git checkout $(ERLANG_MK_COMMIT) +endif + if [ -f $(ERLANG_MK_BUILD_CONFIG) ]; then cp $(ERLANG_MK_BUILD_CONFIG) $(ERLANG_MK_BUILD_DIR)/build.config; fi + $(MAKE) -C $(ERLANG_MK_BUILD_DIR) + cp $(ERLANG_MK_BUILD_DIR)/erlang.mk ./erlang.mk + rm -rf $(ERLANG_MK_BUILD_DIR) + +# The erlang.mk package index is bundled in the default erlang.mk build. +# Search for the string "copyright" to skip to the rest of the code. + +PACKAGES += aberth +pkg_aberth_name = aberth +pkg_aberth_description = Generic BERT-RPC server in Erlang +pkg_aberth_homepage = https://github.com/a13x/aberth +pkg_aberth_fetch = git +pkg_aberth_repo = https://github.com/a13x/aberth +pkg_aberth_commit = master + +PACKAGES += active +pkg_active_name = active +pkg_active_description = Active development for Erlang: rebuild and reload source/binary files while the VM is running +pkg_active_homepage = https://github.com/proger/active +pkg_active_fetch = git +pkg_active_repo = https://github.com/proger/active +pkg_active_commit = master + +PACKAGES += actordb_core +pkg_actordb_core_name = actordb_core +pkg_actordb_core_description = ActorDB main source +pkg_actordb_core_homepage = http://www.actordb.com/ +pkg_actordb_core_fetch = git +pkg_actordb_core_repo = https://github.com/biokoda/actordb_core +pkg_actordb_core_commit = master + +PACKAGES += actordb_thrift +pkg_actordb_thrift_name = actordb_thrift +pkg_actordb_thrift_description = Thrift API for ActorDB +pkg_actordb_thrift_homepage = http://www.actordb.com/ +pkg_actordb_thrift_fetch = git +pkg_actordb_thrift_repo = https://github.com/biokoda/actordb_thrift +pkg_actordb_thrift_commit = master + +PACKAGES += aleppo +pkg_aleppo_name = aleppo +pkg_aleppo_description = Alternative Erlang Pre-Processor +pkg_aleppo_homepage = https://github.com/ErlyORM/aleppo +pkg_aleppo_fetch = git +pkg_aleppo_repo = https://github.com/ErlyORM/aleppo +pkg_aleppo_commit = master + +PACKAGES += alog +pkg_alog_name = alog +pkg_alog_description = Simply the best logging framework for Erlang +pkg_alog_homepage = https://github.com/siberian-fast-food/alogger +pkg_alog_fetch = git +pkg_alog_repo = https://github.com/siberian-fast-food/alogger +pkg_alog_commit = master + +PACKAGES += amqp_client +pkg_amqp_client_name = amqp_client +pkg_amqp_client_description = RabbitMQ Erlang AMQP client +pkg_amqp_client_homepage = https://www.rabbitmq.com/erlang-client-user-guide.html +pkg_amqp_client_fetch = git +pkg_amqp_client_repo = https://github.com/rabbitmq/rabbitmq-erlang-client.git +pkg_amqp_client_commit = master + +PACKAGES += annotations +pkg_annotations_name = annotations +pkg_annotations_description = Simple code instrumentation utilities +pkg_annotations_homepage = https://github.com/hyperthunk/annotations +pkg_annotations_fetch = git +pkg_annotations_repo = https://github.com/hyperthunk/annotations +pkg_annotations_commit = master + +PACKAGES += antidote +pkg_antidote_name = antidote +pkg_antidote_description = Large-scale computation without synchronisation +pkg_antidote_homepage = https://syncfree.lip6.fr/ +pkg_antidote_fetch = git +pkg_antidote_repo = https://github.com/SyncFree/antidote +pkg_antidote_commit = master + +PACKAGES += apns +pkg_apns_name = apns +pkg_apns_description = Apple Push Notification Server for Erlang +pkg_apns_homepage = http://inaka.github.com/apns4erl +pkg_apns_fetch = git +pkg_apns_repo = https://github.com/inaka/apns4erl +pkg_apns_commit = master + +PACKAGES += asciideck +pkg_asciideck_name = asciideck +pkg_asciideck_description = Asciidoc for Erlang. +pkg_asciideck_homepage = https://ninenines.eu +pkg_asciideck_fetch = git +pkg_asciideck_repo = https://github.com/ninenines/asciideck +pkg_asciideck_commit = master + +PACKAGES += azdht +pkg_azdht_name = azdht +pkg_azdht_description = Azureus Distributed Hash Table (DHT) in Erlang +pkg_azdht_homepage = https://github.com/arcusfelis/azdht +pkg_azdht_fetch = git +pkg_azdht_repo = https://github.com/arcusfelis/azdht +pkg_azdht_commit = master + +PACKAGES += backoff +pkg_backoff_name = backoff +pkg_backoff_description = Simple exponential backoffs in Erlang +pkg_backoff_homepage = https://github.com/ferd/backoff +pkg_backoff_fetch = git +pkg_backoff_repo = https://github.com/ferd/backoff +pkg_backoff_commit = master + +PACKAGES += barrel_tcp +pkg_barrel_tcp_name = barrel_tcp +pkg_barrel_tcp_description = barrel is a generic TCP acceptor pool with low latency in Erlang. +pkg_barrel_tcp_homepage = https://github.com/benoitc-attic/barrel_tcp +pkg_barrel_tcp_fetch = git +pkg_barrel_tcp_repo = https://github.com/benoitc-attic/barrel_tcp +pkg_barrel_tcp_commit = master + +PACKAGES += basho_bench +pkg_basho_bench_name = basho_bench +pkg_basho_bench_description = A load-generation and testing tool for basically whatever you can write a returning Erlang function for. +pkg_basho_bench_homepage = https://github.com/basho/basho_bench +pkg_basho_bench_fetch = git +pkg_basho_bench_repo = https://github.com/basho/basho_bench +pkg_basho_bench_commit = master + +PACKAGES += bcrypt +pkg_bcrypt_name = bcrypt +pkg_bcrypt_description = Bcrypt Erlang / C library +pkg_bcrypt_homepage = https://github.com/riverrun/branglecrypt +pkg_bcrypt_fetch = git +pkg_bcrypt_repo = https://github.com/riverrun/branglecrypt +pkg_bcrypt_commit = master + +PACKAGES += beam +pkg_beam_name = beam +pkg_beam_description = BEAM emulator written in Erlang +pkg_beam_homepage = https://github.com/tonyrog/beam +pkg_beam_fetch = git +pkg_beam_repo = https://github.com/tonyrog/beam +pkg_beam_commit = master + +PACKAGES += beanstalk +pkg_beanstalk_name = beanstalk +pkg_beanstalk_description = An Erlang client for beanstalkd +pkg_beanstalk_homepage = https://github.com/tim/erlang-beanstalk +pkg_beanstalk_fetch = git +pkg_beanstalk_repo = https://github.com/tim/erlang-beanstalk +pkg_beanstalk_commit = master + +PACKAGES += bear +pkg_bear_name = bear +pkg_bear_description = a set of statistics functions for erlang +pkg_bear_homepage = https://github.com/boundary/bear +pkg_bear_fetch = git +pkg_bear_repo = https://github.com/boundary/bear +pkg_bear_commit = master + +PACKAGES += bertconf +pkg_bertconf_name = bertconf +pkg_bertconf_description = Make ETS tables out of statc BERT files that are auto-reloaded +pkg_bertconf_homepage = https://github.com/ferd/bertconf +pkg_bertconf_fetch = git +pkg_bertconf_repo = https://github.com/ferd/bertconf +pkg_bertconf_commit = master + +PACKAGES += bifrost +pkg_bifrost_name = bifrost +pkg_bifrost_description = Erlang FTP Server Framework +pkg_bifrost_homepage = https://github.com/thorstadt/bifrost +pkg_bifrost_fetch = git +pkg_bifrost_repo = https://github.com/thorstadt/bifrost +pkg_bifrost_commit = master + +PACKAGES += binpp +pkg_binpp_name = binpp +pkg_binpp_description = Erlang Binary Pretty Printer +pkg_binpp_homepage = https://github.com/jtendo/binpp +pkg_binpp_fetch = git +pkg_binpp_repo = https://github.com/jtendo/binpp +pkg_binpp_commit = master + +PACKAGES += bisect +pkg_bisect_name = bisect +pkg_bisect_description = Ordered fixed-size binary dictionary in Erlang +pkg_bisect_homepage = https://github.com/knutin/bisect +pkg_bisect_fetch = git +pkg_bisect_repo = https://github.com/knutin/bisect +pkg_bisect_commit = master + +PACKAGES += bitcask +pkg_bitcask_name = bitcask +pkg_bitcask_description = because you need another a key/value storage engine +pkg_bitcask_homepage = https://github.com/basho/bitcask +pkg_bitcask_fetch = git +pkg_bitcask_repo = https://github.com/basho/bitcask +pkg_bitcask_commit = develop + +PACKAGES += bitstore +pkg_bitstore_name = bitstore +pkg_bitstore_description = A document based ontology development environment +pkg_bitstore_homepage = https://github.com/bdionne/bitstore +pkg_bitstore_fetch = git +pkg_bitstore_repo = https://github.com/bdionne/bitstore +pkg_bitstore_commit = master + +PACKAGES += bootstrap +pkg_bootstrap_name = bootstrap +pkg_bootstrap_description = A simple, yet powerful Erlang cluster bootstrapping application. +pkg_bootstrap_homepage = https://github.com/schlagert/bootstrap +pkg_bootstrap_fetch = git +pkg_bootstrap_repo = https://github.com/schlagert/bootstrap +pkg_bootstrap_commit = master + +PACKAGES += boss +pkg_boss_name = boss +pkg_boss_description = Erlang web MVC, now featuring Comet +pkg_boss_homepage = https://github.com/ChicagoBoss/ChicagoBoss +pkg_boss_fetch = git +pkg_boss_repo = https://github.com/ChicagoBoss/ChicagoBoss +pkg_boss_commit = master + +PACKAGES += boss_db +pkg_boss_db_name = boss_db +pkg_boss_db_description = BossDB: a sharded, caching, pooling, evented ORM for Erlang +pkg_boss_db_homepage = https://github.com/ErlyORM/boss_db +pkg_boss_db_fetch = git +pkg_boss_db_repo = https://github.com/ErlyORM/boss_db +pkg_boss_db_commit = master + +PACKAGES += brod +pkg_brod_name = brod +pkg_brod_description = Kafka client in Erlang +pkg_brod_homepage = https://github.com/klarna/brod +pkg_brod_fetch = git +pkg_brod_repo = https://github.com/klarna/brod.git +pkg_brod_commit = master + +PACKAGES += bson +pkg_bson_name = bson +pkg_bson_description = BSON documents in Erlang, see bsonspec.org +pkg_bson_homepage = https://github.com/comtihon/bson-erlang +pkg_bson_fetch = git +pkg_bson_repo = https://github.com/comtihon/bson-erlang +pkg_bson_commit = master + +PACKAGES += bullet +pkg_bullet_name = bullet +pkg_bullet_description = Simple, reliable, efficient streaming for Cowboy. +pkg_bullet_homepage = http://ninenines.eu +pkg_bullet_fetch = git +pkg_bullet_repo = https://github.com/ninenines/bullet +pkg_bullet_commit = master + +PACKAGES += cache +pkg_cache_name = cache +pkg_cache_description = Erlang in-memory cache +pkg_cache_homepage = https://github.com/fogfish/cache +pkg_cache_fetch = git +pkg_cache_repo = https://github.com/fogfish/cache +pkg_cache_commit = master + +PACKAGES += cake +pkg_cake_name = cake +pkg_cake_description = Really simple terminal colorization +pkg_cake_homepage = https://github.com/darach/cake-erl +pkg_cake_fetch = git +pkg_cake_repo = https://github.com/darach/cake-erl +pkg_cake_commit = master + +PACKAGES += carotene +pkg_carotene_name = carotene +pkg_carotene_description = Real-time server +pkg_carotene_homepage = https://github.com/carotene/carotene +pkg_carotene_fetch = git +pkg_carotene_repo = https://github.com/carotene/carotene +pkg_carotene_commit = master + +PACKAGES += cberl +pkg_cberl_name = cberl +pkg_cberl_description = NIF based Erlang bindings for Couchbase +pkg_cberl_homepage = https://github.com/chitika/cberl +pkg_cberl_fetch = git +pkg_cberl_repo = https://github.com/chitika/cberl +pkg_cberl_commit = master + +PACKAGES += cecho +pkg_cecho_name = cecho +pkg_cecho_description = An ncurses library for Erlang +pkg_cecho_homepage = https://github.com/mazenharake/cecho +pkg_cecho_fetch = git +pkg_cecho_repo = https://github.com/mazenharake/cecho +pkg_cecho_commit = master + +PACKAGES += cferl +pkg_cferl_name = cferl +pkg_cferl_description = Rackspace / Open Stack Cloud Files Erlang Client +pkg_cferl_homepage = https://github.com/ddossot/cferl +pkg_cferl_fetch = git +pkg_cferl_repo = https://github.com/ddossot/cferl +pkg_cferl_commit = master + +PACKAGES += chaos_monkey +pkg_chaos_monkey_name = chaos_monkey +pkg_chaos_monkey_description = This is The CHAOS MONKEY. It will kill your processes. +pkg_chaos_monkey_homepage = https://github.com/dLuna/chaos_monkey +pkg_chaos_monkey_fetch = git +pkg_chaos_monkey_repo = https://github.com/dLuna/chaos_monkey +pkg_chaos_monkey_commit = master + +PACKAGES += check_node +pkg_check_node_name = check_node +pkg_check_node_description = Nagios Scripts for monitoring Riak +pkg_check_node_homepage = https://github.com/basho-labs/riak_nagios +pkg_check_node_fetch = git +pkg_check_node_repo = https://github.com/basho-labs/riak_nagios +pkg_check_node_commit = master + +PACKAGES += chronos +pkg_chronos_name = chronos +pkg_chronos_description = Timer module for Erlang that makes it easy to abstact time out of the tests. +pkg_chronos_homepage = https://github.com/lehoff/chronos +pkg_chronos_fetch = git +pkg_chronos_repo = https://github.com/lehoff/chronos +pkg_chronos_commit = master + +PACKAGES += chumak +pkg_chumak_name = chumak +pkg_chumak_description = Pure Erlang implementation of ZeroMQ Message Transport Protocol. +pkg_chumak_homepage = http://choven.ca +pkg_chumak_fetch = git +pkg_chumak_repo = https://github.com/chovencorp/chumak +pkg_chumak_commit = master + +PACKAGES += cl +pkg_cl_name = cl +pkg_cl_description = OpenCL binding for Erlang +pkg_cl_homepage = https://github.com/tonyrog/cl +pkg_cl_fetch = git +pkg_cl_repo = https://github.com/tonyrog/cl +pkg_cl_commit = master + +PACKAGES += classifier +pkg_classifier_name = classifier +pkg_classifier_description = An Erlang Bayesian Filter and Text Classifier +pkg_classifier_homepage = https://github.com/inaka/classifier +pkg_classifier_fetch = git +pkg_classifier_repo = https://github.com/inaka/classifier +pkg_classifier_commit = master + +PACKAGES += clique +pkg_clique_name = clique +pkg_clique_description = CLI Framework for Erlang +pkg_clique_homepage = https://github.com/basho/clique +pkg_clique_fetch = git +pkg_clique_repo = https://github.com/basho/clique +pkg_clique_commit = develop + +PACKAGES += cloudi_core +pkg_cloudi_core_name = cloudi_core +pkg_cloudi_core_description = CloudI internal service runtime +pkg_cloudi_core_homepage = http://cloudi.org/ +pkg_cloudi_core_fetch = git +pkg_cloudi_core_repo = https://github.com/CloudI/cloudi_core +pkg_cloudi_core_commit = master + +PACKAGES += cloudi_service_api_requests +pkg_cloudi_service_api_requests_name = cloudi_service_api_requests +pkg_cloudi_service_api_requests_description = CloudI Service API requests (JSON-RPC/Erlang-term support) +pkg_cloudi_service_api_requests_homepage = http://cloudi.org/ +pkg_cloudi_service_api_requests_fetch = git +pkg_cloudi_service_api_requests_repo = https://github.com/CloudI/cloudi_service_api_requests +pkg_cloudi_service_api_requests_commit = master + +PACKAGES += cloudi_service_db +pkg_cloudi_service_db_name = cloudi_service_db +pkg_cloudi_service_db_description = CloudI Database (in-memory/testing/generic) +pkg_cloudi_service_db_homepage = http://cloudi.org/ +pkg_cloudi_service_db_fetch = git +pkg_cloudi_service_db_repo = https://github.com/CloudI/cloudi_service_db +pkg_cloudi_service_db_commit = master + +PACKAGES += cloudi_service_db_cassandra +pkg_cloudi_service_db_cassandra_name = cloudi_service_db_cassandra +pkg_cloudi_service_db_cassandra_description = Cassandra CloudI Service +pkg_cloudi_service_db_cassandra_homepage = http://cloudi.org/ +pkg_cloudi_service_db_cassandra_fetch = git +pkg_cloudi_service_db_cassandra_repo = https://github.com/CloudI/cloudi_service_db_cassandra +pkg_cloudi_service_db_cassandra_commit = master + +PACKAGES += cloudi_service_db_cassandra_cql +pkg_cloudi_service_db_cassandra_cql_name = cloudi_service_db_cassandra_cql +pkg_cloudi_service_db_cassandra_cql_description = Cassandra CQL CloudI Service +pkg_cloudi_service_db_cassandra_cql_homepage = http://cloudi.org/ +pkg_cloudi_service_db_cassandra_cql_fetch = git +pkg_cloudi_service_db_cassandra_cql_repo = https://github.com/CloudI/cloudi_service_db_cassandra_cql +pkg_cloudi_service_db_cassandra_cql_commit = master + +PACKAGES += cloudi_service_db_couchdb +pkg_cloudi_service_db_couchdb_name = cloudi_service_db_couchdb +pkg_cloudi_service_db_couchdb_description = CouchDB CloudI Service +pkg_cloudi_service_db_couchdb_homepage = http://cloudi.org/ +pkg_cloudi_service_db_couchdb_fetch = git +pkg_cloudi_service_db_couchdb_repo = https://github.com/CloudI/cloudi_service_db_couchdb +pkg_cloudi_service_db_couchdb_commit = master + +PACKAGES += cloudi_service_db_elasticsearch +pkg_cloudi_service_db_elasticsearch_name = cloudi_service_db_elasticsearch +pkg_cloudi_service_db_elasticsearch_description = elasticsearch CloudI Service +pkg_cloudi_service_db_elasticsearch_homepage = http://cloudi.org/ +pkg_cloudi_service_db_elasticsearch_fetch = git +pkg_cloudi_service_db_elasticsearch_repo = https://github.com/CloudI/cloudi_service_db_elasticsearch +pkg_cloudi_service_db_elasticsearch_commit = master + +PACKAGES += cloudi_service_db_memcached +pkg_cloudi_service_db_memcached_name = cloudi_service_db_memcached +pkg_cloudi_service_db_memcached_description = memcached CloudI Service +pkg_cloudi_service_db_memcached_homepage = http://cloudi.org/ +pkg_cloudi_service_db_memcached_fetch = git +pkg_cloudi_service_db_memcached_repo = https://github.com/CloudI/cloudi_service_db_memcached +pkg_cloudi_service_db_memcached_commit = master + +PACKAGES += cloudi_service_db_mysql +pkg_cloudi_service_db_mysql_name = cloudi_service_db_mysql +pkg_cloudi_service_db_mysql_description = MySQL CloudI Service +pkg_cloudi_service_db_mysql_homepage = http://cloudi.org/ +pkg_cloudi_service_db_mysql_fetch = git +pkg_cloudi_service_db_mysql_repo = https://github.com/CloudI/cloudi_service_db_mysql +pkg_cloudi_service_db_mysql_commit = master + +PACKAGES += cloudi_service_db_pgsql +pkg_cloudi_service_db_pgsql_name = cloudi_service_db_pgsql +pkg_cloudi_service_db_pgsql_description = PostgreSQL CloudI Service +pkg_cloudi_service_db_pgsql_homepage = http://cloudi.org/ +pkg_cloudi_service_db_pgsql_fetch = git +pkg_cloudi_service_db_pgsql_repo = https://github.com/CloudI/cloudi_service_db_pgsql +pkg_cloudi_service_db_pgsql_commit = master + +PACKAGES += cloudi_service_db_riak +pkg_cloudi_service_db_riak_name = cloudi_service_db_riak +pkg_cloudi_service_db_riak_description = Riak CloudI Service +pkg_cloudi_service_db_riak_homepage = http://cloudi.org/ +pkg_cloudi_service_db_riak_fetch = git +pkg_cloudi_service_db_riak_repo = https://github.com/CloudI/cloudi_service_db_riak +pkg_cloudi_service_db_riak_commit = master + +PACKAGES += cloudi_service_db_tokyotyrant +pkg_cloudi_service_db_tokyotyrant_name = cloudi_service_db_tokyotyrant +pkg_cloudi_service_db_tokyotyrant_description = Tokyo Tyrant CloudI Service +pkg_cloudi_service_db_tokyotyrant_homepage = http://cloudi.org/ +pkg_cloudi_service_db_tokyotyrant_fetch = git +pkg_cloudi_service_db_tokyotyrant_repo = https://github.com/CloudI/cloudi_service_db_tokyotyrant +pkg_cloudi_service_db_tokyotyrant_commit = master + +PACKAGES += cloudi_service_filesystem +pkg_cloudi_service_filesystem_name = cloudi_service_filesystem +pkg_cloudi_service_filesystem_description = Filesystem CloudI Service +pkg_cloudi_service_filesystem_homepage = http://cloudi.org/ +pkg_cloudi_service_filesystem_fetch = git +pkg_cloudi_service_filesystem_repo = https://github.com/CloudI/cloudi_service_filesystem +pkg_cloudi_service_filesystem_commit = master + +PACKAGES += cloudi_service_http_client +pkg_cloudi_service_http_client_name = cloudi_service_http_client +pkg_cloudi_service_http_client_description = HTTP client CloudI Service +pkg_cloudi_service_http_client_homepage = http://cloudi.org/ +pkg_cloudi_service_http_client_fetch = git +pkg_cloudi_service_http_client_repo = https://github.com/CloudI/cloudi_service_http_client +pkg_cloudi_service_http_client_commit = master + +PACKAGES += cloudi_service_http_cowboy +pkg_cloudi_service_http_cowboy_name = cloudi_service_http_cowboy +pkg_cloudi_service_http_cowboy_description = cowboy HTTP/HTTPS CloudI Service +pkg_cloudi_service_http_cowboy_homepage = http://cloudi.org/ +pkg_cloudi_service_http_cowboy_fetch = git +pkg_cloudi_service_http_cowboy_repo = https://github.com/CloudI/cloudi_service_http_cowboy +pkg_cloudi_service_http_cowboy_commit = master + +PACKAGES += cloudi_service_http_elli +pkg_cloudi_service_http_elli_name = cloudi_service_http_elli +pkg_cloudi_service_http_elli_description = elli HTTP CloudI Service +pkg_cloudi_service_http_elli_homepage = http://cloudi.org/ +pkg_cloudi_service_http_elli_fetch = git +pkg_cloudi_service_http_elli_repo = https://github.com/CloudI/cloudi_service_http_elli +pkg_cloudi_service_http_elli_commit = master + +PACKAGES += cloudi_service_map_reduce +pkg_cloudi_service_map_reduce_name = cloudi_service_map_reduce +pkg_cloudi_service_map_reduce_description = Map/Reduce CloudI Service +pkg_cloudi_service_map_reduce_homepage = http://cloudi.org/ +pkg_cloudi_service_map_reduce_fetch = git +pkg_cloudi_service_map_reduce_repo = https://github.com/CloudI/cloudi_service_map_reduce +pkg_cloudi_service_map_reduce_commit = master + +PACKAGES += cloudi_service_oauth1 +pkg_cloudi_service_oauth1_name = cloudi_service_oauth1 +pkg_cloudi_service_oauth1_description = OAuth v1.0 CloudI Service +pkg_cloudi_service_oauth1_homepage = http://cloudi.org/ +pkg_cloudi_service_oauth1_fetch = git +pkg_cloudi_service_oauth1_repo = https://github.com/CloudI/cloudi_service_oauth1 +pkg_cloudi_service_oauth1_commit = master + +PACKAGES += cloudi_service_queue +pkg_cloudi_service_queue_name = cloudi_service_queue +pkg_cloudi_service_queue_description = Persistent Queue Service +pkg_cloudi_service_queue_homepage = http://cloudi.org/ +pkg_cloudi_service_queue_fetch = git +pkg_cloudi_service_queue_repo = https://github.com/CloudI/cloudi_service_queue +pkg_cloudi_service_queue_commit = master + +PACKAGES += cloudi_service_quorum +pkg_cloudi_service_quorum_name = cloudi_service_quorum +pkg_cloudi_service_quorum_description = CloudI Quorum Service +pkg_cloudi_service_quorum_homepage = http://cloudi.org/ +pkg_cloudi_service_quorum_fetch = git +pkg_cloudi_service_quorum_repo = https://github.com/CloudI/cloudi_service_quorum +pkg_cloudi_service_quorum_commit = master + +PACKAGES += cloudi_service_router +pkg_cloudi_service_router_name = cloudi_service_router +pkg_cloudi_service_router_description = CloudI Router Service +pkg_cloudi_service_router_homepage = http://cloudi.org/ +pkg_cloudi_service_router_fetch = git +pkg_cloudi_service_router_repo = https://github.com/CloudI/cloudi_service_router +pkg_cloudi_service_router_commit = master + +PACKAGES += cloudi_service_tcp +pkg_cloudi_service_tcp_name = cloudi_service_tcp +pkg_cloudi_service_tcp_description = TCP CloudI Service +pkg_cloudi_service_tcp_homepage = http://cloudi.org/ +pkg_cloudi_service_tcp_fetch = git +pkg_cloudi_service_tcp_repo = https://github.com/CloudI/cloudi_service_tcp +pkg_cloudi_service_tcp_commit = master + +PACKAGES += cloudi_service_timers +pkg_cloudi_service_timers_name = cloudi_service_timers +pkg_cloudi_service_timers_description = Timers CloudI Service +pkg_cloudi_service_timers_homepage = http://cloudi.org/ +pkg_cloudi_service_timers_fetch = git +pkg_cloudi_service_timers_repo = https://github.com/CloudI/cloudi_service_timers +pkg_cloudi_service_timers_commit = master + +PACKAGES += cloudi_service_udp +pkg_cloudi_service_udp_name = cloudi_service_udp +pkg_cloudi_service_udp_description = UDP CloudI Service +pkg_cloudi_service_udp_homepage = http://cloudi.org/ +pkg_cloudi_service_udp_fetch = git +pkg_cloudi_service_udp_repo = https://github.com/CloudI/cloudi_service_udp +pkg_cloudi_service_udp_commit = master + +PACKAGES += cloudi_service_validate +pkg_cloudi_service_validate_name = cloudi_service_validate +pkg_cloudi_service_validate_description = CloudI Validate Service +pkg_cloudi_service_validate_homepage = http://cloudi.org/ +pkg_cloudi_service_validate_fetch = git +pkg_cloudi_service_validate_repo = https://github.com/CloudI/cloudi_service_validate +pkg_cloudi_service_validate_commit = master + +PACKAGES += cloudi_service_zeromq +pkg_cloudi_service_zeromq_name = cloudi_service_zeromq +pkg_cloudi_service_zeromq_description = ZeroMQ CloudI Service +pkg_cloudi_service_zeromq_homepage = http://cloudi.org/ +pkg_cloudi_service_zeromq_fetch = git +pkg_cloudi_service_zeromq_repo = https://github.com/CloudI/cloudi_service_zeromq +pkg_cloudi_service_zeromq_commit = master + +PACKAGES += cluster_info +pkg_cluster_info_name = cluster_info +pkg_cluster_info_description = Fork of Hibari's nifty cluster_info OTP app +pkg_cluster_info_homepage = https://github.com/basho/cluster_info +pkg_cluster_info_fetch = git +pkg_cluster_info_repo = https://github.com/basho/cluster_info +pkg_cluster_info_commit = master + +PACKAGES += color +pkg_color_name = color +pkg_color_description = ANSI colors for your Erlang +pkg_color_homepage = https://github.com/julianduque/erlang-color +pkg_color_fetch = git +pkg_color_repo = https://github.com/julianduque/erlang-color +pkg_color_commit = master + +PACKAGES += confetti +pkg_confetti_name = confetti +pkg_confetti_description = Erlang configuration provider / application:get_env/2 on steroids +pkg_confetti_homepage = https://github.com/jtendo/confetti +pkg_confetti_fetch = git +pkg_confetti_repo = https://github.com/jtendo/confetti +pkg_confetti_commit = master + +PACKAGES += couchbeam +pkg_couchbeam_name = couchbeam +pkg_couchbeam_description = Apache CouchDB client in Erlang +pkg_couchbeam_homepage = https://github.com/benoitc/couchbeam +pkg_couchbeam_fetch = git +pkg_couchbeam_repo = https://github.com/benoitc/couchbeam +pkg_couchbeam_commit = master + +PACKAGES += covertool +pkg_covertool_name = covertool +pkg_covertool_description = Tool to convert Erlang cover data files into Cobertura XML reports +pkg_covertool_homepage = https://github.com/idubrov/covertool +pkg_covertool_fetch = git +pkg_covertool_repo = https://github.com/idubrov/covertool +pkg_covertool_commit = master + +PACKAGES += cowboy +pkg_cowboy_name = cowboy +pkg_cowboy_description = Small, fast and modular HTTP server. +pkg_cowboy_homepage = http://ninenines.eu +pkg_cowboy_fetch = git +pkg_cowboy_repo = https://github.com/ninenines/cowboy +pkg_cowboy_commit = 1.0.4 + +PACKAGES += cowdb +pkg_cowdb_name = cowdb +pkg_cowdb_description = Pure Key/Value database library for Erlang Applications +pkg_cowdb_homepage = https://github.com/refuge/cowdb +pkg_cowdb_fetch = git +pkg_cowdb_repo = https://github.com/refuge/cowdb +pkg_cowdb_commit = master + +PACKAGES += cowlib +pkg_cowlib_name = cowlib +pkg_cowlib_description = Support library for manipulating Web protocols. +pkg_cowlib_homepage = http://ninenines.eu +pkg_cowlib_fetch = git +pkg_cowlib_repo = https://github.com/ninenines/cowlib +pkg_cowlib_commit = 1.0.2 + +PACKAGES += cpg +pkg_cpg_name = cpg +pkg_cpg_description = CloudI Process Groups +pkg_cpg_homepage = https://github.com/okeuday/cpg +pkg_cpg_fetch = git +pkg_cpg_repo = https://github.com/okeuday/cpg +pkg_cpg_commit = master + +PACKAGES += cqerl +pkg_cqerl_name = cqerl +pkg_cqerl_description = Native Erlang CQL client for Cassandra +pkg_cqerl_homepage = https://matehat.github.io/cqerl/ +pkg_cqerl_fetch = git +pkg_cqerl_repo = https://github.com/matehat/cqerl +pkg_cqerl_commit = master + +PACKAGES += cr +pkg_cr_name = cr +pkg_cr_description = Chain Replication +pkg_cr_homepage = https://synrc.com/apps/cr/doc/cr.htm +pkg_cr_fetch = git +pkg_cr_repo = https://github.com/spawnproc/cr +pkg_cr_commit = master + +PACKAGES += cuttlefish +pkg_cuttlefish_name = cuttlefish +pkg_cuttlefish_description = never lose your childlike sense of wonder baby cuttlefish, promise me? +pkg_cuttlefish_homepage = https://github.com/basho/cuttlefish +pkg_cuttlefish_fetch = git +pkg_cuttlefish_repo = https://github.com/basho/cuttlefish +pkg_cuttlefish_commit = master + +PACKAGES += damocles +pkg_damocles_name = damocles +pkg_damocles_description = Erlang library for generating adversarial network conditions for QAing distributed applications/systems on a single Linux box. +pkg_damocles_homepage = https://github.com/lostcolony/damocles +pkg_damocles_fetch = git +pkg_damocles_repo = https://github.com/lostcolony/damocles +pkg_damocles_commit = master + +PACKAGES += debbie +pkg_debbie_name = debbie +pkg_debbie_description = .DEB Built In Erlang +pkg_debbie_homepage = https://github.com/crownedgrouse/debbie +pkg_debbie_fetch = git +pkg_debbie_repo = https://github.com/crownedgrouse/debbie +pkg_debbie_commit = master + +PACKAGES += decimal +pkg_decimal_name = decimal +pkg_decimal_description = An Erlang decimal arithmetic library +pkg_decimal_homepage = https://github.com/tim/erlang-decimal +pkg_decimal_fetch = git +pkg_decimal_repo = https://github.com/tim/erlang-decimal +pkg_decimal_commit = master + +PACKAGES += detergent +pkg_detergent_name = detergent +pkg_detergent_description = An emulsifying Erlang SOAP library +pkg_detergent_homepage = https://github.com/devinus/detergent +pkg_detergent_fetch = git +pkg_detergent_repo = https://github.com/devinus/detergent +pkg_detergent_commit = master + +PACKAGES += detest +pkg_detest_name = detest +pkg_detest_description = Tool for running tests on a cluster of erlang nodes +pkg_detest_homepage = https://github.com/biokoda/detest +pkg_detest_fetch = git +pkg_detest_repo = https://github.com/biokoda/detest +pkg_detest_commit = master + +PACKAGES += dh_date +pkg_dh_date_name = dh_date +pkg_dh_date_description = Date formatting / parsing library for erlang +pkg_dh_date_homepage = https://github.com/daleharvey/dh_date +pkg_dh_date_fetch = git +pkg_dh_date_repo = https://github.com/daleharvey/dh_date +pkg_dh_date_commit = master + +PACKAGES += dirbusterl +pkg_dirbusterl_name = dirbusterl +pkg_dirbusterl_description = DirBuster successor in Erlang +pkg_dirbusterl_homepage = https://github.com/silentsignal/DirBustErl +pkg_dirbusterl_fetch = git +pkg_dirbusterl_repo = https://github.com/silentsignal/DirBustErl +pkg_dirbusterl_commit = master + +PACKAGES += dispcount +pkg_dispcount_name = dispcount +pkg_dispcount_description = Erlang task dispatcher based on ETS counters. +pkg_dispcount_homepage = https://github.com/ferd/dispcount +pkg_dispcount_fetch = git +pkg_dispcount_repo = https://github.com/ferd/dispcount +pkg_dispcount_commit = master + +PACKAGES += dlhttpc +pkg_dlhttpc_name = dlhttpc +pkg_dlhttpc_description = dispcount-based lhttpc fork for massive amounts of requests to limited endpoints +pkg_dlhttpc_homepage = https://github.com/ferd/dlhttpc +pkg_dlhttpc_fetch = git +pkg_dlhttpc_repo = https://github.com/ferd/dlhttpc +pkg_dlhttpc_commit = master + +PACKAGES += dns +pkg_dns_name = dns +pkg_dns_description = Erlang DNS library +pkg_dns_homepage = https://github.com/aetrion/dns_erlang +pkg_dns_fetch = git +pkg_dns_repo = https://github.com/aetrion/dns_erlang +pkg_dns_commit = master + +PACKAGES += dnssd +pkg_dnssd_name = dnssd +pkg_dnssd_description = Erlang interface to Apple's Bonjour D NS Service Discovery implementation +pkg_dnssd_homepage = https://github.com/benoitc/dnssd_erlang +pkg_dnssd_fetch = git +pkg_dnssd_repo = https://github.com/benoitc/dnssd_erlang +pkg_dnssd_commit = master + +PACKAGES += dtl +pkg_dtl_name = dtl +pkg_dtl_description = Django Template Language: A full-featured port of the Django template engine to Erlang. +pkg_dtl_homepage = https://github.com/oinksoft/dtl +pkg_dtl_fetch = git +pkg_dtl_repo = https://github.com/oinksoft/dtl +pkg_dtl_commit = master + +PACKAGES += dynamic_compile +pkg_dynamic_compile_name = dynamic_compile +pkg_dynamic_compile_description = compile and load erlang modules from string input +pkg_dynamic_compile_homepage = https://github.com/jkvor/dynamic_compile +pkg_dynamic_compile_fetch = git +pkg_dynamic_compile_repo = https://github.com/jkvor/dynamic_compile +pkg_dynamic_compile_commit = master + +PACKAGES += e2 +pkg_e2_name = e2 +pkg_e2_description = Library to simply writing correct OTP applications. +pkg_e2_homepage = http://e2project.org +pkg_e2_fetch = git +pkg_e2_repo = https://github.com/gar1t/e2 +pkg_e2_commit = master + +PACKAGES += eamf +pkg_eamf_name = eamf +pkg_eamf_description = eAMF provides Action Message Format (AMF) support for Erlang +pkg_eamf_homepage = https://github.com/mrinalwadhwa/eamf +pkg_eamf_fetch = git +pkg_eamf_repo = https://github.com/mrinalwadhwa/eamf +pkg_eamf_commit = master + +PACKAGES += eavro +pkg_eavro_name = eavro +pkg_eavro_description = Apache Avro encoder/decoder +pkg_eavro_homepage = https://github.com/SIfoxDevTeam/eavro +pkg_eavro_fetch = git +pkg_eavro_repo = https://github.com/SIfoxDevTeam/eavro +pkg_eavro_commit = master + +PACKAGES += ecapnp +pkg_ecapnp_name = ecapnp +pkg_ecapnp_description = Cap'n Proto library for Erlang +pkg_ecapnp_homepage = https://github.com/kaos/ecapnp +pkg_ecapnp_fetch = git +pkg_ecapnp_repo = https://github.com/kaos/ecapnp +pkg_ecapnp_commit = master + +PACKAGES += econfig +pkg_econfig_name = econfig +pkg_econfig_description = simple Erlang config handler using INI files +pkg_econfig_homepage = https://github.com/benoitc/econfig +pkg_econfig_fetch = git +pkg_econfig_repo = https://github.com/benoitc/econfig +pkg_econfig_commit = master + +PACKAGES += edate +pkg_edate_name = edate +pkg_edate_description = date manipulation library for erlang +pkg_edate_homepage = https://github.com/dweldon/edate +pkg_edate_fetch = git +pkg_edate_repo = https://github.com/dweldon/edate +pkg_edate_commit = master + +PACKAGES += edgar +pkg_edgar_name = edgar +pkg_edgar_description = Erlang Does GNU AR +pkg_edgar_homepage = https://github.com/crownedgrouse/edgar +pkg_edgar_fetch = git +pkg_edgar_repo = https://github.com/crownedgrouse/edgar +pkg_edgar_commit = master + +PACKAGES += edis +pkg_edis_name = edis +pkg_edis_description = An Erlang implementation of Redis KV Store +pkg_edis_homepage = http://inaka.github.com/edis/ +pkg_edis_fetch = git +pkg_edis_repo = https://github.com/inaka/edis +pkg_edis_commit = master + +PACKAGES += edns +pkg_edns_name = edns +pkg_edns_description = Erlang/OTP DNS server +pkg_edns_homepage = https://github.com/hcvst/erlang-dns +pkg_edns_fetch = git +pkg_edns_repo = https://github.com/hcvst/erlang-dns +pkg_edns_commit = master + +PACKAGES += edown +pkg_edown_name = edown +pkg_edown_description = EDoc extension for generating Github-flavored Markdown +pkg_edown_homepage = https://github.com/uwiger/edown +pkg_edown_fetch = git +pkg_edown_repo = https://github.com/uwiger/edown +pkg_edown_commit = master + +PACKAGES += eep +pkg_eep_name = eep +pkg_eep_description = Erlang Easy Profiling (eep) application provides a way to analyze application performance and call hierarchy +pkg_eep_homepage = https://github.com/virtan/eep +pkg_eep_fetch = git +pkg_eep_repo = https://github.com/virtan/eep +pkg_eep_commit = master + +PACKAGES += eep_app +pkg_eep_app_name = eep_app +pkg_eep_app_description = Embedded Event Processing +pkg_eep_app_homepage = https://github.com/darach/eep-erl +pkg_eep_app_fetch = git +pkg_eep_app_repo = https://github.com/darach/eep-erl +pkg_eep_app_commit = master + +PACKAGES += efene +pkg_efene_name = efene +pkg_efene_description = Alternative syntax for the Erlang Programming Language focusing on simplicity, ease of use and programmer UX +pkg_efene_homepage = https://github.com/efene/efene +pkg_efene_fetch = git +pkg_efene_repo = https://github.com/efene/efene +pkg_efene_commit = master + +PACKAGES += egeoip +pkg_egeoip_name = egeoip +pkg_egeoip_description = Erlang IP Geolocation module, currently supporting the MaxMind GeoLite City Database. +pkg_egeoip_homepage = https://github.com/mochi/egeoip +pkg_egeoip_fetch = git +pkg_egeoip_repo = https://github.com/mochi/egeoip +pkg_egeoip_commit = master + +PACKAGES += ehsa +pkg_ehsa_name = ehsa +pkg_ehsa_description = Erlang HTTP server basic and digest authentication modules +pkg_ehsa_homepage = https://bitbucket.org/a12n/ehsa +pkg_ehsa_fetch = hg +pkg_ehsa_repo = https://bitbucket.org/a12n/ehsa +pkg_ehsa_commit = default + +PACKAGES += ej +pkg_ej_name = ej +pkg_ej_description = Helper module for working with Erlang terms representing JSON +pkg_ej_homepage = https://github.com/seth/ej +pkg_ej_fetch = git +pkg_ej_repo = https://github.com/seth/ej +pkg_ej_commit = master + +PACKAGES += ejabberd +pkg_ejabberd_name = ejabberd +pkg_ejabberd_description = Robust, ubiquitous and massively scalable Jabber / XMPP Instant Messaging platform +pkg_ejabberd_homepage = https://github.com/processone/ejabberd +pkg_ejabberd_fetch = git +pkg_ejabberd_repo = https://github.com/processone/ejabberd +pkg_ejabberd_commit = master + +PACKAGES += ejwt +pkg_ejwt_name = ejwt +pkg_ejwt_description = erlang library for JSON Web Token +pkg_ejwt_homepage = https://github.com/artefactop/ejwt +pkg_ejwt_fetch = git +pkg_ejwt_repo = https://github.com/artefactop/ejwt +pkg_ejwt_commit = master + +PACKAGES += ekaf +pkg_ekaf_name = ekaf +pkg_ekaf_description = A minimal, high-performance Kafka client in Erlang. +pkg_ekaf_homepage = https://github.com/helpshift/ekaf +pkg_ekaf_fetch = git +pkg_ekaf_repo = https://github.com/helpshift/ekaf +pkg_ekaf_commit = master + +PACKAGES += elarm +pkg_elarm_name = elarm +pkg_elarm_description = Alarm Manager for Erlang. +pkg_elarm_homepage = https://github.com/esl/elarm +pkg_elarm_fetch = git +pkg_elarm_repo = https://github.com/esl/elarm +pkg_elarm_commit = master + +PACKAGES += eleveldb +pkg_eleveldb_name = eleveldb +pkg_eleveldb_description = Erlang LevelDB API +pkg_eleveldb_homepage = https://github.com/basho/eleveldb +pkg_eleveldb_fetch = git +pkg_eleveldb_repo = https://github.com/basho/eleveldb +pkg_eleveldb_commit = master + +PACKAGES += elli +pkg_elli_name = elli +pkg_elli_description = Simple, robust and performant Erlang web server +pkg_elli_homepage = https://github.com/knutin/elli +pkg_elli_fetch = git +pkg_elli_repo = https://github.com/knutin/elli +pkg_elli_commit = master + +PACKAGES += elvis +pkg_elvis_name = elvis +pkg_elvis_description = Erlang Style Reviewer +pkg_elvis_homepage = https://github.com/inaka/elvis +pkg_elvis_fetch = git +pkg_elvis_repo = https://github.com/inaka/elvis +pkg_elvis_commit = master + +PACKAGES += emagick +pkg_emagick_name = emagick +pkg_emagick_description = Wrapper for Graphics/ImageMagick command line tool. +pkg_emagick_homepage = https://github.com/kivra/emagick +pkg_emagick_fetch = git +pkg_emagick_repo = https://github.com/kivra/emagick +pkg_emagick_commit = master + +PACKAGES += emysql +pkg_emysql_name = emysql +pkg_emysql_description = Stable, pure Erlang MySQL driver. +pkg_emysql_homepage = https://github.com/Eonblast/Emysql +pkg_emysql_fetch = git +pkg_emysql_repo = https://github.com/Eonblast/Emysql +pkg_emysql_commit = master + +PACKAGES += enm +pkg_enm_name = enm +pkg_enm_description = Erlang driver for nanomsg +pkg_enm_homepage = https://github.com/basho/enm +pkg_enm_fetch = git +pkg_enm_repo = https://github.com/basho/enm +pkg_enm_commit = master + +PACKAGES += entop +pkg_entop_name = entop +pkg_entop_description = A top-like tool for monitoring an Erlang node +pkg_entop_homepage = https://github.com/mazenharake/entop +pkg_entop_fetch = git +pkg_entop_repo = https://github.com/mazenharake/entop +pkg_entop_commit = master + +PACKAGES += epcap +pkg_epcap_name = epcap +pkg_epcap_description = Erlang packet capture interface using pcap +pkg_epcap_homepage = https://github.com/msantos/epcap +pkg_epcap_fetch = git +pkg_epcap_repo = https://github.com/msantos/epcap +pkg_epcap_commit = master + +PACKAGES += eper +pkg_eper_name = eper +pkg_eper_description = Erlang performance and debugging tools. +pkg_eper_homepage = https://github.com/massemanet/eper +pkg_eper_fetch = git +pkg_eper_repo = https://github.com/massemanet/eper +pkg_eper_commit = master + +PACKAGES += epgsql +pkg_epgsql_name = epgsql +pkg_epgsql_description = Erlang PostgreSQL client library. +pkg_epgsql_homepage = https://github.com/epgsql/epgsql +pkg_epgsql_fetch = git +pkg_epgsql_repo = https://github.com/epgsql/epgsql +pkg_epgsql_commit = master + +PACKAGES += episcina +pkg_episcina_name = episcina +pkg_episcina_description = A simple non intrusive resource pool for connections +pkg_episcina_homepage = https://github.com/erlware/episcina +pkg_episcina_fetch = git +pkg_episcina_repo = https://github.com/erlware/episcina +pkg_episcina_commit = master + +PACKAGES += eplot +pkg_eplot_name = eplot +pkg_eplot_description = A plot engine written in erlang. +pkg_eplot_homepage = https://github.com/psyeugenic/eplot +pkg_eplot_fetch = git +pkg_eplot_repo = https://github.com/psyeugenic/eplot +pkg_eplot_commit = master + +PACKAGES += epocxy +pkg_epocxy_name = epocxy +pkg_epocxy_description = Erlang Patterns of Concurrency +pkg_epocxy_homepage = https://github.com/duomark/epocxy +pkg_epocxy_fetch = git +pkg_epocxy_repo = https://github.com/duomark/epocxy +pkg_epocxy_commit = master + +PACKAGES += epubnub +pkg_epubnub_name = epubnub +pkg_epubnub_description = Erlang PubNub API +pkg_epubnub_homepage = https://github.com/tsloughter/epubnub +pkg_epubnub_fetch = git +pkg_epubnub_repo = https://github.com/tsloughter/epubnub +pkg_epubnub_commit = master + +PACKAGES += eqm +pkg_eqm_name = eqm +pkg_eqm_description = Erlang pub sub with supply-demand channels +pkg_eqm_homepage = https://github.com/loucash/eqm +pkg_eqm_fetch = git +pkg_eqm_repo = https://github.com/loucash/eqm +pkg_eqm_commit = master + +PACKAGES += eredis +pkg_eredis_name = eredis +pkg_eredis_description = Erlang Redis client +pkg_eredis_homepage = https://github.com/wooga/eredis +pkg_eredis_fetch = git +pkg_eredis_repo = https://github.com/wooga/eredis +pkg_eredis_commit = master + +PACKAGES += eredis_pool +pkg_eredis_pool_name = eredis_pool +pkg_eredis_pool_description = eredis_pool is Pool of Redis clients, using eredis and poolboy. +pkg_eredis_pool_homepage = https://github.com/hiroeorz/eredis_pool +pkg_eredis_pool_fetch = git +pkg_eredis_pool_repo = https://github.com/hiroeorz/eredis_pool +pkg_eredis_pool_commit = master + +PACKAGES += erl_streams +pkg_erl_streams_name = erl_streams +pkg_erl_streams_description = Streams in Erlang +pkg_erl_streams_homepage = https://github.com/epappas/erl_streams +pkg_erl_streams_fetch = git +pkg_erl_streams_repo = https://github.com/epappas/erl_streams +pkg_erl_streams_commit = master + +PACKAGES += erlang_cep +pkg_erlang_cep_name = erlang_cep +pkg_erlang_cep_description = A basic CEP package written in erlang +pkg_erlang_cep_homepage = https://github.com/danmacklin/erlang_cep +pkg_erlang_cep_fetch = git +pkg_erlang_cep_repo = https://github.com/danmacklin/erlang_cep +pkg_erlang_cep_commit = master + +PACKAGES += erlang_js +pkg_erlang_js_name = erlang_js +pkg_erlang_js_description = A linked-in driver for Erlang to Mozilla's Spidermonkey Javascript runtime. +pkg_erlang_js_homepage = https://github.com/basho/erlang_js +pkg_erlang_js_fetch = git +pkg_erlang_js_repo = https://github.com/basho/erlang_js +pkg_erlang_js_commit = master + +PACKAGES += erlang_localtime +pkg_erlang_localtime_name = erlang_localtime +pkg_erlang_localtime_description = Erlang library for conversion from one local time to another +pkg_erlang_localtime_homepage = https://github.com/dmitryme/erlang_localtime +pkg_erlang_localtime_fetch = git +pkg_erlang_localtime_repo = https://github.com/dmitryme/erlang_localtime +pkg_erlang_localtime_commit = master + +PACKAGES += erlang_smtp +pkg_erlang_smtp_name = erlang_smtp +pkg_erlang_smtp_description = Erlang SMTP and POP3 server code. +pkg_erlang_smtp_homepage = https://github.com/tonyg/erlang-smtp +pkg_erlang_smtp_fetch = git +pkg_erlang_smtp_repo = https://github.com/tonyg/erlang-smtp +pkg_erlang_smtp_commit = master + +PACKAGES += erlang_term +pkg_erlang_term_name = erlang_term +pkg_erlang_term_description = Erlang Term Info +pkg_erlang_term_homepage = https://github.com/okeuday/erlang_term +pkg_erlang_term_fetch = git +pkg_erlang_term_repo = https://github.com/okeuday/erlang_term +pkg_erlang_term_commit = master + +PACKAGES += erlastic_search +pkg_erlastic_search_name = erlastic_search +pkg_erlastic_search_description = An Erlang app for communicating with Elastic Search's rest interface. +pkg_erlastic_search_homepage = https://github.com/tsloughter/erlastic_search +pkg_erlastic_search_fetch = git +pkg_erlastic_search_repo = https://github.com/tsloughter/erlastic_search +pkg_erlastic_search_commit = master + +PACKAGES += erlasticsearch +pkg_erlasticsearch_name = erlasticsearch +pkg_erlasticsearch_description = Erlang thrift interface to elastic_search +pkg_erlasticsearch_homepage = https://github.com/dieswaytoofast/erlasticsearch +pkg_erlasticsearch_fetch = git +pkg_erlasticsearch_repo = https://github.com/dieswaytoofast/erlasticsearch +pkg_erlasticsearch_commit = master + +PACKAGES += erlbrake +pkg_erlbrake_name = erlbrake +pkg_erlbrake_description = Erlang Airbrake notification client +pkg_erlbrake_homepage = https://github.com/kenpratt/erlbrake +pkg_erlbrake_fetch = git +pkg_erlbrake_repo = https://github.com/kenpratt/erlbrake +pkg_erlbrake_commit = master + +PACKAGES += erlcloud +pkg_erlcloud_name = erlcloud +pkg_erlcloud_description = Cloud Computing library for erlang (Amazon EC2, S3, SQS, SimpleDB, Mechanical Turk, ELB) +pkg_erlcloud_homepage = https://github.com/gleber/erlcloud +pkg_erlcloud_fetch = git +pkg_erlcloud_repo = https://github.com/gleber/erlcloud +pkg_erlcloud_commit = master + +PACKAGES += erlcron +pkg_erlcron_name = erlcron +pkg_erlcron_description = Erlang cronish system +pkg_erlcron_homepage = https://github.com/erlware/erlcron +pkg_erlcron_fetch = git +pkg_erlcron_repo = https://github.com/erlware/erlcron +pkg_erlcron_commit = master + +PACKAGES += erldb +pkg_erldb_name = erldb +pkg_erldb_description = ORM (Object-relational mapping) application implemented in Erlang +pkg_erldb_homepage = http://erldb.org +pkg_erldb_fetch = git +pkg_erldb_repo = https://github.com/erldb/erldb +pkg_erldb_commit = master + +PACKAGES += erldis +pkg_erldis_name = erldis +pkg_erldis_description = redis erlang client library +pkg_erldis_homepage = https://github.com/cstar/erldis +pkg_erldis_fetch = git +pkg_erldis_repo = https://github.com/cstar/erldis +pkg_erldis_commit = master + +PACKAGES += erldns +pkg_erldns_name = erldns +pkg_erldns_description = DNS server, in erlang. +pkg_erldns_homepage = https://github.com/aetrion/erl-dns +pkg_erldns_fetch = git +pkg_erldns_repo = https://github.com/aetrion/erl-dns +pkg_erldns_commit = master + +PACKAGES += erldocker +pkg_erldocker_name = erldocker +pkg_erldocker_description = Docker Remote API client for Erlang +pkg_erldocker_homepage = https://github.com/proger/erldocker +pkg_erldocker_fetch = git +pkg_erldocker_repo = https://github.com/proger/erldocker +pkg_erldocker_commit = master + +PACKAGES += erlfsmon +pkg_erlfsmon_name = erlfsmon +pkg_erlfsmon_description = Erlang filesystem event watcher for Linux and OSX +pkg_erlfsmon_homepage = https://github.com/proger/erlfsmon +pkg_erlfsmon_fetch = git +pkg_erlfsmon_repo = https://github.com/proger/erlfsmon +pkg_erlfsmon_commit = master + +PACKAGES += erlgit +pkg_erlgit_name = erlgit +pkg_erlgit_description = Erlang convenience wrapper around git executable +pkg_erlgit_homepage = https://github.com/gleber/erlgit +pkg_erlgit_fetch = git +pkg_erlgit_repo = https://github.com/gleber/erlgit +pkg_erlgit_commit = master + +PACKAGES += erlguten +pkg_erlguten_name = erlguten +pkg_erlguten_description = ErlGuten is a system for high-quality typesetting, written purely in Erlang. +pkg_erlguten_homepage = https://github.com/richcarl/erlguten +pkg_erlguten_fetch = git +pkg_erlguten_repo = https://github.com/richcarl/erlguten +pkg_erlguten_commit = master + +PACKAGES += erlmc +pkg_erlmc_name = erlmc +pkg_erlmc_description = Erlang memcached binary protocol client +pkg_erlmc_homepage = https://github.com/jkvor/erlmc +pkg_erlmc_fetch = git +pkg_erlmc_repo = https://github.com/jkvor/erlmc +pkg_erlmc_commit = master + +PACKAGES += erlmongo +pkg_erlmongo_name = erlmongo +pkg_erlmongo_description = Record based Erlang driver for MongoDB with gridfs support +pkg_erlmongo_homepage = https://github.com/SergejJurecko/erlmongo +pkg_erlmongo_fetch = git +pkg_erlmongo_repo = https://github.com/SergejJurecko/erlmongo +pkg_erlmongo_commit = master + +PACKAGES += erlog +pkg_erlog_name = erlog +pkg_erlog_description = Prolog interpreter in and for Erlang +pkg_erlog_homepage = https://github.com/rvirding/erlog +pkg_erlog_fetch = git +pkg_erlog_repo = https://github.com/rvirding/erlog +pkg_erlog_commit = master + +PACKAGES += erlpass +pkg_erlpass_name = erlpass +pkg_erlpass_description = A library to handle password hashing and changing in a safe manner, independent from any kind of storage whatsoever. +pkg_erlpass_homepage = https://github.com/ferd/erlpass +pkg_erlpass_fetch = git +pkg_erlpass_repo = https://github.com/ferd/erlpass +pkg_erlpass_commit = master + +PACKAGES += erlport +pkg_erlport_name = erlport +pkg_erlport_description = ErlPort - connect Erlang to other languages +pkg_erlport_homepage = https://github.com/hdima/erlport +pkg_erlport_fetch = git +pkg_erlport_repo = https://github.com/hdima/erlport +pkg_erlport_commit = master + +PACKAGES += erlsh +pkg_erlsh_name = erlsh +pkg_erlsh_description = Erlang shell tools +pkg_erlsh_homepage = https://github.com/proger/erlsh +pkg_erlsh_fetch = git +pkg_erlsh_repo = https://github.com/proger/erlsh +pkg_erlsh_commit = master + +PACKAGES += erlsha2 +pkg_erlsha2_name = erlsha2 +pkg_erlsha2_description = SHA-224, SHA-256, SHA-384, SHA-512 implemented in Erlang NIFs. +pkg_erlsha2_homepage = https://github.com/vinoski/erlsha2 +pkg_erlsha2_fetch = git +pkg_erlsha2_repo = https://github.com/vinoski/erlsha2 +pkg_erlsha2_commit = master + +PACKAGES += erlsom +pkg_erlsom_name = erlsom +pkg_erlsom_description = XML parser for Erlang +pkg_erlsom_homepage = https://github.com/willemdj/erlsom +pkg_erlsom_fetch = git +pkg_erlsom_repo = https://github.com/willemdj/erlsom +pkg_erlsom_commit = master + +PACKAGES += erlubi +pkg_erlubi_name = erlubi +pkg_erlubi_description = Ubigraph Erlang Client (and Process Visualizer) +pkg_erlubi_homepage = https://github.com/krestenkrab/erlubi +pkg_erlubi_fetch = git +pkg_erlubi_repo = https://github.com/krestenkrab/erlubi +pkg_erlubi_commit = master + +PACKAGES += erlvolt +pkg_erlvolt_name = erlvolt +pkg_erlvolt_description = VoltDB Erlang Client Driver +pkg_erlvolt_homepage = https://github.com/VoltDB/voltdb-client-erlang +pkg_erlvolt_fetch = git +pkg_erlvolt_repo = https://github.com/VoltDB/voltdb-client-erlang +pkg_erlvolt_commit = master + +PACKAGES += erlware_commons +pkg_erlware_commons_name = erlware_commons +pkg_erlware_commons_description = Erlware Commons is an Erlware project focused on all aspects of reusable Erlang components. +pkg_erlware_commons_homepage = https://github.com/erlware/erlware_commons +pkg_erlware_commons_fetch = git +pkg_erlware_commons_repo = https://github.com/erlware/erlware_commons +pkg_erlware_commons_commit = master + +PACKAGES += erlydtl +pkg_erlydtl_name = erlydtl +pkg_erlydtl_description = Django Template Language for Erlang. +pkg_erlydtl_homepage = https://github.com/erlydtl/erlydtl +pkg_erlydtl_fetch = git +pkg_erlydtl_repo = https://github.com/erlydtl/erlydtl +pkg_erlydtl_commit = master + +PACKAGES += errd +pkg_errd_name = errd +pkg_errd_description = Erlang RRDTool library +pkg_errd_homepage = https://github.com/archaelus/errd +pkg_errd_fetch = git +pkg_errd_repo = https://github.com/archaelus/errd +pkg_errd_commit = master + +PACKAGES += erserve +pkg_erserve_name = erserve +pkg_erserve_description = Erlang/Rserve communication interface +pkg_erserve_homepage = https://github.com/del/erserve +pkg_erserve_fetch = git +pkg_erserve_repo = https://github.com/del/erserve +pkg_erserve_commit = master + +PACKAGES += erwa +pkg_erwa_name = erwa +pkg_erwa_description = A WAMP router and client written in Erlang. +pkg_erwa_homepage = https://github.com/bwegh/erwa +pkg_erwa_fetch = git +pkg_erwa_repo = https://github.com/bwegh/erwa +pkg_erwa_commit = master + +PACKAGES += espec +pkg_espec_name = espec +pkg_espec_description = ESpec: Behaviour driven development framework for Erlang +pkg_espec_homepage = https://github.com/lucaspiller/espec +pkg_espec_fetch = git +pkg_espec_repo = https://github.com/lucaspiller/espec +pkg_espec_commit = master + +PACKAGES += estatsd +pkg_estatsd_name = estatsd +pkg_estatsd_description = Erlang stats aggregation app that periodically flushes data to graphite +pkg_estatsd_homepage = https://github.com/RJ/estatsd +pkg_estatsd_fetch = git +pkg_estatsd_repo = https://github.com/RJ/estatsd +pkg_estatsd_commit = master + +PACKAGES += etap +pkg_etap_name = etap +pkg_etap_description = etap is a simple erlang testing library that provides TAP compliant output. +pkg_etap_homepage = https://github.com/ngerakines/etap +pkg_etap_fetch = git +pkg_etap_repo = https://github.com/ngerakines/etap +pkg_etap_commit = master + +PACKAGES += etest +pkg_etest_name = etest +pkg_etest_description = A lightweight, convention over configuration test framework for Erlang +pkg_etest_homepage = https://github.com/wooga/etest +pkg_etest_fetch = git +pkg_etest_repo = https://github.com/wooga/etest +pkg_etest_commit = master + +PACKAGES += etest_http +pkg_etest_http_name = etest_http +pkg_etest_http_description = etest Assertions around HTTP (client-side) +pkg_etest_http_homepage = https://github.com/wooga/etest_http +pkg_etest_http_fetch = git +pkg_etest_http_repo = https://github.com/wooga/etest_http +pkg_etest_http_commit = master + +PACKAGES += etoml +pkg_etoml_name = etoml +pkg_etoml_description = TOML language erlang parser +pkg_etoml_homepage = https://github.com/kalta/etoml +pkg_etoml_fetch = git +pkg_etoml_repo = https://github.com/kalta/etoml +pkg_etoml_commit = master + +PACKAGES += eunit +pkg_eunit_name = eunit +pkg_eunit_description = The EUnit lightweight unit testing framework for Erlang - this is the canonical development repository. +pkg_eunit_homepage = https://github.com/richcarl/eunit +pkg_eunit_fetch = git +pkg_eunit_repo = https://github.com/richcarl/eunit +pkg_eunit_commit = master + +PACKAGES += eunit_formatters +pkg_eunit_formatters_name = eunit_formatters +pkg_eunit_formatters_description = Because eunit's output sucks. Let's make it better. +pkg_eunit_formatters_homepage = https://github.com/seancribbs/eunit_formatters +pkg_eunit_formatters_fetch = git +pkg_eunit_formatters_repo = https://github.com/seancribbs/eunit_formatters +pkg_eunit_formatters_commit = master + +PACKAGES += euthanasia +pkg_euthanasia_name = euthanasia +pkg_euthanasia_description = Merciful killer for your Erlang processes +pkg_euthanasia_homepage = https://github.com/doubleyou/euthanasia +pkg_euthanasia_fetch = git +pkg_euthanasia_repo = https://github.com/doubleyou/euthanasia +pkg_euthanasia_commit = master + +PACKAGES += evum +pkg_evum_name = evum +pkg_evum_description = Spawn Linux VMs as Erlang processes in the Erlang VM +pkg_evum_homepage = https://github.com/msantos/evum +pkg_evum_fetch = git +pkg_evum_repo = https://github.com/msantos/evum +pkg_evum_commit = master + +PACKAGES += exec +pkg_exec_name = erlexec +pkg_exec_description = Execute and control OS processes from Erlang/OTP. +pkg_exec_homepage = http://saleyn.github.com/erlexec +pkg_exec_fetch = git +pkg_exec_repo = https://github.com/saleyn/erlexec +pkg_exec_commit = master + +PACKAGES += exml +pkg_exml_name = exml +pkg_exml_description = XML parsing library in Erlang +pkg_exml_homepage = https://github.com/paulgray/exml +pkg_exml_fetch = git +pkg_exml_repo = https://github.com/paulgray/exml +pkg_exml_commit = master + +PACKAGES += exometer +pkg_exometer_name = exometer +pkg_exometer_description = Basic measurement objects and probe behavior +pkg_exometer_homepage = https://github.com/Feuerlabs/exometer +pkg_exometer_fetch = git +pkg_exometer_repo = https://github.com/Feuerlabs/exometer +pkg_exometer_commit = master + +PACKAGES += exs1024 +pkg_exs1024_name = exs1024 +pkg_exs1024_description = Xorshift1024star pseudo random number generator for Erlang. +pkg_exs1024_homepage = https://github.com/jj1bdx/exs1024 +pkg_exs1024_fetch = git +pkg_exs1024_repo = https://github.com/jj1bdx/exs1024 +pkg_exs1024_commit = master + +PACKAGES += exs64 +pkg_exs64_name = exs64 +pkg_exs64_description = Xorshift64star pseudo random number generator for Erlang. +pkg_exs64_homepage = https://github.com/jj1bdx/exs64 +pkg_exs64_fetch = git +pkg_exs64_repo = https://github.com/jj1bdx/exs64 +pkg_exs64_commit = master + +PACKAGES += exsplus116 +pkg_exsplus116_name = exsplus116 +pkg_exsplus116_description = Xorshift116plus for Erlang +pkg_exsplus116_homepage = https://github.com/jj1bdx/exsplus116 +pkg_exsplus116_fetch = git +pkg_exsplus116_repo = https://github.com/jj1bdx/exsplus116 +pkg_exsplus116_commit = master + +PACKAGES += exsplus128 +pkg_exsplus128_name = exsplus128 +pkg_exsplus128_description = Xorshift128plus pseudo random number generator for Erlang. +pkg_exsplus128_homepage = https://github.com/jj1bdx/exsplus128 +pkg_exsplus128_fetch = git +pkg_exsplus128_repo = https://github.com/jj1bdx/exsplus128 +pkg_exsplus128_commit = master + +PACKAGES += ezmq +pkg_ezmq_name = ezmq +pkg_ezmq_description = zMQ implemented in Erlang +pkg_ezmq_homepage = https://github.com/RoadRunnr/ezmq +pkg_ezmq_fetch = git +pkg_ezmq_repo = https://github.com/RoadRunnr/ezmq +pkg_ezmq_commit = master + +PACKAGES += ezmtp +pkg_ezmtp_name = ezmtp +pkg_ezmtp_description = ZMTP protocol in pure Erlang. +pkg_ezmtp_homepage = https://github.com/a13x/ezmtp +pkg_ezmtp_fetch = git +pkg_ezmtp_repo = https://github.com/a13x/ezmtp +pkg_ezmtp_commit = master + +PACKAGES += fast_disk_log +pkg_fast_disk_log_name = fast_disk_log +pkg_fast_disk_log_description = Pool-based asynchronous Erlang disk logger +pkg_fast_disk_log_homepage = https://github.com/lpgauth/fast_disk_log +pkg_fast_disk_log_fetch = git +pkg_fast_disk_log_repo = https://github.com/lpgauth/fast_disk_log +pkg_fast_disk_log_commit = master + +PACKAGES += feeder +pkg_feeder_name = feeder +pkg_feeder_description = Stream parse RSS and Atom formatted XML feeds. +pkg_feeder_homepage = https://github.com/michaelnisi/feeder +pkg_feeder_fetch = git +pkg_feeder_repo = https://github.com/michaelnisi/feeder +pkg_feeder_commit = master + +PACKAGES += find_crate +pkg_find_crate_name = find_crate +pkg_find_crate_description = Find Rust libs and exes in Erlang application priv directory +pkg_find_crate_homepage = https://github.com/goertzenator/find_crate +pkg_find_crate_fetch = git +pkg_find_crate_repo = https://github.com/goertzenator/find_crate +pkg_find_crate_commit = master + +PACKAGES += fix +pkg_fix_name = fix +pkg_fix_description = http://fixprotocol.org/ implementation. +pkg_fix_homepage = https://github.com/maxlapshin/fix +pkg_fix_fetch = git +pkg_fix_repo = https://github.com/maxlapshin/fix +pkg_fix_commit = master + +PACKAGES += flower +pkg_flower_name = flower +pkg_flower_description = FlowER - a Erlang OpenFlow development platform +pkg_flower_homepage = https://github.com/travelping/flower +pkg_flower_fetch = git +pkg_flower_repo = https://github.com/travelping/flower +pkg_flower_commit = master + +PACKAGES += fn +pkg_fn_name = fn +pkg_fn_description = Function utilities for Erlang +pkg_fn_homepage = https://github.com/reiddraper/fn +pkg_fn_fetch = git +pkg_fn_repo = https://github.com/reiddraper/fn +pkg_fn_commit = master + +PACKAGES += folsom +pkg_folsom_name = folsom +pkg_folsom_description = Expose Erlang Events and Metrics +pkg_folsom_homepage = https://github.com/boundary/folsom +pkg_folsom_fetch = git +pkg_folsom_repo = https://github.com/boundary/folsom +pkg_folsom_commit = master + +PACKAGES += folsom_cowboy +pkg_folsom_cowboy_name = folsom_cowboy +pkg_folsom_cowboy_description = A Cowboy based Folsom HTTP Wrapper. +pkg_folsom_cowboy_homepage = https://github.com/boundary/folsom_cowboy +pkg_folsom_cowboy_fetch = git +pkg_folsom_cowboy_repo = https://github.com/boundary/folsom_cowboy +pkg_folsom_cowboy_commit = master + +PACKAGES += folsomite +pkg_folsomite_name = folsomite +pkg_folsomite_description = blow up your graphite / riemann server with folsom metrics +pkg_folsomite_homepage = https://github.com/campanja/folsomite +pkg_folsomite_fetch = git +pkg_folsomite_repo = https://github.com/campanja/folsomite +pkg_folsomite_commit = master + +PACKAGES += fs +pkg_fs_name = fs +pkg_fs_description = Erlang FileSystem Listener +pkg_fs_homepage = https://github.com/synrc/fs +pkg_fs_fetch = git +pkg_fs_repo = https://github.com/synrc/fs +pkg_fs_commit = master + +PACKAGES += fuse +pkg_fuse_name = fuse +pkg_fuse_description = A Circuit Breaker for Erlang +pkg_fuse_homepage = https://github.com/jlouis/fuse +pkg_fuse_fetch = git +pkg_fuse_repo = https://github.com/jlouis/fuse +pkg_fuse_commit = master + +PACKAGES += gcm +pkg_gcm_name = gcm +pkg_gcm_description = An Erlang application for Google Cloud Messaging +pkg_gcm_homepage = https://github.com/pdincau/gcm-erlang +pkg_gcm_fetch = git +pkg_gcm_repo = https://github.com/pdincau/gcm-erlang +pkg_gcm_commit = master + +PACKAGES += gcprof +pkg_gcprof_name = gcprof +pkg_gcprof_description = Garbage Collection profiler for Erlang +pkg_gcprof_homepage = https://github.com/knutin/gcprof +pkg_gcprof_fetch = git +pkg_gcprof_repo = https://github.com/knutin/gcprof +pkg_gcprof_commit = master + +PACKAGES += geas +pkg_geas_name = geas +pkg_geas_description = Guess Erlang Application Scattering +pkg_geas_homepage = https://github.com/crownedgrouse/geas +pkg_geas_fetch = git +pkg_geas_repo = https://github.com/crownedgrouse/geas +pkg_geas_commit = master + +PACKAGES += geef +pkg_geef_name = geef +pkg_geef_description = Git NEEEEF (Erlang NIF) +pkg_geef_homepage = https://github.com/carlosmn/geef +pkg_geef_fetch = git +pkg_geef_repo = https://github.com/carlosmn/geef +pkg_geef_commit = master + +PACKAGES += gen_coap +pkg_gen_coap_name = gen_coap +pkg_gen_coap_description = Generic Erlang CoAP Client/Server +pkg_gen_coap_homepage = https://github.com/gotthardp/gen_coap +pkg_gen_coap_fetch = git +pkg_gen_coap_repo = https://github.com/gotthardp/gen_coap +pkg_gen_coap_commit = master + +PACKAGES += gen_cycle +pkg_gen_cycle_name = gen_cycle +pkg_gen_cycle_description = Simple, generic OTP behaviour for recurring tasks +pkg_gen_cycle_homepage = https://github.com/aerosol/gen_cycle +pkg_gen_cycle_fetch = git +pkg_gen_cycle_repo = https://github.com/aerosol/gen_cycle +pkg_gen_cycle_commit = develop + +PACKAGES += gen_icmp +pkg_gen_icmp_name = gen_icmp +pkg_gen_icmp_description = Erlang interface to ICMP sockets +pkg_gen_icmp_homepage = https://github.com/msantos/gen_icmp +pkg_gen_icmp_fetch = git +pkg_gen_icmp_repo = https://github.com/msantos/gen_icmp +pkg_gen_icmp_commit = master + +PACKAGES += gen_nb_server +pkg_gen_nb_server_name = gen_nb_server +pkg_gen_nb_server_description = OTP behavior for writing non-blocking servers +pkg_gen_nb_server_homepage = https://github.com/kevsmith/gen_nb_server +pkg_gen_nb_server_fetch = git +pkg_gen_nb_server_repo = https://github.com/kevsmith/gen_nb_server +pkg_gen_nb_server_commit = master + +PACKAGES += gen_paxos +pkg_gen_paxos_name = gen_paxos +pkg_gen_paxos_description = An Erlang/OTP-style implementation of the PAXOS distributed consensus protocol +pkg_gen_paxos_homepage = https://github.com/gburd/gen_paxos +pkg_gen_paxos_fetch = git +pkg_gen_paxos_repo = https://github.com/gburd/gen_paxos +pkg_gen_paxos_commit = master + +PACKAGES += gen_smtp +pkg_gen_smtp_name = gen_smtp +pkg_gen_smtp_description = A generic Erlang SMTP server and client that can be extended via callback modules +pkg_gen_smtp_homepage = https://github.com/Vagabond/gen_smtp +pkg_gen_smtp_fetch = git +pkg_gen_smtp_repo = https://github.com/Vagabond/gen_smtp +pkg_gen_smtp_commit = master + +PACKAGES += gen_tracker +pkg_gen_tracker_name = gen_tracker +pkg_gen_tracker_description = supervisor with ets handling of children and their metadata +pkg_gen_tracker_homepage = https://github.com/erlyvideo/gen_tracker +pkg_gen_tracker_fetch = git +pkg_gen_tracker_repo = https://github.com/erlyvideo/gen_tracker +pkg_gen_tracker_commit = master + +PACKAGES += gen_unix +pkg_gen_unix_name = gen_unix +pkg_gen_unix_description = Erlang Unix socket interface +pkg_gen_unix_homepage = https://github.com/msantos/gen_unix +pkg_gen_unix_fetch = git +pkg_gen_unix_repo = https://github.com/msantos/gen_unix +pkg_gen_unix_commit = master + +PACKAGES += geode +pkg_geode_name = geode +pkg_geode_description = geohash/proximity lookup in pure, uncut erlang. +pkg_geode_homepage = https://github.com/bradfordw/geode +pkg_geode_fetch = git +pkg_geode_repo = https://github.com/bradfordw/geode +pkg_geode_commit = master + +PACKAGES += getopt +pkg_getopt_name = getopt +pkg_getopt_description = Module to parse command line arguments using the GNU getopt syntax +pkg_getopt_homepage = https://github.com/jcomellas/getopt +pkg_getopt_fetch = git +pkg_getopt_repo = https://github.com/jcomellas/getopt +pkg_getopt_commit = master + +PACKAGES += gettext +pkg_gettext_name = gettext +pkg_gettext_description = Erlang internationalization library. +pkg_gettext_homepage = https://github.com/etnt/gettext +pkg_gettext_fetch = git +pkg_gettext_repo = https://github.com/etnt/gettext +pkg_gettext_commit = master + +PACKAGES += giallo +pkg_giallo_name = giallo +pkg_giallo_description = Small and flexible web framework on top of Cowboy +pkg_giallo_homepage = https://github.com/kivra/giallo +pkg_giallo_fetch = git +pkg_giallo_repo = https://github.com/kivra/giallo +pkg_giallo_commit = master + +PACKAGES += gin +pkg_gin_name = gin +pkg_gin_description = The guards and for Erlang parse_transform +pkg_gin_homepage = https://github.com/mad-cocktail/gin +pkg_gin_fetch = git +pkg_gin_repo = https://github.com/mad-cocktail/gin +pkg_gin_commit = master + +PACKAGES += gitty +pkg_gitty_name = gitty +pkg_gitty_description = Git access in erlang +pkg_gitty_homepage = https://github.com/maxlapshin/gitty +pkg_gitty_fetch = git +pkg_gitty_repo = https://github.com/maxlapshin/gitty +pkg_gitty_commit = master + +PACKAGES += gold_fever +pkg_gold_fever_name = gold_fever +pkg_gold_fever_description = A Treasure Hunt for Erlangers +pkg_gold_fever_homepage = https://github.com/inaka/gold_fever +pkg_gold_fever_fetch = git +pkg_gold_fever_repo = https://github.com/inaka/gold_fever +pkg_gold_fever_commit = master + +PACKAGES += gossiperl +pkg_gossiperl_name = gossiperl +pkg_gossiperl_description = Gossip middleware in Erlang +pkg_gossiperl_homepage = http://gossiperl.com/ +pkg_gossiperl_fetch = git +pkg_gossiperl_repo = https://github.com/gossiperl/gossiperl +pkg_gossiperl_commit = master + +PACKAGES += gpb +pkg_gpb_name = gpb +pkg_gpb_description = A Google Protobuf implementation for Erlang +pkg_gpb_homepage = https://github.com/tomas-abrahamsson/gpb +pkg_gpb_fetch = git +pkg_gpb_repo = https://github.com/tomas-abrahamsson/gpb +pkg_gpb_commit = master + +PACKAGES += gproc +pkg_gproc_name = gproc +pkg_gproc_description = Extended process registry for Erlang +pkg_gproc_homepage = https://github.com/uwiger/gproc +pkg_gproc_fetch = git +pkg_gproc_repo = https://github.com/uwiger/gproc +pkg_gproc_commit = master + +PACKAGES += grapherl +pkg_grapherl_name = grapherl +pkg_grapherl_description = Create graphs of Erlang systems and programs +pkg_grapherl_homepage = https://github.com/eproxus/grapherl +pkg_grapherl_fetch = git +pkg_grapherl_repo = https://github.com/eproxus/grapherl +pkg_grapherl_commit = master + +PACKAGES += gun +pkg_gun_name = gun +pkg_gun_description = Asynchronous SPDY, HTTP and Websocket client written in Erlang. +pkg_gun_homepage = http//ninenines.eu +pkg_gun_fetch = git +pkg_gun_repo = https://github.com/ninenines/gun +pkg_gun_commit = master + +PACKAGES += gut +pkg_gut_name = gut +pkg_gut_description = gut is a template printing, aka scaffolding, tool for Erlang. Like rails generate or yeoman +pkg_gut_homepage = https://github.com/unbalancedparentheses/gut +pkg_gut_fetch = git +pkg_gut_repo = https://github.com/unbalancedparentheses/gut +pkg_gut_commit = master + +PACKAGES += hackney +pkg_hackney_name = hackney +pkg_hackney_description = simple HTTP client in Erlang +pkg_hackney_homepage = https://github.com/benoitc/hackney +pkg_hackney_fetch = git +pkg_hackney_repo = https://github.com/benoitc/hackney +pkg_hackney_commit = master + +PACKAGES += hamcrest +pkg_hamcrest_name = hamcrest +pkg_hamcrest_description = Erlang port of Hamcrest +pkg_hamcrest_homepage = https://github.com/hyperthunk/hamcrest-erlang +pkg_hamcrest_fetch = git +pkg_hamcrest_repo = https://github.com/hyperthunk/hamcrest-erlang +pkg_hamcrest_commit = master + +PACKAGES += hanoidb +pkg_hanoidb_name = hanoidb +pkg_hanoidb_description = Erlang LSM BTree Storage +pkg_hanoidb_homepage = https://github.com/krestenkrab/hanoidb +pkg_hanoidb_fetch = git +pkg_hanoidb_repo = https://github.com/krestenkrab/hanoidb +pkg_hanoidb_commit = master + +PACKAGES += hottub +pkg_hottub_name = hottub +pkg_hottub_description = Permanent Erlang Worker Pool +pkg_hottub_homepage = https://github.com/bfrog/hottub +pkg_hottub_fetch = git +pkg_hottub_repo = https://github.com/bfrog/hottub +pkg_hottub_commit = master + +PACKAGES += hpack +pkg_hpack_name = hpack +pkg_hpack_description = HPACK Implementation for Erlang +pkg_hpack_homepage = https://github.com/joedevivo/hpack +pkg_hpack_fetch = git +pkg_hpack_repo = https://github.com/joedevivo/hpack +pkg_hpack_commit = master + +PACKAGES += hyper +pkg_hyper_name = hyper +pkg_hyper_description = Erlang implementation of HyperLogLog +pkg_hyper_homepage = https://github.com/GameAnalytics/hyper +pkg_hyper_fetch = git +pkg_hyper_repo = https://github.com/GameAnalytics/hyper +pkg_hyper_commit = master + +PACKAGES += i18n +pkg_i18n_name = i18n +pkg_i18n_description = International components for unicode from Erlang (unicode, date, string, number, format, locale, localization, transliteration, icu4e) +pkg_i18n_homepage = https://github.com/erlang-unicode/i18n +pkg_i18n_fetch = git +pkg_i18n_repo = https://github.com/erlang-unicode/i18n +pkg_i18n_commit = master + +PACKAGES += ibrowse +pkg_ibrowse_name = ibrowse +pkg_ibrowse_description = Erlang HTTP client +pkg_ibrowse_homepage = https://github.com/cmullaparthi/ibrowse +pkg_ibrowse_fetch = git +pkg_ibrowse_repo = https://github.com/cmullaparthi/ibrowse +pkg_ibrowse_commit = master + +PACKAGES += ierlang +pkg_ierlang_name = ierlang +pkg_ierlang_description = An Erlang language kernel for IPython. +pkg_ierlang_homepage = https://github.com/robbielynch/ierlang +pkg_ierlang_fetch = git +pkg_ierlang_repo = https://github.com/robbielynch/ierlang +pkg_ierlang_commit = master + +PACKAGES += iota +pkg_iota_name = iota +pkg_iota_description = iota (Inter-dependency Objective Testing Apparatus) - a tool to enforce clean separation of responsibilities in Erlang code +pkg_iota_homepage = https://github.com/jpgneves/iota +pkg_iota_fetch = git +pkg_iota_repo = https://github.com/jpgneves/iota +pkg_iota_commit = master + +PACKAGES += irc_lib +pkg_irc_lib_name = irc_lib +pkg_irc_lib_description = Erlang irc client library +pkg_irc_lib_homepage = https://github.com/OtpChatBot/irc_lib +pkg_irc_lib_fetch = git +pkg_irc_lib_repo = https://github.com/OtpChatBot/irc_lib +pkg_irc_lib_commit = master + +PACKAGES += ircd +pkg_ircd_name = ircd +pkg_ircd_description = A pluggable IRC daemon application/library for Erlang. +pkg_ircd_homepage = https://github.com/tonyg/erlang-ircd +pkg_ircd_fetch = git +pkg_ircd_repo = https://github.com/tonyg/erlang-ircd +pkg_ircd_commit = master + +PACKAGES += iris +pkg_iris_name = iris +pkg_iris_description = Iris Erlang binding +pkg_iris_homepage = https://github.com/project-iris/iris-erl +pkg_iris_fetch = git +pkg_iris_repo = https://github.com/project-iris/iris-erl +pkg_iris_commit = master + +PACKAGES += iso8601 +pkg_iso8601_name = iso8601 +pkg_iso8601_description = Erlang ISO 8601 date formatter/parser +pkg_iso8601_homepage = https://github.com/seansawyer/erlang_iso8601 +pkg_iso8601_fetch = git +pkg_iso8601_repo = https://github.com/seansawyer/erlang_iso8601 +pkg_iso8601_commit = master + +PACKAGES += jamdb_sybase +pkg_jamdb_sybase_name = jamdb_sybase +pkg_jamdb_sybase_description = Erlang driver for SAP Sybase ASE +pkg_jamdb_sybase_homepage = https://github.com/erlangbureau/jamdb_sybase +pkg_jamdb_sybase_fetch = git +pkg_jamdb_sybase_repo = https://github.com/erlangbureau/jamdb_sybase +pkg_jamdb_sybase_commit = master + +PACKAGES += jerg +pkg_jerg_name = jerg +pkg_jerg_description = JSON Schema to Erlang Records Generator +pkg_jerg_homepage = https://github.com/ddossot/jerg +pkg_jerg_fetch = git +pkg_jerg_repo = https://github.com/ddossot/jerg +pkg_jerg_commit = master + +PACKAGES += jesse +pkg_jesse_name = jesse +pkg_jesse_description = jesse (JSon Schema Erlang) is an implementation of a json schema validator for Erlang. +pkg_jesse_homepage = https://github.com/for-GET/jesse +pkg_jesse_fetch = git +pkg_jesse_repo = https://github.com/for-GET/jesse +pkg_jesse_commit = master + +PACKAGES += jiffy +pkg_jiffy_name = jiffy +pkg_jiffy_description = JSON NIFs for Erlang. +pkg_jiffy_homepage = https://github.com/davisp/jiffy +pkg_jiffy_fetch = git +pkg_jiffy_repo = https://github.com/davisp/jiffy +pkg_jiffy_commit = master + +PACKAGES += jiffy_v +pkg_jiffy_v_name = jiffy_v +pkg_jiffy_v_description = JSON validation utility +pkg_jiffy_v_homepage = https://github.com/shizzard/jiffy-v +pkg_jiffy_v_fetch = git +pkg_jiffy_v_repo = https://github.com/shizzard/jiffy-v +pkg_jiffy_v_commit = master + +PACKAGES += jobs +pkg_jobs_name = jobs +pkg_jobs_description = a Job scheduler for load regulation +pkg_jobs_homepage = https://github.com/esl/jobs +pkg_jobs_fetch = git +pkg_jobs_repo = https://github.com/esl/jobs +pkg_jobs_commit = master + +PACKAGES += joxa +pkg_joxa_name = joxa +pkg_joxa_description = A Modern Lisp for the Erlang VM +pkg_joxa_homepage = https://github.com/joxa/joxa +pkg_joxa_fetch = git +pkg_joxa_repo = https://github.com/joxa/joxa +pkg_joxa_commit = master + +PACKAGES += json +pkg_json_name = json +pkg_json_description = a high level json library for erlang (17.0+) +pkg_json_homepage = https://github.com/talentdeficit/json +pkg_json_fetch = git +pkg_json_repo = https://github.com/talentdeficit/json +pkg_json_commit = master + +PACKAGES += json_rec +pkg_json_rec_name = json_rec +pkg_json_rec_description = JSON to erlang record +pkg_json_rec_homepage = https://github.com/justinkirby/json_rec +pkg_json_rec_fetch = git +pkg_json_rec_repo = https://github.com/justinkirby/json_rec +pkg_json_rec_commit = master + +PACKAGES += jsone +pkg_jsone_name = jsone +pkg_jsone_description = An Erlang library for encoding, decoding JSON data. +pkg_jsone_homepage = https://github.com/sile/jsone.git +pkg_jsone_fetch = git +pkg_jsone_repo = https://github.com/sile/jsone.git +pkg_jsone_commit = master + +PACKAGES += jsonerl +pkg_jsonerl_name = jsonerl +pkg_jsonerl_description = yet another but slightly different erlang <-> json encoder/decoder +pkg_jsonerl_homepage = https://github.com/lambder/jsonerl +pkg_jsonerl_fetch = git +pkg_jsonerl_repo = https://github.com/lambder/jsonerl +pkg_jsonerl_commit = master + +PACKAGES += jsonpath +pkg_jsonpath_name = jsonpath +pkg_jsonpath_description = Fast Erlang JSON data retrieval and updates via javascript-like notation +pkg_jsonpath_homepage = https://github.com/GeneStevens/jsonpath +pkg_jsonpath_fetch = git +pkg_jsonpath_repo = https://github.com/GeneStevens/jsonpath +pkg_jsonpath_commit = master + +PACKAGES += jsonx +pkg_jsonx_name = jsonx +pkg_jsonx_description = JSONX is an Erlang library for efficient decode and encode JSON, written in C. +pkg_jsonx_homepage = https://github.com/iskra/jsonx +pkg_jsonx_fetch = git +pkg_jsonx_repo = https://github.com/iskra/jsonx +pkg_jsonx_commit = master + +PACKAGES += jsx +pkg_jsx_name = jsx +pkg_jsx_description = An Erlang application for consuming, producing and manipulating JSON. +pkg_jsx_homepage = https://github.com/talentdeficit/jsx +pkg_jsx_fetch = git +pkg_jsx_repo = https://github.com/talentdeficit/jsx +pkg_jsx_commit = master + +PACKAGES += kafka +pkg_kafka_name = kafka +pkg_kafka_description = Kafka consumer and producer in Erlang +pkg_kafka_homepage = https://github.com/wooga/kafka-erlang +pkg_kafka_fetch = git +pkg_kafka_repo = https://github.com/wooga/kafka-erlang +pkg_kafka_commit = master + +PACKAGES += kafka_protocol +pkg_kafka_protocol_name = kafka_protocol +pkg_kafka_protocol_description = Kafka protocol Erlang library +pkg_kafka_protocol_homepage = https://github.com/klarna/kafka_protocol +pkg_kafka_protocol_fetch = git +pkg_kafka_protocol_repo = https://github.com/klarna/kafka_protocol.git +pkg_kafka_protocol_commit = master + +PACKAGES += kai +pkg_kai_name = kai +pkg_kai_description = DHT storage by Takeshi Inoue +pkg_kai_homepage = https://github.com/synrc/kai +pkg_kai_fetch = git +pkg_kai_repo = https://github.com/synrc/kai +pkg_kai_commit = master + +PACKAGES += katja +pkg_katja_name = katja +pkg_katja_description = A simple Riemann client written in Erlang. +pkg_katja_homepage = https://github.com/nifoc/katja +pkg_katja_fetch = git +pkg_katja_repo = https://github.com/nifoc/katja +pkg_katja_commit = master + +PACKAGES += kdht +pkg_kdht_name = kdht +pkg_kdht_description = kdht is an erlang DHT implementation +pkg_kdht_homepage = https://github.com/kevinlynx/kdht +pkg_kdht_fetch = git +pkg_kdht_repo = https://github.com/kevinlynx/kdht +pkg_kdht_commit = master + +PACKAGES += key2value +pkg_key2value_name = key2value +pkg_key2value_description = Erlang 2-way map +pkg_key2value_homepage = https://github.com/okeuday/key2value +pkg_key2value_fetch = git +pkg_key2value_repo = https://github.com/okeuday/key2value +pkg_key2value_commit = master + +PACKAGES += keys1value +pkg_keys1value_name = keys1value +pkg_keys1value_description = Erlang set associative map for key lists +pkg_keys1value_homepage = https://github.com/okeuday/keys1value +pkg_keys1value_fetch = git +pkg_keys1value_repo = https://github.com/okeuday/keys1value +pkg_keys1value_commit = master + +PACKAGES += kinetic +pkg_kinetic_name = kinetic +pkg_kinetic_description = Erlang Kinesis Client +pkg_kinetic_homepage = https://github.com/AdRoll/kinetic +pkg_kinetic_fetch = git +pkg_kinetic_repo = https://github.com/AdRoll/kinetic +pkg_kinetic_commit = master + +PACKAGES += kjell +pkg_kjell_name = kjell +pkg_kjell_description = Erlang Shell +pkg_kjell_homepage = https://github.com/karlll/kjell +pkg_kjell_fetch = git +pkg_kjell_repo = https://github.com/karlll/kjell +pkg_kjell_commit = master + +PACKAGES += kraken +pkg_kraken_name = kraken +pkg_kraken_description = Distributed Pubsub Server for Realtime Apps +pkg_kraken_homepage = https://github.com/Asana/kraken +pkg_kraken_fetch = git +pkg_kraken_repo = https://github.com/Asana/kraken +pkg_kraken_commit = master + +PACKAGES += kucumberl +pkg_kucumberl_name = kucumberl +pkg_kucumberl_description = A pure-erlang, open-source, implementation of Cucumber +pkg_kucumberl_homepage = https://github.com/openshine/kucumberl +pkg_kucumberl_fetch = git +pkg_kucumberl_repo = https://github.com/openshine/kucumberl +pkg_kucumberl_commit = master + +PACKAGES += kvc +pkg_kvc_name = kvc +pkg_kvc_description = KVC - Key Value Coding for Erlang data structures +pkg_kvc_homepage = https://github.com/etrepum/kvc +pkg_kvc_fetch = git +pkg_kvc_repo = https://github.com/etrepum/kvc +pkg_kvc_commit = master + +PACKAGES += kvlists +pkg_kvlists_name = kvlists +pkg_kvlists_description = Lists of key-value pairs (decoded JSON) in Erlang +pkg_kvlists_homepage = https://github.com/jcomellas/kvlists +pkg_kvlists_fetch = git +pkg_kvlists_repo = https://github.com/jcomellas/kvlists +pkg_kvlists_commit = master + +PACKAGES += kvs +pkg_kvs_name = kvs +pkg_kvs_description = Container and Iterator +pkg_kvs_homepage = https://github.com/synrc/kvs +pkg_kvs_fetch = git +pkg_kvs_repo = https://github.com/synrc/kvs +pkg_kvs_commit = master + +PACKAGES += lager +pkg_lager_name = lager +pkg_lager_description = A logging framework for Erlang/OTP. +pkg_lager_homepage = https://github.com/basho/lager +pkg_lager_fetch = git +pkg_lager_repo = https://github.com/basho/lager +pkg_lager_commit = master + +PACKAGES += lager_amqp_backend +pkg_lager_amqp_backend_name = lager_amqp_backend +pkg_lager_amqp_backend_description = AMQP RabbitMQ Lager backend +pkg_lager_amqp_backend_homepage = https://github.com/jbrisbin/lager_amqp_backend +pkg_lager_amqp_backend_fetch = git +pkg_lager_amqp_backend_repo = https://github.com/jbrisbin/lager_amqp_backend +pkg_lager_amqp_backend_commit = master + +PACKAGES += lager_syslog +pkg_lager_syslog_name = lager_syslog +pkg_lager_syslog_description = Syslog backend for lager +pkg_lager_syslog_homepage = https://github.com/basho/lager_syslog +pkg_lager_syslog_fetch = git +pkg_lager_syslog_repo = https://github.com/basho/lager_syslog +pkg_lager_syslog_commit = master + +PACKAGES += lambdapad +pkg_lambdapad_name = lambdapad +pkg_lambdapad_description = Static site generator using Erlang. Yes, Erlang. +pkg_lambdapad_homepage = https://github.com/gar1t/lambdapad +pkg_lambdapad_fetch = git +pkg_lambdapad_repo = https://github.com/gar1t/lambdapad +pkg_lambdapad_commit = master + +PACKAGES += lasp +pkg_lasp_name = lasp +pkg_lasp_description = A Language for Distributed, Eventually Consistent Computations +pkg_lasp_homepage = http://lasp-lang.org/ +pkg_lasp_fetch = git +pkg_lasp_repo = https://github.com/lasp-lang/lasp +pkg_lasp_commit = master + +PACKAGES += lasse +pkg_lasse_name = lasse +pkg_lasse_description = SSE handler for Cowboy +pkg_lasse_homepage = https://github.com/inaka/lasse +pkg_lasse_fetch = git +pkg_lasse_repo = https://github.com/inaka/lasse +pkg_lasse_commit = master + +PACKAGES += ldap +pkg_ldap_name = ldap +pkg_ldap_description = LDAP server written in Erlang +pkg_ldap_homepage = https://github.com/spawnproc/ldap +pkg_ldap_fetch = git +pkg_ldap_repo = https://github.com/spawnproc/ldap +pkg_ldap_commit = master + +PACKAGES += lethink +pkg_lethink_name = lethink +pkg_lethink_description = erlang driver for rethinkdb +pkg_lethink_homepage = https://github.com/taybin/lethink +pkg_lethink_fetch = git +pkg_lethink_repo = https://github.com/taybin/lethink +pkg_lethink_commit = master + +PACKAGES += lfe +pkg_lfe_name = lfe +pkg_lfe_description = Lisp Flavoured Erlang (LFE) +pkg_lfe_homepage = https://github.com/rvirding/lfe +pkg_lfe_fetch = git +pkg_lfe_repo = https://github.com/rvirding/lfe +pkg_lfe_commit = master + +PACKAGES += ling +pkg_ling_name = ling +pkg_ling_description = Erlang on Xen +pkg_ling_homepage = https://github.com/cloudozer/ling +pkg_ling_fetch = git +pkg_ling_repo = https://github.com/cloudozer/ling +pkg_ling_commit = master + +PACKAGES += live +pkg_live_name = live +pkg_live_description = Automated module and configuration reloader. +pkg_live_homepage = http://ninenines.eu +pkg_live_fetch = git +pkg_live_repo = https://github.com/ninenines/live +pkg_live_commit = master + +PACKAGES += lmq +pkg_lmq_name = lmq +pkg_lmq_description = Lightweight Message Queue +pkg_lmq_homepage = https://github.com/iij/lmq +pkg_lmq_fetch = git +pkg_lmq_repo = https://github.com/iij/lmq +pkg_lmq_commit = master + +PACKAGES += locker +pkg_locker_name = locker +pkg_locker_description = Atomic distributed 'check and set' for short-lived keys +pkg_locker_homepage = https://github.com/wooga/locker +pkg_locker_fetch = git +pkg_locker_repo = https://github.com/wooga/locker +pkg_locker_commit = master + +PACKAGES += locks +pkg_locks_name = locks +pkg_locks_description = A scalable, deadlock-resolving resource locker +pkg_locks_homepage = https://github.com/uwiger/locks +pkg_locks_fetch = git +pkg_locks_repo = https://github.com/uwiger/locks +pkg_locks_commit = master + +PACKAGES += log4erl +pkg_log4erl_name = log4erl +pkg_log4erl_description = A logger for erlang in the spirit of Log4J. +pkg_log4erl_homepage = https://github.com/ahmednawras/log4erl +pkg_log4erl_fetch = git +pkg_log4erl_repo = https://github.com/ahmednawras/log4erl +pkg_log4erl_commit = master + +PACKAGES += lol +pkg_lol_name = lol +pkg_lol_description = Lisp on erLang, and programming is fun again +pkg_lol_homepage = https://github.com/b0oh/lol +pkg_lol_fetch = git +pkg_lol_repo = https://github.com/b0oh/lol +pkg_lol_commit = master + +PACKAGES += lucid +pkg_lucid_name = lucid +pkg_lucid_description = HTTP/2 server written in Erlang +pkg_lucid_homepage = https://github.com/tatsuhiro-t/lucid +pkg_lucid_fetch = git +pkg_lucid_repo = https://github.com/tatsuhiro-t/lucid +pkg_lucid_commit = master + +PACKAGES += luerl +pkg_luerl_name = luerl +pkg_luerl_description = Lua in Erlang +pkg_luerl_homepage = https://github.com/rvirding/luerl +pkg_luerl_fetch = git +pkg_luerl_repo = https://github.com/rvirding/luerl +pkg_luerl_commit = develop + +PACKAGES += luwak +pkg_luwak_name = luwak +pkg_luwak_description = Large-object storage interface for Riak +pkg_luwak_homepage = https://github.com/basho/luwak +pkg_luwak_fetch = git +pkg_luwak_repo = https://github.com/basho/luwak +pkg_luwak_commit = master + +PACKAGES += lux +pkg_lux_name = lux +pkg_lux_description = Lux (LUcid eXpect scripting) simplifies test automation and provides an Expect-style execution of commands +pkg_lux_homepage = https://github.com/hawk/lux +pkg_lux_fetch = git +pkg_lux_repo = https://github.com/hawk/lux +pkg_lux_commit = master + +PACKAGES += machi +pkg_machi_name = machi +pkg_machi_description = Machi file store +pkg_machi_homepage = https://github.com/basho/machi +pkg_machi_fetch = git +pkg_machi_repo = https://github.com/basho/machi +pkg_machi_commit = master + +PACKAGES += mad +pkg_mad_name = mad +pkg_mad_description = Small and Fast Rebar Replacement +pkg_mad_homepage = https://github.com/synrc/mad +pkg_mad_fetch = git +pkg_mad_repo = https://github.com/synrc/mad +pkg_mad_commit = master + +PACKAGES += marina +pkg_marina_name = marina +pkg_marina_description = Non-blocking Erlang Cassandra CQL3 client +pkg_marina_homepage = https://github.com/lpgauth/marina +pkg_marina_fetch = git +pkg_marina_repo = https://github.com/lpgauth/marina +pkg_marina_commit = master + +PACKAGES += mavg +pkg_mavg_name = mavg +pkg_mavg_description = Erlang :: Exponential moving average library +pkg_mavg_homepage = https://github.com/EchoTeam/mavg +pkg_mavg_fetch = git +pkg_mavg_repo = https://github.com/EchoTeam/mavg +pkg_mavg_commit = master + +PACKAGES += mc_erl +pkg_mc_erl_name = mc_erl +pkg_mc_erl_description = mc-erl is a server for Minecraft 1.4.7 written in Erlang. +pkg_mc_erl_homepage = https://github.com/clonejo/mc-erl +pkg_mc_erl_fetch = git +pkg_mc_erl_repo = https://github.com/clonejo/mc-erl +pkg_mc_erl_commit = master + +PACKAGES += mcd +pkg_mcd_name = mcd +pkg_mcd_description = Fast memcached protocol client in pure Erlang +pkg_mcd_homepage = https://github.com/EchoTeam/mcd +pkg_mcd_fetch = git +pkg_mcd_repo = https://github.com/EchoTeam/mcd +pkg_mcd_commit = master + +PACKAGES += mcerlang +pkg_mcerlang_name = mcerlang +pkg_mcerlang_description = The McErlang model checker for Erlang +pkg_mcerlang_homepage = https://github.com/fredlund/McErlang +pkg_mcerlang_fetch = git +pkg_mcerlang_repo = https://github.com/fredlund/McErlang +pkg_mcerlang_commit = master + +PACKAGES += meck +pkg_meck_name = meck +pkg_meck_description = A mocking library for Erlang +pkg_meck_homepage = https://github.com/eproxus/meck +pkg_meck_fetch = git +pkg_meck_repo = https://github.com/eproxus/meck +pkg_meck_commit = master + +PACKAGES += mekao +pkg_mekao_name = mekao +pkg_mekao_description = SQL constructor +pkg_mekao_homepage = https://github.com/ddosia/mekao +pkg_mekao_fetch = git +pkg_mekao_repo = https://github.com/ddosia/mekao +pkg_mekao_commit = master + +PACKAGES += memo +pkg_memo_name = memo +pkg_memo_description = Erlang memoization server +pkg_memo_homepage = https://github.com/tuncer/memo +pkg_memo_fetch = git +pkg_memo_repo = https://github.com/tuncer/memo +pkg_memo_commit = master + +PACKAGES += merge_index +pkg_merge_index_name = merge_index +pkg_merge_index_description = MergeIndex is an Erlang library for storing ordered sets on disk. It is very similar to an SSTable (in Google's Bigtable) or an HFile (in Hadoop). +pkg_merge_index_homepage = https://github.com/basho/merge_index +pkg_merge_index_fetch = git +pkg_merge_index_repo = https://github.com/basho/merge_index +pkg_merge_index_commit = master + +PACKAGES += merl +pkg_merl_name = merl +pkg_merl_description = Metaprogramming in Erlang +pkg_merl_homepage = https://github.com/richcarl/merl +pkg_merl_fetch = git +pkg_merl_repo = https://github.com/richcarl/merl +pkg_merl_commit = master + +PACKAGES += mimerl +pkg_mimerl_name = mimerl +pkg_mimerl_description = library to handle mimetypes +pkg_mimerl_homepage = https://github.com/benoitc/mimerl +pkg_mimerl_fetch = git +pkg_mimerl_repo = https://github.com/benoitc/mimerl +pkg_mimerl_commit = master + +PACKAGES += mimetypes +pkg_mimetypes_name = mimetypes +pkg_mimetypes_description = Erlang MIME types library +pkg_mimetypes_homepage = https://github.com/spawngrid/mimetypes +pkg_mimetypes_fetch = git +pkg_mimetypes_repo = https://github.com/spawngrid/mimetypes +pkg_mimetypes_commit = master + +PACKAGES += mixer +pkg_mixer_name = mixer +pkg_mixer_description = Mix in functions from other modules +pkg_mixer_homepage = https://github.com/chef/mixer +pkg_mixer_fetch = git +pkg_mixer_repo = https://github.com/chef/mixer +pkg_mixer_commit = master + +PACKAGES += mochiweb +pkg_mochiweb_name = mochiweb +pkg_mochiweb_description = MochiWeb is an Erlang library for building lightweight HTTP servers. +pkg_mochiweb_homepage = https://github.com/mochi/mochiweb +pkg_mochiweb_fetch = git +pkg_mochiweb_repo = https://github.com/mochi/mochiweb +pkg_mochiweb_commit = master + +PACKAGES += mochiweb_xpath +pkg_mochiweb_xpath_name = mochiweb_xpath +pkg_mochiweb_xpath_description = XPath support for mochiweb's html parser +pkg_mochiweb_xpath_homepage = https://github.com/retnuh/mochiweb_xpath +pkg_mochiweb_xpath_fetch = git +pkg_mochiweb_xpath_repo = https://github.com/retnuh/mochiweb_xpath +pkg_mochiweb_xpath_commit = master + +PACKAGES += mockgyver +pkg_mockgyver_name = mockgyver +pkg_mockgyver_description = A mocking library for Erlang +pkg_mockgyver_homepage = https://github.com/klajo/mockgyver +pkg_mockgyver_fetch = git +pkg_mockgyver_repo = https://github.com/klajo/mockgyver +pkg_mockgyver_commit = master + +PACKAGES += modlib +pkg_modlib_name = modlib +pkg_modlib_description = Web framework based on Erlang's inets httpd +pkg_modlib_homepage = https://github.com/gar1t/modlib +pkg_modlib_fetch = git +pkg_modlib_repo = https://github.com/gar1t/modlib +pkg_modlib_commit = master + +PACKAGES += mongodb +pkg_mongodb_name = mongodb +pkg_mongodb_description = MongoDB driver for Erlang +pkg_mongodb_homepage = https://github.com/comtihon/mongodb-erlang +pkg_mongodb_fetch = git +pkg_mongodb_repo = https://github.com/comtihon/mongodb-erlang +pkg_mongodb_commit = master + +PACKAGES += mongooseim +pkg_mongooseim_name = mongooseim +pkg_mongooseim_description = Jabber / XMPP server with focus on performance and scalability, by Erlang Solutions +pkg_mongooseim_homepage = https://www.erlang-solutions.com/products/mongooseim-massively-scalable-ejabberd-platform +pkg_mongooseim_fetch = git +pkg_mongooseim_repo = https://github.com/esl/MongooseIM +pkg_mongooseim_commit = master + +PACKAGES += moyo +pkg_moyo_name = moyo +pkg_moyo_description = Erlang utility functions library +pkg_moyo_homepage = https://github.com/dwango/moyo +pkg_moyo_fetch = git +pkg_moyo_repo = https://github.com/dwango/moyo +pkg_moyo_commit = master + +PACKAGES += msgpack +pkg_msgpack_name = msgpack +pkg_msgpack_description = MessagePack (de)serializer implementation for Erlang +pkg_msgpack_homepage = https://github.com/msgpack/msgpack-erlang +pkg_msgpack_fetch = git +pkg_msgpack_repo = https://github.com/msgpack/msgpack-erlang +pkg_msgpack_commit = master + +PACKAGES += mu2 +pkg_mu2_name = mu2 +pkg_mu2_description = Erlang mutation testing tool +pkg_mu2_homepage = https://github.com/ramsay-t/mu2 +pkg_mu2_fetch = git +pkg_mu2_repo = https://github.com/ramsay-t/mu2 +pkg_mu2_commit = master + +PACKAGES += mustache +pkg_mustache_name = mustache +pkg_mustache_description = Mustache template engine for Erlang. +pkg_mustache_homepage = https://github.com/mojombo/mustache.erl +pkg_mustache_fetch = git +pkg_mustache_repo = https://github.com/mojombo/mustache.erl +pkg_mustache_commit = master + +PACKAGES += myproto +pkg_myproto_name = myproto +pkg_myproto_description = MySQL Server Protocol in Erlang +pkg_myproto_homepage = https://github.com/altenwald/myproto +pkg_myproto_fetch = git +pkg_myproto_repo = https://github.com/altenwald/myproto +pkg_myproto_commit = master + +PACKAGES += mysql +pkg_mysql_name = mysql +pkg_mysql_description = Erlang MySQL Driver (from code.google.com) +pkg_mysql_homepage = https://github.com/dizzyd/erlang-mysql-driver +pkg_mysql_fetch = git +pkg_mysql_repo = https://github.com/dizzyd/erlang-mysql-driver +pkg_mysql_commit = master + +PACKAGES += n2o +pkg_n2o_name = n2o +pkg_n2o_description = WebSocket Application Server +pkg_n2o_homepage = https://github.com/5HT/n2o +pkg_n2o_fetch = git +pkg_n2o_repo = https://github.com/5HT/n2o +pkg_n2o_commit = master + +PACKAGES += nat_upnp +pkg_nat_upnp_name = nat_upnp +pkg_nat_upnp_description = Erlang library to map your internal port to an external using UNP IGD +pkg_nat_upnp_homepage = https://github.com/benoitc/nat_upnp +pkg_nat_upnp_fetch = git +pkg_nat_upnp_repo = https://github.com/benoitc/nat_upnp +pkg_nat_upnp_commit = master + +PACKAGES += neo4j +pkg_neo4j_name = neo4j +pkg_neo4j_description = Erlang client library for Neo4J. +pkg_neo4j_homepage = https://github.com/dmitriid/neo4j-erlang +pkg_neo4j_fetch = git +pkg_neo4j_repo = https://github.com/dmitriid/neo4j-erlang +pkg_neo4j_commit = master + +PACKAGES += neotoma +pkg_neotoma_name = neotoma +pkg_neotoma_description = Erlang library and packrat parser-generator for parsing expression grammars. +pkg_neotoma_homepage = https://github.com/seancribbs/neotoma +pkg_neotoma_fetch = git +pkg_neotoma_repo = https://github.com/seancribbs/neotoma +pkg_neotoma_commit = master + +PACKAGES += newrelic +pkg_newrelic_name = newrelic +pkg_newrelic_description = Erlang library for sending metrics to New Relic +pkg_newrelic_homepage = https://github.com/wooga/newrelic-erlang +pkg_newrelic_fetch = git +pkg_newrelic_repo = https://github.com/wooga/newrelic-erlang +pkg_newrelic_commit = master + +PACKAGES += nifty +pkg_nifty_name = nifty +pkg_nifty_description = Erlang NIF wrapper generator +pkg_nifty_homepage = https://github.com/parapluu/nifty +pkg_nifty_fetch = git +pkg_nifty_repo = https://github.com/parapluu/nifty +pkg_nifty_commit = master + +PACKAGES += nitrogen_core +pkg_nitrogen_core_name = nitrogen_core +pkg_nitrogen_core_description = The core Nitrogen library. +pkg_nitrogen_core_homepage = http://nitrogenproject.com/ +pkg_nitrogen_core_fetch = git +pkg_nitrogen_core_repo = https://github.com/nitrogen/nitrogen_core +pkg_nitrogen_core_commit = master + +PACKAGES += nkbase +pkg_nkbase_name = nkbase +pkg_nkbase_description = NkBASE distributed database +pkg_nkbase_homepage = https://github.com/Nekso/nkbase +pkg_nkbase_fetch = git +pkg_nkbase_repo = https://github.com/Nekso/nkbase +pkg_nkbase_commit = develop + +PACKAGES += nkdocker +pkg_nkdocker_name = nkdocker +pkg_nkdocker_description = Erlang Docker client +pkg_nkdocker_homepage = https://github.com/Nekso/nkdocker +pkg_nkdocker_fetch = git +pkg_nkdocker_repo = https://github.com/Nekso/nkdocker +pkg_nkdocker_commit = master + +PACKAGES += nkpacket +pkg_nkpacket_name = nkpacket +pkg_nkpacket_description = Generic Erlang transport layer +pkg_nkpacket_homepage = https://github.com/Nekso/nkpacket +pkg_nkpacket_fetch = git +pkg_nkpacket_repo = https://github.com/Nekso/nkpacket +pkg_nkpacket_commit = master + +PACKAGES += nksip +pkg_nksip_name = nksip +pkg_nksip_description = Erlang SIP application server +pkg_nksip_homepage = https://github.com/kalta/nksip +pkg_nksip_fetch = git +pkg_nksip_repo = https://github.com/kalta/nksip +pkg_nksip_commit = master + +PACKAGES += nodefinder +pkg_nodefinder_name = nodefinder +pkg_nodefinder_description = automatic node discovery via UDP multicast +pkg_nodefinder_homepage = https://github.com/erlanger/nodefinder +pkg_nodefinder_fetch = git +pkg_nodefinder_repo = https://github.com/okeuday/nodefinder +pkg_nodefinder_commit = master + +PACKAGES += nprocreg +pkg_nprocreg_name = nprocreg +pkg_nprocreg_description = Minimal Distributed Erlang Process Registry +pkg_nprocreg_homepage = http://nitrogenproject.com/ +pkg_nprocreg_fetch = git +pkg_nprocreg_repo = https://github.com/nitrogen/nprocreg +pkg_nprocreg_commit = master + +PACKAGES += oauth +pkg_oauth_name = oauth +pkg_oauth_description = An Erlang OAuth 1.0 implementation +pkg_oauth_homepage = https://github.com/tim/erlang-oauth +pkg_oauth_fetch = git +pkg_oauth_repo = https://github.com/tim/erlang-oauth +pkg_oauth_commit = master + +PACKAGES += oauth2 +pkg_oauth2_name = oauth2 +pkg_oauth2_description = Erlang Oauth2 implementation +pkg_oauth2_homepage = https://github.com/kivra/oauth2 +pkg_oauth2_fetch = git +pkg_oauth2_repo = https://github.com/kivra/oauth2 +pkg_oauth2_commit = master + +PACKAGES += octopus +pkg_octopus_name = octopus +pkg_octopus_description = Small and flexible pool manager written in Erlang +pkg_octopus_homepage = https://github.com/erlangbureau/octopus +pkg_octopus_fetch = git +pkg_octopus_repo = https://github.com/erlangbureau/octopus +pkg_octopus_commit = master + +PACKAGES += of_protocol +pkg_of_protocol_name = of_protocol +pkg_of_protocol_description = OpenFlow Protocol Library for Erlang +pkg_of_protocol_homepage = https://github.com/FlowForwarding/of_protocol +pkg_of_protocol_fetch = git +pkg_of_protocol_repo = https://github.com/FlowForwarding/of_protocol +pkg_of_protocol_commit = master + +PACKAGES += opencouch +pkg_opencouch_name = couch +pkg_opencouch_description = A embeddable document oriented database compatible with Apache CouchDB +pkg_opencouch_homepage = https://github.com/benoitc/opencouch +pkg_opencouch_fetch = git +pkg_opencouch_repo = https://github.com/benoitc/opencouch +pkg_opencouch_commit = master + +PACKAGES += openflow +pkg_openflow_name = openflow +pkg_openflow_description = An OpenFlow controller written in pure erlang +pkg_openflow_homepage = https://github.com/renatoaguiar/erlang-openflow +pkg_openflow_fetch = git +pkg_openflow_repo = https://github.com/renatoaguiar/erlang-openflow +pkg_openflow_commit = master + +PACKAGES += openid +pkg_openid_name = openid +pkg_openid_description = Erlang OpenID +pkg_openid_homepage = https://github.com/brendonh/erl_openid +pkg_openid_fetch = git +pkg_openid_repo = https://github.com/brendonh/erl_openid +pkg_openid_commit = master + +PACKAGES += openpoker +pkg_openpoker_name = openpoker +pkg_openpoker_description = Genesis Texas hold'em Game Server +pkg_openpoker_homepage = https://github.com/hpyhacking/openpoker +pkg_openpoker_fetch = git +pkg_openpoker_repo = https://github.com/hpyhacking/openpoker +pkg_openpoker_commit = master + +PACKAGES += pal +pkg_pal_name = pal +pkg_pal_description = Pragmatic Authentication Library +pkg_pal_homepage = https://github.com/manifest/pal +pkg_pal_fetch = git +pkg_pal_repo = https://github.com/manifest/pal +pkg_pal_commit = master + +PACKAGES += parse_trans +pkg_parse_trans_name = parse_trans +pkg_parse_trans_description = Parse transform utilities for Erlang +pkg_parse_trans_homepage = https://github.com/uwiger/parse_trans +pkg_parse_trans_fetch = git +pkg_parse_trans_repo = https://github.com/uwiger/parse_trans +pkg_parse_trans_commit = master + +PACKAGES += parsexml +pkg_parsexml_name = parsexml +pkg_parsexml_description = Simple DOM XML parser with convenient and very simple API +pkg_parsexml_homepage = https://github.com/maxlapshin/parsexml +pkg_parsexml_fetch = git +pkg_parsexml_repo = https://github.com/maxlapshin/parsexml +pkg_parsexml_commit = master + +PACKAGES += pegjs +pkg_pegjs_name = pegjs +pkg_pegjs_description = An implementation of PEG.js grammar for Erlang. +pkg_pegjs_homepage = https://github.com/dmitriid/pegjs +pkg_pegjs_fetch = git +pkg_pegjs_repo = https://github.com/dmitriid/pegjs +pkg_pegjs_commit = master + +PACKAGES += percept2 +pkg_percept2_name = percept2 +pkg_percept2_description = Concurrent profiling tool for Erlang +pkg_percept2_homepage = https://github.com/huiqing/percept2 +pkg_percept2_fetch = git +pkg_percept2_repo = https://github.com/huiqing/percept2 +pkg_percept2_commit = master + +PACKAGES += pgsql +pkg_pgsql_name = pgsql +pkg_pgsql_description = Erlang PostgreSQL driver +pkg_pgsql_homepage = https://github.com/semiocast/pgsql +pkg_pgsql_fetch = git +pkg_pgsql_repo = https://github.com/semiocast/pgsql +pkg_pgsql_commit = master + +PACKAGES += pkgx +pkg_pkgx_name = pkgx +pkg_pkgx_description = Build .deb packages from Erlang releases +pkg_pkgx_homepage = https://github.com/arjan/pkgx +pkg_pkgx_fetch = git +pkg_pkgx_repo = https://github.com/arjan/pkgx +pkg_pkgx_commit = master + +PACKAGES += pkt +pkg_pkt_name = pkt +pkg_pkt_description = Erlang network protocol library +pkg_pkt_homepage = https://github.com/msantos/pkt +pkg_pkt_fetch = git +pkg_pkt_repo = https://github.com/msantos/pkt +pkg_pkt_commit = master + +PACKAGES += plain_fsm +pkg_plain_fsm_name = plain_fsm +pkg_plain_fsm_description = A behaviour/support library for writing plain Erlang FSMs. +pkg_plain_fsm_homepage = https://github.com/uwiger/plain_fsm +pkg_plain_fsm_fetch = git +pkg_plain_fsm_repo = https://github.com/uwiger/plain_fsm +pkg_plain_fsm_commit = master + +PACKAGES += plumtree +pkg_plumtree_name = plumtree +pkg_plumtree_description = Epidemic Broadcast Trees +pkg_plumtree_homepage = https://github.com/helium/plumtree +pkg_plumtree_fetch = git +pkg_plumtree_repo = https://github.com/helium/plumtree +pkg_plumtree_commit = master + +PACKAGES += pmod_transform +pkg_pmod_transform_name = pmod_transform +pkg_pmod_transform_description = Parse transform for parameterized modules +pkg_pmod_transform_homepage = https://github.com/erlang/pmod_transform +pkg_pmod_transform_fetch = git +pkg_pmod_transform_repo = https://github.com/erlang/pmod_transform +pkg_pmod_transform_commit = master + +PACKAGES += pobox +pkg_pobox_name = pobox +pkg_pobox_description = External buffer processes to protect against mailbox overflow in Erlang +pkg_pobox_homepage = https://github.com/ferd/pobox +pkg_pobox_fetch = git +pkg_pobox_repo = https://github.com/ferd/pobox +pkg_pobox_commit = master + +PACKAGES += ponos +pkg_ponos_name = ponos +pkg_ponos_description = ponos is a simple yet powerful load generator written in erlang +pkg_ponos_homepage = https://github.com/klarna/ponos +pkg_ponos_fetch = git +pkg_ponos_repo = https://github.com/klarna/ponos +pkg_ponos_commit = master + +PACKAGES += poolboy +pkg_poolboy_name = poolboy +pkg_poolboy_description = A hunky Erlang worker pool factory +pkg_poolboy_homepage = https://github.com/devinus/poolboy +pkg_poolboy_fetch = git +pkg_poolboy_repo = https://github.com/devinus/poolboy +pkg_poolboy_commit = master + +PACKAGES += pooler +pkg_pooler_name = pooler +pkg_pooler_description = An OTP Process Pool Application +pkg_pooler_homepage = https://github.com/seth/pooler +pkg_pooler_fetch = git +pkg_pooler_repo = https://github.com/seth/pooler +pkg_pooler_commit = master + +PACKAGES += pqueue +pkg_pqueue_name = pqueue +pkg_pqueue_description = Erlang Priority Queues +pkg_pqueue_homepage = https://github.com/okeuday/pqueue +pkg_pqueue_fetch = git +pkg_pqueue_repo = https://github.com/okeuday/pqueue +pkg_pqueue_commit = master + +PACKAGES += procket +pkg_procket_name = procket +pkg_procket_description = Erlang interface to low level socket operations +pkg_procket_homepage = http://blog.listincomprehension.com/search/label/procket +pkg_procket_fetch = git +pkg_procket_repo = https://github.com/msantos/procket +pkg_procket_commit = master + +PACKAGES += prop +pkg_prop_name = prop +pkg_prop_description = An Erlang code scaffolding and generator system. +pkg_prop_homepage = https://github.com/nuex/prop +pkg_prop_fetch = git +pkg_prop_repo = https://github.com/nuex/prop +pkg_prop_commit = master + +PACKAGES += proper +pkg_proper_name = proper +pkg_proper_description = PropEr: a QuickCheck-inspired property-based testing tool for Erlang. +pkg_proper_homepage = http://proper.softlab.ntua.gr +pkg_proper_fetch = git +pkg_proper_repo = https://github.com/manopapad/proper +pkg_proper_commit = master + +PACKAGES += props +pkg_props_name = props +pkg_props_description = Property structure library +pkg_props_homepage = https://github.com/greyarea/props +pkg_props_fetch = git +pkg_props_repo = https://github.com/greyarea/props +pkg_props_commit = master + +PACKAGES += protobuffs +pkg_protobuffs_name = protobuffs +pkg_protobuffs_description = An implementation of Google's Protocol Buffers for Erlang, based on ngerakines/erlang_protobuffs. +pkg_protobuffs_homepage = https://github.com/basho/erlang_protobuffs +pkg_protobuffs_fetch = git +pkg_protobuffs_repo = https://github.com/basho/erlang_protobuffs +pkg_protobuffs_commit = master + +PACKAGES += psycho +pkg_psycho_name = psycho +pkg_psycho_description = HTTP server that provides a WSGI-like interface for applications and middleware. +pkg_psycho_homepage = https://github.com/gar1t/psycho +pkg_psycho_fetch = git +pkg_psycho_repo = https://github.com/gar1t/psycho +pkg_psycho_commit = master + +PACKAGES += purity +pkg_purity_name = purity +pkg_purity_description = A side-effect analyzer for Erlang +pkg_purity_homepage = https://github.com/mpitid/purity +pkg_purity_fetch = git +pkg_purity_repo = https://github.com/mpitid/purity +pkg_purity_commit = master + +PACKAGES += push_service +pkg_push_service_name = push_service +pkg_push_service_description = Push service +pkg_push_service_homepage = https://github.com/hairyhum/push_service +pkg_push_service_fetch = git +pkg_push_service_repo = https://github.com/hairyhum/push_service +pkg_push_service_commit = master + +PACKAGES += qdate +pkg_qdate_name = qdate +pkg_qdate_description = Date, time, and timezone parsing, formatting, and conversion for Erlang. +pkg_qdate_homepage = https://github.com/choptastic/qdate +pkg_qdate_fetch = git +pkg_qdate_repo = https://github.com/choptastic/qdate +pkg_qdate_commit = master + +PACKAGES += qrcode +pkg_qrcode_name = qrcode +pkg_qrcode_description = QR Code encoder in Erlang +pkg_qrcode_homepage = https://github.com/komone/qrcode +pkg_qrcode_fetch = git +pkg_qrcode_repo = https://github.com/komone/qrcode +pkg_qrcode_commit = master + +PACKAGES += quest +pkg_quest_name = quest +pkg_quest_description = Learn Erlang through this set of challenges. An interactive system for getting to know Erlang. +pkg_quest_homepage = https://github.com/eriksoe/ErlangQuest +pkg_quest_fetch = git +pkg_quest_repo = https://github.com/eriksoe/ErlangQuest +pkg_quest_commit = master + +PACKAGES += quickrand +pkg_quickrand_name = quickrand +pkg_quickrand_description = Quick Erlang Random Number Generation +pkg_quickrand_homepage = https://github.com/okeuday/quickrand +pkg_quickrand_fetch = git +pkg_quickrand_repo = https://github.com/okeuday/quickrand +pkg_quickrand_commit = master + +PACKAGES += rabbit +pkg_rabbit_name = rabbit +pkg_rabbit_description = RabbitMQ Server +pkg_rabbit_homepage = https://www.rabbitmq.com/ +pkg_rabbit_fetch = git +pkg_rabbit_repo = https://github.com/rabbitmq/rabbitmq-server.git +pkg_rabbit_commit = master + +PACKAGES += rabbit_exchange_type_riak +pkg_rabbit_exchange_type_riak_name = rabbit_exchange_type_riak +pkg_rabbit_exchange_type_riak_description = Custom RabbitMQ exchange type for sticking messages in Riak +pkg_rabbit_exchange_type_riak_homepage = https://github.com/jbrisbin/riak-exchange +pkg_rabbit_exchange_type_riak_fetch = git +pkg_rabbit_exchange_type_riak_repo = https://github.com/jbrisbin/riak-exchange +pkg_rabbit_exchange_type_riak_commit = master + +PACKAGES += rack +pkg_rack_name = rack +pkg_rack_description = Rack handler for erlang +pkg_rack_homepage = https://github.com/erlyvideo/rack +pkg_rack_fetch = git +pkg_rack_repo = https://github.com/erlyvideo/rack +pkg_rack_commit = master + +PACKAGES += radierl +pkg_radierl_name = radierl +pkg_radierl_description = RADIUS protocol stack implemented in Erlang. +pkg_radierl_homepage = https://github.com/vances/radierl +pkg_radierl_fetch = git +pkg_radierl_repo = https://github.com/vances/radierl +pkg_radierl_commit = master + +PACKAGES += rafter +pkg_rafter_name = rafter +pkg_rafter_description = An Erlang library application which implements the Raft consensus protocol +pkg_rafter_homepage = https://github.com/andrewjstone/rafter +pkg_rafter_fetch = git +pkg_rafter_repo = https://github.com/andrewjstone/rafter +pkg_rafter_commit = master + +PACKAGES += ranch +pkg_ranch_name = ranch +pkg_ranch_description = Socket acceptor pool for TCP protocols. +pkg_ranch_homepage = http://ninenines.eu +pkg_ranch_fetch = git +pkg_ranch_repo = https://github.com/ninenines/ranch +pkg_ranch_commit = 1.2.1 + +PACKAGES += rbeacon +pkg_rbeacon_name = rbeacon +pkg_rbeacon_description = LAN discovery and presence in Erlang. +pkg_rbeacon_homepage = https://github.com/refuge/rbeacon +pkg_rbeacon_fetch = git +pkg_rbeacon_repo = https://github.com/refuge/rbeacon +pkg_rbeacon_commit = master + +PACKAGES += rebar +pkg_rebar_name = rebar +pkg_rebar_description = Erlang build tool that makes it easy to compile and test Erlang applications, port drivers and releases. +pkg_rebar_homepage = http://www.rebar3.org +pkg_rebar_fetch = git +pkg_rebar_repo = https://github.com/rebar/rebar3 +pkg_rebar_commit = master + +PACKAGES += rebus +pkg_rebus_name = rebus +pkg_rebus_description = A stupid simple, internal, pub/sub event bus written in- and for Erlang. +pkg_rebus_homepage = https://github.com/olle/rebus +pkg_rebus_fetch = git +pkg_rebus_repo = https://github.com/olle/rebus +pkg_rebus_commit = master + +PACKAGES += rec2json +pkg_rec2json_name = rec2json +pkg_rec2json_description = Compile erlang record definitions into modules to convert them to/from json easily. +pkg_rec2json_homepage = https://github.com/lordnull/rec2json +pkg_rec2json_fetch = git +pkg_rec2json_repo = https://github.com/lordnull/rec2json +pkg_rec2json_commit = master + +PACKAGES += recon +pkg_recon_name = recon +pkg_recon_description = Collection of functions and scripts to debug Erlang in production. +pkg_recon_homepage = https://github.com/ferd/recon +pkg_recon_fetch = git +pkg_recon_repo = https://github.com/ferd/recon +pkg_recon_commit = master + +PACKAGES += record_info +pkg_record_info_name = record_info +pkg_record_info_description = Convert between record and proplist +pkg_record_info_homepage = https://github.com/bipthelin/erlang-record_info +pkg_record_info_fetch = git +pkg_record_info_repo = https://github.com/bipthelin/erlang-record_info +pkg_record_info_commit = master + +PACKAGES += redgrid +pkg_redgrid_name = redgrid +pkg_redgrid_description = automatic Erlang node discovery via redis +pkg_redgrid_homepage = https://github.com/jkvor/redgrid +pkg_redgrid_fetch = git +pkg_redgrid_repo = https://github.com/jkvor/redgrid +pkg_redgrid_commit = master + +PACKAGES += redo +pkg_redo_name = redo +pkg_redo_description = pipelined erlang redis client +pkg_redo_homepage = https://github.com/jkvor/redo +pkg_redo_fetch = git +pkg_redo_repo = https://github.com/jkvor/redo +pkg_redo_commit = master + +PACKAGES += reload_mk +pkg_reload_mk_name = reload_mk +pkg_reload_mk_description = Live reload plugin for erlang.mk. +pkg_reload_mk_homepage = https://github.com/bullno1/reload.mk +pkg_reload_mk_fetch = git +pkg_reload_mk_repo = https://github.com/bullno1/reload.mk +pkg_reload_mk_commit = master + +PACKAGES += reltool_util +pkg_reltool_util_name = reltool_util +pkg_reltool_util_description = Erlang reltool utility functionality application +pkg_reltool_util_homepage = https://github.com/okeuday/reltool_util +pkg_reltool_util_fetch = git +pkg_reltool_util_repo = https://github.com/okeuday/reltool_util +pkg_reltool_util_commit = master + +PACKAGES += relx +pkg_relx_name = relx +pkg_relx_description = Sane, simple release creation for Erlang +pkg_relx_homepage = https://github.com/erlware/relx +pkg_relx_fetch = git +pkg_relx_repo = https://github.com/erlware/relx +pkg_relx_commit = master + +PACKAGES += resource_discovery +pkg_resource_discovery_name = resource_discovery +pkg_resource_discovery_description = An application used to dynamically discover resources present in an Erlang node cluster. +pkg_resource_discovery_homepage = http://erlware.org/ +pkg_resource_discovery_fetch = git +pkg_resource_discovery_repo = https://github.com/erlware/resource_discovery +pkg_resource_discovery_commit = master + +PACKAGES += restc +pkg_restc_name = restc +pkg_restc_description = Erlang Rest Client +pkg_restc_homepage = https://github.com/kivra/restclient +pkg_restc_fetch = git +pkg_restc_repo = https://github.com/kivra/restclient +pkg_restc_commit = master + +PACKAGES += rfc4627_jsonrpc +pkg_rfc4627_jsonrpc_name = rfc4627_jsonrpc +pkg_rfc4627_jsonrpc_description = Erlang RFC4627 (JSON) codec and JSON-RPC server implementation. +pkg_rfc4627_jsonrpc_homepage = https://github.com/tonyg/erlang-rfc4627 +pkg_rfc4627_jsonrpc_fetch = git +pkg_rfc4627_jsonrpc_repo = https://github.com/tonyg/erlang-rfc4627 +pkg_rfc4627_jsonrpc_commit = master + +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 + +PACKAGES += riak_core +pkg_riak_core_name = riak_core +pkg_riak_core_description = Distributed systems infrastructure used by Riak. +pkg_riak_core_homepage = https://github.com/basho/riak_core +pkg_riak_core_fetch = git +pkg_riak_core_repo = https://github.com/basho/riak_core +pkg_riak_core_commit = master + +PACKAGES += riak_dt +pkg_riak_dt_name = riak_dt +pkg_riak_dt_description = Convergent replicated datatypes in Erlang +pkg_riak_dt_homepage = https://github.com/basho/riak_dt +pkg_riak_dt_fetch = git +pkg_riak_dt_repo = https://github.com/basho/riak_dt +pkg_riak_dt_commit = master + +PACKAGES += riak_ensemble +pkg_riak_ensemble_name = riak_ensemble +pkg_riak_ensemble_description = Multi-Paxos framework in Erlang +pkg_riak_ensemble_homepage = https://github.com/basho/riak_ensemble +pkg_riak_ensemble_fetch = git +pkg_riak_ensemble_repo = https://github.com/basho/riak_ensemble +pkg_riak_ensemble_commit = master + +PACKAGES += riak_kv +pkg_riak_kv_name = riak_kv +pkg_riak_kv_description = Riak Key/Value Store +pkg_riak_kv_homepage = https://github.com/basho/riak_kv +pkg_riak_kv_fetch = git +pkg_riak_kv_repo = https://github.com/basho/riak_kv +pkg_riak_kv_commit = master + +PACKAGES += riak_pg +pkg_riak_pg_name = riak_pg +pkg_riak_pg_description = Distributed process groups with riak_core. +pkg_riak_pg_homepage = https://github.com/cmeiklejohn/riak_pg +pkg_riak_pg_fetch = git +pkg_riak_pg_repo = https://github.com/cmeiklejohn/riak_pg +pkg_riak_pg_commit = master + +PACKAGES += riak_pipe +pkg_riak_pipe_name = riak_pipe +pkg_riak_pipe_description = Riak Pipelines +pkg_riak_pipe_homepage = https://github.com/basho/riak_pipe +pkg_riak_pipe_fetch = git +pkg_riak_pipe_repo = https://github.com/basho/riak_pipe +pkg_riak_pipe_commit = master + +PACKAGES += riak_sysmon +pkg_riak_sysmon_name = riak_sysmon +pkg_riak_sysmon_description = Simple OTP app for managing Erlang VM system_monitor event messages +pkg_riak_sysmon_homepage = https://github.com/basho/riak_sysmon +pkg_riak_sysmon_fetch = git +pkg_riak_sysmon_repo = https://github.com/basho/riak_sysmon +pkg_riak_sysmon_commit = master + +PACKAGES += riak_test +pkg_riak_test_name = riak_test +pkg_riak_test_description = I'm in your cluster, testing your riaks +pkg_riak_test_homepage = https://github.com/basho/riak_test +pkg_riak_test_fetch = git +pkg_riak_test_repo = https://github.com/basho/riak_test +pkg_riak_test_commit = master + +PACKAGES += riakc +pkg_riakc_name = riakc +pkg_riakc_description = Erlang clients for Riak. +pkg_riakc_homepage = https://github.com/basho/riak-erlang-client +pkg_riakc_fetch = git +pkg_riakc_repo = https://github.com/basho/riak-erlang-client +pkg_riakc_commit = master + +PACKAGES += riakhttpc +pkg_riakhttpc_name = riakhttpc +pkg_riakhttpc_description = Riak Erlang client using the HTTP interface +pkg_riakhttpc_homepage = https://github.com/basho/riak-erlang-http-client +pkg_riakhttpc_fetch = git +pkg_riakhttpc_repo = https://github.com/basho/riak-erlang-http-client +pkg_riakhttpc_commit = master + +PACKAGES += riaknostic +pkg_riaknostic_name = riaknostic +pkg_riaknostic_description = A diagnostic tool for Riak installations, to find common errors asap +pkg_riaknostic_homepage = https://github.com/basho/riaknostic +pkg_riaknostic_fetch = git +pkg_riaknostic_repo = https://github.com/basho/riaknostic +pkg_riaknostic_commit = master + +PACKAGES += riakpool +pkg_riakpool_name = riakpool +pkg_riakpool_description = erlang riak client pool +pkg_riakpool_homepage = https://github.com/dweldon/riakpool +pkg_riakpool_fetch = git +pkg_riakpool_repo = https://github.com/dweldon/riakpool +pkg_riakpool_commit = master + +PACKAGES += rivus_cep +pkg_rivus_cep_name = rivus_cep +pkg_rivus_cep_description = Complex event processing in Erlang +pkg_rivus_cep_homepage = https://github.com/vascokk/rivus_cep +pkg_rivus_cep_fetch = git +pkg_rivus_cep_repo = https://github.com/vascokk/rivus_cep +pkg_rivus_cep_commit = master + +PACKAGES += rlimit +pkg_rlimit_name = rlimit +pkg_rlimit_description = Magnus Klaar's rate limiter code from etorrent +pkg_rlimit_homepage = https://github.com/jlouis/rlimit +pkg_rlimit_fetch = git +pkg_rlimit_repo = https://github.com/jlouis/rlimit +pkg_rlimit_commit = master + +PACKAGES += rust_mk +pkg_rust_mk_name = rust_mk +pkg_rust_mk_description = Build Rust crates in an Erlang application +pkg_rust_mk_homepage = https://github.com/goertzenator/rust.mk +pkg_rust_mk_fetch = git +pkg_rust_mk_repo = https://github.com/goertzenator/rust.mk +pkg_rust_mk_commit = master + +PACKAGES += safetyvalve +pkg_safetyvalve_name = safetyvalve +pkg_safetyvalve_description = A safety valve for your erlang node +pkg_safetyvalve_homepage = https://github.com/jlouis/safetyvalve +pkg_safetyvalve_fetch = git +pkg_safetyvalve_repo = https://github.com/jlouis/safetyvalve +pkg_safetyvalve_commit = master + +PACKAGES += seestar +pkg_seestar_name = seestar +pkg_seestar_description = The Erlang client for Cassandra 1.2+ binary protocol +pkg_seestar_homepage = https://github.com/iamaleksey/seestar +pkg_seestar_fetch = git +pkg_seestar_repo = https://github.com/iamaleksey/seestar +pkg_seestar_commit = master + +PACKAGES += service +pkg_service_name = service +pkg_service_description = A minimal Erlang behavior for creating CloudI internal services +pkg_service_homepage = http://cloudi.org/ +pkg_service_fetch = git +pkg_service_repo = https://github.com/CloudI/service +pkg_service_commit = master + +PACKAGES += setup +pkg_setup_name = setup +pkg_setup_description = Generic setup utility for Erlang-based systems +pkg_setup_homepage = https://github.com/uwiger/setup +pkg_setup_fetch = git +pkg_setup_repo = https://github.com/uwiger/setup +pkg_setup_commit = master + +PACKAGES += sext +pkg_sext_name = sext +pkg_sext_description = Sortable Erlang Term Serialization +pkg_sext_homepage = https://github.com/uwiger/sext +pkg_sext_fetch = git +pkg_sext_repo = https://github.com/uwiger/sext +pkg_sext_commit = master + +PACKAGES += sfmt +pkg_sfmt_name = sfmt +pkg_sfmt_description = SFMT pseudo random number generator for Erlang. +pkg_sfmt_homepage = https://github.com/jj1bdx/sfmt-erlang +pkg_sfmt_fetch = git +pkg_sfmt_repo = https://github.com/jj1bdx/sfmt-erlang +pkg_sfmt_commit = master + +PACKAGES += sgte +pkg_sgte_name = sgte +pkg_sgte_description = A simple Erlang Template Engine +pkg_sgte_homepage = https://github.com/filippo/sgte +pkg_sgte_fetch = git +pkg_sgte_repo = https://github.com/filippo/sgte +pkg_sgte_commit = master + +PACKAGES += sheriff +pkg_sheriff_name = sheriff +pkg_sheriff_description = Parse transform for type based validation. +pkg_sheriff_homepage = http://ninenines.eu +pkg_sheriff_fetch = git +pkg_sheriff_repo = https://github.com/extend/sheriff +pkg_sheriff_commit = master + +PACKAGES += shotgun +pkg_shotgun_name = shotgun +pkg_shotgun_description = better than just a gun +pkg_shotgun_homepage = https://github.com/inaka/shotgun +pkg_shotgun_fetch = git +pkg_shotgun_repo = https://github.com/inaka/shotgun +pkg_shotgun_commit = master + +PACKAGES += sidejob +pkg_sidejob_name = sidejob +pkg_sidejob_description = Parallel worker and capacity limiting library for Erlang +pkg_sidejob_homepage = https://github.com/basho/sidejob +pkg_sidejob_fetch = git +pkg_sidejob_repo = https://github.com/basho/sidejob +pkg_sidejob_commit = master + +PACKAGES += sieve +pkg_sieve_name = sieve +pkg_sieve_description = sieve is a simple TCP routing proxy (layer 7) in erlang +pkg_sieve_homepage = https://github.com/benoitc/sieve +pkg_sieve_fetch = git +pkg_sieve_repo = https://github.com/benoitc/sieve +pkg_sieve_commit = master + +PACKAGES += sighandler +pkg_sighandler_name = sighandler +pkg_sighandler_description = Handle UNIX signals in Er lang +pkg_sighandler_homepage = https://github.com/jkingsbery/sighandler +pkg_sighandler_fetch = git +pkg_sighandler_repo = https://github.com/jkingsbery/sighandler +pkg_sighandler_commit = master + +PACKAGES += simhash +pkg_simhash_name = simhash +pkg_simhash_description = Simhashing for Erlang -- hashing algorithm to find near-duplicates in binary data. +pkg_simhash_homepage = https://github.com/ferd/simhash +pkg_simhash_fetch = git +pkg_simhash_repo = https://github.com/ferd/simhash +pkg_simhash_commit = master + +PACKAGES += simple_bridge +pkg_simple_bridge_name = simple_bridge +pkg_simple_bridge_description = A simple, standardized interface library to Erlang HTTP Servers. +pkg_simple_bridge_homepage = https://github.com/nitrogen/simple_bridge +pkg_simple_bridge_fetch = git +pkg_simple_bridge_repo = https://github.com/nitrogen/simple_bridge +pkg_simple_bridge_commit = master + +PACKAGES += simple_oauth2 +pkg_simple_oauth2_name = simple_oauth2 +pkg_simple_oauth2_description = Simple erlang OAuth2 client module for any http server framework (Google, Facebook, Yandex, Vkontakte are preconfigured) +pkg_simple_oauth2_homepage = https://github.com/virtan/simple_oauth2 +pkg_simple_oauth2_fetch = git +pkg_simple_oauth2_repo = https://github.com/virtan/simple_oauth2 +pkg_simple_oauth2_commit = master + +PACKAGES += skel +pkg_skel_name = skel +pkg_skel_description = A Streaming Process-based Skeleton Library for Erlang +pkg_skel_homepage = https://github.com/ParaPhrase/skel +pkg_skel_fetch = git +pkg_skel_repo = https://github.com/ParaPhrase/skel +pkg_skel_commit = master + +PACKAGES += slack +pkg_slack_name = slack +pkg_slack_description = Minimal slack notification OTP library. +pkg_slack_homepage = https://github.com/DonBranson/slack +pkg_slack_fetch = git +pkg_slack_repo = https://github.com/DonBranson/slack.git +pkg_slack_commit = master + +PACKAGES += smother +pkg_smother_name = smother +pkg_smother_description = Extended code coverage metrics for Erlang. +pkg_smother_homepage = https://ramsay-t.github.io/Smother/ +pkg_smother_fetch = git +pkg_smother_repo = https://github.com/ramsay-t/Smother +pkg_smother_commit = master + +PACKAGES += snappyer +pkg_snappyer_name = snappyer +pkg_snappyer_description = Snappy as nif for Erlang +pkg_snappyer_homepage = https://github.com/zmstone/snappyer +pkg_snappyer_fetch = git +pkg_snappyer_repo = https://github.com/zmstone/snappyer.git +pkg_snappyer_commit = master + +PACKAGES += social +pkg_social_name = social +pkg_social_description = Cowboy handler for social login via OAuth2 providers +pkg_social_homepage = https://github.com/dvv/social +pkg_social_fetch = git +pkg_social_repo = https://github.com/dvv/social +pkg_social_commit = master + +PACKAGES += spapi_router +pkg_spapi_router_name = spapi_router +pkg_spapi_router_description = Partially-connected Erlang clustering +pkg_spapi_router_homepage = https://github.com/spilgames/spapi-router +pkg_spapi_router_fetch = git +pkg_spapi_router_repo = https://github.com/spilgames/spapi-router +pkg_spapi_router_commit = master + +PACKAGES += sqerl +pkg_sqerl_name = sqerl +pkg_sqerl_description = An Erlang-flavoured SQL DSL +pkg_sqerl_homepage = https://github.com/hairyhum/sqerl +pkg_sqerl_fetch = git +pkg_sqerl_repo = https://github.com/hairyhum/sqerl +pkg_sqerl_commit = master + +PACKAGES += srly +pkg_srly_name = srly +pkg_srly_description = Native Erlang Unix serial interface +pkg_srly_homepage = https://github.com/msantos/srly +pkg_srly_fetch = git +pkg_srly_repo = https://github.com/msantos/srly +pkg_srly_commit = master + +PACKAGES += sshrpc +pkg_sshrpc_name = sshrpc +pkg_sshrpc_description = Erlang SSH RPC module (experimental) +pkg_sshrpc_homepage = https://github.com/jj1bdx/sshrpc +pkg_sshrpc_fetch = git +pkg_sshrpc_repo = https://github.com/jj1bdx/sshrpc +pkg_sshrpc_commit = master + +PACKAGES += stable +pkg_stable_name = stable +pkg_stable_description = Library of assorted helpers for Cowboy web server. +pkg_stable_homepage = https://github.com/dvv/stable +pkg_stable_fetch = git +pkg_stable_repo = https://github.com/dvv/stable +pkg_stable_commit = master + +PACKAGES += statebox +pkg_statebox_name = statebox +pkg_statebox_description = Erlang state monad with merge/conflict-resolution capabilities. Useful for Riak. +pkg_statebox_homepage = https://github.com/mochi/statebox +pkg_statebox_fetch = git +pkg_statebox_repo = https://github.com/mochi/statebox +pkg_statebox_commit = master + +PACKAGES += statebox_riak +pkg_statebox_riak_name = statebox_riak +pkg_statebox_riak_description = Convenience library that makes it easier to use statebox with riak, extracted from best practices in our production code at Mochi Media. +pkg_statebox_riak_homepage = https://github.com/mochi/statebox_riak +pkg_statebox_riak_fetch = git +pkg_statebox_riak_repo = https://github.com/mochi/statebox_riak +pkg_statebox_riak_commit = master + +PACKAGES += statman +pkg_statman_name = statman +pkg_statman_description = Efficiently collect massive volumes of metrics inside the Erlang VM +pkg_statman_homepage = https://github.com/knutin/statman +pkg_statman_fetch = git +pkg_statman_repo = https://github.com/knutin/statman +pkg_statman_commit = master + +PACKAGES += statsderl +pkg_statsderl_name = statsderl +pkg_statsderl_description = StatsD client (erlang) +pkg_statsderl_homepage = https://github.com/lpgauth/statsderl +pkg_statsderl_fetch = git +pkg_statsderl_repo = https://github.com/lpgauth/statsderl +pkg_statsderl_commit = master + +PACKAGES += stdinout_pool +pkg_stdinout_pool_name = stdinout_pool +pkg_stdinout_pool_description = stdinout_pool : stuff goes in, stuff goes out. there's never any miscommunication. +pkg_stdinout_pool_homepage = https://github.com/mattsta/erlang-stdinout-pool +pkg_stdinout_pool_fetch = git +pkg_stdinout_pool_repo = https://github.com/mattsta/erlang-stdinout-pool +pkg_stdinout_pool_commit = master + +PACKAGES += stockdb +pkg_stockdb_name = stockdb +pkg_stockdb_description = Database for storing Stock Exchange quotes in erlang +pkg_stockdb_homepage = https://github.com/maxlapshin/stockdb +pkg_stockdb_fetch = git +pkg_stockdb_repo = https://github.com/maxlapshin/stockdb +pkg_stockdb_commit = master + +PACKAGES += stripe +pkg_stripe_name = stripe +pkg_stripe_description = Erlang interface to the stripe.com API +pkg_stripe_homepage = https://github.com/mattsta/stripe-erlang +pkg_stripe_fetch = git +pkg_stripe_repo = https://github.com/mattsta/stripe-erlang +pkg_stripe_commit = v1 + +PACKAGES += supervisor3 +pkg_supervisor3_name = supervisor3 +pkg_supervisor3_description = OTP supervisor with additional strategies +pkg_supervisor3_homepage = https://github.com/klarna/supervisor3 +pkg_supervisor3_fetch = git +pkg_supervisor3_repo = https://github.com/klarna/supervisor3.git +pkg_supervisor3_commit = master + +PACKAGES += surrogate +pkg_surrogate_name = surrogate +pkg_surrogate_description = Proxy server written in erlang. Supports reverse proxy load balancing and forward proxy with http (including CONNECT), socks4, socks5, and transparent proxy modes. +pkg_surrogate_homepage = https://github.com/skruger/Surrogate +pkg_surrogate_fetch = git +pkg_surrogate_repo = https://github.com/skruger/Surrogate +pkg_surrogate_commit = master + +PACKAGES += swab +pkg_swab_name = swab +pkg_swab_description = General purpose buffer handling module +pkg_swab_homepage = https://github.com/crownedgrouse/swab +pkg_swab_fetch = git +pkg_swab_repo = https://github.com/crownedgrouse/swab +pkg_swab_commit = master + +PACKAGES += swarm +pkg_swarm_name = swarm +pkg_swarm_description = Fast and simple acceptor pool for Erlang +pkg_swarm_homepage = https://github.com/jeremey/swarm +pkg_swarm_fetch = git +pkg_swarm_repo = https://github.com/jeremey/swarm +pkg_swarm_commit = master + +PACKAGES += switchboard +pkg_switchboard_name = switchboard +pkg_switchboard_description = A framework for processing email using worker plugins. +pkg_switchboard_homepage = https://github.com/thusfresh/switchboard +pkg_switchboard_fetch = git +pkg_switchboard_repo = https://github.com/thusfresh/switchboard +pkg_switchboard_commit = master + +PACKAGES += syn +pkg_syn_name = syn +pkg_syn_description = A global Process Registry and Process Group manager for Erlang. +pkg_syn_homepage = https://github.com/ostinelli/syn +pkg_syn_fetch = git +pkg_syn_repo = https://github.com/ostinelli/syn +pkg_syn_commit = master + +PACKAGES += sync +pkg_sync_name = sync +pkg_sync_description = On-the-fly recompiling and reloading in Erlang. +pkg_sync_homepage = https://github.com/rustyio/sync +pkg_sync_fetch = git +pkg_sync_repo = https://github.com/rustyio/sync +pkg_sync_commit = master + +PACKAGES += syntaxerl +pkg_syntaxerl_name = syntaxerl +pkg_syntaxerl_description = Syntax checker for Erlang +pkg_syntaxerl_homepage = https://github.com/ten0s/syntaxerl +pkg_syntaxerl_fetch = git +pkg_syntaxerl_repo = https://github.com/ten0s/syntaxerl +pkg_syntaxerl_commit = master + +PACKAGES += syslog +pkg_syslog_name = syslog +pkg_syslog_description = Erlang port driver for interacting with syslog via syslog(3) +pkg_syslog_homepage = https://github.com/Vagabond/erlang-syslog +pkg_syslog_fetch = git +pkg_syslog_repo = https://github.com/Vagabond/erlang-syslog +pkg_syslog_commit = master + +PACKAGES += taskforce +pkg_taskforce_name = taskforce +pkg_taskforce_description = Erlang worker pools for controlled parallelisation of arbitrary tasks. +pkg_taskforce_homepage = https://github.com/g-andrade/taskforce +pkg_taskforce_fetch = git +pkg_taskforce_repo = https://github.com/g-andrade/taskforce +pkg_taskforce_commit = master + +PACKAGES += tddreloader +pkg_tddreloader_name = tddreloader +pkg_tddreloader_description = Shell utility for recompiling, reloading, and testing code as it changes +pkg_tddreloader_homepage = https://github.com/version2beta/tddreloader +pkg_tddreloader_fetch = git +pkg_tddreloader_repo = https://github.com/version2beta/tddreloader +pkg_tddreloader_commit = master + +PACKAGES += tempo +pkg_tempo_name = tempo +pkg_tempo_description = NIF-based date and time parsing and formatting for Erlang. +pkg_tempo_homepage = https://github.com/selectel/tempo +pkg_tempo_fetch = git +pkg_tempo_repo = https://github.com/selectel/tempo +pkg_tempo_commit = master + +PACKAGES += ticktick +pkg_ticktick_name = ticktick +pkg_ticktick_description = Ticktick is an id generator for message service. +pkg_ticktick_homepage = https://github.com/ericliang/ticktick +pkg_ticktick_fetch = git +pkg_ticktick_repo = https://github.com/ericliang/ticktick +pkg_ticktick_commit = master + +PACKAGES += tinymq +pkg_tinymq_name = tinymq +pkg_tinymq_description = TinyMQ - a diminutive, in-memory message queue +pkg_tinymq_homepage = https://github.com/ChicagoBoss/tinymq +pkg_tinymq_fetch = git +pkg_tinymq_repo = https://github.com/ChicagoBoss/tinymq +pkg_tinymq_commit = master + +PACKAGES += tinymt +pkg_tinymt_name = tinymt +pkg_tinymt_description = TinyMT pseudo random number generator for Erlang. +pkg_tinymt_homepage = https://github.com/jj1bdx/tinymt-erlang +pkg_tinymt_fetch = git +pkg_tinymt_repo = https://github.com/jj1bdx/tinymt-erlang +pkg_tinymt_commit = master + +PACKAGES += tirerl +pkg_tirerl_name = tirerl +pkg_tirerl_description = Erlang interface to Elastic Search +pkg_tirerl_homepage = https://github.com/inaka/tirerl +pkg_tirerl_fetch = git +pkg_tirerl_repo = https://github.com/inaka/tirerl +pkg_tirerl_commit = master + +PACKAGES += traffic_tools +pkg_traffic_tools_name = traffic_tools +pkg_traffic_tools_description = Simple traffic limiting library +pkg_traffic_tools_homepage = https://github.com/systra/traffic_tools +pkg_traffic_tools_fetch = git +pkg_traffic_tools_repo = https://github.com/systra/traffic_tools +pkg_traffic_tools_commit = master + +PACKAGES += trails +pkg_trails_name = trails +pkg_trails_description = A couple of improvements over Cowboy Routes +pkg_trails_homepage = http://inaka.github.io/cowboy-trails/ +pkg_trails_fetch = git +pkg_trails_repo = https://github.com/inaka/cowboy-trails +pkg_trails_commit = master + +PACKAGES += trane +pkg_trane_name = trane +pkg_trane_description = SAX style broken HTML parser in Erlang +pkg_trane_homepage = https://github.com/massemanet/trane +pkg_trane_fetch = git +pkg_trane_repo = https://github.com/massemanet/trane +pkg_trane_commit = master + +PACKAGES += transit +pkg_transit_name = transit +pkg_transit_description = transit format for erlang +pkg_transit_homepage = https://github.com/isaiah/transit-erlang +pkg_transit_fetch = git +pkg_transit_repo = https://github.com/isaiah/transit-erlang +pkg_transit_commit = master + +PACKAGES += trie +pkg_trie_name = trie +pkg_trie_description = Erlang Trie Implementation +pkg_trie_homepage = https://github.com/okeuday/trie +pkg_trie_fetch = git +pkg_trie_repo = https://github.com/okeuday/trie +pkg_trie_commit = master + +PACKAGES += triq +pkg_triq_name = triq +pkg_triq_description = Trifork QuickCheck +pkg_triq_homepage = https://github.com/krestenkrab/triq +pkg_triq_fetch = git +pkg_triq_repo = https://github.com/krestenkrab/triq +pkg_triq_commit = master + +PACKAGES += tunctl +pkg_tunctl_name = tunctl +pkg_tunctl_description = Erlang TUN/TAP interface +pkg_tunctl_homepage = https://github.com/msantos/tunctl +pkg_tunctl_fetch = git +pkg_tunctl_repo = https://github.com/msantos/tunctl +pkg_tunctl_commit = master + +PACKAGES += twerl +pkg_twerl_name = twerl +pkg_twerl_description = Erlang client for the Twitter Streaming API +pkg_twerl_homepage = https://github.com/lucaspiller/twerl +pkg_twerl_fetch = git +pkg_twerl_repo = https://github.com/lucaspiller/twerl +pkg_twerl_commit = oauth + +PACKAGES += twitter_erlang +pkg_twitter_erlang_name = twitter_erlang +pkg_twitter_erlang_description = An Erlang twitter client +pkg_twitter_erlang_homepage = https://github.com/ngerakines/erlang_twitter +pkg_twitter_erlang_fetch = git +pkg_twitter_erlang_repo = https://github.com/ngerakines/erlang_twitter +pkg_twitter_erlang_commit = master + +PACKAGES += ucol_nif +pkg_ucol_nif_name = ucol_nif +pkg_ucol_nif_description = ICU based collation Erlang module +pkg_ucol_nif_homepage = https://github.com/refuge/ucol_nif +pkg_ucol_nif_fetch = git +pkg_ucol_nif_repo = https://github.com/refuge/ucol_nif +pkg_ucol_nif_commit = master + +PACKAGES += unicorn +pkg_unicorn_name = unicorn +pkg_unicorn_description = Generic configuration server +pkg_unicorn_homepage = https://github.com/shizzard/unicorn +pkg_unicorn_fetch = git +pkg_unicorn_repo = https://github.com/shizzard/unicorn +pkg_unicorn_commit = master + +PACKAGES += unsplit +pkg_unsplit_name = unsplit +pkg_unsplit_description = Resolves conflicts in Mnesia after network splits +pkg_unsplit_homepage = https://github.com/uwiger/unsplit +pkg_unsplit_fetch = git +pkg_unsplit_repo = https://github.com/uwiger/unsplit +pkg_unsplit_commit = master + +PACKAGES += uuid +pkg_uuid_name = uuid +pkg_uuid_description = Erlang UUID Implementation +pkg_uuid_homepage = https://github.com/okeuday/uuid +pkg_uuid_fetch = git +pkg_uuid_repo = https://github.com/okeuday/uuid +pkg_uuid_commit = master + +PACKAGES += ux +pkg_ux_name = ux +pkg_ux_description = Unicode eXtention for Erlang (Strings, Collation) +pkg_ux_homepage = https://github.com/erlang-unicode/ux +pkg_ux_fetch = git +pkg_ux_repo = https://github.com/erlang-unicode/ux +pkg_ux_commit = master + +PACKAGES += vert +pkg_vert_name = vert +pkg_vert_description = Erlang binding to libvirt virtualization API +pkg_vert_homepage = https://github.com/msantos/erlang-libvirt +pkg_vert_fetch = git +pkg_vert_repo = https://github.com/msantos/erlang-libvirt +pkg_vert_commit = master + +PACKAGES += verx +pkg_verx_name = verx +pkg_verx_description = Erlang implementation of the libvirtd remote protocol +pkg_verx_homepage = https://github.com/msantos/verx +pkg_verx_fetch = git +pkg_verx_repo = https://github.com/msantos/verx +pkg_verx_commit = master + +PACKAGES += vmq_acl +pkg_vmq_acl_name = vmq_acl +pkg_vmq_acl_description = Component of VerneMQ: A distributed MQTT message broker +pkg_vmq_acl_homepage = https://verne.mq/ +pkg_vmq_acl_fetch = git +pkg_vmq_acl_repo = https://github.com/erlio/vmq_acl +pkg_vmq_acl_commit = master + +PACKAGES += vmq_bridge +pkg_vmq_bridge_name = vmq_bridge +pkg_vmq_bridge_description = Component of VerneMQ: A distributed MQTT message broker +pkg_vmq_bridge_homepage = https://verne.mq/ +pkg_vmq_bridge_fetch = git +pkg_vmq_bridge_repo = https://github.com/erlio/vmq_bridge +pkg_vmq_bridge_commit = master + +PACKAGES += vmq_graphite +pkg_vmq_graphite_name = vmq_graphite +pkg_vmq_graphite_description = Component of VerneMQ: A distributed MQTT message broker +pkg_vmq_graphite_homepage = https://verne.mq/ +pkg_vmq_graphite_fetch = git +pkg_vmq_graphite_repo = https://github.com/erlio/vmq_graphite +pkg_vmq_graphite_commit = master + +PACKAGES += vmq_passwd +pkg_vmq_passwd_name = vmq_passwd +pkg_vmq_passwd_description = Component of VerneMQ: A distributed MQTT message broker +pkg_vmq_passwd_homepage = https://verne.mq/ +pkg_vmq_passwd_fetch = git +pkg_vmq_passwd_repo = https://github.com/erlio/vmq_passwd +pkg_vmq_passwd_commit = master + +PACKAGES += vmq_server +pkg_vmq_server_name = vmq_server +pkg_vmq_server_description = Component of VerneMQ: A distributed MQTT message broker +pkg_vmq_server_homepage = https://verne.mq/ +pkg_vmq_server_fetch = git +pkg_vmq_server_repo = https://github.com/erlio/vmq_server +pkg_vmq_server_commit = master + +PACKAGES += vmq_snmp +pkg_vmq_snmp_name = vmq_snmp +pkg_vmq_snmp_description = Component of VerneMQ: A distributed MQTT message broker +pkg_vmq_snmp_homepage = https://verne.mq/ +pkg_vmq_snmp_fetch = git +pkg_vmq_snmp_repo = https://github.com/erlio/vmq_snmp +pkg_vmq_snmp_commit = master + +PACKAGES += vmq_systree +pkg_vmq_systree_name = vmq_systree +pkg_vmq_systree_description = Component of VerneMQ: A distributed MQTT message broker +pkg_vmq_systree_homepage = https://verne.mq/ +pkg_vmq_systree_fetch = git +pkg_vmq_systree_repo = https://github.com/erlio/vmq_systree +pkg_vmq_systree_commit = master + +PACKAGES += vmstats +pkg_vmstats_name = vmstats +pkg_vmstats_description = tiny Erlang app that works in conjunction with statsderl in order to generate information on the Erlang VM for graphite logs. +pkg_vmstats_homepage = https://github.com/ferd/vmstats +pkg_vmstats_fetch = git +pkg_vmstats_repo = https://github.com/ferd/vmstats +pkg_vmstats_commit = master + +PACKAGES += walrus +pkg_walrus_name = walrus +pkg_walrus_description = Walrus - Mustache-like Templating +pkg_walrus_homepage = https://github.com/devinus/walrus +pkg_walrus_fetch = git +pkg_walrus_repo = https://github.com/devinus/walrus +pkg_walrus_commit = master + +PACKAGES += webmachine +pkg_webmachine_name = webmachine +pkg_webmachine_description = A REST-based system for building web applications. +pkg_webmachine_homepage = https://github.com/basho/webmachine +pkg_webmachine_fetch = git +pkg_webmachine_repo = https://github.com/basho/webmachine +pkg_webmachine_commit = master + +PACKAGES += websocket_client +pkg_websocket_client_name = websocket_client +pkg_websocket_client_description = Erlang websocket client (ws and wss supported) +pkg_websocket_client_homepage = https://github.com/jeremyong/websocket_client +pkg_websocket_client_fetch = git +pkg_websocket_client_repo = https://github.com/jeremyong/websocket_client +pkg_websocket_client_commit = master + +PACKAGES += worker_pool +pkg_worker_pool_name = worker_pool +pkg_worker_pool_description = a simple erlang worker pool +pkg_worker_pool_homepage = https://github.com/inaka/worker_pool +pkg_worker_pool_fetch = git +pkg_worker_pool_repo = https://github.com/inaka/worker_pool +pkg_worker_pool_commit = master + +PACKAGES += wrangler +pkg_wrangler_name = wrangler +pkg_wrangler_description = Import of the Wrangler svn repository. +pkg_wrangler_homepage = http://www.cs.kent.ac.uk/projects/wrangler/Home.html +pkg_wrangler_fetch = git +pkg_wrangler_repo = https://github.com/RefactoringTools/wrangler +pkg_wrangler_commit = master + +PACKAGES += wsock +pkg_wsock_name = wsock +pkg_wsock_description = Erlang library to build WebSocket clients and servers +pkg_wsock_homepage = https://github.com/madtrick/wsock +pkg_wsock_fetch = git +pkg_wsock_repo = https://github.com/madtrick/wsock +pkg_wsock_commit = master + +PACKAGES += xhttpc +pkg_xhttpc_name = xhttpc +pkg_xhttpc_description = Extensible HTTP Client for Erlang +pkg_xhttpc_homepage = https://github.com/seriyps/xhttpc +pkg_xhttpc_fetch = git +pkg_xhttpc_repo = https://github.com/seriyps/xhttpc +pkg_xhttpc_commit = master + +PACKAGES += xref_runner +pkg_xref_runner_name = xref_runner +pkg_xref_runner_description = Erlang Xref Runner (inspired in rebar xref) +pkg_xref_runner_homepage = https://github.com/inaka/xref_runner +pkg_xref_runner_fetch = git +pkg_xref_runner_repo = https://github.com/inaka/xref_runner +pkg_xref_runner_commit = master + +PACKAGES += yamerl +pkg_yamerl_name = yamerl +pkg_yamerl_description = YAML 1.2 parser in pure Erlang +pkg_yamerl_homepage = https://github.com/yakaz/yamerl +pkg_yamerl_fetch = git +pkg_yamerl_repo = https://github.com/yakaz/yamerl +pkg_yamerl_commit = master + +PACKAGES += yamler +pkg_yamler_name = yamler +pkg_yamler_description = libyaml-based yaml loader for Erlang +pkg_yamler_homepage = https://github.com/goertzenator/yamler +pkg_yamler_fetch = git +pkg_yamler_repo = https://github.com/goertzenator/yamler +pkg_yamler_commit = master + +PACKAGES += yaws +pkg_yaws_name = yaws +pkg_yaws_description = Yaws webserver +pkg_yaws_homepage = http://yaws.hyber.org +pkg_yaws_fetch = git +pkg_yaws_repo = https://github.com/klacke/yaws +pkg_yaws_commit = master + +PACKAGES += zab_engine +pkg_zab_engine_name = zab_engine +pkg_zab_engine_description = zab propotocol implement by erlang +pkg_zab_engine_homepage = https://github.com/xinmingyao/zab_engine +pkg_zab_engine_fetch = git +pkg_zab_engine_repo = https://github.com/xinmingyao/zab_engine +pkg_zab_engine_commit = master + +PACKAGES += zabbix_sender +pkg_zabbix_sender_name = zabbix_sender +pkg_zabbix_sender_description = Zabbix trapper for sending data to Zabbix in pure Erlang +pkg_zabbix_sender_homepage = https://github.com/stalkermn/zabbix_sender +pkg_zabbix_sender_fetch = git +pkg_zabbix_sender_repo = https://github.com/stalkermn/zabbix_sender.git +pkg_zabbix_sender_commit = master + +PACKAGES += zeta +pkg_zeta_name = zeta +pkg_zeta_description = HTTP access log parser in Erlang +pkg_zeta_homepage = https://github.com/s1n4/zeta +pkg_zeta_fetch = git +pkg_zeta_repo = https://github.com/s1n4/zeta +pkg_zeta_commit = master + +PACKAGES += zippers +pkg_zippers_name = zippers +pkg_zippers_description = A library for functional zipper data structures in Erlang. Read more on zippers +pkg_zippers_homepage = https://github.com/ferd/zippers +pkg_zippers_fetch = git +pkg_zippers_repo = https://github.com/ferd/zippers +pkg_zippers_commit = master + +PACKAGES += zlists +pkg_zlists_name = zlists +pkg_zlists_description = Erlang lazy lists library. +pkg_zlists_homepage = https://github.com/vjache/erlang-zlists +pkg_zlists_fetch = git +pkg_zlists_repo = https://github.com/vjache/erlang-zlists +pkg_zlists_commit = master + +PACKAGES += zraft_lib +pkg_zraft_lib_name = zraft_lib +pkg_zraft_lib_description = Erlang raft consensus protocol implementation +pkg_zraft_lib_homepage = https://github.com/dreyk/zraft_lib +pkg_zraft_lib_fetch = git +pkg_zraft_lib_repo = https://github.com/dreyk/zraft_lib +pkg_zraft_lib_commit = master + +PACKAGES += zucchini +pkg_zucchini_name = zucchini +pkg_zucchini_description = An Erlang INI parser +pkg_zucchini_homepage = https://github.com/devinus/zucchini +pkg_zucchini_fetch = git +pkg_zucchini_repo = https://github.com/devinus/zucchini +pkg_zucchini_commit = master + +# Copyright (c) 2015-2016, Loïc Hoguin +# This file is part of erlang.mk and subject to the terms of the ISC License. + +.PHONY: search + +define pkg_print + $(verbose) printf "%s\n" \ + $(if $(call core_eq,$(1),$(pkg_$(1)_name)),,"Pkg name: $(1)") \ + "App name: $(pkg_$(1)_name)" \ + "Description: $(pkg_$(1)_description)" \ + "Home page: $(pkg_$(1)_homepage)" \ + "Fetch with: $(pkg_$(1)_fetch)" \ + "Repository: $(pkg_$(1)_repo)" \ + "Commit: $(pkg_$(1)_commit)" \ + "" + +endef + +search: +ifdef q + $(foreach p,$(PACKAGES), \ + $(if $(findstring $(call core_lc,$(q)),$(call core_lc,$(pkg_$(p)_name) $(pkg_$(p)_description))), \ + $(call pkg_print,$(p)))) +else + $(foreach p,$(PACKAGES),$(call pkg_print,$(p))) +endif + +# Copyright (c) 2013-2016, Loïc Hoguin +# This file is part of erlang.mk and subject to the terms of the ISC License. + +.PHONY: distclean-deps clean-tmp-deps.log + +# Configuration. + +ifdef OTP_DEPS +$(warning The variable OTP_DEPS is deprecated in favor of LOCAL_DEPS.) +endif + +IGNORE_DEPS ?= +export IGNORE_DEPS + +APPS_DIR ?= $(CURDIR)/apps +export APPS_DIR + +DEPS_DIR ?= $(CURDIR)/deps +export DEPS_DIR + +REBAR_DEPS_DIR = $(DEPS_DIR) +export REBAR_DEPS_DIR + +dep_name = $(if $(dep_$(1)),$(1),$(if $(pkg_$(1)_name),$(pkg_$(1)_name),$(1))) +dep_repo = $(patsubst git://github.com/%,https://github.com/%, \ + $(if $(dep_$(1)),$(word 2,$(dep_$(1))),$(pkg_$(1)_repo))) +dep_commit = $(if $(dep_$(1)_commit),$(dep_$(1)_commit),$(if $(dep_$(1)),$(word 3,$(dep_$(1))),$(pkg_$(1)_commit))) + +LOCAL_DEPS_DIRS = $(foreach a,$(LOCAL_DEPS),$(if $(wildcard $(APPS_DIR)/$(a)),$(APPS_DIR)/$(a))) +ALL_APPS_DIRS = $(if $(wildcard $(APPS_DIR)/),$(filter-out $(APPS_DIR),$(shell find $(APPS_DIR) -maxdepth 1 -type d))) +ALL_DEPS_DIRS = $(addprefix $(DEPS_DIR)/,$(foreach dep,$(filter-out $(IGNORE_DEPS),$(BUILD_DEPS) $(DEPS)),$(call dep_name,$(dep)))) + +ifeq ($(filter $(APPS_DIR) $(DEPS_DIR),$(subst :, ,$(ERL_LIBS))),) +ifeq ($(ERL_LIBS),) + ERL_LIBS = $(APPS_DIR):$(DEPS_DIR) +else + ERL_LIBS := $(ERL_LIBS):$(APPS_DIR):$(DEPS_DIR) +endif +endif +export ERL_LIBS + +export NO_AUTOPATCH + +# Verbosity. + +dep_verbose_0 = @echo " DEP " $(1); +dep_verbose_2 = set -x; +dep_verbose = $(dep_verbose_$(V)) + +# Core targets. + +apps:: $(ALL_APPS_DIRS) clean-tmp-deps.log +ifeq ($(IS_APP)$(IS_DEP),) + $(verbose) rm -f $(ERLANG_MK_TMP)/apps.log +endif + $(verbose) mkdir -p $(ERLANG_MK_TMP) +# Create ebin directory for all apps to make sure Erlang recognizes them +# as proper OTP applications when using -include_lib. This is a temporary +# fix, a proper fix would be to compile apps/* in the right order. +ifndef IS_APP + $(verbose) for dep in $(ALL_APPS_DIRS) ; do \ + mkdir -p $$dep/ebin || exit $$?; \ + done +endif +# at the toplevel: if LOCAL_DEPS is defined with at least one local app, only +# compile that list of apps. otherwise, compile everything. +# within an app: compile all LOCAL_DEPS that are (uncompiled) local apps + $(verbose) for dep in $(if $(LOCAL_DEPS_DIRS)$(IS_APP),$(LOCAL_DEPS_DIRS),$(ALL_APPS_DIRS)) ; do \ + if grep -qs ^$$dep$$ $(ERLANG_MK_TMP)/apps.log; then \ + :; \ + else \ + echo $$dep >> $(ERLANG_MK_TMP)/apps.log; \ + $(MAKE) -C $$dep IS_APP=1 || exit $$?; \ + fi \ + done + +clean-tmp-deps.log: +ifeq ($(IS_APP)$(IS_DEP),) + $(verbose) rm -f $(ERLANG_MK_TMP)/deps.log +endif + +ifneq ($(SKIP_DEPS),) +deps:: +else +deps:: $(ALL_DEPS_DIRS) apps clean-tmp-deps.log + $(verbose) mkdir -p $(ERLANG_MK_TMP) + $(verbose) for dep in $(ALL_DEPS_DIRS) ; do \ + if grep -qs ^$$dep$$ $(ERLANG_MK_TMP)/deps.log; then \ + :; \ + else \ + echo $$dep >> $(ERLANG_MK_TMP)/deps.log; \ + if [ -f $$dep/GNUmakefile ] || [ -f $$dep/makefile ] || [ -f $$dep/Makefile ]; then \ + $(MAKE) -C $$dep IS_DEP=1 || exit $$?; \ + else \ + echo "Error: No Makefile to build dependency $$dep."; \ + exit 2; \ + fi \ + fi \ + done +endif + +# Deps related targets. + +# @todo rename GNUmakefile and makefile into Makefile first, if they exist +# While Makefile file could be GNUmakefile or makefile, +# in practice only Makefile is needed so far. +define dep_autopatch + if [ -f $(DEPS_DIR)/$(1)/erlang.mk ]; then \ + rm -rf $(DEPS_DIR)/$1/ebin/; \ + $(call erlang,$(call dep_autopatch_appsrc.erl,$(1))); \ + $(call dep_autopatch_erlang_mk,$(1)); \ + elif [ -f $(DEPS_DIR)/$(1)/Makefile ]; then \ + if [ -f $(DEPS_DIR)/$1/rebar.lock ]; then \ + $(call dep_autopatch2,$1); \ + elif [ 0 != `grep -c "include ../\w*\.mk" $(DEPS_DIR)/$(1)/Makefile` ]; then \ + $(call dep_autopatch2,$(1)); \ + elif [ 0 != `grep -ci "^[^#].*rebar" $(DEPS_DIR)/$(1)/Makefile` ]; then \ + $(call dep_autopatch2,$(1)); \ + elif [ -n "`find $(DEPS_DIR)/$(1)/ -type f -name \*.mk -not -name erlang.mk -exec grep -i "^[^#].*rebar" '{}' \;`" ]; then \ + $(call dep_autopatch2,$(1)); \ + fi \ + else \ + if [ ! -d $(DEPS_DIR)/$(1)/src/ ]; then \ + $(call dep_autopatch_noop,$(1)); \ + else \ + $(call dep_autopatch2,$(1)); \ + fi \ + fi +endef + +define dep_autopatch2 + mv -n $(DEPS_DIR)/$1/ebin/$1.app $(DEPS_DIR)/$1/src/$1.app.src; \ + rm -f $(DEPS_DIR)/$1/ebin/$1.app; \ + if [ -f $(DEPS_DIR)/$1/src/$1.app.src.script ]; then \ + $(call erlang,$(call dep_autopatch_appsrc_script.erl,$(1))); \ + fi; \ + $(call erlang,$(call dep_autopatch_appsrc.erl,$(1))); \ + if [ -f $(DEPS_DIR)/$(1)/rebar -o -f $(DEPS_DIR)/$(1)/rebar.config -o -f $(DEPS_DIR)/$(1)/rebar.config.script -o -f $(DEPS_DIR)/$1/rebar.lock ]; then \ + $(call dep_autopatch_fetch_rebar); \ + $(call dep_autopatch_rebar,$(1)); \ + else \ + $(call dep_autopatch_gen,$(1)); \ + fi +endef + +define dep_autopatch_noop + printf "noop:\n" > $(DEPS_DIR)/$(1)/Makefile +endef + +# Overwrite erlang.mk with the current file by default. +ifeq ($(NO_AUTOPATCH_ERLANG_MK),) +define dep_autopatch_erlang_mk + echo "include $(call core_relpath,$(dir $(ERLANG_MK_FILENAME)),$(DEPS_DIR)/app)/erlang.mk" \ + > $(DEPS_DIR)/$1/erlang.mk +endef +else +define dep_autopatch_erlang_mk + : +endef +endif + +define dep_autopatch_gen + printf "%s\n" \ + "ERLC_OPTS = +debug_info" \ + "include ../../erlang.mk" > $(DEPS_DIR)/$(1)/Makefile +endef + +define dep_autopatch_fetch_rebar + mkdir -p $(ERLANG_MK_TMP); \ + if [ ! -d $(ERLANG_MK_TMP)/rebar ]; then \ + git clone -q -n -- https://github.com/rebar/rebar $(ERLANG_MK_TMP)/rebar; \ + cd $(ERLANG_MK_TMP)/rebar; \ + git checkout -q 576e12171ab8d69b048b827b92aa65d067deea01; \ + $(MAKE); \ + cd -; \ + fi +endef + +define dep_autopatch_rebar + if [ -f $(DEPS_DIR)/$(1)/Makefile ]; then \ + mv $(DEPS_DIR)/$(1)/Makefile $(DEPS_DIR)/$(1)/Makefile.orig.mk; \ + fi; \ + $(call erlang,$(call dep_autopatch_rebar.erl,$(1))); \ + rm -f $(DEPS_DIR)/$(1)/ebin/$(1).app +endef + +define dep_autopatch_rebar.erl + application:load(rebar), + application:set_env(rebar, log_level, debug), + rmemo:start(), + Conf1 = case file:consult("$(call core_native_path,$(DEPS_DIR)/$1/rebar.config)") of + {ok, Conf0} -> Conf0; + _ -> [] + end, + {Conf, OsEnv} = fun() -> + case filelib:is_file("$(call core_native_path,$(DEPS_DIR)/$1/rebar.config.script)") of + false -> {Conf1, []}; + true -> + Bindings0 = erl_eval:new_bindings(), + Bindings1 = erl_eval:add_binding('CONFIG', Conf1, Bindings0), + Bindings = erl_eval:add_binding('SCRIPT', "$(call core_native_path,$(DEPS_DIR)/$1/rebar.config.script)", Bindings1), + Before = os:getenv(), + {ok, Conf2} = file:script("$(call core_native_path,$(DEPS_DIR)/$1/rebar.config.script)", Bindings), + {Conf2, lists:foldl(fun(E, Acc) -> lists:delete(E, Acc) end, os:getenv(), Before)} + end + end(), + Write = fun (Text) -> + file:write_file("$(call core_native_path,$(DEPS_DIR)/$1/Makefile)", Text, [append]) + end, + Escape = fun (Text) -> + re:replace(Text, "\\\\$$", "\$$$$", [global, {return, list}]) + end, + Write("IGNORE_DEPS += edown eper eunit_formatters meck node_package " + "rebar_lock_deps_plugin rebar_vsn_plugin reltool_util\n"), + Write("C_SRC_DIR = /path/do/not/exist\n"), + Write("C_SRC_TYPE = rebar\n"), + Write("DRV_CFLAGS = -fPIC\nexport DRV_CFLAGS\n"), + Write(["ERLANG_ARCH = ", rebar_utils:wordsize(), "\nexport ERLANG_ARCH\n"]), + fun() -> + Write("ERLC_OPTS = +debug_info\nexport ERLC_OPTS\n"), + case lists:keyfind(erl_opts, 1, Conf) of + false -> ok; + {_, ErlOpts} -> + lists:foreach(fun + ({d, D}) -> + Write("ERLC_OPTS += -D" ++ atom_to_list(D) ++ "=1\n"); + ({i, I}) -> + Write(["ERLC_OPTS += -I ", I, "\n"]); + ({platform_define, Regex, D}) -> + case rebar_utils:is_arch(Regex) of + true -> Write("ERLC_OPTS += -D" ++ atom_to_list(D) ++ "=1\n"); + false -> ok + end; + ({parse_transform, PT}) -> + Write("ERLC_OPTS += +'{parse_transform, " ++ atom_to_list(PT) ++ "}'\n"); + (_) -> ok + end, ErlOpts) + end, + Write("\n") + end(), + fun() -> + File = case lists:keyfind(deps, 1, Conf) of + false -> []; + {_, Deps} -> + [begin case case Dep of + {N, S} when is_atom(N), is_list(S) -> {N, {hex, S}}; + {N, S} when is_tuple(S) -> {N, S}; + {N, _, S} -> {N, S}; + {N, _, S, _} -> {N, S}; + _ -> false + end of + false -> ok; + {Name, Source} -> + {Method, Repo, Commit} = case Source of + {hex, V} -> {hex, V, undefined}; + {git, R} -> {git, R, master}; + {M, R, {branch, C}} -> {M, R, C}; + {M, R, {ref, C}} -> {M, R, C}; + {M, R, {tag, C}} -> {M, R, C}; + {M, R, C} -> {M, R, C} + end, + Write(io_lib:format("DEPS += ~s\ndep_~s = ~s ~s ~s~n", [Name, Name, Method, Repo, Commit])) + end end || Dep <- Deps] + end + end(), + fun() -> + case lists:keyfind(erl_first_files, 1, Conf) of + false -> ok; + {_, Files} -> + Names = [[" ", case lists:reverse(F) of + "lre." ++ Elif -> lists:reverse(Elif); + Elif -> lists:reverse(Elif) + end] || "src/" ++ F <- Files], + Write(io_lib:format("COMPILE_FIRST +=~s\n", [Names])) + end + end(), + Write("\n\nrebar_dep: preprocess pre-deps deps pre-app app\n"), + Write("\npreprocess::\n"), + Write("\npre-deps::\n"), + Write("\npre-app::\n"), + PatchHook = fun(Cmd) -> + case Cmd of + "make -C" ++ Cmd1 -> "$$\(MAKE) -C" ++ Escape(Cmd1); + "gmake -C" ++ Cmd1 -> "$$\(MAKE) -C" ++ Escape(Cmd1); + "make " ++ Cmd1 -> "$$\(MAKE) -f Makefile.orig.mk " ++ Escape(Cmd1); + "gmake " ++ Cmd1 -> "$$\(MAKE) -f Makefile.orig.mk " ++ Escape(Cmd1); + _ -> Escape(Cmd) + end + end, + fun() -> + case lists:keyfind(pre_hooks, 1, Conf) of + false -> ok; + {_, Hooks} -> + [case H of + {'get-deps', Cmd} -> + Write("\npre-deps::\n\t" ++ PatchHook(Cmd) ++ "\n"); + {compile, Cmd} -> + Write("\npre-app::\n\tCC=$$\(CC) " ++ PatchHook(Cmd) ++ "\n"); + {Regex, compile, Cmd} -> + case rebar_utils:is_arch(Regex) of + true -> Write("\npre-app::\n\tCC=$$\(CC) " ++ PatchHook(Cmd) ++ "\n"); + false -> ok + end; + _ -> ok + end || H <- Hooks] + end + end(), + ShellToMk = fun(V) -> + re:replace(re:replace(V, "(\\\\$$)(\\\\w*)", "\\\\1(\\\\2)", [global]), + "-Werror\\\\b", "", [{return, list}, global]) + end, + PortSpecs = fun() -> + case lists:keyfind(port_specs, 1, Conf) of + false -> + case filelib:is_dir("$(call core_native_path,$(DEPS_DIR)/$1/c_src)") of + false -> []; + true -> + [{"priv/" ++ proplists:get_value(so_name, Conf, "$(1)_drv.so"), + proplists:get_value(port_sources, Conf, ["c_src/*.c"]), []}] + end; + {_, Specs} -> + lists:flatten([case S of + {Output, Input} -> {ShellToMk(Output), Input, []}; + {Regex, Output, Input} -> + case rebar_utils:is_arch(Regex) of + true -> {ShellToMk(Output), Input, []}; + false -> [] + end; + {Regex, Output, Input, [{env, Env}]} -> + case rebar_utils:is_arch(Regex) of + true -> {ShellToMk(Output), Input, Env}; + false -> [] + end + end || S <- Specs]) + end + end(), + PortSpecWrite = fun (Text) -> + file:write_file("$(call core_native_path,$(DEPS_DIR)/$1/c_src/Makefile.erlang.mk)", Text, [append]) + end, + case PortSpecs of + [] -> ok; + _ -> + Write("\npre-app::\n\t$$\(MAKE) -f c_src/Makefile.erlang.mk\n"), + PortSpecWrite(io_lib:format("ERL_CFLAGS ?= -finline-functions -Wall -fPIC -I \\"~s/erts-~s/include\\" -I \\"~s\\"\n", + [code:root_dir(), erlang:system_info(version), code:lib_dir(erl_interface, include)])), + PortSpecWrite(io_lib:format("ERL_LDFLAGS ?= -L \\"~s\\" -lerl_interface -lei\n", + [code:lib_dir(erl_interface, lib)])), + [PortSpecWrite(["\n", E, "\n"]) || E <- OsEnv], + FilterEnv = fun(Env) -> + lists:flatten([case E of + {_, _} -> E; + {Regex, K, V} -> + case rebar_utils:is_arch(Regex) of + true -> {K, V}; + false -> [] + end + end || E <- Env]) + end, + MergeEnv = fun(Env) -> + lists:foldl(fun ({K, V}, Acc) -> + case lists:keyfind(K, 1, Acc) of + false -> [{K, rebar_utils:expand_env_variable(V, K, "")}|Acc]; + {_, V0} -> [{K, rebar_utils:expand_env_variable(V, K, V0)}|Acc] + end + end, [], Env) + end, + PortEnv = case lists:keyfind(port_env, 1, Conf) of + false -> []; + {_, PortEnv0} -> FilterEnv(PortEnv0) + end, + PortSpec = fun ({Output, Input0, Env}) -> + filelib:ensure_dir("$(call core_native_path,$(DEPS_DIR)/$1/)" ++ Output), + Input = [[" ", I] || I <- Input0], + PortSpecWrite([ + [["\n", K, " = ", ShellToMk(V)] || {K, V} <- lists:reverse(MergeEnv(PortEnv))], + case $(PLATFORM) of + darwin -> "\n\nLDFLAGS += -flat_namespace -undefined suppress"; + _ -> "" + end, + "\n\nall:: ", Output, "\n\n", + "%.o: %.c\n\t$$\(CC) -c -o $$\@ $$\< $$\(CFLAGS) $$\(ERL_CFLAGS) $$\(DRV_CFLAGS) $$\(EXE_CFLAGS)\n\n", + "%.o: %.C\n\t$$\(CXX) -c -o $$\@ $$\< $$\(CXXFLAGS) $$\(ERL_CFLAGS) $$\(DRV_CFLAGS) $$\(EXE_CFLAGS)\n\n", + "%.o: %.cc\n\t$$\(CXX) -c -o $$\@ $$\< $$\(CXXFLAGS) $$\(ERL_CFLAGS) $$\(DRV_CFLAGS) $$\(EXE_CFLAGS)\n\n", + "%.o: %.cpp\n\t$$\(CXX) -c -o $$\@ $$\< $$\(CXXFLAGS) $$\(ERL_CFLAGS) $$\(DRV_CFLAGS) $$\(EXE_CFLAGS)\n\n", + [[Output, ": ", K, " += ", ShellToMk(V), "\n"] || {K, V} <- lists:reverse(MergeEnv(FilterEnv(Env)))], + Output, ": $$\(foreach ext,.c .C .cc .cpp,", + "$$\(patsubst %$$\(ext),%.o,$$\(filter %$$\(ext),$$\(wildcard", Input, "))))\n", + "\t$$\(CC) -o $$\@ $$\? $$\(LDFLAGS) $$\(ERL_LDFLAGS) $$\(DRV_LDFLAGS) $$\(EXE_LDFLAGS)", + case {filename:extension(Output), $(PLATFORM)} of + {[], _} -> "\n"; + {_, darwin} -> "\n"; + _ -> " -shared\n" + end]) + end, + [PortSpec(S) || S <- PortSpecs] + end, + Write("\ninclude $(call core_relpath,$(dir $(ERLANG_MK_FILENAME)),$(DEPS_DIR)/app)/erlang.mk"), + RunPlugin = fun(Plugin, Step) -> + case erlang:function_exported(Plugin, Step, 2) of + false -> ok; + true -> + c:cd("$(call core_native_path,$(DEPS_DIR)/$1/)"), + Ret = Plugin:Step({config, "", Conf, dict:new(), dict:new(), dict:new(), + dict:store(base_dir, "", dict:new())}, undefined), + io:format("rebar plugin ~p step ~p ret ~p~n", [Plugin, Step, Ret]) + end + end, + fun() -> + case lists:keyfind(plugins, 1, Conf) of + false -> ok; + {_, Plugins} -> + [begin + case lists:keyfind(deps, 1, Conf) of + false -> ok; + {_, Deps} -> + case lists:keyfind(P, 1, Deps) of + false -> ok; + _ -> + Path = "$(call core_native_path,$(DEPS_DIR)/)" ++ atom_to_list(P), + io:format("~s", [os:cmd("$(MAKE) -C $(call core_native_path,$(DEPS_DIR)/$1) " ++ Path)]), + io:format("~s", [os:cmd("$(MAKE) -C " ++ Path ++ " IS_DEP=1")]), + code:add_patha(Path ++ "/ebin") + end + end + end || P <- Plugins], + [case code:load_file(P) of + {module, P} -> ok; + _ -> + case lists:keyfind(plugin_dir, 1, Conf) of + false -> ok; + {_, PluginsDir} -> + ErlFile = "$(call core_native_path,$(DEPS_DIR)/$1/)" ++ PluginsDir ++ "/" ++ atom_to_list(P) ++ ".erl", + {ok, P, Bin} = compile:file(ErlFile, [binary]), + {module, P} = code:load_binary(P, ErlFile, Bin) + end + end || P <- Plugins], + [RunPlugin(P, preprocess) || P <- Plugins], + [RunPlugin(P, pre_compile) || P <- Plugins], + [RunPlugin(P, compile) || P <- Plugins] + end + end(), + halt() +endef + +define dep_autopatch_appsrc_script.erl + AppSrc = "$(call core_native_path,$(DEPS_DIR)/$1/src/$1.app.src)", + AppSrcScript = AppSrc ++ ".script", + Bindings = erl_eval:new_bindings(), + {ok, Conf} = file:script(AppSrcScript, Bindings), + ok = file:write_file(AppSrc, io_lib:format("~p.~n", [Conf])), + halt() +endef + +define dep_autopatch_appsrc.erl + AppSrcOut = "$(call core_native_path,$(DEPS_DIR)/$1/src/$1.app.src)", + AppSrcIn = case filelib:is_regular(AppSrcOut) of false -> "$(call core_native_path,$(DEPS_DIR)/$1/ebin/$1.app)"; true -> AppSrcOut end, + case filelib:is_regular(AppSrcIn) of + false -> ok; + true -> + {ok, [{application, $(1), L0}]} = file:consult(AppSrcIn), + L1 = lists:keystore(modules, 1, L0, {modules, []}), + L2 = case lists:keyfind(vsn, 1, L1) of {_, git} -> lists:keyreplace(vsn, 1, L1, {vsn, "git"}); _ -> L1 end, + L3 = case lists:keyfind(registered, 1, L2) of false -> [{registered, []}|L2]; _ -> L2 end, + ok = file:write_file(AppSrcOut, io_lib:format("~p.~n", [{application, $(1), L3}])), + case AppSrcOut of AppSrcIn -> ok; _ -> ok = file:delete(AppSrcIn) end + end, + halt() +endef + +define dep_fetch_git + git clone -q -n -- $(call dep_repo,$(1)) $(DEPS_DIR)/$(call dep_name,$(1)); \ + cd $(DEPS_DIR)/$(call dep_name,$(1)) && git checkout -q $(call dep_commit,$(1)); +endef + +define dep_fetch_git-submodule + git submodule update --init -- $(DEPS_DIR)/$1; +endef + +define dep_fetch_hg + hg clone -q -U $(call dep_repo,$(1)) $(DEPS_DIR)/$(call dep_name,$(1)); \ + cd $(DEPS_DIR)/$(call dep_name,$(1)) && hg update -q $(call dep_commit,$(1)); +endef + +define dep_fetch_svn + svn checkout -q $(call dep_repo,$(1)) $(DEPS_DIR)/$(call dep_name,$(1)); +endef + +define dep_fetch_cp + cp -R $(call dep_repo,$(1)) $(DEPS_DIR)/$(call dep_name,$(1)); +endef + +# Hex only has a package version. No need to look in the Erlang.mk packages. +define dep_fetch_hex + mkdir -p $(ERLANG_MK_TMP)/hex $(DEPS_DIR)/$1; \ + $(call core_http_get,$(ERLANG_MK_TMP)/hex/$1.tar,\ + https://s3.amazonaws.com/s3.hex.pm/tarballs/$1-$(strip $(word 2,$(dep_$1))).tar); \ + tar -xOf $(ERLANG_MK_TMP)/hex/$1.tar contents.tar.gz | tar -C $(DEPS_DIR)/$1 -xzf -; +endef + +define dep_fetch_fail + echo "Error: Unknown or invalid dependency: $(1)." >&2; \ + exit 78; +endef + +# Kept for compatibility purposes with older Erlang.mk configuration. +define dep_fetch_legacy + $(warning WARNING: '$(1)' dependency configuration uses deprecated format.) \ + git clone -q -n -- $(word 1,$(dep_$(1))) $(DEPS_DIR)/$(1); \ + cd $(DEPS_DIR)/$(1) && git checkout -q $(if $(word 2,$(dep_$(1))),$(word 2,$(dep_$(1))),master); +endef + +define dep_fetch + $(if $(dep_$(1)), \ + $(if $(dep_fetch_$(word 1,$(dep_$(1)))), \ + $(word 1,$(dep_$(1))), \ + $(if $(IS_DEP),legacy,fail)), \ + $(if $(filter $(1),$(PACKAGES)), \ + $(pkg_$(1)_fetch), \ + fail)) +endef + +define dep_target +$(DEPS_DIR)/$(call dep_name,$1): + $(eval DEP_NAME := $(call dep_name,$1)) + $(eval DEP_STR := $(if $(filter-out $1,$(DEP_NAME)),$1,"$1 ($(DEP_NAME))")) + $(verbose) if test -d $(APPS_DIR)/$(DEP_NAME); then \ + echo "Error: Dependency" $(DEP_STR) "conflicts with application found in $(APPS_DIR)/$(DEP_NAME)."; \ + exit 17; \ + fi + $(verbose) mkdir -p $(DEPS_DIR) + $(dep_verbose) $(call dep_fetch_$(strip $(call dep_fetch,$(1))),$(1)) + $(verbose) if [ -f $(DEPS_DIR)/$(1)/configure.ac -o -f $(DEPS_DIR)/$(1)/configure.in ] \ + && [ ! -f $(DEPS_DIR)/$(1)/configure ]; then \ + echo " AUTO " $(1); \ + cd $(DEPS_DIR)/$(1) && autoreconf -Wall -vif -I m4; \ + fi + - $(verbose) if [ -f $(DEPS_DIR)/$(DEP_NAME)/configure ]; then \ + echo " CONF " $(DEP_STR); \ + cd $(DEPS_DIR)/$(DEP_NAME) && ./configure; \ + fi +ifeq ($(filter $(1),$(NO_AUTOPATCH)),) + $(verbose) if [ "$(1)" = "amqp_client" -a "$(RABBITMQ_CLIENT_PATCH)" ]; then \ + if [ ! -d $(DEPS_DIR)/rabbitmq-codegen ]; then \ + echo " PATCH Downloading rabbitmq-codegen"; \ + git clone https://github.com/rabbitmq/rabbitmq-codegen.git $(DEPS_DIR)/rabbitmq-codegen; \ + fi; \ + if [ ! -d $(DEPS_DIR)/rabbitmq-server ]; then \ + echo " PATCH Downloading rabbitmq-server"; \ + git clone https://github.com/rabbitmq/rabbitmq-server.git $(DEPS_DIR)/rabbitmq-server; \ + fi; \ + ln -s $(DEPS_DIR)/amqp_client/deps/rabbit_common-0.0.0 $(DEPS_DIR)/rabbit_common; \ + elif [ "$(1)" = "rabbit" -a "$(RABBITMQ_SERVER_PATCH)" ]; then \ + if [ ! -d $(DEPS_DIR)/rabbitmq-codegen ]; then \ + echo " PATCH Downloading rabbitmq-codegen"; \ + git clone https://github.com/rabbitmq/rabbitmq-codegen.git $(DEPS_DIR)/rabbitmq-codegen; \ + fi \ + else \ + $$(call dep_autopatch,$(DEP_NAME)) \ + fi +endif +endef + +$(foreach dep,$(BUILD_DEPS) $(DEPS),$(eval $(call dep_target,$(dep)))) + +ifndef IS_APP +clean:: clean-apps + +clean-apps: + $(verbose) for dep in $(ALL_APPS_DIRS) ; do \ + $(MAKE) -C $$dep clean IS_APP=1 || exit $$?; \ + done + +distclean:: distclean-apps + +distclean-apps: + $(verbose) for dep in $(ALL_APPS_DIRS) ; do \ + $(MAKE) -C $$dep distclean IS_APP=1 || exit $$?; \ + done +endif + +ifndef SKIP_DEPS +distclean:: distclean-deps + +distclean-deps: + $(gen_verbose) rm -rf $(DEPS_DIR) +endif + +# Forward-declare variables used in core/deps-tools.mk. This is required +# in case plugins use them. + +ERLANG_MK_RECURSIVE_DEPS_LIST = $(ERLANG_MK_TMP)/recursive-deps-list.log +ERLANG_MK_RECURSIVE_DOC_DEPS_LIST = $(ERLANG_MK_TMP)/recursive-doc-deps-list.log +ERLANG_MK_RECURSIVE_REL_DEPS_LIST = $(ERLANG_MK_TMP)/recursive-rel-deps-list.log +ERLANG_MK_RECURSIVE_TEST_DEPS_LIST = $(ERLANG_MK_TMP)/recursive-test-deps-list.log +ERLANG_MK_RECURSIVE_SHELL_DEPS_LIST = $(ERLANG_MK_TMP)/recursive-shell-deps-list.log + +# Copyright (c) 2015-2016, Loïc Hoguin +# This file is part of erlang.mk and subject to the terms of the ISC License. + +# Verbosity. + +proto_verbose_0 = @echo " PROTO " $(filter %.proto,$(?F)); +proto_verbose = $(proto_verbose_$(V)) + +# Core targets. + +define compile_proto + $(verbose) mkdir -p ebin/ include/ + $(proto_verbose) $(call erlang,$(call compile_proto.erl,$(1))) + $(proto_verbose) erlc +debug_info -o ebin/ ebin/*.erl + $(verbose) rm ebin/*.erl +endef + +define compile_proto.erl + [begin + Dir = filename:dirname(filename:dirname(F)), + protobuffs_compile:generate_source(F, + [{output_include_dir, Dir ++ "/include"}, + {output_src_dir, Dir ++ "/ebin"}]) + end || F <- string:tokens("$(1)", " ")], + halt(). +endef + +ifneq ($(wildcard src/),) +ebin/$(PROJECT).app:: $(sort $(call core_find,src/,*.proto)) + $(if $(strip $?),$(call compile_proto,$?)) +endif + +# Copyright (c) 2013-2016, 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_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_ASN1_OPTS ?= + +ERLC_MIB_OPTS ?= +COMPILE_MIB_FIRST ?= +COMPILE_MIB_FIRST_PATHS = $(addprefix mibs/,$(addsuffix .mib,$(COMPILE_MIB_FIRST))) + +# Verbosity. + +app_verbose_0 = @echo " APP " $(PROJECT); +app_verbose_2 = set -x; +app_verbose = $(app_verbose_$(V)) + +appsrc_verbose_0 = @echo " APP " $(PROJECT).app.src; +appsrc_verbose_2 = set -x; +appsrc_verbose = $(appsrc_verbose_$(V)) + +makedep_verbose_0 = @echo " DEPEND" $(PROJECT).d; +makedep_verbose_2 = set -x; +makedep_verbose = $(makedep_verbose_$(V)) + +erlc_verbose_0 = @echo " ERLC " $(filter-out $(patsubst %,%.erl,$(ERLC_EXCLUDE)),\ + $(filter %.erl %.core,$(?F))); +erlc_verbose_2 = set -x; +erlc_verbose = $(erlc_verbose_$(V)) + +xyrl_verbose_0 = @echo " XYRL " $(filter %.xrl %.yrl,$(?F)); +xyrl_verbose_2 = set -x; +xyrl_verbose = $(xyrl_verbose_$(V)) + +asn1_verbose_0 = @echo " ASN1 " $(filter %.asn1,$(?F)); +asn1_verbose_2 = set -x; +asn1_verbose = $(asn1_verbose_$(V)) + +mib_verbose_0 = @echo " MIB " $(filter %.bin %.mib,$(?F)); +mib_verbose_2 = set -x; +mib_verbose = $(mib_verbose_$(V)) + +ifneq ($(wildcard src/),) + +# Targets. + +ifeq ($(wildcard ebin/test),) +app:: deps $(PROJECT).d + $(verbose) $(MAKE) --no-print-directory app-build +else +app:: clean deps $(PROJECT).d + $(verbose) $(MAKE) --no-print-directory app-build +endif + +ifeq ($(wildcard src/$(PROJECT_MOD).erl),) +define app_file +{application, '$(PROJECT)', [ + {description, "$(PROJECT_DESCRIPTION)"}, + {vsn, "$(PROJECT_VERSION)"},$(if $(IS_DEP), + {id$(comma)$(space)"$(1)"}$(comma)) + {modules, [$(call comma_list,$(2))]}, + {registered, []}, + {applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS))]}, + {env, $(subst \,\\,$(PROJECT_ENV))}$(if $(findstring {,$(PROJECT_APP_EXTRA_KEYS)),$(comma)$(newline)$(tab)$(subst \,\\,$(PROJECT_APP_EXTRA_KEYS)),) +]}. +endef +else +define app_file +{application, '$(PROJECT)', [ + {description, "$(PROJECT_DESCRIPTION)"}, + {vsn, "$(PROJECT_VERSION)"},$(if $(IS_DEP), + {id$(comma)$(space)"$(1)"}$(comma)) + {modules, [$(call comma_list,$(2))]}, + {registered, [$(call comma_list,$(PROJECT)_sup $(PROJECT_REGISTERED))]}, + {applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS))]}, + {mod, {$(PROJECT_MOD), []}}, + {env, $(subst \,\\,$(PROJECT_ENV))}$(if $(findstring {,$(PROJECT_APP_EXTRA_KEYS)),$(comma)$(newline)$(tab)$(subst \,\\,$(PROJECT_APP_EXTRA_KEYS)),) +]}. +endef +endif + +app-build: ebin/$(PROJECT).app + $(verbose) : + +# Source files. + +ALL_SRC_FILES := $(sort $(call core_find,src/,*)) + +ERL_FILES := $(filter %.erl,$(ALL_SRC_FILES)) +CORE_FILES := $(filter %.core,$(ALL_SRC_FILES)) + +# ASN.1 files. + +ifneq ($(wildcard asn1/),) +ASN1_FILES = $(sort $(call core_find,asn1/,*.asn1)) +ERL_FILES += $(addprefix src/,$(patsubst %.asn1,%.erl,$(notdir $(ASN1_FILES)))) + +define compile_asn1 + $(verbose) mkdir -p include/ + $(asn1_verbose) erlc -v -I include/ -o asn1/ +noobj $(ERLC_ASN1_OPTS) $(1) + $(verbose) mv asn1/*.erl src/ + $(verbose) mv asn1/*.hrl include/ + $(verbose) mv asn1/*.asn1db include/ +endef + +$(PROJECT).d:: $(ASN1_FILES) + $(if $(strip $?),$(call compile_asn1,$?)) +endif + +# SNMP MIB files. + +ifneq ($(wildcard mibs/),) +MIB_FILES = $(sort $(call core_find,mibs/,*.mib)) + +$(PROJECT).d:: $(COMPILE_MIB_FIRST_PATHS) $(MIB_FILES) + $(verbose) mkdir -p include/ priv/mibs/ + $(mib_verbose) erlc -v $(ERLC_MIB_OPTS) -o priv/mibs/ -I priv/mibs/ $? + $(mib_verbose) erlc -o include/ -- $(addprefix priv/mibs/,$(patsubst %.mib,%.bin,$(notdir $?))) +endif + +# Leex and Yecc files. + +XRL_FILES := $(filter %.xrl,$(ALL_SRC_FILES)) +XRL_ERL_FILES = $(addprefix src/,$(patsubst %.xrl,%.erl,$(notdir $(XRL_FILES)))) +ERL_FILES += $(XRL_ERL_FILES) + +YRL_FILES := $(filter %.yrl,$(ALL_SRC_FILES)) +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/ $(YRL_ERLC_OPTS) $?) + +# Erlang and Core Erlang files. + +define makedep.erl + E = ets:new(makedep, [bag]), + G = digraph:new([acyclic]), + ErlFiles = lists:usort(string:tokens("$(ERL_FILES)", " ")), + Modules = [{list_to_atom(filename:basename(F, ".erl")), F} || F <- ErlFiles], + Add = fun (Mod, Dep) -> + case lists:keyfind(Dep, 1, Modules) of + false -> ok; + {_, DepFile} -> + {_, ModFile} = lists:keyfind(Mod, 1, Modules), + ets:insert(E, {ModFile, DepFile}), + digraph:add_vertex(G, Mod), + digraph:add_vertex(G, Dep), + digraph:add_edge(G, Mod, Dep) + end + end, + AddHd = fun (F, Mod, DepFile) -> + case file:open(DepFile, [read]) of + {error, enoent} -> ok; + {ok, Fd} -> + F(F, Fd, Mod), + {_, ModFile} = lists:keyfind(Mod, 1, Modules), + ets:insert(E, {ModFile, DepFile}) + end + end, + Attr = fun + (F, Mod, behavior, Dep) -> Add(Mod, Dep); + (F, Mod, behaviour, Dep) -> Add(Mod, Dep); + (F, Mod, compile, {parse_transform, Dep}) -> Add(Mod, Dep); + (F, Mod, compile, Opts) when is_list(Opts) -> + case proplists:get_value(parse_transform, Opts) of + undefined -> ok; + Dep -> Add(Mod, Dep) + end; + (F, Mod, include, Hrl) -> + case filelib:is_file("include/" ++ Hrl) of + true -> AddHd(F, Mod, "include/" ++ Hrl); + false -> + case filelib:is_file("src/" ++ Hrl) of + true -> AddHd(F, Mod, "src/" ++ Hrl); + false -> false + end + end; + (F, Mod, include_lib, "$1/include/" ++ Hrl) -> AddHd(F, Mod, "include/" ++ Hrl); + (F, Mod, include_lib, Hrl) -> AddHd(F, Mod, "include/" ++ Hrl); + (F, Mod, import, {Imp, _}) -> + IsFile = + case lists:keyfind(Imp, 1, Modules) of + false -> false; + {_, FilePath} -> filelib:is_file(FilePath) + end, + case IsFile of + false -> ok; + true -> Add(Mod, Imp) + end; + (_, _, _, _) -> ok + end, + MakeDepend = fun(F, Fd, Mod) -> + case io:parse_erl_form(Fd, undefined) of + {ok, {attribute, _, Key, Value}, _} -> + Attr(F, Mod, Key, Value), + F(F, Fd, Mod); + {eof, _} -> + file:close(Fd); + _ -> + F(F, Fd, Mod) + end + end, + [begin + Mod = list_to_atom(filename:basename(F, ".erl")), + {ok, Fd} = file:open(F, [read]), + MakeDepend(MakeDepend, Fd, Mod) + end || F <- ErlFiles], + Depend = sofs:to_external(sofs:relation_to_family(sofs:relation(ets:tab2list(E)))), + CompileFirst = [X || X <- lists:reverse(digraph_utils:topsort(G)), [] =/= digraph:in_neighbours(G, X)], + TargetPath = fun(Target) -> + case lists:keyfind(Target, 1, Modules) of + false -> ""; + {_, DepFile} -> + DirSubname = tl(string:tokens(filename:dirname(DepFile), "/")), + string:join(DirSubname ++ [atom_to_list(Target)], "/") + end + end, + ok = file:write_file("$(1)", [ + [[F, "::", [[" ", D] || D <- Deps], "; @touch \$$@\n"] || {F, Deps} <- Depend], + "\nCOMPILE_FIRST +=", [[" ", TargetPath(CF)] || CF <- CompileFirst], "\n" + ]), + halt() +endef + +ifeq ($(if $(NO_MAKEDEP),$(wildcard $(PROJECT).d),),) +$(PROJECT).d:: $(ERL_FILES) $(call core_find,include/,*.hrl) $(MAKEFILE_LIST) + $(makedep_verbose) $(call erlang,$(call makedep.erl,$@)) +endif + +ifneq ($(words $(ERL_FILES) $(CORE_FILES) $(ASN1_FILES) $(MIB_FILES) $(XRL_FILES) $(YRL_FILES)),0) +# Rebuild everything when the Makefile changes. +$(ERLANG_MK_TMP)/last-makefile-change: $(MAKEFILE_LIST) + $(verbose) mkdir -p $(ERLANG_MK_TMP) + $(verbose) if test -f $@; then \ + touch $(ERL_FILES) $(CORE_FILES) $(ASN1_FILES) $(MIB_FILES) $(XRL_FILES) $(YRL_FILES); \ + touch -c $(PROJECT).d; \ + fi + $(verbose) touch $@ + +$(ERL_FILES) $(CORE_FILES) $(ASN1_FILES) $(MIB_FILES) $(XRL_FILES) $(YRL_FILES):: $(ERLANG_MK_TMP)/last-makefile-change +ebin/$(PROJECT).app:: $(ERLANG_MK_TMP)/last-makefile-change +endif + +include $(wildcard $(PROJECT).d) + +ebin/$(PROJECT).app:: ebin/ + +ebin/: + $(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) $(wildcard src/$(PROJECT).app.src) + $(eval FILES_TO_COMPILE := $(filter-out src/$(PROJECT).app.src,$?)) + $(if $(strip $(FILES_TO_COMPILE)),$(call compile_erl,$(FILES_TO_COMPILE))) + $(eval GITDESCRIBE := $(shell git describe --dirty --abbrev=7 --tags --always --first-parent 2>/dev/null || true)) + $(eval MODULES := $(patsubst %,'%',$(sort $(notdir $(basename \ + $(filter-out $(ERLC_EXCLUDE_PATHS),$(ERL_FILES) $(CORE_FILES) $(BEAM_FILES))))))) +ifeq ($(wildcard src/$(PROJECT).app.src),) + $(app_verbose) printf '$(subst %,%%,$(subst $(newline),\n,$(subst ','\'',$(call app_file,$(GITDESCRIBE),$(MODULES)))))' \ + > ebin/$(PROJECT).app +else + $(verbose) if [ -z "$$(grep -e '^[^%]*{\s*modules\s*,' src/$(PROJECT).app.src)" ]; then \ + echo "Empty modules entry not found in $(PROJECT).app.src. Please consult the erlang.mk README for instructions." >&2; \ + exit 1; \ + fi + $(appsrc_verbose) cat src/$(PROJECT).app.src \ + | sed "s/{[[:space:]]*modules[[:space:]]*,[[:space:]]*\[\]}/{modules, \[$(call comma_list,$(MODULES))\]}/" \ + | sed "s/{id,[[:space:]]*\"git\"}/{id, \"$(subst /,\/,$(GITDESCRIBE))\"}/" \ + > ebin/$(PROJECT).app +endif + +clean:: clean-app + +clean-app: + $(gen_verbose) rm -rf $(PROJECT).d ebin/ priv/mibs/ $(XRL_ERL_FILES) $(YRL_ERL_FILES) \ + $(addprefix include/,$(patsubst %.mib,%.hrl,$(notdir $(MIB_FILES)))) \ + $(addprefix include/,$(patsubst %.asn1,%.hrl,$(notdir $(ASN1_FILES)))) \ + $(addprefix include/,$(patsubst %.asn1,%.asn1db,$(notdir $(ASN1_FILES)))) \ + $(addprefix src/,$(patsubst %.asn1,%.erl,$(notdir $(ASN1_FILES)))) + +endif + +# Copyright (c) 2016, Loïc Hoguin +# Copyright (c) 2015, Viktor Söderqvist +# This file is part of erlang.mk and subject to the terms of the ISC License. + +.PHONY: docs-deps + +# Configuration. + +ALL_DOC_DEPS_DIRS = $(addprefix $(DEPS_DIR)/,$(DOC_DEPS)) + +# Targets. + +$(foreach dep,$(DOC_DEPS),$(eval $(call dep_target,$(dep)))) + +ifneq ($(SKIP_DEPS),) +doc-deps: +else +doc-deps: $(ALL_DOC_DEPS_DIRS) + $(verbose) for dep in $(ALL_DOC_DEPS_DIRS) ; do $(MAKE) -C $$dep; done +endif + +# Copyright (c) 2015-2016, Loïc Hoguin +# This file is part of erlang.mk and subject to the terms of the ISC License. + +.PHONY: rel-deps + +# Configuration. + +ALL_REL_DEPS_DIRS = $(addprefix $(DEPS_DIR)/,$(REL_DEPS)) + +# Targets. + +$(foreach dep,$(REL_DEPS),$(eval $(call dep_target,$(dep)))) + +ifneq ($(SKIP_DEPS),) +rel-deps: +else +rel-deps: $(ALL_REL_DEPS_DIRS) + $(verbose) for dep in $(ALL_REL_DEPS_DIRS) ; do $(MAKE) -C $$dep; done +endif + +# Copyright (c) 2015-2016, Loïc Hoguin +# This file is part of erlang.mk and subject to the terms of the ISC License. + +.PHONY: test-deps test-dir test-build clean-test-dir + +# Configuration. + +TEST_DIR ?= $(CURDIR)/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)))) + +ifneq ($(SKIP_DEPS),) +test-deps: +else +test-deps: $(ALL_TEST_DEPS_DIRS) + $(verbose) for dep in $(ALL_TEST_DEPS_DIRS) ; do $(MAKE) -C $$dep IS_DEP=1; done +endif + +ifneq ($(wildcard $(TEST_DIR)),) +test-dir: + $(gen_verbose) erlc -v $(TEST_ERLC_OPTS) -I include/ -o $(TEST_DIR) \ + $(call core_find,$(TEST_DIR)/,*.erl) -pa ebin/ +endif + +ifeq ($(wildcard src),) +test-build:: ERLC_OPTS=$(TEST_ERLC_OPTS) +test-build:: clean deps test-deps + $(verbose) $(MAKE) --no-print-directory test-dir ERLC_OPTS="$(TEST_ERLC_OPTS)" +else +ifeq ($(wildcard ebin/test),) +test-build:: ERLC_OPTS=$(TEST_ERLC_OPTS) +test-build:: clean deps test-deps $(PROJECT).d + $(verbose) $(MAKE) --no-print-directory app-build test-dir ERLC_OPTS="$(TEST_ERLC_OPTS)" + $(gen_verbose) touch ebin/test +else +test-build:: ERLC_OPTS=$(TEST_ERLC_OPTS) +test-build:: deps test-deps $(PROJECT).d + $(verbose) $(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 +endif + +# Copyright (c) 2015-2016, Loïc Hoguin +# This file is part of erlang.mk and subject to the terms of the ISC License. + +.PHONY: rebar.config + +# We strip out -Werror because we don't want to fail due to +# warnings when used as a dependency. + +compat_prepare_erlc_opts = $(shell echo "$1" | sed 's/, */,/g') + +define compat_convert_erlc_opts +$(if $(filter-out -Werror,$1),\ + $(if $(findstring +,$1),\ + $(shell echo $1 | cut -b 2-))) +endef + +define compat_erlc_opts_to_list +[$(call comma_list,$(foreach o,$(call compat_prepare_erlc_opts,$1),$(call compat_convert_erlc_opts,$o)))] +endef + +define compat_rebar_config +{deps, [ +$(call comma_list,$(foreach d,$(DEPS),\ + $(if $(filter hex,$(call dep_fetch,$d)),\ + {$(call dep_name,$d)$(comma)"$(call dep_repo,$d)"},\ + {$(call dep_name,$d)$(comma)".*"$(comma){git,"$(call dep_repo,$d)"$(comma)"$(call dep_commit,$d)"}}))) +]}. +{erl_opts, $(call compat_erlc_opts_to_list,$(ERLC_OPTS))}. +endef + +$(eval _compat_rebar_config = $$(compat_rebar_config)) +$(eval export _compat_rebar_config) + +rebar.config: + $(gen_verbose) echo "$${_compat_rebar_config}" > rebar.config + +# Copyright (c) 2015-2016, Loïc Hoguin +# This file is part of erlang.mk and subject to the terms of the ISC License. + +ifeq ($(filter asciideck,$(DEPS) $(DOC_DEPS)),asciideck) + +.PHONY: asciidoc asciidoc-guide asciidoc-manual install-asciidoc distclean-asciidoc-guide distclean-asciidoc-manual + +# Core targets. + +docs:: asciidoc + +distclean:: distclean-asciidoc-guide distclean-asciidoc-manual + +# Plugin-specific targets. + +asciidoc: asciidoc-guide asciidoc-manual + +# User guide. + +ifeq ($(wildcard doc/src/guide/book.asciidoc),) +asciidoc-guide: +else +asciidoc-guide: distclean-asciidoc-guide doc-deps + a2x -v -f pdf doc/src/guide/book.asciidoc && mv doc/src/guide/book.pdf doc/guide.pdf + a2x -v -f chunked doc/src/guide/book.asciidoc && mv doc/src/guide/book.chunked/ doc/html/ + +distclean-asciidoc-guide: + $(gen_verbose) rm -rf doc/html/ doc/guide.pdf +endif + +# Man pages. + +ASCIIDOC_MANUAL_FILES := $(wildcard doc/src/manual/*.asciidoc) + +ifeq ($(ASCIIDOC_MANUAL_FILES),) +asciidoc-manual: +else + +# Configuration. + +MAN_INSTALL_PATH ?= /usr/local/share/man +MAN_SECTIONS ?= 3 7 +MAN_PROJECT ?= $(shell echo $(PROJECT) | sed 's/^./\U&\E/') +MAN_VERSION ?= $(PROJECT_VERSION) + +# Plugin-specific targets. + +define asciidoc2man.erl +try + [begin + io:format(" ADOC ~s~n", [F]), + ok = asciideck:to_manpage(asciideck:parse_file(F), #{ + compress => gzip, + outdir => filename:dirname(F), + extra2 => "$(MAN_PROJECT) $(MAN_VERSION)", + extra3 => "$(MAN_PROJECT) Function Reference" + }) + end || F <- [$(shell echo $(addprefix $(comma)\",$(addsuffix \",$1)) | sed 's/^.//')]], + halt(0) +catch C:E -> + io:format("Exception ~p:~p~nStacktrace: ~p~n", [C, E, erlang:get_stacktrace()]), + halt(1) +end. +endef + +asciidoc-manual:: doc-deps + +asciidoc-manual:: $(ASCIIDOC_MANUAL_FILES) + $(call erlang,$(call asciidoc2man.erl,$?)) + $(foreach s,$(MAN_SECTIONS),mkdir -p doc/man$s/ && mv doc/src/manual/*.$s.gz doc/man$s/;) + +install-docs:: install-asciidoc + +install-asciidoc: asciidoc-manual + $(foreach s,$(MAN_SECTIONS),\ + mkdir -p $(MAN_INSTALL_PATH)/man$s/ && \ + install -g `id -u` -o `id -g` -m 0644 doc/man$s/*.gz $(MAN_INSTALL_PATH)/man$s/;) + +distclean-asciidoc-manual: + $(gen_verbose) rm -rf $(addprefix doc/man,$(MAN_SECTIONS)) +endif +endif + +# Copyright (c) 2014-2016, Loïc Hoguin +# 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 + +# Core targets. + +help:: + $(verbose) printf "%s\n" "" \ + "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-app in=NAME Create a new local OTP application NAME" \ + " new-lib in=NAME Create a new local OTP library NAME" \ + " new t=TPL n=NAME Generate a module NAME based on the template TPL" \ + " new t=T n=N in=APP Generate a module NAME based on the template TPL in APP" \ + " list-templates List available templates" + +# Bootstrap templates. + +define bs_appsrc +{application, $p, [ + {description, ""}, + {vsn, "0.1.0"}, + {id, "git"}, + {modules, []}, + {registered, []}, + {applications, [ + kernel, + stdlib + ]}, + {mod, {$p_app, []}}, + {env, []} +]}. +endef + +define bs_appsrc_lib +{application, $p, [ + {description, ""}, + {vsn, "0.1.0"}, + {id, "git"}, + {modules, []}, + {registered, []}, + {applications, [ + kernel, + stdlib + ]} +]}. +endef + +# To prevent autocompletion issues with ZSH, we add "include erlang.mk" +# separately during the actual bootstrap. +ifdef SP +define bs_Makefile +PROJECT = $p +PROJECT_DESCRIPTION = New project +PROJECT_VERSION = 0.1.0 + +# Whitespace to be used when creating files from templates. +SP = $(SP) + +endef +else +define bs_Makefile +PROJECT = $p +PROJECT_DESCRIPTION = New project +PROJECT_VERSION = 0.1.0 + +endef +endif + +define bs_apps_Makefile +PROJECT = $p +PROJECT_DESCRIPTION = New project +PROJECT_VERSION = 0.1.0 + +include $(call core_relpath,$(dir $(ERLANG_MK_FILENAME)),$(APPS_DIR)/app)/erlang.mk +endef + +define bs_app +-module($p_app). +-behaviour(application). + +-export([start/2]). +-export([stop/1]). + +start(_Type, _Args) -> + $p_sup:start_link(). + +stop(_State) -> + ok. +endef + +define bs_relx_config +{release, {$p_release, "1"}, [$p, sasl, runtime_tools]}. +{extended_start_script, true}. +{sys_config, "rel/sys.config"}. +{vm_args, "rel/vm.args"}. +endef + +define bs_sys_config +[ +]. +endef + +define bs_vm_args +-name $p@127.0.0.1 +-setcookie $p +-heart +endef + +# Normal templates. + +define tpl_supervisor +-module($(n)). +-behaviour(supervisor). + +-export([start_link/0]). +-export([init/1]). + +start_link() -> + supervisor:start_link({local, ?MODULE}, ?MODULE, []). + +init([]) -> + Procs = [], + {ok, {{one_for_one, 1, 5}, Procs}}. +endef + +define tpl_gen_server +-module($(n)). +-behaviour(gen_server). + +%% API. +-export([start_link/0]). + +%% gen_server. +-export([init/1]). +-export([handle_call/3]). +-export([handle_cast/2]). +-export([handle_info/2]). +-export([terminate/2]). +-export([code_change/3]). + +-record(state, { +}). + +%% API. + +-spec start_link() -> {ok, pid()}. +start_link() -> + gen_server:start_link(?MODULE, [], []). + +%% gen_server. + +init([]) -> + {ok, #state{}}. + +handle_call(_Request, _From, State) -> + {reply, ignored, State}. + +handle_cast(_Msg, State) -> + {noreply, State}. + +handle_info(_Info, State) -> + {noreply, State}. + +terminate(_Reason, _State) -> + ok. + +code_change(_OldVsn, State, _Extra) -> + {ok, State}. +endef + +define tpl_module +-module($(n)). +-export([]). +endef + +define tpl_cowboy_http +-module($(n)). +-behaviour(cowboy_http_handler). + +-export([init/3]). +-export([handle/2]). +-export([terminate/3]). + +-record(state, { +}). + +init(_, Req, _Opts) -> + {ok, Req, #state{}}. + +handle(Req, State=#state{}) -> + {ok, Req2} = cowboy_req:reply(200, Req), + {ok, Req2, State}. + +terminate(_Reason, _Req, _State) -> + ok. +endef + +define 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}. +endef + +define tpl_cowboy_loop +-module($(n)). +-behaviour(cowboy_loop_handler). + +-export([init/3]). +-export([info/3]). +-export([terminate/3]). + +-record(state, { +}). + +init(_, Req, _Opts) -> + {loop, Req, #state{}, 5000, hibernate}. + +info(_Info, Req, State) -> + {loop, Req, State, hibernate}. + +terminate(_Reason, _Req, _State) -> + ok. +endef + +define tpl_cowboy_rest +-module($(n)). + +-export([init/3]). +-export([content_types_provided/2]). +-export([get_html/2]). + +init(_, _Req, _Opts) -> + {upgrade, protocol, cowboy_rest}. + +content_types_provided(Req, State) -> + {[{{<<"text">>, <<"html">>, '*'}, get_html}], Req, State}. + +get_html(Req, State) -> + {<<"This is REST!">>, Req, State}. +endef + +define tpl_cowboy_ws +-module($(n)). +-behaviour(cowboy_websocket_handler). + +-export([init/3]). +-export([websocket_init/3]). +-export([websocket_handle/3]). +-export([websocket_info/3]). +-export([websocket_terminate/3]). + +-record(state, { +}). + +init(_, _, _) -> + {upgrade, protocol, cowboy_websocket}. + +websocket_init(_, Req, _Opts) -> + Req2 = cowboy_req:compact(Req), + {ok, Req2, #state{}}. + +websocket_handle({text, Data}, Req, State) -> + {reply, {text, Data}, Req, State}; +websocket_handle({binary, Data}, Req, State) -> + {reply, {binary, Data}, Req, State}; +websocket_handle(_Frame, Req, State) -> + {ok, Req, State}. + +websocket_info(_Info, Req, State) -> + {ok, Req, State}. + +websocket_terminate(_Reason, _Req, _State) -> + ok. +endef + +define tpl_ranch_protocol +-module($(n)). +-behaviour(ranch_protocol). + +-export([start_link/4]). +-export([init/4]). + +-type opts() :: []. +-export_type([opts/0]). + +-record(state, { + socket :: inet:socket(), + transport :: module() +}). + +start_link(Ref, Socket, Transport, Opts) -> + Pid = spawn_link(?MODULE, init, [Ref, Socket, Transport, Opts]), + {ok, Pid}. + +-spec init(ranch:ref(), inet:socket(), module(), opts()) -> ok. +init(Ref, Socket, Transport, _Opts) -> + ok = ranch:accept_ack(Ref), + loop(#state{socket=Socket, transport=Transport}). + +loop(State) -> + loop(State). +endef + +# Plugin-specific targets. + +define render_template + $(verbose) printf -- '$(subst $(newline),\n,$(subst %,%%,$(subst ','\'',$(subst $(tab),$(WS),$(call $(1))))))\n' > $(2) +endef + +ifndef WS +ifdef SP +WS = $(subst a,,a $(wordlist 1,$(SP),a a a a a a a a a a a a a a a a a a a a)) +else +WS = $(tab) +endif +endif + +bootstrap: +ifneq ($(wildcard src/),) + $(error Error: src/ directory already exists) +endif + $(eval p := $(PROJECT)) + $(eval n := $(PROJECT)_sup) + $(call render_template,bs_Makefile,Makefile) + $(verbose) echo "include erlang.mk" >> Makefile + $(verbose) mkdir src/ +ifdef LEGACY + $(call render_template,bs_appsrc,src/$(PROJECT).app.src) +endif + $(call render_template,bs_app,src/$(PROJECT)_app.erl) + $(call render_template,tpl_supervisor,src/$(PROJECT)_sup.erl) + +bootstrap-lib: +ifneq ($(wildcard src/),) + $(error Error: src/ directory already exists) +endif + $(eval p := $(PROJECT)) + $(call render_template,bs_Makefile,Makefile) + $(verbose) echo "include erlang.mk" >> Makefile + $(verbose) mkdir src/ +ifdef LEGACY + $(call render_template,bs_appsrc_lib,src/$(PROJECT).app.src) +endif + +bootstrap-rel: +ifneq ($(wildcard relx.config),) + $(error Error: relx.config already exists) +endif +ifneq ($(wildcard rel/),) + $(error Error: rel/ directory already exists) +endif + $(eval p := $(PROJECT)) + $(call render_template,bs_relx_config,relx.config) + $(verbose) mkdir rel/ + $(call render_template,bs_sys_config,rel/sys.config) + $(call render_template,bs_vm_args,rel/vm.args) + +new-app: +ifndef in + $(error Usage: $(MAKE) new-app in=APP) +endif +ifneq ($(wildcard $(APPS_DIR)/$in),) + $(error Error: Application $in already exists) +endif + $(eval p := $(in)) + $(eval n := $(in)_sup) + $(verbose) mkdir -p $(APPS_DIR)/$p/src/ + $(call render_template,bs_apps_Makefile,$(APPS_DIR)/$p/Makefile) +ifdef LEGACY + $(call render_template,bs_appsrc,$(APPS_DIR)/$p/src/$p.app.src) +endif + $(call render_template,bs_app,$(APPS_DIR)/$p/src/$p_app.erl) + $(call render_template,tpl_supervisor,$(APPS_DIR)/$p/src/$p_sup.erl) + +new-lib: +ifndef in + $(error Usage: $(MAKE) new-lib in=APP) +endif +ifneq ($(wildcard $(APPS_DIR)/$in),) + $(error Error: Application $in already exists) +endif + $(eval p := $(in)) + $(verbose) mkdir -p $(APPS_DIR)/$p/src/ + $(call render_template,bs_apps_Makefile,$(APPS_DIR)/$p/Makefile) +ifdef LEGACY + $(call render_template,bs_appsrc_lib,$(APPS_DIR)/$p/src/$p.app.src) +endif + +new: +ifeq ($(wildcard src/)$(in),) + $(error Error: src/ directory does not exist) +endif +ifndef t + $(error Usage: $(MAKE) new t=TEMPLATE n=NAME [in=APP]) +endif +ifndef n + $(error Usage: $(MAKE) new t=TEMPLATE n=NAME [in=APP]) +endif +ifdef in + $(verbose) $(MAKE) -C $(APPS_DIR)/$(in)/ new t=$t n=$n in= +else + $(call render_template,tpl_$(t),src/$(n).erl) +endif + +list-templates: + $(verbose) echo Available templates: $(sort $(patsubst tpl_%,%,$(filter tpl_%,$(.VARIABLES)))) + +# Copyright (c) 2014-2016, Loïc Hoguin +# This file is part of erlang.mk and subject to the terms of the ISC License. + +.PHONY: clean-c_src distclean-c_src-env + +# Configuration. + +C_SRC_DIR ?= $(CURDIR)/c_src +C_SRC_ENV ?= $(C_SRC_DIR)/env.mk +C_SRC_OUTPUT ?= $(CURDIR)/priv/$(PROJECT) +C_SRC_TYPE ?= shared + +# System type and C compiler/flags. + +ifeq ($(PLATFORM),msys2) + C_SRC_OUTPUT_EXECUTABLE_EXTENSION ?= .exe + C_SRC_OUTPUT_SHARED_EXTENSION ?= .dll +else + C_SRC_OUTPUT_EXECUTABLE_EXTENSION ?= + C_SRC_OUTPUT_SHARED_EXTENSION ?= .so +endif + +ifeq ($(C_SRC_TYPE),shared) + C_SRC_OUTPUT_FILE = $(C_SRC_OUTPUT)$(C_SRC_OUTPUT_SHARED_EXTENSION) +else + C_SRC_OUTPUT_FILE = $(C_SRC_OUTPUT)$(C_SRC_OUTPUT_EXECUTABLE_EXTENSION) +endif + +ifeq ($(PLATFORM),msys2) +# We hardcode the compiler used on MSYS2. The default CC=cc does +# not produce working code. The "gcc" MSYS2 package also doesn't. + CC = /mingw64/bin/gcc + export CC + CFLAGS ?= -O3 -std=c99 -finline-functions -Wall -Wmissing-prototypes + CXXFLAGS ?= -O3 -finline-functions -Wall +else ifeq ($(PLATFORM),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 ($(PLATFORM),freebsd) + CC ?= cc + CFLAGS ?= -O3 -std=c99 -finline-functions -Wall -Wmissing-prototypes + CXXFLAGS ?= -O3 -finline-functions -Wall +else ifeq ($(PLATFORM),linux) + CC ?= gcc + CFLAGS ?= -O3 -std=c99 -finline-functions -Wall -Wmissing-prototypes + CXXFLAGS ?= -O3 -finline-functions -Wall +endif + +ifneq ($(PLATFORM),msys2) + CFLAGS += -fPIC + CXXFLAGS += -fPIC +endif + +CFLAGS += -I"$(ERTS_INCLUDE_DIR)" -I"$(ERL_INTERFACE_INCLUDE_DIR)" +CXXFLAGS += -I"$(ERTS_INCLUDE_DIR)" -I"$(ERL_INTERFACE_INCLUDE_DIR)" + +LDLIBS += -L"$(ERL_INTERFACE_LIB_DIR)" -lerl_interface -lei + +# 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:: app-c_src + +test-build:: app-c_src + +app-c_src: + $(MAKE) -C $(C_SRC_DIR) + +clean:: + $(MAKE) -C $(C_SRC_DIR) clean + +else + +ifeq ($(SOURCES),) +SOURCES := $(sort $(foreach pat,*.c *.C *.cc *.cpp,$(call core_find,$(C_SRC_DIR)/,$(pat)))) +endif +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_FILE) + +test-build:: $(C_SRC_ENV) $(C_SRC_OUTPUT_FILE) + +$(C_SRC_OUTPUT_FILE): $(OBJECTS) + $(verbose) mkdir -p priv/ + $(link_verbose) $(CC) $(OBJECTS) \ + $(LDFLAGS) $(if $(filter $(C_SRC_TYPE),shared),-shared) $(LDLIBS) \ + -o $(C_SRC_OUTPUT_FILE) + +%.o: %.c + $(COMPILE_C) $(OUTPUT_OPTION) $< + +%.o: %.cc + $(COMPILE_CPP) $(OUTPUT_OPTION) $< + +%.o: %.C + $(COMPILE_CPP) $(OUTPUT_OPTION) $< + +%.o: %.cpp + $(COMPILE_CPP) $(OUTPUT_OPTION) $< + +clean:: clean-c_src + +clean-c_src: + $(gen_verbose) rm -f $(C_SRC_OUTPUT_FILE) $(OBJECTS) + +endif + +ifneq ($(wildcard $(C_SRC_DIR)),) +$(C_SRC_ENV): + $(verbose) $(ERL) -eval "file:write_file(\"$(call core_native_path,$(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()." + +distclean:: distclean-c_src-env + +distclean-c_src-env: + $(gen_verbose) rm -f $(C_SRC_ENV) + +-include $(C_SRC_ENV) +endif + +# Templates. + +define bs_c_nif +#include "erl_nif.h" + +static int loads = 0; + +static int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) +{ + /* Initialize private data. */ + *priv_data = NULL; + + loads++; + + return 0; +} + +static int upgrade(ErlNifEnv* env, void** priv_data, void** old_priv_data, ERL_NIF_TERM load_info) +{ + /* Convert the private data to the new version. */ + *priv_data = *old_priv_data; + + loads++; + + return 0; +} + +static void unload(ErlNifEnv* env, void* priv_data) +{ + if (loads == 1) { + /* Destroy the private data. */ + } + + loads--; +} + +static ERL_NIF_TERM hello(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + if (enif_is_atom(env, argv[0])) { + return enif_make_tuple2(env, + enif_make_atom(env, "hello"), + argv[0]); + } + + return enif_make_tuple2(env, + enif_make_atom(env, "error"), + enif_make_atom(env, "badarg")); +} + +static ErlNifFunc nif_funcs[] = { + {"hello", 1, hello} +}; + +ERL_NIF_INIT($n, nif_funcs, load, NULL, upgrade, unload) +endef + +define bs_erl_nif +-module($n). + +-export([hello/1]). + +-on_load(on_load/0). +on_load() -> + PrivDir = case code:priv_dir(?MODULE) of + {error, _} -> + AppPath = filename:dirname(filename:dirname(code:which(?MODULE))), + filename:join(AppPath, "priv"); + Path -> + Path + end, + erlang:load_nif(filename:join(PrivDir, atom_to_list(?MODULE)), 0). + +hello(_) -> + erlang:nif_error({not_loaded, ?MODULE}). +endef + +new-nif: +ifneq ($(wildcard $(C_SRC_DIR)/$n.c),) + $(error Error: $(C_SRC_DIR)/$n.c already exists) +endif +ifneq ($(wildcard src/$n.erl),) + $(error Error: src/$n.erl already exists) +endif +ifdef in + $(verbose) $(MAKE) -C $(APPS_DIR)/$(in)/ new-nif n=$n in= +else + $(verbose) mkdir -p $(C_SRC_DIR) src/ + $(call render_template,bs_c_nif,$(C_SRC_DIR)/$n.c) + $(call render_template,bs_erl_nif,src/$n.erl) +endif + +# Copyright (c) 2015-2016, Loïc Hoguin +# This file is part of erlang.mk and subject to the terms of the ISC License. + +.PHONY: ci ci-prepare ci-setup distclean-kerl + +CI_OTP ?= +CI_HIPE ?= +CI_ERLLVM ?= + +ifeq ($(CI_VM),native) +ERLC_OPTS += +native +TEST_ERLC_OPTS += +native +else ifeq ($(CI_VM),erllvm) +ERLC_OPTS += +native +'{hipe, [to_llvm]}' +TEST_ERLC_OPTS += +native +'{hipe, [to_llvm]}' +endif + +ifeq ($(strip $(CI_OTP) $(CI_HIPE) $(CI_ERLLVM)),) +ci:: +else + +ifeq ($(strip $(KERL)),) +KERL := $(ERLANG_MK_TMP)/kerl/kerl +endif + +export KERL + +KERL_GIT ?= https://github.com/kerl/kerl +KERL_COMMIT ?= master + +KERL_MAKEFLAGS ?= + +OTP_GIT ?= https://github.com/erlang/otp + +CI_INSTALL_DIR ?= $(HOME)/erlang + +ci:: $(addprefix ci-,$(CI_OTP) $(addsuffix -native,$(CI_HIPE)) $(addsuffix -erllvm,$(CI_ERLLVM))) + +ci-prepare: $(addprefix $(CI_INSTALL_DIR)/,$(CI_OTP) $(addsuffix -native,$(CI_HIPE))) + +ci-setup:: + +ci-extra:: + +ci_verbose_0 = @echo " CI " $(1); +ci_verbose = $(ci_verbose_$(V)) + +define ci_target +ci-$1: $(CI_INSTALL_DIR)/$2 + $(verbose) $(MAKE) --no-print-directory clean + $(ci_verbose) \ + PATH="$(CI_INSTALL_DIR)/$2/bin:$(PATH)" \ + CI_OTP_RELEASE="$1" \ + CT_OPTS="-label $1" \ + CI_VM="$3" \ + $(MAKE) ci-setup tests + $(verbose) $(MAKE) --no-print-directory ci-extra +endef + +$(foreach otp,$(CI_OTP),$(eval $(call ci_target,$(otp),$(otp),otp))) +$(foreach otp,$(CI_HIPE),$(eval $(call ci_target,$(otp)-native,$(otp)-native,native))) +$(foreach otp,$(CI_ERLLVM),$(eval $(call ci_target,$(otp)-erllvm,$(otp)-native,erllvm))) + +define ci_otp_target +ifeq ($(wildcard $(CI_INSTALL_DIR)/$(1)),) +$(CI_INSTALL_DIR)/$(1): $(KERL) + MAKEFLAGS="$(KERL_MAKEFLAGS)" $(KERL) build git $(OTP_GIT) $(1) $(1) + $(KERL) install $(1) $(CI_INSTALL_DIR)/$(1) +endif +endef + +$(foreach otp,$(CI_OTP),$(eval $(call ci_otp_target,$(otp)))) + +define ci_hipe_target +ifeq ($(wildcard $(CI_INSTALL_DIR)/$1-native),) +$(CI_INSTALL_DIR)/$1-native: $(KERL) + KERL_CONFIGURE_OPTIONS=--enable-native-libs \ + MAKEFLAGS="$(KERL_MAKEFLAGS)" $(KERL) build git $(OTP_GIT) $1 $1-native + $(KERL) install $1-native $(CI_INSTALL_DIR)/$1-native +endif +endef + +$(foreach otp,$(sort $(CI_HIPE) $(CI_ERLLLVM)),$(eval $(call ci_hipe_target,$(otp)))) + +$(KERL): + $(verbose) mkdir -p $(ERLANG_MK_TMP) + $(gen_verbose) git clone --depth 1 $(KERL_GIT) $(ERLANG_MK_TMP)/kerl + $(verbose) cd $(ERLANG_MK_TMP)/kerl && git checkout $(KERL_COMMIT) + $(verbose) chmod +x $(KERL) + +help:: + $(verbose) printf "%s\n" "" \ + "Continuous Integration targets:" \ + " ci Run '$(MAKE) tests' on all configured Erlang versions." \ + "" \ + "The CI_OTP variable must be defined with the Erlang versions" \ + "that must be tested. For example: CI_OTP = OTP-17.3.4 OTP-17.5.3" + +distclean:: distclean-kerl + +distclean-kerl: + $(gen_verbose) rm -rf $(KERL) +endif + +# Copyright (c) 2013-2016, Loïc Hoguin +# This file is part of erlang.mk and subject to the terms of the ISC License. + +.PHONY: ct apps-ct distclean-ct + +# Configuration. + +CT_OPTS ?= + +ifneq ($(wildcard $(TEST_DIR)),) +ifndef CT_SUITES +CT_SUITES := $(sort $(subst _SUITE.erl,,$(notdir $(call core_find,$(TEST_DIR)/,*_SUITE.erl)))) +endif +endif +CT_SUITES ?= +CT_LOGS_DIR ?= $(CURDIR)/logs + +# Core targets. + +tests:: ct + +distclean:: distclean-ct + +help:: + $(verbose) 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. + +CT_RUN = ct_run \ + -no_auto_compile \ + -noinput \ + -pa $(CURDIR)/ebin $(DEPS_DIR)/*/ebin $(APPS_DIR)/*/ebin $(TEST_DIR) \ + -dir $(TEST_DIR) \ + -logdir $(CT_LOGS_DIR) + +ifeq ($(CT_SUITES),) +ct: $(if $(IS_APP),,apps-ct) +else +ct: test-build $(if $(IS_APP),,apps-ct) + $(verbose) mkdir -p $(CT_LOGS_DIR) + $(gen_verbose) $(CT_RUN) -sname ct_$(PROJECT) -suite $(addsuffix _SUITE,$(CT_SUITES)) $(CT_OPTS) +endif + +ifneq ($(ALL_APPS_DIRS),) +define ct_app_target +apps-ct-$1: + $(MAKE) -C $1 ct IS_APP=1 +endef + +$(foreach app,$(ALL_APPS_DIRS),$(eval $(call ct_app_target,$(app)))) + +apps-ct: test-build $(addprefix apps-ct-,$(ALL_APPS_DIRS)) +endif + +ifndef t +CT_EXTRA = +else +ifeq (,$(findstring :,$t)) +CT_EXTRA = -group $t +else +t_words = $(subst :, ,$t) +CT_EXTRA = -group $(firstword $(t_words)) -case $(lastword $(t_words)) +endif +endif + +define ct_suite_target +ct-$(1): test-build + $(verbose) mkdir -p $(CT_LOGS_DIR) + $(gen_verbose) $(CT_RUN) -sname ct_$(PROJECT) -suite $(addsuffix _SUITE,$(1)) $(CT_EXTRA) $(CT_OPTS) +endef + +$(foreach test,$(CT_SUITES),$(eval $(call ct_suite_target,$(test)))) + +distclean-ct: + $(gen_verbose) rm -rf $(CT_LOGS_DIR) + +# Copyright (c) 2013-2016, 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_DIRS ?= --src -r $(wildcard src) $(ALL_APPS_DIRS) +DIALYZER_OPTS ?= -Werror_handling -Wrace_conditions -Wunmatched_returns # -Wunderspecs + +# Core targets. + +check:: dialyze + +distclean:: distclean-plt + +help:: + $(verbose) printf "%s\n" "" \ + "Dialyzer targets:" \ + " plt Build a PLT file for this project" \ + " dialyze Analyze the project using Dialyzer" + +# Plugin-specific targets. + +define filter_opts.erl + Opts = init:get_plain_arguments(), + {Filtered, _} = lists:foldl(fun + (O, {Os, true}) -> {[O|Os], false}; + (O = "-D", {Os, _}) -> {[O|Os], true}; + (O = [\\$$-, \\$$D, _ | _], {Os, _}) -> {[O|Os], false}; + (O = "-I", {Os, _}) -> {[O|Os], true}; + (O = [\\$$-, \\$$I, _ | _], {Os, _}) -> {[O|Os], false}; + (O = "-pa", {Os, _}) -> {[O|Os], true}; + (_, Acc) -> Acc + end, {[], false}, Opts), + io:format("~s~n", [string:join(lists:reverse(Filtered), " ")]), + halt(). +endef + +$(DIALYZER_PLT): deps app + $(eval DEPS_LOG := $(shell test -f $(ERLANG_MK_TMP)/deps.log && \ + while read p; do test -d $$p/ebin && echo $$p/ebin; done <$(ERLANG_MK_TMP)/deps.log)) + $(verbose) dialyzer --build_plt --apps erts kernel stdlib \ + $(PLT_APPS) $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS_LOG) + +plt: $(DIALYZER_PLT) + +distclean-plt: + $(gen_verbose) rm -f $(DIALYZER_PLT) + +ifneq ($(wildcard $(DIALYZER_PLT)),) +dialyze: +else +dialyze: $(DIALYZER_PLT) +endif + $(verbose) dialyzer --no_native `$(ERL) -eval "$(subst $(newline),,$(subst ",\",$(call filter_opts.erl)))" -extra $(ERLC_OPTS)` $(DIALYZER_DIRS) $(DIALYZER_OPTS) + +# Copyright (c) 2013-2016, Loïc Hoguin +# This file is part of erlang.mk and subject to the terms of the ISC License. + +.PHONY: distclean-edoc edoc + +# Configuration. + +EDOC_OPTS ?= + +# Core targets. + +ifneq ($(wildcard doc/overview.edoc),) +docs:: edoc +endif + +distclean:: distclean-edoc + +# Plugin-specific targets. + +edoc: distclean-edoc doc-deps + $(gen_verbose) $(ERL) -eval 'edoc:application($(PROJECT), ".", [$(EDOC_OPTS)]), halt().' + +distclean-edoc: + $(gen_verbose) rm -f doc/*.css doc/*.html doc/*.png doc/edoc-info + +# Copyright (c) 2013-2016, Loïc Hoguin +# This file is part of erlang.mk and subject to the terms of the ISC License. + +# Configuration. + +DTL_FULL_PATH ?= +DTL_PATH ?= templates/ +DTL_SUFFIX ?= _dtl +DTL_OPTS ?= + +# Verbosity. + +dtl_verbose_0 = @echo " DTL " $(filter %.dtl,$(?F)); +dtl_verbose = $(dtl_verbose_$(V)) + +# Core targets. + +DTL_PATH := $(abspath $(DTL_PATH)) +DTL_FILES := $(sort $(call core_find,$(DTL_PATH),*.dtl)) + +ifneq ($(DTL_FILES),) + +DTL_NAMES = $(addsuffix $(DTL_SUFFIX),$(DTL_FILES:$(DTL_PATH)/%.dtl=%)) +DTL_MODULES = $(if $(DTL_FULL_PATH),$(subst /,_,$(DTL_NAMES)),$(notdir $(DTL_NAMES))) +BEAM_FILES += $(addsuffix .beam,$(addprefix ebin/,$(DTL_MODULES))) + +ifneq ($(words $(DTL_FILES)),0) +# Rebuild templates when the Makefile changes. +$(ERLANG_MK_TMP)/last-makefile-change-erlydtl: $(MAKEFILE_LIST) + @mkdir -p $(ERLANG_MK_TMP) + @if test -f $@; then \ + touch $(DTL_FILES); \ + fi + @touch $@ + +ebin/$(PROJECT).app:: $(ERLANG_MK_TMP)/last-makefile-change-erlydtl +endif + +define erlydtl_compile.erl + [begin + Module0 = case "$(strip $(DTL_FULL_PATH))" of + "" -> + filename:basename(F, ".dtl"); + _ -> + "$(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, [$(DTL_OPTS)] ++ [{out_dir, "ebin/"}, return_errors]) of + ok -> ok; + {ok, _} -> ok + end + end || F <- string:tokens("$(1)", " ")], + halt(). +endef + +ebin/$(PROJECT).app:: $(DTL_FILES) | ebin/ + $(if $(strip $?),\ + $(dtl_verbose) $(call erlang,$(call erlydtl_compile.erl,$(call core_native_path,$?)),\ + -pa ebin/ $(DEPS_DIR)/erlydtl/ebin/)) + +endif + +# Copyright (c) 2016, Loïc Hoguin +# Copyright (c) 2014, Dave Cottlehuber +# This file is part of erlang.mk and subject to the terms of the ISC License. + +.PHONY: distclean-escript escript escript-zip + +# Configuration. + +ESCRIPT_NAME ?= $(PROJECT) +ESCRIPT_FILE ?= $(ESCRIPT_NAME) + +ESCRIPT_SHEBANG ?= /usr/bin/env escript +ESCRIPT_COMMENT ?= This is an -*- erlang -*- file +ESCRIPT_EMU_ARGS ?= -escript main $(ESCRIPT_NAME) + +ESCRIPT_ZIP ?= 7z a -tzip -mx=9 -mtc=off $(if $(filter-out 0,$(V)),,> /dev/null) +ESCRIPT_ZIP_FILE ?= $(ERLANG_MK_TMP)/escript.zip + +# Core targets. + +distclean:: distclean-escript + +help:: + $(verbose) printf "%s\n" "" \ + "Escript targets:" \ + " escript Build an executable escript archive" \ + +# Plugin-specific targets. + +escript-zip:: deps app + $(verbose) mkdir -p $(dir $(ESCRIPT_ZIP)) + $(verbose) rm -f $(ESCRIPT_ZIP_FILE) + $(gen_verbose) cd .. && $(ESCRIPT_ZIP) $(ESCRIPT_ZIP_FILE) $(PROJECT)/ebin/* +ifneq ($(DEPS),) + $(verbose) cd $(DEPS_DIR) && $(ESCRIPT_ZIP) $(ESCRIPT_ZIP_FILE) \ + `cat $(ERLANG_MK_TMP)/deps.log | sed 's/^$(subst /,\/,$(DEPS_DIR))\///' | sed 's/$$/\/ebin\/\*/'` +endif + +escript:: escript-zip + $(gen_verbose) printf "%s\n" \ + "#!$(ESCRIPT_SHEBANG)" \ + "%% $(ESCRIPT_COMMENT)" \ + "%%! $(ESCRIPT_EMU_ARGS)" > $(ESCRIPT_FILE) + $(verbose) cat $(ESCRIPT_ZIP_FILE) >> $(ESCRIPT_FILE) + $(verbose) chmod +x $(ESCRIPT_FILE) + +distclean-escript: + $(gen_verbose) rm -f $(ESCRIPT_FILE) + +# Copyright (c) 2015-2016, Loïc Hoguin +# Copyright (c) 2014, Enrique Fernandez +# This file is contributed to erlang.mk and subject to the terms of the ISC License. + +.PHONY: eunit apps-eunit + +# Configuration + +EUNIT_OPTS ?= +EUNIT_ERL_OPTS ?= + +# Core targets. + +tests:: eunit + +help:: + $(verbose) printf "%s\n" "" \ + "EUnit targets:" \ + " eunit Run all the EUnit tests for this project" + +# Plugin-specific targets. + +define eunit.erl + case "$(COVER)" of + "" -> ok; + _ -> + case cover:compile_beam_directory("ebin") of + {error, _} -> halt(1); + _ -> ok + end + end, + case eunit:test($1, [$(EUNIT_OPTS)]) of + ok -> ok; + error -> halt(2) + end, + case "$(COVER)" of + "" -> ok; + _ -> + cover:export("eunit.coverdata") + end, + halt() +endef + +EUNIT_ERL_OPTS += -pa $(TEST_DIR) $(DEPS_DIR)/*/ebin $(APPS_DIR)/*/ebin $(CURDIR)/ebin + +ifdef t +ifeq (,$(findstring :,$(t))) +eunit: test-build + $(gen_verbose) $(call erlang,$(call eunit.erl,['$(t)']),$(EUNIT_ERL_OPTS)) +else +eunit: test-build + $(gen_verbose) $(call erlang,$(call eunit.erl,fun $(t)/0),$(EUNIT_ERL_OPTS)) +endif +else +EUNIT_EBIN_MODS = $(notdir $(basename $(ERL_FILES) $(BEAM_FILES))) +EUNIT_TEST_MODS = $(notdir $(basename $(call core_find,$(TEST_DIR)/,*.erl))) + +EUNIT_MODS = $(foreach mod,$(EUNIT_EBIN_MODS) $(filter-out \ + $(patsubst %,%_tests,$(EUNIT_EBIN_MODS)),$(EUNIT_TEST_MODS)),'$(mod)') + +eunit: test-build $(if $(IS_APP),,apps-eunit) + $(gen_verbose) $(call erlang,$(call eunit.erl,[$(call comma_list,$(EUNIT_MODS))]),$(EUNIT_ERL_OPTS)) + +ifneq ($(ALL_APPS_DIRS),) +apps-eunit: + $(verbose) eunit_retcode=0 ; for app in $(ALL_APPS_DIRS); do $(MAKE) -C $$app eunit IS_APP=1; \ + [ $$? -ne 0 ] && eunit_retcode=1 ; done ; \ + exit $$eunit_retcode +endif +endif + +# Copyright (c) 2013-2016, Loïc Hoguin +# This file is part of erlang.mk and subject to the terms of the ISC License. + +.PHONY: relx-rel relx-relup distclean-relx-rel run + +# Configuration. + +RELX ?= $(ERLANG_MK_TMP)/relx +RELX_CONFIG ?= $(CURDIR)/relx.config + +RELX_URL ?= https://github.com/erlware/relx/releases/download/v3.19.0/relx +RELX_OPTS ?= +RELX_OUTPUT_DIR ?= _rel +RELX_TAR ?= 1 + +ifdef SFX + RELX_TAR = 1 +endif + +ifeq ($(firstword $(RELX_OPTS)),-o) + RELX_OUTPUT_DIR = $(word 2,$(RELX_OPTS)) +else + RELX_OPTS += -o $(RELX_OUTPUT_DIR) +endif + +# Core targets. + +ifeq ($(IS_DEP),) +ifneq ($(wildcard $(RELX_CONFIG)),) +rel:: relx-rel + +relup:: relx-relup +endif +endif + +distclean:: distclean-relx-rel + +# Plugin-specific targets. + +$(RELX): + $(gen_verbose) $(call core_http_get,$(RELX),$(RELX_URL)) + $(verbose) chmod +x $(RELX) + +relx-rel: $(RELX) rel-deps app + $(verbose) $(RELX) -c $(RELX_CONFIG) $(RELX_OPTS) release $(if $(filter 1,$(RELX_TAR)),tar) + +relx-relup: $(RELX) rel-deps app + $(verbose) $(RELX) -c $(RELX_CONFIG) $(RELX_OPTS) release relup $(if $(filter 1,$(RELX_TAR)),tar) + +distclean-relx-rel: + $(gen_verbose) rm -rf $(RELX_OUTPUT_DIR) + +# Run target. + +ifeq ($(wildcard $(RELX_CONFIG)),) +run: +else + +define get_relx_release.erl + {ok, Config} = file:consult("$(call core_native_path,$(RELX_CONFIG))"), + {release, {Name, Vsn0}, _} = lists:keyfind(release, 1, Config), + Vsn = case Vsn0 of + {cmd, Cmd} -> os:cmd(Cmd); + semver -> ""; + {semver, _} -> ""; + VsnStr -> Vsn0 + end, + io:format("~s ~s", [Name, Vsn]), + halt(0). +endef + +RELX_REL := $(shell $(call erlang,$(get_relx_release.erl))) +RELX_REL_NAME := $(word 1,$(RELX_REL)) +RELX_REL_VSN := $(word 2,$(RELX_REL)) + +run: all + $(verbose) $(RELX_OUTPUT_DIR)/$(RELX_REL_NAME)/bin/$(RELX_REL_NAME) console + +help:: + $(verbose) printf "%s\n" "" \ + "Relx targets:" \ + " run Compile the project, build the release and run it" + +endif + +# Copyright (c) 2015-2016, Loïc Hoguin +# Copyright (c) 2014, M Robert Martin +# This file is contributed to erlang.mk and subject to the terms of the ISC License. + +.PHONY: shell + +# Configuration. + +SHELL_ERL ?= erl +SHELL_PATHS ?= $(CURDIR)/ebin $(APPS_DIR)/*/ebin $(DEPS_DIR)/*/ebin +SHELL_OPTS ?= + +ALL_SHELL_DEPS_DIRS = $(addprefix $(DEPS_DIR)/,$(SHELL_DEPS)) + +# Core targets + +help:: + $(verbose) printf "%s\n" "" \ + "Shell targets:" \ + " shell Run an erlang shell with SHELL_OPTS or reasonable default" + +# Plugin-specific targets. + +$(foreach dep,$(SHELL_DEPS),$(eval $(call dep_target,$(dep)))) + +build-shell-deps: $(ALL_SHELL_DEPS_DIRS) + $(verbose) for dep in $(ALL_SHELL_DEPS_DIRS) ; do $(MAKE) -C $$dep ; done + +shell: build-shell-deps + $(gen_verbose) $(SHELL_ERL) -pa $(SHELL_PATHS) $(SHELL_OPTS) + +# Copyright (c) 2017, Jean-Sébastien Pédron +# This file is contributed to erlang.mk and subject to the terms of the ISC License. + +.PHONY: show-ERL_LIBS show-ERLC_OPTS show-TEST_ERLC_OPTS + +show-ERL_LIBS: + @echo $(ERL_LIBS) + +show-ERLC_OPTS: + @$(foreach opt,$(ERLC_OPTS) -pa ebin -I include,echo "$(opt)";) + +show-TEST_ERLC_OPTS: + @$(foreach opt,$(TEST_ERLC_OPTS) -pa ebin -I include,echo "$(opt)";) + +# Copyright (c) 2015-2016, Loïc Hoguin +# This file is part of erlang.mk and subject to the terms of the ISC License. + +ifeq ($(filter triq,$(DEPS) $(TEST_DEPS)),triq) +.PHONY: triq + +# Targets. + +tests:: triq + +define triq_check.erl + code:add_pathsa(["$(call core_native_path,$(CURDIR)/ebin)", "$(call core_native_path,$(DEPS_DIR)/*/ebin)"]), + try + case $(1) of + all -> [true] =:= lists:usort([triq:check(M) || M <- [$(call comma_list,$(3))]]); + module -> triq:check($(2)); + function -> triq:check($(2)) + end + of + true -> halt(0); + _ -> halt(1) + catch error:undef -> + io:format("Undefined property or module~n"), + halt(0) + end. +endef + +ifdef t +ifeq (,$(findstring :,$(t))) +triq: test-build + $(verbose) $(call erlang,$(call triq_check.erl,module,$(t))) +else +triq: test-build + $(verbose) echo Testing $(t)/0 + $(verbose) $(call erlang,$(call triq_check.erl,function,$(t)())) +endif +else +triq: test-build + $(eval MODULES := $(patsubst %,'%',$(sort $(notdir $(basename $(wildcard ebin/*.beam)))))) + $(gen_verbose) $(call erlang,$(call triq_check.erl,all,undefined,$(MODULES))) +endif +endif + +# Copyright (c) 2016, Loïc Hoguin +# Copyright (c) 2015, Erlang Solutions Ltd. +# This file is part of erlang.mk and subject to the terms of the ISC License. + +.PHONY: xref distclean-xref + +# Configuration. + +ifeq ($(XREF_CONFIG),) + XREFR_ARGS := +else + XREFR_ARGS := -c $(XREF_CONFIG) +endif + +XREFR ?= $(CURDIR)/xrefr +export XREFR + +XREFR_URL ?= https://github.com/inaka/xref_runner/releases/download/0.2.2/xrefr + +# Core targets. + +help:: + $(verbose) printf "%s\n" "" \ + "Xref targets:" \ + " xref Run Xrefr using $XREF_CONFIG as config file if defined" + +distclean:: distclean-xref + +# Plugin-specific targets. + +$(XREFR): + $(gen_verbose) $(call core_http_get,$(XREFR),$(XREFR_URL)) + $(verbose) chmod +x $(XREFR) + +xref: deps app $(XREFR) + $(gen_verbose) $(XREFR) $(XREFR_ARGS) + +distclean-xref: + $(gen_verbose) rm -rf $(XREFR) + +# Copyright (c) 2016, Loïc Hoguin +# Copyright (c) 2015, Viktor Söderqvist +# This file is part of erlang.mk and subject to the terms of the ISC License. + +COVER_REPORT_DIR = cover + +# Hook in coverage to ct + +ifdef COVER +ifdef CT_RUN +# All modules in 'ebin' +COVER_MODS = $(notdir $(basename $(call core_ls,ebin/*.beam))) + +test-build:: $(TEST_DIR)/ct.cover.spec + +$(TEST_DIR)/ct.cover.spec: + $(verbose) echo Cover mods: $(COVER_MODS) + $(gen_verbose) printf "%s\n" \ + '{incl_mods,[$(subst $(space),$(comma),$(COVER_MODS))]}.' \ + '{export,"$(CURDIR)/ct.coverdata"}.' > $@ + +CT_RUN += -cover $(TEST_DIR)/ct.cover.spec +endif +endif + +# Core targets + +ifdef COVER +ifneq ($(COVER_REPORT_DIR),) +tests:: + $(verbose) $(MAKE) --no-print-directory cover-report +endif +endif + +clean:: coverdata-clean + +ifneq ($(COVER_REPORT_DIR),) +distclean:: cover-report-clean +endif + +help:: + $(verbose) printf "%s\n" "" \ + "Cover targets:" \ + " cover-report Generate a HTML coverage report from previously collected" \ + " cover data." \ + " all.coverdata Merge {eunit,ct}.coverdata into one coverdata file." \ + "" \ + "If COVER=1 is set, coverage data is generated by the targets eunit and ct. The" \ + "target tests additionally generates a HTML coverage report from the combined" \ + "coverdata files from each of these testing tools. HTML reports can be disabled" \ + "by setting COVER_REPORT_DIR to empty." + +# Plugin specific targets + +COVERDATA = $(filter-out all.coverdata,$(wildcard *.coverdata)) + +.PHONY: coverdata-clean +coverdata-clean: + $(gen_verbose) rm -f *.coverdata ct.cover.spec + +# Merge all coverdata files into one. +all.coverdata: $(COVERDATA) + $(gen_verbose) $(ERL) -eval ' \ + $(foreach f,$(COVERDATA),cover:import("$(f)") == ok orelse halt(1),) \ + cover:export("$@"), halt(0).' + +# These are only defined if COVER_REPORT_DIR is non-empty. Set COVER_REPORT_DIR to +# empty if you want the coverdata files but not the HTML report. +ifneq ($(COVER_REPORT_DIR),) + +.PHONY: cover-report-clean cover-report + +cover-report-clean: + $(gen_verbose) rm -rf $(COVER_REPORT_DIR) + +ifeq ($(COVERDATA),) +cover-report: +else + +# Modules which include eunit.hrl always contain one line without coverage +# because eunit defines test/0 which is never called. We compensate for this. +EUNIT_HRL_MODS = $(subst $(space),$(comma),$(shell \ + grep -e '^\s*-include.*include/eunit\.hrl"' src/*.erl \ + | sed "s/^src\/\(.*\)\.erl:.*/'\1'/" | uniq)) + +define cover_report.erl + $(foreach f,$(COVERDATA),cover:import("$(f)") == ok orelse halt(1),) + Ms = cover:imported_modules(), + [cover:analyse_to_file(M, "$(COVER_REPORT_DIR)/" ++ atom_to_list(M) + ++ ".COVER.html", [html]) || M <- Ms], + Report = [begin {ok, R} = cover:analyse(M, module), R end || M <- Ms], + EunitHrlMods = [$(EUNIT_HRL_MODS)], + Report1 = [{M, {Y, case lists:member(M, EunitHrlMods) of + true -> N - 1; false -> N end}} || {M, {Y, N}} <- Report], + TotalY = lists:sum([Y || {_, {Y, _}} <- Report1]), + TotalN = lists:sum([N || {_, {_, N}} <- Report1]), + Perc = fun(Y, N) -> case Y + N of 0 -> 100; S -> round(100 * Y / S) end end, + TotalPerc = Perc(TotalY, TotalN), + {ok, F} = file:open("$(COVER_REPORT_DIR)/index.html", [write]), + io:format(F, "~n" + "~n" + "Coverage report~n" + "~n", []), + io:format(F, "

Coverage

~n

Total: ~p%

~n", [TotalPerc]), + io:format(F, "~n", []), + [io:format(F, "" + "~n", + [M, M, Perc(Y, N)]) || {M, {Y, N}} <- Report1], + How = "$(subst $(space),$(comma)$(space),$(basename $(COVERDATA)))", + Date = "$(shell date -u "+%Y-%m-%dT%H:%M:%SZ")", + io:format(F, "
ModuleCoverage
~p~p%
~n" + "

Generated using ~s and erlang.mk on ~s.

~n" + "", [How, Date]), + halt(). +endef + +cover-report: + $(gen_verbose) mkdir -p $(COVER_REPORT_DIR) + $(gen_verbose) $(call erlang,$(cover_report.erl)) + +endif +endif # ifneq ($(COVER_REPORT_DIR),) + +# Copyright (c) 2016, Loïc Hoguin +# This file is part of erlang.mk and subject to the terms of the ISC License. + +.PHONY: sfx + +ifdef RELX_REL +ifdef SFX + +# Configuration. + +SFX_ARCHIVE ?= $(RELX_OUTPUT_DIR)/$(RELX_REL_NAME)/$(RELX_REL_NAME)-$(RELX_REL_VSN).tar.gz +SFX_OUTPUT_FILE ?= $(RELX_OUTPUT_DIR)/$(RELX_REL_NAME).run + +# Core targets. + +rel:: sfx + +# Plugin-specific targets. + +define sfx_stub +#!/bin/sh + +TMPDIR=`mktemp -d` +ARCHIVE=`awk '/^__ARCHIVE_BELOW__$$/ {print NR + 1; exit 0;}' $$0` +FILENAME=$$(basename $$0) +REL=$${FILENAME%.*} + +tail -n+$$ARCHIVE $$0 | tar -xzf - -C $$TMPDIR + +$$TMPDIR/bin/$$REL console +RET=$$? + +rm -rf $$TMPDIR + +exit $$RET + +__ARCHIVE_BELOW__ +endef + +sfx: + $(call render_template,sfx_stub,$(SFX_OUTPUT_FILE)) + $(gen_verbose) cat $(SFX_ARCHIVE) >> $(SFX_OUTPUT_FILE) + $(verbose) chmod +x $(SFX_OUTPUT_FILE) + +endif +endif + +# Copyright (c) 2013-2017, Loïc Hoguin +# This file is part of erlang.mk and subject to the terms of the ISC License. + +# 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)))) + +# Copyright (c) 2013-2015, Loïc Hoguin +# Copyright (c) 2015-2016, Jean-Sébastien Pédron +# This file is part of erlang.mk and subject to the terms of the ISC License. + +# Fetch dependencies recursively (without building them). + +.PHONY: fetch-deps fetch-doc-deps fetch-rel-deps fetch-test-deps \ + fetch-shell-deps + +.PHONY: $(ERLANG_MK_RECURSIVE_DEPS_LIST) \ + $(ERLANG_MK_RECURSIVE_DOC_DEPS_LIST) \ + $(ERLANG_MK_RECURSIVE_REL_DEPS_LIST) \ + $(ERLANG_MK_RECURSIVE_TEST_DEPS_LIST) \ + $(ERLANG_MK_RECURSIVE_SHELL_DEPS_LIST) + +fetch-deps: $(ERLANG_MK_RECURSIVE_DEPS_LIST) +fetch-doc-deps: $(ERLANG_MK_RECURSIVE_DOC_DEPS_LIST) +fetch-rel-deps: $(ERLANG_MK_RECURSIVE_REL_DEPS_LIST) +fetch-test-deps: $(ERLANG_MK_RECURSIVE_TEST_DEPS_LIST) +fetch-shell-deps: $(ERLANG_MK_RECURSIVE_SHELL_DEPS_LIST) + +ifneq ($(SKIP_DEPS),) +$(ERLANG_MK_RECURSIVE_DEPS_LIST) \ +$(ERLANG_MK_RECURSIVE_DOC_DEPS_LIST) \ +$(ERLANG_MK_RECURSIVE_REL_DEPS_LIST) \ +$(ERLANG_MK_RECURSIVE_TEST_DEPS_LIST) \ +$(ERLANG_MK_RECURSIVE_SHELL_DEPS_LIST): + $(verbose) :> $@ +else +# By default, we fetch "normal" dependencies. They are also included no +# matter the type of requested dependencies. +# +# $(ALL_DEPS_DIRS) includes $(BUILD_DEPS). + +$(ERLANG_MK_RECURSIVE_DEPS_LIST): $(ALL_DEPS_DIRS) +$(ERLANG_MK_RECURSIVE_DOC_DEPS_LIST): $(ALL_DEPS_DIRS) $(ALL_DOC_DEPS_DIRS) +$(ERLANG_MK_RECURSIVE_REL_DEPS_LIST): $(ALL_DEPS_DIRS) $(ALL_REL_DEPS_DIRS) +$(ERLANG_MK_RECURSIVE_TEST_DEPS_LIST): $(ALL_DEPS_DIRS) $(ALL_TEST_DEPS_DIRS) +$(ERLANG_MK_RECURSIVE_SHELL_DEPS_LIST): $(ALL_DEPS_DIRS) $(ALL_SHELL_DEPS_DIRS) + +# Allow to use fetch-deps and $(DEP_TYPES) to fetch multiple types of +# dependencies with a single target. +ifneq ($(filter doc,$(DEP_TYPES)),) +$(ERLANG_MK_RECURSIVE_DEPS_LIST): $(ALL_DOC_DEPS_DIRS) +endif +ifneq ($(filter rel,$(DEP_TYPES)),) +$(ERLANG_MK_RECURSIVE_DEPS_LIST): $(ALL_REL_DEPS_DIRS) +endif +ifneq ($(filter test,$(DEP_TYPES)),) +$(ERLANG_MK_RECURSIVE_DEPS_LIST): $(ALL_TEST_DEPS_DIRS) +endif +ifneq ($(filter shell,$(DEP_TYPES)),) +$(ERLANG_MK_RECURSIVE_DEPS_LIST): $(ALL_SHELL_DEPS_DIRS) +endif + +ERLANG_MK_RECURSIVE_TMP_LIST := $(abspath $(ERLANG_MK_TMP)/recursive-tmp-deps.log) + +$(ERLANG_MK_RECURSIVE_DEPS_LIST) \ +$(ERLANG_MK_RECURSIVE_DOC_DEPS_LIST) \ +$(ERLANG_MK_RECURSIVE_REL_DEPS_LIST) \ +$(ERLANG_MK_RECURSIVE_TEST_DEPS_LIST) \ +$(ERLANG_MK_RECURSIVE_SHELL_DEPS_LIST): +ifeq ($(IS_APP)$(IS_DEP),) + $(verbose) mkdir -p $(ERLANG_MK_TMP) + $(verbose) rm -f $(ERLANG_MK_RECURSIVE_TMP_LIST) +endif +ifndef IS_APP + $(verbose) for dep in $(ALL_APPS_DIRS) ; do \ + $(MAKE) -C $$dep $@ \ + IS_APP=1 \ + ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST) \ + || exit $$?; \ + done +endif + $(verbose) for dep in $^ ; do \ + if ! grep -qs ^$$dep$$ $(ERLANG_MK_RECURSIVE_TMP_LIST); then \ + echo $$dep >> $(ERLANG_MK_RECURSIVE_TMP_LIST); \ + if grep -qs -E "^[[:blank:]]*include[[:blank:]]+(erlang\.mk|.*/erlang\.mk)$$" \ + $$dep/GNUmakefile $$dep/makefile $$dep/Makefile; then \ + $(MAKE) -C $$dep fetch-deps \ + IS_DEP=1 \ + ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST) \ + || exit $$?; \ + fi \ + fi \ + done +ifeq ($(IS_APP)$(IS_DEP),) + $(verbose) sort < $(ERLANG_MK_RECURSIVE_TMP_LIST) | uniq > $@ + $(verbose) rm $(ERLANG_MK_RECURSIVE_TMP_LIST) +endif +endif # ifneq ($(SKIP_DEPS),) + +# List dependencies recursively. + +.PHONY: list-deps list-doc-deps list-rel-deps list-test-deps \ + list-shell-deps + +list-deps: $(ERLANG_MK_RECURSIVE_DEPS_LIST) +list-doc-deps: $(ERLANG_MK_RECURSIVE_DOC_DEPS_LIST) +list-rel-deps: $(ERLANG_MK_RECURSIVE_REL_DEPS_LIST) +list-test-deps: $(ERLANG_MK_RECURSIVE_TEST_DEPS_LIST) +list-shell-deps: $(ERLANG_MK_RECURSIVE_SHELL_DEPS_LIST) + +list-deps list-doc-deps list-rel-deps list-test-deps list-shell-deps: + $(verbose) cat $^ diff --git a/lz4.d b/lz4.d new file mode 100644 index 0000000..004e985 --- /dev/null +++ b/lz4.d @@ -0,0 +1,2 @@ + +COMPILE_FIRST += diff --git a/priv/lz4_nif.so b/priv/lz4_nif.so new file mode 100755 index 0000000000000000000000000000000000000000..08973fadd5667f0c81cc931b3c6883a24c9d9bdb GIT binary patch literal 104132 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 U+}yEslMvz?UV$s$1jd>E14|u literal 0 HcmV?d00001 diff --git a/rebar.config b/rebar.config index c849ed0..77e464a 100644 --- a/rebar.config +++ b/rebar.config @@ -9,3 +9,10 @@ {erl_opts, [debug_info, {parse_transform, lager_transform}, {parse_transform, eqc_cover}]}, {plugins, [rebar_eqc]}]} ]}. + +{pre_hooks, [ + {"(linux|darwin|solaris|win32)", clean, "make clean"}, + {"(freebsd|netbsd|openbsd)", clean, "gmake clean"}, + {"(linux|darwin|solaris|win32)", compile, "make"}, + {"(freebsd|netbsd|openbsd)", compile, "gmake"} +]}. \ No newline at end of file diff --git a/src/leveled_cdb.erl b/src/leveled_cdb.erl index 73959b1..2e286ae 100644 --- a/src/leveled_cdb.erl +++ b/src/leveled_cdb.erl @@ -1385,14 +1385,13 @@ perform_write_hash_tables(Handle, HashTreeBin, StartPos) -> %% The List passed in should be made up of {Index, Position, Count} tuples write_top_index_table(Handle, BasePos, IndexList) -> FnWriteIndex = fun({_Index, Pos, Count}, {AccBin, CurrPos}) -> - case Count == 0 of - true -> - PosLE = endian_flip(CurrPos), - NextPos = CurrPos; - false -> - PosLE = endian_flip(Pos), - NextPos = Pos + (Count * ?DWORD_SIZE) - end, + {PosLE, NextPos} = + case Count == 0 of + true -> + {endian_flip(CurrPos), CurrPos}; + false -> + {endian_flip(Pos), Pos + (Count * ?DWORD_SIZE)} + end, CountLE = endian_flip(Count), {<>, NextPos} end, diff --git a/src/lz4_nif.erl b/src/lz4_nif.erl new file mode 100644 index 0000000..c783cd1 --- /dev/null +++ b/src/lz4_nif.erl @@ -0,0 +1,64 @@ +%% Copyright (c) 2017-Present Pivotal Software, Inc. All rights reserved. +%% +%% This package, the LZ4 binding for Erlang, is double-licensed under the Mozilla +%% Public License 1.1 ("MPL") and the Apache License version 2 +%% ("ASL"). For the MPL, please see LICENSE-MPL-RabbitMQ. For the ASL, +%% please see LICENSE-APACHE2. +%% +%% This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, +%% either express or implied. See the LICENSE file for specific language governing +%% rights and limitations of this software. +%% +%% If you have any questions regarding licensing, please contact us at +%% info@rabbitmq.com. + +-module(lz4_nif). + +%% lz4f. +-export([lz4f_compress_frame/2]). +-export([lz4f_create_compression_context/0]). +-export([lz4f_compress_begin/2]). +-export([lz4f_compress_update/2]). +-export([lz4f_flush/1]). +-export([lz4f_compress_end/1]). +-export([lz4f_create_decompression_context/0]). +-export([lz4f_get_frame_info/2]). +-export([lz4f_decompress/2]). + +-on_load(on_load/0). +on_load() -> + case code:priv_dir(leveled) of + {error, _} -> + {error, {load_failed, "Could not determine the leveled priv/ directory."}}; + Path -> + erlang:load_nif(filename:join(Path, atom_to_list(?MODULE)), 0) + end. + +%% lz4f. + +lz4f_compress_frame(_, _) -> + erlang:nif_error({not_loaded, ?MODULE}). + +lz4f_create_compression_context() -> + erlang:nif_error({not_loaded, ?MODULE}). + +lz4f_compress_begin(_, _) -> + erlang:nif_error({not_loaded, ?MODULE}). + +lz4f_compress_update(_, _) -> + erlang:nif_error({not_loaded, ?MODULE}). + +lz4f_flush(_) -> + erlang:nif_error({not_loaded, ?MODULE}). + +lz4f_compress_end(_) -> + erlang:nif_error({not_loaded, ?MODULE}). + +lz4f_create_decompression_context() -> + erlang:nif_error({not_loaded, ?MODULE}). + +lz4f_get_frame_info(_, _) -> + erlang:nif_error({not_loaded, ?MODULE}). + +lz4f_decompress(_, _) -> + erlang:nif_error({not_loaded, ?MODULE}). diff --git a/src/lz4f.erl b/src/lz4f.erl new file mode 100644 index 0000000..80a4a7d --- /dev/null +++ b/src/lz4f.erl @@ -0,0 +1,107 @@ +%% Copyright (c) 2017-Present Pivotal Software, Inc. All rights reserved. +%% +%% This package, the LZ4 binding for Erlang, is double-licensed under the Mozilla +%% Public License 1.1 ("MPL") and the Apache License version 2 +%% ("ASL"). For the MPL, please see LICENSE-MPL-RabbitMQ. For the ASL, +%% please see LICENSE-APACHE2. +%% +%% This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, +%% either express or implied. See the LICENSE file for specific language governing +%% rights and limitations of this software. +%% +%% If you have any questions regarding licensing, please contact us at +%% info@rabbitmq.com. + +-module(lz4f). + +%% Simple compression. +-export([compress_frame/1]). +-export([compress_frame/2]). + +%% Advanced compression. +-export([create_compression_context/0]). +-export([compress_begin/1]). +-export([compress_begin/2]). +-export([compress_update/2]). +-export([flush/1]). +-export([compress_end/1]). + +%% Decompression. +-export([create_decompression_context/0]). +-export([get_frame_info/2]). +-export([decompress/1]). +-export([decompress/2]). + +-type block_size_id() :: default | max64KB | max256KB | max1MB | max4MB. +-type block_mode() :: linked | independent. +-type frame_type() :: frame | skippable_frame. + +-type frame_info() :: #{ + block_size_id => block_size_id(), + block_mode => block_mode(), + content_checksum => boolean(), + frame_type => frame_type(), + content_size => non_neg_integer() +}. + +-type opts() :: #{ + frame_info => frame_info(), + compression_level => 0..16, + auto_flush => boolean() +}. + +-opaque cctx() :: <<>>. %% Resource. +-export_type([cctx/0]). + +-opaque dctx() :: <<>>. %% Resource. +-export_type([dctx/0]). + +-spec compress_frame(binary()) -> binary(). +compress_frame(Data) -> + lz4_nif:lz4f_compress_frame(Data, #{}). + +-spec compress_frame(binary(), opts()) -> binary(). +compress_frame(Data, Opts) -> + lz4_nif:lz4f_compress_frame(Data, Opts). + +-spec create_compression_context() -> cctx(). +create_compression_context() -> + lz4_nif:lz4f_create_compression_context(). + +-spec compress_begin(cctx()) -> binary(). +compress_begin(Cctx) -> + compress_begin(Cctx, #{}). + +-spec compress_begin(cctx(), opts()) -> binary(). +compress_begin(Cctx, Opts) -> + lz4_nif:lz4f_compress_begin(Cctx, Opts). + +-spec compress_update(cctx(), binary()) -> binary(). +compress_update(Cctx, Data) -> + lz4_nif:lz4f_compress_update(Cctx, Data). + +-spec flush(cctx()) -> binary(). +flush(Cctx) -> + lz4_nif:lz4f_flush(Cctx). + +-spec compress_end(cctx()) -> binary(). +compress_end(Cctx) -> + lz4_nif:lz4f_compress_end(Cctx). + +-spec create_decompression_context() -> dctx(). +create_decompression_context() -> + lz4_nif:lz4f_create_decompression_context(). + +-spec get_frame_info(dctx(), binary()) -> {ok, frame_info(), non_neg_integer()}. +get_frame_info(Dctx, Data) -> + lz4_nif:lz4f_get_frame_info(Dctx, Data). + +-spec decompress(binary()) -> iolist(). +decompress(Data) -> + decompress(create_decompression_context(), Data). + +-spec decompress(dctx(), binary()) -> iolist(). +decompress(Dctx, Data) -> + lz4_nif:lz4f_decompress(Dctx, Data). + +%% @todo LZ4F_resetDecompressionContext diff --git a/test/lz4f_SUITE.erl b/test/lz4f_SUITE.erl new file mode 100644 index 0000000..2a9cfb5 --- /dev/null +++ b/test/lz4f_SUITE.erl @@ -0,0 +1,82 @@ +-module(lz4f_SUITE). +-compile(export_all). + +-import(ct_helper, [config/2]). +-import(ct_helper, [doc/1]). + +%% ct. + +all() -> + [{group, all}]. + +groups() -> + [{all, [parallel], ct_helper:all(?MODULE)}]. + +init_per_suite(Config) -> + [{test_file, config(data_dir, Config) ++ "/pdf_reference_1-7.pdf"}|Config]. + +end_per_suite(_) -> + ok. + +%% Tests. + +compress_frame1(Config) -> + doc("Use lz4f:compress_frame/1 and then decompress back."), + {ok, File} = file:read_file(config(test_file, Config)), + Compressed = lz4f:compress_frame(File), + File = iolist_to_binary(lz4f:decompress(Compressed)), + ok. + +compress_update(Config) -> + doc("Stream compress with lz4f:compress_update/2 and then decompress back."), + {ok, File} = file:read_file(config(test_file, Config)), + Chunks = do_slice(File), + Ctx = lz4f:create_compression_context(), + Begin = lz4f:compress_begin(Ctx), + CompressedChunks = [lz4f:compress_update(Ctx, C) || C <- Chunks], + End = lz4f:compress_end(Ctx), + Compressed = iolist_to_binary([Begin, CompressedChunks, End]), + File = iolist_to_binary(lz4f:decompress(Compressed)), + ok. + +compress_flush(Config) -> + doc("Stream compress with some lz4f:flush/1 and then decompress back."), + {ok, File} = file:read_file(config(test_file, Config)), + Chunks = do_insert_flush(do_slice(File)), + Ctx = lz4f:create_compression_context(), + Begin = lz4f:compress_begin(Ctx), + CompressedChunks = [case C of + flush -> lz4f:flush(Ctx); + _ -> lz4f:compress_update(Ctx, C) + end || C <- Chunks], + End = lz4f:compress_end(Ctx), + Compressed = iolist_to_binary([Begin, CompressedChunks, End]), + File = iolist_to_binary(lz4f:decompress(Compressed)), + ok. + +decompress(Config) -> + doc("Compress and then stream decompress with lz4f:decompress/2."), + {ok, File} = file:read_file(config(test_file, Config)), + Compressed = lz4f:compress_frame(File), + Chunks = do_slice(Compressed), + Ctx = lz4f:create_decompression_context(), + DecompressedChunks = [lz4f:decompress(Ctx, C) || C <- Chunks], + File = iolist_to_binary(DecompressedChunks), + ok. + +%% Internal. + +do_insert_flush(L) -> + do_insert_flush(L, 0). + +do_insert_flush([], _) -> + []; +do_insert_flush(L, 5) -> + [flush|do_insert_flush(L, 0)]; +do_insert_flush([H|T], N) -> + [H|do_insert_flush(T, N + 1)]. + +do_slice(<>) -> + [Bin|do_slice(R)]; +do_slice(Bin) -> + [Bin]. From 4fbb770a8caa9b10593893bb0233e4b2439dbb19 Mon Sep 17 00:00:00 2001 From: Martin Sumner Date: Fri, 3 Nov 2017 11:47:25 +0000 Subject: [PATCH 03/13] Revert "Failed attempt to hack in LZ4" This reverts commit 912920a53cf6adf288679b6fce8d7dabdf368acf. --- .erlang.mk/deps.log | 2 - .erlang.mk/last-makefile-change | 0 Makefile | 41 - c_src/env.mk | 3 - c_src/lz4.c | 47 - c_src/lz4.o | Bin 3272 -> 0 bytes c_src/lz4_erlang.h | 58 - c_src/lz4f.c | 286 -- c_src/lz4f.o | Bin 5868 -> 0 bytes ebin/lz4.app | 8 - erlang.mk | 6889 ------------------------------- lz4.d | 2 - priv/lz4_nif.so | Bin 104132 -> 0 bytes rebar.config | 7 - src/leveled_cdb.erl | 15 +- src/lz4_nif.erl | 64 - src/lz4f.erl | 107 - test/lz4f_SUITE.erl | 82 - 18 files changed, 8 insertions(+), 7603 deletions(-) delete mode 100644 .erlang.mk/deps.log delete mode 100644 .erlang.mk/last-makefile-change delete mode 100644 Makefile delete mode 100644 c_src/env.mk delete mode 100644 c_src/lz4.c delete mode 100644 c_src/lz4.o delete mode 100644 c_src/lz4_erlang.h delete mode 100644 c_src/lz4f.c delete mode 100644 c_src/lz4f.o delete mode 100644 ebin/lz4.app delete mode 100644 erlang.mk delete mode 100644 lz4.d delete mode 100755 priv/lz4_nif.so delete mode 100644 src/lz4_nif.erl delete mode 100644 src/lz4f.erl delete mode 100644 test/lz4f_SUITE.erl diff --git a/.erlang.mk/deps.log b/.erlang.mk/deps.log deleted file mode 100644 index 4d0d282..0000000 --- a/.erlang.mk/deps.log +++ /dev/null @@ -1,2 +0,0 @@ -/Users/martinsumner/dbroot/leveled/deps/lz4_src -/Users/martinsumner/dbroot/leveled/deps/nif_helpers diff --git a/.erlang.mk/last-makefile-change b/.erlang.mk/last-makefile-change deleted file mode 100644 index e69de29..0000000 diff --git a/Makefile b/Makefile deleted file mode 100644 index 340db88..0000000 --- a/Makefile +++ /dev/null @@ -1,41 +0,0 @@ -PROJECT = lz4 -PROJECT_DESCRIPTION = New project -PROJECT_VERSION = 0.1.0 - -BUILD_DEPS = lz4_src nif_helpers -dep_lz4_src = git https://github.com/lz4/lz4 v1.8.0 -dep_nif_helpers = git https://github.com/ninenines/nif_helpers master -DEP_PLUGINS = nif_helpers - -C_SRC_OUTPUT = $(CURDIR)/priv/lz4_nif - -TEST_DEPS = ct_helper -dep_ct_helper = git https://github.com/extend/ct_helper master - -include erlang.mk - -CFLAGS += -I $(DEPS_DIR)/lz4_src/lib -# This is required in order to build a liblz4.a that we can -# include in our shared library. -export CPPFLAGS += -shared -fPIC -LDLIBS += $(DEPS_DIR)/lz4_src/lib/liblz4.a - --include c_src/env.mk - -cppcheck: - cppcheck -f --quiet --error-exitcode=2 --enable=all --inconclusive --std=posix \ - -Ideps/lz4_src/lib/ -Ideps/nif_helpers/ -I$(ERTS_INCLUDE_DIR) c_src/ - -scan-build: - make clean - scan-build make - -# Download a large file for use in compression tests. - -PDF_REFERENCE = http://www.adobe.com/content/dam/Adobe/en/devnet/acrobat/pdfs/pdf_reference_1-7.pdf - -test-build:: $(TEST_DIR)/lz4f_SUITE_data/pdf_reference_1-7.pdf - -$(TEST_DIR)/lz4f_SUITE_data/pdf_reference_1-7.pdf: - $(verbose) mkdir -p $(TEST_DIR)/lz4f_SUITE_data/ - $(gen_verbose) $(call core_http_get,$@,$(PDF_REFERENCE)) diff --git a/c_src/env.mk b/c_src/env.mk deleted file mode 100644 index 429d36e..0000000 --- a/c_src/env.mk +++ /dev/null @@ -1,3 +0,0 @@ -ERTS_INCLUDE_DIR ?= /Users/martinsumner/erlang/18.3/erts-7.3/include/ -ERL_INTERFACE_INCLUDE_DIR ?= /Users/martinsumner/erlang/18.3/lib/erl_interface-3.8.2/include -ERL_INTERFACE_LIB_DIR ?= /Users/martinsumner/erlang/18.3/lib/erl_interface-3.8.2/lib diff --git a/c_src/lz4.c b/c_src/lz4.c deleted file mode 100644 index 80b0948..0000000 --- a/c_src/lz4.c +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright (c) 2017-Present Pivotal Software, Inc. All rights reserved. -// -// This package, the LZ4 binding for Erlang, is double-licensed under the Mozilla -// Public License 1.1 ("MPL") and the Apache License version 2 -// ("ASL"). For the MPL, please see LICENSE-MPL-RabbitMQ. For the ASL, -// please see LICENSE-APACHE2. -// -// This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, -// either express or implied. See the LICENSE file for specific language governing -// rights and limitations of this software. -// -// If you have any questions regarding licensing, please contact us at -// info@rabbitmq.com. - -#include "lz4_erlang.h" - -NIF_ATOMS(NIF_ATOM_DECL) -NIF_RESOURCES(NIF_RES_DECL) - -int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info); -int upgrade(ErlNifEnv* env, void** priv_data, void** old_priv_data, ERL_NIF_TERM load_info); -void unload(ErlNifEnv* env, void* priv_data); - -int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) -{ - NIF_ATOMS(NIF_ATOM_INIT) - NIF_RESOURCES(NIF_RES_INIT) - - return 0; -} - -int upgrade(ErlNifEnv* env, void** priv_data, void** old_priv_data, ERL_NIF_TERM load_info) -{ - *priv_data = *old_priv_data; - - return 0; -} - -void unload(ErlNifEnv* env, void* priv_data) -{ -} - -static ErlNifFunc nif_funcs[] = { - NIF_FUNCTIONS(NIF_FUNCTION_ARRAY) -}; - -ERL_NIF_INIT(lz4_nif, nif_funcs, load, NULL, upgrade, unload) diff --git a/c_src/lz4.o b/c_src/lz4.o deleted file mode 100644 index 913c5e50ba277e0092c05a32747899d726b18742..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3272 zcmb7GUuauZ7(eN{v}$`BbyX)0%TSnul@ulpB$&E(L>U^kh=0rJU2>9!CHID#8|?-{ zp*jhpQo$$jVZQk1t)LG@_Yl~8@IfjFKJ3MU2*WAV7a5ycf8ROZP11W4>OOMvo!|HW z+}j{v8q`wnyL(V-Vxg0RAR0K>INc5$;1b$pO@2o9m1bQ_zT$5+tRs7JV}} zI5+!Bu7iWwC=~&scogb$1Q{p6pjaXnkd%41T6LWco|(=~2S|vZjzmW!XpI4JngF7{ zeBJlTPKhN_?yN7U>v4AczB?Fv!jDMGg6-Sw@QA;&zBD`3f zw)|vfW&E@MWxRYdw!d{_@A@_oW(B#;o?@%y7>&sjm$TZ_qEvQxrgYVR@ z=)`kZ+_R$KI!Y+Vt*WY!PPr)k1y9)p=_y}|7oIyIPtBf|xmUBZhfcnB^n}dk{lzDG z$O~FtIe(O5h%?HrPQGb7=L^@WgLIR%!OECm&m5Xn=*QuWT3WvA#Gua3EXs+~XDf3#!g_#9%p-Vv$b z{IZwFnBw-L3nBZk&8NHgf4z%-gZ2H=T-!zedp}1m)Z&HpTi?N5^uc<6#3UwSk!+hm z9>XL0E!J~iiRg6`gN^V789|=$k6aAqR{v25sbClbP~6>sXk59 zR^r0I@zMCR%lZpTb~6|Ye;Yf?SP2o>F($hTMwack-kx?J!tNN`aorKSA+yp0-@q29 z{lHh?V?rGH1ROr8kHAsj`1|04(7(reAAAJ*D(my$_|~T007vadax8xy9Ooc)oaIlm zdPV+wBV7$us1`Ni?i0KP(jaf$d$6y=WPw4p(-a<^G zU)X=kKyjJzCgVSW@!Lc75`7TDhePF)c&jXStV`X z#chE8Av9M54RBwGL@~`bB5nugMs1?IHRaF)cN<<_N&j3;aT - -// List of atoms used by this NIF. -// -// @todo We don't use threads so perhaps we should make nif_helpers -// better by splitting concerns into threads/not and have nif_helpers -// decide when to create the _nif_thread_ret atom or not. - -#define NIF_ATOMS(A) \ - A(ok) \ - A(done) \ - A(enomem) \ - A(_nif_thread_ret_) - -// List of resources used by this NIF. - -#define NIF_RESOURCES(R) \ - R(LZ4F_cctx) \ - R(LZ4F_dctx) - -// List of functions defined in this NIF. - -#define NIF_FUNCTION_NAME(f) lz4_erlang_ ## f -#define NIF_FUNCTIONS(F) \ - F(lz4f_compress_frame, 2) \ - F(lz4f_create_compression_context, 0) \ - F(lz4f_compress_begin, 2) \ - F(lz4f_compress_update, 2) \ - F(lz4f_flush, 1) \ - F(lz4f_compress_end, 1) \ - F(lz4f_create_decompression_context, 0) \ - F(lz4f_get_frame_info, 2) \ - F(lz4f_decompress, 2) - -#include "nif_helpers.h" - -NIF_ATOMS(NIF_ATOM_H_DECL) -NIF_RESOURCES(NIF_RES_H_DECL) -NIF_FUNCTIONS(NIF_FUNCTION_H_DECL) - -#endif diff --git a/c_src/lz4f.c b/c_src/lz4f.c deleted file mode 100644 index c4dd41e..0000000 --- a/c_src/lz4f.c +++ /dev/null @@ -1,286 +0,0 @@ -// Copyright (c) 2017-Present Pivotal Software, Inc. All rights reserved. -// -// This package, the LZ4 binding for Erlang, is double-licensed under the Mozilla -// Public License 1.1 ("MPL") and the Apache License version 2 -// ("ASL"). For the MPL, please see LICENSE-MPL-RabbitMQ. For the ASL, -// please see LICENSE-APACHE2. -// -// This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, -// either express or implied. See the LICENSE file for specific language governing -// rights and limitations of this software. -// -// If you have any questions regarding licensing, please contact us at -// info@rabbitmq.com. - -#include "lz4_erlang.h" -#include - -void dtor_LZ4F_cctx(ErlNifEnv* env, void* obj) -{ - LZ4F_freeCompressionContext(NIF_RES_GET(LZ4F_cctx, obj)); -} - -void dtor_LZ4F_dctx(ErlNifEnv* env, void* obj) -{ - LZ4F_freeDecompressionContext(NIF_RES_GET(LZ4F_dctx, obj)); -} - -NIF_FUNCTION(lz4f_compress_frame) -{ - LZ4F_preferences_t preferences; - size_t dstCapacity, dstSize; - ErlNifBinary srcBin, dstBin; - - BADARG_IF(!enif_inspect_binary(env, argv[0], &srcBin)); - - memset(&preferences, 0, sizeof(preferences)); - - // @todo prefs - - dstCapacity = LZ4F_compressFrameBound(srcBin.size, &preferences); - - if (!enif_alloc_binary(dstCapacity, &dstBin)) - return enif_raise_exception(env, atom_enomem); - - dstSize = LZ4F_compressFrame(dstBin.data, dstCapacity, srcBin.data, srcBin.size, &preferences); - - if (LZ4F_isError(dstSize)) { - enif_release_binary(&dstBin); - return enif_raise_exception(env, enif_make_atom(env, LZ4F_getErrorName(dstSize))); - } - - if (!enif_realloc_binary(&dstBin, dstSize)) { - enif_release_binary(&dstBin); - return enif_raise_exception(env, atom_enomem); - } - - return enif_make_binary(env, &dstBin); -} - -NIF_FUNCTION(lz4f_create_compression_context) -{ - LZ4F_cctx* cctx; - LZ4F_errorCode_t result; - ERL_NIF_TERM term; - - result = LZ4F_createCompressionContext(&cctx, LZ4F_VERSION); - - if (LZ4F_isError(result)) - return enif_raise_exception(env, enif_make_atom(env, LZ4F_getErrorName(result))); - - NIF_RES_TO_TERM(LZ4F_cctx, cctx, term); - - return term; -} - -NIF_FUNCTION(lz4f_compress_begin) -{ - void* cctx_res; - LZ4F_preferences_t preferences; - size_t dstSize; - ErlNifBinary dstBin; - - BADARG_IF(!enif_get_resource(env, argv[0], res_LZ4F_cctx, &cctx_res)); - - memset(&preferences, 0, sizeof(preferences)); - - // @todo prefs - - if (!enif_alloc_binary(LZ4F_HEADER_SIZE_MAX, &dstBin)) - return enif_raise_exception(env, atom_enomem); - - dstSize = LZ4F_compressBegin(NIF_RES_GET(LZ4F_cctx, cctx_res), - dstBin.data, LZ4F_HEADER_SIZE_MAX, &preferences); - - if (LZ4F_isError(dstSize)) { - enif_release_binary(&dstBin); - return enif_raise_exception(env, enif_make_atom(env, LZ4F_getErrorName(dstSize))); - } - - if (!enif_realloc_binary(&dstBin, dstSize)) { - enif_release_binary(&dstBin); - return enif_raise_exception(env, atom_enomem); - } - - return enif_make_binary(env, &dstBin); -} - -NIF_FUNCTION(lz4f_compress_update) -{ - void* cctx_res; - size_t dstCapacity, dstSize; - ErlNifBinary srcBin, dstBin; - - BADARG_IF(!enif_get_resource(env, argv[0], res_LZ4F_cctx, &cctx_res)); - BADARG_IF(!enif_inspect_binary(env, argv[1], &srcBin)); - - // @todo We pass NULL because we don't currently keep the preferences - // setup when the user began the compression. It might be done later - // as an optimization. - dstCapacity = LZ4F_compressBound(srcBin.size, NULL); - - if (!enif_alloc_binary(dstCapacity, &dstBin)) - return enif_raise_exception(env, atom_enomem); - - // We pass NULL because we can't guarantee that the source binary - // data will remain for future calls. It may be garbage collected. - dstSize = LZ4F_compressUpdate(NIF_RES_GET(LZ4F_cctx, cctx_res), - dstBin.data, dstCapacity, srcBin.data, srcBin.size, NULL); - - if (LZ4F_isError(dstSize)) { - enif_release_binary(&dstBin); - return enif_raise_exception(env, enif_make_atom(env, LZ4F_getErrorName(dstSize))); - } - - if (!enif_realloc_binary(&dstBin, dstSize)) { - enif_release_binary(&dstBin); - return enif_raise_exception(env, atom_enomem); - } - - return enif_make_binary(env, &dstBin); -} - -NIF_FUNCTION(lz4f_flush) -{ - void* cctx_res; - size_t dstCapacity, dstSize; - ErlNifBinary dstBin; - - BADARG_IF(!enif_get_resource(env, argv[0], res_LZ4F_cctx, &cctx_res)); - - // We pass 0 to get the upper bound for this operation. - // - // @todo We pass NULL because we don't currently keep the preferences - // setup when the user began the compression. It might be done later - // as an optimization. - dstCapacity = LZ4F_compressBound(0, NULL); - - if (!enif_alloc_binary(dstCapacity, &dstBin)) - return enif_raise_exception(env, atom_enomem); - - // We pass NULL because we can't guarantee that the source binary - // data will remain for future calls. It may be garbage collected. - dstSize = LZ4F_flush(NIF_RES_GET(LZ4F_cctx, cctx_res), - dstBin.data, dstCapacity, NULL); - - if (LZ4F_isError(dstSize)) { - enif_release_binary(&dstBin); - return enif_raise_exception(env, enif_make_atom(env, LZ4F_getErrorName(dstSize))); - } - - if (!enif_realloc_binary(&dstBin, dstSize)) { - enif_release_binary(&dstBin); - return enif_raise_exception(env, atom_enomem); - } - - return enif_make_binary(env, &dstBin); -} - -NIF_FUNCTION(lz4f_compress_end) -{ - void* cctx_res; - size_t dstCapacity, dstSize; - ErlNifBinary dstBin; - - BADARG_IF(!enif_get_resource(env, argv[0], res_LZ4F_cctx, &cctx_res)); - - // We pass 0 to get the upper bound for this operation. - // - // @todo We pass NULL because we don't currently keep the preferences - // setup when the user began the compression. It might be done later - // as an optimization. - dstCapacity = LZ4F_compressBound(0, NULL); - - if (!enif_alloc_binary(dstCapacity, &dstBin)) - return enif_raise_exception(env, atom_enomem); - - // We pass NULL because we can't guarantee that the source binary - // data will remain for future calls. It may be garbage collected. - dstSize = LZ4F_compressEnd(NIF_RES_GET(LZ4F_cctx, cctx_res), - dstBin.data, dstCapacity, NULL); - - if (LZ4F_isError(dstSize)) { - enif_release_binary(&dstBin); - return enif_raise_exception(env, enif_make_atom(env, LZ4F_getErrorName(dstSize))); - } - - if (!enif_realloc_binary(&dstBin, dstSize)) { - enif_release_binary(&dstBin); - return enif_raise_exception(env, atom_enomem); - } - - return enif_make_binary(env, &dstBin); -} - -NIF_FUNCTION(lz4f_create_decompression_context) -{ - LZ4F_dctx* dctx; - LZ4F_errorCode_t result; - ERL_NIF_TERM term; - - result = LZ4F_createDecompressionContext(&dctx, LZ4F_VERSION); - - if (LZ4F_isError(result)) - return enif_raise_exception(env, enif_make_atom(env, LZ4F_getErrorName(result))); - - NIF_RES_TO_TERM(LZ4F_dctx, dctx, term); - - return term; -} - -NIF_FUNCTION(lz4f_get_frame_info) -{ - // @todo - - return atom_ok; -} - -NIF_FUNCTION(lz4f_decompress) -{ - void* dctx_res; - ERL_NIF_TERM head, reversed; - size_t dstSize, srcRead, srcSize; - ErlNifBinary srcBin, dstBin; - - BADARG_IF(!enif_get_resource(env, argv[0], res_LZ4F_dctx, &dctx_res)); - BADARG_IF(!enif_inspect_binary(env, argv[1], &srcBin)); - - srcRead = 0; - srcSize = srcBin.size; - - head = enif_make_list(env, 0); - - while (srcSize) { - dstSize = 65536; // Arbitrary maximum size of chunk. - - if (!enif_alloc_binary(dstSize, &dstBin)) { - return enif_raise_exception(env, atom_enomem); - } - - LZ4F_decompress(NIF_RES_GET(LZ4F_dctx, dctx_res), - dstBin.data, &dstSize, - srcBin.data + srcRead, &srcSize, - NULL); - - if (LZ4F_isError(dstSize)) { - enif_release_binary(&dstBin); - return enif_raise_exception(env, atom_enomem); - } - - if (!enif_realloc_binary(&dstBin, dstSize)) { - enif_release_binary(&dstBin); - return enif_raise_exception(env, atom_enomem); - } - - head = enif_make_list_cell(env, enif_make_binary(env, &dstBin), head); - - srcRead += srcSize; - srcSize = srcBin.size - srcRead; - } - - // We don't check the return value because no error can occur - // according to the function's source code. - enif_make_reverse_list(env, head, &reversed); - - return reversed; -} diff --git a/c_src/lz4f.o b/c_src/lz4f.o deleted file mode 100644 index be8228e18fa3f82e3e7c17744bac147715ec54fa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5868 zcmds*e{5S<6~|xNq)lm7oAG0evDIa5SqnzZ)`?Kjsc!4+Wpbu%ZPwB)yt?*tW7V-U z$7X4#pcE%XPo77BO)C|Gpmoxa_J=2as35voO0>EXKTl?v&g0@1Nw4d3tm z;UCXQhr}Nsp7Q*8_nvdlIrp4<-+Ot#eCzeA%MGJxsevDy%{Ys^@ZrR9BhC)C7deXW zlY(ibqua1)@T6THmX)!GGV)rscL(?GuD12n5C`F)J;pN9W*A9!G#ev?J}fJe>K_P4 zGFCSEXgnFUtk6y^Bek9VDlT({jODV&Fjh*%CAIpNW%pUJbhzJ^y_QuU-%P9QpHcRn z!@Tv}D1psBsUd&Y0$yIW$pq2aM{t_x{1n*w0mC>C1B~T9oV5r0c&@4K{5P+AOy`1` zzpM98)c3457HS6`++`lUGy;V#=c<{%-7NGJFH(Q3jJ?7tO*C*(FAp&ZJq$PMgl}m4VD-P?<6F-9;!a3AMh7@mDG3b~46vSuE)>T4NEO;B2S?sKO0*YP52Ed*W888aW8?Q)iP zow>Uey#@?~oU3|N$kCabF>_O`h%8|BPR2Z|N2x30Lw$0W_N(G(A6~Q2eNk9J2C}k5 zMU-fQr9yp59FD}4U&7Lou|V-Kg1KnsJIp+)ROlZ6Nkz!gRf|#nkBU*&`JQ7j%5ttc z@MtCR7r#GgrDk~M`P`JL%RVmFqnQ1sYt=j2KCdPycU)PQ4xY0$C2l`!IxB6?o+C7gKLZ>4dZ>P=V{2pO-mc(CDn6bk?}?KwsUon@kRAA!l>&h4wYZS2(0oRTe9OiWmf3zgx=*V@2!`B-i*>?J;b5y-IHs60=c5!Cvg^) zU(Q<`TCX$L@^vn8)L+jmLwRawZ%Sw-R5{K67s@iMAyvuJ&d#p#D zUX8uY&>MN{z4ac3UdP)~?^^R1gGkZ+Gv95Whu(3e$9mSq*PUDb__Bh?4f}1p z@2@uu|2`1^Yi!*G-iEdb`oJyVI?xN=3N8WHfPY6}KLP#`#Cx^>RS>T>{ujWTz#RB- zFbduRc7Zp8TftAl&kf*5!GB=gZb1K^LDb6sa}evvUjnK318_as-vd{J-v)00zYfyx zSHSDRJs|Z$AmZ3*0TR+Am?)$#J@uR zBFK511UJIZqaghzK>D>o`rQZ8?>!*>ZUgCe6G*@7K>EEIq`nuV-xd&i&3_4NoPK`? z((g1#zf&Oneg~xACqeoxfVA^vkbcu3{q}+MyBDP2ZjgS1AoV*y`t^fYQ~s48{l0_y z75%;rvVOk?>HjQ9|Hnc4FM#xa45YosK>9xf(tjAF|NBAu?*!@pE|B`|ApNfgSAr`* z`k!r*{$2s;?^%%R+a^m8vrKifgt{|xwka3x4R17zH98ZwT* zfNRkI8pyc0uYKSYNP9m9Y3~J)ai0Wf_nY7<@CeAb4}tg>*Z*0NdAkw(Ft`Hz5V#F~ z@wd#U$OhZ9kdr0F3B?h`{fgTZeTsk>_YKek1Nz9Qw!TkXkGxV|Qaay9o!K78dgF0P z$$g?_7(Y^N{oFc-e(L+w_-9mGKX<;1ej#5b4II_u;fKd>m0UlEZiif%67plvt8Jfi zwcA|n6R!4eT=urYSgpNx;ia~H+9l_Du9fqiT-(0I)%Lmk|H37I&}DDP)qcPg&m%5- zcevV&r8XY!quTaKR~z@vI{DXJ@kLzvy)J);UHb31+FM=rvBvBCb-L`0y5zR2ZCG|P z9<$=f!2uf|l@7#{;q+nEnYI&lc+jrz><=Gu^`z}^B9V&JtJQaQy9h$##2dr7E6jRZB$uEdqG_h zlQg%p*4hqjOenES-6tM>~NMz$EojL1(eJ~EWOq!bRcG*{* zOgNM3H!S{QWu^LUG^#?*$cosBgwjYIYIGM%WC#0{7j<1Va3CBFrw=v`s>GNbh+=KH zg6vV9?o?Jr$q%6QGwT!|MP(lnCsKMZzmv-OFqc?+scbr;yYzl>b*mcJsmkq9Rjkix XDw~YDrXZr(E(MED!)_QBM33<=#HL_z diff --git a/ebin/lz4.app b/ebin/lz4.app deleted file mode 100644 index 6afd72c..0000000 --- a/ebin/lz4.app +++ /dev/null @@ -1,8 +0,0 @@ -{application, 'lz4', [ - {description, "New project"}, - {vsn, "0.1.0"}, - {modules, ['leveled_app','leveled_bookie','leveled_cdb','leveled_codec','leveled_iclerk','leveled_imanifest','leveled_inker','leveled_log','leveled_pclerk','leveled_penciller','leveled_pmanifest','leveled_pmem','leveled_rand','leveled_runner','leveled_sst','leveled_sup','leveled_tictac','leveled_tinybloom','leveled_tree','lz4_nif','lz4f']}, - {registered, []}, - {applications, [kernel,stdlib]}, - {env, []} -]}. \ No newline at end of file diff --git a/erlang.mk b/erlang.mk deleted file mode 100644 index 64aaa4e..0000000 --- a/erlang.mk +++ /dev/null @@ -1,6889 +0,0 @@ -# Copyright (c) 2013-2016, 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 app apps deps search rel relup docs install-docs check tests clean distclean help erlang-mk - -ERLANG_MK_FILENAME := $(realpath $(lastword $(MAKEFILE_LIST))) - -ERLANG_MK_VERSION = 2017.04.25-6-g5922969 - -# Make 3.81 and 3.82 are deprecated. - -ifeq ($(MAKE_VERSION),3.81) -$(warning Please upgrade to GNU Make 4 or later: https://erlang.mk/guide/installation.html) -endif - -ifeq ($(MAKE_VERSION),3.82) -$(warning Please upgrade to GNU Make 4 or later: https://erlang.mk/guide/installation.html) -endif - -# Core configuration. - -PROJECT ?= $(notdir $(CURDIR)) -PROJECT := $(strip $(PROJECT)) - -PROJECT_VERSION ?= rolling -PROJECT_MOD ?= $(PROJECT)_app -PROJECT_ENV ?= [] - -# Verbosity. - -V ?= 0 - -verbose_0 = @ -verbose_2 = set -x; -verbose = $(verbose_$(V)) - -gen_verbose_0 = @echo " GEN " $@; -gen_verbose_2 = set -x; -gen_verbose = $(gen_verbose_$(V)) - -# Temporary files directory. - -ERLANG_MK_TMP ?= $(CURDIR)/.erlang.mk -export ERLANG_MK_TMP - -# "erl" command. - -ERL = erl +A0 -noinput -boot start_clean - -# Platform detection. - -ifeq ($(PLATFORM),) -UNAME_S := $(shell uname -s) - -ifeq ($(UNAME_S),Linux) -PLATFORM = linux -else ifeq ($(UNAME_S),Darwin) -PLATFORM = darwin -else ifeq ($(UNAME_S),SunOS) -PLATFORM = solaris -else ifeq ($(UNAME_S),GNU) -PLATFORM = gnu -else ifeq ($(UNAME_S),FreeBSD) -PLATFORM = freebsd -else ifeq ($(UNAME_S),NetBSD) -PLATFORM = netbsd -else ifeq ($(UNAME_S),OpenBSD) -PLATFORM = openbsd -else ifeq ($(UNAME_S),DragonFly) -PLATFORM = dragonfly -else ifeq ($(shell uname -o),Msys) -PLATFORM = msys2 -else -$(error Unable to detect platform. Please open a ticket with the output of uname -a.) -endif - -export PLATFORM -endif - -# Core targets. - -all:: deps app rel - -# Noop to avoid a Make warning when there's nothing to do. -rel:: - $(verbose) : - -relup:: deps app - -check:: tests - -clean:: clean-crashdump - -clean-crashdump: -ifneq ($(wildcard erl_crash.dump),) - $(gen_verbose) rm -f erl_crash.dump -endif - -distclean:: clean distclean-tmp - -distclean-tmp: - $(gen_verbose) rm -rf $(ERLANG_MK_TMP) - -help:: - $(verbose) printf "%s\n" \ - "erlang.mk (version $(ERLANG_MK_VERSION)) is distributed under the terms of the ISC License." \ - "Copyright (c) 2013-2016 Loïc Hoguin " \ - "" \ - "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" \ - " fetch-deps Fetch dependencies recursively (if needed) without compiling them" \ - " list-deps List dependencies recursively on stdout" \ - " 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" - -# Core functions. - -empty := -space := $(empty) $(empty) -tab := $(empty) $(empty) -comma := , - -define newline - - -endef - -define comma_list -$(subst $(space),$(comma),$(strip $(1))) -endef - -# Adding erlang.mk to make Erlang scripts who call init:get_plain_arguments() happy. -define erlang -$(ERL) $(2) -pz $(ERLANG_MK_TMP)/rebar/ebin -eval "$(subst $(newline),,$(subst ",\",$(1)))" -- erlang.mk -endef - -ifeq ($(PLATFORM),msys2) -core_native_path = $(subst \,\\\\,$(shell cygpath -w $1)) -else -core_native_path = $1 -endif - -core_http_get = curl -Lf$(if $(filter-out 0,$(V)),,s)o $(call core_native_path,$1) $2 - -core_eq = $(and $(findstring $(1),$(2)),$(findstring $(2),$(1))) - -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))))))))))))))))))))))))))) - -core_ls = $(filter-out $(1),$(shell echo $(1))) - -# @todo Use a solution that does not require using perl. -core_relpath = $(shell perl -e 'use File::Spec; print File::Spec->abs2rel(@ARGV) . "\n"' $1 $2) - -# Automated update. - -ERLANG_MK_REPO ?= https://github.com/ninenines/erlang.mk -ERLANG_MK_COMMIT ?= -ERLANG_MK_BUILD_CONFIG ?= build.config -ERLANG_MK_BUILD_DIR ?= .erlang.mk.build - -erlang-mk: - git clone $(ERLANG_MK_REPO) $(ERLANG_MK_BUILD_DIR) -ifdef ERLANG_MK_COMMIT - cd $(ERLANG_MK_BUILD_DIR) && git checkout $(ERLANG_MK_COMMIT) -endif - if [ -f $(ERLANG_MK_BUILD_CONFIG) ]; then cp $(ERLANG_MK_BUILD_CONFIG) $(ERLANG_MK_BUILD_DIR)/build.config; fi - $(MAKE) -C $(ERLANG_MK_BUILD_DIR) - cp $(ERLANG_MK_BUILD_DIR)/erlang.mk ./erlang.mk - rm -rf $(ERLANG_MK_BUILD_DIR) - -# The erlang.mk package index is bundled in the default erlang.mk build. -# Search for the string "copyright" to skip to the rest of the code. - -PACKAGES += aberth -pkg_aberth_name = aberth -pkg_aberth_description = Generic BERT-RPC server in Erlang -pkg_aberth_homepage = https://github.com/a13x/aberth -pkg_aberth_fetch = git -pkg_aberth_repo = https://github.com/a13x/aberth -pkg_aberth_commit = master - -PACKAGES += active -pkg_active_name = active -pkg_active_description = Active development for Erlang: rebuild and reload source/binary files while the VM is running -pkg_active_homepage = https://github.com/proger/active -pkg_active_fetch = git -pkg_active_repo = https://github.com/proger/active -pkg_active_commit = master - -PACKAGES += actordb_core -pkg_actordb_core_name = actordb_core -pkg_actordb_core_description = ActorDB main source -pkg_actordb_core_homepage = http://www.actordb.com/ -pkg_actordb_core_fetch = git -pkg_actordb_core_repo = https://github.com/biokoda/actordb_core -pkg_actordb_core_commit = master - -PACKAGES += actordb_thrift -pkg_actordb_thrift_name = actordb_thrift -pkg_actordb_thrift_description = Thrift API for ActorDB -pkg_actordb_thrift_homepage = http://www.actordb.com/ -pkg_actordb_thrift_fetch = git -pkg_actordb_thrift_repo = https://github.com/biokoda/actordb_thrift -pkg_actordb_thrift_commit = master - -PACKAGES += aleppo -pkg_aleppo_name = aleppo -pkg_aleppo_description = Alternative Erlang Pre-Processor -pkg_aleppo_homepage = https://github.com/ErlyORM/aleppo -pkg_aleppo_fetch = git -pkg_aleppo_repo = https://github.com/ErlyORM/aleppo -pkg_aleppo_commit = master - -PACKAGES += alog -pkg_alog_name = alog -pkg_alog_description = Simply the best logging framework for Erlang -pkg_alog_homepage = https://github.com/siberian-fast-food/alogger -pkg_alog_fetch = git -pkg_alog_repo = https://github.com/siberian-fast-food/alogger -pkg_alog_commit = master - -PACKAGES += amqp_client -pkg_amqp_client_name = amqp_client -pkg_amqp_client_description = RabbitMQ Erlang AMQP client -pkg_amqp_client_homepage = https://www.rabbitmq.com/erlang-client-user-guide.html -pkg_amqp_client_fetch = git -pkg_amqp_client_repo = https://github.com/rabbitmq/rabbitmq-erlang-client.git -pkg_amqp_client_commit = master - -PACKAGES += annotations -pkg_annotations_name = annotations -pkg_annotations_description = Simple code instrumentation utilities -pkg_annotations_homepage = https://github.com/hyperthunk/annotations -pkg_annotations_fetch = git -pkg_annotations_repo = https://github.com/hyperthunk/annotations -pkg_annotations_commit = master - -PACKAGES += antidote -pkg_antidote_name = antidote -pkg_antidote_description = Large-scale computation without synchronisation -pkg_antidote_homepage = https://syncfree.lip6.fr/ -pkg_antidote_fetch = git -pkg_antidote_repo = https://github.com/SyncFree/antidote -pkg_antidote_commit = master - -PACKAGES += apns -pkg_apns_name = apns -pkg_apns_description = Apple Push Notification Server for Erlang -pkg_apns_homepage = http://inaka.github.com/apns4erl -pkg_apns_fetch = git -pkg_apns_repo = https://github.com/inaka/apns4erl -pkg_apns_commit = master - -PACKAGES += asciideck -pkg_asciideck_name = asciideck -pkg_asciideck_description = Asciidoc for Erlang. -pkg_asciideck_homepage = https://ninenines.eu -pkg_asciideck_fetch = git -pkg_asciideck_repo = https://github.com/ninenines/asciideck -pkg_asciideck_commit = master - -PACKAGES += azdht -pkg_azdht_name = azdht -pkg_azdht_description = Azureus Distributed Hash Table (DHT) in Erlang -pkg_azdht_homepage = https://github.com/arcusfelis/azdht -pkg_azdht_fetch = git -pkg_azdht_repo = https://github.com/arcusfelis/azdht -pkg_azdht_commit = master - -PACKAGES += backoff -pkg_backoff_name = backoff -pkg_backoff_description = Simple exponential backoffs in Erlang -pkg_backoff_homepage = https://github.com/ferd/backoff -pkg_backoff_fetch = git -pkg_backoff_repo = https://github.com/ferd/backoff -pkg_backoff_commit = master - -PACKAGES += barrel_tcp -pkg_barrel_tcp_name = barrel_tcp -pkg_barrel_tcp_description = barrel is a generic TCP acceptor pool with low latency in Erlang. -pkg_barrel_tcp_homepage = https://github.com/benoitc-attic/barrel_tcp -pkg_barrel_tcp_fetch = git -pkg_barrel_tcp_repo = https://github.com/benoitc-attic/barrel_tcp -pkg_barrel_tcp_commit = master - -PACKAGES += basho_bench -pkg_basho_bench_name = basho_bench -pkg_basho_bench_description = A load-generation and testing tool for basically whatever you can write a returning Erlang function for. -pkg_basho_bench_homepage = https://github.com/basho/basho_bench -pkg_basho_bench_fetch = git -pkg_basho_bench_repo = https://github.com/basho/basho_bench -pkg_basho_bench_commit = master - -PACKAGES += bcrypt -pkg_bcrypt_name = bcrypt -pkg_bcrypt_description = Bcrypt Erlang / C library -pkg_bcrypt_homepage = https://github.com/riverrun/branglecrypt -pkg_bcrypt_fetch = git -pkg_bcrypt_repo = https://github.com/riverrun/branglecrypt -pkg_bcrypt_commit = master - -PACKAGES += beam -pkg_beam_name = beam -pkg_beam_description = BEAM emulator written in Erlang -pkg_beam_homepage = https://github.com/tonyrog/beam -pkg_beam_fetch = git -pkg_beam_repo = https://github.com/tonyrog/beam -pkg_beam_commit = master - -PACKAGES += beanstalk -pkg_beanstalk_name = beanstalk -pkg_beanstalk_description = An Erlang client for beanstalkd -pkg_beanstalk_homepage = https://github.com/tim/erlang-beanstalk -pkg_beanstalk_fetch = git -pkg_beanstalk_repo = https://github.com/tim/erlang-beanstalk -pkg_beanstalk_commit = master - -PACKAGES += bear -pkg_bear_name = bear -pkg_bear_description = a set of statistics functions for erlang -pkg_bear_homepage = https://github.com/boundary/bear -pkg_bear_fetch = git -pkg_bear_repo = https://github.com/boundary/bear -pkg_bear_commit = master - -PACKAGES += bertconf -pkg_bertconf_name = bertconf -pkg_bertconf_description = Make ETS tables out of statc BERT files that are auto-reloaded -pkg_bertconf_homepage = https://github.com/ferd/bertconf -pkg_bertconf_fetch = git -pkg_bertconf_repo = https://github.com/ferd/bertconf -pkg_bertconf_commit = master - -PACKAGES += bifrost -pkg_bifrost_name = bifrost -pkg_bifrost_description = Erlang FTP Server Framework -pkg_bifrost_homepage = https://github.com/thorstadt/bifrost -pkg_bifrost_fetch = git -pkg_bifrost_repo = https://github.com/thorstadt/bifrost -pkg_bifrost_commit = master - -PACKAGES += binpp -pkg_binpp_name = binpp -pkg_binpp_description = Erlang Binary Pretty Printer -pkg_binpp_homepage = https://github.com/jtendo/binpp -pkg_binpp_fetch = git -pkg_binpp_repo = https://github.com/jtendo/binpp -pkg_binpp_commit = master - -PACKAGES += bisect -pkg_bisect_name = bisect -pkg_bisect_description = Ordered fixed-size binary dictionary in Erlang -pkg_bisect_homepage = https://github.com/knutin/bisect -pkg_bisect_fetch = git -pkg_bisect_repo = https://github.com/knutin/bisect -pkg_bisect_commit = master - -PACKAGES += bitcask -pkg_bitcask_name = bitcask -pkg_bitcask_description = because you need another a key/value storage engine -pkg_bitcask_homepage = https://github.com/basho/bitcask -pkg_bitcask_fetch = git -pkg_bitcask_repo = https://github.com/basho/bitcask -pkg_bitcask_commit = develop - -PACKAGES += bitstore -pkg_bitstore_name = bitstore -pkg_bitstore_description = A document based ontology development environment -pkg_bitstore_homepage = https://github.com/bdionne/bitstore -pkg_bitstore_fetch = git -pkg_bitstore_repo = https://github.com/bdionne/bitstore -pkg_bitstore_commit = master - -PACKAGES += bootstrap -pkg_bootstrap_name = bootstrap -pkg_bootstrap_description = A simple, yet powerful Erlang cluster bootstrapping application. -pkg_bootstrap_homepage = https://github.com/schlagert/bootstrap -pkg_bootstrap_fetch = git -pkg_bootstrap_repo = https://github.com/schlagert/bootstrap -pkg_bootstrap_commit = master - -PACKAGES += boss -pkg_boss_name = boss -pkg_boss_description = Erlang web MVC, now featuring Comet -pkg_boss_homepage = https://github.com/ChicagoBoss/ChicagoBoss -pkg_boss_fetch = git -pkg_boss_repo = https://github.com/ChicagoBoss/ChicagoBoss -pkg_boss_commit = master - -PACKAGES += boss_db -pkg_boss_db_name = boss_db -pkg_boss_db_description = BossDB: a sharded, caching, pooling, evented ORM for Erlang -pkg_boss_db_homepage = https://github.com/ErlyORM/boss_db -pkg_boss_db_fetch = git -pkg_boss_db_repo = https://github.com/ErlyORM/boss_db -pkg_boss_db_commit = master - -PACKAGES += brod -pkg_brod_name = brod -pkg_brod_description = Kafka client in Erlang -pkg_brod_homepage = https://github.com/klarna/brod -pkg_brod_fetch = git -pkg_brod_repo = https://github.com/klarna/brod.git -pkg_brod_commit = master - -PACKAGES += bson -pkg_bson_name = bson -pkg_bson_description = BSON documents in Erlang, see bsonspec.org -pkg_bson_homepage = https://github.com/comtihon/bson-erlang -pkg_bson_fetch = git -pkg_bson_repo = https://github.com/comtihon/bson-erlang -pkg_bson_commit = master - -PACKAGES += bullet -pkg_bullet_name = bullet -pkg_bullet_description = Simple, reliable, efficient streaming for Cowboy. -pkg_bullet_homepage = http://ninenines.eu -pkg_bullet_fetch = git -pkg_bullet_repo = https://github.com/ninenines/bullet -pkg_bullet_commit = master - -PACKAGES += cache -pkg_cache_name = cache -pkg_cache_description = Erlang in-memory cache -pkg_cache_homepage = https://github.com/fogfish/cache -pkg_cache_fetch = git -pkg_cache_repo = https://github.com/fogfish/cache -pkg_cache_commit = master - -PACKAGES += cake -pkg_cake_name = cake -pkg_cake_description = Really simple terminal colorization -pkg_cake_homepage = https://github.com/darach/cake-erl -pkg_cake_fetch = git -pkg_cake_repo = https://github.com/darach/cake-erl -pkg_cake_commit = master - -PACKAGES += carotene -pkg_carotene_name = carotene -pkg_carotene_description = Real-time server -pkg_carotene_homepage = https://github.com/carotene/carotene -pkg_carotene_fetch = git -pkg_carotene_repo = https://github.com/carotene/carotene -pkg_carotene_commit = master - -PACKAGES += cberl -pkg_cberl_name = cberl -pkg_cberl_description = NIF based Erlang bindings for Couchbase -pkg_cberl_homepage = https://github.com/chitika/cberl -pkg_cberl_fetch = git -pkg_cberl_repo = https://github.com/chitika/cberl -pkg_cberl_commit = master - -PACKAGES += cecho -pkg_cecho_name = cecho -pkg_cecho_description = An ncurses library for Erlang -pkg_cecho_homepage = https://github.com/mazenharake/cecho -pkg_cecho_fetch = git -pkg_cecho_repo = https://github.com/mazenharake/cecho -pkg_cecho_commit = master - -PACKAGES += cferl -pkg_cferl_name = cferl -pkg_cferl_description = Rackspace / Open Stack Cloud Files Erlang Client -pkg_cferl_homepage = https://github.com/ddossot/cferl -pkg_cferl_fetch = git -pkg_cferl_repo = https://github.com/ddossot/cferl -pkg_cferl_commit = master - -PACKAGES += chaos_monkey -pkg_chaos_monkey_name = chaos_monkey -pkg_chaos_monkey_description = This is The CHAOS MONKEY. It will kill your processes. -pkg_chaos_monkey_homepage = https://github.com/dLuna/chaos_monkey -pkg_chaos_monkey_fetch = git -pkg_chaos_monkey_repo = https://github.com/dLuna/chaos_monkey -pkg_chaos_monkey_commit = master - -PACKAGES += check_node -pkg_check_node_name = check_node -pkg_check_node_description = Nagios Scripts for monitoring Riak -pkg_check_node_homepage = https://github.com/basho-labs/riak_nagios -pkg_check_node_fetch = git -pkg_check_node_repo = https://github.com/basho-labs/riak_nagios -pkg_check_node_commit = master - -PACKAGES += chronos -pkg_chronos_name = chronos -pkg_chronos_description = Timer module for Erlang that makes it easy to abstact time out of the tests. -pkg_chronos_homepage = https://github.com/lehoff/chronos -pkg_chronos_fetch = git -pkg_chronos_repo = https://github.com/lehoff/chronos -pkg_chronos_commit = master - -PACKAGES += chumak -pkg_chumak_name = chumak -pkg_chumak_description = Pure Erlang implementation of ZeroMQ Message Transport Protocol. -pkg_chumak_homepage = http://choven.ca -pkg_chumak_fetch = git -pkg_chumak_repo = https://github.com/chovencorp/chumak -pkg_chumak_commit = master - -PACKAGES += cl -pkg_cl_name = cl -pkg_cl_description = OpenCL binding for Erlang -pkg_cl_homepage = https://github.com/tonyrog/cl -pkg_cl_fetch = git -pkg_cl_repo = https://github.com/tonyrog/cl -pkg_cl_commit = master - -PACKAGES += classifier -pkg_classifier_name = classifier -pkg_classifier_description = An Erlang Bayesian Filter and Text Classifier -pkg_classifier_homepage = https://github.com/inaka/classifier -pkg_classifier_fetch = git -pkg_classifier_repo = https://github.com/inaka/classifier -pkg_classifier_commit = master - -PACKAGES += clique -pkg_clique_name = clique -pkg_clique_description = CLI Framework for Erlang -pkg_clique_homepage = https://github.com/basho/clique -pkg_clique_fetch = git -pkg_clique_repo = https://github.com/basho/clique -pkg_clique_commit = develop - -PACKAGES += cloudi_core -pkg_cloudi_core_name = cloudi_core -pkg_cloudi_core_description = CloudI internal service runtime -pkg_cloudi_core_homepage = http://cloudi.org/ -pkg_cloudi_core_fetch = git -pkg_cloudi_core_repo = https://github.com/CloudI/cloudi_core -pkg_cloudi_core_commit = master - -PACKAGES += cloudi_service_api_requests -pkg_cloudi_service_api_requests_name = cloudi_service_api_requests -pkg_cloudi_service_api_requests_description = CloudI Service API requests (JSON-RPC/Erlang-term support) -pkg_cloudi_service_api_requests_homepage = http://cloudi.org/ -pkg_cloudi_service_api_requests_fetch = git -pkg_cloudi_service_api_requests_repo = https://github.com/CloudI/cloudi_service_api_requests -pkg_cloudi_service_api_requests_commit = master - -PACKAGES += cloudi_service_db -pkg_cloudi_service_db_name = cloudi_service_db -pkg_cloudi_service_db_description = CloudI Database (in-memory/testing/generic) -pkg_cloudi_service_db_homepage = http://cloudi.org/ -pkg_cloudi_service_db_fetch = git -pkg_cloudi_service_db_repo = https://github.com/CloudI/cloudi_service_db -pkg_cloudi_service_db_commit = master - -PACKAGES += cloudi_service_db_cassandra -pkg_cloudi_service_db_cassandra_name = cloudi_service_db_cassandra -pkg_cloudi_service_db_cassandra_description = Cassandra CloudI Service -pkg_cloudi_service_db_cassandra_homepage = http://cloudi.org/ -pkg_cloudi_service_db_cassandra_fetch = git -pkg_cloudi_service_db_cassandra_repo = https://github.com/CloudI/cloudi_service_db_cassandra -pkg_cloudi_service_db_cassandra_commit = master - -PACKAGES += cloudi_service_db_cassandra_cql -pkg_cloudi_service_db_cassandra_cql_name = cloudi_service_db_cassandra_cql -pkg_cloudi_service_db_cassandra_cql_description = Cassandra CQL CloudI Service -pkg_cloudi_service_db_cassandra_cql_homepage = http://cloudi.org/ -pkg_cloudi_service_db_cassandra_cql_fetch = git -pkg_cloudi_service_db_cassandra_cql_repo = https://github.com/CloudI/cloudi_service_db_cassandra_cql -pkg_cloudi_service_db_cassandra_cql_commit = master - -PACKAGES += cloudi_service_db_couchdb -pkg_cloudi_service_db_couchdb_name = cloudi_service_db_couchdb -pkg_cloudi_service_db_couchdb_description = CouchDB CloudI Service -pkg_cloudi_service_db_couchdb_homepage = http://cloudi.org/ -pkg_cloudi_service_db_couchdb_fetch = git -pkg_cloudi_service_db_couchdb_repo = https://github.com/CloudI/cloudi_service_db_couchdb -pkg_cloudi_service_db_couchdb_commit = master - -PACKAGES += cloudi_service_db_elasticsearch -pkg_cloudi_service_db_elasticsearch_name = cloudi_service_db_elasticsearch -pkg_cloudi_service_db_elasticsearch_description = elasticsearch CloudI Service -pkg_cloudi_service_db_elasticsearch_homepage = http://cloudi.org/ -pkg_cloudi_service_db_elasticsearch_fetch = git -pkg_cloudi_service_db_elasticsearch_repo = https://github.com/CloudI/cloudi_service_db_elasticsearch -pkg_cloudi_service_db_elasticsearch_commit = master - -PACKAGES += cloudi_service_db_memcached -pkg_cloudi_service_db_memcached_name = cloudi_service_db_memcached -pkg_cloudi_service_db_memcached_description = memcached CloudI Service -pkg_cloudi_service_db_memcached_homepage = http://cloudi.org/ -pkg_cloudi_service_db_memcached_fetch = git -pkg_cloudi_service_db_memcached_repo = https://github.com/CloudI/cloudi_service_db_memcached -pkg_cloudi_service_db_memcached_commit = master - -PACKAGES += cloudi_service_db_mysql -pkg_cloudi_service_db_mysql_name = cloudi_service_db_mysql -pkg_cloudi_service_db_mysql_description = MySQL CloudI Service -pkg_cloudi_service_db_mysql_homepage = http://cloudi.org/ -pkg_cloudi_service_db_mysql_fetch = git -pkg_cloudi_service_db_mysql_repo = https://github.com/CloudI/cloudi_service_db_mysql -pkg_cloudi_service_db_mysql_commit = master - -PACKAGES += cloudi_service_db_pgsql -pkg_cloudi_service_db_pgsql_name = cloudi_service_db_pgsql -pkg_cloudi_service_db_pgsql_description = PostgreSQL CloudI Service -pkg_cloudi_service_db_pgsql_homepage = http://cloudi.org/ -pkg_cloudi_service_db_pgsql_fetch = git -pkg_cloudi_service_db_pgsql_repo = https://github.com/CloudI/cloudi_service_db_pgsql -pkg_cloudi_service_db_pgsql_commit = master - -PACKAGES += cloudi_service_db_riak -pkg_cloudi_service_db_riak_name = cloudi_service_db_riak -pkg_cloudi_service_db_riak_description = Riak CloudI Service -pkg_cloudi_service_db_riak_homepage = http://cloudi.org/ -pkg_cloudi_service_db_riak_fetch = git -pkg_cloudi_service_db_riak_repo = https://github.com/CloudI/cloudi_service_db_riak -pkg_cloudi_service_db_riak_commit = master - -PACKAGES += cloudi_service_db_tokyotyrant -pkg_cloudi_service_db_tokyotyrant_name = cloudi_service_db_tokyotyrant -pkg_cloudi_service_db_tokyotyrant_description = Tokyo Tyrant CloudI Service -pkg_cloudi_service_db_tokyotyrant_homepage = http://cloudi.org/ -pkg_cloudi_service_db_tokyotyrant_fetch = git -pkg_cloudi_service_db_tokyotyrant_repo = https://github.com/CloudI/cloudi_service_db_tokyotyrant -pkg_cloudi_service_db_tokyotyrant_commit = master - -PACKAGES += cloudi_service_filesystem -pkg_cloudi_service_filesystem_name = cloudi_service_filesystem -pkg_cloudi_service_filesystem_description = Filesystem CloudI Service -pkg_cloudi_service_filesystem_homepage = http://cloudi.org/ -pkg_cloudi_service_filesystem_fetch = git -pkg_cloudi_service_filesystem_repo = https://github.com/CloudI/cloudi_service_filesystem -pkg_cloudi_service_filesystem_commit = master - -PACKAGES += cloudi_service_http_client -pkg_cloudi_service_http_client_name = cloudi_service_http_client -pkg_cloudi_service_http_client_description = HTTP client CloudI Service -pkg_cloudi_service_http_client_homepage = http://cloudi.org/ -pkg_cloudi_service_http_client_fetch = git -pkg_cloudi_service_http_client_repo = https://github.com/CloudI/cloudi_service_http_client -pkg_cloudi_service_http_client_commit = master - -PACKAGES += cloudi_service_http_cowboy -pkg_cloudi_service_http_cowboy_name = cloudi_service_http_cowboy -pkg_cloudi_service_http_cowboy_description = cowboy HTTP/HTTPS CloudI Service -pkg_cloudi_service_http_cowboy_homepage = http://cloudi.org/ -pkg_cloudi_service_http_cowboy_fetch = git -pkg_cloudi_service_http_cowboy_repo = https://github.com/CloudI/cloudi_service_http_cowboy -pkg_cloudi_service_http_cowboy_commit = master - -PACKAGES += cloudi_service_http_elli -pkg_cloudi_service_http_elli_name = cloudi_service_http_elli -pkg_cloudi_service_http_elli_description = elli HTTP CloudI Service -pkg_cloudi_service_http_elli_homepage = http://cloudi.org/ -pkg_cloudi_service_http_elli_fetch = git -pkg_cloudi_service_http_elli_repo = https://github.com/CloudI/cloudi_service_http_elli -pkg_cloudi_service_http_elli_commit = master - -PACKAGES += cloudi_service_map_reduce -pkg_cloudi_service_map_reduce_name = cloudi_service_map_reduce -pkg_cloudi_service_map_reduce_description = Map/Reduce CloudI Service -pkg_cloudi_service_map_reduce_homepage = http://cloudi.org/ -pkg_cloudi_service_map_reduce_fetch = git -pkg_cloudi_service_map_reduce_repo = https://github.com/CloudI/cloudi_service_map_reduce -pkg_cloudi_service_map_reduce_commit = master - -PACKAGES += cloudi_service_oauth1 -pkg_cloudi_service_oauth1_name = cloudi_service_oauth1 -pkg_cloudi_service_oauth1_description = OAuth v1.0 CloudI Service -pkg_cloudi_service_oauth1_homepage = http://cloudi.org/ -pkg_cloudi_service_oauth1_fetch = git -pkg_cloudi_service_oauth1_repo = https://github.com/CloudI/cloudi_service_oauth1 -pkg_cloudi_service_oauth1_commit = master - -PACKAGES += cloudi_service_queue -pkg_cloudi_service_queue_name = cloudi_service_queue -pkg_cloudi_service_queue_description = Persistent Queue Service -pkg_cloudi_service_queue_homepage = http://cloudi.org/ -pkg_cloudi_service_queue_fetch = git -pkg_cloudi_service_queue_repo = https://github.com/CloudI/cloudi_service_queue -pkg_cloudi_service_queue_commit = master - -PACKAGES += cloudi_service_quorum -pkg_cloudi_service_quorum_name = cloudi_service_quorum -pkg_cloudi_service_quorum_description = CloudI Quorum Service -pkg_cloudi_service_quorum_homepage = http://cloudi.org/ -pkg_cloudi_service_quorum_fetch = git -pkg_cloudi_service_quorum_repo = https://github.com/CloudI/cloudi_service_quorum -pkg_cloudi_service_quorum_commit = master - -PACKAGES += cloudi_service_router -pkg_cloudi_service_router_name = cloudi_service_router -pkg_cloudi_service_router_description = CloudI Router Service -pkg_cloudi_service_router_homepage = http://cloudi.org/ -pkg_cloudi_service_router_fetch = git -pkg_cloudi_service_router_repo = https://github.com/CloudI/cloudi_service_router -pkg_cloudi_service_router_commit = master - -PACKAGES += cloudi_service_tcp -pkg_cloudi_service_tcp_name = cloudi_service_tcp -pkg_cloudi_service_tcp_description = TCP CloudI Service -pkg_cloudi_service_tcp_homepage = http://cloudi.org/ -pkg_cloudi_service_tcp_fetch = git -pkg_cloudi_service_tcp_repo = https://github.com/CloudI/cloudi_service_tcp -pkg_cloudi_service_tcp_commit = master - -PACKAGES += cloudi_service_timers -pkg_cloudi_service_timers_name = cloudi_service_timers -pkg_cloudi_service_timers_description = Timers CloudI Service -pkg_cloudi_service_timers_homepage = http://cloudi.org/ -pkg_cloudi_service_timers_fetch = git -pkg_cloudi_service_timers_repo = https://github.com/CloudI/cloudi_service_timers -pkg_cloudi_service_timers_commit = master - -PACKAGES += cloudi_service_udp -pkg_cloudi_service_udp_name = cloudi_service_udp -pkg_cloudi_service_udp_description = UDP CloudI Service -pkg_cloudi_service_udp_homepage = http://cloudi.org/ -pkg_cloudi_service_udp_fetch = git -pkg_cloudi_service_udp_repo = https://github.com/CloudI/cloudi_service_udp -pkg_cloudi_service_udp_commit = master - -PACKAGES += cloudi_service_validate -pkg_cloudi_service_validate_name = cloudi_service_validate -pkg_cloudi_service_validate_description = CloudI Validate Service -pkg_cloudi_service_validate_homepage = http://cloudi.org/ -pkg_cloudi_service_validate_fetch = git -pkg_cloudi_service_validate_repo = https://github.com/CloudI/cloudi_service_validate -pkg_cloudi_service_validate_commit = master - -PACKAGES += cloudi_service_zeromq -pkg_cloudi_service_zeromq_name = cloudi_service_zeromq -pkg_cloudi_service_zeromq_description = ZeroMQ CloudI Service -pkg_cloudi_service_zeromq_homepage = http://cloudi.org/ -pkg_cloudi_service_zeromq_fetch = git -pkg_cloudi_service_zeromq_repo = https://github.com/CloudI/cloudi_service_zeromq -pkg_cloudi_service_zeromq_commit = master - -PACKAGES += cluster_info -pkg_cluster_info_name = cluster_info -pkg_cluster_info_description = Fork of Hibari's nifty cluster_info OTP app -pkg_cluster_info_homepage = https://github.com/basho/cluster_info -pkg_cluster_info_fetch = git -pkg_cluster_info_repo = https://github.com/basho/cluster_info -pkg_cluster_info_commit = master - -PACKAGES += color -pkg_color_name = color -pkg_color_description = ANSI colors for your Erlang -pkg_color_homepage = https://github.com/julianduque/erlang-color -pkg_color_fetch = git -pkg_color_repo = https://github.com/julianduque/erlang-color -pkg_color_commit = master - -PACKAGES += confetti -pkg_confetti_name = confetti -pkg_confetti_description = Erlang configuration provider / application:get_env/2 on steroids -pkg_confetti_homepage = https://github.com/jtendo/confetti -pkg_confetti_fetch = git -pkg_confetti_repo = https://github.com/jtendo/confetti -pkg_confetti_commit = master - -PACKAGES += couchbeam -pkg_couchbeam_name = couchbeam -pkg_couchbeam_description = Apache CouchDB client in Erlang -pkg_couchbeam_homepage = https://github.com/benoitc/couchbeam -pkg_couchbeam_fetch = git -pkg_couchbeam_repo = https://github.com/benoitc/couchbeam -pkg_couchbeam_commit = master - -PACKAGES += covertool -pkg_covertool_name = covertool -pkg_covertool_description = Tool to convert Erlang cover data files into Cobertura XML reports -pkg_covertool_homepage = https://github.com/idubrov/covertool -pkg_covertool_fetch = git -pkg_covertool_repo = https://github.com/idubrov/covertool -pkg_covertool_commit = master - -PACKAGES += cowboy -pkg_cowboy_name = cowboy -pkg_cowboy_description = Small, fast and modular HTTP server. -pkg_cowboy_homepage = http://ninenines.eu -pkg_cowboy_fetch = git -pkg_cowboy_repo = https://github.com/ninenines/cowboy -pkg_cowboy_commit = 1.0.4 - -PACKAGES += cowdb -pkg_cowdb_name = cowdb -pkg_cowdb_description = Pure Key/Value database library for Erlang Applications -pkg_cowdb_homepage = https://github.com/refuge/cowdb -pkg_cowdb_fetch = git -pkg_cowdb_repo = https://github.com/refuge/cowdb -pkg_cowdb_commit = master - -PACKAGES += cowlib -pkg_cowlib_name = cowlib -pkg_cowlib_description = Support library for manipulating Web protocols. -pkg_cowlib_homepage = http://ninenines.eu -pkg_cowlib_fetch = git -pkg_cowlib_repo = https://github.com/ninenines/cowlib -pkg_cowlib_commit = 1.0.2 - -PACKAGES += cpg -pkg_cpg_name = cpg -pkg_cpg_description = CloudI Process Groups -pkg_cpg_homepage = https://github.com/okeuday/cpg -pkg_cpg_fetch = git -pkg_cpg_repo = https://github.com/okeuday/cpg -pkg_cpg_commit = master - -PACKAGES += cqerl -pkg_cqerl_name = cqerl -pkg_cqerl_description = Native Erlang CQL client for Cassandra -pkg_cqerl_homepage = https://matehat.github.io/cqerl/ -pkg_cqerl_fetch = git -pkg_cqerl_repo = https://github.com/matehat/cqerl -pkg_cqerl_commit = master - -PACKAGES += cr -pkg_cr_name = cr -pkg_cr_description = Chain Replication -pkg_cr_homepage = https://synrc.com/apps/cr/doc/cr.htm -pkg_cr_fetch = git -pkg_cr_repo = https://github.com/spawnproc/cr -pkg_cr_commit = master - -PACKAGES += cuttlefish -pkg_cuttlefish_name = cuttlefish -pkg_cuttlefish_description = never lose your childlike sense of wonder baby cuttlefish, promise me? -pkg_cuttlefish_homepage = https://github.com/basho/cuttlefish -pkg_cuttlefish_fetch = git -pkg_cuttlefish_repo = https://github.com/basho/cuttlefish -pkg_cuttlefish_commit = master - -PACKAGES += damocles -pkg_damocles_name = damocles -pkg_damocles_description = Erlang library for generating adversarial network conditions for QAing distributed applications/systems on a single Linux box. -pkg_damocles_homepage = https://github.com/lostcolony/damocles -pkg_damocles_fetch = git -pkg_damocles_repo = https://github.com/lostcolony/damocles -pkg_damocles_commit = master - -PACKAGES += debbie -pkg_debbie_name = debbie -pkg_debbie_description = .DEB Built In Erlang -pkg_debbie_homepage = https://github.com/crownedgrouse/debbie -pkg_debbie_fetch = git -pkg_debbie_repo = https://github.com/crownedgrouse/debbie -pkg_debbie_commit = master - -PACKAGES += decimal -pkg_decimal_name = decimal -pkg_decimal_description = An Erlang decimal arithmetic library -pkg_decimal_homepage = https://github.com/tim/erlang-decimal -pkg_decimal_fetch = git -pkg_decimal_repo = https://github.com/tim/erlang-decimal -pkg_decimal_commit = master - -PACKAGES += detergent -pkg_detergent_name = detergent -pkg_detergent_description = An emulsifying Erlang SOAP library -pkg_detergent_homepage = https://github.com/devinus/detergent -pkg_detergent_fetch = git -pkg_detergent_repo = https://github.com/devinus/detergent -pkg_detergent_commit = master - -PACKAGES += detest -pkg_detest_name = detest -pkg_detest_description = Tool for running tests on a cluster of erlang nodes -pkg_detest_homepage = https://github.com/biokoda/detest -pkg_detest_fetch = git -pkg_detest_repo = https://github.com/biokoda/detest -pkg_detest_commit = master - -PACKAGES += dh_date -pkg_dh_date_name = dh_date -pkg_dh_date_description = Date formatting / parsing library for erlang -pkg_dh_date_homepage = https://github.com/daleharvey/dh_date -pkg_dh_date_fetch = git -pkg_dh_date_repo = https://github.com/daleharvey/dh_date -pkg_dh_date_commit = master - -PACKAGES += dirbusterl -pkg_dirbusterl_name = dirbusterl -pkg_dirbusterl_description = DirBuster successor in Erlang -pkg_dirbusterl_homepage = https://github.com/silentsignal/DirBustErl -pkg_dirbusterl_fetch = git -pkg_dirbusterl_repo = https://github.com/silentsignal/DirBustErl -pkg_dirbusterl_commit = master - -PACKAGES += dispcount -pkg_dispcount_name = dispcount -pkg_dispcount_description = Erlang task dispatcher based on ETS counters. -pkg_dispcount_homepage = https://github.com/ferd/dispcount -pkg_dispcount_fetch = git -pkg_dispcount_repo = https://github.com/ferd/dispcount -pkg_dispcount_commit = master - -PACKAGES += dlhttpc -pkg_dlhttpc_name = dlhttpc -pkg_dlhttpc_description = dispcount-based lhttpc fork for massive amounts of requests to limited endpoints -pkg_dlhttpc_homepage = https://github.com/ferd/dlhttpc -pkg_dlhttpc_fetch = git -pkg_dlhttpc_repo = https://github.com/ferd/dlhttpc -pkg_dlhttpc_commit = master - -PACKAGES += dns -pkg_dns_name = dns -pkg_dns_description = Erlang DNS library -pkg_dns_homepage = https://github.com/aetrion/dns_erlang -pkg_dns_fetch = git -pkg_dns_repo = https://github.com/aetrion/dns_erlang -pkg_dns_commit = master - -PACKAGES += dnssd -pkg_dnssd_name = dnssd -pkg_dnssd_description = Erlang interface to Apple's Bonjour D NS Service Discovery implementation -pkg_dnssd_homepage = https://github.com/benoitc/dnssd_erlang -pkg_dnssd_fetch = git -pkg_dnssd_repo = https://github.com/benoitc/dnssd_erlang -pkg_dnssd_commit = master - -PACKAGES += dtl -pkg_dtl_name = dtl -pkg_dtl_description = Django Template Language: A full-featured port of the Django template engine to Erlang. -pkg_dtl_homepage = https://github.com/oinksoft/dtl -pkg_dtl_fetch = git -pkg_dtl_repo = https://github.com/oinksoft/dtl -pkg_dtl_commit = master - -PACKAGES += dynamic_compile -pkg_dynamic_compile_name = dynamic_compile -pkg_dynamic_compile_description = compile and load erlang modules from string input -pkg_dynamic_compile_homepage = https://github.com/jkvor/dynamic_compile -pkg_dynamic_compile_fetch = git -pkg_dynamic_compile_repo = https://github.com/jkvor/dynamic_compile -pkg_dynamic_compile_commit = master - -PACKAGES += e2 -pkg_e2_name = e2 -pkg_e2_description = Library to simply writing correct OTP applications. -pkg_e2_homepage = http://e2project.org -pkg_e2_fetch = git -pkg_e2_repo = https://github.com/gar1t/e2 -pkg_e2_commit = master - -PACKAGES += eamf -pkg_eamf_name = eamf -pkg_eamf_description = eAMF provides Action Message Format (AMF) support for Erlang -pkg_eamf_homepage = https://github.com/mrinalwadhwa/eamf -pkg_eamf_fetch = git -pkg_eamf_repo = https://github.com/mrinalwadhwa/eamf -pkg_eamf_commit = master - -PACKAGES += eavro -pkg_eavro_name = eavro -pkg_eavro_description = Apache Avro encoder/decoder -pkg_eavro_homepage = https://github.com/SIfoxDevTeam/eavro -pkg_eavro_fetch = git -pkg_eavro_repo = https://github.com/SIfoxDevTeam/eavro -pkg_eavro_commit = master - -PACKAGES += ecapnp -pkg_ecapnp_name = ecapnp -pkg_ecapnp_description = Cap'n Proto library for Erlang -pkg_ecapnp_homepage = https://github.com/kaos/ecapnp -pkg_ecapnp_fetch = git -pkg_ecapnp_repo = https://github.com/kaos/ecapnp -pkg_ecapnp_commit = master - -PACKAGES += econfig -pkg_econfig_name = econfig -pkg_econfig_description = simple Erlang config handler using INI files -pkg_econfig_homepage = https://github.com/benoitc/econfig -pkg_econfig_fetch = git -pkg_econfig_repo = https://github.com/benoitc/econfig -pkg_econfig_commit = master - -PACKAGES += edate -pkg_edate_name = edate -pkg_edate_description = date manipulation library for erlang -pkg_edate_homepage = https://github.com/dweldon/edate -pkg_edate_fetch = git -pkg_edate_repo = https://github.com/dweldon/edate -pkg_edate_commit = master - -PACKAGES += edgar -pkg_edgar_name = edgar -pkg_edgar_description = Erlang Does GNU AR -pkg_edgar_homepage = https://github.com/crownedgrouse/edgar -pkg_edgar_fetch = git -pkg_edgar_repo = https://github.com/crownedgrouse/edgar -pkg_edgar_commit = master - -PACKAGES += edis -pkg_edis_name = edis -pkg_edis_description = An Erlang implementation of Redis KV Store -pkg_edis_homepage = http://inaka.github.com/edis/ -pkg_edis_fetch = git -pkg_edis_repo = https://github.com/inaka/edis -pkg_edis_commit = master - -PACKAGES += edns -pkg_edns_name = edns -pkg_edns_description = Erlang/OTP DNS server -pkg_edns_homepage = https://github.com/hcvst/erlang-dns -pkg_edns_fetch = git -pkg_edns_repo = https://github.com/hcvst/erlang-dns -pkg_edns_commit = master - -PACKAGES += edown -pkg_edown_name = edown -pkg_edown_description = EDoc extension for generating Github-flavored Markdown -pkg_edown_homepage = https://github.com/uwiger/edown -pkg_edown_fetch = git -pkg_edown_repo = https://github.com/uwiger/edown -pkg_edown_commit = master - -PACKAGES += eep -pkg_eep_name = eep -pkg_eep_description = Erlang Easy Profiling (eep) application provides a way to analyze application performance and call hierarchy -pkg_eep_homepage = https://github.com/virtan/eep -pkg_eep_fetch = git -pkg_eep_repo = https://github.com/virtan/eep -pkg_eep_commit = master - -PACKAGES += eep_app -pkg_eep_app_name = eep_app -pkg_eep_app_description = Embedded Event Processing -pkg_eep_app_homepage = https://github.com/darach/eep-erl -pkg_eep_app_fetch = git -pkg_eep_app_repo = https://github.com/darach/eep-erl -pkg_eep_app_commit = master - -PACKAGES += efene -pkg_efene_name = efene -pkg_efene_description = Alternative syntax for the Erlang Programming Language focusing on simplicity, ease of use and programmer UX -pkg_efene_homepage = https://github.com/efene/efene -pkg_efene_fetch = git -pkg_efene_repo = https://github.com/efene/efene -pkg_efene_commit = master - -PACKAGES += egeoip -pkg_egeoip_name = egeoip -pkg_egeoip_description = Erlang IP Geolocation module, currently supporting the MaxMind GeoLite City Database. -pkg_egeoip_homepage = https://github.com/mochi/egeoip -pkg_egeoip_fetch = git -pkg_egeoip_repo = https://github.com/mochi/egeoip -pkg_egeoip_commit = master - -PACKAGES += ehsa -pkg_ehsa_name = ehsa -pkg_ehsa_description = Erlang HTTP server basic and digest authentication modules -pkg_ehsa_homepage = https://bitbucket.org/a12n/ehsa -pkg_ehsa_fetch = hg -pkg_ehsa_repo = https://bitbucket.org/a12n/ehsa -pkg_ehsa_commit = default - -PACKAGES += ej -pkg_ej_name = ej -pkg_ej_description = Helper module for working with Erlang terms representing JSON -pkg_ej_homepage = https://github.com/seth/ej -pkg_ej_fetch = git -pkg_ej_repo = https://github.com/seth/ej -pkg_ej_commit = master - -PACKAGES += ejabberd -pkg_ejabberd_name = ejabberd -pkg_ejabberd_description = Robust, ubiquitous and massively scalable Jabber / XMPP Instant Messaging platform -pkg_ejabberd_homepage = https://github.com/processone/ejabberd -pkg_ejabberd_fetch = git -pkg_ejabberd_repo = https://github.com/processone/ejabberd -pkg_ejabberd_commit = master - -PACKAGES += ejwt -pkg_ejwt_name = ejwt -pkg_ejwt_description = erlang library for JSON Web Token -pkg_ejwt_homepage = https://github.com/artefactop/ejwt -pkg_ejwt_fetch = git -pkg_ejwt_repo = https://github.com/artefactop/ejwt -pkg_ejwt_commit = master - -PACKAGES += ekaf -pkg_ekaf_name = ekaf -pkg_ekaf_description = A minimal, high-performance Kafka client in Erlang. -pkg_ekaf_homepage = https://github.com/helpshift/ekaf -pkg_ekaf_fetch = git -pkg_ekaf_repo = https://github.com/helpshift/ekaf -pkg_ekaf_commit = master - -PACKAGES += elarm -pkg_elarm_name = elarm -pkg_elarm_description = Alarm Manager for Erlang. -pkg_elarm_homepage = https://github.com/esl/elarm -pkg_elarm_fetch = git -pkg_elarm_repo = https://github.com/esl/elarm -pkg_elarm_commit = master - -PACKAGES += eleveldb -pkg_eleveldb_name = eleveldb -pkg_eleveldb_description = Erlang LevelDB API -pkg_eleveldb_homepage = https://github.com/basho/eleveldb -pkg_eleveldb_fetch = git -pkg_eleveldb_repo = https://github.com/basho/eleveldb -pkg_eleveldb_commit = master - -PACKAGES += elli -pkg_elli_name = elli -pkg_elli_description = Simple, robust and performant Erlang web server -pkg_elli_homepage = https://github.com/knutin/elli -pkg_elli_fetch = git -pkg_elli_repo = https://github.com/knutin/elli -pkg_elli_commit = master - -PACKAGES += elvis -pkg_elvis_name = elvis -pkg_elvis_description = Erlang Style Reviewer -pkg_elvis_homepage = https://github.com/inaka/elvis -pkg_elvis_fetch = git -pkg_elvis_repo = https://github.com/inaka/elvis -pkg_elvis_commit = master - -PACKAGES += emagick -pkg_emagick_name = emagick -pkg_emagick_description = Wrapper for Graphics/ImageMagick command line tool. -pkg_emagick_homepage = https://github.com/kivra/emagick -pkg_emagick_fetch = git -pkg_emagick_repo = https://github.com/kivra/emagick -pkg_emagick_commit = master - -PACKAGES += emysql -pkg_emysql_name = emysql -pkg_emysql_description = Stable, pure Erlang MySQL driver. -pkg_emysql_homepage = https://github.com/Eonblast/Emysql -pkg_emysql_fetch = git -pkg_emysql_repo = https://github.com/Eonblast/Emysql -pkg_emysql_commit = master - -PACKAGES += enm -pkg_enm_name = enm -pkg_enm_description = Erlang driver for nanomsg -pkg_enm_homepage = https://github.com/basho/enm -pkg_enm_fetch = git -pkg_enm_repo = https://github.com/basho/enm -pkg_enm_commit = master - -PACKAGES += entop -pkg_entop_name = entop -pkg_entop_description = A top-like tool for monitoring an Erlang node -pkg_entop_homepage = https://github.com/mazenharake/entop -pkg_entop_fetch = git -pkg_entop_repo = https://github.com/mazenharake/entop -pkg_entop_commit = master - -PACKAGES += epcap -pkg_epcap_name = epcap -pkg_epcap_description = Erlang packet capture interface using pcap -pkg_epcap_homepage = https://github.com/msantos/epcap -pkg_epcap_fetch = git -pkg_epcap_repo = https://github.com/msantos/epcap -pkg_epcap_commit = master - -PACKAGES += eper -pkg_eper_name = eper -pkg_eper_description = Erlang performance and debugging tools. -pkg_eper_homepage = https://github.com/massemanet/eper -pkg_eper_fetch = git -pkg_eper_repo = https://github.com/massemanet/eper -pkg_eper_commit = master - -PACKAGES += epgsql -pkg_epgsql_name = epgsql -pkg_epgsql_description = Erlang PostgreSQL client library. -pkg_epgsql_homepage = https://github.com/epgsql/epgsql -pkg_epgsql_fetch = git -pkg_epgsql_repo = https://github.com/epgsql/epgsql -pkg_epgsql_commit = master - -PACKAGES += episcina -pkg_episcina_name = episcina -pkg_episcina_description = A simple non intrusive resource pool for connections -pkg_episcina_homepage = https://github.com/erlware/episcina -pkg_episcina_fetch = git -pkg_episcina_repo = https://github.com/erlware/episcina -pkg_episcina_commit = master - -PACKAGES += eplot -pkg_eplot_name = eplot -pkg_eplot_description = A plot engine written in erlang. -pkg_eplot_homepage = https://github.com/psyeugenic/eplot -pkg_eplot_fetch = git -pkg_eplot_repo = https://github.com/psyeugenic/eplot -pkg_eplot_commit = master - -PACKAGES += epocxy -pkg_epocxy_name = epocxy -pkg_epocxy_description = Erlang Patterns of Concurrency -pkg_epocxy_homepage = https://github.com/duomark/epocxy -pkg_epocxy_fetch = git -pkg_epocxy_repo = https://github.com/duomark/epocxy -pkg_epocxy_commit = master - -PACKAGES += epubnub -pkg_epubnub_name = epubnub -pkg_epubnub_description = Erlang PubNub API -pkg_epubnub_homepage = https://github.com/tsloughter/epubnub -pkg_epubnub_fetch = git -pkg_epubnub_repo = https://github.com/tsloughter/epubnub -pkg_epubnub_commit = master - -PACKAGES += eqm -pkg_eqm_name = eqm -pkg_eqm_description = Erlang pub sub with supply-demand channels -pkg_eqm_homepage = https://github.com/loucash/eqm -pkg_eqm_fetch = git -pkg_eqm_repo = https://github.com/loucash/eqm -pkg_eqm_commit = master - -PACKAGES += eredis -pkg_eredis_name = eredis -pkg_eredis_description = Erlang Redis client -pkg_eredis_homepage = https://github.com/wooga/eredis -pkg_eredis_fetch = git -pkg_eredis_repo = https://github.com/wooga/eredis -pkg_eredis_commit = master - -PACKAGES += eredis_pool -pkg_eredis_pool_name = eredis_pool -pkg_eredis_pool_description = eredis_pool is Pool of Redis clients, using eredis and poolboy. -pkg_eredis_pool_homepage = https://github.com/hiroeorz/eredis_pool -pkg_eredis_pool_fetch = git -pkg_eredis_pool_repo = https://github.com/hiroeorz/eredis_pool -pkg_eredis_pool_commit = master - -PACKAGES += erl_streams -pkg_erl_streams_name = erl_streams -pkg_erl_streams_description = Streams in Erlang -pkg_erl_streams_homepage = https://github.com/epappas/erl_streams -pkg_erl_streams_fetch = git -pkg_erl_streams_repo = https://github.com/epappas/erl_streams -pkg_erl_streams_commit = master - -PACKAGES += erlang_cep -pkg_erlang_cep_name = erlang_cep -pkg_erlang_cep_description = A basic CEP package written in erlang -pkg_erlang_cep_homepage = https://github.com/danmacklin/erlang_cep -pkg_erlang_cep_fetch = git -pkg_erlang_cep_repo = https://github.com/danmacklin/erlang_cep -pkg_erlang_cep_commit = master - -PACKAGES += erlang_js -pkg_erlang_js_name = erlang_js -pkg_erlang_js_description = A linked-in driver for Erlang to Mozilla's Spidermonkey Javascript runtime. -pkg_erlang_js_homepage = https://github.com/basho/erlang_js -pkg_erlang_js_fetch = git -pkg_erlang_js_repo = https://github.com/basho/erlang_js -pkg_erlang_js_commit = master - -PACKAGES += erlang_localtime -pkg_erlang_localtime_name = erlang_localtime -pkg_erlang_localtime_description = Erlang library for conversion from one local time to another -pkg_erlang_localtime_homepage = https://github.com/dmitryme/erlang_localtime -pkg_erlang_localtime_fetch = git -pkg_erlang_localtime_repo = https://github.com/dmitryme/erlang_localtime -pkg_erlang_localtime_commit = master - -PACKAGES += erlang_smtp -pkg_erlang_smtp_name = erlang_smtp -pkg_erlang_smtp_description = Erlang SMTP and POP3 server code. -pkg_erlang_smtp_homepage = https://github.com/tonyg/erlang-smtp -pkg_erlang_smtp_fetch = git -pkg_erlang_smtp_repo = https://github.com/tonyg/erlang-smtp -pkg_erlang_smtp_commit = master - -PACKAGES += erlang_term -pkg_erlang_term_name = erlang_term -pkg_erlang_term_description = Erlang Term Info -pkg_erlang_term_homepage = https://github.com/okeuday/erlang_term -pkg_erlang_term_fetch = git -pkg_erlang_term_repo = https://github.com/okeuday/erlang_term -pkg_erlang_term_commit = master - -PACKAGES += erlastic_search -pkg_erlastic_search_name = erlastic_search -pkg_erlastic_search_description = An Erlang app for communicating with Elastic Search's rest interface. -pkg_erlastic_search_homepage = https://github.com/tsloughter/erlastic_search -pkg_erlastic_search_fetch = git -pkg_erlastic_search_repo = https://github.com/tsloughter/erlastic_search -pkg_erlastic_search_commit = master - -PACKAGES += erlasticsearch -pkg_erlasticsearch_name = erlasticsearch -pkg_erlasticsearch_description = Erlang thrift interface to elastic_search -pkg_erlasticsearch_homepage = https://github.com/dieswaytoofast/erlasticsearch -pkg_erlasticsearch_fetch = git -pkg_erlasticsearch_repo = https://github.com/dieswaytoofast/erlasticsearch -pkg_erlasticsearch_commit = master - -PACKAGES += erlbrake -pkg_erlbrake_name = erlbrake -pkg_erlbrake_description = Erlang Airbrake notification client -pkg_erlbrake_homepage = https://github.com/kenpratt/erlbrake -pkg_erlbrake_fetch = git -pkg_erlbrake_repo = https://github.com/kenpratt/erlbrake -pkg_erlbrake_commit = master - -PACKAGES += erlcloud -pkg_erlcloud_name = erlcloud -pkg_erlcloud_description = Cloud Computing library for erlang (Amazon EC2, S3, SQS, SimpleDB, Mechanical Turk, ELB) -pkg_erlcloud_homepage = https://github.com/gleber/erlcloud -pkg_erlcloud_fetch = git -pkg_erlcloud_repo = https://github.com/gleber/erlcloud -pkg_erlcloud_commit = master - -PACKAGES += erlcron -pkg_erlcron_name = erlcron -pkg_erlcron_description = Erlang cronish system -pkg_erlcron_homepage = https://github.com/erlware/erlcron -pkg_erlcron_fetch = git -pkg_erlcron_repo = https://github.com/erlware/erlcron -pkg_erlcron_commit = master - -PACKAGES += erldb -pkg_erldb_name = erldb -pkg_erldb_description = ORM (Object-relational mapping) application implemented in Erlang -pkg_erldb_homepage = http://erldb.org -pkg_erldb_fetch = git -pkg_erldb_repo = https://github.com/erldb/erldb -pkg_erldb_commit = master - -PACKAGES += erldis -pkg_erldis_name = erldis -pkg_erldis_description = redis erlang client library -pkg_erldis_homepage = https://github.com/cstar/erldis -pkg_erldis_fetch = git -pkg_erldis_repo = https://github.com/cstar/erldis -pkg_erldis_commit = master - -PACKAGES += erldns -pkg_erldns_name = erldns -pkg_erldns_description = DNS server, in erlang. -pkg_erldns_homepage = https://github.com/aetrion/erl-dns -pkg_erldns_fetch = git -pkg_erldns_repo = https://github.com/aetrion/erl-dns -pkg_erldns_commit = master - -PACKAGES += erldocker -pkg_erldocker_name = erldocker -pkg_erldocker_description = Docker Remote API client for Erlang -pkg_erldocker_homepage = https://github.com/proger/erldocker -pkg_erldocker_fetch = git -pkg_erldocker_repo = https://github.com/proger/erldocker -pkg_erldocker_commit = master - -PACKAGES += erlfsmon -pkg_erlfsmon_name = erlfsmon -pkg_erlfsmon_description = Erlang filesystem event watcher for Linux and OSX -pkg_erlfsmon_homepage = https://github.com/proger/erlfsmon -pkg_erlfsmon_fetch = git -pkg_erlfsmon_repo = https://github.com/proger/erlfsmon -pkg_erlfsmon_commit = master - -PACKAGES += erlgit -pkg_erlgit_name = erlgit -pkg_erlgit_description = Erlang convenience wrapper around git executable -pkg_erlgit_homepage = https://github.com/gleber/erlgit -pkg_erlgit_fetch = git -pkg_erlgit_repo = https://github.com/gleber/erlgit -pkg_erlgit_commit = master - -PACKAGES += erlguten -pkg_erlguten_name = erlguten -pkg_erlguten_description = ErlGuten is a system for high-quality typesetting, written purely in Erlang. -pkg_erlguten_homepage = https://github.com/richcarl/erlguten -pkg_erlguten_fetch = git -pkg_erlguten_repo = https://github.com/richcarl/erlguten -pkg_erlguten_commit = master - -PACKAGES += erlmc -pkg_erlmc_name = erlmc -pkg_erlmc_description = Erlang memcached binary protocol client -pkg_erlmc_homepage = https://github.com/jkvor/erlmc -pkg_erlmc_fetch = git -pkg_erlmc_repo = https://github.com/jkvor/erlmc -pkg_erlmc_commit = master - -PACKAGES += erlmongo -pkg_erlmongo_name = erlmongo -pkg_erlmongo_description = Record based Erlang driver for MongoDB with gridfs support -pkg_erlmongo_homepage = https://github.com/SergejJurecko/erlmongo -pkg_erlmongo_fetch = git -pkg_erlmongo_repo = https://github.com/SergejJurecko/erlmongo -pkg_erlmongo_commit = master - -PACKAGES += erlog -pkg_erlog_name = erlog -pkg_erlog_description = Prolog interpreter in and for Erlang -pkg_erlog_homepage = https://github.com/rvirding/erlog -pkg_erlog_fetch = git -pkg_erlog_repo = https://github.com/rvirding/erlog -pkg_erlog_commit = master - -PACKAGES += erlpass -pkg_erlpass_name = erlpass -pkg_erlpass_description = A library to handle password hashing and changing in a safe manner, independent from any kind of storage whatsoever. -pkg_erlpass_homepage = https://github.com/ferd/erlpass -pkg_erlpass_fetch = git -pkg_erlpass_repo = https://github.com/ferd/erlpass -pkg_erlpass_commit = master - -PACKAGES += erlport -pkg_erlport_name = erlport -pkg_erlport_description = ErlPort - connect Erlang to other languages -pkg_erlport_homepage = https://github.com/hdima/erlport -pkg_erlport_fetch = git -pkg_erlport_repo = https://github.com/hdima/erlport -pkg_erlport_commit = master - -PACKAGES += erlsh -pkg_erlsh_name = erlsh -pkg_erlsh_description = Erlang shell tools -pkg_erlsh_homepage = https://github.com/proger/erlsh -pkg_erlsh_fetch = git -pkg_erlsh_repo = https://github.com/proger/erlsh -pkg_erlsh_commit = master - -PACKAGES += erlsha2 -pkg_erlsha2_name = erlsha2 -pkg_erlsha2_description = SHA-224, SHA-256, SHA-384, SHA-512 implemented in Erlang NIFs. -pkg_erlsha2_homepage = https://github.com/vinoski/erlsha2 -pkg_erlsha2_fetch = git -pkg_erlsha2_repo = https://github.com/vinoski/erlsha2 -pkg_erlsha2_commit = master - -PACKAGES += erlsom -pkg_erlsom_name = erlsom -pkg_erlsom_description = XML parser for Erlang -pkg_erlsom_homepage = https://github.com/willemdj/erlsom -pkg_erlsom_fetch = git -pkg_erlsom_repo = https://github.com/willemdj/erlsom -pkg_erlsom_commit = master - -PACKAGES += erlubi -pkg_erlubi_name = erlubi -pkg_erlubi_description = Ubigraph Erlang Client (and Process Visualizer) -pkg_erlubi_homepage = https://github.com/krestenkrab/erlubi -pkg_erlubi_fetch = git -pkg_erlubi_repo = https://github.com/krestenkrab/erlubi -pkg_erlubi_commit = master - -PACKAGES += erlvolt -pkg_erlvolt_name = erlvolt -pkg_erlvolt_description = VoltDB Erlang Client Driver -pkg_erlvolt_homepage = https://github.com/VoltDB/voltdb-client-erlang -pkg_erlvolt_fetch = git -pkg_erlvolt_repo = https://github.com/VoltDB/voltdb-client-erlang -pkg_erlvolt_commit = master - -PACKAGES += erlware_commons -pkg_erlware_commons_name = erlware_commons -pkg_erlware_commons_description = Erlware Commons is an Erlware project focused on all aspects of reusable Erlang components. -pkg_erlware_commons_homepage = https://github.com/erlware/erlware_commons -pkg_erlware_commons_fetch = git -pkg_erlware_commons_repo = https://github.com/erlware/erlware_commons -pkg_erlware_commons_commit = master - -PACKAGES += erlydtl -pkg_erlydtl_name = erlydtl -pkg_erlydtl_description = Django Template Language for Erlang. -pkg_erlydtl_homepage = https://github.com/erlydtl/erlydtl -pkg_erlydtl_fetch = git -pkg_erlydtl_repo = https://github.com/erlydtl/erlydtl -pkg_erlydtl_commit = master - -PACKAGES += errd -pkg_errd_name = errd -pkg_errd_description = Erlang RRDTool library -pkg_errd_homepage = https://github.com/archaelus/errd -pkg_errd_fetch = git -pkg_errd_repo = https://github.com/archaelus/errd -pkg_errd_commit = master - -PACKAGES += erserve -pkg_erserve_name = erserve -pkg_erserve_description = Erlang/Rserve communication interface -pkg_erserve_homepage = https://github.com/del/erserve -pkg_erserve_fetch = git -pkg_erserve_repo = https://github.com/del/erserve -pkg_erserve_commit = master - -PACKAGES += erwa -pkg_erwa_name = erwa -pkg_erwa_description = A WAMP router and client written in Erlang. -pkg_erwa_homepage = https://github.com/bwegh/erwa -pkg_erwa_fetch = git -pkg_erwa_repo = https://github.com/bwegh/erwa -pkg_erwa_commit = master - -PACKAGES += espec -pkg_espec_name = espec -pkg_espec_description = ESpec: Behaviour driven development framework for Erlang -pkg_espec_homepage = https://github.com/lucaspiller/espec -pkg_espec_fetch = git -pkg_espec_repo = https://github.com/lucaspiller/espec -pkg_espec_commit = master - -PACKAGES += estatsd -pkg_estatsd_name = estatsd -pkg_estatsd_description = Erlang stats aggregation app that periodically flushes data to graphite -pkg_estatsd_homepage = https://github.com/RJ/estatsd -pkg_estatsd_fetch = git -pkg_estatsd_repo = https://github.com/RJ/estatsd -pkg_estatsd_commit = master - -PACKAGES += etap -pkg_etap_name = etap -pkg_etap_description = etap is a simple erlang testing library that provides TAP compliant output. -pkg_etap_homepage = https://github.com/ngerakines/etap -pkg_etap_fetch = git -pkg_etap_repo = https://github.com/ngerakines/etap -pkg_etap_commit = master - -PACKAGES += etest -pkg_etest_name = etest -pkg_etest_description = A lightweight, convention over configuration test framework for Erlang -pkg_etest_homepage = https://github.com/wooga/etest -pkg_etest_fetch = git -pkg_etest_repo = https://github.com/wooga/etest -pkg_etest_commit = master - -PACKAGES += etest_http -pkg_etest_http_name = etest_http -pkg_etest_http_description = etest Assertions around HTTP (client-side) -pkg_etest_http_homepage = https://github.com/wooga/etest_http -pkg_etest_http_fetch = git -pkg_etest_http_repo = https://github.com/wooga/etest_http -pkg_etest_http_commit = master - -PACKAGES += etoml -pkg_etoml_name = etoml -pkg_etoml_description = TOML language erlang parser -pkg_etoml_homepage = https://github.com/kalta/etoml -pkg_etoml_fetch = git -pkg_etoml_repo = https://github.com/kalta/etoml -pkg_etoml_commit = master - -PACKAGES += eunit -pkg_eunit_name = eunit -pkg_eunit_description = The EUnit lightweight unit testing framework for Erlang - this is the canonical development repository. -pkg_eunit_homepage = https://github.com/richcarl/eunit -pkg_eunit_fetch = git -pkg_eunit_repo = https://github.com/richcarl/eunit -pkg_eunit_commit = master - -PACKAGES += eunit_formatters -pkg_eunit_formatters_name = eunit_formatters -pkg_eunit_formatters_description = Because eunit's output sucks. Let's make it better. -pkg_eunit_formatters_homepage = https://github.com/seancribbs/eunit_formatters -pkg_eunit_formatters_fetch = git -pkg_eunit_formatters_repo = https://github.com/seancribbs/eunit_formatters -pkg_eunit_formatters_commit = master - -PACKAGES += euthanasia -pkg_euthanasia_name = euthanasia -pkg_euthanasia_description = Merciful killer for your Erlang processes -pkg_euthanasia_homepage = https://github.com/doubleyou/euthanasia -pkg_euthanasia_fetch = git -pkg_euthanasia_repo = https://github.com/doubleyou/euthanasia -pkg_euthanasia_commit = master - -PACKAGES += evum -pkg_evum_name = evum -pkg_evum_description = Spawn Linux VMs as Erlang processes in the Erlang VM -pkg_evum_homepage = https://github.com/msantos/evum -pkg_evum_fetch = git -pkg_evum_repo = https://github.com/msantos/evum -pkg_evum_commit = master - -PACKAGES += exec -pkg_exec_name = erlexec -pkg_exec_description = Execute and control OS processes from Erlang/OTP. -pkg_exec_homepage = http://saleyn.github.com/erlexec -pkg_exec_fetch = git -pkg_exec_repo = https://github.com/saleyn/erlexec -pkg_exec_commit = master - -PACKAGES += exml -pkg_exml_name = exml -pkg_exml_description = XML parsing library in Erlang -pkg_exml_homepage = https://github.com/paulgray/exml -pkg_exml_fetch = git -pkg_exml_repo = https://github.com/paulgray/exml -pkg_exml_commit = master - -PACKAGES += exometer -pkg_exometer_name = exometer -pkg_exometer_description = Basic measurement objects and probe behavior -pkg_exometer_homepage = https://github.com/Feuerlabs/exometer -pkg_exometer_fetch = git -pkg_exometer_repo = https://github.com/Feuerlabs/exometer -pkg_exometer_commit = master - -PACKAGES += exs1024 -pkg_exs1024_name = exs1024 -pkg_exs1024_description = Xorshift1024star pseudo random number generator for Erlang. -pkg_exs1024_homepage = https://github.com/jj1bdx/exs1024 -pkg_exs1024_fetch = git -pkg_exs1024_repo = https://github.com/jj1bdx/exs1024 -pkg_exs1024_commit = master - -PACKAGES += exs64 -pkg_exs64_name = exs64 -pkg_exs64_description = Xorshift64star pseudo random number generator for Erlang. -pkg_exs64_homepage = https://github.com/jj1bdx/exs64 -pkg_exs64_fetch = git -pkg_exs64_repo = https://github.com/jj1bdx/exs64 -pkg_exs64_commit = master - -PACKAGES += exsplus116 -pkg_exsplus116_name = exsplus116 -pkg_exsplus116_description = Xorshift116plus for Erlang -pkg_exsplus116_homepage = https://github.com/jj1bdx/exsplus116 -pkg_exsplus116_fetch = git -pkg_exsplus116_repo = https://github.com/jj1bdx/exsplus116 -pkg_exsplus116_commit = master - -PACKAGES += exsplus128 -pkg_exsplus128_name = exsplus128 -pkg_exsplus128_description = Xorshift128plus pseudo random number generator for Erlang. -pkg_exsplus128_homepage = https://github.com/jj1bdx/exsplus128 -pkg_exsplus128_fetch = git -pkg_exsplus128_repo = https://github.com/jj1bdx/exsplus128 -pkg_exsplus128_commit = master - -PACKAGES += ezmq -pkg_ezmq_name = ezmq -pkg_ezmq_description = zMQ implemented in Erlang -pkg_ezmq_homepage = https://github.com/RoadRunnr/ezmq -pkg_ezmq_fetch = git -pkg_ezmq_repo = https://github.com/RoadRunnr/ezmq -pkg_ezmq_commit = master - -PACKAGES += ezmtp -pkg_ezmtp_name = ezmtp -pkg_ezmtp_description = ZMTP protocol in pure Erlang. -pkg_ezmtp_homepage = https://github.com/a13x/ezmtp -pkg_ezmtp_fetch = git -pkg_ezmtp_repo = https://github.com/a13x/ezmtp -pkg_ezmtp_commit = master - -PACKAGES += fast_disk_log -pkg_fast_disk_log_name = fast_disk_log -pkg_fast_disk_log_description = Pool-based asynchronous Erlang disk logger -pkg_fast_disk_log_homepage = https://github.com/lpgauth/fast_disk_log -pkg_fast_disk_log_fetch = git -pkg_fast_disk_log_repo = https://github.com/lpgauth/fast_disk_log -pkg_fast_disk_log_commit = master - -PACKAGES += feeder -pkg_feeder_name = feeder -pkg_feeder_description = Stream parse RSS and Atom formatted XML feeds. -pkg_feeder_homepage = https://github.com/michaelnisi/feeder -pkg_feeder_fetch = git -pkg_feeder_repo = https://github.com/michaelnisi/feeder -pkg_feeder_commit = master - -PACKAGES += find_crate -pkg_find_crate_name = find_crate -pkg_find_crate_description = Find Rust libs and exes in Erlang application priv directory -pkg_find_crate_homepage = https://github.com/goertzenator/find_crate -pkg_find_crate_fetch = git -pkg_find_crate_repo = https://github.com/goertzenator/find_crate -pkg_find_crate_commit = master - -PACKAGES += fix -pkg_fix_name = fix -pkg_fix_description = http://fixprotocol.org/ implementation. -pkg_fix_homepage = https://github.com/maxlapshin/fix -pkg_fix_fetch = git -pkg_fix_repo = https://github.com/maxlapshin/fix -pkg_fix_commit = master - -PACKAGES += flower -pkg_flower_name = flower -pkg_flower_description = FlowER - a Erlang OpenFlow development platform -pkg_flower_homepage = https://github.com/travelping/flower -pkg_flower_fetch = git -pkg_flower_repo = https://github.com/travelping/flower -pkg_flower_commit = master - -PACKAGES += fn -pkg_fn_name = fn -pkg_fn_description = Function utilities for Erlang -pkg_fn_homepage = https://github.com/reiddraper/fn -pkg_fn_fetch = git -pkg_fn_repo = https://github.com/reiddraper/fn -pkg_fn_commit = master - -PACKAGES += folsom -pkg_folsom_name = folsom -pkg_folsom_description = Expose Erlang Events and Metrics -pkg_folsom_homepage = https://github.com/boundary/folsom -pkg_folsom_fetch = git -pkg_folsom_repo = https://github.com/boundary/folsom -pkg_folsom_commit = master - -PACKAGES += folsom_cowboy -pkg_folsom_cowboy_name = folsom_cowboy -pkg_folsom_cowboy_description = A Cowboy based Folsom HTTP Wrapper. -pkg_folsom_cowboy_homepage = https://github.com/boundary/folsom_cowboy -pkg_folsom_cowboy_fetch = git -pkg_folsom_cowboy_repo = https://github.com/boundary/folsom_cowboy -pkg_folsom_cowboy_commit = master - -PACKAGES += folsomite -pkg_folsomite_name = folsomite -pkg_folsomite_description = blow up your graphite / riemann server with folsom metrics -pkg_folsomite_homepage = https://github.com/campanja/folsomite -pkg_folsomite_fetch = git -pkg_folsomite_repo = https://github.com/campanja/folsomite -pkg_folsomite_commit = master - -PACKAGES += fs -pkg_fs_name = fs -pkg_fs_description = Erlang FileSystem Listener -pkg_fs_homepage = https://github.com/synrc/fs -pkg_fs_fetch = git -pkg_fs_repo = https://github.com/synrc/fs -pkg_fs_commit = master - -PACKAGES += fuse -pkg_fuse_name = fuse -pkg_fuse_description = A Circuit Breaker for Erlang -pkg_fuse_homepage = https://github.com/jlouis/fuse -pkg_fuse_fetch = git -pkg_fuse_repo = https://github.com/jlouis/fuse -pkg_fuse_commit = master - -PACKAGES += gcm -pkg_gcm_name = gcm -pkg_gcm_description = An Erlang application for Google Cloud Messaging -pkg_gcm_homepage = https://github.com/pdincau/gcm-erlang -pkg_gcm_fetch = git -pkg_gcm_repo = https://github.com/pdincau/gcm-erlang -pkg_gcm_commit = master - -PACKAGES += gcprof -pkg_gcprof_name = gcprof -pkg_gcprof_description = Garbage Collection profiler for Erlang -pkg_gcprof_homepage = https://github.com/knutin/gcprof -pkg_gcprof_fetch = git -pkg_gcprof_repo = https://github.com/knutin/gcprof -pkg_gcprof_commit = master - -PACKAGES += geas -pkg_geas_name = geas -pkg_geas_description = Guess Erlang Application Scattering -pkg_geas_homepage = https://github.com/crownedgrouse/geas -pkg_geas_fetch = git -pkg_geas_repo = https://github.com/crownedgrouse/geas -pkg_geas_commit = master - -PACKAGES += geef -pkg_geef_name = geef -pkg_geef_description = Git NEEEEF (Erlang NIF) -pkg_geef_homepage = https://github.com/carlosmn/geef -pkg_geef_fetch = git -pkg_geef_repo = https://github.com/carlosmn/geef -pkg_geef_commit = master - -PACKAGES += gen_coap -pkg_gen_coap_name = gen_coap -pkg_gen_coap_description = Generic Erlang CoAP Client/Server -pkg_gen_coap_homepage = https://github.com/gotthardp/gen_coap -pkg_gen_coap_fetch = git -pkg_gen_coap_repo = https://github.com/gotthardp/gen_coap -pkg_gen_coap_commit = master - -PACKAGES += gen_cycle -pkg_gen_cycle_name = gen_cycle -pkg_gen_cycle_description = Simple, generic OTP behaviour for recurring tasks -pkg_gen_cycle_homepage = https://github.com/aerosol/gen_cycle -pkg_gen_cycle_fetch = git -pkg_gen_cycle_repo = https://github.com/aerosol/gen_cycle -pkg_gen_cycle_commit = develop - -PACKAGES += gen_icmp -pkg_gen_icmp_name = gen_icmp -pkg_gen_icmp_description = Erlang interface to ICMP sockets -pkg_gen_icmp_homepage = https://github.com/msantos/gen_icmp -pkg_gen_icmp_fetch = git -pkg_gen_icmp_repo = https://github.com/msantos/gen_icmp -pkg_gen_icmp_commit = master - -PACKAGES += gen_nb_server -pkg_gen_nb_server_name = gen_nb_server -pkg_gen_nb_server_description = OTP behavior for writing non-blocking servers -pkg_gen_nb_server_homepage = https://github.com/kevsmith/gen_nb_server -pkg_gen_nb_server_fetch = git -pkg_gen_nb_server_repo = https://github.com/kevsmith/gen_nb_server -pkg_gen_nb_server_commit = master - -PACKAGES += gen_paxos -pkg_gen_paxos_name = gen_paxos -pkg_gen_paxos_description = An Erlang/OTP-style implementation of the PAXOS distributed consensus protocol -pkg_gen_paxos_homepage = https://github.com/gburd/gen_paxos -pkg_gen_paxos_fetch = git -pkg_gen_paxos_repo = https://github.com/gburd/gen_paxos -pkg_gen_paxos_commit = master - -PACKAGES += gen_smtp -pkg_gen_smtp_name = gen_smtp -pkg_gen_smtp_description = A generic Erlang SMTP server and client that can be extended via callback modules -pkg_gen_smtp_homepage = https://github.com/Vagabond/gen_smtp -pkg_gen_smtp_fetch = git -pkg_gen_smtp_repo = https://github.com/Vagabond/gen_smtp -pkg_gen_smtp_commit = master - -PACKAGES += gen_tracker -pkg_gen_tracker_name = gen_tracker -pkg_gen_tracker_description = supervisor with ets handling of children and their metadata -pkg_gen_tracker_homepage = https://github.com/erlyvideo/gen_tracker -pkg_gen_tracker_fetch = git -pkg_gen_tracker_repo = https://github.com/erlyvideo/gen_tracker -pkg_gen_tracker_commit = master - -PACKAGES += gen_unix -pkg_gen_unix_name = gen_unix -pkg_gen_unix_description = Erlang Unix socket interface -pkg_gen_unix_homepage = https://github.com/msantos/gen_unix -pkg_gen_unix_fetch = git -pkg_gen_unix_repo = https://github.com/msantos/gen_unix -pkg_gen_unix_commit = master - -PACKAGES += geode -pkg_geode_name = geode -pkg_geode_description = geohash/proximity lookup in pure, uncut erlang. -pkg_geode_homepage = https://github.com/bradfordw/geode -pkg_geode_fetch = git -pkg_geode_repo = https://github.com/bradfordw/geode -pkg_geode_commit = master - -PACKAGES += getopt -pkg_getopt_name = getopt -pkg_getopt_description = Module to parse command line arguments using the GNU getopt syntax -pkg_getopt_homepage = https://github.com/jcomellas/getopt -pkg_getopt_fetch = git -pkg_getopt_repo = https://github.com/jcomellas/getopt -pkg_getopt_commit = master - -PACKAGES += gettext -pkg_gettext_name = gettext -pkg_gettext_description = Erlang internationalization library. -pkg_gettext_homepage = https://github.com/etnt/gettext -pkg_gettext_fetch = git -pkg_gettext_repo = https://github.com/etnt/gettext -pkg_gettext_commit = master - -PACKAGES += giallo -pkg_giallo_name = giallo -pkg_giallo_description = Small and flexible web framework on top of Cowboy -pkg_giallo_homepage = https://github.com/kivra/giallo -pkg_giallo_fetch = git -pkg_giallo_repo = https://github.com/kivra/giallo -pkg_giallo_commit = master - -PACKAGES += gin -pkg_gin_name = gin -pkg_gin_description = The guards and for Erlang parse_transform -pkg_gin_homepage = https://github.com/mad-cocktail/gin -pkg_gin_fetch = git -pkg_gin_repo = https://github.com/mad-cocktail/gin -pkg_gin_commit = master - -PACKAGES += gitty -pkg_gitty_name = gitty -pkg_gitty_description = Git access in erlang -pkg_gitty_homepage = https://github.com/maxlapshin/gitty -pkg_gitty_fetch = git -pkg_gitty_repo = https://github.com/maxlapshin/gitty -pkg_gitty_commit = master - -PACKAGES += gold_fever -pkg_gold_fever_name = gold_fever -pkg_gold_fever_description = A Treasure Hunt for Erlangers -pkg_gold_fever_homepage = https://github.com/inaka/gold_fever -pkg_gold_fever_fetch = git -pkg_gold_fever_repo = https://github.com/inaka/gold_fever -pkg_gold_fever_commit = master - -PACKAGES += gossiperl -pkg_gossiperl_name = gossiperl -pkg_gossiperl_description = Gossip middleware in Erlang -pkg_gossiperl_homepage = http://gossiperl.com/ -pkg_gossiperl_fetch = git -pkg_gossiperl_repo = https://github.com/gossiperl/gossiperl -pkg_gossiperl_commit = master - -PACKAGES += gpb -pkg_gpb_name = gpb -pkg_gpb_description = A Google Protobuf implementation for Erlang -pkg_gpb_homepage = https://github.com/tomas-abrahamsson/gpb -pkg_gpb_fetch = git -pkg_gpb_repo = https://github.com/tomas-abrahamsson/gpb -pkg_gpb_commit = master - -PACKAGES += gproc -pkg_gproc_name = gproc -pkg_gproc_description = Extended process registry for Erlang -pkg_gproc_homepage = https://github.com/uwiger/gproc -pkg_gproc_fetch = git -pkg_gproc_repo = https://github.com/uwiger/gproc -pkg_gproc_commit = master - -PACKAGES += grapherl -pkg_grapherl_name = grapherl -pkg_grapherl_description = Create graphs of Erlang systems and programs -pkg_grapherl_homepage = https://github.com/eproxus/grapherl -pkg_grapherl_fetch = git -pkg_grapherl_repo = https://github.com/eproxus/grapherl -pkg_grapherl_commit = master - -PACKAGES += gun -pkg_gun_name = gun -pkg_gun_description = Asynchronous SPDY, HTTP and Websocket client written in Erlang. -pkg_gun_homepage = http//ninenines.eu -pkg_gun_fetch = git -pkg_gun_repo = https://github.com/ninenines/gun -pkg_gun_commit = master - -PACKAGES += gut -pkg_gut_name = gut -pkg_gut_description = gut is a template printing, aka scaffolding, tool for Erlang. Like rails generate or yeoman -pkg_gut_homepage = https://github.com/unbalancedparentheses/gut -pkg_gut_fetch = git -pkg_gut_repo = https://github.com/unbalancedparentheses/gut -pkg_gut_commit = master - -PACKAGES += hackney -pkg_hackney_name = hackney -pkg_hackney_description = simple HTTP client in Erlang -pkg_hackney_homepage = https://github.com/benoitc/hackney -pkg_hackney_fetch = git -pkg_hackney_repo = https://github.com/benoitc/hackney -pkg_hackney_commit = master - -PACKAGES += hamcrest -pkg_hamcrest_name = hamcrest -pkg_hamcrest_description = Erlang port of Hamcrest -pkg_hamcrest_homepage = https://github.com/hyperthunk/hamcrest-erlang -pkg_hamcrest_fetch = git -pkg_hamcrest_repo = https://github.com/hyperthunk/hamcrest-erlang -pkg_hamcrest_commit = master - -PACKAGES += hanoidb -pkg_hanoidb_name = hanoidb -pkg_hanoidb_description = Erlang LSM BTree Storage -pkg_hanoidb_homepage = https://github.com/krestenkrab/hanoidb -pkg_hanoidb_fetch = git -pkg_hanoidb_repo = https://github.com/krestenkrab/hanoidb -pkg_hanoidb_commit = master - -PACKAGES += hottub -pkg_hottub_name = hottub -pkg_hottub_description = Permanent Erlang Worker Pool -pkg_hottub_homepage = https://github.com/bfrog/hottub -pkg_hottub_fetch = git -pkg_hottub_repo = https://github.com/bfrog/hottub -pkg_hottub_commit = master - -PACKAGES += hpack -pkg_hpack_name = hpack -pkg_hpack_description = HPACK Implementation for Erlang -pkg_hpack_homepage = https://github.com/joedevivo/hpack -pkg_hpack_fetch = git -pkg_hpack_repo = https://github.com/joedevivo/hpack -pkg_hpack_commit = master - -PACKAGES += hyper -pkg_hyper_name = hyper -pkg_hyper_description = Erlang implementation of HyperLogLog -pkg_hyper_homepage = https://github.com/GameAnalytics/hyper -pkg_hyper_fetch = git -pkg_hyper_repo = https://github.com/GameAnalytics/hyper -pkg_hyper_commit = master - -PACKAGES += i18n -pkg_i18n_name = i18n -pkg_i18n_description = International components for unicode from Erlang (unicode, date, string, number, format, locale, localization, transliteration, icu4e) -pkg_i18n_homepage = https://github.com/erlang-unicode/i18n -pkg_i18n_fetch = git -pkg_i18n_repo = https://github.com/erlang-unicode/i18n -pkg_i18n_commit = master - -PACKAGES += ibrowse -pkg_ibrowse_name = ibrowse -pkg_ibrowse_description = Erlang HTTP client -pkg_ibrowse_homepage = https://github.com/cmullaparthi/ibrowse -pkg_ibrowse_fetch = git -pkg_ibrowse_repo = https://github.com/cmullaparthi/ibrowse -pkg_ibrowse_commit = master - -PACKAGES += ierlang -pkg_ierlang_name = ierlang -pkg_ierlang_description = An Erlang language kernel for IPython. -pkg_ierlang_homepage = https://github.com/robbielynch/ierlang -pkg_ierlang_fetch = git -pkg_ierlang_repo = https://github.com/robbielynch/ierlang -pkg_ierlang_commit = master - -PACKAGES += iota -pkg_iota_name = iota -pkg_iota_description = iota (Inter-dependency Objective Testing Apparatus) - a tool to enforce clean separation of responsibilities in Erlang code -pkg_iota_homepage = https://github.com/jpgneves/iota -pkg_iota_fetch = git -pkg_iota_repo = https://github.com/jpgneves/iota -pkg_iota_commit = master - -PACKAGES += irc_lib -pkg_irc_lib_name = irc_lib -pkg_irc_lib_description = Erlang irc client library -pkg_irc_lib_homepage = https://github.com/OtpChatBot/irc_lib -pkg_irc_lib_fetch = git -pkg_irc_lib_repo = https://github.com/OtpChatBot/irc_lib -pkg_irc_lib_commit = master - -PACKAGES += ircd -pkg_ircd_name = ircd -pkg_ircd_description = A pluggable IRC daemon application/library for Erlang. -pkg_ircd_homepage = https://github.com/tonyg/erlang-ircd -pkg_ircd_fetch = git -pkg_ircd_repo = https://github.com/tonyg/erlang-ircd -pkg_ircd_commit = master - -PACKAGES += iris -pkg_iris_name = iris -pkg_iris_description = Iris Erlang binding -pkg_iris_homepage = https://github.com/project-iris/iris-erl -pkg_iris_fetch = git -pkg_iris_repo = https://github.com/project-iris/iris-erl -pkg_iris_commit = master - -PACKAGES += iso8601 -pkg_iso8601_name = iso8601 -pkg_iso8601_description = Erlang ISO 8601 date formatter/parser -pkg_iso8601_homepage = https://github.com/seansawyer/erlang_iso8601 -pkg_iso8601_fetch = git -pkg_iso8601_repo = https://github.com/seansawyer/erlang_iso8601 -pkg_iso8601_commit = master - -PACKAGES += jamdb_sybase -pkg_jamdb_sybase_name = jamdb_sybase -pkg_jamdb_sybase_description = Erlang driver for SAP Sybase ASE -pkg_jamdb_sybase_homepage = https://github.com/erlangbureau/jamdb_sybase -pkg_jamdb_sybase_fetch = git -pkg_jamdb_sybase_repo = https://github.com/erlangbureau/jamdb_sybase -pkg_jamdb_sybase_commit = master - -PACKAGES += jerg -pkg_jerg_name = jerg -pkg_jerg_description = JSON Schema to Erlang Records Generator -pkg_jerg_homepage = https://github.com/ddossot/jerg -pkg_jerg_fetch = git -pkg_jerg_repo = https://github.com/ddossot/jerg -pkg_jerg_commit = master - -PACKAGES += jesse -pkg_jesse_name = jesse -pkg_jesse_description = jesse (JSon Schema Erlang) is an implementation of a json schema validator for Erlang. -pkg_jesse_homepage = https://github.com/for-GET/jesse -pkg_jesse_fetch = git -pkg_jesse_repo = https://github.com/for-GET/jesse -pkg_jesse_commit = master - -PACKAGES += jiffy -pkg_jiffy_name = jiffy -pkg_jiffy_description = JSON NIFs for Erlang. -pkg_jiffy_homepage = https://github.com/davisp/jiffy -pkg_jiffy_fetch = git -pkg_jiffy_repo = https://github.com/davisp/jiffy -pkg_jiffy_commit = master - -PACKAGES += jiffy_v -pkg_jiffy_v_name = jiffy_v -pkg_jiffy_v_description = JSON validation utility -pkg_jiffy_v_homepage = https://github.com/shizzard/jiffy-v -pkg_jiffy_v_fetch = git -pkg_jiffy_v_repo = https://github.com/shizzard/jiffy-v -pkg_jiffy_v_commit = master - -PACKAGES += jobs -pkg_jobs_name = jobs -pkg_jobs_description = a Job scheduler for load regulation -pkg_jobs_homepage = https://github.com/esl/jobs -pkg_jobs_fetch = git -pkg_jobs_repo = https://github.com/esl/jobs -pkg_jobs_commit = master - -PACKAGES += joxa -pkg_joxa_name = joxa -pkg_joxa_description = A Modern Lisp for the Erlang VM -pkg_joxa_homepage = https://github.com/joxa/joxa -pkg_joxa_fetch = git -pkg_joxa_repo = https://github.com/joxa/joxa -pkg_joxa_commit = master - -PACKAGES += json -pkg_json_name = json -pkg_json_description = a high level json library for erlang (17.0+) -pkg_json_homepage = https://github.com/talentdeficit/json -pkg_json_fetch = git -pkg_json_repo = https://github.com/talentdeficit/json -pkg_json_commit = master - -PACKAGES += json_rec -pkg_json_rec_name = json_rec -pkg_json_rec_description = JSON to erlang record -pkg_json_rec_homepage = https://github.com/justinkirby/json_rec -pkg_json_rec_fetch = git -pkg_json_rec_repo = https://github.com/justinkirby/json_rec -pkg_json_rec_commit = master - -PACKAGES += jsone -pkg_jsone_name = jsone -pkg_jsone_description = An Erlang library for encoding, decoding JSON data. -pkg_jsone_homepage = https://github.com/sile/jsone.git -pkg_jsone_fetch = git -pkg_jsone_repo = https://github.com/sile/jsone.git -pkg_jsone_commit = master - -PACKAGES += jsonerl -pkg_jsonerl_name = jsonerl -pkg_jsonerl_description = yet another but slightly different erlang <-> json encoder/decoder -pkg_jsonerl_homepage = https://github.com/lambder/jsonerl -pkg_jsonerl_fetch = git -pkg_jsonerl_repo = https://github.com/lambder/jsonerl -pkg_jsonerl_commit = master - -PACKAGES += jsonpath -pkg_jsonpath_name = jsonpath -pkg_jsonpath_description = Fast Erlang JSON data retrieval and updates via javascript-like notation -pkg_jsonpath_homepage = https://github.com/GeneStevens/jsonpath -pkg_jsonpath_fetch = git -pkg_jsonpath_repo = https://github.com/GeneStevens/jsonpath -pkg_jsonpath_commit = master - -PACKAGES += jsonx -pkg_jsonx_name = jsonx -pkg_jsonx_description = JSONX is an Erlang library for efficient decode and encode JSON, written in C. -pkg_jsonx_homepage = https://github.com/iskra/jsonx -pkg_jsonx_fetch = git -pkg_jsonx_repo = https://github.com/iskra/jsonx -pkg_jsonx_commit = master - -PACKAGES += jsx -pkg_jsx_name = jsx -pkg_jsx_description = An Erlang application for consuming, producing and manipulating JSON. -pkg_jsx_homepage = https://github.com/talentdeficit/jsx -pkg_jsx_fetch = git -pkg_jsx_repo = https://github.com/talentdeficit/jsx -pkg_jsx_commit = master - -PACKAGES += kafka -pkg_kafka_name = kafka -pkg_kafka_description = Kafka consumer and producer in Erlang -pkg_kafka_homepage = https://github.com/wooga/kafka-erlang -pkg_kafka_fetch = git -pkg_kafka_repo = https://github.com/wooga/kafka-erlang -pkg_kafka_commit = master - -PACKAGES += kafka_protocol -pkg_kafka_protocol_name = kafka_protocol -pkg_kafka_protocol_description = Kafka protocol Erlang library -pkg_kafka_protocol_homepage = https://github.com/klarna/kafka_protocol -pkg_kafka_protocol_fetch = git -pkg_kafka_protocol_repo = https://github.com/klarna/kafka_protocol.git -pkg_kafka_protocol_commit = master - -PACKAGES += kai -pkg_kai_name = kai -pkg_kai_description = DHT storage by Takeshi Inoue -pkg_kai_homepage = https://github.com/synrc/kai -pkg_kai_fetch = git -pkg_kai_repo = https://github.com/synrc/kai -pkg_kai_commit = master - -PACKAGES += katja -pkg_katja_name = katja -pkg_katja_description = A simple Riemann client written in Erlang. -pkg_katja_homepage = https://github.com/nifoc/katja -pkg_katja_fetch = git -pkg_katja_repo = https://github.com/nifoc/katja -pkg_katja_commit = master - -PACKAGES += kdht -pkg_kdht_name = kdht -pkg_kdht_description = kdht is an erlang DHT implementation -pkg_kdht_homepage = https://github.com/kevinlynx/kdht -pkg_kdht_fetch = git -pkg_kdht_repo = https://github.com/kevinlynx/kdht -pkg_kdht_commit = master - -PACKAGES += key2value -pkg_key2value_name = key2value -pkg_key2value_description = Erlang 2-way map -pkg_key2value_homepage = https://github.com/okeuday/key2value -pkg_key2value_fetch = git -pkg_key2value_repo = https://github.com/okeuday/key2value -pkg_key2value_commit = master - -PACKAGES += keys1value -pkg_keys1value_name = keys1value -pkg_keys1value_description = Erlang set associative map for key lists -pkg_keys1value_homepage = https://github.com/okeuday/keys1value -pkg_keys1value_fetch = git -pkg_keys1value_repo = https://github.com/okeuday/keys1value -pkg_keys1value_commit = master - -PACKAGES += kinetic -pkg_kinetic_name = kinetic -pkg_kinetic_description = Erlang Kinesis Client -pkg_kinetic_homepage = https://github.com/AdRoll/kinetic -pkg_kinetic_fetch = git -pkg_kinetic_repo = https://github.com/AdRoll/kinetic -pkg_kinetic_commit = master - -PACKAGES += kjell -pkg_kjell_name = kjell -pkg_kjell_description = Erlang Shell -pkg_kjell_homepage = https://github.com/karlll/kjell -pkg_kjell_fetch = git -pkg_kjell_repo = https://github.com/karlll/kjell -pkg_kjell_commit = master - -PACKAGES += kraken -pkg_kraken_name = kraken -pkg_kraken_description = Distributed Pubsub Server for Realtime Apps -pkg_kraken_homepage = https://github.com/Asana/kraken -pkg_kraken_fetch = git -pkg_kraken_repo = https://github.com/Asana/kraken -pkg_kraken_commit = master - -PACKAGES += kucumberl -pkg_kucumberl_name = kucumberl -pkg_kucumberl_description = A pure-erlang, open-source, implementation of Cucumber -pkg_kucumberl_homepage = https://github.com/openshine/kucumberl -pkg_kucumberl_fetch = git -pkg_kucumberl_repo = https://github.com/openshine/kucumberl -pkg_kucumberl_commit = master - -PACKAGES += kvc -pkg_kvc_name = kvc -pkg_kvc_description = KVC - Key Value Coding for Erlang data structures -pkg_kvc_homepage = https://github.com/etrepum/kvc -pkg_kvc_fetch = git -pkg_kvc_repo = https://github.com/etrepum/kvc -pkg_kvc_commit = master - -PACKAGES += kvlists -pkg_kvlists_name = kvlists -pkg_kvlists_description = Lists of key-value pairs (decoded JSON) in Erlang -pkg_kvlists_homepage = https://github.com/jcomellas/kvlists -pkg_kvlists_fetch = git -pkg_kvlists_repo = https://github.com/jcomellas/kvlists -pkg_kvlists_commit = master - -PACKAGES += kvs -pkg_kvs_name = kvs -pkg_kvs_description = Container and Iterator -pkg_kvs_homepage = https://github.com/synrc/kvs -pkg_kvs_fetch = git -pkg_kvs_repo = https://github.com/synrc/kvs -pkg_kvs_commit = master - -PACKAGES += lager -pkg_lager_name = lager -pkg_lager_description = A logging framework for Erlang/OTP. -pkg_lager_homepage = https://github.com/basho/lager -pkg_lager_fetch = git -pkg_lager_repo = https://github.com/basho/lager -pkg_lager_commit = master - -PACKAGES += lager_amqp_backend -pkg_lager_amqp_backend_name = lager_amqp_backend -pkg_lager_amqp_backend_description = AMQP RabbitMQ Lager backend -pkg_lager_amqp_backend_homepage = https://github.com/jbrisbin/lager_amqp_backend -pkg_lager_amqp_backend_fetch = git -pkg_lager_amqp_backend_repo = https://github.com/jbrisbin/lager_amqp_backend -pkg_lager_amqp_backend_commit = master - -PACKAGES += lager_syslog -pkg_lager_syslog_name = lager_syslog -pkg_lager_syslog_description = Syslog backend for lager -pkg_lager_syslog_homepage = https://github.com/basho/lager_syslog -pkg_lager_syslog_fetch = git -pkg_lager_syslog_repo = https://github.com/basho/lager_syslog -pkg_lager_syslog_commit = master - -PACKAGES += lambdapad -pkg_lambdapad_name = lambdapad -pkg_lambdapad_description = Static site generator using Erlang. Yes, Erlang. -pkg_lambdapad_homepage = https://github.com/gar1t/lambdapad -pkg_lambdapad_fetch = git -pkg_lambdapad_repo = https://github.com/gar1t/lambdapad -pkg_lambdapad_commit = master - -PACKAGES += lasp -pkg_lasp_name = lasp -pkg_lasp_description = A Language for Distributed, Eventually Consistent Computations -pkg_lasp_homepage = http://lasp-lang.org/ -pkg_lasp_fetch = git -pkg_lasp_repo = https://github.com/lasp-lang/lasp -pkg_lasp_commit = master - -PACKAGES += lasse -pkg_lasse_name = lasse -pkg_lasse_description = SSE handler for Cowboy -pkg_lasse_homepage = https://github.com/inaka/lasse -pkg_lasse_fetch = git -pkg_lasse_repo = https://github.com/inaka/lasse -pkg_lasse_commit = master - -PACKAGES += ldap -pkg_ldap_name = ldap -pkg_ldap_description = LDAP server written in Erlang -pkg_ldap_homepage = https://github.com/spawnproc/ldap -pkg_ldap_fetch = git -pkg_ldap_repo = https://github.com/spawnproc/ldap -pkg_ldap_commit = master - -PACKAGES += lethink -pkg_lethink_name = lethink -pkg_lethink_description = erlang driver for rethinkdb -pkg_lethink_homepage = https://github.com/taybin/lethink -pkg_lethink_fetch = git -pkg_lethink_repo = https://github.com/taybin/lethink -pkg_lethink_commit = master - -PACKAGES += lfe -pkg_lfe_name = lfe -pkg_lfe_description = Lisp Flavoured Erlang (LFE) -pkg_lfe_homepage = https://github.com/rvirding/lfe -pkg_lfe_fetch = git -pkg_lfe_repo = https://github.com/rvirding/lfe -pkg_lfe_commit = master - -PACKAGES += ling -pkg_ling_name = ling -pkg_ling_description = Erlang on Xen -pkg_ling_homepage = https://github.com/cloudozer/ling -pkg_ling_fetch = git -pkg_ling_repo = https://github.com/cloudozer/ling -pkg_ling_commit = master - -PACKAGES += live -pkg_live_name = live -pkg_live_description = Automated module and configuration reloader. -pkg_live_homepage = http://ninenines.eu -pkg_live_fetch = git -pkg_live_repo = https://github.com/ninenines/live -pkg_live_commit = master - -PACKAGES += lmq -pkg_lmq_name = lmq -pkg_lmq_description = Lightweight Message Queue -pkg_lmq_homepage = https://github.com/iij/lmq -pkg_lmq_fetch = git -pkg_lmq_repo = https://github.com/iij/lmq -pkg_lmq_commit = master - -PACKAGES += locker -pkg_locker_name = locker -pkg_locker_description = Atomic distributed 'check and set' for short-lived keys -pkg_locker_homepage = https://github.com/wooga/locker -pkg_locker_fetch = git -pkg_locker_repo = https://github.com/wooga/locker -pkg_locker_commit = master - -PACKAGES += locks -pkg_locks_name = locks -pkg_locks_description = A scalable, deadlock-resolving resource locker -pkg_locks_homepage = https://github.com/uwiger/locks -pkg_locks_fetch = git -pkg_locks_repo = https://github.com/uwiger/locks -pkg_locks_commit = master - -PACKAGES += log4erl -pkg_log4erl_name = log4erl -pkg_log4erl_description = A logger for erlang in the spirit of Log4J. -pkg_log4erl_homepage = https://github.com/ahmednawras/log4erl -pkg_log4erl_fetch = git -pkg_log4erl_repo = https://github.com/ahmednawras/log4erl -pkg_log4erl_commit = master - -PACKAGES += lol -pkg_lol_name = lol -pkg_lol_description = Lisp on erLang, and programming is fun again -pkg_lol_homepage = https://github.com/b0oh/lol -pkg_lol_fetch = git -pkg_lol_repo = https://github.com/b0oh/lol -pkg_lol_commit = master - -PACKAGES += lucid -pkg_lucid_name = lucid -pkg_lucid_description = HTTP/2 server written in Erlang -pkg_lucid_homepage = https://github.com/tatsuhiro-t/lucid -pkg_lucid_fetch = git -pkg_lucid_repo = https://github.com/tatsuhiro-t/lucid -pkg_lucid_commit = master - -PACKAGES += luerl -pkg_luerl_name = luerl -pkg_luerl_description = Lua in Erlang -pkg_luerl_homepage = https://github.com/rvirding/luerl -pkg_luerl_fetch = git -pkg_luerl_repo = https://github.com/rvirding/luerl -pkg_luerl_commit = develop - -PACKAGES += luwak -pkg_luwak_name = luwak -pkg_luwak_description = Large-object storage interface for Riak -pkg_luwak_homepage = https://github.com/basho/luwak -pkg_luwak_fetch = git -pkg_luwak_repo = https://github.com/basho/luwak -pkg_luwak_commit = master - -PACKAGES += lux -pkg_lux_name = lux -pkg_lux_description = Lux (LUcid eXpect scripting) simplifies test automation and provides an Expect-style execution of commands -pkg_lux_homepage = https://github.com/hawk/lux -pkg_lux_fetch = git -pkg_lux_repo = https://github.com/hawk/lux -pkg_lux_commit = master - -PACKAGES += machi -pkg_machi_name = machi -pkg_machi_description = Machi file store -pkg_machi_homepage = https://github.com/basho/machi -pkg_machi_fetch = git -pkg_machi_repo = https://github.com/basho/machi -pkg_machi_commit = master - -PACKAGES += mad -pkg_mad_name = mad -pkg_mad_description = Small and Fast Rebar Replacement -pkg_mad_homepage = https://github.com/synrc/mad -pkg_mad_fetch = git -pkg_mad_repo = https://github.com/synrc/mad -pkg_mad_commit = master - -PACKAGES += marina -pkg_marina_name = marina -pkg_marina_description = Non-blocking Erlang Cassandra CQL3 client -pkg_marina_homepage = https://github.com/lpgauth/marina -pkg_marina_fetch = git -pkg_marina_repo = https://github.com/lpgauth/marina -pkg_marina_commit = master - -PACKAGES += mavg -pkg_mavg_name = mavg -pkg_mavg_description = Erlang :: Exponential moving average library -pkg_mavg_homepage = https://github.com/EchoTeam/mavg -pkg_mavg_fetch = git -pkg_mavg_repo = https://github.com/EchoTeam/mavg -pkg_mavg_commit = master - -PACKAGES += mc_erl -pkg_mc_erl_name = mc_erl -pkg_mc_erl_description = mc-erl is a server for Minecraft 1.4.7 written in Erlang. -pkg_mc_erl_homepage = https://github.com/clonejo/mc-erl -pkg_mc_erl_fetch = git -pkg_mc_erl_repo = https://github.com/clonejo/mc-erl -pkg_mc_erl_commit = master - -PACKAGES += mcd -pkg_mcd_name = mcd -pkg_mcd_description = Fast memcached protocol client in pure Erlang -pkg_mcd_homepage = https://github.com/EchoTeam/mcd -pkg_mcd_fetch = git -pkg_mcd_repo = https://github.com/EchoTeam/mcd -pkg_mcd_commit = master - -PACKAGES += mcerlang -pkg_mcerlang_name = mcerlang -pkg_mcerlang_description = The McErlang model checker for Erlang -pkg_mcerlang_homepage = https://github.com/fredlund/McErlang -pkg_mcerlang_fetch = git -pkg_mcerlang_repo = https://github.com/fredlund/McErlang -pkg_mcerlang_commit = master - -PACKAGES += meck -pkg_meck_name = meck -pkg_meck_description = A mocking library for Erlang -pkg_meck_homepage = https://github.com/eproxus/meck -pkg_meck_fetch = git -pkg_meck_repo = https://github.com/eproxus/meck -pkg_meck_commit = master - -PACKAGES += mekao -pkg_mekao_name = mekao -pkg_mekao_description = SQL constructor -pkg_mekao_homepage = https://github.com/ddosia/mekao -pkg_mekao_fetch = git -pkg_mekao_repo = https://github.com/ddosia/mekao -pkg_mekao_commit = master - -PACKAGES += memo -pkg_memo_name = memo -pkg_memo_description = Erlang memoization server -pkg_memo_homepage = https://github.com/tuncer/memo -pkg_memo_fetch = git -pkg_memo_repo = https://github.com/tuncer/memo -pkg_memo_commit = master - -PACKAGES += merge_index -pkg_merge_index_name = merge_index -pkg_merge_index_description = MergeIndex is an Erlang library for storing ordered sets on disk. It is very similar to an SSTable (in Google's Bigtable) or an HFile (in Hadoop). -pkg_merge_index_homepage = https://github.com/basho/merge_index -pkg_merge_index_fetch = git -pkg_merge_index_repo = https://github.com/basho/merge_index -pkg_merge_index_commit = master - -PACKAGES += merl -pkg_merl_name = merl -pkg_merl_description = Metaprogramming in Erlang -pkg_merl_homepage = https://github.com/richcarl/merl -pkg_merl_fetch = git -pkg_merl_repo = https://github.com/richcarl/merl -pkg_merl_commit = master - -PACKAGES += mimerl -pkg_mimerl_name = mimerl -pkg_mimerl_description = library to handle mimetypes -pkg_mimerl_homepage = https://github.com/benoitc/mimerl -pkg_mimerl_fetch = git -pkg_mimerl_repo = https://github.com/benoitc/mimerl -pkg_mimerl_commit = master - -PACKAGES += mimetypes -pkg_mimetypes_name = mimetypes -pkg_mimetypes_description = Erlang MIME types library -pkg_mimetypes_homepage = https://github.com/spawngrid/mimetypes -pkg_mimetypes_fetch = git -pkg_mimetypes_repo = https://github.com/spawngrid/mimetypes -pkg_mimetypes_commit = master - -PACKAGES += mixer -pkg_mixer_name = mixer -pkg_mixer_description = Mix in functions from other modules -pkg_mixer_homepage = https://github.com/chef/mixer -pkg_mixer_fetch = git -pkg_mixer_repo = https://github.com/chef/mixer -pkg_mixer_commit = master - -PACKAGES += mochiweb -pkg_mochiweb_name = mochiweb -pkg_mochiweb_description = MochiWeb is an Erlang library for building lightweight HTTP servers. -pkg_mochiweb_homepage = https://github.com/mochi/mochiweb -pkg_mochiweb_fetch = git -pkg_mochiweb_repo = https://github.com/mochi/mochiweb -pkg_mochiweb_commit = master - -PACKAGES += mochiweb_xpath -pkg_mochiweb_xpath_name = mochiweb_xpath -pkg_mochiweb_xpath_description = XPath support for mochiweb's html parser -pkg_mochiweb_xpath_homepage = https://github.com/retnuh/mochiweb_xpath -pkg_mochiweb_xpath_fetch = git -pkg_mochiweb_xpath_repo = https://github.com/retnuh/mochiweb_xpath -pkg_mochiweb_xpath_commit = master - -PACKAGES += mockgyver -pkg_mockgyver_name = mockgyver -pkg_mockgyver_description = A mocking library for Erlang -pkg_mockgyver_homepage = https://github.com/klajo/mockgyver -pkg_mockgyver_fetch = git -pkg_mockgyver_repo = https://github.com/klajo/mockgyver -pkg_mockgyver_commit = master - -PACKAGES += modlib -pkg_modlib_name = modlib -pkg_modlib_description = Web framework based on Erlang's inets httpd -pkg_modlib_homepage = https://github.com/gar1t/modlib -pkg_modlib_fetch = git -pkg_modlib_repo = https://github.com/gar1t/modlib -pkg_modlib_commit = master - -PACKAGES += mongodb -pkg_mongodb_name = mongodb -pkg_mongodb_description = MongoDB driver for Erlang -pkg_mongodb_homepage = https://github.com/comtihon/mongodb-erlang -pkg_mongodb_fetch = git -pkg_mongodb_repo = https://github.com/comtihon/mongodb-erlang -pkg_mongodb_commit = master - -PACKAGES += mongooseim -pkg_mongooseim_name = mongooseim -pkg_mongooseim_description = Jabber / XMPP server with focus on performance and scalability, by Erlang Solutions -pkg_mongooseim_homepage = https://www.erlang-solutions.com/products/mongooseim-massively-scalable-ejabberd-platform -pkg_mongooseim_fetch = git -pkg_mongooseim_repo = https://github.com/esl/MongooseIM -pkg_mongooseim_commit = master - -PACKAGES += moyo -pkg_moyo_name = moyo -pkg_moyo_description = Erlang utility functions library -pkg_moyo_homepage = https://github.com/dwango/moyo -pkg_moyo_fetch = git -pkg_moyo_repo = https://github.com/dwango/moyo -pkg_moyo_commit = master - -PACKAGES += msgpack -pkg_msgpack_name = msgpack -pkg_msgpack_description = MessagePack (de)serializer implementation for Erlang -pkg_msgpack_homepage = https://github.com/msgpack/msgpack-erlang -pkg_msgpack_fetch = git -pkg_msgpack_repo = https://github.com/msgpack/msgpack-erlang -pkg_msgpack_commit = master - -PACKAGES += mu2 -pkg_mu2_name = mu2 -pkg_mu2_description = Erlang mutation testing tool -pkg_mu2_homepage = https://github.com/ramsay-t/mu2 -pkg_mu2_fetch = git -pkg_mu2_repo = https://github.com/ramsay-t/mu2 -pkg_mu2_commit = master - -PACKAGES += mustache -pkg_mustache_name = mustache -pkg_mustache_description = Mustache template engine for Erlang. -pkg_mustache_homepage = https://github.com/mojombo/mustache.erl -pkg_mustache_fetch = git -pkg_mustache_repo = https://github.com/mojombo/mustache.erl -pkg_mustache_commit = master - -PACKAGES += myproto -pkg_myproto_name = myproto -pkg_myproto_description = MySQL Server Protocol in Erlang -pkg_myproto_homepage = https://github.com/altenwald/myproto -pkg_myproto_fetch = git -pkg_myproto_repo = https://github.com/altenwald/myproto -pkg_myproto_commit = master - -PACKAGES += mysql -pkg_mysql_name = mysql -pkg_mysql_description = Erlang MySQL Driver (from code.google.com) -pkg_mysql_homepage = https://github.com/dizzyd/erlang-mysql-driver -pkg_mysql_fetch = git -pkg_mysql_repo = https://github.com/dizzyd/erlang-mysql-driver -pkg_mysql_commit = master - -PACKAGES += n2o -pkg_n2o_name = n2o -pkg_n2o_description = WebSocket Application Server -pkg_n2o_homepage = https://github.com/5HT/n2o -pkg_n2o_fetch = git -pkg_n2o_repo = https://github.com/5HT/n2o -pkg_n2o_commit = master - -PACKAGES += nat_upnp -pkg_nat_upnp_name = nat_upnp -pkg_nat_upnp_description = Erlang library to map your internal port to an external using UNP IGD -pkg_nat_upnp_homepage = https://github.com/benoitc/nat_upnp -pkg_nat_upnp_fetch = git -pkg_nat_upnp_repo = https://github.com/benoitc/nat_upnp -pkg_nat_upnp_commit = master - -PACKAGES += neo4j -pkg_neo4j_name = neo4j -pkg_neo4j_description = Erlang client library for Neo4J. -pkg_neo4j_homepage = https://github.com/dmitriid/neo4j-erlang -pkg_neo4j_fetch = git -pkg_neo4j_repo = https://github.com/dmitriid/neo4j-erlang -pkg_neo4j_commit = master - -PACKAGES += neotoma -pkg_neotoma_name = neotoma -pkg_neotoma_description = Erlang library and packrat parser-generator for parsing expression grammars. -pkg_neotoma_homepage = https://github.com/seancribbs/neotoma -pkg_neotoma_fetch = git -pkg_neotoma_repo = https://github.com/seancribbs/neotoma -pkg_neotoma_commit = master - -PACKAGES += newrelic -pkg_newrelic_name = newrelic -pkg_newrelic_description = Erlang library for sending metrics to New Relic -pkg_newrelic_homepage = https://github.com/wooga/newrelic-erlang -pkg_newrelic_fetch = git -pkg_newrelic_repo = https://github.com/wooga/newrelic-erlang -pkg_newrelic_commit = master - -PACKAGES += nifty -pkg_nifty_name = nifty -pkg_nifty_description = Erlang NIF wrapper generator -pkg_nifty_homepage = https://github.com/parapluu/nifty -pkg_nifty_fetch = git -pkg_nifty_repo = https://github.com/parapluu/nifty -pkg_nifty_commit = master - -PACKAGES += nitrogen_core -pkg_nitrogen_core_name = nitrogen_core -pkg_nitrogen_core_description = The core Nitrogen library. -pkg_nitrogen_core_homepage = http://nitrogenproject.com/ -pkg_nitrogen_core_fetch = git -pkg_nitrogen_core_repo = https://github.com/nitrogen/nitrogen_core -pkg_nitrogen_core_commit = master - -PACKAGES += nkbase -pkg_nkbase_name = nkbase -pkg_nkbase_description = NkBASE distributed database -pkg_nkbase_homepage = https://github.com/Nekso/nkbase -pkg_nkbase_fetch = git -pkg_nkbase_repo = https://github.com/Nekso/nkbase -pkg_nkbase_commit = develop - -PACKAGES += nkdocker -pkg_nkdocker_name = nkdocker -pkg_nkdocker_description = Erlang Docker client -pkg_nkdocker_homepage = https://github.com/Nekso/nkdocker -pkg_nkdocker_fetch = git -pkg_nkdocker_repo = https://github.com/Nekso/nkdocker -pkg_nkdocker_commit = master - -PACKAGES += nkpacket -pkg_nkpacket_name = nkpacket -pkg_nkpacket_description = Generic Erlang transport layer -pkg_nkpacket_homepage = https://github.com/Nekso/nkpacket -pkg_nkpacket_fetch = git -pkg_nkpacket_repo = https://github.com/Nekso/nkpacket -pkg_nkpacket_commit = master - -PACKAGES += nksip -pkg_nksip_name = nksip -pkg_nksip_description = Erlang SIP application server -pkg_nksip_homepage = https://github.com/kalta/nksip -pkg_nksip_fetch = git -pkg_nksip_repo = https://github.com/kalta/nksip -pkg_nksip_commit = master - -PACKAGES += nodefinder -pkg_nodefinder_name = nodefinder -pkg_nodefinder_description = automatic node discovery via UDP multicast -pkg_nodefinder_homepage = https://github.com/erlanger/nodefinder -pkg_nodefinder_fetch = git -pkg_nodefinder_repo = https://github.com/okeuday/nodefinder -pkg_nodefinder_commit = master - -PACKAGES += nprocreg -pkg_nprocreg_name = nprocreg -pkg_nprocreg_description = Minimal Distributed Erlang Process Registry -pkg_nprocreg_homepage = http://nitrogenproject.com/ -pkg_nprocreg_fetch = git -pkg_nprocreg_repo = https://github.com/nitrogen/nprocreg -pkg_nprocreg_commit = master - -PACKAGES += oauth -pkg_oauth_name = oauth -pkg_oauth_description = An Erlang OAuth 1.0 implementation -pkg_oauth_homepage = https://github.com/tim/erlang-oauth -pkg_oauth_fetch = git -pkg_oauth_repo = https://github.com/tim/erlang-oauth -pkg_oauth_commit = master - -PACKAGES += oauth2 -pkg_oauth2_name = oauth2 -pkg_oauth2_description = Erlang Oauth2 implementation -pkg_oauth2_homepage = https://github.com/kivra/oauth2 -pkg_oauth2_fetch = git -pkg_oauth2_repo = https://github.com/kivra/oauth2 -pkg_oauth2_commit = master - -PACKAGES += octopus -pkg_octopus_name = octopus -pkg_octopus_description = Small and flexible pool manager written in Erlang -pkg_octopus_homepage = https://github.com/erlangbureau/octopus -pkg_octopus_fetch = git -pkg_octopus_repo = https://github.com/erlangbureau/octopus -pkg_octopus_commit = master - -PACKAGES += of_protocol -pkg_of_protocol_name = of_protocol -pkg_of_protocol_description = OpenFlow Protocol Library for Erlang -pkg_of_protocol_homepage = https://github.com/FlowForwarding/of_protocol -pkg_of_protocol_fetch = git -pkg_of_protocol_repo = https://github.com/FlowForwarding/of_protocol -pkg_of_protocol_commit = master - -PACKAGES += opencouch -pkg_opencouch_name = couch -pkg_opencouch_description = A embeddable document oriented database compatible with Apache CouchDB -pkg_opencouch_homepage = https://github.com/benoitc/opencouch -pkg_opencouch_fetch = git -pkg_opencouch_repo = https://github.com/benoitc/opencouch -pkg_opencouch_commit = master - -PACKAGES += openflow -pkg_openflow_name = openflow -pkg_openflow_description = An OpenFlow controller written in pure erlang -pkg_openflow_homepage = https://github.com/renatoaguiar/erlang-openflow -pkg_openflow_fetch = git -pkg_openflow_repo = https://github.com/renatoaguiar/erlang-openflow -pkg_openflow_commit = master - -PACKAGES += openid -pkg_openid_name = openid -pkg_openid_description = Erlang OpenID -pkg_openid_homepage = https://github.com/brendonh/erl_openid -pkg_openid_fetch = git -pkg_openid_repo = https://github.com/brendonh/erl_openid -pkg_openid_commit = master - -PACKAGES += openpoker -pkg_openpoker_name = openpoker -pkg_openpoker_description = Genesis Texas hold'em Game Server -pkg_openpoker_homepage = https://github.com/hpyhacking/openpoker -pkg_openpoker_fetch = git -pkg_openpoker_repo = https://github.com/hpyhacking/openpoker -pkg_openpoker_commit = master - -PACKAGES += pal -pkg_pal_name = pal -pkg_pal_description = Pragmatic Authentication Library -pkg_pal_homepage = https://github.com/manifest/pal -pkg_pal_fetch = git -pkg_pal_repo = https://github.com/manifest/pal -pkg_pal_commit = master - -PACKAGES += parse_trans -pkg_parse_trans_name = parse_trans -pkg_parse_trans_description = Parse transform utilities for Erlang -pkg_parse_trans_homepage = https://github.com/uwiger/parse_trans -pkg_parse_trans_fetch = git -pkg_parse_trans_repo = https://github.com/uwiger/parse_trans -pkg_parse_trans_commit = master - -PACKAGES += parsexml -pkg_parsexml_name = parsexml -pkg_parsexml_description = Simple DOM XML parser with convenient and very simple API -pkg_parsexml_homepage = https://github.com/maxlapshin/parsexml -pkg_parsexml_fetch = git -pkg_parsexml_repo = https://github.com/maxlapshin/parsexml -pkg_parsexml_commit = master - -PACKAGES += pegjs -pkg_pegjs_name = pegjs -pkg_pegjs_description = An implementation of PEG.js grammar for Erlang. -pkg_pegjs_homepage = https://github.com/dmitriid/pegjs -pkg_pegjs_fetch = git -pkg_pegjs_repo = https://github.com/dmitriid/pegjs -pkg_pegjs_commit = master - -PACKAGES += percept2 -pkg_percept2_name = percept2 -pkg_percept2_description = Concurrent profiling tool for Erlang -pkg_percept2_homepage = https://github.com/huiqing/percept2 -pkg_percept2_fetch = git -pkg_percept2_repo = https://github.com/huiqing/percept2 -pkg_percept2_commit = master - -PACKAGES += pgsql -pkg_pgsql_name = pgsql -pkg_pgsql_description = Erlang PostgreSQL driver -pkg_pgsql_homepage = https://github.com/semiocast/pgsql -pkg_pgsql_fetch = git -pkg_pgsql_repo = https://github.com/semiocast/pgsql -pkg_pgsql_commit = master - -PACKAGES += pkgx -pkg_pkgx_name = pkgx -pkg_pkgx_description = Build .deb packages from Erlang releases -pkg_pkgx_homepage = https://github.com/arjan/pkgx -pkg_pkgx_fetch = git -pkg_pkgx_repo = https://github.com/arjan/pkgx -pkg_pkgx_commit = master - -PACKAGES += pkt -pkg_pkt_name = pkt -pkg_pkt_description = Erlang network protocol library -pkg_pkt_homepage = https://github.com/msantos/pkt -pkg_pkt_fetch = git -pkg_pkt_repo = https://github.com/msantos/pkt -pkg_pkt_commit = master - -PACKAGES += plain_fsm -pkg_plain_fsm_name = plain_fsm -pkg_plain_fsm_description = A behaviour/support library for writing plain Erlang FSMs. -pkg_plain_fsm_homepage = https://github.com/uwiger/plain_fsm -pkg_plain_fsm_fetch = git -pkg_plain_fsm_repo = https://github.com/uwiger/plain_fsm -pkg_plain_fsm_commit = master - -PACKAGES += plumtree -pkg_plumtree_name = plumtree -pkg_plumtree_description = Epidemic Broadcast Trees -pkg_plumtree_homepage = https://github.com/helium/plumtree -pkg_plumtree_fetch = git -pkg_plumtree_repo = https://github.com/helium/plumtree -pkg_plumtree_commit = master - -PACKAGES += pmod_transform -pkg_pmod_transform_name = pmod_transform -pkg_pmod_transform_description = Parse transform for parameterized modules -pkg_pmod_transform_homepage = https://github.com/erlang/pmod_transform -pkg_pmod_transform_fetch = git -pkg_pmod_transform_repo = https://github.com/erlang/pmod_transform -pkg_pmod_transform_commit = master - -PACKAGES += pobox -pkg_pobox_name = pobox -pkg_pobox_description = External buffer processes to protect against mailbox overflow in Erlang -pkg_pobox_homepage = https://github.com/ferd/pobox -pkg_pobox_fetch = git -pkg_pobox_repo = https://github.com/ferd/pobox -pkg_pobox_commit = master - -PACKAGES += ponos -pkg_ponos_name = ponos -pkg_ponos_description = ponos is a simple yet powerful load generator written in erlang -pkg_ponos_homepage = https://github.com/klarna/ponos -pkg_ponos_fetch = git -pkg_ponos_repo = https://github.com/klarna/ponos -pkg_ponos_commit = master - -PACKAGES += poolboy -pkg_poolboy_name = poolboy -pkg_poolboy_description = A hunky Erlang worker pool factory -pkg_poolboy_homepage = https://github.com/devinus/poolboy -pkg_poolboy_fetch = git -pkg_poolboy_repo = https://github.com/devinus/poolboy -pkg_poolboy_commit = master - -PACKAGES += pooler -pkg_pooler_name = pooler -pkg_pooler_description = An OTP Process Pool Application -pkg_pooler_homepage = https://github.com/seth/pooler -pkg_pooler_fetch = git -pkg_pooler_repo = https://github.com/seth/pooler -pkg_pooler_commit = master - -PACKAGES += pqueue -pkg_pqueue_name = pqueue -pkg_pqueue_description = Erlang Priority Queues -pkg_pqueue_homepage = https://github.com/okeuday/pqueue -pkg_pqueue_fetch = git -pkg_pqueue_repo = https://github.com/okeuday/pqueue -pkg_pqueue_commit = master - -PACKAGES += procket -pkg_procket_name = procket -pkg_procket_description = Erlang interface to low level socket operations -pkg_procket_homepage = http://blog.listincomprehension.com/search/label/procket -pkg_procket_fetch = git -pkg_procket_repo = https://github.com/msantos/procket -pkg_procket_commit = master - -PACKAGES += prop -pkg_prop_name = prop -pkg_prop_description = An Erlang code scaffolding and generator system. -pkg_prop_homepage = https://github.com/nuex/prop -pkg_prop_fetch = git -pkg_prop_repo = https://github.com/nuex/prop -pkg_prop_commit = master - -PACKAGES += proper -pkg_proper_name = proper -pkg_proper_description = PropEr: a QuickCheck-inspired property-based testing tool for Erlang. -pkg_proper_homepage = http://proper.softlab.ntua.gr -pkg_proper_fetch = git -pkg_proper_repo = https://github.com/manopapad/proper -pkg_proper_commit = master - -PACKAGES += props -pkg_props_name = props -pkg_props_description = Property structure library -pkg_props_homepage = https://github.com/greyarea/props -pkg_props_fetch = git -pkg_props_repo = https://github.com/greyarea/props -pkg_props_commit = master - -PACKAGES += protobuffs -pkg_protobuffs_name = protobuffs -pkg_protobuffs_description = An implementation of Google's Protocol Buffers for Erlang, based on ngerakines/erlang_protobuffs. -pkg_protobuffs_homepage = https://github.com/basho/erlang_protobuffs -pkg_protobuffs_fetch = git -pkg_protobuffs_repo = https://github.com/basho/erlang_protobuffs -pkg_protobuffs_commit = master - -PACKAGES += psycho -pkg_psycho_name = psycho -pkg_psycho_description = HTTP server that provides a WSGI-like interface for applications and middleware. -pkg_psycho_homepage = https://github.com/gar1t/psycho -pkg_psycho_fetch = git -pkg_psycho_repo = https://github.com/gar1t/psycho -pkg_psycho_commit = master - -PACKAGES += purity -pkg_purity_name = purity -pkg_purity_description = A side-effect analyzer for Erlang -pkg_purity_homepage = https://github.com/mpitid/purity -pkg_purity_fetch = git -pkg_purity_repo = https://github.com/mpitid/purity -pkg_purity_commit = master - -PACKAGES += push_service -pkg_push_service_name = push_service -pkg_push_service_description = Push service -pkg_push_service_homepage = https://github.com/hairyhum/push_service -pkg_push_service_fetch = git -pkg_push_service_repo = https://github.com/hairyhum/push_service -pkg_push_service_commit = master - -PACKAGES += qdate -pkg_qdate_name = qdate -pkg_qdate_description = Date, time, and timezone parsing, formatting, and conversion for Erlang. -pkg_qdate_homepage = https://github.com/choptastic/qdate -pkg_qdate_fetch = git -pkg_qdate_repo = https://github.com/choptastic/qdate -pkg_qdate_commit = master - -PACKAGES += qrcode -pkg_qrcode_name = qrcode -pkg_qrcode_description = QR Code encoder in Erlang -pkg_qrcode_homepage = https://github.com/komone/qrcode -pkg_qrcode_fetch = git -pkg_qrcode_repo = https://github.com/komone/qrcode -pkg_qrcode_commit = master - -PACKAGES += quest -pkg_quest_name = quest -pkg_quest_description = Learn Erlang through this set of challenges. An interactive system for getting to know Erlang. -pkg_quest_homepage = https://github.com/eriksoe/ErlangQuest -pkg_quest_fetch = git -pkg_quest_repo = https://github.com/eriksoe/ErlangQuest -pkg_quest_commit = master - -PACKAGES += quickrand -pkg_quickrand_name = quickrand -pkg_quickrand_description = Quick Erlang Random Number Generation -pkg_quickrand_homepage = https://github.com/okeuday/quickrand -pkg_quickrand_fetch = git -pkg_quickrand_repo = https://github.com/okeuday/quickrand -pkg_quickrand_commit = master - -PACKAGES += rabbit -pkg_rabbit_name = rabbit -pkg_rabbit_description = RabbitMQ Server -pkg_rabbit_homepage = https://www.rabbitmq.com/ -pkg_rabbit_fetch = git -pkg_rabbit_repo = https://github.com/rabbitmq/rabbitmq-server.git -pkg_rabbit_commit = master - -PACKAGES += rabbit_exchange_type_riak -pkg_rabbit_exchange_type_riak_name = rabbit_exchange_type_riak -pkg_rabbit_exchange_type_riak_description = Custom RabbitMQ exchange type for sticking messages in Riak -pkg_rabbit_exchange_type_riak_homepage = https://github.com/jbrisbin/riak-exchange -pkg_rabbit_exchange_type_riak_fetch = git -pkg_rabbit_exchange_type_riak_repo = https://github.com/jbrisbin/riak-exchange -pkg_rabbit_exchange_type_riak_commit = master - -PACKAGES += rack -pkg_rack_name = rack -pkg_rack_description = Rack handler for erlang -pkg_rack_homepage = https://github.com/erlyvideo/rack -pkg_rack_fetch = git -pkg_rack_repo = https://github.com/erlyvideo/rack -pkg_rack_commit = master - -PACKAGES += radierl -pkg_radierl_name = radierl -pkg_radierl_description = RADIUS protocol stack implemented in Erlang. -pkg_radierl_homepage = https://github.com/vances/radierl -pkg_radierl_fetch = git -pkg_radierl_repo = https://github.com/vances/radierl -pkg_radierl_commit = master - -PACKAGES += rafter -pkg_rafter_name = rafter -pkg_rafter_description = An Erlang library application which implements the Raft consensus protocol -pkg_rafter_homepage = https://github.com/andrewjstone/rafter -pkg_rafter_fetch = git -pkg_rafter_repo = https://github.com/andrewjstone/rafter -pkg_rafter_commit = master - -PACKAGES += ranch -pkg_ranch_name = ranch -pkg_ranch_description = Socket acceptor pool for TCP protocols. -pkg_ranch_homepage = http://ninenines.eu -pkg_ranch_fetch = git -pkg_ranch_repo = https://github.com/ninenines/ranch -pkg_ranch_commit = 1.2.1 - -PACKAGES += rbeacon -pkg_rbeacon_name = rbeacon -pkg_rbeacon_description = LAN discovery and presence in Erlang. -pkg_rbeacon_homepage = https://github.com/refuge/rbeacon -pkg_rbeacon_fetch = git -pkg_rbeacon_repo = https://github.com/refuge/rbeacon -pkg_rbeacon_commit = master - -PACKAGES += rebar -pkg_rebar_name = rebar -pkg_rebar_description = Erlang build tool that makes it easy to compile and test Erlang applications, port drivers and releases. -pkg_rebar_homepage = http://www.rebar3.org -pkg_rebar_fetch = git -pkg_rebar_repo = https://github.com/rebar/rebar3 -pkg_rebar_commit = master - -PACKAGES += rebus -pkg_rebus_name = rebus -pkg_rebus_description = A stupid simple, internal, pub/sub event bus written in- and for Erlang. -pkg_rebus_homepage = https://github.com/olle/rebus -pkg_rebus_fetch = git -pkg_rebus_repo = https://github.com/olle/rebus -pkg_rebus_commit = master - -PACKAGES += rec2json -pkg_rec2json_name = rec2json -pkg_rec2json_description = Compile erlang record definitions into modules to convert them to/from json easily. -pkg_rec2json_homepage = https://github.com/lordnull/rec2json -pkg_rec2json_fetch = git -pkg_rec2json_repo = https://github.com/lordnull/rec2json -pkg_rec2json_commit = master - -PACKAGES += recon -pkg_recon_name = recon -pkg_recon_description = Collection of functions and scripts to debug Erlang in production. -pkg_recon_homepage = https://github.com/ferd/recon -pkg_recon_fetch = git -pkg_recon_repo = https://github.com/ferd/recon -pkg_recon_commit = master - -PACKAGES += record_info -pkg_record_info_name = record_info -pkg_record_info_description = Convert between record and proplist -pkg_record_info_homepage = https://github.com/bipthelin/erlang-record_info -pkg_record_info_fetch = git -pkg_record_info_repo = https://github.com/bipthelin/erlang-record_info -pkg_record_info_commit = master - -PACKAGES += redgrid -pkg_redgrid_name = redgrid -pkg_redgrid_description = automatic Erlang node discovery via redis -pkg_redgrid_homepage = https://github.com/jkvor/redgrid -pkg_redgrid_fetch = git -pkg_redgrid_repo = https://github.com/jkvor/redgrid -pkg_redgrid_commit = master - -PACKAGES += redo -pkg_redo_name = redo -pkg_redo_description = pipelined erlang redis client -pkg_redo_homepage = https://github.com/jkvor/redo -pkg_redo_fetch = git -pkg_redo_repo = https://github.com/jkvor/redo -pkg_redo_commit = master - -PACKAGES += reload_mk -pkg_reload_mk_name = reload_mk -pkg_reload_mk_description = Live reload plugin for erlang.mk. -pkg_reload_mk_homepage = https://github.com/bullno1/reload.mk -pkg_reload_mk_fetch = git -pkg_reload_mk_repo = https://github.com/bullno1/reload.mk -pkg_reload_mk_commit = master - -PACKAGES += reltool_util -pkg_reltool_util_name = reltool_util -pkg_reltool_util_description = Erlang reltool utility functionality application -pkg_reltool_util_homepage = https://github.com/okeuday/reltool_util -pkg_reltool_util_fetch = git -pkg_reltool_util_repo = https://github.com/okeuday/reltool_util -pkg_reltool_util_commit = master - -PACKAGES += relx -pkg_relx_name = relx -pkg_relx_description = Sane, simple release creation for Erlang -pkg_relx_homepage = https://github.com/erlware/relx -pkg_relx_fetch = git -pkg_relx_repo = https://github.com/erlware/relx -pkg_relx_commit = master - -PACKAGES += resource_discovery -pkg_resource_discovery_name = resource_discovery -pkg_resource_discovery_description = An application used to dynamically discover resources present in an Erlang node cluster. -pkg_resource_discovery_homepage = http://erlware.org/ -pkg_resource_discovery_fetch = git -pkg_resource_discovery_repo = https://github.com/erlware/resource_discovery -pkg_resource_discovery_commit = master - -PACKAGES += restc -pkg_restc_name = restc -pkg_restc_description = Erlang Rest Client -pkg_restc_homepage = https://github.com/kivra/restclient -pkg_restc_fetch = git -pkg_restc_repo = https://github.com/kivra/restclient -pkg_restc_commit = master - -PACKAGES += rfc4627_jsonrpc -pkg_rfc4627_jsonrpc_name = rfc4627_jsonrpc -pkg_rfc4627_jsonrpc_description = Erlang RFC4627 (JSON) codec and JSON-RPC server implementation. -pkg_rfc4627_jsonrpc_homepage = https://github.com/tonyg/erlang-rfc4627 -pkg_rfc4627_jsonrpc_fetch = git -pkg_rfc4627_jsonrpc_repo = https://github.com/tonyg/erlang-rfc4627 -pkg_rfc4627_jsonrpc_commit = master - -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 - -PACKAGES += riak_core -pkg_riak_core_name = riak_core -pkg_riak_core_description = Distributed systems infrastructure used by Riak. -pkg_riak_core_homepage = https://github.com/basho/riak_core -pkg_riak_core_fetch = git -pkg_riak_core_repo = https://github.com/basho/riak_core -pkg_riak_core_commit = master - -PACKAGES += riak_dt -pkg_riak_dt_name = riak_dt -pkg_riak_dt_description = Convergent replicated datatypes in Erlang -pkg_riak_dt_homepage = https://github.com/basho/riak_dt -pkg_riak_dt_fetch = git -pkg_riak_dt_repo = https://github.com/basho/riak_dt -pkg_riak_dt_commit = master - -PACKAGES += riak_ensemble -pkg_riak_ensemble_name = riak_ensemble -pkg_riak_ensemble_description = Multi-Paxos framework in Erlang -pkg_riak_ensemble_homepage = https://github.com/basho/riak_ensemble -pkg_riak_ensemble_fetch = git -pkg_riak_ensemble_repo = https://github.com/basho/riak_ensemble -pkg_riak_ensemble_commit = master - -PACKAGES += riak_kv -pkg_riak_kv_name = riak_kv -pkg_riak_kv_description = Riak Key/Value Store -pkg_riak_kv_homepage = https://github.com/basho/riak_kv -pkg_riak_kv_fetch = git -pkg_riak_kv_repo = https://github.com/basho/riak_kv -pkg_riak_kv_commit = master - -PACKAGES += riak_pg -pkg_riak_pg_name = riak_pg -pkg_riak_pg_description = Distributed process groups with riak_core. -pkg_riak_pg_homepage = https://github.com/cmeiklejohn/riak_pg -pkg_riak_pg_fetch = git -pkg_riak_pg_repo = https://github.com/cmeiklejohn/riak_pg -pkg_riak_pg_commit = master - -PACKAGES += riak_pipe -pkg_riak_pipe_name = riak_pipe -pkg_riak_pipe_description = Riak Pipelines -pkg_riak_pipe_homepage = https://github.com/basho/riak_pipe -pkg_riak_pipe_fetch = git -pkg_riak_pipe_repo = https://github.com/basho/riak_pipe -pkg_riak_pipe_commit = master - -PACKAGES += riak_sysmon -pkg_riak_sysmon_name = riak_sysmon -pkg_riak_sysmon_description = Simple OTP app for managing Erlang VM system_monitor event messages -pkg_riak_sysmon_homepage = https://github.com/basho/riak_sysmon -pkg_riak_sysmon_fetch = git -pkg_riak_sysmon_repo = https://github.com/basho/riak_sysmon -pkg_riak_sysmon_commit = master - -PACKAGES += riak_test -pkg_riak_test_name = riak_test -pkg_riak_test_description = I'm in your cluster, testing your riaks -pkg_riak_test_homepage = https://github.com/basho/riak_test -pkg_riak_test_fetch = git -pkg_riak_test_repo = https://github.com/basho/riak_test -pkg_riak_test_commit = master - -PACKAGES += riakc -pkg_riakc_name = riakc -pkg_riakc_description = Erlang clients for Riak. -pkg_riakc_homepage = https://github.com/basho/riak-erlang-client -pkg_riakc_fetch = git -pkg_riakc_repo = https://github.com/basho/riak-erlang-client -pkg_riakc_commit = master - -PACKAGES += riakhttpc -pkg_riakhttpc_name = riakhttpc -pkg_riakhttpc_description = Riak Erlang client using the HTTP interface -pkg_riakhttpc_homepage = https://github.com/basho/riak-erlang-http-client -pkg_riakhttpc_fetch = git -pkg_riakhttpc_repo = https://github.com/basho/riak-erlang-http-client -pkg_riakhttpc_commit = master - -PACKAGES += riaknostic -pkg_riaknostic_name = riaknostic -pkg_riaknostic_description = A diagnostic tool for Riak installations, to find common errors asap -pkg_riaknostic_homepage = https://github.com/basho/riaknostic -pkg_riaknostic_fetch = git -pkg_riaknostic_repo = https://github.com/basho/riaknostic -pkg_riaknostic_commit = master - -PACKAGES += riakpool -pkg_riakpool_name = riakpool -pkg_riakpool_description = erlang riak client pool -pkg_riakpool_homepage = https://github.com/dweldon/riakpool -pkg_riakpool_fetch = git -pkg_riakpool_repo = https://github.com/dweldon/riakpool -pkg_riakpool_commit = master - -PACKAGES += rivus_cep -pkg_rivus_cep_name = rivus_cep -pkg_rivus_cep_description = Complex event processing in Erlang -pkg_rivus_cep_homepage = https://github.com/vascokk/rivus_cep -pkg_rivus_cep_fetch = git -pkg_rivus_cep_repo = https://github.com/vascokk/rivus_cep -pkg_rivus_cep_commit = master - -PACKAGES += rlimit -pkg_rlimit_name = rlimit -pkg_rlimit_description = Magnus Klaar's rate limiter code from etorrent -pkg_rlimit_homepage = https://github.com/jlouis/rlimit -pkg_rlimit_fetch = git -pkg_rlimit_repo = https://github.com/jlouis/rlimit -pkg_rlimit_commit = master - -PACKAGES += rust_mk -pkg_rust_mk_name = rust_mk -pkg_rust_mk_description = Build Rust crates in an Erlang application -pkg_rust_mk_homepage = https://github.com/goertzenator/rust.mk -pkg_rust_mk_fetch = git -pkg_rust_mk_repo = https://github.com/goertzenator/rust.mk -pkg_rust_mk_commit = master - -PACKAGES += safetyvalve -pkg_safetyvalve_name = safetyvalve -pkg_safetyvalve_description = A safety valve for your erlang node -pkg_safetyvalve_homepage = https://github.com/jlouis/safetyvalve -pkg_safetyvalve_fetch = git -pkg_safetyvalve_repo = https://github.com/jlouis/safetyvalve -pkg_safetyvalve_commit = master - -PACKAGES += seestar -pkg_seestar_name = seestar -pkg_seestar_description = The Erlang client for Cassandra 1.2+ binary protocol -pkg_seestar_homepage = https://github.com/iamaleksey/seestar -pkg_seestar_fetch = git -pkg_seestar_repo = https://github.com/iamaleksey/seestar -pkg_seestar_commit = master - -PACKAGES += service -pkg_service_name = service -pkg_service_description = A minimal Erlang behavior for creating CloudI internal services -pkg_service_homepage = http://cloudi.org/ -pkg_service_fetch = git -pkg_service_repo = https://github.com/CloudI/service -pkg_service_commit = master - -PACKAGES += setup -pkg_setup_name = setup -pkg_setup_description = Generic setup utility for Erlang-based systems -pkg_setup_homepage = https://github.com/uwiger/setup -pkg_setup_fetch = git -pkg_setup_repo = https://github.com/uwiger/setup -pkg_setup_commit = master - -PACKAGES += sext -pkg_sext_name = sext -pkg_sext_description = Sortable Erlang Term Serialization -pkg_sext_homepage = https://github.com/uwiger/sext -pkg_sext_fetch = git -pkg_sext_repo = https://github.com/uwiger/sext -pkg_sext_commit = master - -PACKAGES += sfmt -pkg_sfmt_name = sfmt -pkg_sfmt_description = SFMT pseudo random number generator for Erlang. -pkg_sfmt_homepage = https://github.com/jj1bdx/sfmt-erlang -pkg_sfmt_fetch = git -pkg_sfmt_repo = https://github.com/jj1bdx/sfmt-erlang -pkg_sfmt_commit = master - -PACKAGES += sgte -pkg_sgte_name = sgte -pkg_sgte_description = A simple Erlang Template Engine -pkg_sgte_homepage = https://github.com/filippo/sgte -pkg_sgte_fetch = git -pkg_sgte_repo = https://github.com/filippo/sgte -pkg_sgte_commit = master - -PACKAGES += sheriff -pkg_sheriff_name = sheriff -pkg_sheriff_description = Parse transform for type based validation. -pkg_sheriff_homepage = http://ninenines.eu -pkg_sheriff_fetch = git -pkg_sheriff_repo = https://github.com/extend/sheriff -pkg_sheriff_commit = master - -PACKAGES += shotgun -pkg_shotgun_name = shotgun -pkg_shotgun_description = better than just a gun -pkg_shotgun_homepage = https://github.com/inaka/shotgun -pkg_shotgun_fetch = git -pkg_shotgun_repo = https://github.com/inaka/shotgun -pkg_shotgun_commit = master - -PACKAGES += sidejob -pkg_sidejob_name = sidejob -pkg_sidejob_description = Parallel worker and capacity limiting library for Erlang -pkg_sidejob_homepage = https://github.com/basho/sidejob -pkg_sidejob_fetch = git -pkg_sidejob_repo = https://github.com/basho/sidejob -pkg_sidejob_commit = master - -PACKAGES += sieve -pkg_sieve_name = sieve -pkg_sieve_description = sieve is a simple TCP routing proxy (layer 7) in erlang -pkg_sieve_homepage = https://github.com/benoitc/sieve -pkg_sieve_fetch = git -pkg_sieve_repo = https://github.com/benoitc/sieve -pkg_sieve_commit = master - -PACKAGES += sighandler -pkg_sighandler_name = sighandler -pkg_sighandler_description = Handle UNIX signals in Er lang -pkg_sighandler_homepage = https://github.com/jkingsbery/sighandler -pkg_sighandler_fetch = git -pkg_sighandler_repo = https://github.com/jkingsbery/sighandler -pkg_sighandler_commit = master - -PACKAGES += simhash -pkg_simhash_name = simhash -pkg_simhash_description = Simhashing for Erlang -- hashing algorithm to find near-duplicates in binary data. -pkg_simhash_homepage = https://github.com/ferd/simhash -pkg_simhash_fetch = git -pkg_simhash_repo = https://github.com/ferd/simhash -pkg_simhash_commit = master - -PACKAGES += simple_bridge -pkg_simple_bridge_name = simple_bridge -pkg_simple_bridge_description = A simple, standardized interface library to Erlang HTTP Servers. -pkg_simple_bridge_homepage = https://github.com/nitrogen/simple_bridge -pkg_simple_bridge_fetch = git -pkg_simple_bridge_repo = https://github.com/nitrogen/simple_bridge -pkg_simple_bridge_commit = master - -PACKAGES += simple_oauth2 -pkg_simple_oauth2_name = simple_oauth2 -pkg_simple_oauth2_description = Simple erlang OAuth2 client module for any http server framework (Google, Facebook, Yandex, Vkontakte are preconfigured) -pkg_simple_oauth2_homepage = https://github.com/virtan/simple_oauth2 -pkg_simple_oauth2_fetch = git -pkg_simple_oauth2_repo = https://github.com/virtan/simple_oauth2 -pkg_simple_oauth2_commit = master - -PACKAGES += skel -pkg_skel_name = skel -pkg_skel_description = A Streaming Process-based Skeleton Library for Erlang -pkg_skel_homepage = https://github.com/ParaPhrase/skel -pkg_skel_fetch = git -pkg_skel_repo = https://github.com/ParaPhrase/skel -pkg_skel_commit = master - -PACKAGES += slack -pkg_slack_name = slack -pkg_slack_description = Minimal slack notification OTP library. -pkg_slack_homepage = https://github.com/DonBranson/slack -pkg_slack_fetch = git -pkg_slack_repo = https://github.com/DonBranson/slack.git -pkg_slack_commit = master - -PACKAGES += smother -pkg_smother_name = smother -pkg_smother_description = Extended code coverage metrics for Erlang. -pkg_smother_homepage = https://ramsay-t.github.io/Smother/ -pkg_smother_fetch = git -pkg_smother_repo = https://github.com/ramsay-t/Smother -pkg_smother_commit = master - -PACKAGES += snappyer -pkg_snappyer_name = snappyer -pkg_snappyer_description = Snappy as nif for Erlang -pkg_snappyer_homepage = https://github.com/zmstone/snappyer -pkg_snappyer_fetch = git -pkg_snappyer_repo = https://github.com/zmstone/snappyer.git -pkg_snappyer_commit = master - -PACKAGES += social -pkg_social_name = social -pkg_social_description = Cowboy handler for social login via OAuth2 providers -pkg_social_homepage = https://github.com/dvv/social -pkg_social_fetch = git -pkg_social_repo = https://github.com/dvv/social -pkg_social_commit = master - -PACKAGES += spapi_router -pkg_spapi_router_name = spapi_router -pkg_spapi_router_description = Partially-connected Erlang clustering -pkg_spapi_router_homepage = https://github.com/spilgames/spapi-router -pkg_spapi_router_fetch = git -pkg_spapi_router_repo = https://github.com/spilgames/spapi-router -pkg_spapi_router_commit = master - -PACKAGES += sqerl -pkg_sqerl_name = sqerl -pkg_sqerl_description = An Erlang-flavoured SQL DSL -pkg_sqerl_homepage = https://github.com/hairyhum/sqerl -pkg_sqerl_fetch = git -pkg_sqerl_repo = https://github.com/hairyhum/sqerl -pkg_sqerl_commit = master - -PACKAGES += srly -pkg_srly_name = srly -pkg_srly_description = Native Erlang Unix serial interface -pkg_srly_homepage = https://github.com/msantos/srly -pkg_srly_fetch = git -pkg_srly_repo = https://github.com/msantos/srly -pkg_srly_commit = master - -PACKAGES += sshrpc -pkg_sshrpc_name = sshrpc -pkg_sshrpc_description = Erlang SSH RPC module (experimental) -pkg_sshrpc_homepage = https://github.com/jj1bdx/sshrpc -pkg_sshrpc_fetch = git -pkg_sshrpc_repo = https://github.com/jj1bdx/sshrpc -pkg_sshrpc_commit = master - -PACKAGES += stable -pkg_stable_name = stable -pkg_stable_description = Library of assorted helpers for Cowboy web server. -pkg_stable_homepage = https://github.com/dvv/stable -pkg_stable_fetch = git -pkg_stable_repo = https://github.com/dvv/stable -pkg_stable_commit = master - -PACKAGES += statebox -pkg_statebox_name = statebox -pkg_statebox_description = Erlang state monad with merge/conflict-resolution capabilities. Useful for Riak. -pkg_statebox_homepage = https://github.com/mochi/statebox -pkg_statebox_fetch = git -pkg_statebox_repo = https://github.com/mochi/statebox -pkg_statebox_commit = master - -PACKAGES += statebox_riak -pkg_statebox_riak_name = statebox_riak -pkg_statebox_riak_description = Convenience library that makes it easier to use statebox with riak, extracted from best practices in our production code at Mochi Media. -pkg_statebox_riak_homepage = https://github.com/mochi/statebox_riak -pkg_statebox_riak_fetch = git -pkg_statebox_riak_repo = https://github.com/mochi/statebox_riak -pkg_statebox_riak_commit = master - -PACKAGES += statman -pkg_statman_name = statman -pkg_statman_description = Efficiently collect massive volumes of metrics inside the Erlang VM -pkg_statman_homepage = https://github.com/knutin/statman -pkg_statman_fetch = git -pkg_statman_repo = https://github.com/knutin/statman -pkg_statman_commit = master - -PACKAGES += statsderl -pkg_statsderl_name = statsderl -pkg_statsderl_description = StatsD client (erlang) -pkg_statsderl_homepage = https://github.com/lpgauth/statsderl -pkg_statsderl_fetch = git -pkg_statsderl_repo = https://github.com/lpgauth/statsderl -pkg_statsderl_commit = master - -PACKAGES += stdinout_pool -pkg_stdinout_pool_name = stdinout_pool -pkg_stdinout_pool_description = stdinout_pool : stuff goes in, stuff goes out. there's never any miscommunication. -pkg_stdinout_pool_homepage = https://github.com/mattsta/erlang-stdinout-pool -pkg_stdinout_pool_fetch = git -pkg_stdinout_pool_repo = https://github.com/mattsta/erlang-stdinout-pool -pkg_stdinout_pool_commit = master - -PACKAGES += stockdb -pkg_stockdb_name = stockdb -pkg_stockdb_description = Database for storing Stock Exchange quotes in erlang -pkg_stockdb_homepage = https://github.com/maxlapshin/stockdb -pkg_stockdb_fetch = git -pkg_stockdb_repo = https://github.com/maxlapshin/stockdb -pkg_stockdb_commit = master - -PACKAGES += stripe -pkg_stripe_name = stripe -pkg_stripe_description = Erlang interface to the stripe.com API -pkg_stripe_homepage = https://github.com/mattsta/stripe-erlang -pkg_stripe_fetch = git -pkg_stripe_repo = https://github.com/mattsta/stripe-erlang -pkg_stripe_commit = v1 - -PACKAGES += supervisor3 -pkg_supervisor3_name = supervisor3 -pkg_supervisor3_description = OTP supervisor with additional strategies -pkg_supervisor3_homepage = https://github.com/klarna/supervisor3 -pkg_supervisor3_fetch = git -pkg_supervisor3_repo = https://github.com/klarna/supervisor3.git -pkg_supervisor3_commit = master - -PACKAGES += surrogate -pkg_surrogate_name = surrogate -pkg_surrogate_description = Proxy server written in erlang. Supports reverse proxy load balancing and forward proxy with http (including CONNECT), socks4, socks5, and transparent proxy modes. -pkg_surrogate_homepage = https://github.com/skruger/Surrogate -pkg_surrogate_fetch = git -pkg_surrogate_repo = https://github.com/skruger/Surrogate -pkg_surrogate_commit = master - -PACKAGES += swab -pkg_swab_name = swab -pkg_swab_description = General purpose buffer handling module -pkg_swab_homepage = https://github.com/crownedgrouse/swab -pkg_swab_fetch = git -pkg_swab_repo = https://github.com/crownedgrouse/swab -pkg_swab_commit = master - -PACKAGES += swarm -pkg_swarm_name = swarm -pkg_swarm_description = Fast and simple acceptor pool for Erlang -pkg_swarm_homepage = https://github.com/jeremey/swarm -pkg_swarm_fetch = git -pkg_swarm_repo = https://github.com/jeremey/swarm -pkg_swarm_commit = master - -PACKAGES += switchboard -pkg_switchboard_name = switchboard -pkg_switchboard_description = A framework for processing email using worker plugins. -pkg_switchboard_homepage = https://github.com/thusfresh/switchboard -pkg_switchboard_fetch = git -pkg_switchboard_repo = https://github.com/thusfresh/switchboard -pkg_switchboard_commit = master - -PACKAGES += syn -pkg_syn_name = syn -pkg_syn_description = A global Process Registry and Process Group manager for Erlang. -pkg_syn_homepage = https://github.com/ostinelli/syn -pkg_syn_fetch = git -pkg_syn_repo = https://github.com/ostinelli/syn -pkg_syn_commit = master - -PACKAGES += sync -pkg_sync_name = sync -pkg_sync_description = On-the-fly recompiling and reloading in Erlang. -pkg_sync_homepage = https://github.com/rustyio/sync -pkg_sync_fetch = git -pkg_sync_repo = https://github.com/rustyio/sync -pkg_sync_commit = master - -PACKAGES += syntaxerl -pkg_syntaxerl_name = syntaxerl -pkg_syntaxerl_description = Syntax checker for Erlang -pkg_syntaxerl_homepage = https://github.com/ten0s/syntaxerl -pkg_syntaxerl_fetch = git -pkg_syntaxerl_repo = https://github.com/ten0s/syntaxerl -pkg_syntaxerl_commit = master - -PACKAGES += syslog -pkg_syslog_name = syslog -pkg_syslog_description = Erlang port driver for interacting with syslog via syslog(3) -pkg_syslog_homepage = https://github.com/Vagabond/erlang-syslog -pkg_syslog_fetch = git -pkg_syslog_repo = https://github.com/Vagabond/erlang-syslog -pkg_syslog_commit = master - -PACKAGES += taskforce -pkg_taskforce_name = taskforce -pkg_taskforce_description = Erlang worker pools for controlled parallelisation of arbitrary tasks. -pkg_taskforce_homepage = https://github.com/g-andrade/taskforce -pkg_taskforce_fetch = git -pkg_taskforce_repo = https://github.com/g-andrade/taskforce -pkg_taskforce_commit = master - -PACKAGES += tddreloader -pkg_tddreloader_name = tddreloader -pkg_tddreloader_description = Shell utility for recompiling, reloading, and testing code as it changes -pkg_tddreloader_homepage = https://github.com/version2beta/tddreloader -pkg_tddreloader_fetch = git -pkg_tddreloader_repo = https://github.com/version2beta/tddreloader -pkg_tddreloader_commit = master - -PACKAGES += tempo -pkg_tempo_name = tempo -pkg_tempo_description = NIF-based date and time parsing and formatting for Erlang. -pkg_tempo_homepage = https://github.com/selectel/tempo -pkg_tempo_fetch = git -pkg_tempo_repo = https://github.com/selectel/tempo -pkg_tempo_commit = master - -PACKAGES += ticktick -pkg_ticktick_name = ticktick -pkg_ticktick_description = Ticktick is an id generator for message service. -pkg_ticktick_homepage = https://github.com/ericliang/ticktick -pkg_ticktick_fetch = git -pkg_ticktick_repo = https://github.com/ericliang/ticktick -pkg_ticktick_commit = master - -PACKAGES += tinymq -pkg_tinymq_name = tinymq -pkg_tinymq_description = TinyMQ - a diminutive, in-memory message queue -pkg_tinymq_homepage = https://github.com/ChicagoBoss/tinymq -pkg_tinymq_fetch = git -pkg_tinymq_repo = https://github.com/ChicagoBoss/tinymq -pkg_tinymq_commit = master - -PACKAGES += tinymt -pkg_tinymt_name = tinymt -pkg_tinymt_description = TinyMT pseudo random number generator for Erlang. -pkg_tinymt_homepage = https://github.com/jj1bdx/tinymt-erlang -pkg_tinymt_fetch = git -pkg_tinymt_repo = https://github.com/jj1bdx/tinymt-erlang -pkg_tinymt_commit = master - -PACKAGES += tirerl -pkg_tirerl_name = tirerl -pkg_tirerl_description = Erlang interface to Elastic Search -pkg_tirerl_homepage = https://github.com/inaka/tirerl -pkg_tirerl_fetch = git -pkg_tirerl_repo = https://github.com/inaka/tirerl -pkg_tirerl_commit = master - -PACKAGES += traffic_tools -pkg_traffic_tools_name = traffic_tools -pkg_traffic_tools_description = Simple traffic limiting library -pkg_traffic_tools_homepage = https://github.com/systra/traffic_tools -pkg_traffic_tools_fetch = git -pkg_traffic_tools_repo = https://github.com/systra/traffic_tools -pkg_traffic_tools_commit = master - -PACKAGES += trails -pkg_trails_name = trails -pkg_trails_description = A couple of improvements over Cowboy Routes -pkg_trails_homepage = http://inaka.github.io/cowboy-trails/ -pkg_trails_fetch = git -pkg_trails_repo = https://github.com/inaka/cowboy-trails -pkg_trails_commit = master - -PACKAGES += trane -pkg_trane_name = trane -pkg_trane_description = SAX style broken HTML parser in Erlang -pkg_trane_homepage = https://github.com/massemanet/trane -pkg_trane_fetch = git -pkg_trane_repo = https://github.com/massemanet/trane -pkg_trane_commit = master - -PACKAGES += transit -pkg_transit_name = transit -pkg_transit_description = transit format for erlang -pkg_transit_homepage = https://github.com/isaiah/transit-erlang -pkg_transit_fetch = git -pkg_transit_repo = https://github.com/isaiah/transit-erlang -pkg_transit_commit = master - -PACKAGES += trie -pkg_trie_name = trie -pkg_trie_description = Erlang Trie Implementation -pkg_trie_homepage = https://github.com/okeuday/trie -pkg_trie_fetch = git -pkg_trie_repo = https://github.com/okeuday/trie -pkg_trie_commit = master - -PACKAGES += triq -pkg_triq_name = triq -pkg_triq_description = Trifork QuickCheck -pkg_triq_homepage = https://github.com/krestenkrab/triq -pkg_triq_fetch = git -pkg_triq_repo = https://github.com/krestenkrab/triq -pkg_triq_commit = master - -PACKAGES += tunctl -pkg_tunctl_name = tunctl -pkg_tunctl_description = Erlang TUN/TAP interface -pkg_tunctl_homepage = https://github.com/msantos/tunctl -pkg_tunctl_fetch = git -pkg_tunctl_repo = https://github.com/msantos/tunctl -pkg_tunctl_commit = master - -PACKAGES += twerl -pkg_twerl_name = twerl -pkg_twerl_description = Erlang client for the Twitter Streaming API -pkg_twerl_homepage = https://github.com/lucaspiller/twerl -pkg_twerl_fetch = git -pkg_twerl_repo = https://github.com/lucaspiller/twerl -pkg_twerl_commit = oauth - -PACKAGES += twitter_erlang -pkg_twitter_erlang_name = twitter_erlang -pkg_twitter_erlang_description = An Erlang twitter client -pkg_twitter_erlang_homepage = https://github.com/ngerakines/erlang_twitter -pkg_twitter_erlang_fetch = git -pkg_twitter_erlang_repo = https://github.com/ngerakines/erlang_twitter -pkg_twitter_erlang_commit = master - -PACKAGES += ucol_nif -pkg_ucol_nif_name = ucol_nif -pkg_ucol_nif_description = ICU based collation Erlang module -pkg_ucol_nif_homepage = https://github.com/refuge/ucol_nif -pkg_ucol_nif_fetch = git -pkg_ucol_nif_repo = https://github.com/refuge/ucol_nif -pkg_ucol_nif_commit = master - -PACKAGES += unicorn -pkg_unicorn_name = unicorn -pkg_unicorn_description = Generic configuration server -pkg_unicorn_homepage = https://github.com/shizzard/unicorn -pkg_unicorn_fetch = git -pkg_unicorn_repo = https://github.com/shizzard/unicorn -pkg_unicorn_commit = master - -PACKAGES += unsplit -pkg_unsplit_name = unsplit -pkg_unsplit_description = Resolves conflicts in Mnesia after network splits -pkg_unsplit_homepage = https://github.com/uwiger/unsplit -pkg_unsplit_fetch = git -pkg_unsplit_repo = https://github.com/uwiger/unsplit -pkg_unsplit_commit = master - -PACKAGES += uuid -pkg_uuid_name = uuid -pkg_uuid_description = Erlang UUID Implementation -pkg_uuid_homepage = https://github.com/okeuday/uuid -pkg_uuid_fetch = git -pkg_uuid_repo = https://github.com/okeuday/uuid -pkg_uuid_commit = master - -PACKAGES += ux -pkg_ux_name = ux -pkg_ux_description = Unicode eXtention for Erlang (Strings, Collation) -pkg_ux_homepage = https://github.com/erlang-unicode/ux -pkg_ux_fetch = git -pkg_ux_repo = https://github.com/erlang-unicode/ux -pkg_ux_commit = master - -PACKAGES += vert -pkg_vert_name = vert -pkg_vert_description = Erlang binding to libvirt virtualization API -pkg_vert_homepage = https://github.com/msantos/erlang-libvirt -pkg_vert_fetch = git -pkg_vert_repo = https://github.com/msantos/erlang-libvirt -pkg_vert_commit = master - -PACKAGES += verx -pkg_verx_name = verx -pkg_verx_description = Erlang implementation of the libvirtd remote protocol -pkg_verx_homepage = https://github.com/msantos/verx -pkg_verx_fetch = git -pkg_verx_repo = https://github.com/msantos/verx -pkg_verx_commit = master - -PACKAGES += vmq_acl -pkg_vmq_acl_name = vmq_acl -pkg_vmq_acl_description = Component of VerneMQ: A distributed MQTT message broker -pkg_vmq_acl_homepage = https://verne.mq/ -pkg_vmq_acl_fetch = git -pkg_vmq_acl_repo = https://github.com/erlio/vmq_acl -pkg_vmq_acl_commit = master - -PACKAGES += vmq_bridge -pkg_vmq_bridge_name = vmq_bridge -pkg_vmq_bridge_description = Component of VerneMQ: A distributed MQTT message broker -pkg_vmq_bridge_homepage = https://verne.mq/ -pkg_vmq_bridge_fetch = git -pkg_vmq_bridge_repo = https://github.com/erlio/vmq_bridge -pkg_vmq_bridge_commit = master - -PACKAGES += vmq_graphite -pkg_vmq_graphite_name = vmq_graphite -pkg_vmq_graphite_description = Component of VerneMQ: A distributed MQTT message broker -pkg_vmq_graphite_homepage = https://verne.mq/ -pkg_vmq_graphite_fetch = git -pkg_vmq_graphite_repo = https://github.com/erlio/vmq_graphite -pkg_vmq_graphite_commit = master - -PACKAGES += vmq_passwd -pkg_vmq_passwd_name = vmq_passwd -pkg_vmq_passwd_description = Component of VerneMQ: A distributed MQTT message broker -pkg_vmq_passwd_homepage = https://verne.mq/ -pkg_vmq_passwd_fetch = git -pkg_vmq_passwd_repo = https://github.com/erlio/vmq_passwd -pkg_vmq_passwd_commit = master - -PACKAGES += vmq_server -pkg_vmq_server_name = vmq_server -pkg_vmq_server_description = Component of VerneMQ: A distributed MQTT message broker -pkg_vmq_server_homepage = https://verne.mq/ -pkg_vmq_server_fetch = git -pkg_vmq_server_repo = https://github.com/erlio/vmq_server -pkg_vmq_server_commit = master - -PACKAGES += vmq_snmp -pkg_vmq_snmp_name = vmq_snmp -pkg_vmq_snmp_description = Component of VerneMQ: A distributed MQTT message broker -pkg_vmq_snmp_homepage = https://verne.mq/ -pkg_vmq_snmp_fetch = git -pkg_vmq_snmp_repo = https://github.com/erlio/vmq_snmp -pkg_vmq_snmp_commit = master - -PACKAGES += vmq_systree -pkg_vmq_systree_name = vmq_systree -pkg_vmq_systree_description = Component of VerneMQ: A distributed MQTT message broker -pkg_vmq_systree_homepage = https://verne.mq/ -pkg_vmq_systree_fetch = git -pkg_vmq_systree_repo = https://github.com/erlio/vmq_systree -pkg_vmq_systree_commit = master - -PACKAGES += vmstats -pkg_vmstats_name = vmstats -pkg_vmstats_description = tiny Erlang app that works in conjunction with statsderl in order to generate information on the Erlang VM for graphite logs. -pkg_vmstats_homepage = https://github.com/ferd/vmstats -pkg_vmstats_fetch = git -pkg_vmstats_repo = https://github.com/ferd/vmstats -pkg_vmstats_commit = master - -PACKAGES += walrus -pkg_walrus_name = walrus -pkg_walrus_description = Walrus - Mustache-like Templating -pkg_walrus_homepage = https://github.com/devinus/walrus -pkg_walrus_fetch = git -pkg_walrus_repo = https://github.com/devinus/walrus -pkg_walrus_commit = master - -PACKAGES += webmachine -pkg_webmachine_name = webmachine -pkg_webmachine_description = A REST-based system for building web applications. -pkg_webmachine_homepage = https://github.com/basho/webmachine -pkg_webmachine_fetch = git -pkg_webmachine_repo = https://github.com/basho/webmachine -pkg_webmachine_commit = master - -PACKAGES += websocket_client -pkg_websocket_client_name = websocket_client -pkg_websocket_client_description = Erlang websocket client (ws and wss supported) -pkg_websocket_client_homepage = https://github.com/jeremyong/websocket_client -pkg_websocket_client_fetch = git -pkg_websocket_client_repo = https://github.com/jeremyong/websocket_client -pkg_websocket_client_commit = master - -PACKAGES += worker_pool -pkg_worker_pool_name = worker_pool -pkg_worker_pool_description = a simple erlang worker pool -pkg_worker_pool_homepage = https://github.com/inaka/worker_pool -pkg_worker_pool_fetch = git -pkg_worker_pool_repo = https://github.com/inaka/worker_pool -pkg_worker_pool_commit = master - -PACKAGES += wrangler -pkg_wrangler_name = wrangler -pkg_wrangler_description = Import of the Wrangler svn repository. -pkg_wrangler_homepage = http://www.cs.kent.ac.uk/projects/wrangler/Home.html -pkg_wrangler_fetch = git -pkg_wrangler_repo = https://github.com/RefactoringTools/wrangler -pkg_wrangler_commit = master - -PACKAGES += wsock -pkg_wsock_name = wsock -pkg_wsock_description = Erlang library to build WebSocket clients and servers -pkg_wsock_homepage = https://github.com/madtrick/wsock -pkg_wsock_fetch = git -pkg_wsock_repo = https://github.com/madtrick/wsock -pkg_wsock_commit = master - -PACKAGES += xhttpc -pkg_xhttpc_name = xhttpc -pkg_xhttpc_description = Extensible HTTP Client for Erlang -pkg_xhttpc_homepage = https://github.com/seriyps/xhttpc -pkg_xhttpc_fetch = git -pkg_xhttpc_repo = https://github.com/seriyps/xhttpc -pkg_xhttpc_commit = master - -PACKAGES += xref_runner -pkg_xref_runner_name = xref_runner -pkg_xref_runner_description = Erlang Xref Runner (inspired in rebar xref) -pkg_xref_runner_homepage = https://github.com/inaka/xref_runner -pkg_xref_runner_fetch = git -pkg_xref_runner_repo = https://github.com/inaka/xref_runner -pkg_xref_runner_commit = master - -PACKAGES += yamerl -pkg_yamerl_name = yamerl -pkg_yamerl_description = YAML 1.2 parser in pure Erlang -pkg_yamerl_homepage = https://github.com/yakaz/yamerl -pkg_yamerl_fetch = git -pkg_yamerl_repo = https://github.com/yakaz/yamerl -pkg_yamerl_commit = master - -PACKAGES += yamler -pkg_yamler_name = yamler -pkg_yamler_description = libyaml-based yaml loader for Erlang -pkg_yamler_homepage = https://github.com/goertzenator/yamler -pkg_yamler_fetch = git -pkg_yamler_repo = https://github.com/goertzenator/yamler -pkg_yamler_commit = master - -PACKAGES += yaws -pkg_yaws_name = yaws -pkg_yaws_description = Yaws webserver -pkg_yaws_homepage = http://yaws.hyber.org -pkg_yaws_fetch = git -pkg_yaws_repo = https://github.com/klacke/yaws -pkg_yaws_commit = master - -PACKAGES += zab_engine -pkg_zab_engine_name = zab_engine -pkg_zab_engine_description = zab propotocol implement by erlang -pkg_zab_engine_homepage = https://github.com/xinmingyao/zab_engine -pkg_zab_engine_fetch = git -pkg_zab_engine_repo = https://github.com/xinmingyao/zab_engine -pkg_zab_engine_commit = master - -PACKAGES += zabbix_sender -pkg_zabbix_sender_name = zabbix_sender -pkg_zabbix_sender_description = Zabbix trapper for sending data to Zabbix in pure Erlang -pkg_zabbix_sender_homepage = https://github.com/stalkermn/zabbix_sender -pkg_zabbix_sender_fetch = git -pkg_zabbix_sender_repo = https://github.com/stalkermn/zabbix_sender.git -pkg_zabbix_sender_commit = master - -PACKAGES += zeta -pkg_zeta_name = zeta -pkg_zeta_description = HTTP access log parser in Erlang -pkg_zeta_homepage = https://github.com/s1n4/zeta -pkg_zeta_fetch = git -pkg_zeta_repo = https://github.com/s1n4/zeta -pkg_zeta_commit = master - -PACKAGES += zippers -pkg_zippers_name = zippers -pkg_zippers_description = A library for functional zipper data structures in Erlang. Read more on zippers -pkg_zippers_homepage = https://github.com/ferd/zippers -pkg_zippers_fetch = git -pkg_zippers_repo = https://github.com/ferd/zippers -pkg_zippers_commit = master - -PACKAGES += zlists -pkg_zlists_name = zlists -pkg_zlists_description = Erlang lazy lists library. -pkg_zlists_homepage = https://github.com/vjache/erlang-zlists -pkg_zlists_fetch = git -pkg_zlists_repo = https://github.com/vjache/erlang-zlists -pkg_zlists_commit = master - -PACKAGES += zraft_lib -pkg_zraft_lib_name = zraft_lib -pkg_zraft_lib_description = Erlang raft consensus protocol implementation -pkg_zraft_lib_homepage = https://github.com/dreyk/zraft_lib -pkg_zraft_lib_fetch = git -pkg_zraft_lib_repo = https://github.com/dreyk/zraft_lib -pkg_zraft_lib_commit = master - -PACKAGES += zucchini -pkg_zucchini_name = zucchini -pkg_zucchini_description = An Erlang INI parser -pkg_zucchini_homepage = https://github.com/devinus/zucchini -pkg_zucchini_fetch = git -pkg_zucchini_repo = https://github.com/devinus/zucchini -pkg_zucchini_commit = master - -# Copyright (c) 2015-2016, Loïc Hoguin -# This file is part of erlang.mk and subject to the terms of the ISC License. - -.PHONY: search - -define pkg_print - $(verbose) printf "%s\n" \ - $(if $(call core_eq,$(1),$(pkg_$(1)_name)),,"Pkg name: $(1)") \ - "App name: $(pkg_$(1)_name)" \ - "Description: $(pkg_$(1)_description)" \ - "Home page: $(pkg_$(1)_homepage)" \ - "Fetch with: $(pkg_$(1)_fetch)" \ - "Repository: $(pkg_$(1)_repo)" \ - "Commit: $(pkg_$(1)_commit)" \ - "" - -endef - -search: -ifdef q - $(foreach p,$(PACKAGES), \ - $(if $(findstring $(call core_lc,$(q)),$(call core_lc,$(pkg_$(p)_name) $(pkg_$(p)_description))), \ - $(call pkg_print,$(p)))) -else - $(foreach p,$(PACKAGES),$(call pkg_print,$(p))) -endif - -# Copyright (c) 2013-2016, Loïc Hoguin -# This file is part of erlang.mk and subject to the terms of the ISC License. - -.PHONY: distclean-deps clean-tmp-deps.log - -# Configuration. - -ifdef OTP_DEPS -$(warning The variable OTP_DEPS is deprecated in favor of LOCAL_DEPS.) -endif - -IGNORE_DEPS ?= -export IGNORE_DEPS - -APPS_DIR ?= $(CURDIR)/apps -export APPS_DIR - -DEPS_DIR ?= $(CURDIR)/deps -export DEPS_DIR - -REBAR_DEPS_DIR = $(DEPS_DIR) -export REBAR_DEPS_DIR - -dep_name = $(if $(dep_$(1)),$(1),$(if $(pkg_$(1)_name),$(pkg_$(1)_name),$(1))) -dep_repo = $(patsubst git://github.com/%,https://github.com/%, \ - $(if $(dep_$(1)),$(word 2,$(dep_$(1))),$(pkg_$(1)_repo))) -dep_commit = $(if $(dep_$(1)_commit),$(dep_$(1)_commit),$(if $(dep_$(1)),$(word 3,$(dep_$(1))),$(pkg_$(1)_commit))) - -LOCAL_DEPS_DIRS = $(foreach a,$(LOCAL_DEPS),$(if $(wildcard $(APPS_DIR)/$(a)),$(APPS_DIR)/$(a))) -ALL_APPS_DIRS = $(if $(wildcard $(APPS_DIR)/),$(filter-out $(APPS_DIR),$(shell find $(APPS_DIR) -maxdepth 1 -type d))) -ALL_DEPS_DIRS = $(addprefix $(DEPS_DIR)/,$(foreach dep,$(filter-out $(IGNORE_DEPS),$(BUILD_DEPS) $(DEPS)),$(call dep_name,$(dep)))) - -ifeq ($(filter $(APPS_DIR) $(DEPS_DIR),$(subst :, ,$(ERL_LIBS))),) -ifeq ($(ERL_LIBS),) - ERL_LIBS = $(APPS_DIR):$(DEPS_DIR) -else - ERL_LIBS := $(ERL_LIBS):$(APPS_DIR):$(DEPS_DIR) -endif -endif -export ERL_LIBS - -export NO_AUTOPATCH - -# Verbosity. - -dep_verbose_0 = @echo " DEP " $(1); -dep_verbose_2 = set -x; -dep_verbose = $(dep_verbose_$(V)) - -# Core targets. - -apps:: $(ALL_APPS_DIRS) clean-tmp-deps.log -ifeq ($(IS_APP)$(IS_DEP),) - $(verbose) rm -f $(ERLANG_MK_TMP)/apps.log -endif - $(verbose) mkdir -p $(ERLANG_MK_TMP) -# Create ebin directory for all apps to make sure Erlang recognizes them -# as proper OTP applications when using -include_lib. This is a temporary -# fix, a proper fix would be to compile apps/* in the right order. -ifndef IS_APP - $(verbose) for dep in $(ALL_APPS_DIRS) ; do \ - mkdir -p $$dep/ebin || exit $$?; \ - done -endif -# at the toplevel: if LOCAL_DEPS is defined with at least one local app, only -# compile that list of apps. otherwise, compile everything. -# within an app: compile all LOCAL_DEPS that are (uncompiled) local apps - $(verbose) for dep in $(if $(LOCAL_DEPS_DIRS)$(IS_APP),$(LOCAL_DEPS_DIRS),$(ALL_APPS_DIRS)) ; do \ - if grep -qs ^$$dep$$ $(ERLANG_MK_TMP)/apps.log; then \ - :; \ - else \ - echo $$dep >> $(ERLANG_MK_TMP)/apps.log; \ - $(MAKE) -C $$dep IS_APP=1 || exit $$?; \ - fi \ - done - -clean-tmp-deps.log: -ifeq ($(IS_APP)$(IS_DEP),) - $(verbose) rm -f $(ERLANG_MK_TMP)/deps.log -endif - -ifneq ($(SKIP_DEPS),) -deps:: -else -deps:: $(ALL_DEPS_DIRS) apps clean-tmp-deps.log - $(verbose) mkdir -p $(ERLANG_MK_TMP) - $(verbose) for dep in $(ALL_DEPS_DIRS) ; do \ - if grep -qs ^$$dep$$ $(ERLANG_MK_TMP)/deps.log; then \ - :; \ - else \ - echo $$dep >> $(ERLANG_MK_TMP)/deps.log; \ - if [ -f $$dep/GNUmakefile ] || [ -f $$dep/makefile ] || [ -f $$dep/Makefile ]; then \ - $(MAKE) -C $$dep IS_DEP=1 || exit $$?; \ - else \ - echo "Error: No Makefile to build dependency $$dep."; \ - exit 2; \ - fi \ - fi \ - done -endif - -# Deps related targets. - -# @todo rename GNUmakefile and makefile into Makefile first, if they exist -# While Makefile file could be GNUmakefile or makefile, -# in practice only Makefile is needed so far. -define dep_autopatch - if [ -f $(DEPS_DIR)/$(1)/erlang.mk ]; then \ - rm -rf $(DEPS_DIR)/$1/ebin/; \ - $(call erlang,$(call dep_autopatch_appsrc.erl,$(1))); \ - $(call dep_autopatch_erlang_mk,$(1)); \ - elif [ -f $(DEPS_DIR)/$(1)/Makefile ]; then \ - if [ -f $(DEPS_DIR)/$1/rebar.lock ]; then \ - $(call dep_autopatch2,$1); \ - elif [ 0 != `grep -c "include ../\w*\.mk" $(DEPS_DIR)/$(1)/Makefile` ]; then \ - $(call dep_autopatch2,$(1)); \ - elif [ 0 != `grep -ci "^[^#].*rebar" $(DEPS_DIR)/$(1)/Makefile` ]; then \ - $(call dep_autopatch2,$(1)); \ - elif [ -n "`find $(DEPS_DIR)/$(1)/ -type f -name \*.mk -not -name erlang.mk -exec grep -i "^[^#].*rebar" '{}' \;`" ]; then \ - $(call dep_autopatch2,$(1)); \ - fi \ - else \ - if [ ! -d $(DEPS_DIR)/$(1)/src/ ]; then \ - $(call dep_autopatch_noop,$(1)); \ - else \ - $(call dep_autopatch2,$(1)); \ - fi \ - fi -endef - -define dep_autopatch2 - mv -n $(DEPS_DIR)/$1/ebin/$1.app $(DEPS_DIR)/$1/src/$1.app.src; \ - rm -f $(DEPS_DIR)/$1/ebin/$1.app; \ - if [ -f $(DEPS_DIR)/$1/src/$1.app.src.script ]; then \ - $(call erlang,$(call dep_autopatch_appsrc_script.erl,$(1))); \ - fi; \ - $(call erlang,$(call dep_autopatch_appsrc.erl,$(1))); \ - if [ -f $(DEPS_DIR)/$(1)/rebar -o -f $(DEPS_DIR)/$(1)/rebar.config -o -f $(DEPS_DIR)/$(1)/rebar.config.script -o -f $(DEPS_DIR)/$1/rebar.lock ]; then \ - $(call dep_autopatch_fetch_rebar); \ - $(call dep_autopatch_rebar,$(1)); \ - else \ - $(call dep_autopatch_gen,$(1)); \ - fi -endef - -define dep_autopatch_noop - printf "noop:\n" > $(DEPS_DIR)/$(1)/Makefile -endef - -# Overwrite erlang.mk with the current file by default. -ifeq ($(NO_AUTOPATCH_ERLANG_MK),) -define dep_autopatch_erlang_mk - echo "include $(call core_relpath,$(dir $(ERLANG_MK_FILENAME)),$(DEPS_DIR)/app)/erlang.mk" \ - > $(DEPS_DIR)/$1/erlang.mk -endef -else -define dep_autopatch_erlang_mk - : -endef -endif - -define dep_autopatch_gen - printf "%s\n" \ - "ERLC_OPTS = +debug_info" \ - "include ../../erlang.mk" > $(DEPS_DIR)/$(1)/Makefile -endef - -define dep_autopatch_fetch_rebar - mkdir -p $(ERLANG_MK_TMP); \ - if [ ! -d $(ERLANG_MK_TMP)/rebar ]; then \ - git clone -q -n -- https://github.com/rebar/rebar $(ERLANG_MK_TMP)/rebar; \ - cd $(ERLANG_MK_TMP)/rebar; \ - git checkout -q 576e12171ab8d69b048b827b92aa65d067deea01; \ - $(MAKE); \ - cd -; \ - fi -endef - -define dep_autopatch_rebar - if [ -f $(DEPS_DIR)/$(1)/Makefile ]; then \ - mv $(DEPS_DIR)/$(1)/Makefile $(DEPS_DIR)/$(1)/Makefile.orig.mk; \ - fi; \ - $(call erlang,$(call dep_autopatch_rebar.erl,$(1))); \ - rm -f $(DEPS_DIR)/$(1)/ebin/$(1).app -endef - -define dep_autopatch_rebar.erl - application:load(rebar), - application:set_env(rebar, log_level, debug), - rmemo:start(), - Conf1 = case file:consult("$(call core_native_path,$(DEPS_DIR)/$1/rebar.config)") of - {ok, Conf0} -> Conf0; - _ -> [] - end, - {Conf, OsEnv} = fun() -> - case filelib:is_file("$(call core_native_path,$(DEPS_DIR)/$1/rebar.config.script)") of - false -> {Conf1, []}; - true -> - Bindings0 = erl_eval:new_bindings(), - Bindings1 = erl_eval:add_binding('CONFIG', Conf1, Bindings0), - Bindings = erl_eval:add_binding('SCRIPT', "$(call core_native_path,$(DEPS_DIR)/$1/rebar.config.script)", Bindings1), - Before = os:getenv(), - {ok, Conf2} = file:script("$(call core_native_path,$(DEPS_DIR)/$1/rebar.config.script)", Bindings), - {Conf2, lists:foldl(fun(E, Acc) -> lists:delete(E, Acc) end, os:getenv(), Before)} - end - end(), - Write = fun (Text) -> - file:write_file("$(call core_native_path,$(DEPS_DIR)/$1/Makefile)", Text, [append]) - end, - Escape = fun (Text) -> - re:replace(Text, "\\\\$$", "\$$$$", [global, {return, list}]) - end, - Write("IGNORE_DEPS += edown eper eunit_formatters meck node_package " - "rebar_lock_deps_plugin rebar_vsn_plugin reltool_util\n"), - Write("C_SRC_DIR = /path/do/not/exist\n"), - Write("C_SRC_TYPE = rebar\n"), - Write("DRV_CFLAGS = -fPIC\nexport DRV_CFLAGS\n"), - Write(["ERLANG_ARCH = ", rebar_utils:wordsize(), "\nexport ERLANG_ARCH\n"]), - fun() -> - Write("ERLC_OPTS = +debug_info\nexport ERLC_OPTS\n"), - case lists:keyfind(erl_opts, 1, Conf) of - false -> ok; - {_, ErlOpts} -> - lists:foreach(fun - ({d, D}) -> - Write("ERLC_OPTS += -D" ++ atom_to_list(D) ++ "=1\n"); - ({i, I}) -> - Write(["ERLC_OPTS += -I ", I, "\n"]); - ({platform_define, Regex, D}) -> - case rebar_utils:is_arch(Regex) of - true -> Write("ERLC_OPTS += -D" ++ atom_to_list(D) ++ "=1\n"); - false -> ok - end; - ({parse_transform, PT}) -> - Write("ERLC_OPTS += +'{parse_transform, " ++ atom_to_list(PT) ++ "}'\n"); - (_) -> ok - end, ErlOpts) - end, - Write("\n") - end(), - fun() -> - File = case lists:keyfind(deps, 1, Conf) of - false -> []; - {_, Deps} -> - [begin case case Dep of - {N, S} when is_atom(N), is_list(S) -> {N, {hex, S}}; - {N, S} when is_tuple(S) -> {N, S}; - {N, _, S} -> {N, S}; - {N, _, S, _} -> {N, S}; - _ -> false - end of - false -> ok; - {Name, Source} -> - {Method, Repo, Commit} = case Source of - {hex, V} -> {hex, V, undefined}; - {git, R} -> {git, R, master}; - {M, R, {branch, C}} -> {M, R, C}; - {M, R, {ref, C}} -> {M, R, C}; - {M, R, {tag, C}} -> {M, R, C}; - {M, R, C} -> {M, R, C} - end, - Write(io_lib:format("DEPS += ~s\ndep_~s = ~s ~s ~s~n", [Name, Name, Method, Repo, Commit])) - end end || Dep <- Deps] - end - end(), - fun() -> - case lists:keyfind(erl_first_files, 1, Conf) of - false -> ok; - {_, Files} -> - Names = [[" ", case lists:reverse(F) of - "lre." ++ Elif -> lists:reverse(Elif); - Elif -> lists:reverse(Elif) - end] || "src/" ++ F <- Files], - Write(io_lib:format("COMPILE_FIRST +=~s\n", [Names])) - end - end(), - Write("\n\nrebar_dep: preprocess pre-deps deps pre-app app\n"), - Write("\npreprocess::\n"), - Write("\npre-deps::\n"), - Write("\npre-app::\n"), - PatchHook = fun(Cmd) -> - case Cmd of - "make -C" ++ Cmd1 -> "$$\(MAKE) -C" ++ Escape(Cmd1); - "gmake -C" ++ Cmd1 -> "$$\(MAKE) -C" ++ Escape(Cmd1); - "make " ++ Cmd1 -> "$$\(MAKE) -f Makefile.orig.mk " ++ Escape(Cmd1); - "gmake " ++ Cmd1 -> "$$\(MAKE) -f Makefile.orig.mk " ++ Escape(Cmd1); - _ -> Escape(Cmd) - end - end, - fun() -> - case lists:keyfind(pre_hooks, 1, Conf) of - false -> ok; - {_, Hooks} -> - [case H of - {'get-deps', Cmd} -> - Write("\npre-deps::\n\t" ++ PatchHook(Cmd) ++ "\n"); - {compile, Cmd} -> - Write("\npre-app::\n\tCC=$$\(CC) " ++ PatchHook(Cmd) ++ "\n"); - {Regex, compile, Cmd} -> - case rebar_utils:is_arch(Regex) of - true -> Write("\npre-app::\n\tCC=$$\(CC) " ++ PatchHook(Cmd) ++ "\n"); - false -> ok - end; - _ -> ok - end || H <- Hooks] - end - end(), - ShellToMk = fun(V) -> - re:replace(re:replace(V, "(\\\\$$)(\\\\w*)", "\\\\1(\\\\2)", [global]), - "-Werror\\\\b", "", [{return, list}, global]) - end, - PortSpecs = fun() -> - case lists:keyfind(port_specs, 1, Conf) of - false -> - case filelib:is_dir("$(call core_native_path,$(DEPS_DIR)/$1/c_src)") of - false -> []; - true -> - [{"priv/" ++ proplists:get_value(so_name, Conf, "$(1)_drv.so"), - proplists:get_value(port_sources, Conf, ["c_src/*.c"]), []}] - end; - {_, Specs} -> - lists:flatten([case S of - {Output, Input} -> {ShellToMk(Output), Input, []}; - {Regex, Output, Input} -> - case rebar_utils:is_arch(Regex) of - true -> {ShellToMk(Output), Input, []}; - false -> [] - end; - {Regex, Output, Input, [{env, Env}]} -> - case rebar_utils:is_arch(Regex) of - true -> {ShellToMk(Output), Input, Env}; - false -> [] - end - end || S <- Specs]) - end - end(), - PortSpecWrite = fun (Text) -> - file:write_file("$(call core_native_path,$(DEPS_DIR)/$1/c_src/Makefile.erlang.mk)", Text, [append]) - end, - case PortSpecs of - [] -> ok; - _ -> - Write("\npre-app::\n\t$$\(MAKE) -f c_src/Makefile.erlang.mk\n"), - PortSpecWrite(io_lib:format("ERL_CFLAGS ?= -finline-functions -Wall -fPIC -I \\"~s/erts-~s/include\\" -I \\"~s\\"\n", - [code:root_dir(), erlang:system_info(version), code:lib_dir(erl_interface, include)])), - PortSpecWrite(io_lib:format("ERL_LDFLAGS ?= -L \\"~s\\" -lerl_interface -lei\n", - [code:lib_dir(erl_interface, lib)])), - [PortSpecWrite(["\n", E, "\n"]) || E <- OsEnv], - FilterEnv = fun(Env) -> - lists:flatten([case E of - {_, _} -> E; - {Regex, K, V} -> - case rebar_utils:is_arch(Regex) of - true -> {K, V}; - false -> [] - end - end || E <- Env]) - end, - MergeEnv = fun(Env) -> - lists:foldl(fun ({K, V}, Acc) -> - case lists:keyfind(K, 1, Acc) of - false -> [{K, rebar_utils:expand_env_variable(V, K, "")}|Acc]; - {_, V0} -> [{K, rebar_utils:expand_env_variable(V, K, V0)}|Acc] - end - end, [], Env) - end, - PortEnv = case lists:keyfind(port_env, 1, Conf) of - false -> []; - {_, PortEnv0} -> FilterEnv(PortEnv0) - end, - PortSpec = fun ({Output, Input0, Env}) -> - filelib:ensure_dir("$(call core_native_path,$(DEPS_DIR)/$1/)" ++ Output), - Input = [[" ", I] || I <- Input0], - PortSpecWrite([ - [["\n", K, " = ", ShellToMk(V)] || {K, V} <- lists:reverse(MergeEnv(PortEnv))], - case $(PLATFORM) of - darwin -> "\n\nLDFLAGS += -flat_namespace -undefined suppress"; - _ -> "" - end, - "\n\nall:: ", Output, "\n\n", - "%.o: %.c\n\t$$\(CC) -c -o $$\@ $$\< $$\(CFLAGS) $$\(ERL_CFLAGS) $$\(DRV_CFLAGS) $$\(EXE_CFLAGS)\n\n", - "%.o: %.C\n\t$$\(CXX) -c -o $$\@ $$\< $$\(CXXFLAGS) $$\(ERL_CFLAGS) $$\(DRV_CFLAGS) $$\(EXE_CFLAGS)\n\n", - "%.o: %.cc\n\t$$\(CXX) -c -o $$\@ $$\< $$\(CXXFLAGS) $$\(ERL_CFLAGS) $$\(DRV_CFLAGS) $$\(EXE_CFLAGS)\n\n", - "%.o: %.cpp\n\t$$\(CXX) -c -o $$\@ $$\< $$\(CXXFLAGS) $$\(ERL_CFLAGS) $$\(DRV_CFLAGS) $$\(EXE_CFLAGS)\n\n", - [[Output, ": ", K, " += ", ShellToMk(V), "\n"] || {K, V} <- lists:reverse(MergeEnv(FilterEnv(Env)))], - Output, ": $$\(foreach ext,.c .C .cc .cpp,", - "$$\(patsubst %$$\(ext),%.o,$$\(filter %$$\(ext),$$\(wildcard", Input, "))))\n", - "\t$$\(CC) -o $$\@ $$\? $$\(LDFLAGS) $$\(ERL_LDFLAGS) $$\(DRV_LDFLAGS) $$\(EXE_LDFLAGS)", - case {filename:extension(Output), $(PLATFORM)} of - {[], _} -> "\n"; - {_, darwin} -> "\n"; - _ -> " -shared\n" - end]) - end, - [PortSpec(S) || S <- PortSpecs] - end, - Write("\ninclude $(call core_relpath,$(dir $(ERLANG_MK_FILENAME)),$(DEPS_DIR)/app)/erlang.mk"), - RunPlugin = fun(Plugin, Step) -> - case erlang:function_exported(Plugin, Step, 2) of - false -> ok; - true -> - c:cd("$(call core_native_path,$(DEPS_DIR)/$1/)"), - Ret = Plugin:Step({config, "", Conf, dict:new(), dict:new(), dict:new(), - dict:store(base_dir, "", dict:new())}, undefined), - io:format("rebar plugin ~p step ~p ret ~p~n", [Plugin, Step, Ret]) - end - end, - fun() -> - case lists:keyfind(plugins, 1, Conf) of - false -> ok; - {_, Plugins} -> - [begin - case lists:keyfind(deps, 1, Conf) of - false -> ok; - {_, Deps} -> - case lists:keyfind(P, 1, Deps) of - false -> ok; - _ -> - Path = "$(call core_native_path,$(DEPS_DIR)/)" ++ atom_to_list(P), - io:format("~s", [os:cmd("$(MAKE) -C $(call core_native_path,$(DEPS_DIR)/$1) " ++ Path)]), - io:format("~s", [os:cmd("$(MAKE) -C " ++ Path ++ " IS_DEP=1")]), - code:add_patha(Path ++ "/ebin") - end - end - end || P <- Plugins], - [case code:load_file(P) of - {module, P} -> ok; - _ -> - case lists:keyfind(plugin_dir, 1, Conf) of - false -> ok; - {_, PluginsDir} -> - ErlFile = "$(call core_native_path,$(DEPS_DIR)/$1/)" ++ PluginsDir ++ "/" ++ atom_to_list(P) ++ ".erl", - {ok, P, Bin} = compile:file(ErlFile, [binary]), - {module, P} = code:load_binary(P, ErlFile, Bin) - end - end || P <- Plugins], - [RunPlugin(P, preprocess) || P <- Plugins], - [RunPlugin(P, pre_compile) || P <- Plugins], - [RunPlugin(P, compile) || P <- Plugins] - end - end(), - halt() -endef - -define dep_autopatch_appsrc_script.erl - AppSrc = "$(call core_native_path,$(DEPS_DIR)/$1/src/$1.app.src)", - AppSrcScript = AppSrc ++ ".script", - Bindings = erl_eval:new_bindings(), - {ok, Conf} = file:script(AppSrcScript, Bindings), - ok = file:write_file(AppSrc, io_lib:format("~p.~n", [Conf])), - halt() -endef - -define dep_autopatch_appsrc.erl - AppSrcOut = "$(call core_native_path,$(DEPS_DIR)/$1/src/$1.app.src)", - AppSrcIn = case filelib:is_regular(AppSrcOut) of false -> "$(call core_native_path,$(DEPS_DIR)/$1/ebin/$1.app)"; true -> AppSrcOut end, - case filelib:is_regular(AppSrcIn) of - false -> ok; - true -> - {ok, [{application, $(1), L0}]} = file:consult(AppSrcIn), - L1 = lists:keystore(modules, 1, L0, {modules, []}), - L2 = case lists:keyfind(vsn, 1, L1) of {_, git} -> lists:keyreplace(vsn, 1, L1, {vsn, "git"}); _ -> L1 end, - L3 = case lists:keyfind(registered, 1, L2) of false -> [{registered, []}|L2]; _ -> L2 end, - ok = file:write_file(AppSrcOut, io_lib:format("~p.~n", [{application, $(1), L3}])), - case AppSrcOut of AppSrcIn -> ok; _ -> ok = file:delete(AppSrcIn) end - end, - halt() -endef - -define dep_fetch_git - git clone -q -n -- $(call dep_repo,$(1)) $(DEPS_DIR)/$(call dep_name,$(1)); \ - cd $(DEPS_DIR)/$(call dep_name,$(1)) && git checkout -q $(call dep_commit,$(1)); -endef - -define dep_fetch_git-submodule - git submodule update --init -- $(DEPS_DIR)/$1; -endef - -define dep_fetch_hg - hg clone -q -U $(call dep_repo,$(1)) $(DEPS_DIR)/$(call dep_name,$(1)); \ - cd $(DEPS_DIR)/$(call dep_name,$(1)) && hg update -q $(call dep_commit,$(1)); -endef - -define dep_fetch_svn - svn checkout -q $(call dep_repo,$(1)) $(DEPS_DIR)/$(call dep_name,$(1)); -endef - -define dep_fetch_cp - cp -R $(call dep_repo,$(1)) $(DEPS_DIR)/$(call dep_name,$(1)); -endef - -# Hex only has a package version. No need to look in the Erlang.mk packages. -define dep_fetch_hex - mkdir -p $(ERLANG_MK_TMP)/hex $(DEPS_DIR)/$1; \ - $(call core_http_get,$(ERLANG_MK_TMP)/hex/$1.tar,\ - https://s3.amazonaws.com/s3.hex.pm/tarballs/$1-$(strip $(word 2,$(dep_$1))).tar); \ - tar -xOf $(ERLANG_MK_TMP)/hex/$1.tar contents.tar.gz | tar -C $(DEPS_DIR)/$1 -xzf -; -endef - -define dep_fetch_fail - echo "Error: Unknown or invalid dependency: $(1)." >&2; \ - exit 78; -endef - -# Kept for compatibility purposes with older Erlang.mk configuration. -define dep_fetch_legacy - $(warning WARNING: '$(1)' dependency configuration uses deprecated format.) \ - git clone -q -n -- $(word 1,$(dep_$(1))) $(DEPS_DIR)/$(1); \ - cd $(DEPS_DIR)/$(1) && git checkout -q $(if $(word 2,$(dep_$(1))),$(word 2,$(dep_$(1))),master); -endef - -define dep_fetch - $(if $(dep_$(1)), \ - $(if $(dep_fetch_$(word 1,$(dep_$(1)))), \ - $(word 1,$(dep_$(1))), \ - $(if $(IS_DEP),legacy,fail)), \ - $(if $(filter $(1),$(PACKAGES)), \ - $(pkg_$(1)_fetch), \ - fail)) -endef - -define dep_target -$(DEPS_DIR)/$(call dep_name,$1): - $(eval DEP_NAME := $(call dep_name,$1)) - $(eval DEP_STR := $(if $(filter-out $1,$(DEP_NAME)),$1,"$1 ($(DEP_NAME))")) - $(verbose) if test -d $(APPS_DIR)/$(DEP_NAME); then \ - echo "Error: Dependency" $(DEP_STR) "conflicts with application found in $(APPS_DIR)/$(DEP_NAME)."; \ - exit 17; \ - fi - $(verbose) mkdir -p $(DEPS_DIR) - $(dep_verbose) $(call dep_fetch_$(strip $(call dep_fetch,$(1))),$(1)) - $(verbose) if [ -f $(DEPS_DIR)/$(1)/configure.ac -o -f $(DEPS_DIR)/$(1)/configure.in ] \ - && [ ! -f $(DEPS_DIR)/$(1)/configure ]; then \ - echo " AUTO " $(1); \ - cd $(DEPS_DIR)/$(1) && autoreconf -Wall -vif -I m4; \ - fi - - $(verbose) if [ -f $(DEPS_DIR)/$(DEP_NAME)/configure ]; then \ - echo " CONF " $(DEP_STR); \ - cd $(DEPS_DIR)/$(DEP_NAME) && ./configure; \ - fi -ifeq ($(filter $(1),$(NO_AUTOPATCH)),) - $(verbose) if [ "$(1)" = "amqp_client" -a "$(RABBITMQ_CLIENT_PATCH)" ]; then \ - if [ ! -d $(DEPS_DIR)/rabbitmq-codegen ]; then \ - echo " PATCH Downloading rabbitmq-codegen"; \ - git clone https://github.com/rabbitmq/rabbitmq-codegen.git $(DEPS_DIR)/rabbitmq-codegen; \ - fi; \ - if [ ! -d $(DEPS_DIR)/rabbitmq-server ]; then \ - echo " PATCH Downloading rabbitmq-server"; \ - git clone https://github.com/rabbitmq/rabbitmq-server.git $(DEPS_DIR)/rabbitmq-server; \ - fi; \ - ln -s $(DEPS_DIR)/amqp_client/deps/rabbit_common-0.0.0 $(DEPS_DIR)/rabbit_common; \ - elif [ "$(1)" = "rabbit" -a "$(RABBITMQ_SERVER_PATCH)" ]; then \ - if [ ! -d $(DEPS_DIR)/rabbitmq-codegen ]; then \ - echo " PATCH Downloading rabbitmq-codegen"; \ - git clone https://github.com/rabbitmq/rabbitmq-codegen.git $(DEPS_DIR)/rabbitmq-codegen; \ - fi \ - else \ - $$(call dep_autopatch,$(DEP_NAME)) \ - fi -endif -endef - -$(foreach dep,$(BUILD_DEPS) $(DEPS),$(eval $(call dep_target,$(dep)))) - -ifndef IS_APP -clean:: clean-apps - -clean-apps: - $(verbose) for dep in $(ALL_APPS_DIRS) ; do \ - $(MAKE) -C $$dep clean IS_APP=1 || exit $$?; \ - done - -distclean:: distclean-apps - -distclean-apps: - $(verbose) for dep in $(ALL_APPS_DIRS) ; do \ - $(MAKE) -C $$dep distclean IS_APP=1 || exit $$?; \ - done -endif - -ifndef SKIP_DEPS -distclean:: distclean-deps - -distclean-deps: - $(gen_verbose) rm -rf $(DEPS_DIR) -endif - -# Forward-declare variables used in core/deps-tools.mk. This is required -# in case plugins use them. - -ERLANG_MK_RECURSIVE_DEPS_LIST = $(ERLANG_MK_TMP)/recursive-deps-list.log -ERLANG_MK_RECURSIVE_DOC_DEPS_LIST = $(ERLANG_MK_TMP)/recursive-doc-deps-list.log -ERLANG_MK_RECURSIVE_REL_DEPS_LIST = $(ERLANG_MK_TMP)/recursive-rel-deps-list.log -ERLANG_MK_RECURSIVE_TEST_DEPS_LIST = $(ERLANG_MK_TMP)/recursive-test-deps-list.log -ERLANG_MK_RECURSIVE_SHELL_DEPS_LIST = $(ERLANG_MK_TMP)/recursive-shell-deps-list.log - -# Copyright (c) 2015-2016, Loïc Hoguin -# This file is part of erlang.mk and subject to the terms of the ISC License. - -# Verbosity. - -proto_verbose_0 = @echo " PROTO " $(filter %.proto,$(?F)); -proto_verbose = $(proto_verbose_$(V)) - -# Core targets. - -define compile_proto - $(verbose) mkdir -p ebin/ include/ - $(proto_verbose) $(call erlang,$(call compile_proto.erl,$(1))) - $(proto_verbose) erlc +debug_info -o ebin/ ebin/*.erl - $(verbose) rm ebin/*.erl -endef - -define compile_proto.erl - [begin - Dir = filename:dirname(filename:dirname(F)), - protobuffs_compile:generate_source(F, - [{output_include_dir, Dir ++ "/include"}, - {output_src_dir, Dir ++ "/ebin"}]) - end || F <- string:tokens("$(1)", " ")], - halt(). -endef - -ifneq ($(wildcard src/),) -ebin/$(PROJECT).app:: $(sort $(call core_find,src/,*.proto)) - $(if $(strip $?),$(call compile_proto,$?)) -endif - -# Copyright (c) 2013-2016, 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_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_ASN1_OPTS ?= - -ERLC_MIB_OPTS ?= -COMPILE_MIB_FIRST ?= -COMPILE_MIB_FIRST_PATHS = $(addprefix mibs/,$(addsuffix .mib,$(COMPILE_MIB_FIRST))) - -# Verbosity. - -app_verbose_0 = @echo " APP " $(PROJECT); -app_verbose_2 = set -x; -app_verbose = $(app_verbose_$(V)) - -appsrc_verbose_0 = @echo " APP " $(PROJECT).app.src; -appsrc_verbose_2 = set -x; -appsrc_verbose = $(appsrc_verbose_$(V)) - -makedep_verbose_0 = @echo " DEPEND" $(PROJECT).d; -makedep_verbose_2 = set -x; -makedep_verbose = $(makedep_verbose_$(V)) - -erlc_verbose_0 = @echo " ERLC " $(filter-out $(patsubst %,%.erl,$(ERLC_EXCLUDE)),\ - $(filter %.erl %.core,$(?F))); -erlc_verbose_2 = set -x; -erlc_verbose = $(erlc_verbose_$(V)) - -xyrl_verbose_0 = @echo " XYRL " $(filter %.xrl %.yrl,$(?F)); -xyrl_verbose_2 = set -x; -xyrl_verbose = $(xyrl_verbose_$(V)) - -asn1_verbose_0 = @echo " ASN1 " $(filter %.asn1,$(?F)); -asn1_verbose_2 = set -x; -asn1_verbose = $(asn1_verbose_$(V)) - -mib_verbose_0 = @echo " MIB " $(filter %.bin %.mib,$(?F)); -mib_verbose_2 = set -x; -mib_verbose = $(mib_verbose_$(V)) - -ifneq ($(wildcard src/),) - -# Targets. - -ifeq ($(wildcard ebin/test),) -app:: deps $(PROJECT).d - $(verbose) $(MAKE) --no-print-directory app-build -else -app:: clean deps $(PROJECT).d - $(verbose) $(MAKE) --no-print-directory app-build -endif - -ifeq ($(wildcard src/$(PROJECT_MOD).erl),) -define app_file -{application, '$(PROJECT)', [ - {description, "$(PROJECT_DESCRIPTION)"}, - {vsn, "$(PROJECT_VERSION)"},$(if $(IS_DEP), - {id$(comma)$(space)"$(1)"}$(comma)) - {modules, [$(call comma_list,$(2))]}, - {registered, []}, - {applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS))]}, - {env, $(subst \,\\,$(PROJECT_ENV))}$(if $(findstring {,$(PROJECT_APP_EXTRA_KEYS)),$(comma)$(newline)$(tab)$(subst \,\\,$(PROJECT_APP_EXTRA_KEYS)),) -]}. -endef -else -define app_file -{application, '$(PROJECT)', [ - {description, "$(PROJECT_DESCRIPTION)"}, - {vsn, "$(PROJECT_VERSION)"},$(if $(IS_DEP), - {id$(comma)$(space)"$(1)"}$(comma)) - {modules, [$(call comma_list,$(2))]}, - {registered, [$(call comma_list,$(PROJECT)_sup $(PROJECT_REGISTERED))]}, - {applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS))]}, - {mod, {$(PROJECT_MOD), []}}, - {env, $(subst \,\\,$(PROJECT_ENV))}$(if $(findstring {,$(PROJECT_APP_EXTRA_KEYS)),$(comma)$(newline)$(tab)$(subst \,\\,$(PROJECT_APP_EXTRA_KEYS)),) -]}. -endef -endif - -app-build: ebin/$(PROJECT).app - $(verbose) : - -# Source files. - -ALL_SRC_FILES := $(sort $(call core_find,src/,*)) - -ERL_FILES := $(filter %.erl,$(ALL_SRC_FILES)) -CORE_FILES := $(filter %.core,$(ALL_SRC_FILES)) - -# ASN.1 files. - -ifneq ($(wildcard asn1/),) -ASN1_FILES = $(sort $(call core_find,asn1/,*.asn1)) -ERL_FILES += $(addprefix src/,$(patsubst %.asn1,%.erl,$(notdir $(ASN1_FILES)))) - -define compile_asn1 - $(verbose) mkdir -p include/ - $(asn1_verbose) erlc -v -I include/ -o asn1/ +noobj $(ERLC_ASN1_OPTS) $(1) - $(verbose) mv asn1/*.erl src/ - $(verbose) mv asn1/*.hrl include/ - $(verbose) mv asn1/*.asn1db include/ -endef - -$(PROJECT).d:: $(ASN1_FILES) - $(if $(strip $?),$(call compile_asn1,$?)) -endif - -# SNMP MIB files. - -ifneq ($(wildcard mibs/),) -MIB_FILES = $(sort $(call core_find,mibs/,*.mib)) - -$(PROJECT).d:: $(COMPILE_MIB_FIRST_PATHS) $(MIB_FILES) - $(verbose) mkdir -p include/ priv/mibs/ - $(mib_verbose) erlc -v $(ERLC_MIB_OPTS) -o priv/mibs/ -I priv/mibs/ $? - $(mib_verbose) erlc -o include/ -- $(addprefix priv/mibs/,$(patsubst %.mib,%.bin,$(notdir $?))) -endif - -# Leex and Yecc files. - -XRL_FILES := $(filter %.xrl,$(ALL_SRC_FILES)) -XRL_ERL_FILES = $(addprefix src/,$(patsubst %.xrl,%.erl,$(notdir $(XRL_FILES)))) -ERL_FILES += $(XRL_ERL_FILES) - -YRL_FILES := $(filter %.yrl,$(ALL_SRC_FILES)) -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/ $(YRL_ERLC_OPTS) $?) - -# Erlang and Core Erlang files. - -define makedep.erl - E = ets:new(makedep, [bag]), - G = digraph:new([acyclic]), - ErlFiles = lists:usort(string:tokens("$(ERL_FILES)", " ")), - Modules = [{list_to_atom(filename:basename(F, ".erl")), F} || F <- ErlFiles], - Add = fun (Mod, Dep) -> - case lists:keyfind(Dep, 1, Modules) of - false -> ok; - {_, DepFile} -> - {_, ModFile} = lists:keyfind(Mod, 1, Modules), - ets:insert(E, {ModFile, DepFile}), - digraph:add_vertex(G, Mod), - digraph:add_vertex(G, Dep), - digraph:add_edge(G, Mod, Dep) - end - end, - AddHd = fun (F, Mod, DepFile) -> - case file:open(DepFile, [read]) of - {error, enoent} -> ok; - {ok, Fd} -> - F(F, Fd, Mod), - {_, ModFile} = lists:keyfind(Mod, 1, Modules), - ets:insert(E, {ModFile, DepFile}) - end - end, - Attr = fun - (F, Mod, behavior, Dep) -> Add(Mod, Dep); - (F, Mod, behaviour, Dep) -> Add(Mod, Dep); - (F, Mod, compile, {parse_transform, Dep}) -> Add(Mod, Dep); - (F, Mod, compile, Opts) when is_list(Opts) -> - case proplists:get_value(parse_transform, Opts) of - undefined -> ok; - Dep -> Add(Mod, Dep) - end; - (F, Mod, include, Hrl) -> - case filelib:is_file("include/" ++ Hrl) of - true -> AddHd(F, Mod, "include/" ++ Hrl); - false -> - case filelib:is_file("src/" ++ Hrl) of - true -> AddHd(F, Mod, "src/" ++ Hrl); - false -> false - end - end; - (F, Mod, include_lib, "$1/include/" ++ Hrl) -> AddHd(F, Mod, "include/" ++ Hrl); - (F, Mod, include_lib, Hrl) -> AddHd(F, Mod, "include/" ++ Hrl); - (F, Mod, import, {Imp, _}) -> - IsFile = - case lists:keyfind(Imp, 1, Modules) of - false -> false; - {_, FilePath} -> filelib:is_file(FilePath) - end, - case IsFile of - false -> ok; - true -> Add(Mod, Imp) - end; - (_, _, _, _) -> ok - end, - MakeDepend = fun(F, Fd, Mod) -> - case io:parse_erl_form(Fd, undefined) of - {ok, {attribute, _, Key, Value}, _} -> - Attr(F, Mod, Key, Value), - F(F, Fd, Mod); - {eof, _} -> - file:close(Fd); - _ -> - F(F, Fd, Mod) - end - end, - [begin - Mod = list_to_atom(filename:basename(F, ".erl")), - {ok, Fd} = file:open(F, [read]), - MakeDepend(MakeDepend, Fd, Mod) - end || F <- ErlFiles], - Depend = sofs:to_external(sofs:relation_to_family(sofs:relation(ets:tab2list(E)))), - CompileFirst = [X || X <- lists:reverse(digraph_utils:topsort(G)), [] =/= digraph:in_neighbours(G, X)], - TargetPath = fun(Target) -> - case lists:keyfind(Target, 1, Modules) of - false -> ""; - {_, DepFile} -> - DirSubname = tl(string:tokens(filename:dirname(DepFile), "/")), - string:join(DirSubname ++ [atom_to_list(Target)], "/") - end - end, - ok = file:write_file("$(1)", [ - [[F, "::", [[" ", D] || D <- Deps], "; @touch \$$@\n"] || {F, Deps} <- Depend], - "\nCOMPILE_FIRST +=", [[" ", TargetPath(CF)] || CF <- CompileFirst], "\n" - ]), - halt() -endef - -ifeq ($(if $(NO_MAKEDEP),$(wildcard $(PROJECT).d),),) -$(PROJECT).d:: $(ERL_FILES) $(call core_find,include/,*.hrl) $(MAKEFILE_LIST) - $(makedep_verbose) $(call erlang,$(call makedep.erl,$@)) -endif - -ifneq ($(words $(ERL_FILES) $(CORE_FILES) $(ASN1_FILES) $(MIB_FILES) $(XRL_FILES) $(YRL_FILES)),0) -# Rebuild everything when the Makefile changes. -$(ERLANG_MK_TMP)/last-makefile-change: $(MAKEFILE_LIST) - $(verbose) mkdir -p $(ERLANG_MK_TMP) - $(verbose) if test -f $@; then \ - touch $(ERL_FILES) $(CORE_FILES) $(ASN1_FILES) $(MIB_FILES) $(XRL_FILES) $(YRL_FILES); \ - touch -c $(PROJECT).d; \ - fi - $(verbose) touch $@ - -$(ERL_FILES) $(CORE_FILES) $(ASN1_FILES) $(MIB_FILES) $(XRL_FILES) $(YRL_FILES):: $(ERLANG_MK_TMP)/last-makefile-change -ebin/$(PROJECT).app:: $(ERLANG_MK_TMP)/last-makefile-change -endif - -include $(wildcard $(PROJECT).d) - -ebin/$(PROJECT).app:: ebin/ - -ebin/: - $(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) $(wildcard src/$(PROJECT).app.src) - $(eval FILES_TO_COMPILE := $(filter-out src/$(PROJECT).app.src,$?)) - $(if $(strip $(FILES_TO_COMPILE)),$(call compile_erl,$(FILES_TO_COMPILE))) - $(eval GITDESCRIBE := $(shell git describe --dirty --abbrev=7 --tags --always --first-parent 2>/dev/null || true)) - $(eval MODULES := $(patsubst %,'%',$(sort $(notdir $(basename \ - $(filter-out $(ERLC_EXCLUDE_PATHS),$(ERL_FILES) $(CORE_FILES) $(BEAM_FILES))))))) -ifeq ($(wildcard src/$(PROJECT).app.src),) - $(app_verbose) printf '$(subst %,%%,$(subst $(newline),\n,$(subst ','\'',$(call app_file,$(GITDESCRIBE),$(MODULES)))))' \ - > ebin/$(PROJECT).app -else - $(verbose) if [ -z "$$(grep -e '^[^%]*{\s*modules\s*,' src/$(PROJECT).app.src)" ]; then \ - echo "Empty modules entry not found in $(PROJECT).app.src. Please consult the erlang.mk README for instructions." >&2; \ - exit 1; \ - fi - $(appsrc_verbose) cat src/$(PROJECT).app.src \ - | sed "s/{[[:space:]]*modules[[:space:]]*,[[:space:]]*\[\]}/{modules, \[$(call comma_list,$(MODULES))\]}/" \ - | sed "s/{id,[[:space:]]*\"git\"}/{id, \"$(subst /,\/,$(GITDESCRIBE))\"}/" \ - > ebin/$(PROJECT).app -endif - -clean:: clean-app - -clean-app: - $(gen_verbose) rm -rf $(PROJECT).d ebin/ priv/mibs/ $(XRL_ERL_FILES) $(YRL_ERL_FILES) \ - $(addprefix include/,$(patsubst %.mib,%.hrl,$(notdir $(MIB_FILES)))) \ - $(addprefix include/,$(patsubst %.asn1,%.hrl,$(notdir $(ASN1_FILES)))) \ - $(addprefix include/,$(patsubst %.asn1,%.asn1db,$(notdir $(ASN1_FILES)))) \ - $(addprefix src/,$(patsubst %.asn1,%.erl,$(notdir $(ASN1_FILES)))) - -endif - -# Copyright (c) 2016, Loïc Hoguin -# Copyright (c) 2015, Viktor Söderqvist -# This file is part of erlang.mk and subject to the terms of the ISC License. - -.PHONY: docs-deps - -# Configuration. - -ALL_DOC_DEPS_DIRS = $(addprefix $(DEPS_DIR)/,$(DOC_DEPS)) - -# Targets. - -$(foreach dep,$(DOC_DEPS),$(eval $(call dep_target,$(dep)))) - -ifneq ($(SKIP_DEPS),) -doc-deps: -else -doc-deps: $(ALL_DOC_DEPS_DIRS) - $(verbose) for dep in $(ALL_DOC_DEPS_DIRS) ; do $(MAKE) -C $$dep; done -endif - -# Copyright (c) 2015-2016, Loïc Hoguin -# This file is part of erlang.mk and subject to the terms of the ISC License. - -.PHONY: rel-deps - -# Configuration. - -ALL_REL_DEPS_DIRS = $(addprefix $(DEPS_DIR)/,$(REL_DEPS)) - -# Targets. - -$(foreach dep,$(REL_DEPS),$(eval $(call dep_target,$(dep)))) - -ifneq ($(SKIP_DEPS),) -rel-deps: -else -rel-deps: $(ALL_REL_DEPS_DIRS) - $(verbose) for dep in $(ALL_REL_DEPS_DIRS) ; do $(MAKE) -C $$dep; done -endif - -# Copyright (c) 2015-2016, Loïc Hoguin -# This file is part of erlang.mk and subject to the terms of the ISC License. - -.PHONY: test-deps test-dir test-build clean-test-dir - -# Configuration. - -TEST_DIR ?= $(CURDIR)/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)))) - -ifneq ($(SKIP_DEPS),) -test-deps: -else -test-deps: $(ALL_TEST_DEPS_DIRS) - $(verbose) for dep in $(ALL_TEST_DEPS_DIRS) ; do $(MAKE) -C $$dep IS_DEP=1; done -endif - -ifneq ($(wildcard $(TEST_DIR)),) -test-dir: - $(gen_verbose) erlc -v $(TEST_ERLC_OPTS) -I include/ -o $(TEST_DIR) \ - $(call core_find,$(TEST_DIR)/,*.erl) -pa ebin/ -endif - -ifeq ($(wildcard src),) -test-build:: ERLC_OPTS=$(TEST_ERLC_OPTS) -test-build:: clean deps test-deps - $(verbose) $(MAKE) --no-print-directory test-dir ERLC_OPTS="$(TEST_ERLC_OPTS)" -else -ifeq ($(wildcard ebin/test),) -test-build:: ERLC_OPTS=$(TEST_ERLC_OPTS) -test-build:: clean deps test-deps $(PROJECT).d - $(verbose) $(MAKE) --no-print-directory app-build test-dir ERLC_OPTS="$(TEST_ERLC_OPTS)" - $(gen_verbose) touch ebin/test -else -test-build:: ERLC_OPTS=$(TEST_ERLC_OPTS) -test-build:: deps test-deps $(PROJECT).d - $(verbose) $(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 -endif - -# Copyright (c) 2015-2016, Loïc Hoguin -# This file is part of erlang.mk and subject to the terms of the ISC License. - -.PHONY: rebar.config - -# We strip out -Werror because we don't want to fail due to -# warnings when used as a dependency. - -compat_prepare_erlc_opts = $(shell echo "$1" | sed 's/, */,/g') - -define compat_convert_erlc_opts -$(if $(filter-out -Werror,$1),\ - $(if $(findstring +,$1),\ - $(shell echo $1 | cut -b 2-))) -endef - -define compat_erlc_opts_to_list -[$(call comma_list,$(foreach o,$(call compat_prepare_erlc_opts,$1),$(call compat_convert_erlc_opts,$o)))] -endef - -define compat_rebar_config -{deps, [ -$(call comma_list,$(foreach d,$(DEPS),\ - $(if $(filter hex,$(call dep_fetch,$d)),\ - {$(call dep_name,$d)$(comma)"$(call dep_repo,$d)"},\ - {$(call dep_name,$d)$(comma)".*"$(comma){git,"$(call dep_repo,$d)"$(comma)"$(call dep_commit,$d)"}}))) -]}. -{erl_opts, $(call compat_erlc_opts_to_list,$(ERLC_OPTS))}. -endef - -$(eval _compat_rebar_config = $$(compat_rebar_config)) -$(eval export _compat_rebar_config) - -rebar.config: - $(gen_verbose) echo "$${_compat_rebar_config}" > rebar.config - -# Copyright (c) 2015-2016, Loïc Hoguin -# This file is part of erlang.mk and subject to the terms of the ISC License. - -ifeq ($(filter asciideck,$(DEPS) $(DOC_DEPS)),asciideck) - -.PHONY: asciidoc asciidoc-guide asciidoc-manual install-asciidoc distclean-asciidoc-guide distclean-asciidoc-manual - -# Core targets. - -docs:: asciidoc - -distclean:: distclean-asciidoc-guide distclean-asciidoc-manual - -# Plugin-specific targets. - -asciidoc: asciidoc-guide asciidoc-manual - -# User guide. - -ifeq ($(wildcard doc/src/guide/book.asciidoc),) -asciidoc-guide: -else -asciidoc-guide: distclean-asciidoc-guide doc-deps - a2x -v -f pdf doc/src/guide/book.asciidoc && mv doc/src/guide/book.pdf doc/guide.pdf - a2x -v -f chunked doc/src/guide/book.asciidoc && mv doc/src/guide/book.chunked/ doc/html/ - -distclean-asciidoc-guide: - $(gen_verbose) rm -rf doc/html/ doc/guide.pdf -endif - -# Man pages. - -ASCIIDOC_MANUAL_FILES := $(wildcard doc/src/manual/*.asciidoc) - -ifeq ($(ASCIIDOC_MANUAL_FILES),) -asciidoc-manual: -else - -# Configuration. - -MAN_INSTALL_PATH ?= /usr/local/share/man -MAN_SECTIONS ?= 3 7 -MAN_PROJECT ?= $(shell echo $(PROJECT) | sed 's/^./\U&\E/') -MAN_VERSION ?= $(PROJECT_VERSION) - -# Plugin-specific targets. - -define asciidoc2man.erl -try - [begin - io:format(" ADOC ~s~n", [F]), - ok = asciideck:to_manpage(asciideck:parse_file(F), #{ - compress => gzip, - outdir => filename:dirname(F), - extra2 => "$(MAN_PROJECT) $(MAN_VERSION)", - extra3 => "$(MAN_PROJECT) Function Reference" - }) - end || F <- [$(shell echo $(addprefix $(comma)\",$(addsuffix \",$1)) | sed 's/^.//')]], - halt(0) -catch C:E -> - io:format("Exception ~p:~p~nStacktrace: ~p~n", [C, E, erlang:get_stacktrace()]), - halt(1) -end. -endef - -asciidoc-manual:: doc-deps - -asciidoc-manual:: $(ASCIIDOC_MANUAL_FILES) - $(call erlang,$(call asciidoc2man.erl,$?)) - $(foreach s,$(MAN_SECTIONS),mkdir -p doc/man$s/ && mv doc/src/manual/*.$s.gz doc/man$s/;) - -install-docs:: install-asciidoc - -install-asciidoc: asciidoc-manual - $(foreach s,$(MAN_SECTIONS),\ - mkdir -p $(MAN_INSTALL_PATH)/man$s/ && \ - install -g `id -u` -o `id -g` -m 0644 doc/man$s/*.gz $(MAN_INSTALL_PATH)/man$s/;) - -distclean-asciidoc-manual: - $(gen_verbose) rm -rf $(addprefix doc/man,$(MAN_SECTIONS)) -endif -endif - -# Copyright (c) 2014-2016, Loïc Hoguin -# 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 - -# Core targets. - -help:: - $(verbose) printf "%s\n" "" \ - "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-app in=NAME Create a new local OTP application NAME" \ - " new-lib in=NAME Create a new local OTP library NAME" \ - " new t=TPL n=NAME Generate a module NAME based on the template TPL" \ - " new t=T n=N in=APP Generate a module NAME based on the template TPL in APP" \ - " list-templates List available templates" - -# Bootstrap templates. - -define bs_appsrc -{application, $p, [ - {description, ""}, - {vsn, "0.1.0"}, - {id, "git"}, - {modules, []}, - {registered, []}, - {applications, [ - kernel, - stdlib - ]}, - {mod, {$p_app, []}}, - {env, []} -]}. -endef - -define bs_appsrc_lib -{application, $p, [ - {description, ""}, - {vsn, "0.1.0"}, - {id, "git"}, - {modules, []}, - {registered, []}, - {applications, [ - kernel, - stdlib - ]} -]}. -endef - -# To prevent autocompletion issues with ZSH, we add "include erlang.mk" -# separately during the actual bootstrap. -ifdef SP -define bs_Makefile -PROJECT = $p -PROJECT_DESCRIPTION = New project -PROJECT_VERSION = 0.1.0 - -# Whitespace to be used when creating files from templates. -SP = $(SP) - -endef -else -define bs_Makefile -PROJECT = $p -PROJECT_DESCRIPTION = New project -PROJECT_VERSION = 0.1.0 - -endef -endif - -define bs_apps_Makefile -PROJECT = $p -PROJECT_DESCRIPTION = New project -PROJECT_VERSION = 0.1.0 - -include $(call core_relpath,$(dir $(ERLANG_MK_FILENAME)),$(APPS_DIR)/app)/erlang.mk -endef - -define bs_app --module($p_app). --behaviour(application). - --export([start/2]). --export([stop/1]). - -start(_Type, _Args) -> - $p_sup:start_link(). - -stop(_State) -> - ok. -endef - -define bs_relx_config -{release, {$p_release, "1"}, [$p, sasl, runtime_tools]}. -{extended_start_script, true}. -{sys_config, "rel/sys.config"}. -{vm_args, "rel/vm.args"}. -endef - -define bs_sys_config -[ -]. -endef - -define bs_vm_args --name $p@127.0.0.1 --setcookie $p --heart -endef - -# Normal templates. - -define tpl_supervisor --module($(n)). --behaviour(supervisor). - --export([start_link/0]). --export([init/1]). - -start_link() -> - supervisor:start_link({local, ?MODULE}, ?MODULE, []). - -init([]) -> - Procs = [], - {ok, {{one_for_one, 1, 5}, Procs}}. -endef - -define tpl_gen_server --module($(n)). --behaviour(gen_server). - -%% API. --export([start_link/0]). - -%% gen_server. --export([init/1]). --export([handle_call/3]). --export([handle_cast/2]). --export([handle_info/2]). --export([terminate/2]). --export([code_change/3]). - --record(state, { -}). - -%% API. - --spec start_link() -> {ok, pid()}. -start_link() -> - gen_server:start_link(?MODULE, [], []). - -%% gen_server. - -init([]) -> - {ok, #state{}}. - -handle_call(_Request, _From, State) -> - {reply, ignored, State}. - -handle_cast(_Msg, State) -> - {noreply, State}. - -handle_info(_Info, State) -> - {noreply, State}. - -terminate(_Reason, _State) -> - ok. - -code_change(_OldVsn, State, _Extra) -> - {ok, State}. -endef - -define tpl_module --module($(n)). --export([]). -endef - -define tpl_cowboy_http --module($(n)). --behaviour(cowboy_http_handler). - --export([init/3]). --export([handle/2]). --export([terminate/3]). - --record(state, { -}). - -init(_, Req, _Opts) -> - {ok, Req, #state{}}. - -handle(Req, State=#state{}) -> - {ok, Req2} = cowboy_req:reply(200, Req), - {ok, Req2, State}. - -terminate(_Reason, _Req, _State) -> - ok. -endef - -define 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}. -endef - -define tpl_cowboy_loop --module($(n)). --behaviour(cowboy_loop_handler). - --export([init/3]). --export([info/3]). --export([terminate/3]). - --record(state, { -}). - -init(_, Req, _Opts) -> - {loop, Req, #state{}, 5000, hibernate}. - -info(_Info, Req, State) -> - {loop, Req, State, hibernate}. - -terminate(_Reason, _Req, _State) -> - ok. -endef - -define tpl_cowboy_rest --module($(n)). - --export([init/3]). --export([content_types_provided/2]). --export([get_html/2]). - -init(_, _Req, _Opts) -> - {upgrade, protocol, cowboy_rest}. - -content_types_provided(Req, State) -> - {[{{<<"text">>, <<"html">>, '*'}, get_html}], Req, State}. - -get_html(Req, State) -> - {<<"This is REST!">>, Req, State}. -endef - -define tpl_cowboy_ws --module($(n)). --behaviour(cowboy_websocket_handler). - --export([init/3]). --export([websocket_init/3]). --export([websocket_handle/3]). --export([websocket_info/3]). --export([websocket_terminate/3]). - --record(state, { -}). - -init(_, _, _) -> - {upgrade, protocol, cowboy_websocket}. - -websocket_init(_, Req, _Opts) -> - Req2 = cowboy_req:compact(Req), - {ok, Req2, #state{}}. - -websocket_handle({text, Data}, Req, State) -> - {reply, {text, Data}, Req, State}; -websocket_handle({binary, Data}, Req, State) -> - {reply, {binary, Data}, Req, State}; -websocket_handle(_Frame, Req, State) -> - {ok, Req, State}. - -websocket_info(_Info, Req, State) -> - {ok, Req, State}. - -websocket_terminate(_Reason, _Req, _State) -> - ok. -endef - -define tpl_ranch_protocol --module($(n)). --behaviour(ranch_protocol). - --export([start_link/4]). --export([init/4]). - --type opts() :: []. --export_type([opts/0]). - --record(state, { - socket :: inet:socket(), - transport :: module() -}). - -start_link(Ref, Socket, Transport, Opts) -> - Pid = spawn_link(?MODULE, init, [Ref, Socket, Transport, Opts]), - {ok, Pid}. - --spec init(ranch:ref(), inet:socket(), module(), opts()) -> ok. -init(Ref, Socket, Transport, _Opts) -> - ok = ranch:accept_ack(Ref), - loop(#state{socket=Socket, transport=Transport}). - -loop(State) -> - loop(State). -endef - -# Plugin-specific targets. - -define render_template - $(verbose) printf -- '$(subst $(newline),\n,$(subst %,%%,$(subst ','\'',$(subst $(tab),$(WS),$(call $(1))))))\n' > $(2) -endef - -ifndef WS -ifdef SP -WS = $(subst a,,a $(wordlist 1,$(SP),a a a a a a a a a a a a a a a a a a a a)) -else -WS = $(tab) -endif -endif - -bootstrap: -ifneq ($(wildcard src/),) - $(error Error: src/ directory already exists) -endif - $(eval p := $(PROJECT)) - $(eval n := $(PROJECT)_sup) - $(call render_template,bs_Makefile,Makefile) - $(verbose) echo "include erlang.mk" >> Makefile - $(verbose) mkdir src/ -ifdef LEGACY - $(call render_template,bs_appsrc,src/$(PROJECT).app.src) -endif - $(call render_template,bs_app,src/$(PROJECT)_app.erl) - $(call render_template,tpl_supervisor,src/$(PROJECT)_sup.erl) - -bootstrap-lib: -ifneq ($(wildcard src/),) - $(error Error: src/ directory already exists) -endif - $(eval p := $(PROJECT)) - $(call render_template,bs_Makefile,Makefile) - $(verbose) echo "include erlang.mk" >> Makefile - $(verbose) mkdir src/ -ifdef LEGACY - $(call render_template,bs_appsrc_lib,src/$(PROJECT).app.src) -endif - -bootstrap-rel: -ifneq ($(wildcard relx.config),) - $(error Error: relx.config already exists) -endif -ifneq ($(wildcard rel/),) - $(error Error: rel/ directory already exists) -endif - $(eval p := $(PROJECT)) - $(call render_template,bs_relx_config,relx.config) - $(verbose) mkdir rel/ - $(call render_template,bs_sys_config,rel/sys.config) - $(call render_template,bs_vm_args,rel/vm.args) - -new-app: -ifndef in - $(error Usage: $(MAKE) new-app in=APP) -endif -ifneq ($(wildcard $(APPS_DIR)/$in),) - $(error Error: Application $in already exists) -endif - $(eval p := $(in)) - $(eval n := $(in)_sup) - $(verbose) mkdir -p $(APPS_DIR)/$p/src/ - $(call render_template,bs_apps_Makefile,$(APPS_DIR)/$p/Makefile) -ifdef LEGACY - $(call render_template,bs_appsrc,$(APPS_DIR)/$p/src/$p.app.src) -endif - $(call render_template,bs_app,$(APPS_DIR)/$p/src/$p_app.erl) - $(call render_template,tpl_supervisor,$(APPS_DIR)/$p/src/$p_sup.erl) - -new-lib: -ifndef in - $(error Usage: $(MAKE) new-lib in=APP) -endif -ifneq ($(wildcard $(APPS_DIR)/$in),) - $(error Error: Application $in already exists) -endif - $(eval p := $(in)) - $(verbose) mkdir -p $(APPS_DIR)/$p/src/ - $(call render_template,bs_apps_Makefile,$(APPS_DIR)/$p/Makefile) -ifdef LEGACY - $(call render_template,bs_appsrc_lib,$(APPS_DIR)/$p/src/$p.app.src) -endif - -new: -ifeq ($(wildcard src/)$(in),) - $(error Error: src/ directory does not exist) -endif -ifndef t - $(error Usage: $(MAKE) new t=TEMPLATE n=NAME [in=APP]) -endif -ifndef n - $(error Usage: $(MAKE) new t=TEMPLATE n=NAME [in=APP]) -endif -ifdef in - $(verbose) $(MAKE) -C $(APPS_DIR)/$(in)/ new t=$t n=$n in= -else - $(call render_template,tpl_$(t),src/$(n).erl) -endif - -list-templates: - $(verbose) echo Available templates: $(sort $(patsubst tpl_%,%,$(filter tpl_%,$(.VARIABLES)))) - -# Copyright (c) 2014-2016, Loïc Hoguin -# This file is part of erlang.mk and subject to the terms of the ISC License. - -.PHONY: clean-c_src distclean-c_src-env - -# Configuration. - -C_SRC_DIR ?= $(CURDIR)/c_src -C_SRC_ENV ?= $(C_SRC_DIR)/env.mk -C_SRC_OUTPUT ?= $(CURDIR)/priv/$(PROJECT) -C_SRC_TYPE ?= shared - -# System type and C compiler/flags. - -ifeq ($(PLATFORM),msys2) - C_SRC_OUTPUT_EXECUTABLE_EXTENSION ?= .exe - C_SRC_OUTPUT_SHARED_EXTENSION ?= .dll -else - C_SRC_OUTPUT_EXECUTABLE_EXTENSION ?= - C_SRC_OUTPUT_SHARED_EXTENSION ?= .so -endif - -ifeq ($(C_SRC_TYPE),shared) - C_SRC_OUTPUT_FILE = $(C_SRC_OUTPUT)$(C_SRC_OUTPUT_SHARED_EXTENSION) -else - C_SRC_OUTPUT_FILE = $(C_SRC_OUTPUT)$(C_SRC_OUTPUT_EXECUTABLE_EXTENSION) -endif - -ifeq ($(PLATFORM),msys2) -# We hardcode the compiler used on MSYS2. The default CC=cc does -# not produce working code. The "gcc" MSYS2 package also doesn't. - CC = /mingw64/bin/gcc - export CC - CFLAGS ?= -O3 -std=c99 -finline-functions -Wall -Wmissing-prototypes - CXXFLAGS ?= -O3 -finline-functions -Wall -else ifeq ($(PLATFORM),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 ($(PLATFORM),freebsd) - CC ?= cc - CFLAGS ?= -O3 -std=c99 -finline-functions -Wall -Wmissing-prototypes - CXXFLAGS ?= -O3 -finline-functions -Wall -else ifeq ($(PLATFORM),linux) - CC ?= gcc - CFLAGS ?= -O3 -std=c99 -finline-functions -Wall -Wmissing-prototypes - CXXFLAGS ?= -O3 -finline-functions -Wall -endif - -ifneq ($(PLATFORM),msys2) - CFLAGS += -fPIC - CXXFLAGS += -fPIC -endif - -CFLAGS += -I"$(ERTS_INCLUDE_DIR)" -I"$(ERL_INTERFACE_INCLUDE_DIR)" -CXXFLAGS += -I"$(ERTS_INCLUDE_DIR)" -I"$(ERL_INTERFACE_INCLUDE_DIR)" - -LDLIBS += -L"$(ERL_INTERFACE_LIB_DIR)" -lerl_interface -lei - -# 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:: app-c_src - -test-build:: app-c_src - -app-c_src: - $(MAKE) -C $(C_SRC_DIR) - -clean:: - $(MAKE) -C $(C_SRC_DIR) clean - -else - -ifeq ($(SOURCES),) -SOURCES := $(sort $(foreach pat,*.c *.C *.cc *.cpp,$(call core_find,$(C_SRC_DIR)/,$(pat)))) -endif -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_FILE) - -test-build:: $(C_SRC_ENV) $(C_SRC_OUTPUT_FILE) - -$(C_SRC_OUTPUT_FILE): $(OBJECTS) - $(verbose) mkdir -p priv/ - $(link_verbose) $(CC) $(OBJECTS) \ - $(LDFLAGS) $(if $(filter $(C_SRC_TYPE),shared),-shared) $(LDLIBS) \ - -o $(C_SRC_OUTPUT_FILE) - -%.o: %.c - $(COMPILE_C) $(OUTPUT_OPTION) $< - -%.o: %.cc - $(COMPILE_CPP) $(OUTPUT_OPTION) $< - -%.o: %.C - $(COMPILE_CPP) $(OUTPUT_OPTION) $< - -%.o: %.cpp - $(COMPILE_CPP) $(OUTPUT_OPTION) $< - -clean:: clean-c_src - -clean-c_src: - $(gen_verbose) rm -f $(C_SRC_OUTPUT_FILE) $(OBJECTS) - -endif - -ifneq ($(wildcard $(C_SRC_DIR)),) -$(C_SRC_ENV): - $(verbose) $(ERL) -eval "file:write_file(\"$(call core_native_path,$(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()." - -distclean:: distclean-c_src-env - -distclean-c_src-env: - $(gen_verbose) rm -f $(C_SRC_ENV) - --include $(C_SRC_ENV) -endif - -# Templates. - -define bs_c_nif -#include "erl_nif.h" - -static int loads = 0; - -static int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) -{ - /* Initialize private data. */ - *priv_data = NULL; - - loads++; - - return 0; -} - -static int upgrade(ErlNifEnv* env, void** priv_data, void** old_priv_data, ERL_NIF_TERM load_info) -{ - /* Convert the private data to the new version. */ - *priv_data = *old_priv_data; - - loads++; - - return 0; -} - -static void unload(ErlNifEnv* env, void* priv_data) -{ - if (loads == 1) { - /* Destroy the private data. */ - } - - loads--; -} - -static ERL_NIF_TERM hello(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{ - if (enif_is_atom(env, argv[0])) { - return enif_make_tuple2(env, - enif_make_atom(env, "hello"), - argv[0]); - } - - return enif_make_tuple2(env, - enif_make_atom(env, "error"), - enif_make_atom(env, "badarg")); -} - -static ErlNifFunc nif_funcs[] = { - {"hello", 1, hello} -}; - -ERL_NIF_INIT($n, nif_funcs, load, NULL, upgrade, unload) -endef - -define bs_erl_nif --module($n). - --export([hello/1]). - --on_load(on_load/0). -on_load() -> - PrivDir = case code:priv_dir(?MODULE) of - {error, _} -> - AppPath = filename:dirname(filename:dirname(code:which(?MODULE))), - filename:join(AppPath, "priv"); - Path -> - Path - end, - erlang:load_nif(filename:join(PrivDir, atom_to_list(?MODULE)), 0). - -hello(_) -> - erlang:nif_error({not_loaded, ?MODULE}). -endef - -new-nif: -ifneq ($(wildcard $(C_SRC_DIR)/$n.c),) - $(error Error: $(C_SRC_DIR)/$n.c already exists) -endif -ifneq ($(wildcard src/$n.erl),) - $(error Error: src/$n.erl already exists) -endif -ifdef in - $(verbose) $(MAKE) -C $(APPS_DIR)/$(in)/ new-nif n=$n in= -else - $(verbose) mkdir -p $(C_SRC_DIR) src/ - $(call render_template,bs_c_nif,$(C_SRC_DIR)/$n.c) - $(call render_template,bs_erl_nif,src/$n.erl) -endif - -# Copyright (c) 2015-2016, Loïc Hoguin -# This file is part of erlang.mk and subject to the terms of the ISC License. - -.PHONY: ci ci-prepare ci-setup distclean-kerl - -CI_OTP ?= -CI_HIPE ?= -CI_ERLLVM ?= - -ifeq ($(CI_VM),native) -ERLC_OPTS += +native -TEST_ERLC_OPTS += +native -else ifeq ($(CI_VM),erllvm) -ERLC_OPTS += +native +'{hipe, [to_llvm]}' -TEST_ERLC_OPTS += +native +'{hipe, [to_llvm]}' -endif - -ifeq ($(strip $(CI_OTP) $(CI_HIPE) $(CI_ERLLVM)),) -ci:: -else - -ifeq ($(strip $(KERL)),) -KERL := $(ERLANG_MK_TMP)/kerl/kerl -endif - -export KERL - -KERL_GIT ?= https://github.com/kerl/kerl -KERL_COMMIT ?= master - -KERL_MAKEFLAGS ?= - -OTP_GIT ?= https://github.com/erlang/otp - -CI_INSTALL_DIR ?= $(HOME)/erlang - -ci:: $(addprefix ci-,$(CI_OTP) $(addsuffix -native,$(CI_HIPE)) $(addsuffix -erllvm,$(CI_ERLLVM))) - -ci-prepare: $(addprefix $(CI_INSTALL_DIR)/,$(CI_OTP) $(addsuffix -native,$(CI_HIPE))) - -ci-setup:: - -ci-extra:: - -ci_verbose_0 = @echo " CI " $(1); -ci_verbose = $(ci_verbose_$(V)) - -define ci_target -ci-$1: $(CI_INSTALL_DIR)/$2 - $(verbose) $(MAKE) --no-print-directory clean - $(ci_verbose) \ - PATH="$(CI_INSTALL_DIR)/$2/bin:$(PATH)" \ - CI_OTP_RELEASE="$1" \ - CT_OPTS="-label $1" \ - CI_VM="$3" \ - $(MAKE) ci-setup tests - $(verbose) $(MAKE) --no-print-directory ci-extra -endef - -$(foreach otp,$(CI_OTP),$(eval $(call ci_target,$(otp),$(otp),otp))) -$(foreach otp,$(CI_HIPE),$(eval $(call ci_target,$(otp)-native,$(otp)-native,native))) -$(foreach otp,$(CI_ERLLVM),$(eval $(call ci_target,$(otp)-erllvm,$(otp)-native,erllvm))) - -define ci_otp_target -ifeq ($(wildcard $(CI_INSTALL_DIR)/$(1)),) -$(CI_INSTALL_DIR)/$(1): $(KERL) - MAKEFLAGS="$(KERL_MAKEFLAGS)" $(KERL) build git $(OTP_GIT) $(1) $(1) - $(KERL) install $(1) $(CI_INSTALL_DIR)/$(1) -endif -endef - -$(foreach otp,$(CI_OTP),$(eval $(call ci_otp_target,$(otp)))) - -define ci_hipe_target -ifeq ($(wildcard $(CI_INSTALL_DIR)/$1-native),) -$(CI_INSTALL_DIR)/$1-native: $(KERL) - KERL_CONFIGURE_OPTIONS=--enable-native-libs \ - MAKEFLAGS="$(KERL_MAKEFLAGS)" $(KERL) build git $(OTP_GIT) $1 $1-native - $(KERL) install $1-native $(CI_INSTALL_DIR)/$1-native -endif -endef - -$(foreach otp,$(sort $(CI_HIPE) $(CI_ERLLLVM)),$(eval $(call ci_hipe_target,$(otp)))) - -$(KERL): - $(verbose) mkdir -p $(ERLANG_MK_TMP) - $(gen_verbose) git clone --depth 1 $(KERL_GIT) $(ERLANG_MK_TMP)/kerl - $(verbose) cd $(ERLANG_MK_TMP)/kerl && git checkout $(KERL_COMMIT) - $(verbose) chmod +x $(KERL) - -help:: - $(verbose) printf "%s\n" "" \ - "Continuous Integration targets:" \ - " ci Run '$(MAKE) tests' on all configured Erlang versions." \ - "" \ - "The CI_OTP variable must be defined with the Erlang versions" \ - "that must be tested. For example: CI_OTP = OTP-17.3.4 OTP-17.5.3" - -distclean:: distclean-kerl - -distclean-kerl: - $(gen_verbose) rm -rf $(KERL) -endif - -# Copyright (c) 2013-2016, Loïc Hoguin -# This file is part of erlang.mk and subject to the terms of the ISC License. - -.PHONY: ct apps-ct distclean-ct - -# Configuration. - -CT_OPTS ?= - -ifneq ($(wildcard $(TEST_DIR)),) -ifndef CT_SUITES -CT_SUITES := $(sort $(subst _SUITE.erl,,$(notdir $(call core_find,$(TEST_DIR)/,*_SUITE.erl)))) -endif -endif -CT_SUITES ?= -CT_LOGS_DIR ?= $(CURDIR)/logs - -# Core targets. - -tests:: ct - -distclean:: distclean-ct - -help:: - $(verbose) 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. - -CT_RUN = ct_run \ - -no_auto_compile \ - -noinput \ - -pa $(CURDIR)/ebin $(DEPS_DIR)/*/ebin $(APPS_DIR)/*/ebin $(TEST_DIR) \ - -dir $(TEST_DIR) \ - -logdir $(CT_LOGS_DIR) - -ifeq ($(CT_SUITES),) -ct: $(if $(IS_APP),,apps-ct) -else -ct: test-build $(if $(IS_APP),,apps-ct) - $(verbose) mkdir -p $(CT_LOGS_DIR) - $(gen_verbose) $(CT_RUN) -sname ct_$(PROJECT) -suite $(addsuffix _SUITE,$(CT_SUITES)) $(CT_OPTS) -endif - -ifneq ($(ALL_APPS_DIRS),) -define ct_app_target -apps-ct-$1: - $(MAKE) -C $1 ct IS_APP=1 -endef - -$(foreach app,$(ALL_APPS_DIRS),$(eval $(call ct_app_target,$(app)))) - -apps-ct: test-build $(addprefix apps-ct-,$(ALL_APPS_DIRS)) -endif - -ifndef t -CT_EXTRA = -else -ifeq (,$(findstring :,$t)) -CT_EXTRA = -group $t -else -t_words = $(subst :, ,$t) -CT_EXTRA = -group $(firstword $(t_words)) -case $(lastword $(t_words)) -endif -endif - -define ct_suite_target -ct-$(1): test-build - $(verbose) mkdir -p $(CT_LOGS_DIR) - $(gen_verbose) $(CT_RUN) -sname ct_$(PROJECT) -suite $(addsuffix _SUITE,$(1)) $(CT_EXTRA) $(CT_OPTS) -endef - -$(foreach test,$(CT_SUITES),$(eval $(call ct_suite_target,$(test)))) - -distclean-ct: - $(gen_verbose) rm -rf $(CT_LOGS_DIR) - -# Copyright (c) 2013-2016, 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_DIRS ?= --src -r $(wildcard src) $(ALL_APPS_DIRS) -DIALYZER_OPTS ?= -Werror_handling -Wrace_conditions -Wunmatched_returns # -Wunderspecs - -# Core targets. - -check:: dialyze - -distclean:: distclean-plt - -help:: - $(verbose) printf "%s\n" "" \ - "Dialyzer targets:" \ - " plt Build a PLT file for this project" \ - " dialyze Analyze the project using Dialyzer" - -# Plugin-specific targets. - -define filter_opts.erl - Opts = init:get_plain_arguments(), - {Filtered, _} = lists:foldl(fun - (O, {Os, true}) -> {[O|Os], false}; - (O = "-D", {Os, _}) -> {[O|Os], true}; - (O = [\\$$-, \\$$D, _ | _], {Os, _}) -> {[O|Os], false}; - (O = "-I", {Os, _}) -> {[O|Os], true}; - (O = [\\$$-, \\$$I, _ | _], {Os, _}) -> {[O|Os], false}; - (O = "-pa", {Os, _}) -> {[O|Os], true}; - (_, Acc) -> Acc - end, {[], false}, Opts), - io:format("~s~n", [string:join(lists:reverse(Filtered), " ")]), - halt(). -endef - -$(DIALYZER_PLT): deps app - $(eval DEPS_LOG := $(shell test -f $(ERLANG_MK_TMP)/deps.log && \ - while read p; do test -d $$p/ebin && echo $$p/ebin; done <$(ERLANG_MK_TMP)/deps.log)) - $(verbose) dialyzer --build_plt --apps erts kernel stdlib \ - $(PLT_APPS) $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS_LOG) - -plt: $(DIALYZER_PLT) - -distclean-plt: - $(gen_verbose) rm -f $(DIALYZER_PLT) - -ifneq ($(wildcard $(DIALYZER_PLT)),) -dialyze: -else -dialyze: $(DIALYZER_PLT) -endif - $(verbose) dialyzer --no_native `$(ERL) -eval "$(subst $(newline),,$(subst ",\",$(call filter_opts.erl)))" -extra $(ERLC_OPTS)` $(DIALYZER_DIRS) $(DIALYZER_OPTS) - -# Copyright (c) 2013-2016, Loïc Hoguin -# This file is part of erlang.mk and subject to the terms of the ISC License. - -.PHONY: distclean-edoc edoc - -# Configuration. - -EDOC_OPTS ?= - -# Core targets. - -ifneq ($(wildcard doc/overview.edoc),) -docs:: edoc -endif - -distclean:: distclean-edoc - -# Plugin-specific targets. - -edoc: distclean-edoc doc-deps - $(gen_verbose) $(ERL) -eval 'edoc:application($(PROJECT), ".", [$(EDOC_OPTS)]), halt().' - -distclean-edoc: - $(gen_verbose) rm -f doc/*.css doc/*.html doc/*.png doc/edoc-info - -# Copyright (c) 2013-2016, Loïc Hoguin -# This file is part of erlang.mk and subject to the terms of the ISC License. - -# Configuration. - -DTL_FULL_PATH ?= -DTL_PATH ?= templates/ -DTL_SUFFIX ?= _dtl -DTL_OPTS ?= - -# Verbosity. - -dtl_verbose_0 = @echo " DTL " $(filter %.dtl,$(?F)); -dtl_verbose = $(dtl_verbose_$(V)) - -# Core targets. - -DTL_PATH := $(abspath $(DTL_PATH)) -DTL_FILES := $(sort $(call core_find,$(DTL_PATH),*.dtl)) - -ifneq ($(DTL_FILES),) - -DTL_NAMES = $(addsuffix $(DTL_SUFFIX),$(DTL_FILES:$(DTL_PATH)/%.dtl=%)) -DTL_MODULES = $(if $(DTL_FULL_PATH),$(subst /,_,$(DTL_NAMES)),$(notdir $(DTL_NAMES))) -BEAM_FILES += $(addsuffix .beam,$(addprefix ebin/,$(DTL_MODULES))) - -ifneq ($(words $(DTL_FILES)),0) -# Rebuild templates when the Makefile changes. -$(ERLANG_MK_TMP)/last-makefile-change-erlydtl: $(MAKEFILE_LIST) - @mkdir -p $(ERLANG_MK_TMP) - @if test -f $@; then \ - touch $(DTL_FILES); \ - fi - @touch $@ - -ebin/$(PROJECT).app:: $(ERLANG_MK_TMP)/last-makefile-change-erlydtl -endif - -define erlydtl_compile.erl - [begin - Module0 = case "$(strip $(DTL_FULL_PATH))" of - "" -> - filename:basename(F, ".dtl"); - _ -> - "$(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, [$(DTL_OPTS)] ++ [{out_dir, "ebin/"}, return_errors]) of - ok -> ok; - {ok, _} -> ok - end - end || F <- string:tokens("$(1)", " ")], - halt(). -endef - -ebin/$(PROJECT).app:: $(DTL_FILES) | ebin/ - $(if $(strip $?),\ - $(dtl_verbose) $(call erlang,$(call erlydtl_compile.erl,$(call core_native_path,$?)),\ - -pa ebin/ $(DEPS_DIR)/erlydtl/ebin/)) - -endif - -# Copyright (c) 2016, Loïc Hoguin -# Copyright (c) 2014, Dave Cottlehuber -# This file is part of erlang.mk and subject to the terms of the ISC License. - -.PHONY: distclean-escript escript escript-zip - -# Configuration. - -ESCRIPT_NAME ?= $(PROJECT) -ESCRIPT_FILE ?= $(ESCRIPT_NAME) - -ESCRIPT_SHEBANG ?= /usr/bin/env escript -ESCRIPT_COMMENT ?= This is an -*- erlang -*- file -ESCRIPT_EMU_ARGS ?= -escript main $(ESCRIPT_NAME) - -ESCRIPT_ZIP ?= 7z a -tzip -mx=9 -mtc=off $(if $(filter-out 0,$(V)),,> /dev/null) -ESCRIPT_ZIP_FILE ?= $(ERLANG_MK_TMP)/escript.zip - -# Core targets. - -distclean:: distclean-escript - -help:: - $(verbose) printf "%s\n" "" \ - "Escript targets:" \ - " escript Build an executable escript archive" \ - -# Plugin-specific targets. - -escript-zip:: deps app - $(verbose) mkdir -p $(dir $(ESCRIPT_ZIP)) - $(verbose) rm -f $(ESCRIPT_ZIP_FILE) - $(gen_verbose) cd .. && $(ESCRIPT_ZIP) $(ESCRIPT_ZIP_FILE) $(PROJECT)/ebin/* -ifneq ($(DEPS),) - $(verbose) cd $(DEPS_DIR) && $(ESCRIPT_ZIP) $(ESCRIPT_ZIP_FILE) \ - `cat $(ERLANG_MK_TMP)/deps.log | sed 's/^$(subst /,\/,$(DEPS_DIR))\///' | sed 's/$$/\/ebin\/\*/'` -endif - -escript:: escript-zip - $(gen_verbose) printf "%s\n" \ - "#!$(ESCRIPT_SHEBANG)" \ - "%% $(ESCRIPT_COMMENT)" \ - "%%! $(ESCRIPT_EMU_ARGS)" > $(ESCRIPT_FILE) - $(verbose) cat $(ESCRIPT_ZIP_FILE) >> $(ESCRIPT_FILE) - $(verbose) chmod +x $(ESCRIPT_FILE) - -distclean-escript: - $(gen_verbose) rm -f $(ESCRIPT_FILE) - -# Copyright (c) 2015-2016, Loïc Hoguin -# Copyright (c) 2014, Enrique Fernandez -# This file is contributed to erlang.mk and subject to the terms of the ISC License. - -.PHONY: eunit apps-eunit - -# Configuration - -EUNIT_OPTS ?= -EUNIT_ERL_OPTS ?= - -# Core targets. - -tests:: eunit - -help:: - $(verbose) printf "%s\n" "" \ - "EUnit targets:" \ - " eunit Run all the EUnit tests for this project" - -# Plugin-specific targets. - -define eunit.erl - case "$(COVER)" of - "" -> ok; - _ -> - case cover:compile_beam_directory("ebin") of - {error, _} -> halt(1); - _ -> ok - end - end, - case eunit:test($1, [$(EUNIT_OPTS)]) of - ok -> ok; - error -> halt(2) - end, - case "$(COVER)" of - "" -> ok; - _ -> - cover:export("eunit.coverdata") - end, - halt() -endef - -EUNIT_ERL_OPTS += -pa $(TEST_DIR) $(DEPS_DIR)/*/ebin $(APPS_DIR)/*/ebin $(CURDIR)/ebin - -ifdef t -ifeq (,$(findstring :,$(t))) -eunit: test-build - $(gen_verbose) $(call erlang,$(call eunit.erl,['$(t)']),$(EUNIT_ERL_OPTS)) -else -eunit: test-build - $(gen_verbose) $(call erlang,$(call eunit.erl,fun $(t)/0),$(EUNIT_ERL_OPTS)) -endif -else -EUNIT_EBIN_MODS = $(notdir $(basename $(ERL_FILES) $(BEAM_FILES))) -EUNIT_TEST_MODS = $(notdir $(basename $(call core_find,$(TEST_DIR)/,*.erl))) - -EUNIT_MODS = $(foreach mod,$(EUNIT_EBIN_MODS) $(filter-out \ - $(patsubst %,%_tests,$(EUNIT_EBIN_MODS)),$(EUNIT_TEST_MODS)),'$(mod)') - -eunit: test-build $(if $(IS_APP),,apps-eunit) - $(gen_verbose) $(call erlang,$(call eunit.erl,[$(call comma_list,$(EUNIT_MODS))]),$(EUNIT_ERL_OPTS)) - -ifneq ($(ALL_APPS_DIRS),) -apps-eunit: - $(verbose) eunit_retcode=0 ; for app in $(ALL_APPS_DIRS); do $(MAKE) -C $$app eunit IS_APP=1; \ - [ $$? -ne 0 ] && eunit_retcode=1 ; done ; \ - exit $$eunit_retcode -endif -endif - -# Copyright (c) 2013-2016, Loïc Hoguin -# This file is part of erlang.mk and subject to the terms of the ISC License. - -.PHONY: relx-rel relx-relup distclean-relx-rel run - -# Configuration. - -RELX ?= $(ERLANG_MK_TMP)/relx -RELX_CONFIG ?= $(CURDIR)/relx.config - -RELX_URL ?= https://github.com/erlware/relx/releases/download/v3.19.0/relx -RELX_OPTS ?= -RELX_OUTPUT_DIR ?= _rel -RELX_TAR ?= 1 - -ifdef SFX - RELX_TAR = 1 -endif - -ifeq ($(firstword $(RELX_OPTS)),-o) - RELX_OUTPUT_DIR = $(word 2,$(RELX_OPTS)) -else - RELX_OPTS += -o $(RELX_OUTPUT_DIR) -endif - -# Core targets. - -ifeq ($(IS_DEP),) -ifneq ($(wildcard $(RELX_CONFIG)),) -rel:: relx-rel - -relup:: relx-relup -endif -endif - -distclean:: distclean-relx-rel - -# Plugin-specific targets. - -$(RELX): - $(gen_verbose) $(call core_http_get,$(RELX),$(RELX_URL)) - $(verbose) chmod +x $(RELX) - -relx-rel: $(RELX) rel-deps app - $(verbose) $(RELX) -c $(RELX_CONFIG) $(RELX_OPTS) release $(if $(filter 1,$(RELX_TAR)),tar) - -relx-relup: $(RELX) rel-deps app - $(verbose) $(RELX) -c $(RELX_CONFIG) $(RELX_OPTS) release relup $(if $(filter 1,$(RELX_TAR)),tar) - -distclean-relx-rel: - $(gen_verbose) rm -rf $(RELX_OUTPUT_DIR) - -# Run target. - -ifeq ($(wildcard $(RELX_CONFIG)),) -run: -else - -define get_relx_release.erl - {ok, Config} = file:consult("$(call core_native_path,$(RELX_CONFIG))"), - {release, {Name, Vsn0}, _} = lists:keyfind(release, 1, Config), - Vsn = case Vsn0 of - {cmd, Cmd} -> os:cmd(Cmd); - semver -> ""; - {semver, _} -> ""; - VsnStr -> Vsn0 - end, - io:format("~s ~s", [Name, Vsn]), - halt(0). -endef - -RELX_REL := $(shell $(call erlang,$(get_relx_release.erl))) -RELX_REL_NAME := $(word 1,$(RELX_REL)) -RELX_REL_VSN := $(word 2,$(RELX_REL)) - -run: all - $(verbose) $(RELX_OUTPUT_DIR)/$(RELX_REL_NAME)/bin/$(RELX_REL_NAME) console - -help:: - $(verbose) printf "%s\n" "" \ - "Relx targets:" \ - " run Compile the project, build the release and run it" - -endif - -# Copyright (c) 2015-2016, Loïc Hoguin -# Copyright (c) 2014, M Robert Martin -# This file is contributed to erlang.mk and subject to the terms of the ISC License. - -.PHONY: shell - -# Configuration. - -SHELL_ERL ?= erl -SHELL_PATHS ?= $(CURDIR)/ebin $(APPS_DIR)/*/ebin $(DEPS_DIR)/*/ebin -SHELL_OPTS ?= - -ALL_SHELL_DEPS_DIRS = $(addprefix $(DEPS_DIR)/,$(SHELL_DEPS)) - -# Core targets - -help:: - $(verbose) printf "%s\n" "" \ - "Shell targets:" \ - " shell Run an erlang shell with SHELL_OPTS or reasonable default" - -# Plugin-specific targets. - -$(foreach dep,$(SHELL_DEPS),$(eval $(call dep_target,$(dep)))) - -build-shell-deps: $(ALL_SHELL_DEPS_DIRS) - $(verbose) for dep in $(ALL_SHELL_DEPS_DIRS) ; do $(MAKE) -C $$dep ; done - -shell: build-shell-deps - $(gen_verbose) $(SHELL_ERL) -pa $(SHELL_PATHS) $(SHELL_OPTS) - -# Copyright (c) 2017, Jean-Sébastien Pédron -# This file is contributed to erlang.mk and subject to the terms of the ISC License. - -.PHONY: show-ERL_LIBS show-ERLC_OPTS show-TEST_ERLC_OPTS - -show-ERL_LIBS: - @echo $(ERL_LIBS) - -show-ERLC_OPTS: - @$(foreach opt,$(ERLC_OPTS) -pa ebin -I include,echo "$(opt)";) - -show-TEST_ERLC_OPTS: - @$(foreach opt,$(TEST_ERLC_OPTS) -pa ebin -I include,echo "$(opt)";) - -# Copyright (c) 2015-2016, Loïc Hoguin -# This file is part of erlang.mk and subject to the terms of the ISC License. - -ifeq ($(filter triq,$(DEPS) $(TEST_DEPS)),triq) -.PHONY: triq - -# Targets. - -tests:: triq - -define triq_check.erl - code:add_pathsa(["$(call core_native_path,$(CURDIR)/ebin)", "$(call core_native_path,$(DEPS_DIR)/*/ebin)"]), - try - case $(1) of - all -> [true] =:= lists:usort([triq:check(M) || M <- [$(call comma_list,$(3))]]); - module -> triq:check($(2)); - function -> triq:check($(2)) - end - of - true -> halt(0); - _ -> halt(1) - catch error:undef -> - io:format("Undefined property or module~n"), - halt(0) - end. -endef - -ifdef t -ifeq (,$(findstring :,$(t))) -triq: test-build - $(verbose) $(call erlang,$(call triq_check.erl,module,$(t))) -else -triq: test-build - $(verbose) echo Testing $(t)/0 - $(verbose) $(call erlang,$(call triq_check.erl,function,$(t)())) -endif -else -triq: test-build - $(eval MODULES := $(patsubst %,'%',$(sort $(notdir $(basename $(wildcard ebin/*.beam)))))) - $(gen_verbose) $(call erlang,$(call triq_check.erl,all,undefined,$(MODULES))) -endif -endif - -# Copyright (c) 2016, Loïc Hoguin -# Copyright (c) 2015, Erlang Solutions Ltd. -# This file is part of erlang.mk and subject to the terms of the ISC License. - -.PHONY: xref distclean-xref - -# Configuration. - -ifeq ($(XREF_CONFIG),) - XREFR_ARGS := -else - XREFR_ARGS := -c $(XREF_CONFIG) -endif - -XREFR ?= $(CURDIR)/xrefr -export XREFR - -XREFR_URL ?= https://github.com/inaka/xref_runner/releases/download/0.2.2/xrefr - -# Core targets. - -help:: - $(verbose) printf "%s\n" "" \ - "Xref targets:" \ - " xref Run Xrefr using $XREF_CONFIG as config file if defined" - -distclean:: distclean-xref - -# Plugin-specific targets. - -$(XREFR): - $(gen_verbose) $(call core_http_get,$(XREFR),$(XREFR_URL)) - $(verbose) chmod +x $(XREFR) - -xref: deps app $(XREFR) - $(gen_verbose) $(XREFR) $(XREFR_ARGS) - -distclean-xref: - $(gen_verbose) rm -rf $(XREFR) - -# Copyright (c) 2016, Loïc Hoguin -# Copyright (c) 2015, Viktor Söderqvist -# This file is part of erlang.mk and subject to the terms of the ISC License. - -COVER_REPORT_DIR = cover - -# Hook in coverage to ct - -ifdef COVER -ifdef CT_RUN -# All modules in 'ebin' -COVER_MODS = $(notdir $(basename $(call core_ls,ebin/*.beam))) - -test-build:: $(TEST_DIR)/ct.cover.spec - -$(TEST_DIR)/ct.cover.spec: - $(verbose) echo Cover mods: $(COVER_MODS) - $(gen_verbose) printf "%s\n" \ - '{incl_mods,[$(subst $(space),$(comma),$(COVER_MODS))]}.' \ - '{export,"$(CURDIR)/ct.coverdata"}.' > $@ - -CT_RUN += -cover $(TEST_DIR)/ct.cover.spec -endif -endif - -# Core targets - -ifdef COVER -ifneq ($(COVER_REPORT_DIR),) -tests:: - $(verbose) $(MAKE) --no-print-directory cover-report -endif -endif - -clean:: coverdata-clean - -ifneq ($(COVER_REPORT_DIR),) -distclean:: cover-report-clean -endif - -help:: - $(verbose) printf "%s\n" "" \ - "Cover targets:" \ - " cover-report Generate a HTML coverage report from previously collected" \ - " cover data." \ - " all.coverdata Merge {eunit,ct}.coverdata into one coverdata file." \ - "" \ - "If COVER=1 is set, coverage data is generated by the targets eunit and ct. The" \ - "target tests additionally generates a HTML coverage report from the combined" \ - "coverdata files from each of these testing tools. HTML reports can be disabled" \ - "by setting COVER_REPORT_DIR to empty." - -# Plugin specific targets - -COVERDATA = $(filter-out all.coverdata,$(wildcard *.coverdata)) - -.PHONY: coverdata-clean -coverdata-clean: - $(gen_verbose) rm -f *.coverdata ct.cover.spec - -# Merge all coverdata files into one. -all.coverdata: $(COVERDATA) - $(gen_verbose) $(ERL) -eval ' \ - $(foreach f,$(COVERDATA),cover:import("$(f)") == ok orelse halt(1),) \ - cover:export("$@"), halt(0).' - -# These are only defined if COVER_REPORT_DIR is non-empty. Set COVER_REPORT_DIR to -# empty if you want the coverdata files but not the HTML report. -ifneq ($(COVER_REPORT_DIR),) - -.PHONY: cover-report-clean cover-report - -cover-report-clean: - $(gen_verbose) rm -rf $(COVER_REPORT_DIR) - -ifeq ($(COVERDATA),) -cover-report: -else - -# Modules which include eunit.hrl always contain one line without coverage -# because eunit defines test/0 which is never called. We compensate for this. -EUNIT_HRL_MODS = $(subst $(space),$(comma),$(shell \ - grep -e '^\s*-include.*include/eunit\.hrl"' src/*.erl \ - | sed "s/^src\/\(.*\)\.erl:.*/'\1'/" | uniq)) - -define cover_report.erl - $(foreach f,$(COVERDATA),cover:import("$(f)") == ok orelse halt(1),) - Ms = cover:imported_modules(), - [cover:analyse_to_file(M, "$(COVER_REPORT_DIR)/" ++ atom_to_list(M) - ++ ".COVER.html", [html]) || M <- Ms], - Report = [begin {ok, R} = cover:analyse(M, module), R end || M <- Ms], - EunitHrlMods = [$(EUNIT_HRL_MODS)], - Report1 = [{M, {Y, case lists:member(M, EunitHrlMods) of - true -> N - 1; false -> N end}} || {M, {Y, N}} <- Report], - TotalY = lists:sum([Y || {_, {Y, _}} <- Report1]), - TotalN = lists:sum([N || {_, {_, N}} <- Report1]), - Perc = fun(Y, N) -> case Y + N of 0 -> 100; S -> round(100 * Y / S) end end, - TotalPerc = Perc(TotalY, TotalN), - {ok, F} = file:open("$(COVER_REPORT_DIR)/index.html", [write]), - io:format(F, "~n" - "~n" - "Coverage report~n" - "~n", []), - io:format(F, "

Coverage

~n

Total: ~p%

~n", [TotalPerc]), - io:format(F, "~n", []), - [io:format(F, "" - "~n", - [M, M, Perc(Y, N)]) || {M, {Y, N}} <- Report1], - How = "$(subst $(space),$(comma)$(space),$(basename $(COVERDATA)))", - Date = "$(shell date -u "+%Y-%m-%dT%H:%M:%SZ")", - io:format(F, "
ModuleCoverage
~p~p%
~n" - "

Generated using ~s and erlang.mk on ~s.

~n" - "", [How, Date]), - halt(). -endef - -cover-report: - $(gen_verbose) mkdir -p $(COVER_REPORT_DIR) - $(gen_verbose) $(call erlang,$(cover_report.erl)) - -endif -endif # ifneq ($(COVER_REPORT_DIR),) - -# Copyright (c) 2016, Loïc Hoguin -# This file is part of erlang.mk and subject to the terms of the ISC License. - -.PHONY: sfx - -ifdef RELX_REL -ifdef SFX - -# Configuration. - -SFX_ARCHIVE ?= $(RELX_OUTPUT_DIR)/$(RELX_REL_NAME)/$(RELX_REL_NAME)-$(RELX_REL_VSN).tar.gz -SFX_OUTPUT_FILE ?= $(RELX_OUTPUT_DIR)/$(RELX_REL_NAME).run - -# Core targets. - -rel:: sfx - -# Plugin-specific targets. - -define sfx_stub -#!/bin/sh - -TMPDIR=`mktemp -d` -ARCHIVE=`awk '/^__ARCHIVE_BELOW__$$/ {print NR + 1; exit 0;}' $$0` -FILENAME=$$(basename $$0) -REL=$${FILENAME%.*} - -tail -n+$$ARCHIVE $$0 | tar -xzf - -C $$TMPDIR - -$$TMPDIR/bin/$$REL console -RET=$$? - -rm -rf $$TMPDIR - -exit $$RET - -__ARCHIVE_BELOW__ -endef - -sfx: - $(call render_template,sfx_stub,$(SFX_OUTPUT_FILE)) - $(gen_verbose) cat $(SFX_ARCHIVE) >> $(SFX_OUTPUT_FILE) - $(verbose) chmod +x $(SFX_OUTPUT_FILE) - -endif -endif - -# Copyright (c) 2013-2017, Loïc Hoguin -# This file is part of erlang.mk and subject to the terms of the ISC License. - -# 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)))) - -# Copyright (c) 2013-2015, Loïc Hoguin -# Copyright (c) 2015-2016, Jean-Sébastien Pédron -# This file is part of erlang.mk and subject to the terms of the ISC License. - -# Fetch dependencies recursively (without building them). - -.PHONY: fetch-deps fetch-doc-deps fetch-rel-deps fetch-test-deps \ - fetch-shell-deps - -.PHONY: $(ERLANG_MK_RECURSIVE_DEPS_LIST) \ - $(ERLANG_MK_RECURSIVE_DOC_DEPS_LIST) \ - $(ERLANG_MK_RECURSIVE_REL_DEPS_LIST) \ - $(ERLANG_MK_RECURSIVE_TEST_DEPS_LIST) \ - $(ERLANG_MK_RECURSIVE_SHELL_DEPS_LIST) - -fetch-deps: $(ERLANG_MK_RECURSIVE_DEPS_LIST) -fetch-doc-deps: $(ERLANG_MK_RECURSIVE_DOC_DEPS_LIST) -fetch-rel-deps: $(ERLANG_MK_RECURSIVE_REL_DEPS_LIST) -fetch-test-deps: $(ERLANG_MK_RECURSIVE_TEST_DEPS_LIST) -fetch-shell-deps: $(ERLANG_MK_RECURSIVE_SHELL_DEPS_LIST) - -ifneq ($(SKIP_DEPS),) -$(ERLANG_MK_RECURSIVE_DEPS_LIST) \ -$(ERLANG_MK_RECURSIVE_DOC_DEPS_LIST) \ -$(ERLANG_MK_RECURSIVE_REL_DEPS_LIST) \ -$(ERLANG_MK_RECURSIVE_TEST_DEPS_LIST) \ -$(ERLANG_MK_RECURSIVE_SHELL_DEPS_LIST): - $(verbose) :> $@ -else -# By default, we fetch "normal" dependencies. They are also included no -# matter the type of requested dependencies. -# -# $(ALL_DEPS_DIRS) includes $(BUILD_DEPS). - -$(ERLANG_MK_RECURSIVE_DEPS_LIST): $(ALL_DEPS_DIRS) -$(ERLANG_MK_RECURSIVE_DOC_DEPS_LIST): $(ALL_DEPS_DIRS) $(ALL_DOC_DEPS_DIRS) -$(ERLANG_MK_RECURSIVE_REL_DEPS_LIST): $(ALL_DEPS_DIRS) $(ALL_REL_DEPS_DIRS) -$(ERLANG_MK_RECURSIVE_TEST_DEPS_LIST): $(ALL_DEPS_DIRS) $(ALL_TEST_DEPS_DIRS) -$(ERLANG_MK_RECURSIVE_SHELL_DEPS_LIST): $(ALL_DEPS_DIRS) $(ALL_SHELL_DEPS_DIRS) - -# Allow to use fetch-deps and $(DEP_TYPES) to fetch multiple types of -# dependencies with a single target. -ifneq ($(filter doc,$(DEP_TYPES)),) -$(ERLANG_MK_RECURSIVE_DEPS_LIST): $(ALL_DOC_DEPS_DIRS) -endif -ifneq ($(filter rel,$(DEP_TYPES)),) -$(ERLANG_MK_RECURSIVE_DEPS_LIST): $(ALL_REL_DEPS_DIRS) -endif -ifneq ($(filter test,$(DEP_TYPES)),) -$(ERLANG_MK_RECURSIVE_DEPS_LIST): $(ALL_TEST_DEPS_DIRS) -endif -ifneq ($(filter shell,$(DEP_TYPES)),) -$(ERLANG_MK_RECURSIVE_DEPS_LIST): $(ALL_SHELL_DEPS_DIRS) -endif - -ERLANG_MK_RECURSIVE_TMP_LIST := $(abspath $(ERLANG_MK_TMP)/recursive-tmp-deps.log) - -$(ERLANG_MK_RECURSIVE_DEPS_LIST) \ -$(ERLANG_MK_RECURSIVE_DOC_DEPS_LIST) \ -$(ERLANG_MK_RECURSIVE_REL_DEPS_LIST) \ -$(ERLANG_MK_RECURSIVE_TEST_DEPS_LIST) \ -$(ERLANG_MK_RECURSIVE_SHELL_DEPS_LIST): -ifeq ($(IS_APP)$(IS_DEP),) - $(verbose) mkdir -p $(ERLANG_MK_TMP) - $(verbose) rm -f $(ERLANG_MK_RECURSIVE_TMP_LIST) -endif -ifndef IS_APP - $(verbose) for dep in $(ALL_APPS_DIRS) ; do \ - $(MAKE) -C $$dep $@ \ - IS_APP=1 \ - ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST) \ - || exit $$?; \ - done -endif - $(verbose) for dep in $^ ; do \ - if ! grep -qs ^$$dep$$ $(ERLANG_MK_RECURSIVE_TMP_LIST); then \ - echo $$dep >> $(ERLANG_MK_RECURSIVE_TMP_LIST); \ - if grep -qs -E "^[[:blank:]]*include[[:blank:]]+(erlang\.mk|.*/erlang\.mk)$$" \ - $$dep/GNUmakefile $$dep/makefile $$dep/Makefile; then \ - $(MAKE) -C $$dep fetch-deps \ - IS_DEP=1 \ - ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST) \ - || exit $$?; \ - fi \ - fi \ - done -ifeq ($(IS_APP)$(IS_DEP),) - $(verbose) sort < $(ERLANG_MK_RECURSIVE_TMP_LIST) | uniq > $@ - $(verbose) rm $(ERLANG_MK_RECURSIVE_TMP_LIST) -endif -endif # ifneq ($(SKIP_DEPS),) - -# List dependencies recursively. - -.PHONY: list-deps list-doc-deps list-rel-deps list-test-deps \ - list-shell-deps - -list-deps: $(ERLANG_MK_RECURSIVE_DEPS_LIST) -list-doc-deps: $(ERLANG_MK_RECURSIVE_DOC_DEPS_LIST) -list-rel-deps: $(ERLANG_MK_RECURSIVE_REL_DEPS_LIST) -list-test-deps: $(ERLANG_MK_RECURSIVE_TEST_DEPS_LIST) -list-shell-deps: $(ERLANG_MK_RECURSIVE_SHELL_DEPS_LIST) - -list-deps list-doc-deps list-rel-deps list-test-deps list-shell-deps: - $(verbose) cat $^ diff --git a/lz4.d b/lz4.d deleted file mode 100644 index 004e985..0000000 --- a/lz4.d +++ /dev/null @@ -1,2 +0,0 @@ - -COMPILE_FIRST += diff --git a/priv/lz4_nif.so b/priv/lz4_nif.so deleted file mode 100755 index 08973fadd5667f0c81cc931b3c6883a24c9d9bdb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 104132 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 U+}yEslMvz?UV$s$1jd>E14|u diff --git a/rebar.config b/rebar.config index 77e464a..c849ed0 100644 --- a/rebar.config +++ b/rebar.config @@ -9,10 +9,3 @@ {erl_opts, [debug_info, {parse_transform, lager_transform}, {parse_transform, eqc_cover}]}, {plugins, [rebar_eqc]}]} ]}. - -{pre_hooks, [ - {"(linux|darwin|solaris|win32)", clean, "make clean"}, - {"(freebsd|netbsd|openbsd)", clean, "gmake clean"}, - {"(linux|darwin|solaris|win32)", compile, "make"}, - {"(freebsd|netbsd|openbsd)", compile, "gmake"} -]}. \ No newline at end of file diff --git a/src/leveled_cdb.erl b/src/leveled_cdb.erl index 2e286ae..73959b1 100644 --- a/src/leveled_cdb.erl +++ b/src/leveled_cdb.erl @@ -1385,13 +1385,14 @@ perform_write_hash_tables(Handle, HashTreeBin, StartPos) -> %% The List passed in should be made up of {Index, Position, Count} tuples write_top_index_table(Handle, BasePos, IndexList) -> FnWriteIndex = fun({_Index, Pos, Count}, {AccBin, CurrPos}) -> - {PosLE, NextPos} = - case Count == 0 of - true -> - {endian_flip(CurrPos), CurrPos}; - false -> - {endian_flip(Pos), Pos + (Count * ?DWORD_SIZE)} - end, + case Count == 0 of + true -> + PosLE = endian_flip(CurrPos), + NextPos = CurrPos; + false -> + PosLE = endian_flip(Pos), + NextPos = Pos + (Count * ?DWORD_SIZE) + end, CountLE = endian_flip(Count), {<>, NextPos} end, diff --git a/src/lz4_nif.erl b/src/lz4_nif.erl deleted file mode 100644 index c783cd1..0000000 --- a/src/lz4_nif.erl +++ /dev/null @@ -1,64 +0,0 @@ -%% Copyright (c) 2017-Present Pivotal Software, Inc. All rights reserved. -%% -%% This package, the LZ4 binding for Erlang, is double-licensed under the Mozilla -%% Public License 1.1 ("MPL") and the Apache License version 2 -%% ("ASL"). For the MPL, please see LICENSE-MPL-RabbitMQ. For the ASL, -%% please see LICENSE-APACHE2. -%% -%% This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, -%% either express or implied. See the LICENSE file for specific language governing -%% rights and limitations of this software. -%% -%% If you have any questions regarding licensing, please contact us at -%% info@rabbitmq.com. - --module(lz4_nif). - -%% lz4f. --export([lz4f_compress_frame/2]). --export([lz4f_create_compression_context/0]). --export([lz4f_compress_begin/2]). --export([lz4f_compress_update/2]). --export([lz4f_flush/1]). --export([lz4f_compress_end/1]). --export([lz4f_create_decompression_context/0]). --export([lz4f_get_frame_info/2]). --export([lz4f_decompress/2]). - --on_load(on_load/0). -on_load() -> - case code:priv_dir(leveled) of - {error, _} -> - {error, {load_failed, "Could not determine the leveled priv/ directory."}}; - Path -> - erlang:load_nif(filename:join(Path, atom_to_list(?MODULE)), 0) - end. - -%% lz4f. - -lz4f_compress_frame(_, _) -> - erlang:nif_error({not_loaded, ?MODULE}). - -lz4f_create_compression_context() -> - erlang:nif_error({not_loaded, ?MODULE}). - -lz4f_compress_begin(_, _) -> - erlang:nif_error({not_loaded, ?MODULE}). - -lz4f_compress_update(_, _) -> - erlang:nif_error({not_loaded, ?MODULE}). - -lz4f_flush(_) -> - erlang:nif_error({not_loaded, ?MODULE}). - -lz4f_compress_end(_) -> - erlang:nif_error({not_loaded, ?MODULE}). - -lz4f_create_decompression_context() -> - erlang:nif_error({not_loaded, ?MODULE}). - -lz4f_get_frame_info(_, _) -> - erlang:nif_error({not_loaded, ?MODULE}). - -lz4f_decompress(_, _) -> - erlang:nif_error({not_loaded, ?MODULE}). diff --git a/src/lz4f.erl b/src/lz4f.erl deleted file mode 100644 index 80a4a7d..0000000 --- a/src/lz4f.erl +++ /dev/null @@ -1,107 +0,0 @@ -%% Copyright (c) 2017-Present Pivotal Software, Inc. All rights reserved. -%% -%% This package, the LZ4 binding for Erlang, is double-licensed under the Mozilla -%% Public License 1.1 ("MPL") and the Apache License version 2 -%% ("ASL"). For the MPL, please see LICENSE-MPL-RabbitMQ. For the ASL, -%% please see LICENSE-APACHE2. -%% -%% This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, -%% either express or implied. See the LICENSE file for specific language governing -%% rights and limitations of this software. -%% -%% If you have any questions regarding licensing, please contact us at -%% info@rabbitmq.com. - --module(lz4f). - -%% Simple compression. --export([compress_frame/1]). --export([compress_frame/2]). - -%% Advanced compression. --export([create_compression_context/0]). --export([compress_begin/1]). --export([compress_begin/2]). --export([compress_update/2]). --export([flush/1]). --export([compress_end/1]). - -%% Decompression. --export([create_decompression_context/0]). --export([get_frame_info/2]). --export([decompress/1]). --export([decompress/2]). - --type block_size_id() :: default | max64KB | max256KB | max1MB | max4MB. --type block_mode() :: linked | independent. --type frame_type() :: frame | skippable_frame. - --type frame_info() :: #{ - block_size_id => block_size_id(), - block_mode => block_mode(), - content_checksum => boolean(), - frame_type => frame_type(), - content_size => non_neg_integer() -}. - --type opts() :: #{ - frame_info => frame_info(), - compression_level => 0..16, - auto_flush => boolean() -}. - --opaque cctx() :: <<>>. %% Resource. --export_type([cctx/0]). - --opaque dctx() :: <<>>. %% Resource. --export_type([dctx/0]). - --spec compress_frame(binary()) -> binary(). -compress_frame(Data) -> - lz4_nif:lz4f_compress_frame(Data, #{}). - --spec compress_frame(binary(), opts()) -> binary(). -compress_frame(Data, Opts) -> - lz4_nif:lz4f_compress_frame(Data, Opts). - --spec create_compression_context() -> cctx(). -create_compression_context() -> - lz4_nif:lz4f_create_compression_context(). - --spec compress_begin(cctx()) -> binary(). -compress_begin(Cctx) -> - compress_begin(Cctx, #{}). - --spec compress_begin(cctx(), opts()) -> binary(). -compress_begin(Cctx, Opts) -> - lz4_nif:lz4f_compress_begin(Cctx, Opts). - --spec compress_update(cctx(), binary()) -> binary(). -compress_update(Cctx, Data) -> - lz4_nif:lz4f_compress_update(Cctx, Data). - --spec flush(cctx()) -> binary(). -flush(Cctx) -> - lz4_nif:lz4f_flush(Cctx). - --spec compress_end(cctx()) -> binary(). -compress_end(Cctx) -> - lz4_nif:lz4f_compress_end(Cctx). - --spec create_decompression_context() -> dctx(). -create_decompression_context() -> - lz4_nif:lz4f_create_decompression_context(). - --spec get_frame_info(dctx(), binary()) -> {ok, frame_info(), non_neg_integer()}. -get_frame_info(Dctx, Data) -> - lz4_nif:lz4f_get_frame_info(Dctx, Data). - --spec decompress(binary()) -> iolist(). -decompress(Data) -> - decompress(create_decompression_context(), Data). - --spec decompress(dctx(), binary()) -> iolist(). -decompress(Dctx, Data) -> - lz4_nif:lz4f_decompress(Dctx, Data). - -%% @todo LZ4F_resetDecompressionContext diff --git a/test/lz4f_SUITE.erl b/test/lz4f_SUITE.erl deleted file mode 100644 index 2a9cfb5..0000000 --- a/test/lz4f_SUITE.erl +++ /dev/null @@ -1,82 +0,0 @@ --module(lz4f_SUITE). --compile(export_all). - --import(ct_helper, [config/2]). --import(ct_helper, [doc/1]). - -%% ct. - -all() -> - [{group, all}]. - -groups() -> - [{all, [parallel], ct_helper:all(?MODULE)}]. - -init_per_suite(Config) -> - [{test_file, config(data_dir, Config) ++ "/pdf_reference_1-7.pdf"}|Config]. - -end_per_suite(_) -> - ok. - -%% Tests. - -compress_frame1(Config) -> - doc("Use lz4f:compress_frame/1 and then decompress back."), - {ok, File} = file:read_file(config(test_file, Config)), - Compressed = lz4f:compress_frame(File), - File = iolist_to_binary(lz4f:decompress(Compressed)), - ok. - -compress_update(Config) -> - doc("Stream compress with lz4f:compress_update/2 and then decompress back."), - {ok, File} = file:read_file(config(test_file, Config)), - Chunks = do_slice(File), - Ctx = lz4f:create_compression_context(), - Begin = lz4f:compress_begin(Ctx), - CompressedChunks = [lz4f:compress_update(Ctx, C) || C <- Chunks], - End = lz4f:compress_end(Ctx), - Compressed = iolist_to_binary([Begin, CompressedChunks, End]), - File = iolist_to_binary(lz4f:decompress(Compressed)), - ok. - -compress_flush(Config) -> - doc("Stream compress with some lz4f:flush/1 and then decompress back."), - {ok, File} = file:read_file(config(test_file, Config)), - Chunks = do_insert_flush(do_slice(File)), - Ctx = lz4f:create_compression_context(), - Begin = lz4f:compress_begin(Ctx), - CompressedChunks = [case C of - flush -> lz4f:flush(Ctx); - _ -> lz4f:compress_update(Ctx, C) - end || C <- Chunks], - End = lz4f:compress_end(Ctx), - Compressed = iolist_to_binary([Begin, CompressedChunks, End]), - File = iolist_to_binary(lz4f:decompress(Compressed)), - ok. - -decompress(Config) -> - doc("Compress and then stream decompress with lz4f:decompress/2."), - {ok, File} = file:read_file(config(test_file, Config)), - Compressed = lz4f:compress_frame(File), - Chunks = do_slice(Compressed), - Ctx = lz4f:create_decompression_context(), - DecompressedChunks = [lz4f:decompress(Ctx, C) || C <- Chunks], - File = iolist_to_binary(DecompressedChunks), - ok. - -%% Internal. - -do_insert_flush(L) -> - do_insert_flush(L, 0). - -do_insert_flush([], _) -> - []; -do_insert_flush(L, 5) -> - [flush|do_insert_flush(L, 0)]; -do_insert_flush([H|T], N) -> - [H|do_insert_flush(T, N + 1)]. - -do_slice(<>) -> - [Bin|do_slice(R)]; -do_slice(Bin) -> - [Bin]. From 9fa8ed6cca4949c25a7eb09a9593497e7a9ae1c1 Mon Sep 17 00:00:00 2001 From: Martin Sumner Date: Fri, 3 Nov 2017 14:18:49 +0000 Subject: [PATCH 04/13] Add LZ4 --- rebar.config | 4 ++++ src/leveled_sst.erl | 6 ++++-- test/ledger_manifest/nonzero_2.pnd | Bin 0 -> 1158 bytes test/new_file.sst.discarded | Bin 0 -> 9 bytes test/range1_src.sst | Bin 0 -> 4909 bytes test/simple_test.sst.discarded | Bin 0 -> 59086 bytes 6 files changed, 8 insertions(+), 2 deletions(-) create mode 100644 test/ledger_manifest/nonzero_2.pnd create mode 100644 test/new_file.sst.discarded create mode 100644 test/range1_src.sst create mode 100644 test/simple_test.sst.discarded diff --git a/rebar.config b/rebar.config index c849ed0..ea69915 100644 --- a/rebar.config +++ b/rebar.config @@ -9,3 +9,7 @@ {erl_opts, [debug_info, {parse_transform, lager_transform}, {parse_transform, eqc_cover}]}, {plugins, [rebar_eqc]}]} ]}. + +{deps, [ + {lz4, ".*", {git, "https://github.com/martinsumner/erlang-lz4", {branch, "mas-leveled"}}} + ]}. diff --git a/src/leveled_sst.erl b/src/leveled_sst.erl index 4b251f9..f78de93 100644 --- a/src/leveled_sst.erl +++ b/src/leveled_sst.erl @@ -747,7 +747,8 @@ generate_filenames(RootFilename) -> %% compression methods. Also, perhaps standardise applictaion of CRC %% checks serialise_block(Term) -> - term_to_binary(Term, ?BINARY_SETTINGS). + {ok, Bin} = lz4:pack(term_to_binary(Term)), + Bin. -spec deserialise_block(binary()) -> any(). @@ -757,7 +758,8 @@ serialise_block(Term) -> %% compression methods. Also, perhaps standardise applictaion of CRC %% checks deserialise_block(Bin) -> - binary_to_term(Bin). + {ok, Bin0} = lz4:unpack(Bin), + binary_to_term(Bin0). %%%============================================================================ diff --git a/test/ledger_manifest/nonzero_2.pnd b/test/ledger_manifest/nonzero_2.pnd new file mode 100644 index 0000000000000000000000000000000000000000..7ee1056ba58d79007882a3ae544ef487e8cbdb52 GIT binary patch literal 1158 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 literal 0 HcmV?d00001 From 0ecb83f8ec80f7517472d4931d2d9008da4db13d Mon Sep 17 00:00:00 2001 From: Martin Sumner Date: Fri, 3 Nov 2017 14:26:18 +0000 Subject: [PATCH 05/13] Remove eroneously added files --- rebar.lock | 1 - test/new_file.sst.discarded | Bin 9 -> 0 bytes test/range1_src.sst | Bin 4909 -> 0 bytes test/simple_test.sst.discarded | Bin 59086 -> 0 bytes 4 files changed, 1 deletion(-) delete mode 100644 rebar.lock delete mode 100644 test/new_file.sst.discarded delete mode 100644 test/range1_src.sst delete mode 100644 test/simple_test.sst.discarded diff --git a/rebar.lock b/rebar.lock deleted file mode 100644 index 57afcca..0000000 --- a/rebar.lock +++ /dev/null @@ -1 +0,0 @@ -[]. diff --git a/test/new_file.sst.discarded b/test/new_file.sst.discarded deleted file mode 100644 index 92c63d4753aacbb61a56eb2b69b5b383924ef7c5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9 QcmZq9W?;=o&B@6J01iO|2mk;8 diff --git a/test/range1_src.sst b/test/range1_src.sst deleted file mode 100644 index ff772d7c1b5468c2998c24d60ab454c336eba6a1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4909 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 From 99428d0e5525a5c56f81fe721e7c65c01b6c9c96 Mon Sep 17 00:00:00 2001 From: Martin Sumner Date: Fri, 3 Nov 2017 14:26:51 +0000 Subject: [PATCH 06/13] Remove erroneously added file --- test/ledger_manifest/nonzero_2.pnd | Bin 1158 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 test/ledger_manifest/nonzero_2.pnd diff --git a/test/ledger_manifest/nonzero_2.pnd b/test/ledger_manifest/nonzero_2.pnd deleted file mode 100644 index 7ee1056ba58d79007882a3ae544ef487e8cbdb52..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1158 zcmd6l!A^rf5Qc|^+SY^Vqey8BTs@c=E}na7CSA<1AR44e>(!%A>Pr}Rq?1MpHjNh{ zuw-}V|G)i*_tnSe8x1v#99_{M+zWP|&OX4_aY1@OSWnjp)^O@e(=#e6a1LTk2^9q|N`oR7e^j=*; zm@(*V&(uD1BxgRm=Z#BuA4F9U=8Q1!Lf~W Date: Sun, 5 Nov 2017 21:48:57 +0000 Subject: [PATCH 07/13] Compress journal with lz4 When the value is a binary (which should be the case with Riak) --- src/leveled_codec.erl | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/leveled_codec.erl b/src/leveled_codec.erl index 53bc81a..0d111d2 100644 --- a/src/leveled_codec.erl +++ b/src/leveled_codec.erl @@ -270,7 +270,8 @@ maybe_compress(JournalBin) -> serialise_object(Object, false) when is_binary(Object) -> Object; serialise_object(Object, true) when is_binary(Object) -> - zlib:compress(Object); + {ok, Bin} = lz4:pack(Object), + Bin; serialise_object(Object, false) -> term_to_binary(Object); serialise_object(Object, true) -> @@ -297,7 +298,8 @@ revert_value_from_journal(JournalBin, ToIgnoreKeyChanges) -> end. deserialise_object(Binary, true, true) -> - zlib:uncompress(Binary); + {ok, Deflated} = lz4:unpack(Binary), + Deflated; deserialise_object(Binary, true, false) -> Binary; deserialise_object(Binary, false, _) -> From 4c44e86eabc226ea68380db9d7e19f21a0b54bb8 Mon Sep 17 00:00:00 2001 From: Martin Sumner Date: Sun, 5 Nov 2017 22:33:32 +0000 Subject: [PATCH 08/13] Enable compression on receipt to Journal Rather than always deferring compression until compaction --- src/leveled_codec.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/leveled_codec.erl b/src/leveled_codec.erl index 0d111d2..64755d7 100644 --- a/src/leveled_codec.erl +++ b/src/leveled_codec.erl @@ -218,7 +218,7 @@ to_inkerkv(LedgerKey, SQN, to_fetch, null) -> {{SQN, ?INKT_STND, LedgerKey}, null, true}; to_inkerkv(LedgerKey, SQN, Object, KeyChanges) -> InkerType = check_forinkertype(LedgerKey, Object), - Value = create_value_for_journal({Object, KeyChanges}, false), + Value = create_value_for_journal({Object, KeyChanges}, true), {{SQN, InkerType, LedgerKey}, Value}. %% Used when fetching objects, so only handles standard, hashable entries From 61b7be5039d0f225ff41dc1ed5e09a62d3d989e9 Mon Sep 17 00:00:00 2001 From: Martin Sumner Date: Mon, 6 Nov 2017 15:54:58 +0000 Subject: [PATCH 09/13] Make compression algorithm an option Compression can be switched between LZ4 and zlib (native). The setting to determine if compression should happen on receipt is now a macro definition in leveled_codec. --- include/leveled.hrl | 3 + src/leveled_bookie.erl | 26 ++- src/leveled_codec.erl | 79 ++++--- src/leveled_iclerk.erl | 15 +- src/leveled_inker.erl | 51 ++-- src/leveled_pclerk.erl | 66 ++++-- src/leveled_penciller.erl | 49 ++-- src/leveled_sst.erl | 397 +++++++++++++++++++------------- test/end_to_end/basic_SUITE.erl | 52 ++++- 9 files changed, 474 insertions(+), 264 deletions(-) diff --git a/include/leveled.hrl b/include/leveled.hrl index e3ed9c7..aacb7eb 100644 --- a/include/leveled.hrl +++ b/include/leveled.hrl @@ -48,6 +48,7 @@ source_inker :: pid() | undefined, reload_strategy = [] :: list(), waste_retention_period :: integer() | undefined, + compression_method :: lz4|native, max_run_length}). -record(penciller_options, @@ -58,6 +59,7 @@ bookies_mem :: tuple() | undefined, source_penciller :: pid() | undefined, snapshot_longrunning = true :: boolean(), + compression_method :: lz4|native, levelzero_cointoss = false :: boolean()}). -record(iclerk_options, @@ -65,6 +67,7 @@ max_run_length :: integer() | undefined, cdb_options = #cdb_options{} :: #cdb_options{}, waste_retention_period :: integer() | undefined, + compression_method :: lz4|native, reload_strategy = [] :: list()}). -record(recent_aae, {filter :: whitelist|blacklist, diff --git a/src/leveled_bookie.erl b/src/leveled_bookie.erl index 42d5e0f..af1dd2b 100644 --- a/src/leveled_bookie.erl +++ b/src/leveled_bookie.erl @@ -81,6 +81,7 @@ -define(JOURNAL_SIZE_JITTER, 20). -define(LONG_RUNNING, 80000). -define(RECENT_AAE, false). +-define(COMPRESSION_METHOD, lz4). -record(ledger_cache, {mem :: ets:tab(), loader = leveled_tree:empty(?CACHE_TYPE) @@ -387,7 +388,8 @@ init([Opts]) -> unit_minutes = UnitMinutes} end, - {Inker, Penciller} = startup(InkerOpts, PencillerOpts, RecentAAE), + {Inker, Penciller} = + startup(InkerOpts, PencillerOpts, RecentAAE), NewETS = ets:new(mem, [ordered_set]), leveled_log:log("B0001", [Inker, Penciller]), @@ -911,16 +913,30 @@ set_options(Opts) -> ok = filelib:ensure_dir(JournalFP), ok = filelib:ensure_dir(LedgerFP), + CompressionMethod = + case get_opt(compression_method, Opts, ?COMPRESSION_METHOD) of + native -> + % Note native compression will have reduced performance + % https://github.com/martinsumner/leveled/issues/95 + native; + lz4 -> + % Must include lz4 library in rebar.config + lz4 + end, + {#inker_options{root_path = JournalFP, reload_strategy = ReloadStrategy, max_run_length = get_opt(max_run_length, Opts), waste_retention_period = WRP, - cdb_options = #cdb_options{max_size=MaxJournalSize, - binary_mode=true, - sync_strategy=SyncStrat}}, + compression_method = CompressionMethod, + cdb_options = + #cdb_options{max_size=MaxJournalSize, + binary_mode=true, + sync_strategy=SyncStrat}}, #penciller_options{root_path = LedgerFP, max_inmemory_tablesize = PCLL0CacheSize, - levelzero_cointoss = true}}. + levelzero_cointoss = true, + compression_method = CompressionMethod}}. startup(InkerOpts, PencillerOpts, RecentAAE) -> {ok, Inker} = leveled_inker:ink_start(InkerOpts), diff --git a/src/leveled_codec.erl b/src/leveled_codec.erl index 64755d7..6232a02 100644 --- a/src/leveled_codec.erl +++ b/src/leveled_codec.erl @@ -46,7 +46,7 @@ to_ledgerkey/3, to_ledgerkey/5, from_ledgerkey/1, - to_inkerkv/4, + to_inkerkv/5, from_inkerkv/1, from_inkerkv/2, from_journalkey/1, @@ -54,7 +54,7 @@ split_inkvalue/1, check_forinkertype/2, maybe_compress/1, - create_value_for_journal/2, + create_value_for_journal/3, build_metadata_object/2, generate_ledgerkv/5, get_size/2, @@ -73,6 +73,7 @@ -define(LMD_FORMAT, "~4..0w~2..0w~2..0w~2..0w~2..0w"). -define(NRT_IDX, "$aae."). -define(ALL_BUCKETS, <<"$all">>). +-define(COMPRESS_ON_RECEIPT, true). -type recent_aae() :: #recent_aae{}. -type riak_metadata() :: {binary()|delete, % Sibling Metadata @@ -214,11 +215,14 @@ to_ledgerkey(Bucket, Key, Tag) -> %% Return the Key, Value and Hash Option for this object. The hash option %% indicates whether the key would ever be looked up directly, and so if it %% requires an entry in the hash table -to_inkerkv(LedgerKey, SQN, to_fetch, null) -> +to_inkerkv(LedgerKey, SQN, to_fetch, null, _CompressionMethod) -> {{SQN, ?INKT_STND, LedgerKey}, null, true}; -to_inkerkv(LedgerKey, SQN, Object, KeyChanges) -> +to_inkerkv(LedgerKey, SQN, Object, KeyChanges, CompressionMethod) -> InkerType = check_forinkertype(LedgerKey, Object), - Value = create_value_for_journal({Object, KeyChanges}, true), + Value = + create_value_for_journal({Object, KeyChanges}, + ?COMPRESS_ON_RECEIPT, + CompressionMethod), {{SQN, InkerType, LedgerKey}, Value}. %% Used when fetching objects, so only handles standard, hashable entries @@ -235,46 +239,55 @@ from_inkerkv(Object, ToIgnoreKeyChanges) -> Object end. -create_value_for_journal({Object, KeyChanges}, Compress) +create_value_for_journal({Object, KeyChanges}, Compress, Method) when not is_binary(KeyChanges) -> KeyChangeBin = term_to_binary(KeyChanges, [compressed]), - create_value_for_journal({Object, KeyChangeBin}, Compress); -create_value_for_journal({Object, KeyChangeBin}, Compress) -> + create_value_for_journal({Object, KeyChangeBin}, Compress, Method); +create_value_for_journal({Object, KeyChangeBin}, Compress, Method) -> KeyChangeBinLen = byte_size(KeyChangeBin), - ObjectBin = serialise_object(Object, Compress), - TypeCode = encode_valuetype(is_binary(Object), Compress), + ObjectBin = serialise_object(Object, Compress, Method), + TypeCode = encode_valuetype(is_binary(Object), Compress, Method), <>. maybe_compress({null, KeyChanges}) -> - create_value_for_journal({null, KeyChanges}, false); + create_value_for_journal({null, KeyChanges}, false, native); maybe_compress(JournalBin) -> Length0 = byte_size(JournalBin) - 5, <> = JournalBin, - {IsBinary, IsCompressed} = decode_valuetype(Type), + {IsBinary, IsCompressed, IsLz4} = decode_valuetype(Type), case IsCompressed of true -> JournalBin; false -> Length1 = Length0 - KeyChangeLength, <> = JBin0, - V0 = {deserialise_object(OBin2, IsBinary, IsCompressed), + V0 = {deserialise_object(OBin2, IsBinary, IsCompressed, IsLz4), binary_to_term(KCBin2)}, - create_value_for_journal(V0, true) + PressMethod = case IsLz4 of + true -> lz4; + false -> native + end, + create_value_for_journal(V0, true, PressMethod) end. -serialise_object(Object, false) when is_binary(Object) -> +serialise_object(Object, false, _Method) when is_binary(Object) -> Object; -serialise_object(Object, true) when is_binary(Object) -> - {ok, Bin} = lz4:pack(Object), - Bin; -serialise_object(Object, false) -> +serialise_object(Object, true, Method) when is_binary(Object) -> + case Method of + lz4 -> + {ok, Bin} = lz4:pack(Object), + Bin; + native -> + zlib:compress(Object) + end; +serialise_object(Object, false, _Method) -> term_to_binary(Object); -serialise_object(Object, true) -> +serialise_object(Object, true, _Method) -> term_to_binary(Object, [compressed]). revert_value_from_journal(JournalBin) -> @@ -285,27 +298,34 @@ revert_value_from_journal(JournalBin, ToIgnoreKeyChanges) -> <> = JournalBin, - {IsBinary, IsCompressed} = decode_valuetype(Type), + {IsBinary, IsCompressed, IsLz4} = decode_valuetype(Type), Length1 = Length0 - KeyChangeLength, case ToIgnoreKeyChanges of true -> <> = JBin0, - {deserialise_object(OBin2, IsBinary, IsCompressed), []}; + {deserialise_object(OBin2, IsBinary, IsCompressed, IsLz4), []}; false -> <> = JBin0, - {deserialise_object(OBin2, IsBinary, IsCompressed), + {deserialise_object(OBin2, IsBinary, IsCompressed, IsLz4), binary_to_term(KCBin2)} end. -deserialise_object(Binary, true, true) -> +deserialise_object(Binary, true, true, true) -> {ok, Deflated} = lz4:unpack(Binary), Deflated; -deserialise_object(Binary, true, false) -> +deserialise_object(Binary, true, true, false) -> + zlib:uncompress(Binary); +deserialise_object(Binary, true, false, _IsLz4) -> Binary; -deserialise_object(Binary, false, _) -> +deserialise_object(Binary, false, _, _IsLz4) -> binary_to_term(Binary). -encode_valuetype(IsBinary, IsCompressed) -> +encode_valuetype(IsBinary, IsCompressed, Method) -> + Bit3 = + case Method of + lz4 -> 4; + native -> 0 + end, Bit2 = case IsBinary of true -> 2; @@ -316,12 +336,13 @@ encode_valuetype(IsBinary, IsCompressed) -> true -> 1; false -> 0 end, - Bit1 + Bit2. + Bit1 + Bit2 + Bit3. decode_valuetype(TypeInt) -> IsCompressed = TypeInt band 1 == 1, IsBinary = TypeInt band 2 == 2, - {IsBinary, IsCompressed}. + IsLz4 = TypeInt band 4 ==4, + {IsBinary, IsCompressed, IsLz4}. from_journalkey({SQN, _Type, LedgerKey}) -> {SQN, LedgerKey}. diff --git a/src/leveled_iclerk.erl b/src/leveled_iclerk.erl index 5c6b126..81aa36b 100644 --- a/src/leveled_iclerk.erl +++ b/src/leveled_iclerk.erl @@ -109,7 +109,8 @@ cdb_options, waste_retention_period :: integer() | undefined, waste_path :: string() | undefined, - reload_strategy = ?DEFAULT_RELOAD_STRATEGY :: list()}). + reload_strategy = ?DEFAULT_RELOAD_STRATEGY :: list(), + compression_method :: lz4|native}). -record(candidate, {low_sqn :: integer() | undefined, filename :: string() | undefined, @@ -167,7 +168,9 @@ init([IClerkOpts]) -> cdb_options = CDBopts, reload_strategy = ReloadStrategy, waste_path = WP, - waste_retention_period = WRP}}. + waste_retention_period = WRP, + compression_method = + IClerkOpts#iclerk_options.compression_method}}. handle_call(_Msg, _From, State) -> {reply, not_supported, State}. @@ -754,7 +757,7 @@ test_ledgerkey(Key) -> {o, "Bucket", Key, null}. test_inkerkv(SQN, Key, V, IdxSpecs) -> - leveled_codec:to_inkerkv(test_ledgerkey(Key), SQN, V, IdxSpecs). + leveled_codec:to_inkerkv(test_ledgerkey(Key), SQN, V, IdxSpecs, native). fetch_testcdb(RP) -> FN1 = leveled_inker:filepath(RP, 1, new_journal), @@ -936,13 +939,15 @@ compact_singlefile_totwosmallfiles_testto() -> lists:foreach(fun(X) -> LK = test_ledgerkey("Key" ++ integer_to_list(X)), Value = leveled_rand:rand_bytes(1024), - {IK, IV} = leveled_codec:to_inkerkv(LK, X, Value, []), + {IK, IV} = + leveled_codec:to_inkerkv(LK, X, Value, [], native), ok = leveled_cdb:cdb_put(CDB1, IK, IV) end, lists:seq(1, 1000)), {ok, NewName} = leveled_cdb:cdb_complete(CDB1), {ok, CDBr} = leveled_cdb:cdb_open_reader(NewName), - CDBoptsSmall = #cdb_options{binary_mode=true, max_size=400000, file_path=CP}, + CDBoptsSmall = + #cdb_options{binary_mode=true, max_size=400000, file_path=CP}, BestRun1 = [#candidate{low_sqn=1, filename=leveled_cdb:cdb_filename(CDBr), journal=CDBr, diff --git a/src/leveled_inker.erl b/src/leveled_inker.erl index 209abc4..85da448 100644 --- a/src/leveled_inker.erl +++ b/src/leveled_inker.erl @@ -136,6 +136,7 @@ clerk :: pid() | undefined, compaction_pending = false :: boolean(), is_snapshot = false :: boolean(), + compression_method :: lz4|native, source_inker :: pid() | undefined}). @@ -509,10 +510,12 @@ start_from_file(InkOpts) -> ReloadStrategy = InkOpts#inker_options.reload_strategy, MRL = InkOpts#inker_options.max_run_length, WRP = InkOpts#inker_options.waste_retention_period, + Compression = InkOpts#inker_options.compression_method, IClerkOpts = #iclerk_options{inker = self(), cdb_options=IClerkCDBOpts, waste_retention_period = WRP, reload_strategy = ReloadStrategy, + compression_method = Compression, max_run_length = MRL}, {ok, Clerk} = leveled_iclerk:clerk_new(IClerkOpts), @@ -528,16 +531,19 @@ start_from_file(InkOpts) -> active_journaldb = ActiveJournal, root_path = RootPath, cdb_options = CDBopts#cdb_options{waste_path=WasteFP}, + compression_method = Compression, clerk = Clerk}}. put_object(LedgerKey, Object, KeyChanges, State) -> NewSQN = State#state.journal_sqn + 1, ActiveJournal = State#state.active_journaldb, - {JournalKey, JournalBin} = leveled_codec:to_inkerkv(LedgerKey, - NewSQN, - Object, - KeyChanges), + {JournalKey, JournalBin} = + leveled_codec:to_inkerkv(LedgerKey, + NewSQN, + Object, + KeyChanges, + State#state.compression_method), case leveled_cdb:cdb_put(ActiveJournal, JournalKey, JournalBin) of @@ -579,19 +585,23 @@ get_object(LedgerKey, SQN, Manifest) -> get_object(LedgerKey, SQN, Manifest, ToIgnoreKeyChanges) -> JournalP = leveled_imanifest:find_entry(SQN, Manifest), - {InkerKey, _V, true} = leveled_codec:to_inkerkv(LedgerKey, - SQN, - to_fetch, - null), + {InkerKey, _V, true} = + leveled_codec:to_inkerkv(LedgerKey, + SQN, + to_fetch, + null, + not_applicable), Obj = leveled_cdb:cdb_get(JournalP, InkerKey), leveled_codec:from_inkerkv(Obj, ToIgnoreKeyChanges). key_check(LedgerKey, SQN, Manifest) -> JournalP = leveled_imanifest:find_entry(SQN, Manifest), - {InkerKey, _V, true} = leveled_codec:to_inkerkv(LedgerKey, - SQN, - to_fetch, - null), + {InkerKey, _V, true} = + leveled_codec:to_inkerkv(LedgerKey, + SQN, + to_fetch, + null, + not_applicable), leveled_cdb:cdb_keycheck(JournalP, InkerKey). build_manifest(ManifestFilenames, @@ -851,7 +861,7 @@ initiate_penciller_snapshot(Bookie) -> -ifdef(TEST). create_value_for_journal(Obj, Comp) -> - leveled_codec:create_value_for_journal(Obj, Comp). + leveled_codec:create_value_for_journal(Obj, Comp, native). build_dummy_journal() -> F = fun(X) -> X end, @@ -933,7 +943,8 @@ simple_inker_test() -> build_dummy_journal(), CDBopts = #cdb_options{max_size=300000, binary_mode=true}, {ok, Ink1} = ink_start(#inker_options{root_path=RootPath, - cdb_options=CDBopts}), + cdb_options=CDBopts, + compression_method=native}), Obj1 = ink_get(Ink1, "Key1", 1), ?assertMatch({{1, "Key1"}, {"TestValue1", []}}, Obj1), Obj3 = ink_get(Ink1, "Key1", 3), @@ -955,7 +966,8 @@ simple_inker_completeactivejournal_test() -> F1r = filename:join(JournalFP, "nursery_1.pnd"), ok = file:rename(F1, F1r), {ok, Ink1} = ink_start(#inker_options{root_path=RootPath, - cdb_options=CDBopts}), + cdb_options=CDBopts, + compression_method=native}), Obj1 = ink_get(Ink1, "Key1", 1), ?assertMatch({{1, "Key1"}, {"TestValue1", []}}, Obj1), Obj2 = ink_get(Ink1, "Key4", 4), @@ -973,7 +985,8 @@ compact_journal_test() -> RStrategy = [{?STD_TAG, recovr}], {ok, Ink1} = ink_start(#inker_options{root_path=RootPath, cdb_options=CDBopts, - reload_strategy=RStrategy}), + reload_strategy=RStrategy, + compression_method=native}), {ok, NewSQN1, _ObjSize} = ink_put(Ink1, test_ledgerkey("KeyAA"), "TestValueAA", @@ -1039,7 +1052,8 @@ empty_manifest_test() -> clean_testdir(RootPath), CDBopts = #cdb_options{max_size=300000}, {ok, Ink1} = ink_start(#inker_options{root_path=RootPath, - cdb_options=CDBopts}), + cdb_options=CDBopts, + compression_method=native}), ?assertMatch(not_present, ink_fetch(Ink1, "Key1", 1)), CheckFun = fun(L, K, SQN) -> lists:member({SQN, K}, L) end, @@ -1059,7 +1073,8 @@ empty_manifest_test() -> ok = file:write_file(FN, term_to_binary("Hello")), {ok, Ink2} = ink_start(#inker_options{root_path=RootPath, - cdb_options=CDBopts}), + cdb_options=CDBopts, + compression_method=native}), ?assertMatch(not_present, ink_fetch(Ink2, "Key1", 1)), {ok, SQN, Size} = ink_put(Ink2, "Key1", "Value1", {[], infinity}), ?assertMatch(2, SQN), diff --git a/src/leveled_pclerk.erl b/src/leveled_pclerk.erl index c412cf4..ec10982 100644 --- a/src/leveled_pclerk.erl +++ b/src/leveled_pclerk.erl @@ -35,7 +35,7 @@ ]). -export([ - clerk_new/2, + clerk_new/3, clerk_prompt/1, clerk_push/2, clerk_close/1, @@ -49,15 +49,19 @@ -record(state, {owner :: pid() | undefined, root_path :: string() | undefined, - pending_deletions = dict:new() % OTP 16 does not like type + pending_deletions = dict:new(), % OTP 16 does not like type + compression_method :: lz4|native }). %%%============================================================================ %%% API %%%============================================================================ -clerk_new(Owner, Manifest) -> - {ok, Pid} = gen_server:start(?MODULE, [], []), +clerk_new(Owner, Manifest, CompressionMethod) -> + {ok, Pid} = + gen_server:start(?MODULE, + [{compression_method, CompressionMethod}], + []), ok = gen_server:call(Pid, {load, Owner, Manifest}, infinity), leveled_log:log("PC001", [Pid, Owner]), {ok, Pid}. @@ -78,8 +82,8 @@ clerk_close(Pid) -> %%% gen_server callbacks %%%============================================================================ -init([]) -> - {ok, #state{}}. +init([{compression_method, CompressionMethod}]) -> + {ok, #state{compression_method = CompressionMethod}}. handle_call({load, Owner, RootPath}, _From, State) -> {reply, ok, State#state{owner=Owner, root_path=RootPath}, ?MIN_TIMEOUT}; @@ -120,7 +124,8 @@ request_work(State) -> handle_work({SrcLevel, Manifest}, State) -> {UpdManifest, EntriesToDelete} = merge(SrcLevel, Manifest, - State#state.root_path), + State#state.root_path, + State#state.compression_method), leveled_log:log("PC007", []), SWMC = os:timestamp(), ok = leveled_penciller:pcl_manifestchange(State#state.owner, @@ -132,7 +137,7 @@ handle_work({SrcLevel, Manifest}, State) -> leveled_log:log_timer("PC018", [], SWSM), {leveled_pmanifest:get_manifest_sqn(UpdManifest), EntriesToDelete}. -merge(SrcLevel, Manifest, RootPath) -> +merge(SrcLevel, Manifest, RootPath, CompressionMethod) -> Src = leveled_pmanifest:mergefile_selector(Manifest, SrcLevel), NewSQN = leveled_pmanifest:get_manifest_sqn(Manifest) + 1, SinkList = leveled_pmanifest:merge_lookup(Manifest, @@ -152,7 +157,9 @@ merge(SrcLevel, Manifest, RootPath) -> {Man0, []}; _ -> SST_RP = leveled_penciller:sst_rootpath(RootPath), - perform_merge(Manifest, Src, SinkList, SrcLevel, SST_RP, NewSQN) + perform_merge(Manifest, + Src, SinkList, SrcLevel, + SST_RP, NewSQN, CompressionMethod) end. notify_deletions([], _Penciller) -> @@ -167,16 +174,21 @@ notify_deletions([Head|Tail], Penciller) -> %% %% SrcLevel is the level of the src sst file, the sink should be srcLevel + 1 -perform_merge(Manifest, Src, SinkList, SrcLevel, RootPath, NewSQN) -> +perform_merge(Manifest, + Src, SinkList, SrcLevel, + RootPath, NewSQN, + CompressionMethod) -> leveled_log:log("PC010", [Src#manifest_entry.filename, NewSQN]), SrcList = [{next, Src, all}], MaxSQN = leveled_sst:sst_getmaxsequencenumber(Src#manifest_entry.owner), SinkLevel = SrcLevel + 1, SinkBasement = leveled_pmanifest:is_basement(Manifest, SinkLevel), - Additions = do_merge(SrcList, SinkList, - SinkLevel, SinkBasement, - RootPath, NewSQN, MaxSQN, - []), + Additions = + do_merge(SrcList, SinkList, + SinkLevel, SinkBasement, + RootPath, NewSQN, MaxSQN, + CompressionMethod, + []), RevertPointerFun = fun({next, ME, _SK}) -> ME @@ -193,22 +205,23 @@ perform_merge(Manifest, Src, SinkList, SrcLevel, RootPath, NewSQN) -> Src), {Man2, [Src|SinkManifestList]}. -do_merge([], [], SinkLevel, _SinkB, _RP, NewSQN, _MaxSQN, Additions) -> +do_merge([], [], SinkLevel, _SinkB, _RP, NewSQN, _MaxSQN, _CM, Additions) -> leveled_log:log("PC011", [NewSQN, SinkLevel, length(Additions)]), Additions; -do_merge(KL1, KL2, SinkLevel, SinkB, RP, NewSQN, MaxSQN, Additions) -> +do_merge(KL1, KL2, SinkLevel, SinkB, RP, NewSQN, MaxSQN, CM, Additions) -> FileName = leveled_penciller:sst_filename(NewSQN, SinkLevel, length(Additions)), leveled_log:log("PC012", [NewSQN, FileName, SinkB]), TS1 = os:timestamp(), case leveled_sst:sst_new(RP, FileName, - KL1, KL2, SinkB, SinkLevel, MaxSQN) of + KL1, KL2, SinkB, SinkLevel, MaxSQN, CM) of empty -> leveled_log:log("PC013", [FileName]), do_merge([], [], SinkLevel, SinkB, RP, NewSQN, MaxSQN, + CM, Additions); {ok, Pid, Reply} -> {{KL1Rem, KL2Rem}, SmallestKey, HighestKey} = Reply, @@ -220,6 +233,7 @@ do_merge(KL1, KL2, SinkLevel, SinkB, RP, NewSQN, MaxSQN, Additions) -> do_merge(KL1Rem, KL2Rem, SinkLevel, SinkB, RP, NewSQN, MaxSQN, + CM, Additions ++ [Entry]) end. @@ -265,31 +279,36 @@ merge_file_test() -> "KL1_L1.sst", 1, KL1_L1, - 999999), + 999999, + native), KL1_L2 = lists:sort(generate_randomkeys(8000, 0, 250)), {ok, PidL2_1, _} = leveled_sst:sst_new("../test/", "KL1_L2.sst", 2, KL1_L2, - 999999), + 999999, + native), KL2_L2 = lists:sort(generate_randomkeys(8000, 250, 250)), {ok, PidL2_2, _} = leveled_sst:sst_new("../test/", "KL2_L2.sst", 2, KL2_L2, - 999999), + 999999, + lz4), KL3_L2 = lists:sort(generate_randomkeys(8000, 500, 250)), {ok, PidL2_3, _} = leveled_sst:sst_new("../test/", "KL3_L2.sst", 2, KL3_L2, - 999999), + 999999, + lz4), KL4_L2 = lists:sort(generate_randomkeys(8000, 750, 250)), {ok, PidL2_4, _} = leveled_sst:sst_new("../test/", "KL4_L2.sst", 2, KL4_L2, - 999999), + 999999, + lz4), E1 = #manifest_entry{owner = PidL1_1, filename = "./KL1_L1.sst", @@ -321,7 +340,8 @@ merge_file_test() -> PointerList = lists:map(fun(ME) -> {next, ME, all} end, [E2, E3, E4, E5]), - {Man6, _Dels} = perform_merge(Man5, E1, PointerList, 1, "../test", 3), + {Man6, _Dels} = + perform_merge(Man5, E1, PointerList, 1, "../test", 3, native), ?assertMatch(3, leveled_pmanifest:get_manifest_sqn(Man6)). diff --git a/src/leveled_penciller.erl b/src/leveled_penciller.erl index 8abe56b..adf8252 100644 --- a/src/leveled_penciller.erl +++ b/src/leveled_penciller.erl @@ -243,7 +243,9 @@ work_ongoing = false :: boolean(), % i.e. compaction work work_backlog = false :: boolean(), % i.e. compaction work - head_timing :: tuple() | undefined}). + head_timing :: tuple() | undefined, + + compression_method :: lz4|native}). -type penciller_options() :: #penciller_options{}. -type bookies_memory() :: {tuple()|empty_cache, @@ -835,25 +837,28 @@ sst_filename(ManSQN, Level, Count) -> start_from_file(PCLopts) -> RootPath = PCLopts#penciller_options.root_path, - MaxTableSize = case PCLopts#penciller_options.max_inmemory_tablesize of - undefined -> - ?MAX_TABLESIZE; - M -> - M - end, + MaxTableSize = + case PCLopts#penciller_options.max_inmemory_tablesize of + undefined -> + ?MAX_TABLESIZE; + M -> + M + end, + PressMethod = PCLopts#penciller_options.compression_method, - {ok, MergeClerk} = leveled_pclerk:clerk_new(self(), RootPath), + {ok, MergeClerk} = leveled_pclerk:clerk_new(self(), RootPath, PressMethod), CoinToss = PCLopts#penciller_options.levelzero_cointoss, % Used to randomly defer the writing of L0 file. Intended to help with % vnode syncronisation issues (e.g. stop them all by default merging to % level zero concurrently) - InitState = #state{clerk=MergeClerk, - root_path=RootPath, - levelzero_maxcachesize=MaxTableSize, - levelzero_cointoss=CoinToss, - levelzero_index=leveled_pmem:new_index()}, + InitState = #state{clerk = MergeClerk, + root_path = RootPath, + levelzero_maxcachesize = MaxTableSize, + levelzero_cointoss = CoinToss, + levelzero_index = leveled_pmem:new_index(), + compression_method = PressMethod}, %% Open manifest Manifest0 = leveled_pmanifest:open_manifest(RootPath), @@ -861,7 +866,8 @@ start_from_file(PCLopts) -> fun(FN) -> {ok, Pid, - {_FK, _LK}} = leveled_sst:sst_open(sst_rootpath(RootPath), FN), + {_FK, _LK}} = + leveled_sst:sst_open(sst_rootpath(RootPath), FN), Pid end, SQNFun = fun leveled_sst:sst_getmaxsequencenumber/1, @@ -1006,7 +1012,8 @@ roll_memory(State, false) -> length(State#state.levelzero_cache), FetchFun, PCL, - State#state.ledger_sqn), + State#state.ledger_sqn, + State#state.compression_method), {ok, Constructor, _} = R, Constructor; roll_memory(State, true) -> @@ -1020,7 +1027,8 @@ roll_memory(State, true) -> FileName, 0, KVList, - State#state.ledger_sqn), + State#state.ledger_sqn, + State#state.compression_method), {ok, Constructor, _} = R, Constructor. @@ -1401,7 +1409,8 @@ simple_server_test() -> RootPath = "../test/ledger", clean_testdir(RootPath), {ok, PCL} = pcl_start(#penciller_options{root_path=RootPath, - max_inmemory_tablesize=1000}), + max_inmemory_tablesize=1000, + compression_method=native}), Key1_Pre = {{o,"Bucket0001", "Key0001", null}, {1, {active, infinity}, null}}, Key1 = add_missing_hash(Key1_Pre), @@ -1440,7 +1449,8 @@ simple_server_test() -> ok = pcl_close(PCL), {ok, PCLr} = pcl_start(#penciller_options{root_path=RootPath, - max_inmemory_tablesize=1000}), + max_inmemory_tablesize=1000, + compression_method=native}), ?assertMatch(2003, pcl_getstartupsequencenumber(PCLr)), % ok = maybe_pause_push(PCLr, [Key2] ++ KL2 ++ [Key3]), @@ -1689,7 +1699,8 @@ create_file_test() -> 1, FetchFun, undefined, - 10000), + 10000, + native), lists:foreach(fun(X) -> case checkready(SP) of timeout -> diff --git a/src/leveled_sst.erl b/src/leveled_sst.erl index f78de93..79b0212 100644 --- a/src/leveled_sst.erl +++ b/src/leveled_sst.erl @@ -91,9 +91,9 @@ delete_pending/2, delete_pending/3]). --export([sst_new/5, - sst_new/7, - sst_newlevelzero/6, +-export([sst_new/6, + sst_new/8, + sst_newlevelzero/7, sst_open/2, sst_get/2, sst_get/3, @@ -127,14 +127,16 @@ %% see Issue 52. Handling within the SST process may lead to contention and %% extra copying. Files at the top of the tree yield, those lower down don't. --record(state, {summary, - handle :: file:fd() | undefined, - sst_timings :: tuple() | undefined, - penciller :: pid() | undefined, - root_path, - filename, - yield_blockquery = false :: boolean(), - blockindex_cache}). +-record(state, + {summary, + handle :: file:fd() | undefined, + sst_timings :: tuple() | undefined, + penciller :: pid() | undefined, + root_path, + filename, + yield_blockquery = false :: boolean(), + blockindex_cache, + compression_method :: lz4|native}). %%%============================================================================ @@ -159,29 +161,30 @@ sst_open(RootPath, Filename) -> {ok, Pid, {SK, EK}} end. --spec sst_new(string(), string(), integer(), list(), integer()) -> +-spec sst_new(string(), string(), integer(), list(), integer(), lz4|native) -> {ok, pid(), {tuple(), tuple()}}. %% @doc %% Start a new SST file at the assigned level passing in a list of Key, Value %% pairs. This should not be used for basement levels or unexpanded Key/Value %% lists as merge_lists will not be called. -sst_new(RootPath, Filename, Level, KVList, MaxSQN) -> +sst_new(RootPath, Filename, Level, KVList, MaxSQN, PressMethod) -> {ok, Pid} = gen_fsm:start(?MODULE, [], []), - {[], [], SlotList, FK} = merge_lists(KVList), + {[], [], SlotList, FK} = merge_lists(KVList, PressMethod), case gen_fsm:sync_send_event(Pid, {sst_new, RootPath, Filename, Level, {SlotList, FK}, - MaxSQN}, + MaxSQN, + PressMethod}, infinity) of {ok, {SK, EK}} -> {ok, Pid, {SK, EK}} end. -spec sst_new(string(), string(), list(), list(), - boolean(), integer(), integer()) -> + boolean(), integer(), integer(), lz4|native) -> empty|{ok, pid(), {{list(), list()}, tuple(), tuple()}}. %% @doc %% Start a new SST file at the assigned level passing in a two lists of @@ -194,8 +197,11 @@ sst_new(RootPath, Filename, Level, KVList, MaxSQN) -> %% be that the merge_lists returns nothin (for example when a basement file is %% all tombstones) - and the atome empty is returned in this case so that the %% file is not added to the manifest. -sst_new(RootPath, Filename, KVL1, KVL2, IsBasement, Level, MaxSQN) -> - {Rem1, Rem2, SlotList, FK} = merge_lists(KVL1, KVL2, {IsBasement, Level}), +sst_new(RootPath, Filename, + KVL1, KVL2, IsBasement, Level, + MaxSQN, PressMethod) -> + {Rem1, Rem2, SlotList, FK} = + merge_lists(KVL1, KVL2, {IsBasement, Level}, PressMethod), case SlotList of [] -> empty; @@ -207,7 +213,8 @@ sst_new(RootPath, Filename, KVL1, KVL2, IsBasement, Level, MaxSQN) -> Filename, Level, {SlotList, FK}, - MaxSQN}, + MaxSQN, + PressMethod}, infinity) of {ok, {SK, EK}} -> {ok, Pid, {{Rem1, Rem2}, SK, EK}} @@ -215,13 +222,16 @@ sst_new(RootPath, Filename, KVL1, KVL2, IsBasement, Level, MaxSQN) -> end. -spec sst_newlevelzero(string(), string(), - integer(), fun(), pid()|undefined, integer()) -> + integer(), fun(), pid()|undefined, integer(), + lz4|native) -> {ok, pid(), noreply}. %% @doc %% Start a new file at level zero. At this level the file size is not fixed - %% it will be as big as the input. Also the KVList is not passed in, it is %% fetched slot by slot using the FetchFun -sst_newlevelzero(RootPath, Filename, Slots, FetchFun, Penciller, MaxSQN) -> +sst_newlevelzero(RootPath, Filename, + Slots, FetchFun, Penciller, + MaxSQN, PressMethod) -> {ok, Pid} = gen_fsm:start(?MODULE, [], []), gen_fsm:send_event(Pid, {sst_newlevelzero, @@ -230,7 +240,8 @@ sst_newlevelzero(RootPath, Filename, Slots, FetchFun, Penciller, MaxSQN) -> Slots, FetchFun, Penciller, - MaxSQN}), + MaxSQN, + PressMethod}), {ok, Pid, noreply}. -spec sst_get(pid(), tuple()) -> tuple()|not_present. @@ -261,10 +272,10 @@ sst_getkvrange(Pid, StartKey, EndKey, ScanWidth) -> case gen_fsm:sync_send_event(Pid, {get_kvrange, StartKey, EndKey, ScanWidth}, infinity) of - {yield, SlotsToFetchBinList, SlotsToPoint} -> + {yield, SlotsToFetchBinList, SlotsToPoint, PressMethod} -> FetchFun = fun({SlotBin, SK, EK}, Acc) -> - Acc ++ binaryslot_trimmedlist(SlotBin, SK, EK) + Acc ++ binaryslot_trimmedlist(SlotBin, SK, EK, PressMethod) end, lists:foldl(FetchFun, [], SlotsToFetchBinList) ++ SlotsToPoint; Reply -> @@ -276,10 +287,11 @@ sst_getkvrange(Pid, StartKey, EndKey, ScanWidth) -> %% Get a list of slots by their ID. The slot will be converted from the binary %% to term form outside of the FSM loop sst_getslots(Pid, SlotList) -> - SlotBins = gen_fsm:sync_send_event(Pid, {get_slots, SlotList}, infinity), + {SlotBins, PressMethod} + = gen_fsm:sync_send_event(Pid, {get_slots, SlotList}, infinity), FetchFun = fun({SlotBin, SK, EK}, Acc) -> - Acc ++ binaryslot_trimmedlist(SlotBin, SK, EK) + Acc ++ binaryslot_trimmedlist(SlotBin, SK, EK, PressMethod) end, lists:foldl(FetchFun, [], SlotBins). @@ -350,19 +362,22 @@ starting({sst_open, RootPath, Filename}, _From, State) -> {ok, {Summary#summary.first_key, Summary#summary.last_key}}, reader, UpdState}; -starting({sst_new, RootPath, Filename, Level, {SlotList, FirstKey}, MaxSQN}, - _From, State) -> +starting({sst_new, + RootPath, Filename, Level, + {SlotList, FirstKey}, MaxSQN, + PressMethod}, _From, State) -> SW = os:timestamp(), {Length, SlotIndex, BlockIndex, - SlotsBin} = build_all_slots(SlotList), + SlotsBin} = build_all_slots(SlotList, PressMethod), SummaryBin = build_table_summary(SlotIndex, Level, FirstKey, Length, MaxSQN), - ActualFilename = write_file(RootPath, Filename, SummaryBin, SlotsBin), + ActualFilename = + write_file(RootPath, Filename, SummaryBin, SlotsBin, PressMethod), YBQ = Level =< 2, UpdState = read_file(ActualFilename, State#state{root_path=RootPath, @@ -377,20 +392,22 @@ starting({sst_new, RootPath, Filename, Level, {SlotList, FirstKey}, MaxSQN}, UpdState#state{blockindex_cache = BlockIndex}}. starting({sst_newlevelzero, RootPath, Filename, - Slots, FetchFun, Penciller, MaxSQN}, State) -> + Slots, FetchFun, Penciller, MaxSQN, + PressMethod}, State) -> SW = os:timestamp(), KVList = leveled_pmem:to_list(Slots, FetchFun), - {[], [], SlotList, FirstKey} = merge_lists(KVList), + {[], [], SlotList, FirstKey} = merge_lists(KVList, PressMethod), {SlotCount, SlotIndex, BlockIndex, - SlotsBin} = build_all_slots(SlotList), + SlotsBin} = build_all_slots(SlotList, PressMethod), SummaryBin = build_table_summary(SlotIndex, 0, FirstKey, SlotCount, MaxSQN), - ActualFilename = write_file(RootPath, Filename, SummaryBin, SlotsBin), + ActualFilename = + write_file(RootPath, Filename, SummaryBin, SlotsBin, PressMethod), UpdState = read_file(ActualFilename, State#state{root_path = RootPath, yield_blockquery = true}), @@ -400,13 +417,17 @@ starting({sst_newlevelzero, RootPath, Filename, SW), case Penciller of undefined -> - {next_state, reader, UpdState#state{blockindex_cache = BlockIndex}}; + {next_state, + reader, + UpdState#state{blockindex_cache = BlockIndex}}; _ -> leveled_penciller:pcl_confirml0complete(Penciller, UpdState#state.filename, Summary#summary.first_key, Summary#summary.last_key), - {next_state, reader, UpdState#state{blockindex_cache = BlockIndex}} + {next_state, + reader, + UpdState#state{blockindex_cache = BlockIndex}} end. @@ -420,16 +441,20 @@ reader({get_kvrange, StartKey, EndKey, ScanWidth}, _From, State) -> EndKey, ScanWidth, State), + PressMethod = State#state.compression_method, case State#state.yield_blockquery of true -> {reply, - {yield, SlotsToFetchBinList, SlotsToPoint}, + {yield, + SlotsToFetchBinList, + SlotsToPoint, + PressMethod}, reader, State}; false -> FetchFun = fun({SlotBin, SK, EK}, Acc) -> - Acc ++ binaryslot_trimmedlist(SlotBin, SK, EK) + Acc ++ binaryslot_trimmedlist(SlotBin, SK, EK, PressMethod) end, {reply, lists:foldl(FetchFun, [], SlotsToFetchBinList) ++ SlotsToPoint, @@ -438,7 +463,7 @@ reader({get_kvrange, StartKey, EndKey, ScanWidth}, _From, State) -> end; reader({get_slots, SlotList}, _From, State) -> SlotBins = read_slots(State#state.handle, SlotList), - {reply, SlotBins, reader, State}; + {reply, {SlotBins, State#state.compression_method}, reader, State}; reader(get_maxsequencenumber, _From, State) -> Summary = State#state.summary, {reply, Summary#summary.max_sqn, reader, State}; @@ -475,14 +500,19 @@ delete_pending({get_kvrange, StartKey, EndKey, ScanWidth}, _From, State) -> ScanWidth, State), % Always yield as about to clear and de-reference + PressMethod = State#state.compression_method, {reply, - {yield, SlotsToFetchBinList, SlotsToPoint}, + {yield, SlotsToFetchBinList, SlotsToPoint, PressMethod}, delete_pending, State, ?DELETE_TIMEOUT}; delete_pending({get_slots, SlotList}, _From, State) -> SlotBins = read_slots(State#state.handle, SlotList), - {reply, SlotBins, delete_pending, State, ?DELETE_TIMEOUT}; + {reply, + {SlotBins, State#state.compression_method}, + delete_pending, + State, + ?DELETE_TIMEOUT}; delete_pending(close, _From, State) -> leveled_log:log("SST07", [State#state.filename]), ok = file:close(State#state.handle), @@ -528,6 +558,7 @@ code_change(_OldVsn, StateName, State, _Extra) -> fetch(LedgerKey, Hash, State) -> Summary = State#state.summary, + PressMethod = State#state.compression_method, Slot = lookup_slot(LedgerKey, Summary#summary.index), SlotID = Slot#slot_index_value.slot_id, Bloom = Slot#slot_index_value.bloom, @@ -540,9 +571,8 @@ fetch(LedgerKey, Hash, State) -> case CachedBlockIdx of none -> SlotBin = read_slot(State#state.handle, Slot), - {Result, - BlockLengths, - BlockIdx} = binaryslot_get(SlotBin, LedgerKey, Hash), + {Result, BlockLengths, BlockIdx} = + binaryslot_get(SlotBin, LedgerKey, Hash, PressMethod), BlockIndexCache = array:set(SlotID - 1, <>, @@ -560,11 +590,13 @@ fetch(LedgerKey, Hash, State) -> [] -> {not_present, slot_bloom, SlotID, State}; _ -> - Result = check_blocks(PosList, - State#state.handle, - Slot, - BlockLengths, - LedgerKey), + Result = + check_blocks(PosList, + State#state.handle, + Slot, + BlockLengths, + LedgerKey, + PressMethod), {Result, slot_fetch, SlotID, State} end end @@ -626,12 +658,14 @@ fetch_range(StartKey, EndKey, ScanWidth, State) -> {SlotsToFetchBinList, SlotsToPoint}. -write_file(RootPath, Filename, SummaryBin, SlotsBin) -> +write_file(RootPath, Filename, SummaryBin, SlotsBin, PressMethod) -> SummaryLength = byte_size(SummaryBin), SlotsLength = byte_size(SlotsBin), {PendingName, FinalName} = generate_filenames(Filename), + FileVersion = gen_fileversion(PressMethod), ok = file:write_file(filename:join(RootPath, PendingName), - <>, @@ -650,27 +684,48 @@ write_file(RootPath, Filename, SummaryBin, SlotsBin) -> FinalName. read_file(Filename, State) -> - {Handle, SummaryBin} = open_reader(filename:join(State#state.root_path, - Filename)), + {Handle, FileVersion, SummaryBin} = + open_reader(filename:join(State#state.root_path, Filename)), + UpdState0 = imp_fileversion(FileVersion, State), {Summary, SlotList} = read_table_summary(SummaryBin), BlockIndexCache = array:new([{size, Summary#summary.size}, {default, none}]), - UpdState = State#state{blockindex_cache = BlockIndexCache}, + UpdState1 = UpdState0#state{blockindex_cache = BlockIndexCache}, SlotIndex = from_list(SlotList), UpdSummary = Summary#summary{index = SlotIndex}, leveled_log:log("SST03", [Filename, Summary#summary.size, Summary#summary.max_sqn]), - UpdState#state{summary = UpdSummary, + UpdState1#state{summary = UpdSummary, handle = Handle, filename = Filename}. +gen_fileversion(PressMethod) -> + Bit1 = + case PressMethod of + lz4 -> 1; + native -> 0 + end, + Bit1. + +imp_fileversion(VersionInt, State) -> + UpdState = + case VersionInt band 1 of + 0 -> + State#state{compression_method = native}; + 1 -> + State#state{compression_method = lz4} + end, + UpdState. + open_reader(Filename) -> {ok, Handle} = file:open(Filename, [binary, raw, read]), - {ok, Lengths} = file:pread(Handle, 0, 8), - <> = Lengths, - {ok, SummaryBin} = file:pread(Handle, SlotsLength + 8, SummaryLength), - {Handle, SummaryBin}. + {ok, Lengths} = file:pread(Handle, 0, 9), + <> = Lengths, + {ok, SummaryBin} = file:pread(Handle, SlotsLength + 9, SummaryLength), + {Handle, FileVersion, SummaryBin}. build_table_summary(SlotIndex, _Level, FirstKey, SlotCount, MaxSQN) -> [{LastKey, _LastV}|_Rest] = SlotIndex, @@ -693,23 +748,26 @@ read_table_summary(BinWithCheck) -> end. -build_all_slots(SlotList) -> +build_all_slots(SlotList, PressMethod) -> SlotCount = length(SlotList), BuildResponse = build_all_slots(SlotList, - 8, + 9, 1, [], array:new([{size, SlotCount}, {default, none}]), - <<>>), + <<>>, + PressMethod), {SlotIndex, BlockIndex, SlotsBin} = BuildResponse, {SlotCount, SlotIndex, BlockIndex, SlotsBin}. build_all_slots([], _Pos, _SlotID, - SlotIdxAcc, BlockIdxAcc, SlotBinAcc) -> + SlotIdxAcc, BlockIdxAcc, SlotBinAcc, + _PressMethod) -> {SlotIdxAcc, BlockIdxAcc, SlotBinAcc}; build_all_slots([SlotD|Rest], Pos, SlotID, - SlotIdxAcc, BlockIdxAcc, SlotBinAcc) -> + SlotIdxAcc, BlockIdxAcc, SlotBinAcc, + PressMethod) -> {BlockIdx, SlotBin, HashList, LastKey} = SlotD, Length = byte_size(SlotBin), Bloom = leveled_tinybloom:create_bloom(HashList), @@ -722,7 +780,8 @@ build_all_slots([SlotD|Rest], Pos, SlotID, SlotID + 1, [{LastKey, SlotIndexV}|SlotIdxAcc], array:set(SlotID - 1, BlockIdx, BlockIdxAcc), - <>). + <>, + PressMethod). generate_filenames(RootFilename) -> @@ -740,26 +799,30 @@ generate_filenames(RootFilename) -> end. --spec serialise_block(any()) -> binary(). +-spec serialise_block(any(), lz4|native) -> binary(). %% @doc %% Convert term to binary %% Function split out to make it easier to experiment with different %% compression methods. Also, perhaps standardise applictaion of CRC %% checks -serialise_block(Term) -> +serialise_block(Term, lz4) -> {ok, Bin} = lz4:pack(term_to_binary(Term)), - Bin. + Bin; +serialise_block(Term, native) -> + term_to_binary(Term, ?BINARY_SETTINGS). --spec deserialise_block(binary()) -> any(). +-spec deserialise_block(binary(), lz4|native) -> any(). %% @doc %% Convert binary to term %% Function split out to make it easier to experiment with different %% compression methods. Also, perhaps standardise applictaion of CRC %% checks -deserialise_block(Bin) -> +deserialise_block(Bin, lz4) -> {ok, Bin0} = lz4:unpack(Bin), - binary_to_term(Bin0). + binary_to_term(Bin0); +deserialise_block(Bin, native) -> + binary_to_term(Bin). %%%============================================================================ @@ -823,7 +886,7 @@ lookup_slots(StartKey, EndKey, Tree) -> %% based on a 17-bit hash (so 0.0039 fpr). -generate_binary_slot(Lookup, KVL) -> +generate_binary_slot(Lookup, KVL, PressMethod) -> HashFoldFun = fun({K, V}, {PosBinAcc, NoHashCount, HashAcc}) -> @@ -887,45 +950,45 @@ generate_binary_slot(Lookup, KVL) -> {B1, B2, B3, B4, B5} = case length(KVL) of L when L =< SideBlockSize -> - {serialise_block(KVL), + {serialise_block(KVL, PressMethod), <<0:0>>, <<0:0>>, <<0:0>>, <<0:0>>}; L when L =< 2 * SideBlockSize -> {KVLA, KVLB} = lists:split(SideBlockSize, KVL), - {serialise_block(KVLA), - serialise_block(KVLB), + {serialise_block(KVLA, PressMethod), + serialise_block(KVLB, PressMethod), <<0:0>>, <<0:0>>, <<0:0>>}; L when L =< (2 * SideBlockSize + MidBlockSize) -> {KVLA, KVLB_Rest} = lists:split(SideBlockSize, KVL), {KVLB, KVLC} = lists:split(SideBlockSize, KVLB_Rest), - {serialise_block(KVLA), - serialise_block(KVLB), - serialise_block(KVLC), + {serialise_block(KVLA, PressMethod), + serialise_block(KVLB, PressMethod), + serialise_block(KVLC, PressMethod), <<0:0>>, <<0:0>>}; L when L =< (3 * SideBlockSize + MidBlockSize) -> {KVLA, KVLB_Rest} = lists:split(SideBlockSize, KVL), {KVLB, KVLC_Rest} = lists:split(SideBlockSize, KVLB_Rest), {KVLC, KVLD} = lists:split(MidBlockSize, KVLC_Rest), - {serialise_block(KVLA), - serialise_block(KVLB), - serialise_block(KVLC), - serialise_block(KVLD), + {serialise_block(KVLA, PressMethod), + serialise_block(KVLB, PressMethod), + serialise_block(KVLC, PressMethod), + serialise_block(KVLD, PressMethod), <<0:0>>}; L when L =< (4 * SideBlockSize + MidBlockSize) -> {KVLA, KVLB_Rest} = lists:split(SideBlockSize, KVL), {KVLB, KVLC_Rest} = lists:split(SideBlockSize, KVLB_Rest), {KVLC, KVLD_Rest} = lists:split(MidBlockSize, KVLC_Rest), {KVLD, KVLE} = lists:split(SideBlockSize, KVLD_Rest), - {serialise_block(KVLA), - serialise_block(KVLB), - serialise_block(KVLC), - serialise_block(KVLD), - serialise_block(KVLE)} + {serialise_block(KVLA, PressMethod), + serialise_block(KVLB, PressMethod), + serialise_block(KVLC, PressMethod), + serialise_block(KVLD, PressMethod), + serialise_block(KVLE, PressMethod)} end, B1P = byte_size(PosBinIndex), @@ -951,18 +1014,21 @@ generate_binary_slot(Lookup, KVL) -> {<>, FullBin, HashL, LastKey}. -check_blocks([], _Handle, _Slot, _BlockLengths, _LedgerKey) -> +check_blocks([], _Handle, _Slot, _BlockLengths, _LedgerKey, _PressMethod) -> not_present; -check_blocks([Pos|Rest], Handle, Slot, BlockLengths, LedgerKey) -> +check_blocks([Pos|Rest], Handle, Slot, BlockLengths, LedgerKey, PressMethod) -> {BlockNumber, BlockPos} = revert_position(Pos), BlockBin = read_block(Handle, Slot, BlockLengths, BlockNumber), - BlockL = deserialise_block(BlockBin), + BlockL = deserialise_block(BlockBin, PressMethod), {K, V} = lists:nth(BlockPos, BlockL), case K of LedgerKey -> {K, V}; _ -> - check_blocks(Rest, Handle, Slot, BlockLengths, LedgerKey) + check_blocks(Rest, + Handle, Slot, BlockLengths, + LedgerKey, + PressMethod) end. @@ -1018,7 +1084,7 @@ read_slots(Handle, SlotList) -> lists:map(BinSplitMapFun, LengthList). -binaryslot_get(FullBin, Key, Hash) -> +binaryslot_get(FullBin, Key, Hash, PressMethod) -> case crc_check_slot(FullBin) of {BlockLengths, Rest} -> <> = BlockLengths, @@ -1027,7 +1093,7 @@ binaryslot_get(FullBin, Key, Hash) -> extra_hash(Hash), [], 0), - {fetch_value(PosList, BlockLengths, Blocks, Key), + {fetch_value(PosList, BlockLengths, Blocks, Key, PressMethod), BlockLengths, PosBinIndex}; crc_wonky -> @@ -1036,7 +1102,7 @@ binaryslot_get(FullBin, Key, Hash) -> none} end. -binaryslot_tolist(FullBin) -> +binaryslot_tolist(FullBin, PressMethod) -> BlockFetchFun = fun(Length, {Acc, Bin}) -> case Length of @@ -1044,7 +1110,7 @@ binaryslot_tolist(FullBin) -> {Acc, Bin}; _ -> <> = Bin, - {Acc ++ deserialise_block(Block), Rest} + {Acc ++ deserialise_block(Block, PressMethod), Rest} end end, @@ -1067,9 +1133,9 @@ binaryslot_tolist(FullBin) -> Out. -binaryslot_trimmedlist(FullBin, all, all) -> - binaryslot_tolist(FullBin); -binaryslot_trimmedlist(FullBin, StartKey, EndKey) -> +binaryslot_trimmedlist(FullBin, all, all, PressMethod) -> + binaryslot_tolist(FullBin, PressMethod); +binaryslot_trimmedlist(FullBin, StartKey, EndKey, PressMethod) -> LTrimFun = fun({K, _V}) -> K < StartKey end, RTrimFun = fun({K, _V}) -> not leveled_codec:endkey_passed(EndKey, K) end, @@ -1098,7 +1164,8 @@ binaryslot_trimmedlist(FullBin, StartKey, EndKey) -> 0 -> [Block1, Block2]; _ -> - MidBlockList = deserialise_block(MidBlock), + MidBlockList = + deserialise_block(MidBlock, PressMethod), {MidFirst, _} = lists:nth(1, MidBlockList), {MidLast, _} = lists:last(MidBlockList), Split = {StartKey > MidLast, @@ -1136,7 +1203,7 @@ binaryslot_trimmedlist(FullBin, StartKey, EndKey) -> BlockList = case is_binary(Block) of true -> - deserialise_block(Block); + deserialise_block(Block, PressMethod); false -> Block end, @@ -1212,21 +1279,21 @@ extra_hash({SegHash, _ExtraHash}) when is_integer(SegHash) -> extra_hash(NotHash) -> NotHash. -fetch_value([], _BlockLengths, _Blocks, _Key) -> +fetch_value([], _BlockLengths, _Blocks, _Key, _PressMethod) -> not_present; -fetch_value([Pos|Rest], BlockLengths, Blocks, Key) -> +fetch_value([Pos|Rest], BlockLengths, Blocks, Key, PressMethod) -> {BlockNumber, BlockPos} = revert_position(Pos), {_BlockPos, Offset, Length} = block_offsetandlength(BlockLengths, BlockNumber), <<_Pre:Offset/binary, Block:Length/binary, _Rest/binary>> = Blocks, - BlockL = deserialise_block(Block), + BlockL = deserialise_block(Block, PressMethod), {K, V} = lists:nth(BlockPos, BlockL), case K of Key -> {K, V}; _ -> - fetch_value(Rest, BlockLengths, Blocks, Key) + fetch_value(Rest, BlockLengths, Blocks, Key, PressMethod) end. @@ -1290,31 +1357,33 @@ find_pos(<<0:1/integer, NHC:7/integer, T/binary>>, Hash, PosList, Count) -> %% there are matching keys then the highest sequence number must be chosen and %% any lower sequence numbers should be compacted out of existence -merge_lists(KVList1) -> +merge_lists(KVList1, PressMethod) -> SlotCount = length(KVList1) div ?LOOK_SLOTSIZE, {[], [], - split_lists(KVList1, [], SlotCount), + split_lists(KVList1, [], SlotCount, PressMethod), element(1, lists:nth(1, KVList1))}. -split_lists([], SlotLists, 0) -> +split_lists([], SlotLists, 0, _PressMethod) -> lists:reverse(SlotLists); -split_lists(LastPuff, SlotLists, 0) -> - SlotD = generate_binary_slot(lookup, LastPuff), +split_lists(LastPuff, SlotLists, 0, PressMethod) -> + SlotD = generate_binary_slot(lookup, LastPuff, PressMethod), lists:reverse([SlotD|SlotLists]); -split_lists(KVList1, SlotLists, N) -> +split_lists(KVList1, SlotLists, N, PressMethod) -> {Slot, KVListRem} = lists:split(?LOOK_SLOTSIZE, KVList1), - SlotD = generate_binary_slot(lookup, Slot), - split_lists(KVListRem, [SlotD|SlotLists], N - 1). + SlotD = generate_binary_slot(lookup, Slot, PressMethod), + split_lists(KVListRem, [SlotD|SlotLists], N - 1, PressMethod). -merge_lists(KVList1, KVList2, LevelInfo) -> - merge_lists(KVList1, KVList2, LevelInfo, [], null, 0). +merge_lists(KVList1, KVList2, LevelInfo, PressMethod) -> + merge_lists(KVList1, KVList2, LevelInfo, [], null, 0, PressMethod). -merge_lists(KVList1, KVList2, _LI, SlotList, FirstKey, ?MAX_SLOTS) -> +merge_lists(KVList1, KVList2, _LI, SlotList, FirstKey, ?MAX_SLOTS, + _PressMethod) -> {KVList1, KVList2, lists:reverse(SlotList), FirstKey}; -merge_lists([], [], _LI, SlotList, FirstKey, _SlotCount) -> +merge_lists([], [], _LI, SlotList, FirstKey, _SlotCount, _PressMethod) -> {[], [], lists:reverse(SlotList), FirstKey}; -merge_lists(KVList1, KVList2, LI, SlotList, FirstKey, SlotCount) -> +merge_lists(KVList1, KVList2, LI, SlotList, FirstKey, SlotCount, + PressMethod) -> {KVRem1, KVRem2, Slot, FK0} = form_slot(KVList1, KVList2, LI, no_lookup, 0, [], FirstKey), case Slot of @@ -1324,15 +1393,17 @@ merge_lists(KVList1, KVList2, LI, SlotList, FirstKey, SlotCount) -> LI, SlotList, FK0, - SlotCount); + SlotCount, + PressMethod); {Lookup, KVL} -> - SlotD = generate_binary_slot(Lookup, KVL), + SlotD = generate_binary_slot(Lookup, KVL, PressMethod), merge_lists(KVRem1, KVRem2, LI, [SlotD|SlotList], FK0, - SlotCount + 1) + SlotCount + 1, + PressMethod) end. form_slot([], [], _LI, Type, _Size, Slot, FK) -> @@ -1545,7 +1616,7 @@ form_slot_test() -> ?assertMatch({[], [], {no_lookup, Slot}, {o, "B1", "K5", null}}, R1). merge_tombstonelist_test() -> - % Merge lists wiht nothing but tombstones + % Merge lists with nothing but tombstones SkippingKV1 = {{o, "B1", "K9995", null}, {9995, tomb, 1234567, {}}}, SkippingKV2 = {{o, "B1", "K9996", null}, {9996, tomb, 1234567, {}}}, SkippingKV3 = {{o, "B1", "K9997", null}, {9997, tomb, 1234567, {}}}, @@ -1553,7 +1624,8 @@ merge_tombstonelist_test() -> SkippingKV5 = {{o, "B1", "K9999", null}, {9999, tomb, 1234567, {}}}, R = merge_lists([SkippingKV1, SkippingKV3, SkippingKV5], [SkippingKV2, SkippingKV4], - {true, 9999999}), + {true, 9999999}, + native), ?assertMatch({[], [], [], null}, R). indexed_list_test() -> @@ -1564,7 +1636,8 @@ indexed_list_test() -> SW0 = os:timestamp(), - {_PosBinIndex1, FullBin, _HL, _LK} = generate_binary_slot(lookup, KVL1), + {_PosBinIndex1, FullBin, _HL, _LK} = + generate_binary_slot(lookup, KVL1, native), io:format(user, "Indexed list created slot in ~w microseconds of size ~w~n", [timer:now_diff(os:timestamp(), SW0), byte_size(FullBin)]), @@ -1592,7 +1665,8 @@ indexed_list_mixedkeys_test() -> KVL1 = lists:sublist(KVL0, 33), Keys = lists:ukeysort(1, generate_indexkeys(60) ++ KVL1), - {_PosBinIndex1, FullBin, _HL, _LK} = generate_binary_slot(lookup, Keys), + {_PosBinIndex1, FullBin, _HL, _LK} = + generate_binary_slot(lookup, Keys, native), {TestK1, TestV1} = lists:nth(4, KVL1), MH1 = leveled_codec:segment_hash(TestK1), @@ -1618,7 +1692,8 @@ indexed_list_mixedkeys2_test() -> IdxKeys2 = lists:ukeysort(1, generate_indexkeys(30)), % this isn't actually ordered correctly Keys = IdxKeys1 ++ KVL1 ++ IdxKeys2, - {_PosBinIndex1, FullBin, _HL, _LK} = generate_binary_slot(lookup, Keys), + {_PosBinIndex1, FullBin, _HL, _LK} = + generate_binary_slot(lookup, Keys, native), lists:foreach(fun({K, V}) -> MH = leveled_codec:segment_hash(K), test_binary_slot(FullBin, K, MH, {K, V}) @@ -1628,34 +1703,37 @@ indexed_list_mixedkeys2_test() -> indexed_list_allindexkeys_test() -> Keys = lists:sublist(lists:ukeysort(1, generate_indexkeys(150)), ?LOOK_SLOTSIZE), - {PosBinIndex1, FullBin, _HL, _LK} = generate_binary_slot(lookup, Keys), + {PosBinIndex1, FullBin, _HL, _LK} = + generate_binary_slot(lookup, Keys, native), EmptySlotSize = ?LOOK_SLOTSIZE - 1, ?assertMatch(<<_BL:24/binary, EmptySlotSize:8/integer>>, PosBinIndex1), % SW = os:timestamp(), - BinToList = binaryslot_tolist(FullBin), + BinToList = binaryslot_tolist(FullBin, native), % io:format(user, % "Indexed list flattened in ~w microseconds ~n", % [timer:now_diff(os:timestamp(), SW)]), ?assertMatch(Keys, BinToList), - ?assertMatch(Keys, binaryslot_trimmedlist(FullBin, all, all)). + ?assertMatch(Keys, binaryslot_trimmedlist(FullBin, all, all, native)). indexed_list_allindexkeys_nolookup_test() -> Keys = lists:sublist(lists:ukeysort(1, generate_indexkeys(1000)), ?NOLOOK_SLOTSIZE), - {PosBinIndex1, FullBin, _HL, _LK} = generate_binary_slot(no_lookup, Keys), + {PosBinIndex1, FullBin, _HL, _LK} = + generate_binary_slot(no_lookup, Keys, native), ?assertMatch(<<_BL:24/binary, 127:8/integer>>, PosBinIndex1), % SW = os:timestamp(), - BinToList = binaryslot_tolist(FullBin), + BinToList = binaryslot_tolist(FullBin, native), % io:format(user, % "Indexed list flattened in ~w microseconds ~n", % [timer:now_diff(os:timestamp(), SW)]), ?assertMatch(Keys, BinToList), - ?assertMatch(Keys, binaryslot_trimmedlist(FullBin, all, all)). + ?assertMatch(Keys, binaryslot_trimmedlist(FullBin, all, all, native)). indexed_list_allindexkeys_trimmed_test() -> Keys = lists:sublist(lists:ukeysort(1, generate_indexkeys(150)), ?LOOK_SLOTSIZE), - {PosBinIndex1, FullBin, _HL, _LK} = generate_binary_slot(lookup, Keys), + {PosBinIndex1, FullBin, _HL, _LK} = + generate_binary_slot(lookup, Keys, native), EmptySlotSize = ?LOOK_SLOTSIZE - 1, ?assertMatch(<<_BL:24/binary, EmptySlotSize:8/integer>>, PosBinIndex1), ?assertMatch(Keys, binaryslot_trimmedlist(FullBin, @@ -1666,26 +1744,27 @@ indexed_list_allindexkeys_trimmed_test() -> {i, "Bucket", {"t1_int", 99999}, - null})), + null}, + native)), {SK1, _} = lists:nth(10, Keys), {EK1, _} = lists:nth(100, Keys), R1 = lists:sublist(Keys, 10, 91), - O1 = binaryslot_trimmedlist(FullBin, SK1, EK1), + O1 = binaryslot_trimmedlist(FullBin, SK1, EK1, native), ?assertMatch(91, length(O1)), ?assertMatch(R1, O1), {SK2, _} = lists:nth(10, Keys), {EK2, _} = lists:nth(20, Keys), R2 = lists:sublist(Keys, 10, 11), - O2 = binaryslot_trimmedlist(FullBin, SK2, EK2), + O2 = binaryslot_trimmedlist(FullBin, SK2, EK2, native), ?assertMatch(11, length(O2)), ?assertMatch(R2, O2), {SK3, _} = lists:nth(?LOOK_SLOTSIZE - 1, Keys), {EK3, _} = lists:nth(?LOOK_SLOTSIZE, Keys), R3 = lists:sublist(Keys, ?LOOK_SLOTSIZE - 1, 2), - O3 = binaryslot_trimmedlist(FullBin, SK3, EK3), + O3 = binaryslot_trimmedlist(FullBin, SK3, EK3, native), ?assertMatch(2, length(O3)), ?assertMatch(R3, O3). @@ -1694,7 +1773,8 @@ indexed_list_mixedkeys_bitflip_test() -> KVL0 = lists:ukeysort(1, generate_randomkeys(1, 50, 1, 4)), KVL1 = lists:sublist(KVL0, 33), Keys = lists:ukeysort(1, generate_indexkeys(60) ++ KVL1), - {_PosBinIndex1, FullBin, _HL, LK} = generate_binary_slot(lookup, Keys), + {_PosBinIndex1, FullBin, _HL, LK} = + generate_binary_slot(lookup, Keys, native), ?assertMatch(LK, element(1, lists:last(Keys))), L = byte_size(FullBin), Byte1 = leveled_rand:uniform(L), @@ -1711,12 +1791,12 @@ indexed_list_mixedkeys_bitflip_test() -> MH1 = leveled_codec:segment_hash(TestK1), test_binary_slot(FullBin0, TestK1, MH1, not_present), - ToList = binaryslot_tolist(FullBin0), + ToList = binaryslot_tolist(FullBin0, native), ?assertMatch([], ToList), {SK1, _} = lists:nth(10, Keys), {EK1, _} = lists:nth(50, Keys), - O1 = binaryslot_trimmedlist(FullBin0, SK1, EK1), + O1 = binaryslot_trimmedlist(FullBin0, SK1, EK1, native), ?assertMatch(0, length(O1)), ?assertMatch([], O1). @@ -1724,7 +1804,7 @@ indexed_list_mixedkeys_bitflip_test() -> test_binary_slot(FullBin, Key, Hash, ExpectedValue) -> % SW = os:timestamp(), - {ReturnedValue, _BLs, _Idx} = binaryslot_get(FullBin, Key, Hash), + {ReturnedValue, _BLs, _Idx} = binaryslot_get(FullBin, Key, Hash, native), ?assertMatch(ExpectedValue, ReturnedValue). % io:format(user, "Fetch success in ~w microseconds ~n", % [timer:now_diff(os:timestamp(), SW)]). @@ -1737,8 +1817,10 @@ merge_test() -> KVL2 = lists:ukeysort(1, generate_randomkeys(1, N, 1, 20)), KVL3 = lists:ukeymerge(1, KVL1, KVL2), SW0 = os:timestamp(), - {ok, P1, {FK1, LK1}} = sst_new("../test/", "level1_src", 1, KVL1, 6000), - {ok, P2, {FK2, LK2}} = sst_new("../test/", "level2_src", 2, KVL2, 3000), + {ok, P1, {FK1, LK1}} = + sst_new("../test/", "level1_src", 1, KVL1, 6000, native), + {ok, P2, {FK2, LK2}} = + sst_new("../test/", "level2_src", 2, KVL2, 3000, native), ExpFK1 = element(1, lists:nth(1, KVL1)), ExpLK1 = element(1, lists:last(KVL1)), ExpFK2 = element(1, lists:nth(1, KVL2)), @@ -1749,7 +1831,8 @@ merge_test() -> ?assertMatch(ExpLK2, LK2), ML1 = [{next, #manifest_entry{owner = P1}, FK1}], ML2 = [{next, #manifest_entry{owner = P2}, FK2}], - NewR = sst_new("../test/", "level2_merge", ML1, ML2, false, 2, N * 2), + NewR = + sst_new("../test/", "level2_merge", ML1, ML2, false, 2, N * 2, native), {ok, P3, {{Rem1, Rem2}, FK3, LK3}} = NewR, ?assertMatch([], Rem1), ?assertMatch([], Rem2), @@ -1783,11 +1866,8 @@ simple_persisted_range_test() -> KVList1 = lists:ukeysort(1, KVList0), [{FirstKey, _FV}|_Rest] = KVList1, {LastKey, _LV} = lists:last(KVList1), - {ok, Pid, {FirstKey, LastKey}} = sst_new(RP, - Filename, - 1, - KVList1, - length(KVList1)), + {ok, Pid, {FirstKey, LastKey}} = + sst_new(RP, Filename, 1, KVList1, length(KVList1), native), {o, B, K, null} = LastKey, SK1 = {o, B, K, 0}, @@ -1836,11 +1916,8 @@ additional_range_test() -> [], lists:seq(?NOLOOK_SLOTSIZE + Gap + 1, 2 * ?NOLOOK_SLOTSIZE + Gap)), - {ok, - P1, - {{Rem1, Rem2}, - SK, - EK}} = sst_new("../test/", "range1_src", IK1, IK2, false, 1, 9999), + {ok, P1, {{Rem1, Rem2}, SK, EK}} = + sst_new("../test/", "range1_src", IK1, IK2, false, 1, 9999, native), ?assertMatch([], Rem1), ?assertMatch([], Rem2), ?assertMatch(SK, element(1, lists:nth(1, IK1))), @@ -1897,11 +1974,8 @@ simple_persisted_slotsize_test() -> ?LOOK_SLOTSIZE), [{FirstKey, _FV}|_Rest] = KVList1, {LastKey, _LV} = lists:last(KVList1), - {ok, Pid, {FirstKey, LastKey}} = sst_new(RP, - Filename, - 1, - KVList1, - length(KVList1)), + {ok, Pid, {FirstKey, LastKey}} = + sst_new(RP, Filename, 1, KVList1, length(KVList1), native), lists:foreach(fun({K, V}) -> ?assertMatch({K, V}, sst_get(Pid, K)) end, @@ -1915,11 +1989,8 @@ simple_persisted_test() -> KVList1 = lists:ukeysort(1, KVList0), [{FirstKey, _FV}|_Rest] = KVList1, {LastKey, _LV} = lists:last(KVList1), - {ok, Pid, {FirstKey, LastKey}} = sst_new(RP, - Filename, - 1, - KVList1, - length(KVList1)), + {ok, Pid, {FirstKey, LastKey}} = + sst_new(RP, Filename, 1, KVList1, length(KVList1), native), SW0 = os:timestamp(), lists:foreach(fun({K, V}) -> ?assertMatch({K, V}, sst_get(Pid, K)) diff --git a/test/end_to_end/basic_SUITE.erl b/test/end_to_end/basic_SUITE.erl index 3c2e550..4e82cc8 100644 --- a/test/end_to_end/basic_SUITE.erl +++ b/test/end_to_end/basic_SUITE.erl @@ -9,7 +9,8 @@ load_and_count/1, load_and_count_withdelete/1, space_clear_ondelete/1, - is_empty_test/1 + is_empty_test/1, + many_put_fetch_switchcompression/1 ]). all() -> [ @@ -20,7 +21,8 @@ all() -> [ load_and_count, load_and_count_withdelete, space_clear_ondelete, - is_empty_test + is_empty_test, + many_put_fetch_switchcompression ]. @@ -683,3 +685,49 @@ is_empty_test(_Config) -> true = sets:size(BLpd3()) == 0, ok = leveled_bookie:book_close(Bookie1). + + +many_put_fetch_switchcompression(_Config) -> + RootPath = testutil:reset_filestructure(), + StartOpts1 = [{root_path, RootPath}, + {max_pencillercachesize, 16000}, + {sync_strategy, riak_sync}, + {compression_method, native}], + {ok, Bookie1} = leveled_bookie:book_start(StartOpts1), + {TestObject, TestSpec} = testutil:generate_testobject(), + ok = testutil:book_riakput(Bookie1, TestObject, TestSpec), + testutil:check_forobject(Bookie1, TestObject), + ok = leveled_bookie:book_close(Bookie1), + StartOpts2 = [{root_path, RootPath}, + {max_journalsize, 500000000}, + {max_pencillercachesize, 32000}, + {sync_strategy, testutil:sync_strategy()}, + {compression_method, lz4}], + + %% Change compression method + {ok, Bookie2} = leveled_bookie:book_start(StartOpts2), + testutil:check_forobject(Bookie2, TestObject), + GenList = [2, 40002, 80002, 120002], + CLs = testutil:load_objects(40000, GenList, Bookie2, TestObject, + fun testutil:generate_smallobjects/2), + CL1A = lists:nth(1, CLs), + ChkListFixed = lists:nth(length(CLs), CLs), + testutil:check_forlist(Bookie2, CL1A), + ObjList2A = testutil:generate_objects(5000, 2), + testutil:riakload(Bookie2, ObjList2A), + ChkList2A = lists:sublist(lists:sort(ObjList2A), 1000), + testutil:check_forlist(Bookie2, ChkList2A), + testutil:check_forlist(Bookie2, ChkListFixed), + testutil:check_forobject(Bookie2, TestObject), + testutil:check_forlist(Bookie2, ChkList2A), + testutil:check_forlist(Bookie2, ChkListFixed), + testutil:check_forobject(Bookie2, TestObject), + ok = leveled_bookie:book_close(Bookie2), + + %% Change method back again + {ok, Bookie3} = leveled_bookie:book_start(StartOpts1), + testutil:check_forlist(Bookie3, ChkList2A), + testutil:check_forlist(Bookie3, ChkListFixed), + testutil:check_forobject(Bookie3, TestObject), + testutil:check_formissingobject(Bookie3, "Bookie1", "MissingKey0123"), + ok = leveled_bookie:book_destroy(Bookie3). \ No newline at end of file From 1d475235d18f8689e5ce609ef288d56d0ca93a1e Mon Sep 17 00:00:00 2001 From: Martin Sumner Date: Mon, 6 Nov 2017 18:44:08 +0000 Subject: [PATCH 10/13] Improve test coverage Make compress on receipt/compaction configurable --- include/leveled.hrl | 1 + src/leveled_bookie.erl | 12 +++++++++ src/leveled_codec.erl | 15 ++++++------ src/leveled_iclerk.erl | 6 +++-- src/leveled_inker.erl | 39 +++++++++++++++--------------- test/end_to_end/basic_SUITE.erl | 6 +++-- test/end_to_end/recovery_SUITE.erl | 12 ++++++--- 7 files changed, 56 insertions(+), 35 deletions(-) diff --git a/include/leveled.hrl b/include/leveled.hrl index aacb7eb..348467f 100644 --- a/include/leveled.hrl +++ b/include/leveled.hrl @@ -49,6 +49,7 @@ reload_strategy = [] :: list(), waste_retention_period :: integer() | undefined, compression_method :: lz4|native, + compress_on_receipt :: boolean(), max_run_length}). -record(penciller_options, diff --git a/src/leveled_bookie.erl b/src/leveled_bookie.erl index af1dd2b..60ed4bf 100644 --- a/src/leveled_bookie.erl +++ b/src/leveled_bookie.erl @@ -82,6 +82,7 @@ -define(LONG_RUNNING, 80000). -define(RECENT_AAE, false). -define(COMPRESSION_METHOD, lz4). +-define(COMPRESSION_POINT, on_receipt). -record(ledger_cache, {mem :: ets:tab(), loader = leveled_tree:empty(?CACHE_TYPE) @@ -923,12 +924,23 @@ set_options(Opts) -> % Must include lz4 library in rebar.config lz4 end, + CompressOnReceipt = + case get_opt(compression_point, Opts, ?COMPRESSION_POINT) of + on_receipt -> + % Note this will add measurable delay to PUT time + % https://github.com/martinsumner/leveled/issues/95 + true; + on_compact -> + % If using lz4 this is not recommended + false + end, {#inker_options{root_path = JournalFP, reload_strategy = ReloadStrategy, max_run_length = get_opt(max_run_length, Opts), waste_retention_period = WRP, compression_method = CompressionMethod, + compress_on_receipt = CompressOnReceipt, cdb_options = #cdb_options{max_size=MaxJournalSize, binary_mode=true, diff --git a/src/leveled_codec.erl b/src/leveled_codec.erl index 6232a02..401c709 100644 --- a/src/leveled_codec.erl +++ b/src/leveled_codec.erl @@ -46,7 +46,8 @@ to_ledgerkey/3, to_ledgerkey/5, from_ledgerkey/1, - to_inkerkv/5, + to_inkerkv/3, + to_inkerkv/6, from_inkerkv/1, from_inkerkv/2, from_journalkey/1, @@ -73,7 +74,6 @@ -define(LMD_FORMAT, "~4..0w~2..0w~2..0w~2..0w~2..0w"). -define(NRT_IDX, "$aae."). -define(ALL_BUCKETS, <<"$all">>). --define(COMPRESS_ON_RECEIPT, true). -type recent_aae() :: #recent_aae{}. -type riak_metadata() :: {binary()|delete, % Sibling Metadata @@ -215,14 +215,13 @@ to_ledgerkey(Bucket, Key, Tag) -> %% Return the Key, Value and Hash Option for this object. The hash option %% indicates whether the key would ever be looked up directly, and so if it %% requires an entry in the hash table -to_inkerkv(LedgerKey, SQN, to_fetch, null, _CompressionMethod) -> - {{SQN, ?INKT_STND, LedgerKey}, null, true}; -to_inkerkv(LedgerKey, SQN, Object, KeyChanges, CompressionMethod) -> +to_inkerkv(LedgerKey, SQN, to_fetch) -> + {{SQN, ?INKT_STND, LedgerKey}, null, true}. + +to_inkerkv(LedgerKey, SQN, Object, KeyChanges, PressMethod, Compress) -> InkerType = check_forinkertype(LedgerKey, Object), Value = - create_value_for_journal({Object, KeyChanges}, - ?COMPRESS_ON_RECEIPT, - CompressionMethod), + create_value_for_journal({Object, KeyChanges}, Compress, PressMethod), {{SQN, InkerType, LedgerKey}, Value}. %% Used when fetching objects, so only handles standard, hashable entries diff --git a/src/leveled_iclerk.erl b/src/leveled_iclerk.erl index 81aa36b..9663328 100644 --- a/src/leveled_iclerk.erl +++ b/src/leveled_iclerk.erl @@ -757,7 +757,8 @@ test_ledgerkey(Key) -> {o, "Bucket", Key, null}. test_inkerkv(SQN, Key, V, IdxSpecs) -> - leveled_codec:to_inkerkv(test_ledgerkey(Key), SQN, V, IdxSpecs, native). + leveled_codec:to_inkerkv(test_ledgerkey(Key), SQN, V, IdxSpecs, + native, false). fetch_testcdb(RP) -> FN1 = leveled_inker:filepath(RP, 1, new_journal), @@ -940,7 +941,8 @@ compact_singlefile_totwosmallfiles_testto() -> LK = test_ledgerkey("Key" ++ integer_to_list(X)), Value = leveled_rand:rand_bytes(1024), {IK, IV} = - leveled_codec:to_inkerkv(LK, X, Value, [], native), + leveled_codec:to_inkerkv(LK, X, Value, [], + native, true), ok = leveled_cdb:cdb_put(CDB1, IK, IV) end, lists:seq(1, 1000)), diff --git a/src/leveled_inker.erl b/src/leveled_inker.erl index 85da448..9859aa2 100644 --- a/src/leveled_inker.erl +++ b/src/leveled_inker.erl @@ -137,6 +137,7 @@ compaction_pending = false :: boolean(), is_snapshot = false :: boolean(), compression_method :: lz4|native, + compress_on_receipt :: boolean(), source_inker :: pid() | undefined}). @@ -510,12 +511,13 @@ start_from_file(InkOpts) -> ReloadStrategy = InkOpts#inker_options.reload_strategy, MRL = InkOpts#inker_options.max_run_length, WRP = InkOpts#inker_options.waste_retention_period, - Compression = InkOpts#inker_options.compression_method, + PressMethod = InkOpts#inker_options.compression_method, + PressOnReceipt = InkOpts#inker_options.compress_on_receipt, IClerkOpts = #iclerk_options{inker = self(), cdb_options=IClerkCDBOpts, waste_retention_period = WRP, reload_strategy = ReloadStrategy, - compression_method = Compression, + compression_method = PressMethod, max_run_length = MRL}, {ok, Clerk} = leveled_iclerk:clerk_new(IClerkOpts), @@ -531,7 +533,8 @@ start_from_file(InkOpts) -> active_journaldb = ActiveJournal, root_path = RootPath, cdb_options = CDBopts#cdb_options{waste_path=WasteFP}, - compression_method = Compression, + compression_method = PressMethod, + compress_on_receipt = PressOnReceipt, clerk = Clerk}}. @@ -543,7 +546,8 @@ put_object(LedgerKey, Object, KeyChanges, State) -> NewSQN, Object, KeyChanges, - State#state.compression_method), + State#state.compression_method, + State#state.compress_on_receipt), case leveled_cdb:cdb_put(ActiveJournal, JournalKey, JournalBin) of @@ -586,22 +590,14 @@ get_object(LedgerKey, SQN, Manifest) -> get_object(LedgerKey, SQN, Manifest, ToIgnoreKeyChanges) -> JournalP = leveled_imanifest:find_entry(SQN, Manifest), {InkerKey, _V, true} = - leveled_codec:to_inkerkv(LedgerKey, - SQN, - to_fetch, - null, - not_applicable), + leveled_codec:to_inkerkv(LedgerKey, SQN, to_fetch), Obj = leveled_cdb:cdb_get(JournalP, InkerKey), leveled_codec:from_inkerkv(Obj, ToIgnoreKeyChanges). key_check(LedgerKey, SQN, Manifest) -> JournalP = leveled_imanifest:find_entry(SQN, Manifest), {InkerKey, _V, true} = - leveled_codec:to_inkerkv(LedgerKey, - SQN, - to_fetch, - null, - not_applicable), + leveled_codec:to_inkerkv(LedgerKey, SQN, to_fetch), leveled_cdb:cdb_keycheck(JournalP, InkerKey). build_manifest(ManifestFilenames, @@ -944,7 +940,8 @@ simple_inker_test() -> CDBopts = #cdb_options{max_size=300000, binary_mode=true}, {ok, Ink1} = ink_start(#inker_options{root_path=RootPath, cdb_options=CDBopts, - compression_method=native}), + compression_method=native, + compress_on_receipt=true}), Obj1 = ink_get(Ink1, "Key1", 1), ?assertMatch({{1, "Key1"}, {"TestValue1", []}}, Obj1), Obj3 = ink_get(Ink1, "Key1", 3), @@ -967,7 +964,8 @@ simple_inker_completeactivejournal_test() -> ok = file:rename(F1, F1r), {ok, Ink1} = ink_start(#inker_options{root_path=RootPath, cdb_options=CDBopts, - compression_method=native}), + compression_method=native, + compress_on_receipt=true}), Obj1 = ink_get(Ink1, "Key1", 1), ?assertMatch({{1, "Key1"}, {"TestValue1", []}}, Obj1), Obj2 = ink_get(Ink1, "Key4", 4), @@ -986,7 +984,8 @@ compact_journal_test() -> {ok, Ink1} = ink_start(#inker_options{root_path=RootPath, cdb_options=CDBopts, reload_strategy=RStrategy, - compression_method=native}), + compression_method=native, + compress_on_receipt=false}), {ok, NewSQN1, _ObjSize} = ink_put(Ink1, test_ledgerkey("KeyAA"), "TestValueAA", @@ -1053,7 +1052,8 @@ empty_manifest_test() -> CDBopts = #cdb_options{max_size=300000}, {ok, Ink1} = ink_start(#inker_options{root_path=RootPath, cdb_options=CDBopts, - compression_method=native}), + compression_method=native, + compress_on_receipt=true}), ?assertMatch(not_present, ink_fetch(Ink1, "Key1", 1)), CheckFun = fun(L, K, SQN) -> lists:member({SQN, K}, L) end, @@ -1074,7 +1074,8 @@ empty_manifest_test() -> {ok, Ink2} = ink_start(#inker_options{root_path=RootPath, cdb_options=CDBopts, - compression_method=native}), + compression_method=native, + compress_on_receipt=false}), ?assertMatch(not_present, ink_fetch(Ink2, "Key1", 1)), {ok, SQN, Size} = ink_put(Ink2, "Key1", "Value1", {[], infinity}), ?assertMatch(2, SQN), diff --git a/test/end_to_end/basic_SUITE.erl b/test/end_to_end/basic_SUITE.erl index 4e82cc8..ec59e54 100644 --- a/test/end_to_end/basic_SUITE.erl +++ b/test/end_to_end/basic_SUITE.erl @@ -74,7 +74,8 @@ many_put_fetch_head(_Config) -> RootPath = testutil:reset_filestructure(), StartOpts1 = [{root_path, RootPath}, {max_pencillercachesize, 16000}, - {sync_strategy, riak_sync}], + {sync_strategy, riak_sync}, + {compression_point, on_compact}], {ok, Bookie1} = leveled_bookie:book_start(StartOpts1), {TestObject, TestSpec} = testutil:generate_testobject(), ok = testutil:book_riakput(Bookie1, TestObject, TestSpec), @@ -83,7 +84,8 @@ many_put_fetch_head(_Config) -> StartOpts2 = [{root_path, RootPath}, {max_journalsize, 500000000}, {max_pencillercachesize, 32000}, - {sync_strategy, testutil:sync_strategy()}], + {sync_strategy, testutil:sync_strategy()}, + {compression_point, on_receipt}], {ok, Bookie2} = leveled_bookie:book_start(StartOpts2), testutil:check_forobject(Bookie2, TestObject), GenList = [2, 20002, 40002, 60002, 80002, diff --git a/test/end_to_end/recovery_SUITE.erl b/test/end_to_end/recovery_SUITE.erl index 8df2e21..55e4c2c 100644 --- a/test/end_to_end/recovery_SUITE.erl +++ b/test/end_to_end/recovery_SUITE.erl @@ -307,17 +307,20 @@ aae_bustedjournal(_Config) -> journal_compaction_bustedjournal(_Config) -> % Different circumstances will be created in different runs - busted_journal_test(10000000), - busted_journal_test(7777777). + busted_journal_test(10000000, native, on_receipt), + busted_journal_test(7777777, native, on_compact), + busted_journal_test(8888888, lz4, on_receipt). -busted_journal_test(MaxJournalSize) -> +busted_journal_test(MaxJournalSize, PressMethod, PressPoint) -> % Simply confirms that none of this causes a crash RootPath = testutil:reset_filestructure(), StartOpts1 = [{root_path, RootPath}, {max_journalsize, MaxJournalSize}, {max_run_length, 10}, - {sync_strategy, testutil:sync_strategy()}], + {sync_strategy, testutil:sync_strategy()}, + {compression_method, PressMethod}, + {compression_point, PressPoint}], {ok, Bookie1} = leveled_bookie:book_start(StartOpts1), {TestObject, TestSpec} = testutil:generate_testobject(), ok = testutil:book_riakput(Bookie1, TestObject, TestSpec), @@ -360,6 +363,7 @@ busted_journal_test(MaxJournalSize) -> testutil:reset_filestructure(10000). + rotating_object_check(BookOpts, B, NumberOfObjects) -> {ok, Book1} = leveled_bookie:book_start(BookOpts), {KSpcL1, V1} = testutil:put_indexed_objects(Book1, B, NumberOfObjects), From 9a0a4ced0dc642689cfffbd799f7d9ad3822525f Mon Sep 17 00:00:00 2001 From: Martin Sumner Date: Mon, 6 Nov 2017 19:42:35 +0000 Subject: [PATCH 11/13] Test LZ4 from uncompressed Coverage issue --- test/end_to_end/recovery_SUITE.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/end_to_end/recovery_SUITE.erl b/test/end_to_end/recovery_SUITE.erl index 55e4c2c..33dc977 100644 --- a/test/end_to_end/recovery_SUITE.erl +++ b/test/end_to_end/recovery_SUITE.erl @@ -308,7 +308,7 @@ aae_bustedjournal(_Config) -> journal_compaction_bustedjournal(_Config) -> % Different circumstances will be created in different runs busted_journal_test(10000000, native, on_receipt), - busted_journal_test(7777777, native, on_compact), + busted_journal_test(7777777, lz4, on_compact), busted_journal_test(8888888, lz4, on_receipt). From f358bd7622a01253700ff1874cb8b9f0f8c82ced Mon Sep 17 00:00:00 2001 From: Martin Sumner Date: Mon, 6 Nov 2017 21:16:46 +0000 Subject: [PATCH 12/13] Switch to using passed in compression method for maybe_compress When the compaction discovers compression is required it will used the passed in method at startup - not the method which had been previously defined. --- src/leveled_codec.erl | 12 ++++------ src/leveled_iclerk.erl | 35 ++++++++++++++++++------------ test/end_to_end/recovery_SUITE.erl | 26 ++++++++++++++-------- 3 files changed, 42 insertions(+), 31 deletions(-) diff --git a/src/leveled_codec.erl b/src/leveled_codec.erl index 401c709..be65bf8 100644 --- a/src/leveled_codec.erl +++ b/src/leveled_codec.erl @@ -54,7 +54,7 @@ compact_inkerkvc/2, split_inkvalue/1, check_forinkertype/2, - maybe_compress/1, + maybe_compress/2, create_value_for_journal/3, build_metadata_object/2, generate_ledgerkv/5, @@ -251,9 +251,9 @@ create_value_for_journal({Object, KeyChangeBin}, Compress, Method) -> KeyChangeBinLen:32/integer, TypeCode:8/integer>>. -maybe_compress({null, KeyChanges}) -> +maybe_compress({null, KeyChanges}, _PressMethod) -> create_value_for_journal({null, KeyChanges}, false, native); -maybe_compress(JournalBin) -> +maybe_compress(JournalBin, PressMethod) -> Length0 = byte_size(JournalBin) - 5, < <> = JBin0, V0 = {deserialise_object(OBin2, IsBinary, IsCompressed, IsLz4), binary_to_term(KCBin2)}, - PressMethod = case IsLz4 of - true -> lz4; - false -> native - end, create_value_for_journal(V0, true, PressMethod) end. @@ -340,7 +336,7 @@ encode_valuetype(IsBinary, IsCompressed, Method) -> decode_valuetype(TypeInt) -> IsCompressed = TypeInt band 1 == 1, IsBinary = TypeInt band 2 == 2, - IsLz4 = TypeInt band 4 ==4, + IsLz4 = TypeInt band 4 == 4, {IsBinary, IsCompressed, IsLz4}. from_journalkey({SQN, _Type, LedgerKey}) -> diff --git a/src/leveled_iclerk.erl b/src/leveled_iclerk.erl index 9663328..f60de64 100644 --- a/src/leveled_iclerk.erl +++ b/src/leveled_iclerk.erl @@ -197,7 +197,8 @@ handle_cast({compact, Checker, InitiateFun, CloseFun, FilterFun, Inker, _TO}, FilterFun, FilterServer, MaxSQN, - State#state.reload_strategy), + State#state.reload_strategy, + State#state.compression_method), FilesToDelete = lists:map(fun(C) -> {C#candidate.low_sqn, C#candidate.filename, @@ -493,7 +494,8 @@ update_inker(Inker, ManifestSlice, FilesToDelete) -> FilesToDelete), ok. -compact_files(BestRun, CDBopts, FilterFun, FilterServer, MaxSQN, RStrategy) -> +compact_files(BestRun, CDBopts, FilterFun, FilterServer, + MaxSQN, RStrategy, PressMethod) -> BatchesOfPositions = get_all_positions(BestRun, []), compact_files(BatchesOfPositions, CDBopts, @@ -502,19 +504,20 @@ compact_files(BestRun, CDBopts, FilterFun, FilterServer, MaxSQN, RStrategy) -> FilterServer, MaxSQN, RStrategy, + PressMethod, []). compact_files([], _CDBopts, null, _FilterFun, _FilterServer, _MaxSQN, - _RStrategy, ManSlice0) -> + _RStrategy, _PressMethod, ManSlice0) -> ManSlice0; compact_files([], _CDBopts, ActiveJournal0, _FilterFun, _FilterServer, _MaxSQN, - _RStrategy, ManSlice0) -> + _RStrategy, _PressMethod, ManSlice0) -> ManSlice1 = ManSlice0 ++ leveled_imanifest:generate_entry(ActiveJournal0), ManSlice1; compact_files([Batch|T], CDBopts, ActiveJournal0, FilterFun, FilterServer, MaxSQN, - RStrategy, ManSlice0) -> + RStrategy, PressMethod, ManSlice0) -> {SrcJournal, PositionList} = Batch, KVCs0 = leveled_cdb:cdb_directfetch(SrcJournal, PositionList, @@ -527,9 +530,10 @@ compact_files([Batch|T], CDBopts, ActiveJournal0, {ActiveJournal1, ManSlice1} = write_values(KVCs1, CDBopts, ActiveJournal0, - ManSlice0), + ManSlice0, + PressMethod), compact_files(T, CDBopts, ActiveJournal1, FilterFun, FilterServer, MaxSQN, - RStrategy, ManSlice1). + RStrategy, PressMethod, ManSlice1). get_all_positions([], PositionBatches) -> PositionBatches; @@ -580,12 +584,12 @@ filter_output(KVCs, FilterFun, FilterServer, MaxSQN, ReloadStrategy) -> KVCs). -write_values([], _CDBopts, Journal0, ManSlice0) -> +write_values([], _CDBopts, Journal0, ManSlice0, _PressMethod) -> {Journal0, ManSlice0}; -write_values(KVCList, CDBopts, Journal0, ManSlice0) -> +write_values(KVCList, CDBopts, Journal0, ManSlice0, PressMethod) -> KVList = lists:map(fun({K, V, _C}) -> % Compress the value as part of compaction - {K, leveled_codec:maybe_compress(V)} + {K, leveled_codec:maybe_compress(V, PressMethod)} end, KVCList), {ok, Journal1} = case Journal0 of @@ -608,7 +612,7 @@ write_values(KVCList, CDBopts, Journal0, ManSlice0) -> {Journal1, ManSlice0}; roll -> ManSlice1 = ManSlice0 ++ leveled_imanifest:generate_entry(Journal1), - write_values(KVCList, CDBopts, null, ManSlice1) + write_values(KVCList, CDBopts, null, ManSlice1, PressMethod) end. clear_waste(State) -> @@ -843,7 +847,8 @@ compact_single_file_recovr_test() -> LedgerFun1, LedgerSrv1, 9, - [{?STD_TAG, recovr}]), + [{?STD_TAG, recovr}], + native), io:format("FN of ~s~n", [FN]), ?assertMatch(2, LowSQN), ?assertMatch(probably, @@ -880,7 +885,8 @@ compact_single_file_retain_test() -> LedgerFun1, LedgerSrv1, 9, - [{?STD_TAG, retain}]), + [{?STD_TAG, retain}], + native), io:format("FN of ~s~n", [FN]), ?assertMatch(1, LowSQN), ?assertMatch(probably, @@ -961,7 +967,8 @@ compact_singlefile_totwosmallfiles_testto() -> FakeFilterFun, null, 900, - [{?STD_TAG, recovr}]), + [{?STD_TAG, recovr}], + native), ?assertMatch(2, length(ManifestSlice)), lists:foreach(fun({_SQN, _FN, CDB, _LK}) -> ok = leveled_cdb:cdb_deletepending(CDB), diff --git a/test/end_to_end/recovery_SUITE.erl b/test/end_to_end/recovery_SUITE.erl index 33dc977..dd92716 100644 --- a/test/end_to_end/recovery_SUITE.erl +++ b/test/end_to_end/recovery_SUITE.erl @@ -307,12 +307,13 @@ aae_bustedjournal(_Config) -> journal_compaction_bustedjournal(_Config) -> % Different circumstances will be created in different runs - busted_journal_test(10000000, native, on_receipt), - busted_journal_test(7777777, lz4, on_compact), - busted_journal_test(8888888, lz4, on_receipt). + busted_journal_test(10000000, native, on_receipt, true), + busted_journal_test(7777777, lz4, on_compact, true), + busted_journal_test(8888888, lz4, on_receipt, true), + busted_journal_test(7777777, lz4, on_compact, false). -busted_journal_test(MaxJournalSize, PressMethod, PressPoint) -> +busted_journal_test(MaxJournalSize, PressMethod, PressPoint, Bust) -> % Simply confirms that none of this causes a crash RootPath = testutil:reset_filestructure(), StartOpts1 = [{root_path, RootPath}, @@ -336,11 +337,18 @@ busted_journal_test(MaxJournalSize, PressMethod, PressPoint) -> ObjList2), ok = leveled_bookie:book_close(Bookie1), - CDBFiles = testutil:find_journals(RootPath), - lists:foreach(fun(FN) -> - testutil:corrupt_journal(RootPath, FN, 100, 2048, 1000) - end, - CDBFiles), + case Bust of + true -> + CDBFiles = testutil:find_journals(RootPath), + lists:foreach(fun(FN) -> + testutil:corrupt_journal(RootPath, + FN, + 100, 2048, 1000) + end, + CDBFiles); + false -> + ok + end, {ok, Bookie2} = leveled_bookie:book_start(StartOpts1), From 0af0d852394b5d271d23e3180da6c2362c50801c Mon Sep 17 00:00:00 2001 From: Martin Sumner Date: Tue, 7 Nov 2017 10:22:27 +0000 Subject: [PATCH 13/13] Add option description Add documentation of new options --- src/leveled_bookie.erl | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/src/leveled_bookie.erl b/src/leveled_bookie.erl index 60ed4bf..1e291d1 100644 --- a/src/leveled_bookie.erl +++ b/src/leveled_bookie.erl @@ -145,19 +145,34 @@ book_start(RootPath, LedgerCacheSize, JournalSize, SyncStrategy) -> %% @doc Start a Leveled Key/Value store - full options support. %% %% Allows an options proplists to be passed for setting options. There are -%% two primary additional options this allows over book_start/4: +%% four primary additional options this allows over book_start/4: %% - retain_strategy %% - waste_retention_period +%% - compression_method +%% - compression_point %% -%% Both of these relate to compaction in the Journal. The retain_strategy -%% determines if a skinny record of the object should be retained following -%% compaction, and how thta should be used when recovering lost state in the -%% Ledger. +%% Both of the first two options relate to compaction in the Journal. The +%% retain_strategydetermines if a skinny record of the object should be +%% retained following compaction, and how that should be used when recovering +%% lost state in the Ledger. +%% +%% This is relevant to when Riak uses Leveled in that KeyChanges are presented +%% by the vnode to the backend as deltas. This means that if those key +%% changes do not remain recorded in the journal once the value has been +%% compacted - rebuilding the ledger from the Journal would lead to incorrect +%% index entries being present. %% %% Currently compacted records no longer in use are not removed but moved to %% a journal_waste folder, and the waste_retention_period determines how long %% this history should be kept for (for example to allow for it to be backed -%% up before deletion) +%% up before deletion). +%% +%% Compression method and point allow Leveled to be switched from using bif +%% based compression (zlib) to suing nif based compression (lz4). The +%% compression point can be changed between on_receipt (all values are +%% compressed as they are received), to on_compact where values are originally +%% stored uncompressed (speeding PUT times), and are only compressed when +%% they are first subject to compaction %% %% TODO: %% The reload_strategy is exposed as currently no firm decision has been made