用make生成c源的目标文件时如何生成依赖文件
Posted
技术标签:
【中文标题】用make生成c源的目标文件时如何生成依赖文件【英文标题】:How to generate dependency files when generating object files of c sources with make 【发布时间】:2020-04-16 22:08:19 【问题描述】:我正在使用 GNU 工具、GCC 和 make 来实现一个构建系统来编译多个目标,将它们链接在一起并创建一个最终的可执行文件。所有这些都支持两个平台;主机环境和嵌入式系统MSP432。
我正在参加关于嵌入式系统的入门课程,并完成了几天前我正在努力解决的任务。我正在尝试自己通过互联网阅读,也在***中阅读,但我还不明白,我仍然是这方面的新手,所以我希望有人能解释我或给我关于如何解决问题的提示
如前所述,构建系统必须支持这两个平台,所以第一步,我专注于确保一切都适用于主机环境。
在 makefile 中,我为以下目标创建了规则:
build
- 生成可执行文件、目标文件、依赖文件和映射文件
%.o: %.c
... - 生成目标文件及其依赖项
compile-all
- 编译所有对象但不链接它们
%.i: %.c
- 生成 C 源文件的预处理输出
%.asm: %.C
- 生成 C 源文件的汇编输出
clean
- 清理所有生成的文件
问题是在执行make build PLATFORM=HOST
时
运行命令,我们得到:
.../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.
我注意到错误的出现是因为我们有第 132 行
%.o: %.c
这一行旨在禁用内置规则并使用下一行的用户定义的规则,但它没有这样做,所以我尝试评论这一行并再次执行构建,我们得到:
.../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
现在它说它没有找到“platform.h”,尽管它由包含头文件位置的 INCLUDES 变量指示。此外,它使用内置配方生成目标文件并失败。
所以我被困在这一点上,想法是在执行“make build PLATFORM=HOST”时构建输出可执行文件、映射文件、目标文件及其依赖文件。
一开始我编写构建目标只是为了生成输出、映射和目标文件并且确实工作了,然后在为生成依赖文件进行修改之后我迷失了这个错误。
用于生成预处理文件、汇编文件和进行清理的其他方法工作正常。
您可以从以下位置克隆包含所有需要文件的文件夹:https://github.com/Fornaso/C1M2.git
提前谢谢大家。
这是我的 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
【问题讨论】:
关于:COMMONCFLAGS = -Wall -Werror -g -std=c99
最好写成:COMMONCFLAGS := -Wall -Wextra -Wconverson -pedantic -Werror -g -std=c99
注意启用警告的额外选项和:=
的使用,因此宏只评估一次。
关于:CC = gcc LD = ld LDFLAGS = $(COMMONLDFLAGS) CFLAGS = $(COMMONCFLAGS) -D$(PLATFORM) OBJDUMP = objdump
强烈建议使用:=
而不是=
,这样宏只会被评估一次
链接器从左到右处理参数,因此目标文件列表应该在任何库文件引用之前
关于:%.o: %.c $(DEPDIR)/%.d | $(DEPDIR) $(CC) $(DEPFLAGS) -c $(CFLAGS) $(INCLUDES) -o $@ $^
强烈建议将其分为两条规则,一条用于创建依赖文件,一条用于执行编译
我会做这些修改。现在我开始检查嵌入式 MSP432 平台的构建,我正在寻找如何使用链接器文件,因为 make 文件和源文件在 src 文件夹内,而链接器文件在 src 文件夹之外,并且构建没有找到它.
【参考方案1】:
有两个小错误我花了很长时间才看到:
DEPFILES := $(SRCS:%.c=$(DEPDIR)/%.d)
必须是 DEPFILES := $(SOURCES:%.c=$(DEPDIR)/%.d)
- 否则 DEPFILES
为空,因为 SRCS
未定义。
在$(CC) $(DEPFLAGS) -c $(CFLAGS) $(INCLUDES) -o $@ $^
中,$^
(所有先决条件)扩展为 e。 G。 main.c .deps/main.d
,因此尚不存在的.deps/main.d
作为输入文件传递;我们想要$*.c
而不是$^
。
另一个小错误是:
rm -r .dep
应该是 rm -r .deps
或 rm -r $(DEPDIR)
。
【讨论】:
比$*.c
更传统的是使用$<
,它扩展到第一个先决条件。
感谢 Armali 和 MadScienist,我使用您所说的内容更正了代码,现在它正在根据需要在 .deps 文件夹中生成依赖项。执行构建我们得到所有生成的文件。现在,当我使用 make 运行 clean target 时,我注意到了一些事情;我们无法删除生成的文件,列出它们我们看到它们以-rw-r--r--
权限出现,但是,root 作为所有者名称,root 作为所有者组。您是否建议更改在 clean target recipe 中添加命令的权限,或者是否有更简洁的方法,例如生成具有用户权限的文件?
我注意到为什么,干净的目标有这种行为;这是因为我执行的是make clean
而不是make clean PLATFORM=HOST
。如果编写代码的方式,我们需要指定平台的开关工作,导致在平台开关内部定义的变量和标志,即 SOURCES 变量的情况,然后用于列出变量 OBJECTS 内的对象。不是clean没有删除文件导致权限,而是因为命令rm -f $(OBJECTS) ... was having OBJECTS variable empty when doing
make clean`。以上是关于用make生成c源的目标文件时如何生成依赖文件的主要内容,如果未能解决你的问题,请参考以下文章