If conditions in a Makefile, inside a target - if-statement

I'm trying to setup a Makefile that will search and copy some files (if-else condition) and I can't figure out what exactly is wrong with it? (thou I'm pretty sure it's because a combination of spaces/tabs written in the wrong place).
Can I get some help with it, please?
Here's what I have currently:
obj-m = linuxmon.o
KDIR = /lib/modules/$(shell uname -r)/build
UNAME := $(shell uname -m)
all:
$(info Checking if custom header is needed)
ifeq ($(UNAME), x86_64)
$(info Yes)
F1_EXISTS=$(shell [ -e /usr/include/asm/unistd_32.h ] && echo 1 || echo 0 )
ifeq ($(F1_EXISTS), 1)
$(info Copying custom header)
$(shell sed -e 's/__NR_/__NR32_/g' /usr/include/asm/unistd_32.h > unistd_32.h)
else
F2_EXISTS=$(shell [[ -e /usr/include/asm-i386/unistd.h ]] && echo 1 || echo 0 )
ifeq ($(F2_EXISTS), 1)
$(info Copying custom header)
$(shell sed -e 's/__NR_/__NR32_/g' /usr/include/asm-i386/unistd.h > unistd_32.h)
else
$(error asm/unistd_32.h and asm-386/unistd.h does not exist)
endif
endif
$(info No)
endif
#make -C $(KDIR) M=$(PWD) modules
clean:
make -C $(KDIR) M=$(PWD) clean
rm unistd_32.h
Anyways, that'll print "Yes", "Copying header" twice and then it will quit saying that sed can't read /usr/include/asm-i386/unistd.h (which of course it can't read as I'm on a x64 system).
I could say that make just isn't understanding the if/else and instead is running everything line by line.

You can simply use shell commands. If you want to suppress echoing the output, use the "#" sign. For example:
clean:
#if [ "test" = "test" ]; then\
echo "Hello world";\
fi
Note that the closing ; and \ at each line are necessary
(This is because make interpret each line as a seperate command unless it ends with \)

There are several problems here, so I'll start with my usual high-level advice: Start small and simple, add complexity a little at a time, test at every step, and never add to code that doesn't work. (I really ought to have that hotkeyed.)
You're mixing Make syntax and shell syntax in a way that is just dizzying. You should never let it get this big without testing. Let's start from the outside and work inward.
UNAME := $(shell uname -m)
all:
$(info Checking if custom header is needed)
ifeq ($(UNAME), x86_64)
... do some things to build unistd_32.h
endif
#make -C $(KDIR) M=$(PWD) modules
So you want unistd_32.h built (maybe) before you invoke the second make, you can make it a prerequisite. And since you want that only in a certain case, you can put it in a conditional:
ifeq ($(UNAME), x86_64)
all: unistd_32.h
endif
all:
#make -C $(KDIR) M=$(PWD) modules
unistd_32.h:
... do some things to build unistd_32.h
Now for building unistd_32.h:
F1_EXISTS=$(shell [ -e /usr/include/asm/unistd_32.h ] && echo 1 || echo 0 )
ifeq ($(F1_EXISTS), 1)
$(info Copying custom header)
$(shell sed -e 's/__NR_/__NR32_/g' /usr/include/asm/unistd_32.h > unistd_32.h)
else
F2_EXISTS=$(shell [[ -e /usr/include/asm-i386/unistd.h ]] && echo 1 || echo 0 )
ifeq ($(F2_EXISTS), 1)
$(info Copying custom header)
$(shell sed -e 's/__NR_/__NR32_/g' /usr/include/asm-i386/unistd.h > unistd_32.h)
else
$(error asm/unistd_32.h and asm-386/unistd.h does not exist)
endif
endif
You are trying to build unistd.h from unistd_32.h; the only trick is that unistd_32.h could be in either of two places. The simplest way to clean this up is to use a vpath directive:
vpath unistd.h /usr/include/asm /usr/include/asm-i386
unistd_32.h: unistd.h
sed -e 's/__NR_/__NR32_/g' $< > $#

Related

gcc -MM on cygwin: sometimes unix style paths

I have a (legacy) bash script that parses a list of directories for source files and echos them into a makefile to create actual makerules with gcc -MM. The script works just fine, the problem is that running make on the generated makefile results in inconsistent path format in the makerules - while most makerules have mixed style paths (which is ok), some have cygwin style paths (not ok). This didn't happen on a very old (~2002 release) of cygwin.
Here is the script:
#!/bin/bash
fn_source=SourceFileList
fn_depmake=depMakefile
fn_orgMake=Makefile
fn_depIncl=Depend.incl
fn_optIncl=OptimizationCflags.incl
#
# create a Makefile to determine the dependencies
echo -n create dep-makefile ...\
#
# generate include for Optimization-File
#
echo > $fn_depIncl
echo include $fn_optIncl >> $fn_depIncl
echo >> $fn_depIncl
#
touch $fn_optIncl
#
echo > $fn_depmake
# CFLAGS options from Makefile
grep "^CFLAGS[ =]" $fn_orgMake | grep $BUILD_SPEC >> $fn_depmake
grep "^CFLAGS_AS[ =]" $fn_orgMake | grep $BUILD_SPEC >> $fn_depmake
grep "^CFLAGS_GCC[ =]" $fn_orgMake | grep $BUILD_SPEC >> $fn_depmake
echo -n generate depMakefile ...
echo >> $fn_depmake
# the default rule
echo all: >> $fn_depmake
FileList=`cat $fn_source`
incPath=""
rawIncPath=""
for fn in $FileList; do
incPath=$incPath" -I"${fn%/*}
rawIncPath=$rawIncPath" "${fn%/*}
done
# filter out redundant paths
echo $incPath | \
sed -n -e 's/ /\
/gp' | \
sort -u > _tmp
incPath=`cat _tmp`
# filter out redundant paths
echo $rawIncPath | \
sed -n -e 's/ /\
/gp' | \
sort -u > _tmp
rawIncPath=`cat _tmp`
#
# create file with include paths
#
echo LibIncludes=$incPath > LibIncludes
#
# put all source files with the same path in one gcc command
#
for path in $rawIncPath; do
sourceFiles=`grep $path/ $fn_source`
echo " "echo do $path >> $fn_depmake
echo " "gcc -MM '$(CFLAGS_GCC)' -I$path $sourceFiles \>\> _tmp >> $fn_depmake
done
# start the Makefile
echo start Makefile ...
rm _tmp; touch _tmp
make -s -f $fn_depmake
# add ./obj/xxxxx*.o to the make-rules
sed -e 's/\(^[a-zA-Z]\)/.\/obj\/\1/' < _tmp > _tmp1
# add command to the make-rules
sed -e '/\\/!a\
$(CC) $($(*F)CFLAGS) -I$(<D) $(CFLAGS) -c $< -o $#' < _tmp1 >> $fn_depIncl
# workaround for gcc generating cygdrive paths for some paths only
sed -i 's#/cygdrive/d#d:#' $fn_depIncl
rm _tmp _tmp1
rm $fn_depmake
echo done.
As you can see, I am currently using a workaround to convert those paths to mixed styled ones using sed, but I would like to know why this happens in the first place?
Example of a rule gone "wrong":
some.o: d:/some/path/A/some.cpp \
d:/some/path/A/some.h \
/cygdrive/d/some/path/B/subpathA/anotherheader.h \
/cygdrive/d/some/path/B/subpathA/yetanotherheader.h \
D:/some/path/B/subpathB/header.h
The main problem about this is that the resulting makefile is fed to a windows native make.exe, which has no idea what /cygdrive/d/path is supposed to be.
I have looked at the paths after each step, and they are all mixed style until make -s -f $fn_depmake is executed. Meaning, the occasional conversion to cygwin style paths is done by the gcc -MM command in the generated makefile.
It is always the same list of directories that get converted to /cygdrive/d/pathA/somefile.cpp style of paths. There is nothing unusual about the path, no spaces, only a-z/A-Z in name, length of the path is short, longest path is 76 characters including the filename, base path is 34 characters long.
How can I force gcc to generate mixed style paths?
I can see the same problem on my project. I'm using gmake.exe from Cygwin on Windows.
One invocation of gcc -MM outputs both Windows-style paths (C:/project/file.h) and Cygwin-style paths (/cygdrive/c/project/file.h).
I don't know how to fix that yet, but I found what triggers the change to Cygwin-style paths. This happens when an include uses a relative path. So, when a file contains #include "../file.h", then the output of gcc -MM will contain the absolute path of file.h, using a Cygwin-style path.

How to find specific files in folders and do an operation in case they exist

I am having some difficulty doing a basic script in sh shell.
What I want to do is simple though:
I want to do a sh script (can also be csh) that looks through a number of folders and for each folder that contains the files I am interested in, it should do a specific operation of pasting the corresponding filename into a sh script with rdseed commands.
The script I wrote in sh shell and doesn't work is:
for dir in EV*
do
echo $dir
cd $dir
if [ -f GEFLE* = true ];
then
set dataless = gur_ini_dataless.seed
for file GEFLE*
do
echo "rdseed -d -o 2 -f "$file " -g " $dataless >> runmseed2ahGEFLE.sh
done
else
echo "File does not exists"
fi
sleep 0.5
cd ..
done
Does anyone know a solution?
Please try this... I'm adding some comments to the lines...
#!/bin/sh
for dir in EV*
do
echo $dir
cd $dir
if [ -f GEFLE* ] # true if at least one FILE named "GEFLE*" exists
then
dataless=gur_ini_dataless.seed # no `set`, no spaces
for file in GEFLE* # will match all FILES/DIRS/... that start with "GEFLE"
do
echo "rdseed -d -o 2 -f $file -g $dataless" >> runmseed2ahGEFLE.sh # vars are substituted in double quoted strings
done
else
echo "File does not exists"
fi
cd ..
done
Please note this will only look one level deep into the directories. If you need some resursion you should better use something like
for dir in `find . -type d -name 'EV*'`; do
# ...
done
The way I had put this is:
for f in `find EV* -name GEFLE* -type f`; do
echo "rdseed -d -o 2 -f ./$f -g gur_ini_dataless.seed >> ./`dirname $f`/runmseed2ahGEFLE.sh"
done

Using sed to replace multiple instances in the same line before a comment

I am trying to use sed to replace all instances of a command with a variable, expect when they come after a comment or is part of another word. I have gotten close, being able to replace one instance before a comment, but not if there is more than one.
I have a test file with the line:
rm rm # rm
I want to make this read:
$RM $RM # rm
This is what I have so far:
sed -i 's/\(^\|[^[#.*]]\)\brm\b/\1$RM/' file1
Which returns:
$RM rm # rm
Any help is much appreciated. Other solutions not involving sed are welcome, but I might need some help understanding them.
Thanks!
EDIT:
This is just an example of what I am looking for. Not every line will be formatted like this, and not every line will contain a command before the comment, or vise versa. I am just looking for a solution that will also cover a situation similar to this example. Sorry for the lack of explanation. Here is a slightly better example:
"$#" #rm
# rm
rm # rm
rm
"rm "
'rm '
`rm `
{rm }
$# rm # rm
rm rm # rm
rm # rm rm
rmremovermlink
Output should be:
"$#" #rm
# rm
$RM # rm
$RM
"$RM "
'$RM '
`$RM `
{$RM }
$# $RM # rm
$RM $RM # rm
$RM # rm rm
rmremovermlink
You can use this sed command:
sed ':a;s/^\([^#]*\)\<rm\>/\1$RM/;ta;' file
# ^ ^ ^ ^ ^ ^ ^---- go to label "a" if something is replaced
# | | | | | '---- back-reference to capture group 1
# | | | '---'---- word boundaries
# | | '---- capture group 1
# | '---- replacement (only one occurrence)
# '---- defines the label "a"
You can use this perl command for this substitution:
perl -pe 's/(?<!\$)#.*$(*SKIP)(*F)|\brm\b/\$RM/g' file
"$#" #rm
# rm
$RM # rm
$RM
"$RM "
'$RM '
`$RM `
{$RM }
$# $RM # rm
$RM $RM # rm
$RM # rm rm
rmremovermlink
RegEx Demo
\$[A-Z]{2}\s\$[A-Z]{2}\s\#\s[a-z]{2}
Let me know if this is what you're looking for. It matches :
$RM $RM # rm

Understanding a Makefile with automatic dependencies: Problems with 'sed' part

I have followed this tutorial. It explains the making of Makefile which take care of dependencies. I have made the following Makefile which works according to the following directory structure:
folder--|Makefile
|src----|(all .c and .h files here)
|obj----|(all objects file are made here)
|bin----|(target is made here)
The Makefile is:
TARGET = exec
CC = gcc
CFLAGS = -g -I.
LINKER = gcc -o
LFLAGS = -I. -lm -lpthread
BINDIR = bin
OBJDIR = obj
SRCDIR = src
INTERFACE = interface
STD = -std=c99
PROGRAMSOURCES := $(wildcard $(SRCDIR)/*.c)
PROGRAMINTERFACE:= $(wildcard $(INTERFACE)/*.h)
OBJECTS := $(PROGRAMSOURCES:$(SRCDIR)/%.c=$(OBJDIR)/%.o)
$(BINDIR)/$(TARGET) : $(OBJECTS)
$(LINKER) $# $(LFLAGS) $(OBJECTS) $(STD)
#pull the dependencies to the .o files
-include $(OBJECTS:.o=.d)
#the -o $# says to put the output of the compilation in the file named on the left side of the :.
#the $< is the first item in the dependencies list. Basically the name of the .c file which is to be compiled.
$(OBJECTS) : $(OBJDIR)/%.o :$(SRCDIR)/%.c
$(CC) $(CFLAGS) -c $< -o $# $(STD)
$(CC) $(CFLAGS) -MM $< > $*.d
#mv -f $*.d $*.d.tmp #changes file name
#sed -e 's|.*:|$*.o:|' < $*.d.tmp > $*.d #Unable to understand
#sed -e 's/.*://' -e 's/\\$$//' < $*.d.tmp | fmt -1 | \
sed -e 's/^ *//' -e 's/$$/:/' >> $*.d #Unable to understand
#rm -f $*.d.tmp
.PHONY : run
run :
./$(BINDIR)/$(TARGET) ${TYPE} ${INP_FILE}
print:
#echo $(OBJECTS)
I have understood that it is trying to pre-process the temp file to generate auto dependency. What I am unable to understand is how it is being done. Here are the two lines I want explanation of:
#sed -e 's|.*:|$*.o:|' < $*.d.tmp > $*.d #Unable to understand
#sed -e 's/.*://' -e 's/\\$$//' < $*.d.tmp | fmt -1 | \
sed -e 's/^ *//' -e 's/$$/:/' >> $*.d #Unable to understand
I have never used sed before so I am having problems.
Any help appreciated.
Suppose you are building obj/foo.o from src/foo.c, so the file foo.d.tmp contains:
foo.o: src/foo.c src/foo.h src/bar.h
Now the first sed statement:
#sed -e 's|.*:|$*.o:|' < $*.d.tmp > $*.d
"Read foo.d.tmp, take everything up to a colon and change it to 'foo.o', and write the result to foo.d." So now foo.d contains:
foo.o: src/foo.c src/foo.h src/bar.h
(No change in this case.) Now the next command:
#sed -e 's/.*://' -e 's/\\$$//' < $*.d.tmp | fmt -1 | sed -e 's/^ *//' -e 's/$$/:/' >> $*.d
"Read foo.d.tmp, remove everything up to (and including) a colon, remove the trailing \ if there is one. Take the result and put each word (i.e each prerequisite) on its own line. Then for each line, remove the leading spaces, put a colon at the end, and append the result to foo.d." So now foo.d contains:
foo.o: src/foo.c src/foo.h src/bar.h
src/foo.c:
src/foo.h:
src/bar.h:
The idea is to create an empty rule for each prerequisite, so that if the code has changed and a certain prerequisite is no longer needed -- and no longer present -- but it is still listed in the old foo.d, Make will not panic at being unable to build it.

GNU Make: How to add an option in front of every entry of a list

I have a problem where I want to create a variable that looks like this,
INCDIRS = -I dir0 -I dir1 -Idir2 -I dir3 ... -I dirN
where dir1, ... , dirN are the names of all the subdirectories some base directory, base_dir.
How would I go about building up this variable? Originally, I thought I could do the following,
INCDIRS = $(shell for x in `find base_dir -type -d -print`; do echo -I $x; done;)
but this just results in
INCDIRS = -I -I -I -I ... -I
If anyone could explain how to do this, or explain why my original command got the output that it did, I would greatly appreciate it. Thanks!
You have two errors in your INCDIRS assignment. One is in the find command. It should be find -type d -print (or just find -type d; the -print is superfluous here). The other error the use of $x. You need to escape the $ with another $:
INCDIRS = $(shell for x in `find base_dir -type d -print`; do echo -I $$x; done;)
You could first get the list of directories, then add -I in front of each one:
SOURCE_DIRS := $(shell find base_dir -type d -print)
INCDIRS = $(addprefix -I,$(SOURCE_DIRS))
which may be better if you need $(SOURCE_DIRS) for something else.