Packages
glazer
0.5.10
0.5.17
0.5.16
0.5.15
0.5.14
retired
0.5.13
retired
0.5.12
retired
0.5.11
retired
0.5.10
retired
0.5.9
retired
0.5.8
retired
0.5.7
retired
0.5.6
retired
0.5.5
retired
0.5.4
retired
0.5.3
retired
0.5.2
retired
0.5.1
retired
0.5.0
retired
0.3.2
retired
0.3.0
retired
0.2.6
retired
0.2.5
retired
0.2.4
retired
0.2.3
retired
0.2.2
retired
0.2.1
retired
0.2.0
retired
0.1.7
retired
0.1.6
retired
0.1.5
retired
0.1.4
retired
Erlang NIF JSON encoder/decoder using the glaze C++ library
Retired package: Deprecated - Deprecated
Current section
Files
Jump to
Current section
Files
Makefile
ifndef VERBOSE
MAKEFLAGS += --no-print-directory
endif
PRIV_DIR := $(abspath priv)
OBJ_DIR := $(abspath obj)
DEBUG ?= 0
REBAR ?= rebar3
APP := $(shell sed -nE 's/^\{application, ([a-zA-Z0-9_]+),.*/\1/p' src/*.app.src | head -n1)
OPTIMIZE ?= 0
ifneq ($(filter $(OPTIMIZE),1 true),)
override OPTIMIZE := 1
else
override OPTIMIZE := 0
endif
all: compile
help:
@echo "Usage: make [target] [VAR=value ...]"
@echo ""
@echo "Build targets:"
@echo " all Build NIF and compile Erlang (default)"
@echo " nif Build only the C NIF shared library"
@echo " compile Build NIF + run rebar3 compile"
@echo " optimize PGO build: instrument → benchmark → rebuild (fastest binary)"
@echo " clean Remove build artifacts"
@echo " distclean Remove all generated files including deps"
@echo ""
@echo "Development:"
@echo " test Run eunit test suite"
@echo " cover Run eunit with coverage analysis and print a summary"
@echo " check Run xref and dialyzer"
@echo " dialyzer Run dialyzer"
@echo " memcheck Build with ASan (-fsanitize=address) and run eunit (leak=1 adds LSan)"
@echo " benchmark Run benchmarks via mix bench"
@echo " deps Fetch mix dependencies"
@echo " doc Generate documentation"
@echo ""
@echo "Publishing:"
@echo " bump-version Increment patch version and commit"
@echo " publish Publish to hex.pm (pass replace=1 to replace existing)"
@echo " deprecate Deprecate a hex.pm release (pass vsn=X.Y.Z)"
@echo ""
@echo "Variables:"
@echo " DEBUG=1 Build NIF without optimisations (-O0 -g)"
@echo " ASAN=1 Build with AddressSanitizer (implied by memcheck)"
@echo " leak=1 Enable LeakSanitizer during memcheck (off by default)"
@echo " VERBOSE=1 Show full compiler command lines"
@echo " OPTIMIZE=1 Make all/compile run the PGO 'optimize' build (same as 'make optimize')"
nif: $(PRIV_DIR)/glazer.so
$(PRIV_DIR)/glazer.so: $(wildcard c_src/*.cpp c_src/*.hpp)
@$(MAKE) -C c_src PRIV_DIR=$(PRIV_DIR) OBJ_DIR=$(OBJ_DIR) DEBUG=$(DEBUG) \
$(if $(VERBOSE),VERBOSE=1,) --no-print-directory
ifeq ($(OPTIMIZE),1)
compile: optimize
else
compile: $(PRIV_DIR)/glazer.so
$(REBAR) compile
endif
clean:
$(REBAR) clean
@$(MAKE) --no-print-directory -C c_src PRIV_DIR=$(PRIV_DIR) OBJ_DIR=$(OBJ_DIR) DEBUG=$(DEBUG) clean 2>/dev/null || true
distclean: clean
@rm -rf obj priv/glazer.so _build .perf.txt
test:
$(REBAR) eunit
# Locate the ASan runtime for LD_PRELOAD on Linux.
# macOS memcheck is not supported (DYLD_INSERT_LIBRARIES is dropped by the
# rebar3 shell wrapper before reaching erl), so this block is Linux-only.
# Try Clang's resource-dir path first; fall back to GCC's -print-file-name.
_CXX_RESOURCE_DIR := $(shell $(CXX) -print-resource-dir 2>/dev/null)
_HOST_ARCH := $(shell uname -m)
_ASAN_CLANG := $(_CXX_RESOURCE_DIR)/lib/linux/libclang_rt.asan-$(_HOST_ARCH).so
_ASAN_GCC := $(shell $(CXX) -print-file-name=libasan.so 2>/dev/null)
ASAN_RT := $(strip $(if $(wildcard $(_ASAN_CLANG)),$(_ASAN_CLANG),\
$(if $(filter-out $(notdir $(_ASAN_GCC)),$(_ASAN_GCC)),$(_ASAN_GCC))))
ASAN_PRELOAD = LD_PRELOAD="$(ASAN_RT)"
LSAN_SUPPRESSIONS := $(abspath c_src/lsan_suppressions.txt)
leak ?= 0
ifeq ($(leak),0)
DETECT_LEAKS := 0
else
DETECT_LEAKS := 1
endif
check:
$(REBAR) xref
$(REBAR) dialyzer
memcheck:
@echo "==> Building NIF with AddressSanitizer"
@$(MAKE) -C c_src PRIV_DIR=$(PRIV_DIR) OBJ_DIR=$(OBJ_DIR) ASAN=1 \
$(if $(VERBOSE),VERBOSE=1,) clean all
@$(REBAR) compile
@echo "==> Running eunit under ASan$(if $(filter 1,$(DETECT_LEAKS)), + LeakSanitizer,) ($(ASAN_PRELOAD))"
ERL_FLAGS="+A 1" ASAN_OPTIONS="detect_leaks=$(DETECT_LEAKS)" \
LSAN_OPTIONS="suppressions=$(LSAN_SUPPRESSIONS)" \
$(ASAN_PRELOAD) \
$(REBAR) eunit
@echo "==> Rebuilding normal NIF (removing ASan instrumentation)"
@$(MAKE) -C c_src PRIV_DIR=$(PRIV_DIR) OBJ_DIR=$(OBJ_DIR) \
$(if $(VERBOSE),VERBOSE=1,) clean all
@$(REBAR) compile
doc docs:
$(REBAR) ex_doc
# yaml_rustler and rusty_csv can't be resolved together (see mix.exs and the
# `deps` target below), so a full `make bench` run only includes whichever
# one BENCH_SET selects (neither, by default). Use `make bench-yaml` /
# `make bench-csv` (each sets BENCH_SET itself, see below) to benchmark
# against yaml_rustler/rusty_csv specifically; `make bench-json` needs
# neither and ignores BENCH_SET.
benchmark bench: do-bench
do-bench: deps
@rm .perf.txt
@$(MAKE) --no-print-directory optimize
for b in json yaml csv; do \
$(MAKE) --no-print-directory bench-$$b | tee -a .perf.txt; \
done
bench-yaml: BENCH_SET ?= yaml
bench-csv: BENCH_SET ?= csv
bench-json bench-yaml bench-csv:
@rm -f mix.lock
@BENCH_SET=$(BENCH_SET) mix deps.get
BENCH_SET=$(BENCH_SET) PARALLEL=$(if $(PARALLEL),$(PARALLEL),1) MIX_ENV=bench mix $@
# Profile-guided optimisation: instrument → run tests as workload → rebuild.
# Usage: make optimize
optimize:
@echo "==> PGO step 1/3: build instrumented binary"
@$(MAKE) -C c_src PRIV_DIR=$(PRIV_DIR) OBJ_DIR=$(OBJ_DIR) PGO=generate clean all
@$(REBAR) compile
@echo "==> PGO step 2/3: collect profile data"
@./bin/pgo-profile.es
@echo "==> PGO step 3/3: rebuild with profile data"
@rm -f $(OBJ_DIR)/glazer_nif.o $(PRIV_DIR)/glazer.so
@$(MAKE) -C c_src PRIV_DIR=$(PRIV_DIR) OBJ_DIR=$(OBJ_DIR) PGO=use all
@$(REBAR) compile
@echo "==> PGO build complete"
# BENCH_SET picks which of the two mutually-exclusive Rust NIF benchmark
# deps to resolve: yaml_rustler (BENCH_SET=yaml) or rusty_csv (BENCH_SET=csv).
# They can't be deps.get'd together because they pin incompatible :rustler
# versions (~> 0.34.0 vs ~> 0.37) — see mix.exs. `bench-json` and plain
# `bench`/`do-bench` need neither (mix.exs's bench_set_deps/0 returns [] for
# an unset/empty BENCH_SET), so whichever of the two was locked by a *prior*
# `bench-yaml`/`bench-csv` run must be purged first — otherwise mix.lock
# still pins it while it's absent from deps(), leaving a stale, half-built
# dependency in _build/bench (e.g. a missing .app file) that crashes
# `Mix.Task.run("app.start")`. Each branch below detects a leftover lock
# from the *other* mode (or, for an unset BENCH_SET, either mode) and forces
# mix to re-resolve from scratch.
deps:
@if [ "$(BENCH_SET)" = "yaml" ] && grep -q '"rusty_csv"' mix.lock 2>/dev/null; then \
echo "==> BENCH_SET=yaml but mix.lock has rusty_csv locked; re-resolving"; \
rm -f mix.lock; \
rm -rf _build/bench deps/rusty_csv deps/rustler_precompiled deps/rustler; \
elif [ "$(BENCH_SET)" = "csv" ] && grep -q '"yaml_rustler"' mix.lock 2>/dev/null; then \
echo "==> BENCH_SET=csv but mix.lock has yaml_rustler locked; re-resolving"; \
rm -f mix.lock; \
rm -rf _build/bench deps/yaml_rustler deps/rustler_precompiled deps/rustler; \
elif [ -z "$(BENCH_SET)" ] && (grep -q '"rusty_csv"' mix.lock 2>/dev/null || grep -q '"yaml_rustler"' mix.lock 2>/dev/null); then \
echo "==> BENCH_SET unset but mix.lock has rusty_csv/yaml_rustler locked; re-resolving"; \
rm -f mix.lock; \
rm -rf _build/bench deps/rusty_csv deps/yaml_rustler deps/rustler_precompiled deps/rustler; \
fi
@BENCH_SET=$(BENCH_SET) mix deps.get
publish: docs
$(REBAR) hex publish$(if $(replace), --replace)
deprecate:
@if [ -z $(vsn) ]; then \
echo "Usage: $(MAKE) $@ vsn=X.Y.Z - Deprecate version X.Y.Z"; \
exit 1; \
fi
$(REBAR) hex retire $(APP) $(vsn) deprecated --message Deprecated
cover:
$(REBAR) cover --verbose
bump-version:
@FILE=$$(ls -1 src/*.app.src | head -n1); \
CURRENT=$$(grep -m1 '{vsn,' $$FILE | sed -E 's/.*"([0-9]+\.[0-9]+\.[0-9]+)".*/\1/'); \
MAJOR=$$(echo $$CURRENT | cut -d. -f1); \
MINOR=$$(echo $$CURRENT | cut -d. -f2); \
PATCH=$$(echo $$CURRENT | cut -d. -f3); \
NEW=$$(echo "$${MAJOR}.$${MINOR}.$$((PATCH + 1))" | tr -d '\n'); \
echo "Bumping version from $${CURRENT} to $${NEW}"; \
sed -i "s/{vsn, \"$${CURRENT}\"}/{vsn, \"$${NEW}\"}/" $$FILE; \
echo "Changed: {vsn, \"$${CURRENT}\"} -> {vsn, \"$${NEW}\"}"; \
sed -i 's/\({:\?glazer,[[:space:]]*"~>\)[^"]*/\1 '"$${MAJOR}.$${MINOR}"'/' README.md; \
echo ""; \
read -p "Commit this change? [Y/n] " -n 1 -r || true; \
echo ""; \
if [[ $$REPLY =~ ^[Yy]$$ ]] || [[ -z $$REPLY ]]; then \
git commit -am "Bump version to $${NEW}"; \
fi
.PHONY: all help doc compile clean distclean test cover check dialyzer memcheck nif \
optimize benchmark bench publish deprecate bump-version deps \
bench-yaml bench-json bench-csv do-bench