#!/usr/bin/env just # * Light project scripts, without file dependendencies # using https://github.com/casey/just 0.16. # https://docs.rs/regex/1.5.4/regex/#syntax Regexps # https://just.systems/man/en/chapter_31.html Functions # https://cheatography.com/linux-china/cheat-sheets/justfile Cheatsheet # https://github.com/casey/just/discussions # See also Makefile, Shake.hs. # ** prelude @help: just -lu # This and all other just invocations assume you are in this justfile's directory, # otherwise we must write --justfile {{ justfile() }} or {{ just }} everywhere. @_check: just --fmt --unstable --check @_fmt: just -q _check || just --fmt --unstable # ** vars # GHC-compiled executables require a locale (and not just C) or they # will die on encountering non-ascii data. Set LANG to something if not already set. # export LANG? := 'en_US.UTF-8' # command to run during profiling (time and heap) PROFCMD := 'stack exec --profile -- hledger balance -f examples/10000x1000x10.journal >/dev/null' #PROFRTSFLAGS := '-p' PROFRTSFLAGS := '-P' # # command to run during "make coverage" # COVCMD := 'test' # COVCMD := '-f test-wf.csv print' # misc. system tools BROWSE := 'open' # VIEWHTML := '{{ BROWSE }}' # VIEWPDF := '{{ BROWSE }}' # PRINT := 'lpr' #GHC := 'ghc' GHCI := 'ghci' # GHCPKG := 'ghc-pkg' # HADDOCK := 'haddock' # CABAL := 'cabal' # CABALINSTALL := 'cabal install -w {{ GHC }}' # Which stack command (and in particular, stack yaml/GHC version) to use for building etc. ? STACK := 'stack' #STACK := 'stack --stack-yaml=stack8.10.yaml' # Or override temporarily with an env var: # STACK := '"stack --stack-yaml=stack8.10.yaml" make functest' # Which stack command (stack yaml, GHC version) to use for ghci[d] operations ? STACKGHCI := STACK #STACKGHCI := 'stack --stack-yaml=stack9.2.yaml' # if using an unreleased stack with a newer hpack than the one mentioned in */*.cabal, # it will give warnings. To silence these, put the old hpack-X.Y in $PATH and uncomment: #STACK := 'stack --with-hpack=hpack-0.20' # --threads := '16 sometimes gives "commitAndReleaseBuffer: resource vanished (Broken pipe)" but seems harmless' # --timeout := 'N is not much use here - can be defeated by multiple threads, unoptimised builds, ' # slow hackage index or compiler setup on first build, etc. WATCHEXEC := 'watchexec' PACKAGES := '\ hledger-lib \ hledger \ hledger-ui \ hledger-web \ ' BINARIES := '\ hledger \ hledger-ui \ hledger-web ' INCLUDEPATHS := '\ -ihledger-lib \ -ihledger \ -ihledger-ui \ -ihledger-web \ -ihledger-web/app \ ' MAIN := 'hledger/app/hledger-cli.hs' # All source files in the project (plus a few strays like Setup.hs & hlint.hs). # Used eg for building tags. Doesn't reliably catch all source files. SOURCEFILES := '\ dev.hs \ hledger/*hs \ hledger/app/*hs \ hledger/bench/*hs \ hledger/test/*hs \ hledger/Hledger/*hs \ hledger/Hledger/*/*hs \ hledger/Hledger/*/*/*hs \ hledger-*/*hs \ hledger-*/app/*hs \ hledger-*/test/*hs \ hledger-*/Hledger/*hs \ hledger-*/Hledger/*/*hs \ hledger-*/Hledger/*/*/*hs \ hledger-lib/Text/*/*hs \ ' # hledger-*/src/*hs \ HPACKFILES := '\ hledger/*package.yaml \ hledger-*/*package.yaml \ ' CABALFILES := '\ hledger/hledger.cabal \ hledger-*/*.cabal \ ' MANUALSOURCEFILES := '\ doc/common.m4 \ */*.m4.md \ ' MANUALGENFILES := '\ hledger*/hledger*.{1,5,info,txt} \ ' COMMANDHELPFILES := '\ hledger/Hledger/Cli/Commands/*.md \ ' WEBTEMPLATEFILES := '\ hledger-web/templates/* \ ' WEBCODEFILES := '\ hledger-web/static/*.js \ hledger-web/static/*.css \ ' DOCSOURCEFILES := '\ README.md \ CONTRIBUTING.md' + MANUALSOURCEFILES + COMMANDHELPFILES TESTFILES := '\ hledger/test/*.test \ hledger/test/*/*.test \ hledger/test/*/*/*.test \ ' # # file(s) which require recompilation for a build to have an up-to-date version string # VERSIONSOURCEFILE := 'hledger/Hledger/Cli/Version.hs' # Two or three-part version string, set as program version in builds made by this makefile. # We use hledger CLI's current version (XXX for all packages, which isn't quite right). export VERSION := `cat hledger/.version` # Flags for ghc builds. # Warnings to see during dev tasks like make ghci*. See also the warnings in package.yamls. # XXX redundant with package.yamls ? WARNINGS := '\ -Wall \ -Wno-incomplete-uni-patterns \ -Wno-missing-signatures \ -Wno-orphans \ -Wno-type-defaults \ -Wno-unused-do-bind \ ' # if you have need to try building in less memory GHCLOWMEMFLAGS := '' # ghc-only builds need the macro definitions generated by cabal # from cabal's dist or dist-sandbox dir, hopefully there's just one: #CABALMACROSFLAGS := '-optP-include -optP hledger/dist*/build/autogen/cabal_macros.h' # or from stack's dist dir: #CABALMACROSFLAGS := '-optP-include -optP hledger/.stack-work/dist/*/*/build/autogen/cabal_macros.h' CABALMACROSFLAGS := '' BUILDFLAGS := '-rtsopts ' + WARNINGS + GHCLOWMEMFLAGS + CABALMACROSFLAGS + ' -DDEVELOPMENT' + ' -DVERSION=\"' + VERSION + '\"' + INCLUDEPATHS # -fplugin Debug.Breakpoint \ # -fhide-source-paths \ # PROFBUILDFLAGS := '-prof -fprof-auto -osuf hs_p' TIME := "{{ shell date +'%Y%m%d%H%M' }}" MONTHYEAR := "{{ shell date +'%B %Y' }}" # ** GHCI GHCI: # run GHCI on hledger-lib + hledger @ghci: {{ STACKGHCI }} exec -- {{ GHCI }} {{ BUILDFLAGS }} hledger/Hledger/Cli.hs # run GHCI on hledger-lib + hledger with profiling/call stack information @ghci-prof: stack build --profile hledger --only-dependencies {{ STACKGHCI }} exec -- {{ GHCI }} {{ BUILDFLAGS }} -fexternal-interpreter -prof -fprof-auto hledger/Hledger/Cli.hs # # run GHCI on hledger-lib + hledger + dev.hs script # @ghci-dev: # {{ STACKGHCI }} exec -- {{ GHCI }} {{ BUILDFLAGS }} -fno-warn-unused-imports -fno-warn-unused-binds dev.hs # run GHCI on hledger-lib + hledger + hledger-ui @ghci-ui: {{ STACKGHCI }} exec -- {{ GHCI }} {{ BUILDFLAGS }} hledger-ui/Hledger/UI/Main.hs # run GHCI on hledger-lib + hledger + hledger-web @ghci-web: {{ STACKGHCI }} exec -- {{ GHCI }} {{ BUILDFLAGS }} hledger-web/app/main.hs # run GHCI on hledger-lib + hledger + hledger-web + hledger-web test suite @ghci-web-test: {{ STACKGHCI }} exec -- {{ GHCI }} {{ BUILDFLAGS }} hledger-web/test/test.hs # # better than stack exec ? # # XXX does not see changes to files # # run GHCI on hledger-lib + test runner # ghci-lib-test: # {{ STACKGHCI }} ghci --ghc-options="\'-rtsopts {{ WARNINGS }} -ihledger-lib -DDEVELOPMENT -DVERSION=\"1.26.99\"\'" hledger-lib/test/unittest.hs # run GHCI on all the hledger # ghci-all: # {{ STACK }} exec -- {{ GHCI }} {{ BUILDFLAGS }} \ # hledger-ui/Hledger/UI/Main.hs \ # hledger-web/app/main.hs \ # run GHCI on hledger-lib doctests @ghci-doctest: cd hledger-lib; {{ STACKGHCI }} ghci hledger-lib:test:doctest # run GHCI on Shake.hs @ghci-shake: {{ STACK }} exec {{ SHAKEDEPS }} -- ghci Shake.hs # ** ghcid GHCID: # run ghcid on hledger-lib + hledger @ghcid: ghcid -c 'just ghci' # run ghcid autobuilder on hledger-lib + hledger + hledger-ui @ghcid-ui: ghcid -c 'just ghci-ui' # run ghcid autobuilder on hledger-lib + hledger + hledger-web @ghcid-web: ghcid -c 'just ghci-web' # run ghcid autobuilding and running hledger-web with sample journal on port 5001 @ghcid-web-run: ghcid -c 'just ghci-web' --test ':main -f examples/sample.journal --port 5001 --serve' # # run ghcid autobuilding and running the test command # ghcid-test: # ghcid -c 'just ghci' --test ':main test -- --color=always' # # run ghcid autobuilding and running the test command with this TESTPATTERN # ghcid-test-%: # ghcid -c 'just ghci' --test ':main test -- --color=always -p$*' # run ghcid autobuilding and running hledger-lib doctests @ghcid-doctest: ghcid -c 'cd hledger-lib; {{ STACK }} ghci hledger-lib:test:doctest' --test ':main' --reload hledger-lib GHCIDRESTART := '--restart Makefile --restart Makefile.local' GHCIDRELOAD := '--reload t.j --reload t.timedot' GHCIDCMD := ':main -f t.j bal date:today -S' # # run ghcid autobuilding and running a custom GHCI command with reload/restart on certain files - customise this # ghcid-watch watch: # ghcid -c 'just ghci' --test '{{ GHCIDCMD }}' {{ GHCIDRELOAD }} {{ GHCIDRESTART }} # keep synced with Shake.hs header SHAKEDEPS := '\ --package base-prelude \ --package directory \ --package extra \ --package process \ --package regex \ --package safe \ --package shake \ --package time \ ' # --package hledger-lib \ # for Hledger.Utils.Debug # run ghcid autobuilder on Shake.hs ghcid-shake: stack exec {{ SHAKEDEPS }} -- ghcid Shake.hs # ** dev.hs script # # hledger-lib/Hledger/Read/TimeclockReaderPP.hs # # build the dev.hs script for quick experiments (with ghc) # dev: # {{ STACK }} ghc -- {{ CABALMACROSFLAGS }} -ihledger-lib dev.hs \ # # to get profiling deps installed, first do something like: # # stack build --library-profiling hledger-lib timeit criterion # # build the dev.hs script with profiling support # devprof: # {{ STACK }} ghc -- {{ CABALMACROSFLAGS }} -ihledger-lib dev.hs -rtsopts -prof -fprof-auto -osuf p_o -o devprof # # get a time & space profile of the dev.hs script # dev-profile: # time ./devprof +RTS -P \ # && cp devprof.prof devprof.prof.{{ TIME }} \ # && profiterole devprof.prof # # get heap profiles of the dev.hs script # dev-heap: # time ./devprof +RTS -hc -L1000 && cp devprof.hp devprof-hc.hp && hp2ps devprof-hc.hp # time ./devprof +RTS -hr -L1000 && cp devprof.hp devprof-hr.hp && hp2ps devprof-hr.hp # dev-heap-upload: # curl -F "file=@devprof-hc.hp" -F "title='hledger parser'" http://heap.ezyang.com/upload # curl -F "file=@devprof-hr.hp" -F "title='hledger parser'" http://heap.ezyang.com/upload # ** building BUILDING: # build the hledger package showing GHC codegen times/allocations @buildtimes: time ({{ STACK }} build hledger --force-dirty --ghc-options='-fforce-recomp -ddump-timings' 2>&1 | grep -E '\bCodeGen \[.*time=') # # build an unoptimised hledger at bin/hledger.EXT.unopt (default: git describe) # build-unopt *EXT: # #!/usr/bin/env bash # ext={{ if EXT == '' { `git describe --tags` } else { EXT } }} # exe="bin/hledger.$ext.unopt" # {{ STACK }} --verbosity=error install --ghc-options=-O0 hledger --local-bin-path=bin # mv bin/hledger "$exe" # echo "$exe" # # build hledger with profiling enabled at bin/hledgerprof # hledgerprof: # # {{ STACK }} --verbosity=error install --local-bin-path=bin hledger # {{ STACK }} build --profile hledger # # hledger-lib --ghc-options=-fprof-auto # # @echo "to profile, use {{ STACK }} exec --profile -- hledger ..." # # build "bin/hledgercov" for coverage reports (with ghc) # hledgercov: # {{ STACK }} ghc {{ MAIN }} -fhpc -o bin/hledgercov -outputdir .hledgercovobjs {{ BUILDFLAGS }} # ** testing TESTING: # run tests that are reasonably quick (files, unit, functional) and benchmarks test: filestest functest #bench # For quieter tests add --silent. It may hide troubleshooting info. # For very verbose tests add --verbosity=debug. It seems hard to get something in between. STACKTEST := STACK + ' test --fast' # # When doing build testing, save a little time and output noise by not # # running tests & benchmarks. Comment this out if you want to run them. # SKIPTESTSBENCHS := '--no-run-tests --no-run-benchmarks' # check all files embedded with file-embed are declared in extra-source-files @filestest: tools/checkembeddedfiles # # stack build --dry-run all hledger packages ensuring an install plan with default snapshot) # buildplantest: # buildplantest-stack.yaml # # stack build --dry-run all hledger packages ensuring an install plan with each ghc version/stackage snapshot # buildplantest-all: # for F in stack*.yaml; do make --no-print-directory buildplantest-$F; done # # stack build --dry-run all hledger packages ensuring an install plan with the given stack yaml file; eg make buildplantest-stack8.2.yaml # buildplantest-%: # {{ STACK }} build --dry-run --test --bench --stack-yaml=$* # # force-rebuild all hledger packages/modules quickly ensuring no warnings with default snapshot) # buildtest: # buildtest-stack.yaml # # force-rebuild all hledger packages/modules quickly ensuring no warnings with each ghc version/stackage snapshot # buildtest-all: # for F in stack*.yaml; do make --no-print-directory buildtest-$F; done # # force-rebuild all hledger packages/modules quickly ensuring no warnings with the given stack yaml file; eg make buildtest-stack8.2.yaml # buildtest-%: # {{ STACK }} build --test --bench {{ SKIPTESTSBENCHS }} --fast --force-dirty --ghc-options=-fforce-recomp --ghc-options=-Werror --stack-yaml=$* # # build any outdated hledger packages/modules quickly ensuring no warnings with default snapshot. Wont detect warnings in up-to-date modules.) # incr-buildtest: # incr-buildtest-stack.yaml # # build any outdated hledger packages/modules quickly ensuring no warnings with each ghc version/stackage snapshot. Wont detect warnings in up-to-date modules. # incr-buildtest-all: # for F in stack*.yaml; do make --no-print-directory incr-buildtest-$F; done # # build any outdated hledger packages/modules quickly ensuring no warnings with the stack yaml file; eg make buildtest-stack8.2.yaml. Wont detect warnings in up-to-date modules. # incr-buildtest-%: # {{ STACK }} build --test --bench {{ SKIPTESTSBENCHS }} --fast --ghc-options=-Werror --stack-yaml=$* # # do a stack clean --full with all ghc versions for paranoia/troubleshooting # stack-clean-all: # for F in stack*.yaml; do {{ STACK }} clean --full --stack-yaml=$F; done # run all test suites in the hledger packages @pkgtest: ({{ STACKTEST }} && echo $@ PASSED) || (echo $@ FAILED; false) # doctest with ghc 8.4 on mac requires a workaround, see hledger-lib/package.yaml. # Or, could run it with ghc 8.2: # @({{ STACKTEST }} --stack-yaml stack8.2.yaml hledger-lib:test:doctest && echo $@ PASSED) || (echo $@ FAILED; false) # run the doctests in hledger-lib module/function docs @doctest: ({{ STACKTEST }} hledger-lib:test:doctest && echo $@ PASSED) || (echo $@ FAILED; false) # # run the unit tests in hledger-lib # unittest: # @({{ STACKTEST }} hledger-lib:test:unittest && echo $@ PASSED) || (echo $@ FAILED; false) # run hledger & hledger-lib unit tests (do a stack build hledger first). @unittest: ({{ STACK }} exec hledger test && echo $@ PASSED) || (echo $@ FAILED; false) SHELLTEST := 'COLUMNS=80 ' + STACK + ' exec -- shelltest --execdir --threads=64 --exclude=/_' # --hide-successes # build hledger quickly and run functional tests, or just the ones matching PAT. (Run mktestaddons first.) @functest *PAT: {{ STACK }} build --fast hledger ({{ SHELLTEST }} {{ if PAT == '' { '' } else { '-i "'+PAT+'"' } }} \ hledger/test/ bin/ \ -x ledger-compat/ledger-baseline -x ledger-compat/ledger-regress -x ledger-compat/ledger-extra \ && echo $@ PASSED) || (echo $@ FAILED; false) ADDONEXTS := 'pl py rb sh hs lhs rkt exe com bat' # generate dummy add-ons for testing the CLI @mktestaddons: rm -rf hledger/test/addons/hledger-* printf '#!/bin/sh\necho add-on: $0\necho args: $*\n' >hledger/test/addons/hledger-addon for E in '' {{ ADDONEXTS }}; do \ cp hledger/test/addons/hledger-addon hledger/test/addons/hledger-addon.$E; done for F in addon. addon2 addon2.hs addon3.exe addon3.lhs addon4.exe add reg; do \ cp hledger/test/addons/hledger-addon hledger/test/addons/hledger-$F; done mkdir hledger/test/addons/hledger-addondir chmod +x hledger/test/addons/hledger-* # generate a hlint report # hlinttest hlint: # hlint --hint=hlint --report=hlint.html {{ SOURCEFILES }} # # run haddock to make sure it can generate docs without dying # @haddocktest: # (make --quiet haddock && echo $@ PASSED) || (echo $@ FAILED; false) # # run cabal check to test cabal file syntax # cabalfiletest: # @(make --no-print-directory cabalcheck && echo $@ PASSED) || (echo $@ FAILED; false) # test-stack%yaml: # {{ STACK }} --stack-yaml stack$*yaml clean # {{ STACK }} --stack-yaml stack$*yaml build --ghc-options="{{ WARNINGS }} -Werror" --test --bench --haddock --no-haddock-deps # committest: hlinttest unittest doctest functest haddocktest buildtest quickcabaltest \ # # releasetest: Clean unittest functest fullcabaltest haddocktest #buildtest doctest \ # {{ call def-help,releasetest,pre-release tests }} # run hledger-install.sh not from inside a haskell project installtest: cd; {{ justfile_directory() }}/hledger-install/hledger-install.sh # ** benchmarking BENCHMARKING: # generate standard sample journals in examples/ mksamplejournals: # samplejournals: $(call def-help,samplejournals, regenerate standard sample journals in examples/) \ # examples/sample.journal \ # examples/10x10x10.journal \ # examples/100x100x10.journal \ # examples/1000x1000x10.journal \ # examples/1000x10000x10.journal \ # examples/2000x1000x10.journal \ # examples/3000x1000x10.journal \ # examples/4000x1000x10.journal \ # examples/5000x1000x10.journal \ # examples/6000x1000x10.journal \ # examples/7000x1000x10.journal \ # examples/8000x1000x10.journal \ # examples/9000x1000x10.journal \ # examples/10000x1000x10.journal \ # examples/10000x10000x10.journal \ # examples/100000x1000x10.journal \ # examples/1000000x1000x10.journal \ # # examples/ascii.journal \ # # examples/chinese.journal \ # # examples/mixed.journal \ # examples/sample.journal: # true # XXX should probably regenerate this # examples/10x10x10.journal: tools/generatejournal # tools/generatejournal 10 10 10 >$@ # examples/100x100x10.journal: tools/generatejournal # tools/generatejournal 100 100 10 >$@ # examples/1000x1000x10.journal: tools/generatejournal # tools/generatejournal 1000 1000 10 >$@ # examples/1000x10000x10.journal: tools/generatejournal # tools/generatejournal 1000 10000 10 >$@ # examples/2000x1000x10.journal: tools/generatejournal # tools/generatejournal 2000 1000 10 >$@ # examples/3000x1000x10.journal: tools/generatejournal # tools/generatejournal 3000 1000 10 >$@ # examples/4000x1000x10.journal: tools/generatejournal # tools/generatejournal 4000 1000 10 >$@ # examples/5000x1000x10.journal: tools/generatejournal # tools/generatejournal 5000 1000 10 >$@ # examples/6000x1000x10.journal: tools/generatejournal # tools/generatejournal 6000 1000 10 >$@ # examples/7000x1000x10.journal: tools/generatejournal # tools/generatejournal 7000 1000 10 >$@ # examples/8000x1000x10.journal: tools/generatejournal # tools/generatejournal 8000 1000 10 >$@ # examples/9000x1000x10.journal: tools/generatejournal # tools/generatejournal 9000 1000 10 >$@ # examples/10000x1000x10.journal: tools/generatejournal # tools/generatejournal 10000 1000 10 >$@ # examples/10000x10000x10.journal: tools/generatejournal # tools/generatejournal 10000 10000 10 >$@ # examples/100000x1000x10.journal: tools/generatejournal # tools/generatejournal 100000 1000 10 >$@ # examples/1000000x1000x10.journal: tools/generatejournal # tools/generatejournal 1000000 1000 10 >$@ # examples/ascii.journal: tools/generatejournal # tools/generatejournal 3 5 5 >$@ # examples/chinese.journal: tools/generatejournal # tools/generatejournal 3 5 5 --chinese >$@ # examples/mixed.journal: tools/generatejournal # tools/generatejournal 3 5 5 --mixed >$@ # # hledger executables to bench test, can be overridden by env var, # # eg: BENCHEXES=ledger,hledger-1.18,hledger make bench # BENCHEXES ?= hledger # bench: quickbench # quickbench: samplejournals bench.sh $(call def-help,quickbench, benchmark commands in bench.sh with quickbench and $(BENCHEXES)) # @echo; echo "run quick performance benchmarks in bench.sh (approximate, can be skewed):" # @which quickbench >/dev/null && quickbench -w $(BENCHEXES) || echo "quickbench not installed (see bench.sh), skipping" # # bench: samplejournals tests/bench.tests tools/simplebench \ # # $(call def-help,bench,\ # # run simple performance benchmarks and archive results\ # # Requires some commands defined in tests/bench.tests and some BENCHEXES defined above.\ # # ) # # tools/simplebench -v -ftests/bench.tests $(BENCHEXES) | tee doc/profs/$(TIME).bench # # @rm -f benchresults.* # # @(cd doc/profs; rm -f latest.bench; ln -s $(TIME).bench latest.bench) # # criterionbench: samplejournals tools/criterionbench \ # # $(call def-help,criterionbench,\ # # run criterion benchmark tests and save graphical results\ # # ) # # tools/criterionbench -t png -k png # # progressionbench: samplejournals tools/progressionbench \ # # $(call def-help,progressionbench,\ # # run progression benchmark tests and save graphical results\ # # ) # # tools/progressionbench -- -t png -k png # throughput: throughput-hledger \ # $(call def-help,throughput, show throughput at various data sizes with the default hledger executable ) # throughput-%: samplejournals \ # $(call def-help,throughput-HLEDGEREXE, show throughput at various data sizes with the given hledger executable ) # @echo date: `date` # @echo system: `uname -a` # @echo executable: $* # @echo version: `$* --version` # @for n in 1000 2000 3000 4000 5000 6000 7000 8000 9000 10000 100000 ; do \ # printf "%6d txns: " $$n; $* stats -f examples/$${n}x1000x10.journal | tail -1; \ # done # @date # throughput-dev: samplejournals \ # $(call def-help,throughput-dev, show throughput at various data sizes with the latest hledger dev build (optimised or not) ) # @stack build hledger # @stack exec -- make -s throughput # # prof: samplejournals \ # # $(call def-help,prof,\ # # generate and archive an execution profile\ # # ) #bin/hledgerprof # # @echo "Profiling: $(PROFCMD)" # # -$(PROFCMD) +RTS $(PROFRTSFLAGS) -RTS # # mv hledgerprof.prof doc/profs/$(TIME).prof # # (cd doc/profs; rm -f latest*.prof; ln -s $(TIME).prof latest.prof) # # viewprof: prof \ # # $(call def-help,viewprof,\ # # generate, archive, simplify and display an execution profile\ # # ) # # tools/simplifyprof.hs doc/profs/latest.prof # quickprof-%: hledgerprof samplejournals \ # $(call def-help,quickprof-"CMD", run some command against a standard sample journal and display the execution profile ) # $(STACK) exec --profile -- hledger +RTS $(PROFRTSFLAGS) -RTS $* -f examples/1000x1000x10.journal >/dev/null # profiterole hledger.prof # @echo # @head -20 hledger.prof # @echo ... # @echo # @head -20 hledger.profiterole.txt # @echo ... # @echo # @echo "See hledger.prof, hledger.profiterole.txt, hledger.profiterole.html for more." # # heap: samplejournals \ # # $(call def-help,heap,\ # # generate and archive a graphical heap profile\ # # ) #bin/hledgerprof # # @echo "Profiling heap with: $(PROFCMD)" # # $(PROFCMD) +RTS -hc -RTS # # mv hledgerprof.hp doc/profs/$(TIME).hp # # (cd doc/profs; rm -f latest.hp; ln -s $(TIME).hp latest.hp; \ # # hp2ps $(TIME).hp; rm -f latest.ps; ln -s $(TIME).ps latest.ps; rm -f *.aux) # # viewheap: heap \ # # $(call def-help,viewheap,\ # # \ # # ) # # $(VIEWPS) doc/profs/latest.ps # quickheap-%: hledgerprof samplejournals \ # $(call def-help,quickheap-"CMD", run some command against a sample journal and display the heap profile ) # $(STACK) exec -- hledgerprof +RTS -hc -RTS $* -f examples/10000x1000x10.journal >/dev/null # hp2ps hledgerprof.hp # @echo generated hledgerprof.ps # $(VIEWPS) hledgerprof.ps # # quickcoverage: hledgercov \ # # $(call def-help,quickcoverage,\ # # display a code coverage text report from running hledger COVCMD\ # # ) # # @echo "Generating code coverage text report for hledger command: $(COVCMD)" # # tools/runhledgercov "report" $(COVCMD) # # coverage: samplejournals hledgercov \ # # $(call def-help,coverage,\ # # generate a code coverage html report from running hledger COVCMD\ # # ) # # @echo "Generating code coverage html report for hledger command: $(COVCMD)" # # tools/runhledgercov "markup --destdir=doc/profs/coverage" $(COVCMD) # # cd doc/profs/coverage; rm -f index.html; ln -s hpc_index.html index.html # # viewcoverage: \ # # $(call def-help,viewcoverage,\ # # view the last html code coverage report\ # # ) # # $(VIEWHTML) doc/profs/coverage/index.html # ############################################################################### # $(call def-help-subheading,DOCUMENTATION: (see also Shake.hs)) # # http://www.haskell.org/haddock/doc/html/invoking.html # STACKHADDOCK=time $(STACK) --verbosity=error haddock --fast --no-keep-going \ # --only-locals --no-haddock-deps --no-haddock-hyperlink-source \ # --haddock-arguments="--no-warnings" # # -ghc-options='-optP-P' # workaround for http://trac.haskell.org/haddock/ticket/284 # # uncomment to run haddock on fewer packages, saving time # #HADDOCKPKGS=hledger-lib # releasediag: \ # $(call def-help,releasediag, optimise and commit RELEASING value map diagram ) # pngquant doc/HledgerReleaseValueMap.png -f -o doc/HledgerReleaseValueMap.png && git add doc/HledgerReleaseValueMap.png && git commit -m ';doc: RELEASING: update value map' -- doc/HledgerReleaseValueMap.png # # Renders all hledger packages. Run make haddock-open to open contents page. # haddock: \ # $(call def-help,haddock, regenerate haddock docs for the hledger packages and open them ) # $(STACKHADDOCK) $(HADDOCKPKGS) && make -s haddock-open # --open shows all deps and packages # # Rerenders all hledger packages. Run make haddock-open to open contents page. # haddock-watch1: \ # $(call def-help,haddock-watch, regenerate haddock docs when files change ) # $(STACKHADDOCK) $(HADDOCK_PKGS) --file-watch --exec='echo done' # # Rerenders hledger-lib modules, opens hledger-lib contents page. # haddock-watch2: \ # $(call def-help,haddock-watch2, regenerate hledger-lib haddock docs when files change ) # watchexec -r -e yaml,cabal,hs --print-events -- \ # $(STACKHADDOCK) --verbosity=info $(HADDOCK_PKGS) --exec="'echo done'" hledger-lib --open # # Rerenders/reopens the Hledger module, without submodules. (Fastest) # haddock-watch: \ # $(call def-help,haddock-watch3, quickly regenerate & reload Hledger.hs haddock when files change ) # watchexec -r -e yaml,cabal,hs --print-events --shell=none -- bash -c 'mkdir -p tmp && rm -f tmp/Hledger.html && haddock -h -o tmp hledger-lib/Hledger.hs --no-warnings --no-print-missing-docs 2>&1 | grep -v "Could not find documentation" && open tmp/Hledger.html' # haddock-open: \ # $(call def-help,haddock-open, open the haddock packages contents page in a browser ) # $(BROWSE) `stack path --local-install-root`/doc/index.html # hoogle-setup: $(call def-help,hoogle-setup, install hoogle then build haddocks and a hoogle db for the project and all deps ) # stack hoogle --rebuild # HOOGLEBROWSER="/Applications/Firefox Dev.app/Contents/MacOS/firefox" # safari not supported # hoogle-serve: $(call def-help,hoogle-serve, run hoogle web app and open in browser after doing setup if needed ) # $(HOOGLEBROWSER) http://localhost:8080 & # stack --verbosity=warn hoogle --server # # sourcegraph: \ # # $(call def-help,sourcegraph,\ # # \ # # ) # # for p in $(PACKAGES); do (cd $$p; SourceGraph $$p.cabal); done # manuals-watch: Shake \ # $(call def-help,manuals-watch, rerender manuals when their source files change ) # ls $(DOCSOURCEFILES) | entr ./Shake -VV manuals # man-watch: man-watch-hledger \ # $(call def-help,man-watch, run man on the hledger man page when its source file changes ) # man-watch-%: Shake \ # $(call def-help,man-watch-PROG, run man on the given man page when its source file changes. Eg make man-watch-hledger-web ) # $(WATCHEXEC) -r -w $*/$*.m4.md './Shake $*/$*.1 && man $*/$*.1' # shakehelp-watch: \ # $(call def-help,shakehelp-watch, rerender Shake.hs's help when it changes) # ls Shake.hs | entr -c ./Shake.hs # # The following rule, for updating the website, gets called on hledger.org by: # # 1. github-post-receive (github webhook handler), when something is pushed # # to the main or wiki repos on Github. Config: # # /etc/supervisord.conf -> [program:github-post-receive] # # /etc/github-post-receive.conf # # 2. cron, nightly. Config: /etc/crontab # # 3. manually: "make site" on hledger.org, or "make hledgerorg" elsewhere (cf Makefile.local). # .PHONY: site # # Use the existing Shake executable without recompiling it, so as not to automatially run unreviewed code by hook ? I think this no longer applies. # # site: $(call def-help,site-build, update the hledger.org website (run this on hledger.org, or run "make hledgerorg" elsewhere) ) # # @[ ! -x Shake ] \ # # && echo 'Please run "make Shake" first (manual compilation required for safety)' \ # # || ( \ # # echo; \ # # ./Shake -V site; \ # # ) 2>&1 | tee -a site.log # site: Shake \ # $(call def-help,site, update the hledger.org website (run on hledger.org, or run "make hledgerorg" elsewhere) ) # ./Shake -V site 2>&1 | tee -a site.log # BROWSE=open # BROWSEDELAY=5 # LOCALSITEURL=http://localhost:3000/dev/hledger.html # site-watch: $(call def-help,site-watch, open a browser on the website (in ./site) and rerender when docs or web pages change ) # @make -s Shake # @(printf "\nbrowser will open in $(BROWSEDELAY)s (adjust BROWSE in Makefile if needed)...\n\n"; sleep $(BROWSEDELAY); $(BROWSE) $(LOCALSITEURL)) & # @$(WATCHEXEC) --print-events -e md,m4 -i hledger.md -i hledger-ui.md -i hledger-web.md -r './Shake webmanuals && ./Shake orgfiles && make -sC site serve' # ** installing INSTALLING: # # copy the current ~/.local/bin/hledger to bin/old/hledger-VER # @copy-as VER: # cp ~/.local/bin/hledger bin/old/hledger-{{ VER }}; echo "bin/hledger-{{ VER }}" # stack install, then copy the hledger executables to bin/old/hledger*-VER @installas VER: {{ STACK }} install --local-bin-path bin/old for e in hledger hledger-ui hledger-web ; do cp bin/old/$e bin/old/$e-{{ VER }}; echo "bin/$e-{{ VER }}"; done # # make must be GNU Make 4.3+ # .PHONY: shellcompletions # # update shell completions in hledger package # shellcompletions: # make -C hledger/shell-completion/ clean-all all # ** releasing RELEASING: # Symlink/copy important files temporarily in .relfiles/. relfiles: #!/usr/bin/env bash echo "linking/copying important release files in .relfiles/ for convenient access..." mkdir -p .relfiles cd .relfiles for f in \ ../stack.yaml \ ../Shake.hs \ ../hledger-install/hledger-install.sh \ ../CHANGES.md \ ../hledger/CHANGES.md \ ../hledger-ui/CHANGES.md \ ../hledger-web/CHANGES.md \ ../hledger-lib/CHANGES.md \ ../doc/github-release.md \ ../doc/ANNOUNCE \ ../doc/ANNOUNCE.masto \ ../site/src/release-notes.md \ ../site/src/install.md \ ; do ln -sf $f .; done cp ../doc/RELEASING.md ./RELEASING2.md # temp copy which can be edited without disruption # Prepare to release today, creating/switching to release branch, updating versions, manuals, changelogs etc. relprep VER: #!/usr/bin/env bash [[ -z {{ VER }} ]] && usage BRANCH=$(just _versionReleaseBranch {{ VER }}) COMMIT="-c" echo "Switching to $BRANCH, auto-creating it if needed..." _gitSwitchAutoCreate "$BRANCH" echo "Bumping all version strings to {{ VER }} ..." ./Shake setversion {{ VER }} $COMMIT echo "Updating all command help texts for embedding..." ./Shake cmdhelp $COMMIT echo "Updating all dates in man pages..." ./Shake mandates echo "Generating all the manuals in all formats...." ./Shake manuals $COMMIT echo "Updating CHANGES.md files with latest commits..." ./Shake changelogs $COMMIT # Push the current branch to github to generate release binaries. @relbin: # assumes the github remote is named "github" git push -f github HEAD:binaries # *** hledger version number helpers # (as hidden recipes, since just doesn't support global custom functions) # See doc/RELEASING.md > Glossary. # First 0-2 parts of a dotted version number. @_versionMajorPart VER: echo {{ replace_regex(VER, '(\d+(\.\d+)?).*', '$1') }} # Third part of a dotted version number, if any. @_versionMinorPart VER: echo {{ if VER =~ '\d+(\.\d+){2,}' { replace_regex(VER, '\d+\.\d+\.(\d+).*', '$1') } else { '' } }} # Fourth part of a dotted version number, if any. @_versionFourthPart VER: echo {{ if VER =~ '\d+(\.\d+){3,}' { replace_regex(VER, '\d+(\.\d+){2}\.(\d+).*', '$2') } else { '' } }} # Does this dotted version number have a .99 third part and no fourth part ? @_versionIsDev VER: echo {{ if VER =~ '(\d+\.){2}99$' { 'y' } else { '' } }} # Does this dotted version number have a .99 third part and a fourth part ? @_versionIsPreview VER: echo {{ if VER =~ '(\d+\.){2}99\.\d+' { 'y' } else { '' } }} # Increment a major version number to the next. # @majorVersionIncrement MAJORVER: # python3 -c "print({{MAJORVER}} + 0.01)" # Appropriate release branch name for the given version number. _versionReleaseBranch VER: #!/usr/bin/env bash MAJOR=$(just _versionMajorPart {{ VER }}) if [[ $(just _versionIsDev {{ VER }}) == y ]] then echo "{{ VER }} is not a releasable version" >&2 exit 1 elif [[ $(just _versionIsPreview {{ VER }}) == y ]] then # echo "$(just majorVersionIncrement "$MAJOR")-branch" echo "{{ VER }} is not a releasable version" >&2 exit 1 else echo "$MAJOR-branch" fi # *** git helpers # Does the named branch exist in this git repo ? @_gitBranchExists BRANCH: git branch -l {{ BRANCH }} | grep -q {{ BRANCH }} # Switch to the named git branch, creating it if it doesn't exist. _gitSwitchAutoCreate BRANCH: #!/usr/bin/env bash if just _gitBranchExists {{ BRANCH }}; then git switch {{ BRANCH }} else git switch -c {{ BRANCH }} fi # ############################################################################### # $(call def-help-subheading,RELEASING:) # # old/desired release process: # # a normal release: echo 0.7 >.version; make release # # a bugfix release: echo 0.7.1 >.version; make release # #release: releasetest bumpversion tagrelease $(call def-help,release, prepare and test a release and tag the repo ) # #publish: hackageupload pushtags $(call def-help,upload, publish latest hackage packages and push tags to github ) # #releaseandpublish: release upload $(call def-help,releaseandpublish, release and upload and publish updated docs ) # ISCLEAN=git diff-index --quiet HEAD -- # # stop if the working directory has uncommitted changes # iscleanwd: # @$(ISCLEAN) || (echo "please clean the working directory first"; false) # # stop if the given file(s) have uncommitted changes # isclean-%: # @$(ISCLEAN) $* || (echo "please clean these files first: $*"; false) # # update all cabal files based on latest package.yaml files using stack's built-in hpack # cabal: $(call def-help,cabal, regenerate cabal files from package.yaml files with stack ) # $(STACK) build --dry-run --silent # # Update all cabal files based on latest package.yaml files using a specific hpack version. # # To avoid warnings, this should be the same version as stack's built-in hpack. # cabal-with-hpack-%: # $(STACK) build --with-hpack hpack-$* --dry-run --silent # # updatecabal: gencabal $(call def-help,updatecabal, regenerate cabal files and commit ) # # @read -p "please review changes then press enter to commit $(shell ls */*.cabal)" # # git commit -m "update cabal files" $(shell ls */*.cabal) # # we use shake for this job; so dependencies aren't checked here # manuals: Shake $(call def-help,manuals, regenerate and commit CLI help and manuals (might need -B) ) # ./Shake manuals # git commit -m ";doc: regen manuals" -m "[ci skip]" hledger*/hledger*.{1,5,info,txt} hledger/Hledger/Cli/Commands/*.txt # tag: $(call def-help,tag, make git release tags for the project and all packages ) # @for p in $(PACKAGES); do make tag-$$p; done # @make tag-project # tag-%: $(call def-help,tag-PKG, make a git release tag for PKG ) # git tag -fs $*-`cat $*/.version` -m "Release $*-`cat $*/.version`" # tag-project: $(call def-help,tag-project, make a git release tag for the project as a whole ) # git tag -fs `cat .version` -m "Release `cat .version`, https://hledger.org/release-notes.html#hledger-`cat .version | sed -e 's/\./-/g'`" # @printf "if tagging a major release, please also review and run this command:\n" # @printf " git tag -fs `cat .version`.99 master -m \"Start of next release cycle. This tag influences git describe and dev builds' version strings.\"\n" # # hackageupload-dry: \ # # $(call def-help,hackageupload-dry,\ # # upload all packages to hackage; dry run\ # # ) # # for p in $(PACKAGES); do cabal upload $$p/dist/$$p-$(VERSION).tar.gz -v2 --check; done # hackageupload: \ # $(call def-help,hackageupload, upload all packages to hackage from a release branch) # tools/hackageupload $(PACKAGES) # # showreleasestats stats: \ # # showreleasedays \ # # showunreleasedchangecount \ # # showloc \ # # showtestcount \ # # showunittestcoverage \ # # showreleaseauthors \ # # showunreleasedcodechanges \ # # showunpushedchanges \ # # $(call def-help,showreleasestats stats,\ # # show project stats useful for release notes\ # # ) # # # showerrors # # FROMTAG=. # # showreleasedays: \ # # $(call def-help,showreleasedays,\ # # \ # # ) # # @echo Days since last release: # # @tools/dayssincetag.hs $(FROMTAG) | head -1 | cut -d' ' -f-1 # # @echo # # # XXX # # showunreleasedchangecount: \ # # $(call def-help,showunreleasedchangecount,\ # # \ # # ) # # @echo Commits since last release: # # @darcs changes --from-tag $(FROMTAG) --count # # @echo # describe: $(call def-help,describe, show a precise git-describe version string ) # @git describe --tags --match 'hledger-[0-9]*' --dirty # # showreleaseauthors: $(call def-help,showreleaseauthors, show author names since last release) # # @echo Commit authors since last release: # # @git shortlog -sn $(CHANGELOGSTART).. # TODO undefined # showauthors: $(call def-help,showauthors, show all commit author names) # @echo "Commit authors ($$(git shortlog -sn | wc -l | awk '{print $$1}'))": # @git shortlog -sn # cloc: $(call def-help,cloc, count lines of source code ) # @echo Lines of code including tests: # @cloc --exclude-lang=HTML --exclude-dir=.stack-work,.idea,dist,old,bin,doc,site,.tutorial-data,static,angular . # SCC=scc -z --cocomo-project-type semi-detached -f wide -s code # scc: $(call def-help,scc, count lines of source code with scc) # @echo Lines of code including tests: # @$(SCC) -i hs,sh,m4,hamlet # sccv: $(call def-help,sccv, count lines of source code with scc showing all files) # @echo Lines of code including tests: # @$(SCC) -i hs,sh,m4,hamlet --by-file # # `ls $(SOURCEFILES)` # # sloc: \ # # $(call def-help,sloc,\ # # \ # # ) # # @sloccount hledger-lib hledger hledger-web # # cloc: \ # # $(call def-help,cloc,\ # # \ # # ) # # @echo # # @echo "Lines of code as of `date`:" # # @echo # # @echo "hledger-lib, hledger" # # @cloc -q hledger-lib hledger 2>&1 | grep -v 'defined(' # # @echo # # @echo "hledger-web" # # @cloc -q hledger-web 2>&1 | grep -v 'defined(' # # @echo # # @echo "hledger-lib, hledger, hledger-web" # # @cloc -q hledger-lib hledger hledger-web 2>&1 | grep -v 'defined(' # # showtestcount: \ # # $(call def-help,showtestcount,\ # # \ # # ) # # @echo "Unit tests:" # # @hledger test 2>&1 | cut -d' ' -f2 # # @echo "Functional tests:" # # @make --no-print functest | egrep '^ Total' | awk '{print $$2}' # # @echo # # showunittestcoverage: \ # # $(call def-help,showunittestcoverage,\ # # \ # # ) # # @echo Unit test coverage: # # @make --no-print quickcoverage | grep 'expressions' # # @echo # # # showerrors: # # # @echo Known errors: # # # @awk '/^** errors/, /^** / && !/^** errors/' NOTES.org | grep '^\*\*\* ' | tail +1 # # # @echo # # # XXX # # showunpushedchanges showunpushed: \ # # $(call def-help,showunpushedchanges showunpushed,\ # # \ # # ) # # @echo "Changes not yet pushed upstream (to `darcs show repo | grep 'Default Remote' | cut -c 17-`):" # # @-darcs push simon@joyful.com:/repos/hledger --dry-run | grep '*' | tac # # @echo # # # XXX # # showunreleasedcodechanges showunreleased showchanges: \ # # $(call def-help,showunreleasedcodechanges showunreleased showchanges,\ # # \ # # ) # # @echo "hledger code changes since last release:" # # @darcs changes --from-tag $(FROMTAG) --matches "not (name docs: or name doc: or name site: or name tools:)" | grep '*' # # @echo # # # XXX # # showcodechanges: \ # # $(call def-help,showcodechanges,\ # # \ # # ) # # @echo "hledger code changes:" # # @darcs changes --matches "not (name docs: or name site: or name tools:)" | egrep '^ +(\*|tagged)' # # @echo # nix-hledger-version: $(call def-help,nix-hledger-version, show which version of hledger has reached nixpkgs) # @curl -s https://raw.githubusercontent.com/NixOS/nixpkgs/master/pkgs/development/haskell-modules/hackage-packages.nix | grep -A1 'pname = "hledger"' # nix-hledger-versions: $(call def-help,nix-hledger-versions, show versions of all hledger packages in nixpkgs) # @curl -s https://raw.githubusercontent.com/NixOS/nixpkgs/master/pkgs/development/haskell-modules/hackage-packages.nix | grep -A1 'pname = "hledger' # nix-view-commits: $(call def-help,nix-view-commits, show recent haskell commits in nixpkgs) # @open 'https://github.com/NixOS/nixpkgs/commits/master/pkgs/development/haskell-modules/hackage-packages.nix' # list-commits: $(call def-help,list-commits, list all commits chronologically and numbered) # @git log --format='%ad %h %s (%an)' --date=short --reverse | cat -n # ** misc MISC: # push to github CI, wait for tests to pass, then push to master @push: tools/push # run some tests to validate the development environment # check-setup: # run some tests to validate the development environment\ # ) # @echo sanity-checking developer environment: # @({{ SHELLTEST }} checks \ # && echo $@ PASSED) || echo $@ FAILED # sym-link some directories required by hledger-web dev builds mkwebdirs: echo "#ln -sf hledger-web/config # disabled, causes makeinfo warnings" ln -sf hledger-web/messages ln -sf hledger-web/static ln -sf hledger-web/templates # Show last week's activity, for TWIH @_lastweek: echo "hledger time last 7 days including today (this should be run on a Friday):" tt bal hledger -DTS -b '6 days ago' --drop 2 echo echo "By activity type, percentage:" tt bal hledger -DTS -b '6 days ago' --pivot t -% -c 1% | tail +1 echo echo "Time log details:" tt print hledger -b '6 days ago' | grep -E '^[^ ]|hledger' echo echo "main repo:" git log --format='%C(yellow)%cd %ad %Cred%h%Creset %s %Cgreen(%an)%Creset%C(bold blue)%d%Creset' --date=short --since="6 days ago" --reverse echo echo "site repo:" git -C site log --format='%C(yellow)%cd %ad %Cred%h%Creset %s %Cgreen(%an)%Creset%C(bold blue)%d%Creset' --date=short --since="6 days ago" --reverse echo echo "finance repo:" git -C finance log --format='%C(yellow)%cd %ad %Cred%h%Creset %s %Cgreen(%an)%Creset%C(bold blue)%d%Creset' --date=short --since="6 days ago" --reverse echo # show the sorted, unique files matched by SOURCEFILES @_listsourcefiles: for f in {{ SOURCEFILES }}; do echo $f; done | sort | uniq # show the sorted, unique subdirectories containing hs files @_listsourcedirs: find . -name '*hs' | sed -e 's%[^/]*hs$%%' | sort | uniq # show the ghc versions used by all stack files @_listghcversions: for F in stack*.yaml; do {{ STACK }} --stack-yaml=$F --no-install-ghc exec -- ghc --version; done 2>&1 | grep -v 'To install the correct GHC' # Show a bunch of debug messages. @_dbgmsgs: rg --sort path -t hs 'dbg.*?(".*")' -r '$1' -o # # Extract Hledger.hs examples to jargon.j. # @_jargon: # rg '^ *> (.*)' -or '$1' hledger-lib/Hledger.hs > jargon.j # echo "wrote jargon.j" # Extract ledger/hledger/beancount commit stats to project-commits.j. @_projectcommits: # https://hledger.org/reporting-version-control-stats.html printf "account ledger\naccount hledger\naccount beancount\n\n" >project-commits.j for p in ledger hledger beancount; do git -C ../$p log --format="%cd (%h) %s%n ($p) 1%n" --date=short --reverse >> project-commits.j; done echo "wrote project-commits.j" # ############################################################################### # $(call def-help-subheading,MISCELLANEOUS:) # installcommithook: $(call def-help,installcommithook, symlink tools/commitlint as .git/hooks/commit-msg) # ln -s ../../tools/commitlint .git/hooks/commit-msg # watch-%: $(call def-help,watch-RULE, run make RULE repeatedly when any committed file changes) # @git ls-files | entr -r make $* # Shake: Shake.hs $(call def-help,Shake, ensure the Shake script is compiled ) # ./Shake.hs # usage: cabalusage stackusage \ # $(call def-help,usage, show size of various dirs ) # du -sh .git bin data doc extra # du -sh . # stackusage: \ # $(call def-help,stackusage, show size of stack working dirs if any ) # -du -shc `find . -name '.stack*'` # cabalusage: \ # $(call def-help,cabalusage, show size of cabal working dirs if any ) # -du -shc */dist* 2>/dev/null # # Generate an emacs TAGS file. Tag: # # 1. haskell source files with hasktags # # 2. other source files recognised by (exuberant) ctags and not excluded by .ctags. Keep .ctags up to date. # # 3. some extra files missed by the above, as just their file names (for tags-search, tags-query-replace etc.) # etags:$(call def-help,etags, generate emacs TAGS file for haskell source and other project files ) # hasktags -e $(SOURCEFILES) # ctags -a -e -R # for f in \ # $(WEBTEMPLATEFILES) \ # $(DOCSOURCEFILES) \ # $(TESTFILES) \ # $(HPACKFILES) \ # $(CABALFILES) \ # Shake.hs \ # ; do printf " \n$$f,1\n" >> TAGS; done # -etagsls >TAGS.files # etags-ls: # list files indexed in TAGS # @rg -v '[  ]' TAGS | rg -r '$$1' '^(.*?)([0-9]+)?,[0-9,]+*' # cleantags: \ # $(call def-help-hide,cleantags, remove tag files ) # rm -f TAGS tags # stackclean: \ # $(call def-help-hide,stackclean, remove .stack-work/ dirs ) # $(STACK) purge # cleanghco: \ # $(call def-help-hide,cleanghc, remove ghc build leftovers ) # rm -rf `find . -name "*.o" -o -name "*.hi" -o -name "*.dyn_o" -o -name "*.dyn_hi" -o -name "*~" | grep -vE '\.(stack-work|cabal-sandbox|virthualenv)'` # #rm -f `fd -I '\.(hi|o|dyn_hi|dyn_o)$'` # clean: cleanghco \ # $(call def-help,clean, default cleanup (ghc build leftovers) ) # Clean: stackclean cleanghco cleantags \ # $(call def-help,Clean, thorough cleanup (stack/ghc leftovers/tags) ) # # reverse = $(if $(wordlist 2,2,$(1)),$(call reverse,$(wordlist 2,$(words $(1)),$(1))) $(firstword $(1)),$(1))