Repository navigation
Expand file tree
/
Copy pathMakefile
More file actions
247 lines (208 loc) · 9.88 KB
/
Copy pathMakefile
File metadata and controls
247 lines (208 loc) · 9.88 KB
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
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
#
# Makefile for irdmtools, a Go package and command line tools.
#
# Everyday targets: build, test, test-live, test-release, install, uninstall, clean
# Release targets: generate, release, dist (see "Releasing" below)
#
# A plain `make`, `make build`, `make test` and `make install` need only git,
# make, go and pandoc. `cmt` is run only by `make generate` and by
# `make release`, never by the everyday targets, so a fresh clone builds with
# the committed version.go, CITATION.cff and installers.
#
PROJECT = irdmtools
GIT_GROUP = caltechlibrary
PROGRAMS = $(shell ls -1 cmd)
# version.go is left out: it has a rule (cmt, under generate), and listing it
# here would make every build run cmt through that rule.
PACKAGE = $(shell ls -1 *.go | grep -v 'version.go')
MAN_PAGES = $(shell ls -1 docs/*.1.md 2>/dev/null | sed -E 's|^docs/||; s/\.1.md/.1/g')
VERSION = $(shell grep '"version":' codemeta.json | cut -d\" -f 4)
RELEASE_HASH = $(shell git log --pretty=format:'%h' -n 1)
BRANCH = $(shell git branch | grep '* ' | cut -d\ -f 2)
OS = $(shell uname)
PREFIX = $(HOME)
ifneq ($(prefix),)
PREFIX = $(prefix)
endif
EXT =
ifeq ($(OS), Windows)
EXT = .exe
endif
# One entry per release platform: name:GOOS:GOARCH
PLATFORMS = Linux-x86_64:linux:amd64 Linux-aarch64:linux:arm64 \
macOS-x86_64:darwin:amd64 macOS-arm64:darwin:arm64 \
Windows-x86_64:windows:amd64 Windows-arm64:windows:arm64
build: $(PROGRAMS)
@$(MAKE) man
# Building a program also writes its manual page (the program's own -help
# output) into docs/, which is where the documentation site is built from, and
# one page per help topic, docs/NAME-TOPIC.1.md, from "NAME -help topics" and
# "NAME -help TOPIC". The old topic pages are removed first, so a renamed or
# removed action does not leave a page behind; a program that cannot list its
# topics stops the build. man is a sub-make so that it sees the pages written
# here: MAN_PAGES is read when the Makefile is read.
$(PROGRAMS): $(PACKAGE)
@mkdir -p bin
go build -o "bin/$@$(EXT)" ./cmd/$@
@mkdir -p docs
@./bin/$@$(EXT) -help >docs/$@.1.md
@TOPICS=$$(./bin/$@$(EXT) -help topics) || exit 1; \
rm -f docs/$@-*.1.md; \
for TOPIC in $$(echo "$$TOPICS" | grep -v '^#' | awk '{print $$1}'); do \
./bin/$@$(EXT) -help $$TOPIC >docs/$@-$$TOPIC.1.md || exit 1; \
done
# man renders each docs/NAME.1.md into man/man1/NAME.1.
man: $(MAN_PAGES)
$(MAN_PAGES): .FORCE
@mkdir -p man/man1
pandoc docs/$@.md --from markdown --to man -s >man/man1/$@
# test runs offline: vet, then the Go tests. The ones that need an RDM or
# Postgres configuration skip themselves, and the ones that call public APIs
# (DataCite, CrossRef, ROR) skip unless -live is given.
test: .FORCE
go vet ./...
go test ./...
# test-live also calls the public APIs. CrossRef and DataCite ask callers to
# identify themselves; the tests send the address in the environment variable
# TEST_MAILTO and skip the ones that need it when it is not set.
test-live: check-mailto .FORCE
go test . -live
# check-mailto says so when TEST_MAILTO is not set, so a skipped live test is
# not a surprise. It does not stop the run.
check-mailto: .FORCE
@if [ -z "$$TEST_MAILTO" ]; then echo "TEST_MAILTO is not set: the live tests that call CrossRef and DataCite will be skipped."; echo "Set it to your email address, e.g. TEST_MAILTO=you@example.edu make test-live"; fi
# test-release runs the tests of release.bash, and of release.ps1 when pwsh is
# installed. They use throwaway repositories and a stub gh, so nothing reaches
# GitHub.
test-release: .FORCE
./release_test.bash
@if command -v pwsh >/dev/null 2>&1; then RELEASE_IMPL=ps1 ./release_test.bash; else echo "pwsh not found: release.ps1 not tested"; fi
# check vets the code and lists, without changing them, files gofmt would
# reformat. Existing files are not reformatted as a side effect of other work.
check: .FORCE
go vet ./...
@UNFORMATTED="$$(gofmt -l . )"; if [ -n "$$UNFORMATTED" ]; then echo "not gofmt-formatted (left as is):"; echo "$$UNFORMATTED"; fi
# NOTE: on macOS you must use "mv" instead of "cp" to avoid problems
install: build .FORCE
@if [ ! -d $(PREFIX)/bin ]; then mkdir -p $(PREFIX)/bin; fi
@echo "Installing programs in $(PREFIX)/bin"
@for FNAME in $(PROGRAMS); do if [ -f ./bin/$$FNAME$(EXT) ]; then mv -v ./bin/$$FNAME$(EXT) $(PREFIX)/bin/$$FNAME$(EXT); fi; done
@echo ""
@echo "Make sure $(PREFIX)/bin is in your PATH"
@echo ""
@if [ ! -d $(PREFIX)/man/man1 ]; then mkdir -p $(PREFIX)/man/man1; fi
@for MAN_PAGE in $(MAN_PAGES); do cp -v man/man1/$$MAN_PAGE $(PREFIX)/man/man1/;done
@echo ""
@echo "Make sure $(PREFIX)/man is in your MANPATH"
@echo ""
uninstall: .FORCE
@echo "Removing programs in $(PREFIX)/bin"
-for FNAME in $(PROGRAMS); do if [ -f $(PREFIX)/bin/$$FNAME$(EXT) ]; then rm -v $(PREFIX)/bin/$$FNAME$(EXT); fi; done
-for MAN_PAGE in $(MAN_PAGES); do if [ -f "$(PREFIX)/man/man1/$$MAN_PAGE" ]; then rm "$(PREFIX)/man/man1/$$MAN_PAGE"; fi; done
hash: .FORCE
@git log --pretty=format:'%h' -n 1
# clean never removes version.go: it is committed, so that a fresh clone builds.
clean: .FORCE
-if [ -d bin ]; then rm -fR bin; fi
-if [ -d dist ]; then rm -fR dist; fi
-if [ -d man ]; then rm -fR man; fi
-if [ -d testout ]; then rm -fR testout; fi
status:
git status
save:
@git commit -am "$${msg:-Quick Save}"
git push origin $(BRANCH)
refresh:
git fetch origin
git pull origin $(BRANCH)
#
# Releasing
#
# 1. Edit codemeta.json by hand: version, dateModified, releaseNotes.
# 2. make release regenerates version.go, CITATION.cff and both installers
# from codemeta.json with cmt (the only place cmt runs
# besides an explicit `make generate`), rebuilds the
# programs and their manual pages from that version.go,
# runs the tests, builds one zip per platform in dist/ and
# runs release-check, which stops here if anything does
# not carry the version and the hash of the commit, or a
# zip lacks one of the programs.
# 3. ./release.bash refuses if anything beyond those files is uncommitted,
# asks for confirmation, commits and pushes the regenerated
# files ("prep for vX.Y.Z"), and creates the DRAFT GitHub
# release from dist/*.zip. Publish the draft from the
# repository's releases page (or with gh release edit).
#
# Run `make test-release` after changing release.bash, release.ps1 or
# release_test.bash.
#
# generate runs cmt over codemeta.json. Run it when the version or the
# metadata change; a plain `make` leaves the committed copies alone.
generate: version.go CITATION.cff installer.sh installer.ps1
version.go: .FORCE
cmt codemeta.json version.go
CITATION.cff: .FORCE
cmt codemeta.json CITATION.cff
installer.sh: .FORCE
cmt codemeta.json installer.sh
installer.ps1: .FORCE
cmt codemeta.json installer.ps1
# dist builds one zip per platform in PLATFORMS, starting from an empty dist/.
dist: build .FORCE
@rm -fR dist
@mkdir -p dist
@for ENTRY in $(PLATFORMS); do \
NAME=$$(echo $$ENTRY | cut -d: -f1); GOOS=$$(echo $$ENTRY | cut -d: -f2); GOARCH=$$(echo $$ENTRY | cut -d: -f3); \
SUFFIX=""; if [ "$$GOOS" = "windows" ]; then SUFFIX=".exe"; fi; \
rm -fR dist/stage; mkdir -p dist/stage/bin; \
for FNAME in $(PROGRAMS); do \
env GOOS=$$GOOS GOARCH=$$GOARCH go build -o "dist/stage/bin/$${FNAME}$$SUFFIX" ./cmd/$$FNAME || exit 1; \
done; \
cp LICENSE codemeta.json CITATION.cff README.md INSTALL.md dist/stage/; \
cp -R docs man dist/stage/; \
(cd dist/stage && zip -qr ../$(PROJECT)-v$(VERSION)-$$NAME.zip .) || exit 1; \
echo "dist/$(PROJECT)-v$(VERSION)-$$NAME.zip"; \
done
@rm -fR dist/stage
release-version: .FORCE
@$(MAKE) -B generate
# release-check stops the release unless version.go, the installers,
# CITATION.cff, the manual pages in docs/ and the zip files all carry the
# version in codemeta.json and the hash of the commit being released, and
# every zip carries every program.
release-check: .FORCE
@HASH=$$(git log --pretty=format:%h -n 1); \
if ! grep -q 'Version = "$(VERSION)"' version.go; then \
echo "error: version.go does not carry version $(VERSION)" >&2; exit 1; fi; \
if ! grep -q "ReleaseHash = \"$$HASH\"" version.go; then \
echo "error: version.go does not carry hash $$HASH" >&2; exit 1; fi; \
if ! grep -q '^version: $(VERSION)' CITATION.cff; then \
echo "error: CITATION.cff does not carry version $(VERSION)" >&2; exit 1; fi; \
for FNAME in installer.sh installer.ps1; do \
if ! grep -q 'VERSION.*"$(VERSION)"' $$FNAME; then \
echo "error: $$FNAME does not carry version $(VERSION)" >&2; exit 1; fi; \
done; \
for FNAME in docs/*.?.md; do \
if ! head -4 $$FNAME | grep -q "version $(VERSION) $$HASH"; then \
echo "error: $$FNAME does not carry version $(VERSION) $$HASH" >&2; exit 1; fi; \
done; \
COUNT=0; \
for ENTRY in $(PLATFORMS); do \
NAME=$$(echo $$ENTRY | cut -d: -f1); GOOS=$$(echo $$ENTRY | cut -d: -f2); ZIP=dist/$(PROJECT)-v$(VERSION)-$$NAME.zip; \
SUFFIX=""; if [ "$$GOOS" = "windows" ]; then SUFFIX=".exe"; fi; \
if [ ! -f $$ZIP ]; then echo "error: missing $$ZIP" >&2; exit 1; fi; \
for FNAME in $(PROGRAMS); do \
if ! unzip -Z1 $$ZIP | grep -qx "bin/$$FNAME$$SUFFIX"; then \
echo "error: $$ZIP does not carry bin/$$FNAME$$SUFFIX" >&2; exit 1; fi; \
done; \
for PAGE in $$(unzip -Z1 $$ZIP 'docs/*.?.md'); do \
if ! unzip -p $$ZIP $$PAGE | head -4 | grep -q "version $(VERSION) $$HASH"; then \
echo "error: $$ZIP $$PAGE does not carry version $(VERSION) $$HASH" >&2; exit 1; fi; \
done; \
COUNT=$$((COUNT + 1)); \
done; \
echo "release-check: version.go, CITATION.cff, installers, docs/ and $$COUNT zip files carry $(VERSION) $$HASH"
# release: see "Releasing" above. It commits nothing; release.bash does that.
release: clean release-version test dist release-check
@printf "\nrelease-check passed. Now run: ./release.bash (or ./release.bash --dry-run first)\n\n"
.FORCE: