Adrian
Adrian

Reputation: 33

How to generate dependency files when generating object files of c sources with make

I am implementing a build system using the GNU tools, GCC and make to compile multiple targets, link them together and create a final executable. All these support two platforms; the host environment and the embedded system MSP432.

I am taking an introductory course on embedded systems and doing an assignment that I am fighting with some days ago. I was trying by myself reading over the internet, also reading here in stackoverflow but I don’t get it yet, I am still a rookie on this, so I hope someone can explain me or giving me a hint about how to fix the issue

As said, the build system must support the two platforms so at first step, I focus on making sure all works for the host environment.

In the makefile I have created rules for the following targets:

  1. build - Generates executable file, object files, dependency files and map file
  2. %.o: %.c ... - Generates object files and its dependencies
  3. compile-all - Compiles all objects but do not link them
  4. %.i: %.c - Generates preprocessed output of C source files
  5. %.asm: %.C - Generates assembly output of C source files
  6. clean - Clean all generated files

The issue is when executing make build PLATFORM=HOST

Running the command, we get:

.../src$ sudo make build PLATFORM=HOST
gcc -Wall -Werror -g -std=c99 -DHOST -Wl,-O0,-Map=c1m2.map main.c memory.c -I../includes/common -o c1m2.out
make: *** No rule to make target 'main.o', needed by 'build'.  Stop.

I notice that the error comes because the line 132 were we have

%.o: %.c

This line is intended for disabling the built-in rule and using the user defined one that comes next line, but it is not doing it, so I try commenting this line and executing the build again and we get:

.../src$ sudo make build PLATFORM=HOST
gcc -Wall -Werror -g -std=c99 -DHOST -Wl,-O0,-Map=c1m2.map main.c memory.c -I../includes/common -o c1m2.out
gcc -Wall -Werror -g -std=c99 -DHOST -E  -c -o main.o main.c
main.c:23:22: fatal error: platform.h: No such file or directory
compilation terminated.
< builtin >: recipe for target 'main.o' failed
make: *** [main.o] Error 1

Now it says that it does not find “platform.h” despite it being indicated by the INCLUDES variable that contains the location of the header file. Also, it is using the built-in recipe for generating the object files and fails.

So I am stuck at this point, the idea is building the output executable, the map file, the object files and its dependencies files when executing “make build PLATFORM=HOST”.

At the beginning I wrote the build target just for generating the output, the map and object files and did work and then after doing the modifications for generating the dependency files I got lost with this error.

The other recipes for generating preproceessed files, assembly files and doing a clean were working ok.

You can clone the folder with all the needed files from: https://github.com/Fornaso/C1M2.git

Thank you all in advance.

Here is my Makefile:

#****************************************************************************** # Copyright (C) 2017 by Alex Fosdick - University of Colorado # # Redistribution, modification or use of this software in source or binary # forms is permitted as long as the files maintain this copyright. Users are # permitted to modify this and use it to learn about the field of embedded # software. Alex Fosdick and the University of Colorado are not liable for any # misuse of this material. # #****************************************************************************** # Modified on April 2020 by Adrián Fornaso #------------------------------------------------------------------------------ # Simple Makefile for multitarget build system # # Use: make [TARGET] [PLATFORM-OVERRIDES] # # Build Targets: # # build - Builds and links all source files and genereates: # # c1m2.map - Map file for the full build # *.d - Dependency Files for each source file # *.o - Individual object files # c1m2.out - Output Executable file # #<FILE>.i - Builds <FILE>.i preprocessed file. #<FILE>.asm - Builds <FILE>.i assembly file. #<FILE>.o - Builds <FILE>.o object file. #compile-all - Compile all objects but do NOT link them. #clean - Removes all generated files. # # Platform Overrides: Conditionally assign the appropriate compiler flags, # linker flags, and architecture flags. The target platform # must be provided at the command line with the make # command to set the platform you are compiling for. # # PLATFORM = MSP432 - The target embedded system will use # the cross compiler, arm-none-eabi-gcc. # PLATFORM = HOST - The host embedded system will use the # native compiler, gcc. # #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ #General Flags (Both Platforms) # # -Wall Enable All Warning Messages (CFLAGS) # -Werror Treats All Warnings as Errors(CFLAGS) # -g Generate Debugging Info in Executable (CFLAGS) # -O0 The level of optimization (-O0, -O1, -O2, -O3)) (LDFLAGS) # -std=c99 The C standard set (CFLAGS) # #------------------------------------------------------------------------------ #Target name BASENAME = c1m2 TARGET = $(BASENAME).out #General Flags COMMONCFLAGS = -Wall -Werror -g -std=c99 COMMONLDFLAGS = -Wl,-O0,-Map=$(BASENAME).map #No spaces after commas after -Wl option. CPPFLAGS = -E # -E flag makes the compiler stop in the preprocessed output #Compile time switches ifeq ($(PLATFORM), MSP432) INCLUDES = -I../includes/common \ -I../includes/msp432 \ -I../includes/CMSIS SOURCES = main.c \ memory.c \ interrupts_msp432p401r_gcc.c \ startup_msp432p401r_gcc.c \ system_msp432p401r.c LINKER_FILE = msp432p401r.lds CPU = cortex-m4 ARCH = armv7e-m SPECS = nosys.specs CC = arm-none-eabi-gcc LD = arm-none-eabi-ld LDFLAGS = $(COMMONLDFLAGS), -T=$(LINKER_FILE) CFLAGS = $(COMMONCFLAGS) -D$(PLATFORM) -mcpu=$(CPU) \ -march=$(ARCH) --specs=$(SPECS) OBJDUMP = arm-none-eabi-objdump endif ifeq ($(PLATFORM), HOST) INCLUDES = -I../includes/common SOURCES = main.c \ memory.c CC = gcc LD = ld LDFLAGS = $(COMMONLDFLAGS) CFLAGS = $(COMMONCFLAGS) -D$(PLATFORM) OBJDUMP = objdump endif #Listing object files: OBJECTS = $(SOURCES:.c=.o) # 1. -------------------------------------------------------------------------- # Complete build: c1m2.map - Map file for the full build # *.d - Dependency Files for each source file # *.o - Individual object files # c1m2.out - Output Executable file # LDFLAGS contains the flags for creating the *.map file .PHONY: build build: $(TARGET) $(OBJECTS) $(TARGET): $(CC) $(CFLAGS) $(LDFLAGS) $(SOURCES) $(INCLUDES) -o $@ # 2. -------------------------------------------------------------------------- # //// Generates the object files of all c-program implementation files and its # dependecies. /////////////////////////////////////////////////////////// #This implementation places dependency files into a subdirectory named .deps. DEPDIR := .deps DEPFLAGS = -MT $@ -MD -MP -MF $(DEPDIR)/$*.d # Delete the built-in rules for building object files from .c files, so that # our rule is used instead. #%.o: %.c # Our rule for building object files with its dependency %.o: %.c $(DEPDIR)/%.d | $(DEPDIR) $(CC) $(DEPFLAGS) -c $(CFLAGS) $(INCLUDES) -o $@ $^ # Declare a rule for creating the dependency directory if it doesn’t exist. $(DEPDIR): ; @mkdir -p $@ # Generate a list of all the dependency files that could exist. DEPFILES := $(SRCS:%.c=$(DEPDIR)/%.d) # Mention each dependency file as a target, so that make won’t fail if the file # doesn’t exist. $(DEPFILES): # 2 bis. ---------------------------------------------------------------------- # /// Generates the object file of all c-program implementation files. //////// #%.o: %.c # $(CC) -c $(CFLAGS) $(INCLUDES) -o $@ $^ # 3. -------------------------------------------------------------------------- # /// Compile all objects but do NOT link them. /////////////////////////////// .PHONY: compile-all compile-all: $(SOURCES) $(CC) -c $(CFLAGS) $(INCLUDES) $^ # 4. -------------------------------------------------------------------------- # /// Generates the preprocessed output of all c-program implementation files. %.i: %.c $(CC) $(CPPFLAGS) $(CFLAGS) $(INCLUDES) -o $@ $^ # 5. -------------------------------------------------------------------------- # /// Create assembler file of a C source. //////////////////////////////////// %.asm: %.c $(CC) -S $(CFLAGS) $(INCLUDES) $< -o $@ # -S flag tells the compiler just generate the assembly file # 6. -------------------------------------------------------------------------- # /// Removes all compiled objects, preprocessed outputs, assembly outputs, # executable files and build output files. //////////////////////////////// .PHONY: clean clean: rm -f $(OBJECTS) $(TARGET) $(BASENAME).map *.asm *.i rm -r .dep #End of file

Upvotes: 3

Views: 1843

Answers (1)

Armali
Armali

Reputation: 19395

There are two small mistakes which took me quite a while to see:

  • DEPFILES := $(SRCS:%.c=$(DEPDIR)/%.d) has to be DEPFILES := $(SOURCES:%.c=$(DEPDIR)/%.d) - otherwise DEPFILES is empty since SRCS is undefined.
  • In $(CC) $(DEPFLAGS) -c $(CFLAGS) $(INCLUDES) -o $@ $^ the $^ (all the prerequisites) expands to e. g. main.c .deps/main.d, so a not yet existing .deps/main.d is passed as an input file; we want $*.c instead of $^.

Another minor error is:

  • rm -r .dep should be rm -r .deps or rm -r $(DEPDIR).

Upvotes: 2

Related Questions