Makefile issue with calling another shell file for make - bash

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.

Related

How do I read BASH parameters correctly from a file?

I have this configuration file in my CI where I'm specifying a header file and some CMAKE flags on one line.
The configuration file looks like this (filelist):
./settings6.h -DMY_COMPILE_FLAGS="-m32 -fstrict-aliasing"
./settings7.h -DMY_FEATURE_1=ON
./settings8.h -DMY_FLAG=ON -DMY_FEATURE_2=ON -DMY_INCLUDE_DIR=/usr/include/
Now, I'm using a bash script to process this configuration file:
#!/bin/bash
SCRIPTDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
while read i; do
HEADERFILE=$(echo $i | cut -d ' ' -f 1)
CMAKEFLAGS=$(echo $i | cut -s -d ' ' -f 2-)
if [[ "$HEADERFILE" == "" ]]; then
continue
fi
CFLAGS="-Werror" cmake "my_build_dir" "$CMAKEFLAGS" -G "Ninja" -DMY_EXTRA_INCLUDE="$SCRIPTDIR/$HEADERFILE" -B"build_env_dir" > /dev/null
ninja -C "build_env_dir"
done <<ENDOFINPUT
$(grep -v '^#' $SCRIPTDIR/filelist)
ENDOFINPUT
When I have the bash script as above, the line with settings6.h gets processed properly, i.e. the MY_COMPILE_FLAGS are set to -m32 -fstrict-aliasing.
However, settings8.h is failing because the value of MY_FLAG is seen by CMAKE as ON -DMY_FEATURE_2=ON -DMY_INCLUDE_DIR=/usr/include/, so MY_FEATURE_2 and MY_INCLUDE_DIR are not processed correctly.
After googling around a bit, I thought, well, surely a quoting issue, probably I have to remove the quotes around $CMAKEFLAGS like this:
CFLAGS="-Werror" cmake "my_build_dir" $CMAKEFLAGS -G "Ninja" -DMY_EXTRA_INCLUDE="$SCRIPTDIR/$HEADERFILE" -B"build_env_dir" > /dev/null
In fact, this lets settings8.h work as expected (all three options are processed), but now, settings6.h is suddenly failing since CMAKE complains:
CMake Error: The source directory "/src/-fstrict-aliasing"" does not exist
Can someone guide me please how I read the settings correctly from my filelist so that settings6.h and settings8.h both succeed?
Here's a Makefile which refactors this into a sequence of recipes.
Cases := $(patsubst %.h,%,$(wildcard ./settings*.h))
all_done := $(patsubst %,.%.done,$(Cases))
.PHONY: all
all: $(all_done)
cases.mk: filelist.txt
sed 's%^\./%case_%;s% % := %' $< >$#
include cases.mk
.%_done: ./%.h
CFLAGS="-Werror" cmake "my_build_dir" $(case_$*) -G "Ninja" \
-DMY_EXTRA_INCLUDE="$<" -B"build_env_dir" > /dev/null
ninja -C "build_env_dir"

GNU makefile syntax for loop in windows

I have a GNU makefile written for linux operating system. I want to compile it on windows but there are some syntax changes for windows. I have done some changes but I am getting an error on for loop syntax.
Make file code is already written and I have to modify it for running on windows OS. Original code is attached and the changes i made are also given
Original code:
This is the original code:
#for i in $(subdirs); do \ (cd $$i && $(MAKE) $#) || break; \ done
After changes:
# pass make directives to subdirectories
SHELL = sh
%:
for f in $$(subdirs); \
do \
(cd $$i && $(MAKE) $#) || break; \
done
# eof
I am getting the error on line 16 which is first line of for loop
GNUmakefile(16) : fatal error U1035: syntax error : expected ':' or '=' separator
I made certain changes after reading nmake syntax. Now the problem is resolved.
The %: is not needed and #for does not work. So we need to turn off echo using #echo off: and then for loop will work with simple 'for' keyword instead of #for.
Working code is here:
SHELL = sh
# %:
#echo off :
for i in $(subdirs) ; \
do \
(cd $$i && $(MAKE) $#) || break; \
done
# eof

Makefile check if folder and/or file exists

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"

variable name expansion not working inside target in GNU Makefile

I am trying to create a directory inside target based on a rule but for some reason the variable is not getting expanded i.e., $(OUT_DIR) is blank and mkdir -p does not work. Here is the code
target_%: /home/user/%/.file : file.c
export OUT_DIR = /home/user/$* ; \
mkdir -p $(OUT_DIR) ;\
.
.
.
After making the changes suggested by #Beta, here is how the code looks like
target_%: /home/user/%/.file : file.c
export OUT_DIR=/home/user/$* ; \
mkdir -p $${OUT_DIR} ;\
cd $${OUT_DIR} ; \
ln -s /home/user/file1.c file1.c.link ;\
When I run the code, i get the Error
/bin/sh: line 3: : command not found
on mkdir -p command. I have played by removing ;\ and it works till mkdir $${OUT_DIR} and cd $${OUT_DIR} but i see the soft link created in the directory where the makefile is rather than in the OUT_DIR where it should be created.
I am also not sure when to use \ vs ; \ vs not using at all. Any suggestion on that would be awesome
Make variables and shell variables are not the same thing.
If you have something like $(OUT_DIR) in the makefile, Make will expand it, and since you haven't defined it in the makefile, it expands to nothing. Your rule then looks like:
target_%: /home/user/%/.file : file.c
export OUT_DIR = /home/user/$* ; \
mkdir -p ;\
...
If the command (export ...) you define OUT_DIR as a shell variable. The way to expand a shell variable in the shell is with '$', like this:
...$OUT_DIR ...
But '$' is a special character in Make, so to use it as you intend, you must escape is with another '$':
target_%: /home/user/%/.file : file.c
export OUT_DIR = /home/user/$* ; \
mkdir -p $$OUT_DIR ;\
...

Makefile (counting)

I'm completely stumped on how to do this in a Makefile
Let's say I have a target. Inside the target I have a loop. How do i change a variable to keep track of the iterations?
For example:
COUNTER = 0
target:
(loop){
COUNTER++
echo COUNTER
}
I know that variables in Makefiles are only expanded, and I'm not sure if they can be permanently changed, but there has to be a way to do this, right? :(
Here are some sources that are asking similar questions. It seems like those examples only change the variable temporarily:
How do I perform arithmetic in a makefile?
How to do arithmetic operation in makefile?
Doing simple math in Makefile
Maybe I have to use the eval function somehow?
Maybe I have to append onto a Makefile string a character each time and then use something in the shell to count the characters?
If the variable doesn't have to survive the rule, this should do (I'm assuming bash):
clean:
#n=0 ; \
for x in $(THINGS_TO_BE_DELETED); do \
if [ -f $$x ] ; then \
rm $$x; \
let "n+=1" ; \
fi ; \
done ; \
echo deleted $$n files;
Here is one solution: Write a simple script like this:
#!/bin/bash
count=`cat count.txt`
count=$((count + 1))
echo $count
cat $count > count.txt
Initialize the file by doing
$ echo "0" > count.txt
Then include it as a .PHONY requirement to build whatever you'd like.
This is similar to the accepted answer, but the syntax below should work with a POSIX compliant shell. Quotes should also be used inside of the test.
clean:
#n=0; \
for x in *.a *.b *.c ; do \
if [ -f "$$x" ]; then \
rm "$$x"; \
n=$$((n+1)); \
fi; \
done; \
echo deleted $$n files;
Note: tabs must be used for indentation

Resources