I have a problem with a Makefile that's using an auto-generated Makefile to compile .o files from auto-generated .c and .s (assembler) source files.
The context is embedded programming for STM32 microcontrollers, where a Makefile and hardware initialization source code is generated from a hardware configuration file (.ioc file).
Suppose, we have the following directory structure:
.
├── archive
│ ├── a
│ │ └── a.c
│ ├── b
│ │ └── b.s
│ └── Makefile
├── Makefile
├── source
│ └── generate.sh
The files archive/a/a.c and archive/b/b.s may be empty here. My main ./Makefile looks like this:
.PHONY: default all clean generate objects
BUILD_DIR := build
SOURCE_DIR := source
OBJECTS := build/source/a/a.o build/source/b/b.o
default: all
all: objects
objects: $(OBJECTS)
$(BUILD_DIR)/%.o: %.c $(SOURCE_DIR)/Makefile
mkdir -p $(shell dirname $#)
$(MAKE) -C $(SOURCE_DIR) BUILD_DIR=../$(shell dirname $#) VPATH=../$(shell dirname $<) ../$#
$(BUILD_DIR)/%.o: %.s $(SOURCE_DIR)/Makefile
mkdir -p $(shell dirname $#)
$(MAKE) -C $(SOURCE_DIR) BUILD_DIR=../$(shell dirname $#) VPATH=../$(shell dirname $<) ../$#
$(SOURCE_DIR)/%.c :: generate
$(SOURCE_DIR)/%.s :: generate
$(SOURCE_DIR)/Makefile :: generate
generate: $(SOURCE_DIR)/generate.sh
cd $(SOURCE_DIR) ; bash generate.sh
clean:
$(RM) -r build $(SOURCE_DIR)/Makefile $(SOURCE_DIR)/a $(SOURCE_DIR)/b
The source/generate.sh looks like this:
cp -r ../archive/. ./
In reality, this process much more complicated and takes a few minutes.
Finally, the archive/Makefile looks like this:
BUILD_DIR := build
$(BUILD_DIR)/%.o: %.c
cp $^ $#
$(BUILD_DIR)/%.o: %.s
cp $^ $#
In reality, we use gcc instead of a simple cp to compile the C and ASM source files.
The auto-generated Makefile uses VPATH to find the required input file and is actually meant to compile everything in a flat build folder (which I don't want).
I have no control over the contents of the auto-generated Makefile! The source/generate.sh should be treated as a black-box that I have no influence on.
Therefore, the superordinate Makefile has to slightly abuse the auto-generated Makefile by overwriting the BUILD_DIR and VPATH accordingly.
Now, if I first run make generate and then, e.g. make build/source/a/a.o, everything works as planned:
First, the source directory is populated with the auto-generated stuff, and then build/source/a/a.o is "compiled" from source/a/a.c.
However, if I don't run make generate first, I get:
$ make build/source/a/a.o
make: *** No rule to make target 'build/source/a/a.o'. Stop.
My assumption was that the recipes would have been resolved in the following manner:
$(BUILD_DIR)/%.o: $(SOURCE_DIR)/%.c $(SOURCE_DIR)/Makefile
-> $(SOURCE_DIR)/%.c: generate
-> generate: $(SOURCE_DIR)/generate.sh
-> $(SOURCE_DIR)/Makefile: generate
-> generate: $(SOURCE_DIR)/generate.sh
After a bit of trial and error, I figured that I could somewhat fix this behaviour by defining the recipes as follows:
$(SOURCE_DIR)/%.c: generate
true
$(SOURCE_DIR)/%.s: generate
true
$(SOURCE_DIR)/Makefile: generate
true
This yields:
$ make build/source/a/a.o
cd source ; bash generate.sh
true
true
mkdir -p build/source/a
make -C source BUILD_DIR=../build/source/a VPATH=../source/a ../build/source/a/a.o
make[1]: Entering directory '/[...]/source'
cp ../source/a/a.c ../build/source/a/a.o
make[1]: Leaving directory '/[...]/source'
rm source/a/a.c
However, this leads to the generate step being run every time I run make build/source/a/a.o. Since the generate step is expensive in real life, this is not an option.
Also, source/a/a.c is treated as a temporary file that is meant to be deleted which I don't want.
How can I design my superordinate ./Makefile so that the generate step is being run automatically, but only if necessary?
I also need to be able to build non-auto-generated source files from the root directory.
A few more strange make behaviours:
Simply running make (i.e. make objects) with this Makefile yields
cd source ; bash generate.sh
true
true
mkdir -p build/source/a
make -C source BUILD_DIR=../build/source/a VPATH=../source/a ../build/source/a/a.o
make[1]: Entering directory '/[...]/source'
cp ../source/a/a.c ../build/source/a/a.o
make[1]: Leaving directory '/[...]/source'
true
mkdir -p build/source/b
make -C source BUILD_DIR=../build/source/b VPATH=../source/b ../build/source/b/b.o
make[1]: Entering directory '/[...]/source'
cp ../source/b/b.s ../build/source/b/b.o
make[1]: Leaving directory '/[...]/source'
rm source/a/a.c
Why does make remove source/a/a.c, but not source/b/b.c? For the record: I want neither of these files to be auto-removed.
An issue that I am sadly unable to reproduce with my example is that make basically outputs the following line at the start (adpated to the example):
make: Circular source/generate.sh.c <- generate dependency dropped.
Changing the recipe to the following
$(SOURCE_DIR)/%.c: generate
true
$(SOURCE_DIR)/%.s: generate
true
$(SOURCE_DIR)/Makefile: generate
yields:
$ make build/source/a/a.o
cd source ; bash generate.sh
true
cc -c -o source/Makefile.o source/Makefile.c
cc1: fatal error: source/Makefile.c: No such file or directory
compilation terminated.
make: *** [<builtin>: source/Makefile.o] Error 1
Why does make think that it needs to build a Makefile.o here?
Related
I am writing a matrix library in C for practice. To enforce good habits I am testing using the Unity Testing Framework, meaning I compile multiple test files and run them. Since I do not know how many test files I will end up with, I've been trying to write my Makefile such that any .c file in my tests/ directory will be found and used to make a corresponding executable.
Here is my project file structure:
├── bin
├── include
| ├── fml.h
├── src
| ├── fml.c
├── test-framework
| ├── unity.c
| ├── unity.h
| ├── unity_internals.h
├── tests
| ├── check_fml_at.c
| ├── check_fml_set_at.c
| ├── check_fml_mem_management.c
├── Makefile
├── Readme
And here is the Makefile I'm struggling with:
BIN_DIR = bin
SRC_DIR = src
TESTS_DIR = tests
FML = $(BIN_DIR)/fml.o
TEST_FRAMEWORK = $(BIN_DIR)/unity.o
CFLAGS = -Wall -Werror -g
CC = gcc
TESTS = $(patsubst %.c, %, $(patsubst $(TESTS_DIR)/%, \
$(BIN_DIR)/%, $(wildcard $(TESTS_DIR)/*.c)))
$(info $$var is [${TESTS}])
$(TESTS): $(BIN_DIR)/%: $(TESTS_DIR)/%.c $(FML) $(TEST_FRAMEWORK)
$(CC) $(CFLAGS) $^ -o $# -Iinclude -Itest-framework
$(FML): src/fml.c include/fml.h
$(CC) $(CFLAGS) -c $< -o $# -Iinclude
$(TEST_FRAMEWORK): test-framework/unity.c test-framework/unity.h test-framework/unity_internals.h
$(CC) $(CFLAGS) -c $< -o $#
memcheck:
valgrind $(TESTS)
check:
$(TESTS)
clean:
rm $(TESTS) $(OBJS)
Each test file needs the library (fml) and the test framework (unity).
The expected behavior when running make is for check_fml_at, check_fml_set_at, and check_fml_mem_management to appear as executables in the bin/ file, along with fml.o and unity.o libraries. I've checked and the TESTS variable correctly evaluates to check_fml_at, check_fml_set_at, and check_fml_mem_management. But for some reason, only the first test is built, and when I run make I only see check_fml_at along with fml.o and unity.o in the bin dir.
Why is the makefile behaving this way? And what can I do to ensure every file in TESTS is built so I can run every test?
Thank you for any help!
-- Tomas Dougan
There does not appear to be anything inherently wrong with your static pattern rule. The reason that you are getting only one test built when you run make without arguments is that make then chooses a default target, not a default rule. If the first rule in the makefile has multiple targets then the one that appears first is the default target.
If you want the default to be to build all the tests, then insert a new first rule that specifies all the tests as prerequisites:
.PHONY: all
all: $(TESTS)
Do note, by the way, that your memcheck and check recipes aren't going to work as you appear to intend when more than one test is designated by $(TESTS). Instead of running each test, you will run just the first, with the names of the others passed as command-line arguments. Perhaps you want something more like
.PHONY: memcheck check
memcheck:
for t in $(TESTS); do valgrind $$t; done
check:
for t in $(TESTS); do $$t; done
I ended up installing ceedling which uses ruby code to auto-generate test runs. Much better tool set for learning imo. I'm going to leave the question open in case anybody else has the same issue and someone else has the answer.
Hi StackOverflow family,
I'm currently in the process of reading through "Test-Driven Development for Embedded C" and am attempting to get Unity (unit tester) to work. I am able to compile some example code using gcc.
When I try to follow the make tutorial and run 'make' in my command line:
make: *** No rule to make target 'build/results/TestEx.txt', needed by 'test'. Stop.
I tried to follow the tree outline from throwtheswitch, and my personal tree is below (removed Unity's files for clarity):
.
├── build
│ ├── depends
│ ├── objs
│ └── results
├── makefile
├── src
│ ├── ex.c
│ └── ex.h
├── test
│ └── TestEx.c
├── testex
└── Unity
.
.
.
My makefile looks as such (Remember, it's copied):
ifeq ($(OS),Windows_NT)
ifeq ($(shell uname -s),) # not in a bash-like shell
CLEANUP = del /F /Q
MKDIR = mkdir
else # in a bash-like shell, like msys
CLEANUP = rm -f
MKDIR = mkdir -p
endif
TARGET_EXTENSION=.exe
else
CLEANUP = rm -f
MKDIR = mkdir -p
TARGET_EXTENSION=out
endif
.PHONY: clean
.PHONY: test
PATHU = Unity/src/
PATHS = src/
PATHT = test/
PATHB = build/
PATHD = build/depends/
PATHO = build/objs/
PATHR = build/results/
BUILD_PATHS = $(PATHB) $(PATHD) $(PATHO) $(PATHR)
SRCT = $(wildcard $(PATHT)*.c)
COMPILE=gcc -c
LINK=gcc
DEPEND=gcc -MM -MG -MF
CFLAGS=-I. -I$(PATHU) -I$(PATHS) -DTEST
RESULTS = $(patsubst $(PATHT)Test%.c,$(PATHR)Test%.txt,$(SRCT) )
PASSED = `grep -s PASS $(PATHR)*.txt`
FAIL = `grep -s FAIL $(PATHR)*.txt`
IGNORE = `grep -s IGNORE $(PATHR)*.txt`
test: $(BUILD_PATHS) $(RESULTS)
#echo "-----------------------\nIGNORES:\n-----------------------"
#echo `grep -s IGNORE $(PATHR)*.txt`
#echo "-----------------------\nFAILURES:\n-----------------------"
#echo `grep -s FAIL $(PATHR)*.txt`
#echo "-----------------------\nPASSED:\n-----------------------"
#echo "$(PASSED)"
#echo "\nDONE"
$(PATHR)%.txt: $(PATHB)%.$(TARGET_EXTENSION)
-./$< > $# 2>&1
$(PATHB)Test%.$(TARGET_EXTENSION): $(PATHO)Test%.o $(PATHO)%.o $(PATHU)unity.o #$(PATHD)Test%.d
$(LINK) -o $# $^
$(PATHO)%.o:: $(PATHT)%.c
$(COMPILE) $(CFLAGS) $< -o $#
$(PATHO)%.o:: $(PATHS)%.c
$(COMPILE) $(CFLAGS) $< -o $#
$(PATHO)%.o:: $(PATHU)%.c $(PATHU)%.h
$(COMPILE) $(CFLAGS) $< -o $#
$(PATHD)%.d:: $(PATHT)%.c
$(DEPEND) $# $<
$(PATHB):
$(MKDIR) $(PATHB)
$(PATHD):
$(MKDIR) $(PATHD)
$(PATHO):
$(MKDIR) $(PATHO)
$(PATHR):
$(MKDIR) $(PATHR)
clean:
$(CLEANUP) $(PATHO)*.o
$(CLEANUP) $(PATHB)*.$(TARGET_EXTENSION)
$(CLEANUP) $(PATHR)*.txt
.PRECIOUS: $(PATHB)Test%.$(TARGET_EXTENSION)
.PRECIOUS: $(PATHD)%.d
.PRECIOUS: $(PATHO)%.o
.PRECIOUS: $(PATHR)%.txt
Running make -d test (removed text to fit in body, but condensed to the important bits):
Trying pattern rule with stem 'TestEx'.
Trying implicit prerequisite 'build/TestEx'.
Looking for a rule with intermediate file 'build/TestEx'.
Avoiding implicit rule recursion.
Avoiding implicit rule recursion.
Trying pattern rule with stem 'TestEx'.
Trying implicit prerequisite 'build/TestEx,v'.
Trying pattern rule with stem 'TestEx'.
Trying implicit prerequisite 'build/RCS/TestEx,v'.
Trying pattern rule with stem 'TestEx'.
Trying implicit prerequisite 'build/RCS/TestEx'.
Trying pattern rule with stem 'TestEx'.
Trying implicit prerequisite 'build/s.TestEx'.
Trying pattern rule with stem 'TestEx'.
Trying implicit prerequisite 'build/SCCS/s.TestEx'.
No implicit rule found for 'build/results/TestEx.txt'.
Finished prerequisites of target file 'build/results/TestEx.txt'.
Must remake target 'build/results/TestEx.txt'.
make: *** No rule to make target 'build/results/TestEx.txt', needed by 'test'. Stop.
Ill re-open the thread because i wasn't able to find a solution to the same issue.
Are you using GNU make built for GNUWin32?
I found that GNUWin32 make uses Unix style filenames for the targets and dependencies but as I had edited makefile's PATHS to Windows style filenames to use the windows mkdir command then makefile wasn't able to find recipes.
You should edit your makefile if you are running it with GNU Makefile built for GNUWin32
Makefile rules paths should be like this:
test/build/objs/%.o: test/%.c
and mkdir command should be like this:
mkdir test\build\objs
This worked for me. Hopes it helps!
I added Unity as submodule, but did not clone it at the first place.
So, I revised ".github/workflows/makefile.yml" as the following:
- uses: actions/checkout#v3
with:
submodules: recursive
Then, it work!
I'm trying to write a Makefile that when I add some deps in my application I just have to change DEPS_NAME variable, but something is wrong and I can't figure out what. I know that this is not the only problem with this Makefile, I just started to study this technology.
This is my project structure
application/
├── deps/
│ ├── buffer/
│ │ ├── buffer.c
│ │ └── buffer.h
│ └── other/
│ ├── other.c
│ └── other.h
├── objs/
├── application.c
└── Makefile
This is my Makefile
CC = gcc
APP_NAME = application
OBJS_PATH = objs
DEPS_PATH = deps
DEPS_NAME = buffer other
DEPS = $(patsubst %,$(OBJS_PATH)/%.o,$(DEPS_NAME))
$(OBJS_PATH)/%.o: $(DEPS_PATH)/%/%.c
$(CC) -o $# -c $^
$(APP_NAME): $(DEPS)
$(CC) -o $# $#.c $^
all: $(APP_NAME)
This is the error when i type make:
make: *** No rule to make target `objs/buffer.o', needed by `application'. Stop.
until you have determined the root cause of the problem, suggest a separate target for each *.c to *.o compile step.
you want the 'all' target to be the first target in the makefile.
(that is what a 'all' target is for, so can call the makefile with:
make
without specifying a target in the make file
Note: 'all' is a phony target (produces no file named 'all')
so should be written similar to:
.PHONY: all
all : $(app_name) $(DEPS)
When make performs the compile step(s)
It needs to know how to find the header files.
and since the header files are scattered,
it may be advisable to either 1) list all the paths/*.h files in the compile rule (simple but can result in unneeded compiles) or 2) generate dependancy files.(difficult, but best as the file count grows) or 3) write a separate compile rule for each source file. (least flexable, but easy for those new to make)
the compile rule needs to have parameters indicating where to find the header files. (in gcc, use '-I./other/.' and '-I./buffer/.)
I have never seen multiple stem references. Looks like it badly confuses the parser.
On a bright side it seems that
$(OBJS_PATH)/%.o: $($(DEPS_PATH)/%/%.c)
does the trick. Don't ask me why.
Putting the object files in the same directory of the source files I solved the problem with this Makefile
CC = gcc
CFLAGS = -Ideps
APP_NAME = application
DEPS_SOURCE = $(wildcard deps/*/*.c)
DEPS_OBJECT = $(DEPS_SOURCE:.c=.o)
all: $(APP_NAME)
$(APP_NAME): $(DEPS_OBJECT)
$(CC) $#.c -o $# $(DEPS_OBJECT) $(CFLAGS)
%.o: %.c
$(CC) -o $# -c $^
clean:
rm -f $(DEPS_OBJECT) $(APP_NAME)
.PHONY: all clean
Whit this solution I don't have to change the Makefile when I add other deps in the project.
How can I make a simple makefile to generate the object files and output binary in a bin directory from any number of c source and header files in a src directory? In this example structure, main includes module_a.h and module_b.h. module_a.h and module_b.h each only include stdio.h.
I can manage the clean rule, but do not know how to automatically generate the .o files and dependencies.
├── bin
├── makefile
├── README.md
└── src
├── main.c
├── module_a.c
├── module_a.h
├── module_b.c
└── module_b.h
This is a simple Makefile that I use. I'm not a Makefile guru so most likely it can be improved. But it should give you something to start with. The only tricky bit is the dependency handling. I am aware that there are other ways to do this. The example shows one way - generate .d files for each object file which lists the dependencies for that object file. Then include all the .d files into the Makefile to pick up all those dependency rules.
BIN := bin/my_binary
# Include all C files in the src directory
SRCS := $(shell find src -name '*.c')
# List of object files to link
OBJS := $(patsubst %.c,%.o,$(SRCS))
# Link rule
$(BIN): $(OBJS)
$(CC) -o $# $(OBJS)
# Include Dependency files
-include $(OBJS:.o=.d)
# Builds object file and generates dependency
%.o: %.c
$(CC) -c $(CFLAGS) $*.c -o $*.o
$(CC) -MM $(CFLAGS) $*.c > $*.d
mv -f $*.d $*.d.tmp
sed -e 's|.*:|$*.o:|' < $*.d.tmp > $*.d
rm $*.d.tmp
Is it possible to get make to check for a file before linking?
I have a makefile system with a top level Makefile that calls into other subdirectories and issues make in them. The goal of my system is:
build the source
Stop the build on the parent directory and any current child directory if there are any compilation errors.
link the executables
Display an error during the linking stage if there is a failure due to a missing archive file. Note: Only the build at the current subdirectory level should show the error and bounce out, but the overall procedure should continue and move onto the next child directory.
Display an error during the linking stage if there is a failure due to an undefined symbol in an existing archive file
So right now I have my child Makefile doing a "if build fails, then parent fail, if link fails, then parent continue" sort of thing by:
#build the source code
$(CC) -o $# -c $<
#link the executable
-$(CC) $^ -o $# $(LIB) #the - allows the parent to continue even if this fails
And this works, however this will allow any link error to go through, I only want to allow the parent to continue if the archive $(LIB) doesn't exist.
To clarify: Given the following directory structure:
C
├── Makefile
└── childdir
├── a.c // This source file uses functions from idontexist.a
└── Makefile
The top level Makefile is:
.PHONEY: all
all:
#echo "Start the build!" # I want to see this always
$(MAKE) --directory=childdir # then if this works, or fails because the
# .a is missing
#echo "Do more stuff!" # then I want to see this
The Makefile at childdir/ is:
LIB=idontexist.a #This doesn't exist and that's fine
EXE=target
SRC=a.c
OBJS=$(patsubst %.c,%.o,$(SRC))
%.o : %.c
$(CC) -o $# -c $< #If this fails, I want the ENTIRE build to fail, that's
# good, I want that.
.PHONEY: all
all: $(EXE)
$(EXE):$(OBJS)
-$(CC) $^ -o $# $(LIB) #If this fails, because $(LIB) is missing
# I don't really care, if it's because we can't find
# some symbol AND the file DOES exist, that's a problem
You can use:
LIB = $(wildcard idontexist.a)
which will expand to the filename if it exists, or empty if it doesn't.