.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