Makefile check if folder and/or file exists - makefile

I have the following Makefile:
build: clean
${GOPATH}/bin/dep ensure
env GOOS=linux go build -o ./bin/status ./lib/status/main.go
elm-app build
init:
${GOPATH}/bin/dep init -v
test:
env GOOS=linux go test -v ./lib/status
strip:
strip ./bin/status
clean:
if [ -f ./bin/status ]; then
rm -f ./bin/status
fi
but I get
if [ -f ./bin/status ]; then
/bin/sh: 1: Syntax error: end of file unexpected
Makefile:16: recipe for target 'clean' failed
make: *** [clean] Error 2
what am i missing?
any advice is much appreciated

Each line of a makefile is run in a separate shell. This means your rule here:
clean:
if [ -f ./bin/status ]; then
rm -f ./bin/status
fi
actually runs the following commands:
/bin/sh -c "if [ -f ./bin/status ]; then"
/bin/sh -c "rm -f ./bin/status"
/bin/sh -c "fi"
You can see why you get this message. To ensure that all lines are send into a single shell you need to use backslashes to continue the lines like this:
clean:
if [ -f ./bin/status ]; then \
rm -f ./bin/status; \
fi
Note this means you also need a semicolon after the rm command so separate it from the ending fi.
Now you get a shell invocation like this:
/bin/sh -c "if [ -f ./bin/status ]; then \
rm -f ./bin/status; \
fi"

Related

What are the differences between running a command in a shell (sh or bash) and in a Makefile?

The file myfile does not exist.
In a shell, rm myFile returns an error : it is OK
In a shell, test -f myFile && rm myFile does not return an error : it is OK
But in a simple Makefile :
.PHONY: clean
clean:
test -f myFile && rm myFile
returns an error.
I know how to avoid it : -test -f myFile && rm myFile (errors are still there but ignored) or $(shell test -f myFile1 && rm myFile1) (There is no error, job is done but 'nothing to do' is displayed) or rm -rf or ...
But, I would like to understand why test -f myFile && rm myFile does not return an error in a shell but it returns an error in a Makefile.
I read this answer (https://stackoverflow.com/a/66907116/7462275). But the behaviour is the same in a bash shell or in a sh shell.
Answers are in comments.

Makefile remove stopped Docker containers

I'm new to Makefiles, I'm trying to use one to help me automate the build of my docker images.
I'm struggling to write, what feels like, a simple recipe that does the following:
Check to see if there are running containers (docker ps -a -q)
If running containers exist remove them (docker rm $(docker ps -a -q))
This is what I've manage to cobble together so far through various Google searches:
DOCKER_CONTAINER_LIST := $(shell docker ps -a -q)
clean-docker:
if [[ ! $(DOCKER_CONTAINER_LIST) == "" ]];then \
docker rm $$(docker ps -a -q) \
fi
Which yields the following output:
if [[ de6e694da4f9 == "" ]];then \
/bin/sh: -c: line 0: conditional binary operator expected
/bin/sh: -c: line 0: syntax error near 'de6e694da4f9'
/bin/sh: -c: line 0: 'if [[ de6e694da4f9 == "" ]];then \ '
make: *** [clean-docker] Error 2
Would be great if someone could show me how to achieve this please
UPDATE
Thank #I0b0 for your answer, I think I'm almost there now. The Makefile now looks like this:
DOCKER_CONTAINER_LIST := $(shell docker ps -a -q)
clean-docker:
if [ -n "$(DOCKER_CONTAINER_LIST)" ] \
then \
docker rm "$(DOCKER_CONTAINER_LIST)" \
fi
But I get the following error when running it:
if [ -n "6778d35e5c95" ] \
then \
docker rm "6778d35e5c95" \
fi
/usr/bin/sh: -c: line 4: syntax error: unexpected end of file
make: *** [clean-docker] Error 1
I had a look at this answer regarding line endings, but that made no difference. I am using Makefile on both Mac and Windows.
It can be done with:
# for images
docker rmi $(shell docker images -a -q)
# for containers
docker rm $(shell docker ps -a -q)
# for volumes
docker volume rm $(shell docker volume ls -q)
PS. macOS
if [ -n "$(DOCKER_CONTAINER_LIST)" ] should do it:
-n is the standard test for a non-empty string
Use More Quotes™ never gets old
[[ is a bashism, but sh is not bash
Bonus points for reusing the variable with docker rm "$(DOCKER_CONTAINER_LIST)".
After the update the problem is related to a fundamental difference between scripts and makefiles: the latter cannot be broken into lines in the exact same way as scripts by just adding backslashes before the end of line. What you've ended up with is a single line if [ -n "$(DOCKER_CONTAINER_LIST)" ] then docker rm "$(DOCKER_CONTAINER_LIST)" fi. Because newlines in a makefile don't work like ; in a shell script you have to add two:
if [ -n "$(DOCKER_CONTAINER_LIST)" ]; \
then \
docker rm "$(DOCKER_CONTAINER_LIST)"; \
fi
I couldn't figure out how to divide those commands into multiple lines either, but ugly oneliners work for me.
So what you were asking about would be something like this
DOCKER_CONTAINER_LIST := $(shell docker ps -a -q)
clean_docker:
#if [ -n "$(DOCKER_CONTAINER_LIST)" ]; then echo "Removing docker containers" && docker rm "$(DOCKER_CONTAINER_LIST)"; else echo "No containers found"; fi;

Loading variables in bash from another file

I build some ROMs and other software for various Android devices and to make it easier for me, I use bash based scripts.
Here's an example from my TWRP build script:
#!/usr/bin/env bash
# Variables
export TW_DEVICE_VERSION="0"
export BRANCH="android-5.1"
# Don't touch this
VERSION=$( grep "TW_MAIN_VERSION_STR" bootable/recovery/variables.h -m 1 | cut -d \" -f2 )-${TW_DEVICE_VERSION}
# Acer Liquid Z500 specific TWRP build configuration
export BRAND="acer"
export DEVICE="acer_Z500"
git clone https://github.com/liquidporting/android_device_${BRAND}_${DEVICE}.git -b ${BRANCH} device/${BRAND}/${DEVICE}
. build/envsetup.sh
lunch omni_${DEVICE}-eng
mka recoveryimage > twrp_${DEVICE}.log
cd out/target/product/${DEVICE}
if [ -f "recovery.img" ]
then
mv recovery.img twrp-${VERSION}-${DEVICE}.img
else
echo ""
echo "*******************************************************************************"
echo "Something went wrong during the build process, try checking your device tree."
echo "After that, run the script again and see if you messed up something new or not."
echo "*******************************************************************************"
echo ""
fi
if [ -f "twrp-${VERSION}-${DEVICE}.img" ]
then
megarm /Root/LPAD/TWRP/twrp-${VERSION}-${DEVICE}.img
megarm /Root/LPAD/TWRP/twrp_${DEVICE}.log
megaput --no-progress --path /Root/LPAD/TWRP twrp-${VERSION}-${DEVICE}.img
megaput --no-progress --path /Root/LPAD/TWRP ../../../../twrp_${DEVICE}.log
fi
if [ -f "twrp-${VERSION}-${DEVICE}.img" ]
then
cd ../../../..
rm twrp_${DEVICE}.log
make clean
cd device
rm -rf ${BRAND}
cd ..
else
rm twrp_${DEVICE}.log
make clean
cd device
rm -rf ${BRAND}
cd ..
echo ""
echo "**************************************************************"
echo "The build process of TWRP Recovery failed for device ${DEVICE}"
echo "**************************************************************"
echo ""
exit
fi
# Lenovo A328 specific TWRP build configuration
export BRAND="lenovo"
export DEVICE="A328"
git clone https://github.com/liquidporting/android_device_${BRAND}_${DEVICE}.git -b ${BRANCH} device/${BRAND}/${DEVICE}
. build/envsetup.sh
lunch omni_${DEVICE}-eng
mka recoveryimage > twrp_${DEVICE}.log
cd out/target/product/${DEVICE}
if [ -f "recovery.img" ]
then
mv recovery.img twrp-${VERSION}-${DEVICE}.img
else
echo ""
echo "*******************************************************************************"
echo "Something went wrong during the build process, try checking your device tree."
echo "After that, run the script again and see if you messed up something new or not."
echo "*******************************************************************************"
echo ""
fi
if [ -f "twrp-${VERSION}-${DEVICE}.img" ]
then
megarm /Root/LPAD/TWRP/twrp-${VERSION}-${DEVICE}.img
megarm /Root/LPAD/TWRP/twrp_${DEVICE}.log
megaput --no-progress --path /Root/LPAD/TWRP twrp-${VERSION}-${DEVICE}.img
megaput --no-progress --path /Root/LPAD/TWRP ../../../../twrp_${DEVICE}.log
fi
if [ -f "twrp-${VERSION}-${DEVICE}.img" ]
then
cd ../../../..
rm twrp_${DEVICE}.log
make clean
cd device
rm -rf ${BRAND}
cd ..
else
rm twrp_${DEVICE}.log
make clean
cd device
rm -rf ${BRAND}
cd ..
echo ""
echo "**************************************************************"
echo "The build process of TWRP Recovery failed for device ${DEVICE}"
echo "**************************************************************"
echo ""
exit
fi
Is possible to have a separated bash script containing build instructions and another one containing set of variables?
I Mean something like this:
#!/usr/bin/env bash
export TW_DEVICE_VERSION="0"
export BRANCH="android-5.1"
VERSION=$( grep "TW_MAIN_VERSION_STR" bootable/recovery/variables.h -m 1 | cut -d \" -f2 )-${TW_DEVICE_VERSION}
export BRAND="acer"
export DEVICE="acer_Z500"
export BRAND="lenovo"
export DEVICE="A328"
export BRAND="doogee"
export DEVICE="X5"
But after each device specific configuration I need it to launch the bash script containing the build instructions.
Yes, that is very much feasible and it is a good thing to do as well.
Put your configuration in a file, say, tw_config.sh. Then, invoke the configuration script this way:
source /path/to/tw_config.sh
if [ $? -ne 0 ]; then
# couldn't load config
# your logic here - makes sense to exit
fi
If tw_config.sh is in your PATH, you can simply say:
source tw_config.sh

What is this error in a Makefile?

Using 'make' from the ARM DS-5 5.22 release on Cygwin, I am processing a makefile containing the lines:
if [[ ! -d "$(OBJ_DIR)" ]]; \
then $(MD) "$(OBJ_DIR)"; fi
This evaluates to:
if [[ ! -d "../../output/obj" ]]; \
then mkdir -p "../../output/obj"; fi
When making, I get an error:
make -C ./src/modules
make[1]: Entering directory `C:/Users/me/Documents/proj/src/modules'
if [[ ! -d "../../output/obj" ]]; \
then mkdir -p "../../output/obj"; fi
! was unexpected at this time.
make[1]: *** [setenv] Error 255
make[1]: Leaving directory `C:/Users/me/Documents/proj/src/modules'
make: *** [drivers] Error 2
When explicitly running the mkdir command from the command line, it works as expected and the output/obj directory is created with no problems.
This script works fine on other computers.
What does the ! was unexpected at this time. error mean, and how to fix that?
UPDATE: The make command is invoked from the following shell script:
#!/bin/bash
set -e
export PATH='./:/usr/bin/:/cygdrive/c/Program Files/ARMCompiler6.6/bin/'
export ARM_PRODUCT_PATH='c:/DS-5_v5.22/sw/mappings'
export USEARMCOMPILER6=1
export DS5VER=DS-5_5.22
/cygdrive/c/DS-5_v5.22/bin/make "$#"

Makefile issue with calling another shell file for make

I have created a parent makefile. as below:
SHELL = /bin/bash
HOMEDIR = $(shell pwd)
PKGNAM = PARAMETIS
override VERSION = 4.0.3
YESDIR = $(shell echo $(#:install-%=%) | tr A-Z a-z)
NODIR = $(shell echo $(#:clean-%=%) | tr A-Z a-z)
install:
$(MAKE) install-$(VERSION)
install-%:
#if [ ! -e $(YESDIR) ]; then \
echo "Library $(PKGNAM) Version=$(YESDIR) does not exist"; \
elif [ -e $(YESDIR)/Install.sh ]; then \
echo "Installing $(PKGNAM) version=$(YESDIR)" ; \
cd $(YESDIR) ;\
$(SHELL) Install.sh $(HOMEDIR) 1 ;\
elif [ -e $(YESDIR)/Makefile ]; then \
cd $(YESDIR); \
$(MAKE); \
else \
echo "Installation instruction for $(#:install-%=%) Version=$(YESDIR) does not exist"; \
fi;
clean:
#$(MAKE) clean-$(VERSION)
clean-%:
#if [! -e ${NODIR} ]; then ;\
echo "Library does not exist $(PKGNAM) version=$(NODIR)" ; \
else \
cd $(NODIR) ;\
echo "Installing $(PKGNAM) version=$(NODIR)" ; \
$(SHELL) Install.sh $(HOMEDIR) 0 ;\
fi;
This makefile calls different bash files inside each version of the libraries directories to build them, the bash files can successfully build each library if I call it from the terminal, tho when I call them from my make file using,
make install
after it executes the install.sh and build the library, I get this error that
No rule to make target 'w'. Stop.
any idea why it happens and how can I get rid of it ?
HERE is the bash file if it helps:
if (test $2 = 1) then
make --silent -f Makefile config prefix=$1/exec
make --silent -f Makefile
make --silent -f Makefile install
elif (test $2 = 0) then
make --silent -f Makefile clean
fi
Thanks
The problem was caused by calling other Makefiles using -C in the library's makefile called in the Install.sh. This sets the MAKEFLAGS to w automatically. unfortunately, the developers of the library has made mistake in calling the makefiles as below:
$(MAKE) -C $(SUBDIR) $# $(MAKEFLAGS)
when the makefile is called from another makefile, this MAKEFLAGS are set to w, but in the bash called from the terminal they are empty. because developers have forgotten to add MAKEFLAGS= before the flags, it assumes that w is another target and because it is not defined it generates the error, I menotiond.
I solved the issue by changing their makefile as below:
$(MAKE) -c $(SUBDIR) $# MAKEFLAGS=$(MAKEFLAGS)
and now everything works as expected.

Resources