Makefile -rw-r--r-- 11.4 KiB
1# meli - Makefile
2#
3# Copyright 2017-2020 Manos Pitsidianakis
4#
5# This file is part of meli.
6#
7# meli is free software: you can redistribute it and/or modify
8# it under the terms of the GNU General Public License as published by
9# the Free Software Foundation, either version 3 of the License, or
10# (at your option) any later version.
11#
12# meli is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15# GNU General Public License for more details.
16#
17# You should have received a copy of the GNU General Public License
18# along with meli. If not, see <http://www.gnu.org/licenses/>.
19.POSIX:
20.SUFFIXES:
21CARGO_TARGET_DIR ?= target
22CARGO_BIN ?= cargo
23TAGREF_BIN ?= tagref
24CARGO_ARGS ?=
25RUSTFLAGS ?= -D warnings -W unreachable-pub -W rust-2021-compatibility
26CARGO_SORT_BIN = cargo-sort
27CARGO_HACK_BIN = cargo-hack
28PRINTF = /usr/bin/printf
29
30# Options
31PREFIX ?= /usr/local
32EXPANDED_PREFIX := `cd ${PREFIX} && pwd -P`
33BINDIR ?= ${EXPANDED_PREFIX}/bin
34MANDIR ?= ${EXPANDED_PREFIX}/share/man
35
36# Installation parameters
37DOCS_SUBDIR ?= meli/docs/
38MANPAGES ?= meli.1 meli.conf.5 meli-themes.5 meli.7
39FEATURES != [ -z "$${MELI_FEATURES}" ] && ($(PRINTF) -- '--all-features') || ($(PRINTF) -- '--features %s' "$${MELI_FEATURES}")
40
41MANPATHS != ACCUM="";for m in `manpath 2> /dev/null | tr ':' ' '`; do if [ -d "$${m}" ]; then REAL_PATH=`cd $${m} && pwd` ACCUM="$${ACCUM}:$${REAL_PATH}";fi;done;echo $${ACCUM}'\c' | sed 's/^://'
42VERSION = `grep -m1 version meli/Cargo.toml | head -n1 | cut -d'"' -f 2 | head -n1`
43MIN_RUSTC = `grep -m1 rust-version meli/Cargo.toml | head -n1 | cut -d'"' -f 2 | head -n1`
44GIT_COMMIT = `git show-ref -s --abbrev HEAD`
45DATE = `date -I`
46
47# Output parameters
48BOLD ?= `[ -z $${TERM} ] && echo "" || tput bold`
49UNDERLINE ?= `[ -z $${TERM} ] && echo "" || tput smul`
50ANSI_RESET ?= `[ -z $${TERM} ] && echo "" || tput sgr0`
51CARGO_COLOR ?= `[ -z $${NO_COLOR+x} ] && echo "" || echo "--color=never "`
52RED ?= `[ -z $${NO_COLOR+x} ] && ([ -z $${TERM} ] && echo "" || tput setaf 1) || echo ""`
53GREEN ?= `[ -z $${NO_COLOR+x} ] && ([ -z $${TERM} ] && echo "" || tput setaf 2) || echo ""`
54YELLOW ?= `[ -z $${NO_COLOR+x} ] && ([ -z $${TERM} ] && echo "" || tput setaf 3) || echo ""`
55
56.PHONY: meli
57meli: check-deps
58 ${CARGO_BIN} build ${CARGO_ARGS} ${CARGO_COLOR}--target-dir="${CARGO_TARGET_DIR}" ${FEATURES} --release --bin meli
59
60.PHONY: help
61help:
62 @echo "For a quick start, build and install locally:\n\n${BOLD}${GREEN}make PREFIX=~/.local install${ANSI_RESET}\n"
63 @echo "Available subcommands:"
64 @echo " - ${BOLD}meli${ANSI_RESET} (builds meli with optimizations in \$$CARGO_TARGET_DIR)"
65 @echo " - ${BOLD}install${ANSI_RESET} (installs binary in \$$BINDIR and documentation to \$$MANDIR)"
66 @echo " - ${BOLD}uninstall${ANSI_RESET}"
67 @echo "\nSecondary subcommands:"
68 @echo " - ${BOLD}clean${ANSI_RESET} (cleans build artifacts)"
69 @echo " - ${BOLD}check-deps${ANSI_RESET} (checks dependencies)"
70 @echo " - ${BOLD}install-bin${ANSI_RESET} (installs binary to \$$BINDIR)"
71 @echo " - ${BOLD}install-doc${ANSI_RESET} (installs manpages to \$$MANDIR)"
72 @echo " - ${BOLD}help${ANSI_RESET} (prints this information)"
73
74 @echo " - ${BOLD}dist${ANSI_RESET} (creates release tarball named meli-"${VERSION}".tar.gz in this directory)"
75 @echo " - ${BOLD}deb-dist${ANSI_RESET} (builds debian package in the parent directory)"
76 @echo " - ${BOLD}distclean${ANSI_RESET} (cleans distribution build artifacts)"
77 @echo " - ${BOLD}build-rustdoc${ANSI_RESET} (builds rustdoc documentation for all packages in \$$CARGO_TARGET_DIR)"
78 @echo "\nENVIRONMENT variables of interest:"
79 @$(PRINTF) "* MELI_FEATURES "
80 @[ -z $${MELI_FEATURES+x} ] && echo "unset" || echo "= ${UNDERLINE}"$${MELI_FEATURES}${ANSI_RESET}
81 @$(PRINTF) "* PREFIX "
82 @[ -z ${EXPANDED_PREFIX} ] && echo "unset" || echo "= ${UNDERLINE}"${EXPANDED_PREFIX}${ANSI_RESET}
83 @$(PRINTF) "* BINDIR = %s\n" "${UNDERLINE}${BINDIR}${ANSI_RESET}"
84 @$(PRINTF) "* MANDIR "
85 @[ -z ${MANDIR} ] && echo "unset" || echo "= ${UNDERLINE}"${MANDIR}${ANSI_RESET}
86 @$(PRINTF) "* MANPATH = "
87 @[ $${MANPATH+x} ] && echo ${UNDERLINE}$${MANPATH}${ANSI_RESET} || echo "unset"
88 @echo "* (cleaned) output of manpath(1) = ${UNDERLINE}${MANPATHS}${ANSI_RESET}"
89 @$(PRINTF) "* NO_MAN "
90 @[ $${NO_MAN+x} ] && echo "set" || echo "unset"
91 @$(PRINTF) "* NO_COLOR "
92 @[ $${NO_COLOR+x} ] && echo "set" || echo "unset"
93 @echo "* CARGO_BIN = ${UNDERLINE}${CARGO_BIN}${ANSI_RESET}"
94 @$(PRINTF) "* CARGO_ARGS "
95 @[ -z $${CARGO_ARGS+x} ] && echo "unset" || echo "= ${UNDERLINE}"$${CARGO_ARGS}${ANSI_RESET}
96 @$(PRINTF) "* AUTHOR (for deb-dist) "
97 @[ -z $${AUTHOR+x} ] && echo "unset" || echo "= ${UNDERLINE}"$${AUTHOR}${ANSI_RESET}
98 @echo "* MIN_RUSTC = ${UNDERLINE}${MIN_RUSTC}${ANSI_RESET}"
99 @echo "* VERSION = ${UNDERLINE}${VERSION}${ANSI_RESET}"
100 @echo "* GIT_COMMIT = ${UNDERLINE}${GIT_COMMIT}${ANSI_RESET}"
101 @#@echo "* CARGO_COLOR = ${CARGO_COLOR}"
102
103.PHONY: check
104check: check-tagrefs
105 RUSTFLAGS='${RUSTFLAGS}' ${CARGO_BIN} check ${CARGO_ARGS} ${CARGO_COLOR}--target-dir="${CARGO_TARGET_DIR}" ${FEATURES} --all --tests --examples --benches --bins
106
107.PHONY: fmt
108fmt:
109 $(CARGO_BIN) +nightly fmt --all || $(CARGO_BIN) fmt --all
110 @OUT=$$($(CARGO_SORT_BIN) melib -w 2>&1 && $(CARGO_SORT_BIN) meli -w 2>&1) || $(PRINTF) "WARN: %s cargo-sort failed or binary not found in PATH.\n" "$$OUT"
111
112.PHONY: lint
113lint:
114 RUSTFLAGS='${RUSTFLAGS}' $(CARGO_BIN) clippy --no-deps ${FEATURES} --all --tests --examples --benches --bins
115
116.PHONY: test
117test: test-docs
118 RUSTFLAGS='${RUSTFLAGS}' ${CARGO_BIN} test ${CARGO_ARGS} ${CARGO_COLOR}--target-dir="${CARGO_TARGET_DIR}" ${FEATURES} --all --tests --examples --benches --bins
119
120.PHONY: test-docs
121test-docs:
122 RUSTFLAGS='${RUSTFLAGS}' ${CARGO_BIN} test ${CARGO_ARGS} ${CARGO_COLOR}--target-dir="${CARGO_TARGET_DIR}" ${FEATURES} --all --doc
123
124.PHONY: test-feature-permutations
125test-feature-permutations:
126 $(CARGO_HACK_BIN) hack --feature-powerset
127
128.PHONY: check-deps
129check-deps:
130 @(if ! echo ${MIN_RUSTC}\\n`${CARGO_BIN} --version | grep ^cargo | cut -d ' ' -f 2` | sort -CV; then echo "rust version >= ${RED}${MIN_RUSTC}${ANSI_RESET} required, found: `which ${CARGO_BIN}` `${CARGO_BIN} --version | cut -d ' ' -f 2`" \
131 "\nYour options:\n - Set CARGO_BIN to a supported version\n - Install a supported version from your distribution's package manager\n - Install a supported version from ${UNDERLINE}https://rustup.rs/${ANSI_RESET}" ; exit 1; fi)
132
133
134.PHONY: clean
135clean:
136 -rm -rf ./${CARGO_TARGET_DIR}/
137
138.PHONY: distclean
139distclean:
140 rm -f meli-${VERSION}.tar.gz
141 rm -rf .pc # rm debian stuff
142
143.PHONY: uninstall
144uninstall:
145 rm -f $(DESTDIR)${BINDIR}/meli
146 for MANPAGE in ${MANPAGES}; do \
147 SECTION=`echo $${MANPAGE} | rev | cut -d "." -f 1`; \
148 MANPAGEPATH="${DESTDIR}${MANDIR}/man$${SECTION}/$${MANPAGE}.gz"; \
149 rm -f "$${MANAGEPATH}"
150 ; done
151
152.PHONY: install-doc
153install-doc:
154 @(if [ -z $${NO_MAN+x} ]; then \
155 echo " - ${BOLD}Installing manpages to ${ANSI_RESET}${DESTDIR}${MANDIR}:" ; \
156 for MANPAGE in ${MANPAGES}; do \
157 SECTION=`echo $${MANPAGE} | rev | cut -d "." -f 1`; \
158 mkdir -p $(DESTDIR)${MANDIR}/man$${SECTION} ; \
159 MANPAGEPATH=${DESTDIR}${MANDIR}/man$${SECTION}/$${MANPAGE}.gz; \
160 echo " * installing $${MANPAGE} → ${GREEN}$${MANPAGEPATH}${ANSI_RESET}"; \
161 gzip -n < ${DOCS_SUBDIR}$${MANPAGE} > $${MANPAGEPATH} \
162 ; done ; \
163 (case ":${MANPATHS}:" in \
164 *:${DESTDIR}${MANDIR}:*) echo "\c";; \
165 *) echo "\n${RED}${BOLD}WARNING${ANSI_RESET}: ${UNDERLINE}Path ${DESTDIR}${MANDIR} is not contained in your MANPATH variable or the output of \`manpath\` command.${ANSI_RESET} \`man\` might fail finding the installed manpages. Consider adding it if necessary.\nMANPATH variable / output of \`manpath\`: ${MANPATHS}" ;; \
166 esac) ; \
167 else echo "NO_MAN is defined, so no documentation is going to be installed." ; fi)
168
169.PHONY: install-bin
170install-bin: meli
171 mkdir -p $(DESTDIR)${BINDIR}
172 @echo " - ${BOLD}Installing binary to ${ANSI_RESET}${GREEN}${DESTDIR}${BINDIR}/meli${ANSI_RESET}"
173 @case ":${PATH}:" in \
174 *:${DESTDIR}${BINDIR}:*) echo "\n";; \
175 *) echo "\n${RED}${BOLD}WARNING${ANSI_RESET}: ${UNDERLINE}Path ${DESTDIR}${BINDIR} is not contained in your PATH variable.${ANSI_RESET} Consider adding it if necessary.\nPATH variable: ${PATH}";; \
176 esac
177 mkdir -p $(DESTDIR)${BINDIR}
178 rm -f $(DESTDIR)${BINDIR}/meli
179 cp ./${CARGO_TARGET_DIR}/release/meli $(DESTDIR)${BINDIR}/meli
180 chmod 755 $(DESTDIR)${BINDIR}/meli
181
182
183.PHONY: install
184.NOTPARALLEL: yes
185install: meli install-bin install-doc
186 @(if [ -z $${NO_MAN+x} ]; then \
187 $(PRINTF) "\n You're ready to go. You might want to read the \"STARTING WITH meli\" section in the manpage (\`man meli\`)" ;\
188 $(PRINTF) "\n or the tutorial in meli(7) (\`man 7 meli\`).\n" ;\
189 fi)
190 @$(PRINTF) " - Report bugs in the mailing list or git issue tracker ${UNDERLINE}https://git.meli-email.org${ANSI_RESET}\n"
191 @$(PRINTF) " - If you have a specific feature or workflow you want to use, you can post in the mailing list or git issue tracker.\n"
192
193.PHONY: dist
194dist:
195 git archive --format=tar.gz --prefix=meli-${VERSION}/ HEAD >meli-${VERSION}.tar.gz
196 @echo meli-${VERSION}.tar.gz
197
198AUTHOR ?= grep -m1 authors meli/Cargo.toml | head -n1 | cut -d'"' -f 2 | head -n1
199.PHONY: deb-dist
200deb-dist:
201 @$(PRINTF) "Override AUTHOR environment variable to set package metadata.\n"
202 dpkg-buildpackage -b -rfakeroot -us -uc --build-by="${AUTHOR}" --release-by="${AUTHOR}"
203 @echo ${BOLD}${GREEN}Generated${ANSI_RESET} ../meli_${VERSION}-1_`dpkg --print-architecture`.deb
204
205.PHONY: build-rustdoc
206build-rustdoc:
207 RUSTDOCFLAGS="--crate-version ${VERSION}_${GIT_COMMIT}_${DATE}" ${CARGO_BIN} doc ${CARGO_ARGS} ${CARGO_COLOR}--target-dir="${CARGO_TARGET_DIR}" --all-features --no-deps --workspace --document-private-items --open
208
209.PHONY: check-tagrefs
210check-tagrefs:
211 @(if ! command -v "$(TAGREF_BIN)" > /dev/null;\
212 then \
213 $(PRINTF) "Warning: tagref binary not in PATH.\n" 1>&2;\
214 exit;\
215 else \
216 $(TAGREF_BIN);\
217 fi)
218
219.PHONY: test-makefile
220test-makefile:
221 @$(PRINTF) "Checking that current version is detected. "
222 @([ ! -z "${VERSION}" ] && $(PRINTF) "${GREEN}OK${ANSI_RESET}\n") || $(PRINTF) "${RED}ERROR${ANSI_RESET}\nVERSION env var is empty, check its definition.\n" 1>&2
223 @$(PRINTF) "Checking that 'date -I' works on this platform. "
224 @export DATEVAL=$$(printf "%s" ${DATE} | wc -c | tr -d "[:blank:]" 2>&1); ([ "$${DATEVAL}" = "10" ] && $(PRINTF) "${GREEN}OK${ANSI_RESET}\n") || $(PRINTF) "${RED}ERROR${ANSI_RESET}\n'date -I' does not produce a YYYY-MM-DD output on this platform.\n" 1>&2
225 @$(PRINTF) "Checking that the git commit SHA can be detected. "
226 @([ ! -z "$(GIT_COMMIT)" ] && $(PRINTF) "${GREEN}OK${ANSI_RESET}\n") || $(PRINTF) "${YELLOW}WARN${ANSI_RESET}\nGIT_COMMIT env var is empty.\n" 1>&2
227
228# Checking if mdoc changes produce new lint warnings from mandoc(1) compared to HEAD version:
229#
230# example invocation: `mandoc_lint meli.1`
231#
232# with diff(1)
233# ============
234#function mandoc_lint () {
235#diff <(mandoc -T lint <(git show HEAD:./meli/docs/$1) 2> /dev/null | cut -d':' -f 3-) <(mandoc -T lint ./meli/docs/$1 2> /dev/null | cut -d':' -f 3-)
236#}
237#
238# with sdiff(1) (side by side)
239# ============================
240#
241#function mandoc_lint () {
242#sdiff <(mandoc -T lint <(git show HEAD:./meli/docs/$1) 2> /dev/null | cut -d':' -f 3-) <(mandoc -T lint ./meli/docs/$1 2> /dev/null | cut -d':' -f 3-)
243#}
244#
245# with delta(1)
246# =============
247#
248#function mandoc_lint () {
249#delta --side-by-side <(mandoc -T lint <(git show HEAD:./meli/docs/$1) 2> /dev/null | cut -d':' -f 3-) <(mandoc -T lint ./meli/docs/$1 2> /dev/null | cut -d':' -f 3-)
250#}