using ifeq statement in makefile, missing separator even when indented with tab correctly - makefile

I was just practising with GNU make and I was trying to write a clean target, the make file is this:
includePath := -I $(CURDIR)
outputDir = $(CURDIR)/output
COMPILER := g++
standard := -std=c++17
#sourceFiles
main.cpp := main.cpp
#output files
binaryPath = $(outputDir)
binary = main.out
#int_binaryPath = intermediate binary
int_binaryPath = $(outputDir)/int_binaries
int_binary = main.o
GPP = $(COMPILER) $(standard) $(includePath)
#print info while make is executing
# $(info $(includeDir) )
VPATH = outputDir/int_binaries $(CURDIR)/glad/src
#build=compile,link and run
$(binary): $(int_binary) glad.c -lGL -lGLU -lglfw -ldl
$(GPP) $(int_binaryPath)/main.o $(CURDIR)/glad/src/glad.c -o $(binaryPath)/$# -lGL -lGLU -lglfw -ldl
$(int_binary): $(main.cpp)
$(GPP) $(main.cpp) -c -o $(int_binaryPath)/$(int_binary)
build: $(binary)
$(binaryPath)/$(binary)
build#bg: $(binary)
$(binaryPath)/$(binary) &
rebuild: clean build
#echo "rebuild successful"
.PHONY: clean
clean:
ifeq($(wildcard $(outputDir)/$(binary)), $(outputDir)/$(binary))
rm -rf $(outputDir)/$(binary)
else ifeq($(wildcard $(outputDir)/$(int_binary)), $(outputDir)/$(int_binary))
rm -rf $(outputDir)/$(int_binary)
endif
now the part that is throwing up error is this:
.PHONY: clean
clean:
ifeq($(wildcard $(outputDir)/$(binary)), $(outputDir)/$(binary)) #this has spaces, line 47
rm -rf $(outputDir)/$(binary) #this has 2 tabs, but even 1 tab didn't work
else ifeq($(wildcard $(outputDir)/$(int_binary)), $(outputDir)/$(int_binary))
rm -rf $(outputDir)/$(int_binary)
endif
now, when I do make clean, it returns the following error:
makefile:47: *** missing separator. Stop.
What exactly am I doing wrong? Why isn't it working and what does the error mean?

You have to have a space between the ifeq and the open paren.
You have other issues; for example there's no comma between words in the wildcard function, so you're literally asking it to look for the file ./output/main.out, (including the ,) exists.
Anyway, this is not needed because rm -rf foo is simply a no-op if foo doesn't exist... you don't need all this complexity.

Related

Makefile 'missing separator' for ifneq

I know there are other issues with similar titles, but they don't seem to hold the solution.
Heres my makefile:
# Compiler Command
CC = mpiCC
CFLAGS = -c -I./header
# collecting object file names
src = $(wildcard source/*.cpp)
src1 = $(src:.cpp=.o)
objects := $(src1:source/%=bin/%)
# Compile object files into binary
all : $(objects)
$(CC) -o run $(objects)
ifneq($(n),) // <- error location , line 15
mpirun -np $(n) run
endif
# Generate object files by compiling .cpp and .h files
bin/%.o : source/%.cpp
$(CC) $(CFLAGS) $?
mv *.o bin
# Clean Recipe
.PHONY : clean
clean :
rm -rf all $(objects)
The goal of the ifneq is to have the binary run whenever it finishes compiling.
for example, a user runs the command:
make <- builds without running
make n=5 <- builds and runs on 5 processes
Whenever I use either of these, I get the error:
makefile:15: *** missing separator. Stop.
I've used cat -e -t -v to verify everything is tabbed instead of spaced. according to (https://www.gnu.org/software/make/manual/make.html#Conditional-Example) this conditional should function.
#MadScientist solved it. You need to put a space in between ifneq and its argument. For example:
ifneq($(n),0) is invalid.
ifneq ($(n),0) is valid.

Simple makefile command not found

I was given a makefile that looks like this, and told not to change it.
all: clean flex scanner.lex bison -d parser.ypp g++ -std=c++11 -o hw2 *.c *.cpp clean: rm -f lex.yy.c rm -f parser.tab.*pp rm -f hw2
I am trying to run this makefile in a folder with files named: scanner.lex, parser.ypp, output.hpp and output.cpp
I copied it to a file like this:
all:
clean flex scanner.lex bison -d parser.ypp g++ -std=c++11 -o hw2 *.c *.cpp
clean:
rm -f lex.yy.c rm -f parser.tab.*pp rm -f hw2
When I run the make command in my terminal I get an error:
clean flex scanner.lex bison -d parser.ypp g++ -std=c++11 -o hw2 *.c *.cpp
/bin/sh: clean: command not found
make: *** [all] Error 127
Am I doing something wrong? Again, I was given this line and told not to change it.
Thanks a lot.
Line breaks are essential in most computer environments. If you were given a Makefile without the line breaks and you try to cut it randomly you will have difficulties before if finally works. Try this, maybe:
all: clean
flex scanner.lex
bison -d parser.ypp
g++ -std=c++11 -o hw2 *.c *.cpp
clean:
rm -f lex.yy.c
rm -f parser.tab.*pp
rm -f hw2
And use tabs to indent the indented lines, not spaces.
Explanations: all and clean are what is called a target in make parlance. They are the names of the things you want make to do. clean to delete some files, all to do everything else. The
target: prerequisite1 prerequisite2...
recipe1
recipe2
...
template is the basic make template. It means that target depends on prerequisite1, prerequisite2 and that in order to build it make shall pass recipe1 to the shell for execution, then recipe2...
Note that this Makefile is poorly written. As all and clean are not real file names they should be declared as phony, such that, if a file with that name exists make does the job anyway. As is, it wouldn't. Give it a try:
$ make all
$ touch clean
$ make clean
make: 'clean' is up to date.
See? Because a file named clean exists you cannot make clean anymore, make considers that there is nothing to do for clean. Add this at the beginning of your Makefile:
.PHONY: all clean
A second issue is that make works by comparing last modification times of targets and prerequisites to decide if targets must be rebuilt or not. With your Makefile make will always recompile everything, even if the inputs did not change and the outputs are up-to-date. This is a waste. A better (but untested) Makefile would be something like:
.PHONY: all clean
CFILES := $(filter-out lex.yy.c,$(wildcard *.c))
CPPFILES := $(filter-out parser.tab.cpp,$(wildcard *.cpp))
all: hw2
hw2: lex.yy.c parser.tab.cpp $(CFILES) $(CPPFILES)
g++ -std=c++11 -o $# $^
lex.yy.c: scanner.lex
flex $<
parser.tab.cpp: parser.ypp
bison -d $<
clean:
rm -f lex.yy.c
rm -f parser.tab.*pp
rm -f hw2
Understanding it and why it is better is left as an exercise.

Why do I get an "Unterminated quoted string" error?

This is my makefile for a particular C++ project, but when I run make all, I get:
/bin/sh: 1: Syntax error: Unterminated quoted string
Makefile:28: recipe for target '"system' failed
make: *** ["system] Error 2
When I change exe to something with no spaces, it works fine.
EDIT #1: The reason I used quotes was so I could use spaces in the output file name.
cc = g++
exe = "system software version 1.0"
src_dir = src
obj_dir = obj
src = $(wildcard $(src_dir)/*.cpp)
obj = $(patsubst $(src_dir)/%.cpp, $(obj_dir)/%.o, $(src))
libs = -lallegro -lallegro_primitives
include_paths = -I "include" -I "~/allegro5/include/"
lib_paths = -L "/usr/lib/"
flags = -Wall -Wextra -Wpedantic -g -std=c++14
all: $(exe)
play: all
$(exe)
$(exe): $(obj)
$(cc) $(lib_paths) $^ $(libs) -o $#
compile_only: $(obj)
$(obj_dir)/%.o: $(src_dir)/%.cpp
$(cc) $(flags) $(include_paths) -c $^ -o $#
clean:
rm -f $(obj_dir)/*.o
build: all
rebuild: clean build
.PHONY: all clean rebuild
You cannot use paths containing whitespace with make. There are crazy tricks that can make some limited things work, but basically it's simply not supported.
See this recent question and answer: https://stackoverflow.com/a/56411000/939557

Make says "missing separator" for source .c file from CuTest

I'm trying to get started with CuTest to do unit testing in C.
When make-ing, I get the following error:
dev:~/bistro# make
cutest/CuTest.c:10: *** missing separator. Stop.
The file cutest/CuTest.c comes directly from the library. I have done no mods to it. Here are the concerned lines:
08 - #include "CuTest.h"
09 -
10 - /*-------------------------------------------------------------------------*
11 - * CuStr
12 - *-------------------------------------------------------------------------*/
13 -
14 - char* CuStrAlloc(int size)
Here's the Makefile I'm using, for complete reference:
NAME = bistro
SOURCES_DIR = src
OBJECTS_DIR = obj
SOURCES = $(shell find $(SOURCES_DIR) -type f -name *.c) cutest/CuTest.c
OBJECTS = $(patsubst $(SOURCES_DIR)/%.c, $(OBJECTS_DIR)/%.o, $(SOURCES))
DEPS = $(OBJECTS:.o=.d)
CFLAGS = -Wall -Werror -Wextra
COMPILER = gcc -I cutest -I $(SOURCES_DIR) $(CFLAGS)
BISTRO_MAIN = $(OBJECTS_DIR)/bistro/bistro_main.o
.PHONY: test all clean fclean re
all: $(NAME)
# header dependencies
-include $(DEPS)
$(NAME): $(OBJECTS)
$(COMPILER) -o $(NAME) $(OBJECTS)
test: $(filter-out $(BISTRO_MAIN), $(OBJECTS))
$(COMPILER) -c all_tests.c -o all_tests.o
$(COMPILER) -o test $(filter-out $(BISTRO_MAIN), $(OBJECTS)) all_tests.o
rm -f all_tests.o
$(OBJECTS_DIR)/%.o: $(SOURCES_DIR)/%.c
#if [ ! -d "$(#D)" ]; then mkdir -p $(#D); fi
$(COMPILER) -MMD -c $< -o $#
clean:
rm -Rf $(OBJECTS_DIR)/*
fclean: clean
rm -f $(NAME)
re: fclean all
What could be the cause of this error message?
EDIT 1: The makefile is indented with 4-space tabs only. Could the call to the "find" command be the cause of that? Also, how come the error says the missing separator is in the .c file?
EDIT 2: The accepted answer shows the error. In addition, the call did not work because it was searching for *.c, which should be "*.c".
It means you are using (four) spaces instead of tab symbol.
Make target's command must be indented with a tab.
See http://www.gnu.org/software/make/manual/make.html#Rule-Introduction:
Please note: you need to put a tab character at the beginning of every recipe line! This is an obscurity that catches the unwary. If you prefer to prefix your recipes with a character other than tab, you can set the .RECIPEPREFIX variable to an alternate character
Your error comes because you include a C source file into Makefile.
SOURCES = $(shell find $(SOURCES_DIR) -type f -name *.c) cutest/CuTest.c
OBJECTS = $(patsubst $(SOURCES_DIR)/%.c, $(OBJECTS_DIR)/%.o, $(SOURCES)) # cutest/CuTest.c stays cutest/CuTest.c
DEPS = $(OBJECTS:.o=.d)
-include $(DEPS) # oops, includes cutest/CuTest.c

Make won't accept appended CFLAGS value

I've searched around for this issue, but nobody but me seems to have it, which is why I'll now ask.
If have this basic makefile:
CCPP = arm-none-linux-gnueabi-g++
CFLAGS = "-WALL -DPLATFORM_TARGET -DPRINT_MESSAGE"
LIB = lib/libarm.a
LDFLAGS = -lpthread
OBJECTS = $(wildcard ./*/*.o)
PROG = /exports/appl
MODULES = lib src
all: $(PROG)
$(CCPP) $(LDFLAGS) $(OBJECTS) $(LIB) -o $(PROG)
$(PROG): $(MODULES)
#for i in $(MODULES); do (cd $$i && $(MAKE) \
CCPP=$(CCPP) LDPP=$(CCPP) CFLAGS=$(CFLAGS) LDFLAGS=$(LDFLAGS)) || exit 1 ;\
done
clean:
#for i in $(MODULES); do (cd $$i && $(MAKE) clean) || exit 1 ; done
rm $(PROG)
lib:
ar cr ../lib/$(LIB) $(OBJECTS)
This works. It takes whatever source file is within lib and src and compiles and links it nicely together. (By using local makefiles found in these folders which I can post too if need be)
Anyway, what I WANT now, is add more -D directives conditionally.
I've tried:
ifdef ALLOW_BACKTRACE
CFLAGS += -DALLOW_BACKTRACE
LDFLAGS += -rdynamic
endif
and also:
ifdef ALLOW_BACKTRACE
CFLAGS := $(CFLAGS) -DALLOW_BACKTRACE
#endif
or by putting the whole thing in quotes etc...but each time I try, it brings up the help page of make, telling me that it can't 'recognize' the new define.
Any idea what I'm doing wrong?
Any help is much appreciated.
Okay, this should be a more correct version of your makefile, I can not test it though because I don't have your sources:
export CCPP := arm-none-linux-gnueabi-g++
# Note that -pthread is required for both compiling and linking.
export CFLAGS := -pthread -WALL -DPLATFORM_TARGET -DPRINT_MESSAGE
export LDFLAGS := -pthread
LIB := lib/libarm.a
PROG := /exports/appl
MODULES := lib src
all: $(PROG)
$(PROG): $(MODULES)
$(CCPP) -o $# $(LDFLAGS) ./*/*.o $(LIB)
$(MODULES) : % :
$(MAKE) -C $#
touch $#
clean-module.%:
$(MAKE) -C $* clean
clean : $(MODULE:%=clean-module.%)
rm -f $(PROG)
.PHONY: all clean clean-module.%
What I changed:
LDFLAGS = -lpthread: when building multi-threaded applications you need both an extra compiler and linker flag, which is what -pthread/-pthreads gcc options is.
Contents of OBJECTS = $(wildcard ./*/*.o) are only correct when $(MODULES) built correctly. Removed it.
$(PROG) commands actually build $(PROG) target as it should.
$(MODULES) commands build the modules by invoking make in the corresponding directory. And then they update the timestamp of the directory to force rebuild of $(PROG). Since it is a recursive make it can't know whether anything have actually been updated in the module, hence it need to trigger the rebuild of anything that depends on the modules.
I still have a feeling that this won't work for you because your original makefile is missing dependencies.
Try doing this -->
ifeq ($(ALLOW_BACKTRACE),1)
CFLAGS += -DALLOW_BACKTRACE
endif
You've got to be KIDDING me!
Ahem. I seem to have found the solution to my own problem. I don't quite get it, but whatever works, right?
Anyway, here's what I did:
CFLAGS += -Wall -DPLATFORM_TARGET -DPRINT_MESSAGE
ifdef ALLOW_BACKTRACE
CFLAGS += -DALLOW_BACKTRACE
LDFLAGS += -rdynamic
endif
LDFLAGS += -lpthread
$(PROG): $(MODULES)
#for i in $(MODULES); do (cd $$i && $(MAKE) \
CCPP=$(CCPP) LDPP=$(CCPP) CFLAGS="$(CFLAGS)" LDFLAGS=$(LDFLAGS)) || exit 1 ;\
done
First thing: -rdynamic needs to be the first flag in the linker, otherwise it refuses to work. (Don't ask me why, if anyone could enlighten me, be my guest.
Second: I had to put quotes around the expanded $(CFLAGS) in my actual build step. As soon as I did that, it worked like a charm...probably because it had a problem with the spaces.
Thanks to everyone, who went to the trouble of trying to help me.

Resources