.PHONY: all build install doc clean html deb pkg bintar sdist
.PHONY: profile-build profile-rgl-time profile-rgl-memory profile-rgl-clean
.PHONY: bench-build bench-rgl bench-rgl-pre-pmcfg bench-rgl-clean
.PHONY: test-pmcfg test-pmcfg-core test-pmcfg-equivalence test-pmcfg-rgl-equivalence test-pmcfg-clean
# This gets the numeric part of the version from the cabal file
VERSION=$(shell sed -ne "s/^version: *\([0-9.]*\).*/\1/p" gf.cabal)
# Check if stack is installed
STACK=$(shell if hash stack 2>/dev/null; then echo "1"; else echo "0"; fi)
# Check if cabal >= 2.4 is installed (with v1- and v2- commands)
CABAL_NEW=$(shell if cabal v1-repl --help >/dev/null 2>&1 ; then echo "1"; else echo "0"; fi)
ifeq ($(STACK),1)
CMD=stack
else
CMD=cabal
ifeq ($(CABAL_NEW),1)
CMD_PFX=v1-
endif
endif
# Profiling configuration.
#
# The profiling targets intentionally use Stack even when the normal build falls
# back to cabal, because Stack's --work-dir makes it easy to keep profiled and
# unprofiled build products separate. The default RGL target is German, which
# is complex and a good compiler stress test; override
# RGL_PROFILE_MODULES or RGL_PROFILE_GF_OPTIONS for different experiments.
PROFILE_STACK_WORK ?= .stack-work-profile
PROFILE_GHC_OPTIONS ?= -fprof-auto -fprof-cafs
PROFILE_DIR ?= $(CURDIR)/profile
RGL_DIR ?= ../gf-rgl
RGL_PROFILE_DIR ?= $(PROFILE_DIR)/rgl
RGL_PROFILE_GFO_DIR ?= $(RGL_PROFILE_DIR)/gfo
RGL_PROFILE_PGF_DIR ?= $(RGL_PROFILE_DIR)/pgf
RGL_PROFILE_MODULES ?= $(RGL_DIR)/src/german/LangGer.gf
RGL_PROFILE_IMPORT_DIRS ?= $(RGL_DIR)/src/api
RGL_PROFILE_IMPORT_OPTIONS = $(foreach dir,$(RGL_PROFILE_IMPORT_DIRS),-i $(dir))
RGL_PROFILE_GF_OPTIONS ?= --make --quiet --src --preproc=mkPresent --gf-lib-path=$(RGL_PROFILE_GFO_DIR) --gfo-dir=$(RGL_PROFILE_GFO_DIR) --output-dir=$(RGL_PROFILE_PGF_DIR) --name=RGLProfile $(RGL_PROFILE_IMPORT_OPTIONS)
RGL_PROFILE_GF = stack --stack-yaml $(CURDIR)/stack.yaml --work-dir $(PROFILE_STACK_WORK) exec --profile gf --
RGL_TIME_STEM ?= $(RGL_PROFILE_DIR)/rgl-time
RGL_MEMORY_STEM ?= $(RGL_PROFILE_DIR)/rgl-memory
RGL_MEMORY_RTS ?= -hc
RGL_HEAP_SAMPLE_INTERVAL ?= 0.1
BENCH_STACK_WORK ?= $(PROFILE_STACK_WORK)
BENCH_PROFILE_FLAGS ?= --profile
BENCH_GHC_OPTIONS ?= $(PROFILE_GHC_OPTIONS)
BENCH_TARGET ?= gf:bench:gf-compiler-bench
BENCH_TASTY_OPTIONS ?=
BENCH_PRE_PMCFG_FLAG ?= --flag gf:pre-pmcfg
BENCH_PRE_PMCFG_STACK_WORK ?= .stack-work-profile-pre-pmcfg
RGL_BENCH_DIR ?= $(PROFILE_DIR)/rgl-bench
RGL_BENCH_GFO_DIR ?= $(RGL_BENCH_DIR)/gfo
RGL_BENCH_PGF_DIR ?= $(RGL_BENCH_DIR)/pgf
RGL_BENCH_NAME ?= RGLBench
RGL_BENCH_MODULES ?=
RGL_BENCH_IMPORT_DIRS ?= $(RGL_PROFILE_IMPORT_DIRS) $(RGL_DIR)/src/prelude
RGL_BENCH_PRE_PMCFG_DIR ?= $(PROFILE_DIR)/rgl-bench-pre-pmcfg
RGL_BENCH_PRE_PMCFG_NAME ?= RGLBenchPrePmcfg
BENCH_RTS_OPTIONS ?= +RTS -T -RTS
BENCH_STACK = stack --stack-yaml $(CURDIR)/stack.yaml --work-dir $(BENCH_STACK_WORK)
PMCFG_TEST_CORE_STACK_WORK ?= .stack-work-pmcfg-test-hooks
PMCFG_TEST_STACK_WORK ?= .stack-work-pmcfg-test
PMCFG_TEST_PRE_STACK_WORK ?= .stack-work-pmcfg-test-pre
PMCFG_TEST_DIR ?= $(PROFILE_DIR)/pmcfg-test
PMCFG_EQ_DIR ?= $(PMCFG_TEST_DIR)/equivalence
PMCFG_EQ_NAME ?= PMCFGEquiv
PMCFG_TEST_IMPORT_DIRS ?= testsuite/canonical/grammars
PMCFG_TEST_IMPORT_OPTIONS = $(foreach dir,$(PMCFG_TEST_IMPORT_DIRS),-i $(dir))
PMCFG_TEST_MODULES ?= testsuite/pmcfg/fixtures/PmcfgDiffCnc.gf testsuite/compiler/params/paramsCnc.gf testsuite/runtime/parser/LiteralsCnc.gf testsuite/runtime/parser/DummyNatCnc.gf testsuite/runtime/linearize/TestCnc.gf testsuite/compiler/check/lins/linsCnc.gf testsuite/canonical/gold/FoodsFin.gf
PMCFG_TEST_GF_OPTIONS ?= --make --quiet --src $(PMCFG_TEST_IMPORT_OPTIONS)
RGL_PMCFG_TEST_MODULES ?= $(RGL_DIR)/src/german/LangGer.gf $(RGL_DIR)/src/finnish/LangFin.gf $(RGL_DIR)/src/swedish/LangSwe.gf
RGL_PMCFG_TEST_GF_OPTIONS ?= --make --quiet --src --preproc=mkPresent $(RGL_PROFILE_IMPORT_OPTIONS)
all: build
dist/setup-config: gf.cabal Setup.hs WebSetup.hs
ifneq ($(STACK),1)
cabal ${CMD_PFX}configure
endif
build: dist/setup-config
${CMD} ${CMD_PFX}build
profile-build:
stack --work-dir $(PROFILE_STACK_WORK) build --profile --ghc-options "$(PROFILE_GHC_OPTIONS)"
profile-rgl-time: profile-build
@test -n "$(RGL_PROFILE_MODULES)" || { echo "No RGL modules found. Set RGL_DIR or RGL_PROFILE_MODULES."; exit 1; }
@command -v ghc-prof-flamegraph >/dev/null || { echo "Missing ghc-prof-flamegraph."; exit 1; }
rm -rf $(RGL_PROFILE_GFO_DIR) $(RGL_PROFILE_PGF_DIR)
mkdir -p $(RGL_PROFILE_DIR) $(RGL_PROFILE_GFO_DIR) $(RGL_PROFILE_PGF_DIR)
rm -f $(RGL_TIME_STEM).prof $(RGL_TIME_STEM).svg $(RGL_TIME_STEM).log
$(RGL_PROFILE_GF) $(RGL_PROFILE_GF_OPTIONS) $(RGL_PROFILE_MODULES) +RTS -s -p -po$(RGL_TIME_STEM) -RTS > $(RGL_TIME_STEM).log 2>&1 || { tail -n 80 $(RGL_TIME_STEM).log; exit 1; }
ghc-prof-flamegraph $(RGL_TIME_STEM).prof --output $(RGL_TIME_STEM).svg
profile-rgl-memory: profile-build
@test -n "$(RGL_PROFILE_MODULES)" || { echo "No RGL modules found. Set RGL_DIR or RGL_PROFILE_MODULES."; exit 1; }
@command -v hp2ps >/dev/null || { echo "Missing hp2ps."; exit 1; }
rm -rf $(RGL_PROFILE_GFO_DIR) $(RGL_PROFILE_PGF_DIR)
mkdir -p $(RGL_PROFILE_DIR) $(RGL_PROFILE_GFO_DIR) $(RGL_PROFILE_PGF_DIR)
rm -f $(RGL_MEMORY_STEM).hp $(RGL_MEMORY_STEM).ps $(RGL_MEMORY_STEM).aux $(RGL_MEMORY_STEM).tmp.ps $(RGL_MEMORY_STEM).log
$(RGL_PROFILE_GF) $(RGL_PROFILE_GF_OPTIONS) $(RGL_PROFILE_MODULES) +RTS -s $(RGL_MEMORY_RTS) -i$(RGL_HEAP_SAMPLE_INTERVAL) -po$(RGL_MEMORY_STEM) -RTS > $(RGL_MEMORY_STEM).log 2>&1 || { tail -n 80 $(RGL_MEMORY_STEM).log; exit 1; }
cd $(dir $(RGL_MEMORY_STEM)) && hp2ps $(notdir $(RGL_MEMORY_STEM)).hp
@if command -v gs >/dev/null 2>&1; then \
gs -q -dNOPAUSE -dBATCH -sOutputFile=$(RGL_MEMORY_STEM).tmp.ps -sDEVICE=ps2write -c "<> setpagedevice" -- $(RGL_MEMORY_STEM).ps && mv $(RGL_MEMORY_STEM).tmp.ps $(RGL_MEMORY_STEM).ps; \
else \
echo "Missing gs; leaving $(RGL_MEMORY_STEM).ps without orientation fix."; \
fi
profile-rgl-clean:
rm -rf $(RGL_PROFILE_DIR)
bench-build:
$(BENCH_STACK) build $(BENCH_TARGET) --bench --no-run-benchmarks $(BENCH_PROFILE_FLAGS) --ghc-options "$(BENCH_GHC_OPTIONS)"
bench-rgl: bench-build
@test -d "$(RGL_DIR)" || { echo "Missing RGL_DIR: $(RGL_DIR)"; exit 1; }
rm -rf $(RGL_BENCH_GFO_DIR) $(RGL_BENCH_PGF_DIR)
mkdir -p $(RGL_BENCH_GFO_DIR) $(RGL_BENCH_PGF_DIR)
GF_BENCH_RGL_DIR="$(RGL_DIR)" GF_BENCH_NAME="$(RGL_BENCH_NAME)" GF_BENCH_GFO_DIR="$(RGL_BENCH_GFO_DIR)" GF_BENCH_PGF_DIR="$(RGL_BENCH_PGF_DIR)" GF_BENCH_MODULES="$(RGL_BENCH_MODULES)" GF_BENCH_IMPORT_DIRS="$(RGL_BENCH_IMPORT_DIRS)" $(BENCH_STACK) bench $(BENCH_TARGET) $(BENCH_PROFILE_FLAGS) --ghc-options "$(BENCH_GHC_OPTIONS)" --benchmark-arguments "$(BENCH_TASTY_OPTIONS) $(BENCH_RTS_OPTIONS)"; \
status=$$?; \
if test -f gf-compiler-bench.prof; then mv gf-compiler-bench.prof "$(RGL_BENCH_DIR)/gf-compiler-bench.prof"; fi; \
exit $$status
bench-rgl-pre-pmcfg:
$(MAKE) bench-rgl BENCH_STACK_WORK="$(BENCH_PRE_PMCFG_STACK_WORK)" RGL_BENCH_DIR="$(RGL_BENCH_PRE_PMCFG_DIR)" RGL_BENCH_NAME="$(RGL_BENCH_PRE_PMCFG_NAME)" BENCH_PROFILE_FLAGS="$(BENCH_PROFILE_FLAGS) $(BENCH_PRE_PMCFG_FLAG)"
bench-rgl-clean:
rm -rf $(RGL_BENCH_DIR) $(RGL_BENCH_PRE_PMCFG_DIR)
test-pmcfg: test-pmcfg-core test-pmcfg-equivalence
test-pmcfg-core:
stack --work-dir $(PMCFG_TEST_CORE_STACK_WORK) test gf:pmcfg-tests --flag gf:pmcfg-test-hooks
test-pmcfg-equivalence:
stack --work-dir $(PMCFG_TEST_STACK_WORK) build
stack --work-dir $(PMCFG_TEST_PRE_STACK_WORK) build --flag gf:pre-pmcfg
rm -rf $(PMCFG_EQ_DIR)
mkdir -p $(PMCFG_EQ_DIR)
@set -e; \
i=0; \
for module in $(PMCFG_TEST_MODULES); do \
i=$$((i+1)); \
case_name=$$(basename "$$module" .gf); \
case_dir="$(PMCFG_EQ_DIR)/$$i-$$case_name"; \
new_gfo="$$case_dir/new-gfo"; \
new_pgf="$$case_dir/new-pgf"; \
pre_gfo="$$case_dir/pre-gfo"; \
pre_pgf="$$case_dir/pre-pgf"; \
mkdir -p "$$new_gfo" "$$new_pgf" "$$pre_gfo" "$$pre_pgf"; \
echo "PMCFG equivalence: $$module"; \
stack --work-dir $(PMCFG_TEST_STACK_WORK) exec gf -- $(PMCFG_TEST_GF_OPTIONS) --gfo-dir="$$new_gfo" --output-dir="$$new_pgf" --name=$(PMCFG_EQ_NAME) "$$module"; \
stack --work-dir $(PMCFG_TEST_PRE_STACK_WORK) exec gf -- $(PMCFG_TEST_GF_OPTIONS) --gfo-dir="$$pre_gfo" --output-dir="$$pre_pgf" --name=$(PMCFG_EQ_NAME) "$$module"; \
test -f "$$new_pgf/$(PMCFG_EQ_NAME).pgf"; \
test -f "$$pre_pgf/$(PMCFG_EQ_NAME).pgf"; \
cmp "$$new_pgf/$(PMCFG_EQ_NAME).pgf" "$$pre_pgf/$(PMCFG_EQ_NAME).pgf" || { echo "PMCFG output differs for $$module; artifacts kept in $$case_dir"; exit 1; }; \
done
test-pmcfg-rgl-equivalence:
@test -d "$(RGL_DIR)" || { echo "Missing RGL_DIR: $(RGL_DIR)"; exit 1; }
$(MAKE) test-pmcfg-equivalence PMCFG_TEST_MODULES="$(RGL_PMCFG_TEST_MODULES)" PMCFG_TEST_GF_OPTIONS="$(RGL_PMCFG_TEST_GF_OPTIONS)" PMCFG_EQ_DIR="$(PMCFG_TEST_DIR)/rgl-equivalence"
test-pmcfg-clean:
rm -rf $(PMCFG_TEST_DIR)
install:
ifeq ($(STACK),1)
stack install
else
cabal ${CMD_PFX}copy
cabal ${CMD_PFX}register
endif
doc:
${CMD} ${CMD_PFX}haddock
clean:
${CMD} ${CMD_PFX}clean
bash bin/clean_html
html::
bash bin/update_html
# Make a debian package. First add a suitable entry with the correct GF version
# number to the top of debian/changelog.
# (Tested on Ubuntu 15.04. You need to install dpkg-dev & debhelper.)
deb:
dpkg-buildpackage -b -uc -d
# Make a macOS installer package
pkg:
FMT=pkg bash bin/build-binary-dist.sh
# Make a binary tar distribution
bintar:
bash bin/build-binary-dist.sh
#sdist:
# cabal sdist
# Make a source tar.gz distribution using git to make sure that everything is included.
# We put the distribution in dist/ so it is removed on `make clean`
# sdist:
# test -d dist || mkdir dist
# git archive --format=tar.gz --output=dist/gf-${VERSION}.tar.gz HEAD