Makefile: Target dependency - How to always-make i

2019-07-27 01:23发布

问题:

This will most probably be obvious and / or a banality. But since I am trying different approaches for hours without success...

I am on Linux Mint 19. I am entirely new to Makefiles. Excuse me, if the problem is trivial.

Let it be clear, that I care for the distrib and SHA512SUMS targets only in this question.

As I heavily change the code of those scripts, I would like the SHA512SUMS file to be re-generated each time I run the distrib target, but not in case I run the check target, of course, that would make the check target irrelevant, as you can see.

This Makefile is becoming to be a little complicated for a shell scripter. Any help will be appreciated.


PREFIX?=/usr/local/bin
install_path=$(DESTDIR)$(PREFIX)

encrypt_script=encrypt-file-aes256
decrypt_script=decrypt-file-aes256
distrib_name=openssl-file-encryption-decryption-shell-scripts

.PHONY: check install uninstall distrib

check: $(encrypt_script) $(decrypt_script) SHA512SUMS
    echo && sha512sum --check --status SHA512SUMS && ( echo "Ok. You may use 'sudo make install' or '(sudo) make install PREFIX=SomeDir' command now." ) || ( echo "ERROR: Files hash sum mismatch!" && echo && exit 1 )

install: check
    echo && [ -d $(install_path) ] || mkdir --parents $(install_path)
    install --verbose --mode=0755 --target-directory=$(install_path) $(encrypt_script) $(decrypt_script)

uninstall:
    rm $(install_path)/$(encrypt_script) $(install_path)/$(decrypt_script)
    rmdir --ignore-fail-on-non-empty $(install_path)

distrib: check $(encrypt_script) $(decrypt_script) Makefile SHA512SUMS
    if [ $$(id --user) -eq 0 ]; then ( echo && echo "Target 'distrib' has to be run as normal user!" && echo && exit 1 ) fi
    rm --force $(distrib_name).tar.xz
    rm --force $(distrib_name).tar.xz.asc
    rm --force --recursive $(distrib_name)
    mkdir $(distrib_name)
#   sha512sum $(encrypt_script) $(decrypt_script) > $(distrib_name)/SHA512SUMS
    cp $(encrypt_script) $(decrypt_script) Makefile SHA512SUMS $(distrib_name)
    wget --quiet --output-document=$(distrib_name)/LICENSE https://git.io/fxByv # https://raw.githubusercontent.com/burianvlastimil/openssl-file-encryption-decryption-shell-scripts/master/LICENSE
    wget --quiet --output-document=$(distrib_name)/README.md https://git.io/fxByJ # https://raw.githubusercontent.com/burianvlastimil/openssl-file-encryption-decryption-shell-scripts/master/README.md
    chmod 755 $(distrib_name)/$(encrypt_script) $(distrib_name)/$(decrypt_script)
    chmod 644 $(distrib_name)/Makefile $(distrib_name)/SHA512SUMS $(distrib_name)/LICENSE $(distrib_name)/README.md
    tar --create --file=$(distrib_name).tar $(distrib_name)
    xz --format=xz -9 --extreme --check=sha256 $(distrib_name).tar
    rm --force --recursive $(distrib_name)
    gpg --local-user 7D2E022E39A88ACF3EF6D4498F37AF4CE46008C3 --sign --armor --output $(distrib_name).tar.xz.asc --detach-sig $(distrib_name).tar.xz

SHA512SUMS:
    sha512sum --check --status SHA512SUMS || sha512sum $(encrypt_script) $(decrypt_script) > SHA512SUMS

回答1:

Reading on Force Targets, I came up, hopefully, with a solution below:

If a rule has no prerequisites or recipe, and the target of the rule is a nonexistent file, then make imagines this target to have been updated whenever its rule is run. This implies that all targets depending on this one will always have their recipe run.

Hence, I created an empty target force-rebuild-hash-file:.

Thus, the SHA512SUM file will always be re-created when calling this target or distrib target which depends on it.

I think it is finally solved overall, feel free to comment, if not.

In case you or I, myself, find any errors, I will update this answer to reflect them.

To de-duplicate some code I came across this answer and applied it.

I renamed SHA512SUMS to SHA512SUM, it does not matter much, but I find it more used.

I found $@ to print target names a good way of keeping track of what targets are being run. For instance, if SHA512SUM does not exist and we install it like this:

make install PREFIX=./test

we get a very nice overview (output):

echo && echo Target: check && echo

Target: check

if [ -f SHA512SUM ]; then ( echo && sha512sum --check SHA512SUM && ( echo && echo "Ok. You may use 'sudo make install' or '(sudo) make install PREFIX=SomeDir' command now." ) || ( echo && echo "ERROR: Files hash sum mismatch!" && echo && exit 1 ) ) else make --file=Makefile SHA512SUM; fi
make[1]: Entering directory '/home/vlastimil/Development/sh/openssl-encryption'
echo && echo Target: SHA512SUM && echo

Target: SHA512SUM

sha512sum encrypt-file-aes256 decrypt-file-aes256 > SHA512SUM
make[1]: Leaving directory '/home/vlastimil/Development/sh/openssl-encryption'
echo && echo Target: install && echo

Target: install

echo && [ -d ./test ] || mkdir --parents ./test

install --verbose --mode=0755 --target-directory=./test encrypt-file-aes256 decrypt-file-aes256
'encrypt-file-aes256' -> './test/encrypt-file-aes256'
'decrypt-file-aes256' -> './test/decrypt-file-aes256'

Current Makefile

DESTDIR ?=
PREFIX ?= /usr/local/bin
install_path := $(DESTDIR)$(PREFIX)
encrypt_script := encrypt-file-aes256
decrypt_script := decrypt-file-aes256
distrib_name := openssl-encryption
this_file := $(lastword $(MAKEFILE_LIST))

.PHONY: check install uninstall distrib

# https://stackoverflow.com/a/27132934/1997354
check: $(encrypt_script) $(decrypt_script)
    echo && echo Target: $@ && echo
    if [ -f SHA512SUM ]; then ( echo && sha512sum --check SHA512SUM && ( echo && echo "Ok. You may use 'sudo make install' or '(sudo) make install PREFIX=SomeDir' command now." ) || ( echo && echo "ERROR: Files hash sum mismatch!" && echo && exit 1 ) ) else $(MAKE) --file=$(this_file) SHA512SUM; fi

install: check
    echo && echo Target: $@ && echo
    echo && [ -d $(install_path) ] || mkdir --parents $(install_path)
    install --verbose --mode=0755 --target-directory=$(install_path) $(encrypt_script) $(decrypt_script)

uninstall:
    echo && echo Target: $@ && echo
    rm $(install_path)/$(encrypt_script) $(install_path)/$(decrypt_script)
    rmdir --ignore-fail-on-non-empty $(install_path)

distrib: SHA512SUM check $(encrypt_script) $(decrypt_script) Makefile
    echo && echo Target: $@ && echo
    # https://english.stackexchange.com/a/468131/319970
    # https://stackoverflow.com/a/52782747/1997354
    if [ $$(id --user) -eq 0 ]; then ( echo && echo "Target 'distrib' has to be run as normal user!" && echo && exit 1 ) fi
    rm --force $(distrib_name).tar.xz
    rm --force $(distrib_name).tar.xz.asc
    rm --force --recursive $(distrib_name)
    mkdir $(distrib_name)
    cp $(encrypt_script) $(decrypt_script) Makefile SHA512SUM $(distrib_name)
    wget --quiet --output-document=$(distrib_name)/LICENSE https://git.io/fxByv
    wget --quiet --output-document=$(distrib_name)/README https://git.io/fxByJ
    chmod 755 $(distrib_name)/$(encrypt_script) $(distrib_name)/$(decrypt_script)
    chmod 644 $(distrib_name)/Makefile $(distrib_name)/SHA512SUM $(distrib_name)/LICENSE $(distrib_name)/README
    tar --create --file=$(distrib_name).tar $(distrib_name)
    xz --format=xz -9 --extreme --check=sha256 $(distrib_name).tar
    rm --force --recursive $(distrib_name)
    gpg --local-user 7D2E022E39A88ACF3EF6D4498F37AF4CE46008C3 --sign --armor --output $(distrib_name).tar.xz.asc --detach-sig $(distrib_name).tar.xz

# https://www.gnu.org/software/make/manual/html_node/Force-Targets.html
force-rebuild-hash-file:

# real target file
SHA512SUM: force-rebuild-hash-file
    echo && echo Target: $@ && echo
    sha512sum $(encrypt_script) $(decrypt_script) > SHA512SUM


回答2:

You could also declare SHA512SUM as order-only prerequisite of check, not a prerequisite of distrib but add $(MAKE) --always-make SHA512SUM to your distrib recipe:

check: $(encrypt_script) $(decrypt_script) | SHA512SUM
    ...

distrib: check $(encrypt_script) $(decrypt_script) Makefile
    $(MAKE) --always-make SHA512SUM
    ...


回答3:

I think the best would be to remove the explicit dependency on SHA512SUMS from the check-target, and have a (shell) test inside that target and explicitly create the file if it's not there yet:

check: ... ... if [ ! -e SHA512SUM ] ; then <create SHA512SUM HERE>; fi .. do something with SHA512SUM ...