Makefile eval function causing unexpected end of file - makefile

I'm trying to increment a Makefile variable with eval function inside a rule (to do a fancy loading bar), by doing
$(eval COUNTER=$(shell echo $$(($(COUNTER) + 1))))
I found on internet that this is working for some people but this line causes me a Syntax Error: unexpected end of file, what should I do to increment my counter? Are there some non-horrific alternatives to this function, or a way to avoid this unexpected end of file? thanks for your help
Here is the entire Makefile I'm working on
SHELL = /bin/sh
.SUFFIXES = .c .h .o
NAME = Necklace
SRCD = src
INCD = inc
OBJD = .obj
SRC = $(addsuffix $(word 1, $(.SUFFIXES)),\
main\
necklace\
utils)
INC = $(addsuffix $(word 2, $(.SUFFIXES)),\
necklace)
OBJ = $(SRC:$(word 1, $(.SUFFIXES))=$(word 3, $(.SUFFIXES)))
CC = gcc
CFLAGS = -Wall\
-Werror\
-Wextra\
-I $(INCD)
COUNT = $(shell cat file.count 2>/dev/null)
COMPILED = $(shell echo $$(($(shell ls src/*$(word 1, $(.SUFFIXES)) | wc -l | tr -d ' ') - $(COUNT))))
define set
set = $(eval $1 := $2)
ifeq ($(COUNT),)
all:
echo $(words $(SRC)) > file.count && $(MAKE) -n | grep $(CC) | wc -l | tr -d ' ' > tmp.txt && rm -f file.count && echo $$(($$(cat tmp.txt) - 1)) > file.count && make
else
all: $(NAME)
endif
$(NAME): $(addprefix $(OBJD)/, $(OBJ)) | $(addprefix $(INCD)/, $(INC))
$(CC) $(CFLAGS) $^ -o $#
$(OBJD):
mkdir $#
$(OBJD)/%.o: $(SRCD)/%.c | $(OBJD)
#printf '\r\033[2KCompiling [' && sh prc.sh $(COMPILED) $(COUNT) && printf ']' && $(CC) $(CFLAGS) -c $< -o $# && $(eval COMPILED:=$(shell echo $$(($(COMPILED) + 1)))) #Syntax Error is here
clean:
rm -rf $(OBJD)
fclean: clean
rm -rf $(NAME)
re: fclean all

I managed to do it by writing and reading the variable into a file instead of using Make variables

Related

makefile error: No rule to make target How to fix?

The error:
make: *** No rule to make target 'build-x86_64'. Stop.
My makefile:
x86_64_asm_source_files := $(shell find src/impl/x86_64 -name *.asm)
x86_64_asm_object_files := $(patsubst src/impl/x86_64/%.asm, build/x86_64/%.o, $(x86_64_asm_source_files))
$(x86_64_asm_object_files): build/x86_64/%.o : src/impl/x86_64/%.asm
mkdir -p $(dir $#) && \
nasm -f elf64 $(patsubst build/x86_64/%.o, src/impl/x86_64/%.asm, $#) -o $#
.PHONY: build-x86_64
build-x86_64: $(x86_64_asm_object_files)
mkdir -p dist/x86_64 && .
x86_64-elf-ld -n -o dist/x86_64/kernel.bin -T targets/x86_64/linker.ld $(x86_64_asm_object_files) && \
cp dist/x86_64/kernel.bin targets/x86_64/iso/boot/kernel.bin && \
grub-mkrescue /usr/lib/grub/i386-pc -o dist/x86_64/target.iso targets/x86_64/iso

make: *** No rule to make target 'build-x86_64'. Stop. error in my OS when i run make build-x86_64

x86_64_asm_source_files := $(shell find src/impl/x86_64 -name *.asm)
x86_64_asm_object_files := $(patsubst src/impl/x86_64/%.asm, build/x86_64/%.o, $(x86_64_asm_source_files))
$(x86_64_asm_object_files): build/x86_64/%.o : src/impl/x86_64/%.asm
mkdir -p $(dir $#) && \
nasm -f elf64 $(patsubst build/x86_64/%.o, src/impl/x86_64/%.asm, $#) -o $#
.PHONY: build-x86_64
build-x86_64: $(x86_64_asm_object_files)
mkdir -p dist/x86_64 && \
x86_64-elf-ld -n -o dist/x86_64/kernel.bin -T targets/x86_64/linker.ld $(x86_64_asm_object_files) && \
cp dist/x86_64/kernel.bin targets/x86_64/iso/boot/kernel.bin && \
grub-mkrescue /usr/lib/grub/i386-pc -o dist/x86_64/kernel.iso targets/x86_64/iso

Function `wildcard` doesn't see $#

I have the following Makefile:
.PHONY: all clean run
INC_DIR = include
SRC_DIR = src
KERNEL_NAME = mykernel
AS = as
ASPARAMS = --32
CXX = g++
CXXPARAMS = -m32 -ffreestanding -fno-exceptions -fno-rtti -I$(INC_DIR)
#CXXPARAMS = -m32 -fno-use-cxa-atexit -nostdlib -fno-builtin -fno-rtti -fno-exceptions -fno-leading-underscore -I$(INC_DIR)
LDPARAMS = -melf_i386
BUILD_DIR = build
ISO_DIR = $(BUILD_DIR)/iso
ISO_BOOT = $(ISO_DIR)/boot
GRUB_CONFIG = $(ISO_BOOT)/grub/grub.cfg
SRC = $(wildcard $(SRC_DIR)/*.cpp)
SRC += $(wildcard $(SRC_DIR)/**/*.cpp)
SRC += $(wildcard $(SRC_DIR)/*.s)
OBJS = $(SRC:=.o)
OBJS := $(addprefix $(BUILD_DIR)/, $(OBJS))
.SECONDARY: $(OBJS)
all: $(KERNEL_NAME).iso
run: $(KERNEL_NAME).iso
qemu-system-i386 -cdrom $<
$(KERNEL_NAME).iso: $(GRUB_CONFIG) $(ISO_BOOT)/$(KERNEL_NAME).bin
grub-mkrescue -o $# $(ISO_DIR)
$(ISO_BOOT)/$(KERNEL_NAME).bin: $(SRC_DIR)/linker.ld $(OBJS)
ld $(LDPARAMS) -T $^ -o $# -nostdlib
$(GRUB_CONFIG):
mkdir -p $(dir $#)
echo 'set timeout=0' > $(GRUB_CONFIG)
echo 'set default=0' >> $(GRUB_CONFIG)
echo '' >> $(GRUB_CONFIG)
echo 'menuentry "$(KERNEL_NAME) OS" {' >> $(GRUB_CONFIG)
echo ' multiboot2 /boot/$(KERNEL_NAME).bin' >> $(GRUB_CONFIG)
echo ' boot' >> $(GRUB_CONFIG)
echo '}' >> $(GRUB_CONFIG)
$(BUILD_DIR)/%.cpp.o: %.cpp
echo $(dir $#)
echo $(wildcard (dir $#))
#ifeq ($(wildcard (dir $#)),)
# echo "mkdir -p $(dir $#)" ;
# $(CXX) $(CXXPARAMS) $< -c -o $#
#endif
$(BUILD_DIR)/%.s.o: %.s
#if [ ! -d $(dir $#) ]; then \
echo "mkdir -p $(dir $#)" ; \
mkdir -p $(dir $#) ; \
fi
$(AS) $(ASPARAMS) $< -o $#
clean:
rm -rf $(KERNEL_NAME).iso $(BUILD_DIR)
Why do I get the output with empty echo by running make? The directory build/src exists.
echo build/src/
build/src/
echo
echo build/src/
build/src/
echo
echo build/src/
build/src/
echo
echo build/src/
build/src/
echo
echo build/src/
build/src/
echo
echo build/src/
build/src/
echo
echo build/src/gui/
build/src/gui/
echo
echo build/src/gui/
build/src/gui/
echo
ld -melf_i386 -T src/linker.ld build/src/Keyboard.cpp.o build/src/VGArray.cpp.o build/src/mykernel.cpp.o build/src/TextCursor.cpp.o build/src/VideoContext.cpp.o build/src/GDTable.cpp.o build/src/gui/DrawContext.cpp.o build/src/gui/Window.cpp.o build/src/loader.s.o -o build/iso/boot/mykernel.bin -nostdlib
ld: cannot find build/src/Keyboard.cpp.o: No such file or directory
ld: cannot find build/src/VGArray.cpp.o: No such file or directory
ld: cannot find build/src/mykernel.cpp.o: No such file or directory
ld: cannot find build/src/TextCursor.cpp.o: No such file or directory
ld: cannot find build/src/VideoContext.cpp.o: No such file or directory
ld: cannot find build/src/GDTable.cpp.o: No such file or directory
ld: cannot find build/src/gui/DrawContext.cpp.o: No such file or directory
ld: cannot find build/src/gui/Window.cpp.o: No such file or directory
Makefile:33: recipe for target 'build/iso/boot/mykernel.bin' failed
make: *** [build/iso/boot/mykernel.bin] Error 1
I tried to make a minimal example, but it works right when build directory exists.
.PHONY: all clean
CXX = g++
OPTS = -Wall -Wextra
BUILD_DIR = build
APP = $(BUILD_DIR)/main
SRC_DIR = src
SRC = $(wildcard $(SRC_DIR)/*.cpp)
all: $(APP)
$(APP): $(APP).o
$(BUILD_DIR)/%.o: $(SRC_DIR)/%.cpp
echo $(dir $#)
echo $(wildcard $(dir $#))
clean:
rm -rf $(BUILD_DIR) main *.o
Replace echo $(wildcard (dir $#)) by echo $(wildcard $(dir $#)) and things should work as you expect.
Note: SRC += $(wildcard $(SRC_DIR)/**/*.cpp) will not recurse in subdirectories. If you believed it would, try:
SRC = $(shell find $(SRC_DIR) -type f -name '*.cpp')
SRC += $(wildcard $(SRC_DIR)/*.s)

How to ensure `make` rebuilds all objects after deletion of a file

Consider the following Makefile skeleton:
HEADERS := $(shell find . -name "*.h" | sort)
SOURCES := $(shell find . -name "*.c" | sort)
OBJECTS := $(patsubst %.c, %.o, $(SOURCES))
executable: $(OBJECTS)
$(CC) $(CFLAGS) -o $# $(OBJECTS)
%.o: %.c
rm -f $#
$(CC) $(CFLAGS) -c -o $# $< || { rm -f $#; exit 1; }
$(OBJECTS): Makefile $(HEADERS)
These rules ensure that OBJECTS and executable are recompiled if any of the following files is modified:
Declaration files (*.h)
Implementation files (*.c)
The Makefile itself
This works very nicely. It also covers the case of adding a new source code file to a directory of the project (assuming the new file wasn't added using cp -a or mv). The case that isn't covered is the deletion of a file.
Recompilation on deletion is useful because it catches leftovers in the remaining source code and because it removes superfluous data from the executable.
What is a succinct and efficient way of ensuring the make command rebuilds all objects after a source code file is deleted from the project?
The answer can use any common Linux command via $(shell ...).
The reply provided by MadScientist is very good (particularly regarting the use of a modern auto-generated dependencies method).
If the modern auto-generated dependencies method is not an option, an alternative to using echo and cmp would be to have the dependencies handled by make, using include and $(file ...) (note the extra dependency for $(OBJECTS)):
HEADERS := $(shell find . -name "*.h" | sort)
SOURCES := $(shell find . -name "*.c" | sort)
OBJECTS := $(patsubst %.c, %.o, $(SOURCES))
executable: $(OBJECTS)
$(CC) $(CFLAGS) -o $# $(OBJECTS)
%.o: %.c
rm -f $#
$(CC) $(CFLAGS) -c -o $# $< || { rm -f $#; exit 1; }
$(OBJECTS): Makefile $(HEADERS) deps.mk
include deps.mk
deps.mk: $(HEADERS) $(SOURCES)
$(file >$#,$#: $(HEADERS) $(SOURCES))
$(file >>$#,$(HEADERS) $(SOURCES):)
That is only one example and any combination of behavior is possible. For instance if your requirements were:
recompile everything when
a header file is created or modified; or
a header or source file is deleted
otherwise, recompile only the added or modified source files - if any
you could capture the list of deleted files in a MISSING using the filter-out function:
HEADERS := $(shell find . -name "*.h" | sort)
SOURCES := $(shell find . -name "*.c" | sort)
OBJECTS := $(patsubst %.c, %.o, $(SOURCES))
executable: $(OBJECTS)
$(CC) $(CFLAGS) -o $# $(OBJECTS)
%.o: %.c
rm -f $#
$(CC) $(CFLAGS) -c -o $# $< || { rm -f $#; exit 1; }
include missing.mk
ifneq (,$(MISSING))
$(MISSING):
endif
$(OBJECTS): Makefile $(HEADERS) $(MISSING)
missing.mk: $(MISSING) $(HEADERS) $(SOURCES)
$(file >$#,MISSING=$$(filter-out $$(HEADERS) $$(SOURCES),$(HEADERS) $(SOURCES)))
You can create a listfile (name listfile) which contains the list of headers and C sources. If you delete a file from source tree you'll need to re-create this listfile. The listfile should be dependency in Makefile.
HEADERS := $(shell find . -name "*.h" | sort)
SOURCES := $(shell find . -name "*.c" | sort)
OBJECTS := $(patsubst %.c, %.o, $(SOURCES))
executable: $(OBJECTS)
$(CC) $(CFLAGS) -o $# $(OBJECTS)
%.o: %.c
rm -f $#
$(CC) $(CFLAGS) -c -o $# $< || { rm -f $#; exit 1; }
genlist:
find . -name \*.c -or -name \*.h > listfile
$(OBJECTS): Makefile $(HEADERS) listfile
.PHONY: genlist
After deletion you should run make genlist.
Of course you can expand this idea: you'll create a listfile and every make will generate a temporary listfile (for example use mktemp) and compares to the "official" listfile. If they differ will overwrite the "official" listfile - and don't have to run make genlist after deletion.
Everybody suggests to generate auxiliary makefile. I quite support these suggestions. However I'd prefer to keep isolated generation of this makefile. Here is solution to keep it in a single $(shell ...) command:
THIS_MAKEFILE := $(lastword $(MAKEFILE_LIST))
$(if $(shell find -name '*.h' -or -name '*.c' | \
awk '\
/.*\.h$$/ { h=h " " $$0 } \
/.*\.c$$/ { c=c " " $$0 } \
END { \
print "HEADERS :=" h; \
print "SOURCES :=" c \
}' > $(THIS_MAKEFILE).include.new && \
diff -q $(THIS_MAKEFILE).include.new $(THIS_MAKEFILE).include 2> /dev/null || mv $(THIS_MAKEFILE).include.new $(THIS_MAKEFILE).include),)
include $(THIS_MAKEFILE).include
OBJECTS := $(patsubst %.c, %.o, $(SOURCES))
executable: $(OBJECTS); $(CC) $(CFLAGS) -o $# $(OBJECTS)
%.o: %.c; rm -f $# && $(CC) $(CFLAGS) -c -o $# $< || { rm -f $#; exit 1; }
$(OBJECTS): $(THIS_MAKEFILE) $(THIS_MAKEFILE).include $(HEADERS)
$(shell) is wrapped in $(if) to suppress any output from it.
This solution has one $(shell) call instead of two and doesn't depend on relatively new $(file) GNU Make function.
Good for small project, but doesn't scale well.
One way to handle this would be to use a modern auto-generated dependencies method such as the one described here. These methods have built-in properties that handle deleted files correctly.
If you don't want to do that, then something similar to what #uzsolt suggests is what you need to do, but you have to play a trick if you want to avoid the need to run make genlist explicitly before make realizes a file is missing:
HEADERS := $(shell find . -name "*.h" | sort)
SOURCES := $(shell find . -name "*.c" | sort)
OBJECTS := $(patsubst %.c, %.o, $(SOURCES))
executable: $(OBJECTS)
$(CC) $(CFLAGS) -o $# $(OBJECTS)
%.o: %.c
rm -f $#
$(CC) $(CFLAGS) -c -o $# $< || { rm -f $#; exit 1; }
$(OBJECTS): Makefile $(HEADERS) sourcelist
sourcelist: FORCE
#for f in $(SOURCES) $(HEADERS); do echo "$$f"; done > $#.tmp
#[ `comm -23 $# $#.tmp | wc -l` -eq 0 ] || mv $#.tmp $#
FORCE:
The idea here is that you compare the old list to the new list and only modify the list if the old list contains something that the new list doesn't. This ensures that unless something is deleted the timestamp for sourcelist doesn't change, and so it won't force the object files to be out of date.

Makefile to compile lists of source files

I have lists of files that I want my Makefile to compile, one list for each source language:
CFILES= Src/Application/main.c Src/Core/data.c Lib/routines.c
ASFILES= Src/Application/startup.s Lib/sqrt.s
I want all the output in one directory:
OBJDIR= output
How do I do the equivalent of:
output/main.o : Src/Application/main.c
cc -c -o output/main.o Src/Application/main.c
output/data.o : Src/Core/data.c
cc -c -o output/data.o Src/Core/data.c
output/routines.o : Lib/routines.c
cc -c -o output/routines.o Lib/routines.c
output/startup.o : Src/Application/startup.s
as -o output/startup.o Src/Application/startup.s
output/sqrt.o : Lib/sqrt.s
as -o output/sqrt.o Lib/sqrt.s
The recipes are the same for every file in its list.
The input files are in all sorts of different directories and it is not acceptable to just list their filenames and use a search path to find them, their explicit paths must be used.
The output filename is the basename of the source file name with the extension changed to o. There are no duplicated basenames between the lists for the different source languages.
I do not want to have to list the object files, this should be derived from the source lists.
I am using gnu make, but bonus points for a portable solution.
Something like the following could do:
all :
OBJDIR := output
CFILES := Src/Application/main.c Src/Core/data.c Lib/routines.c
ASFILES := Src/Application/startup.s Lib/sqrt.s
target = ${OBJDIR}/$(patsubst %.s,%.o,$(notdir ${1}))
obj.c :=
obj.s :=
define obj
$(call target,${1}) : ${1} | ${OBJDIR}
obj$(suffix ${1}) += $(call target,${1})
${1} : ; mkdir -p `dirname $$#` && touch $$# # Create the source for testing. Remove this.
endef
define SOURCES
$(foreach src,${1},$(eval $(call obj,${src})))
endef
$(eval $(call SOURCES,${CFILES}))
$(eval $(call SOURCES,${ASFILES}))
all : ${obj.c} ${obj.s}
${obj.c} : % :
#echo cc -c -o $# $^; touch $# # echo and touch are for testing. Remove these.
${obj.s} : % :
#echo as -o $# $^; touch $# # echo and touch are for testing. Remove these.
${OBJDIR} :
mkdir $#
.PHONY: all
Output:
$ make
make: Entering directory '/home/max/tmp'
mkdir -p `dirname Src/Application/main.c` && touch Src/Application/main.c # Create the source for testing. Remove this.
mkdir output
cc -c -o output/main.c Src/Application/main.c
mkdir -p `dirname Src/Core/data.c` && touch Src/Core/data.c # Create the source for testing. Remove this.
cc -c -o output/data.c Src/Core/data.c
mkdir -p `dirname Lib/routines.c` && touch Lib/routines.c # Create the source for testing. Remove this.
cc -c -o output/routines.c Lib/routines.c
mkdir -p `dirname Src/Application/startup.s` && touch Src/Application/startup.s # Create the source for testing. Remove this.
as -o output/startup.o Src/Application/startup.s
mkdir -p `dirname Lib/sqrt.s` && touch Lib/sqrt.s # Create the source for testing. Remove this.
as -o output/sqrt.o Lib/sqrt.s
make: Leaving directory '/home/max/tmp'
I have little experience in writing makefiles, so this is just an attempt. In my example I have C and C++ files in a few directories and build a program made of these files.
$ cat Makefile
.PHONY : clean all
CC=gcc
CXX=g++
CFILES = c/f.c c/g.c
CPPFILES = cpp/main.cpp
OUTPUT = ./output
SOURCE_DIRS := $(dir $(CFILES))
SOURCE_DIRS += $(dir $(CPPFILES))
VPATH = $(sort $(SOURCE_DIRS))
C_FILENAMES := $(notdir $(CFILES))
CPP_FILENAMES += $(notdir $(CPPFILES))
OBJ_FILES := $(patsubst %.c, $(OUTPUT)/%.o, $(C_FILENAMES) )
OBJ_FILES += $(patsubst %.cpp, $(OUTPUT)/%.o, $(CPP_FILENAMES) )
all : $(OUTPUT)/program
$(OUTPUT)/program : $(OBJ_FILES)
g++ -o $# $^
$(OUTPUT)/%.o : %.cpp
$(shell mkdir -p $(OUTPUT) )
$(CXX) $(CXXFLAGS) -c $< -o $#
$(OUTPUT)/%.o : %.c
$(shell mkdir -p $(OUTPUT) )
$(CC) $(CFLAGS) -c $< -o $#
clean:
rm -fr $(OUTPUT)
And this is an example of using my makefile:
$ make
gcc -c c/f.c -o output/f.o
gcc -c c/g.c -o output/g.o
g++ -c cpp/main.cpp -o output/main.o
g++ -o output/program output/f.o output/g.o output/main.o
The following method should do what you want: compile all of your specified source files and put the object files in the directory ./output automatically. Of course, you need to provide compiler options, proper libraries necessary for linking, and so on.
OBJDIR =./output
SRCDIR1 =./Src/Application
SRCDIR2 =./Src/Core
SRCDIR3 =./Lib
SRC1 =$(SRCDIR1)/main.c
SRC2 =$(SRCDIR2)/data.c
SRC3 =$(SRCDIR3)/routines.c
SRC4 =$(SRCDIR1)/startup.s
SRC5 =$(SRCDIR3)/sqrt.s
OBJ1 =$(patsubst $(SRCDIR1)/%.c,$(OBJDIR)/%.o,$(SRC1))
OBJ2 =$(patsubst $(SRCDIR2)/%.c,$(OBJDIR)/%.o,$(SRC2))
OBJ3 =$(patsubst $(SRCDIR3)/%.c,$(OBJDIR)/%.o,$(SRC3))
OBJ4 =$(patsubst $(SRCDIR1)/%.s,$(OBJDIR)/%.o,$(SRC4))
OBJ5 =$(patsubst $(SRCDIR3)/%.s,$(OBJDIR)/%.o,$(SRC5))
vpath %.c $(SRCDIR1): $(SRCDIR2): $(SRCDIR3)
vpath %.s $(SRCDIR1): $(SRCDIR3)
all: $(OBJ1) $(OBJ2) $(OBJ3) $(OBJ4) $(OBJ5)
cc $^ -o executable
$(OBJDIR)/%.o: %.c | $(OBJDIR)
cc -c $< -o $#
$(OBJDIR)/%.o: %.s | $(OBJDIR)
cc -c $< -o $#
$(OBJDIR):
mkdir -p $(OBJDIR)

Resources