1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
|
# SPDX-License-Identifier: GPL-3.0-or-later
# Works with GNU make and BSD make.
DESTDIR ?=
PREFIX ?= $(HOME)/.local
BINDIR = $(DESTDIR)$(PREFIX)/bin
SHAREDIR = $(DESTDIR)$(PREFIX)/share
MANDIR = $(SHAREDIR)/man
DOCDIR = $(SHAREDIR)/doc/krino
BIN = krino
# The tag's "v" is dropped, so dist directories read krino-0.0.8-..., as
# `make release VERSION=0.0.8` names them.
VERSION != (git describe --tags --always --dirty 2>/dev/null || echo dev) | sed 's/^v//'
LDFLAGS = -s -w -X main.version=$(VERSION)
# VERSION reaches -ldflags and directory names, so every target using it
# refuses anything but a plain version string.
CHECK_VERSION = case '$(VERSION)' in ''|*[!A-Za-z0-9._+-]*) echo "refusing: VERSION must contain only letters, digits, and the characters . - _ +" >&2; exit 1 ;; esac
CROSS_PLATFORMS = linux/amd64 linux/arm64 freebsd/amd64 openbsd/amd64
.PHONY: all help build install uninstall test vet fmt lint ci bench fuzz race vulncheck cross dist release deps install-hooks clean
all: build
help: ## show this help
@grep -E '^[a-z-]+:.*## ' Makefile | \
awk 'BEGIN {FS = ":.*## "}; {printf " %-10s %s\n", $$1, $$2}'
@echo " version: $(VERSION) prefix: $(PREFIX)"
build: ## build ./krino; Go fetches module dependencies itself; reports missing optional extractors
@$(CHECK_VERSION)
CGO_ENABLED=0 go build -trimpath -ldflags "$(LDFLAGS)" -o $(BIN) ./cmd/krino
@missing=""; \
for t in pdftotext antiword catdoc xls2csv catppt; do \
command -v "$$t" >/dev/null 2>&1 || missing="$$missing $$t"; \
done; \
if [ -n "$$missing" ]; then \
echo "optional extractors not found:$$missing (needed for some content rules; run 'make deps' or install by hand)"; \
else \
echo "all optional extractors found: pdftotext antiword catdoc xls2csv catppt"; \
fi
install: build ## install binary, man pages and examples under $(PREFIX)
mkdir -p $(BINDIR) $(MANDIR)/man1 $(MANDIR)/man5 $(DOCDIR)/examples
rm -f $(BINDIR)/$(BIN)
install -m 755 $(BIN) $(BINDIR)/$(BIN)
install -m 644 man/krino.1 $(MANDIR)/man1/krino.1
install -m 644 man/krino.conf.5 $(MANDIR)/man5/krino.conf.5
install -m 644 examples/*.conf $(DOCDIR)/examples/
install -m 644 docs/sexp-primer.md $(DOCDIR)/sexp-primer.md
uninstall: ## remove everything install placed, and nothing else
rm -f $(BINDIR)/$(BIN)
rm -f $(MANDIR)/man1/krino.1
rm -f $(MANDIR)/man5/krino.conf.5
rm -f $(DOCDIR)/sexp-primer.md
for f in examples/*.conf; do \
rm -f $(DOCDIR)/examples/$$(basename "$$f"); \
done
-rmdir $(DOCDIR)/examples $(DOCDIR) 2>/dev/null
test: ## run the tests
go test ./...
vet: ## go vet
go vet ./...
fmt: ## gofmt the tree
gofmt -w .
lint: ## lint the man pages
scripts/man-lint
ci: ## the gate: gofmt, vet, tests, dependencies, no personal data staged, man page lint
@test -z "$$(gofmt -l .)" || { echo "gofmt needed:"; gofmt -l .; exit 1; }
go vet ./...
GOOS=freebsd CGO_ENABLED=0 go vet ./...
GOOS=openbsd CGO_ENABLED=0 go vet ./...
go test ./...
@deps=$$(for os in linux freebsd openbsd; do GOOS=$$os CGO_ENABLED=0 go list -deps -test -f '{{with .Module}}{{.Path}}{{end}}' ./... || echo "go list failed for $$os"; done | sort -u | grep -vxE '(krino|golang\.org/x/(term|text|sys))?' || true); \
test -z "$$deps" || { echo "unexpected dependencies:"; echo "$$deps"; exit 1; }
@scripts/leak-check
@scripts/man-lint
@echo "ci ok"
bench: ## run the Go benchmarks on generated trees
go test -run '^$$' -bench . -benchmem ./...
# FUZZ_TARGETS lists every fuzz target as package-directory:FuzzName.
FUZZTIME ?= 20s
FUZZ_TARGETS = \
internal/sexp:FuzzParse \
internal/journal:FuzzEscapeRoundTrip \
internal/journal:FuzzParseLine \
internal/journal:FuzzEntryRoundTrip \
internal/trash:FuzzPercentRoundTrip \
internal/trash:FuzzParsePath \
internal/sexp:FuzzQuoteRoundTrip \
internal/config:FuzzParseSize \
internal/config:FuzzParseDuration \
internal/config:FuzzPrintRule \
internal/norm:FuzzTextIdempotent \
internal/norm:FuzzFoldMapped \
internal/plan:FuzzExpand \
internal/ignore:FuzzMatch \
internal/kwcache:FuzzLoad \
internal/extract:FuzzStripMarkup \
internal/extract:FuzzZipText \
cmd/krino:FuzzDisplay
fuzz: ## run every fuzz target for FUZZTIME each (default 20s); a crasher is saved under testdata/fuzz
@for t in $(FUZZ_TARGETS); do \
pkg=$${t%%:*}; name=$${t##*:}; \
echo "fuzz $$pkg $$name ($(FUZZTIME))"; \
go test -run '^$$' -fuzz "^$$name$$" -fuzztime $(FUZZTIME) ./$$pkg || exit 1; \
done
race: ## run the tests under the race detector (needs cgo and a C compiler)
CGO_ENABLED=1 go test -race ./...
# govulncheck is pinned; it runs on the toolchain go.mod names, so the
# standard library it checks is the one release builds use.
vulncheck: ## check the standard library and dependencies against the Go vulnerability database (network)
go run golang.org/x/vuln/cmd/govulncheck@v1.8.0 ./...
cross: ## cross-compile linux/amd64, linux/arm64, freebsd/amd64, openbsd/amd64 into dist/krino-$(VERSION)-<os>-<arch>/
@$(CHECK_VERSION)
@for t in $(CROSS_PLATFORMS); do \
os=$${t%/*}; arch=$${t#*/}; \
dir=dist/krino-$(VERSION)-$$os-$$arch; \
case "$$dir" in \
dist/krino-*) rm -rf "$$dir" ;; \
*) echo "refusing to remove unexpected path: $$dir" >&2; exit 1 ;; \
esac; \
mkdir -p "$$dir"; \
echo "cross: $$os/$$arch -> $$dir/$(BIN)"; \
CGO_ENABLED=0 GOOS=$$os GOARCH=$$arch go build -trimpath -ldflags "$(LDFLAGS)" -o "$$dir/$(BIN)" ./cmd/krino || exit 1; \
for f in README.md LICENSE CHANGELOG.md man/krino.1 man/krino.conf.5 docs/sexp-primer.md; do \
test -f "$$f" && cp "$$f" "$$dir/$$(basename "$$f")"; \
done; \
test -d examples && cp -r examples "$$dir/examples"; \
done
# dist: cross plus tarballs and SHA256SUMS, no tag. Undocumented plumbing so
# `make release` is verifiable without tagging; deliberately absent from
# `make help` (no `## ` comment).
dist: cross
@command -v sha256sum >/dev/null 2>&1 && SUM=sha256sum || SUM="sha256 -r"; \
tarballs=""; \
cd dist && \
for t in $(CROSS_PLATFORMS); do \
os=$${t%/*}; arch=$${t#*/}; \
name=krino-$(VERSION)-$$os-$$arch; \
tar czf "$$name.tar.gz" "$$name" || exit 1; \
tarballs="$$tarballs $$name.tar.gz"; \
done && \
$$SUM $$tarballs > SHA256SUMS
release: ## make cross + tarballs + SHA256SUMS in dist/, then tag v$(VERSION) (annotated; never pushes) - usage: make release VERSION=0.0.1
@$(CHECK_VERSION)
@default_version=$$( (git describe --tags --always --dirty 2>/dev/null || echo dev) | sed 's/^v//'); \
test -n "$(VERSION)" && test "$(VERSION)" != "$$default_version" || \
{ echo "refusing: pass an explicit VERSION, e.g. make release VERSION=0.0.1"; exit 1; }
@test -z "$$(git status --porcelain)" || \
{ echo "refusing: working tree is not clean (git status --porcelain)"; exit 1; }
@git rev-parse -q --verify refs/tags/v$(VERSION) >/dev/null 2>&1 && \
{ echo "refusing: tag v$(VERSION) already exists"; exit 1; } || true
@test -f README.md || \
{ echo "refusing: README.md is missing"; exit 1; }
$(MAKE) ci
$(MAKE) dist VERSION=$(VERSION)
git tag -a "v$(VERSION)" -m "krino v$(VERSION)"
@echo "release v$(VERSION) built in dist/; tag v$(VERSION) created, not pushed"
deps: ## install the optional content extractors; may ask for privileges
scripts/deps
install-hooks: ## install the pre-commit hook that runs the leak check
install -m 755 scripts/hooks/pre-commit "$$(git rev-parse --git-path hooks)/pre-commit"
clean: ## remove build output
rm -f $(BIN)
|