C语言 避免使用Makefile编译未更改的源代码

wribegjk  于 2023-04-11  发布在  其他
关注(0)|答案(4)|浏览(249)

我试着写了一个Makefile,它应该按要求将所有源代码编译成对象,然后构建输出二进制文件。
我已经尝试了几个例子,并找到了解决方案,但只有当我把所有的源代码在同一个文件夹。
但在这个项目中,文件分布在多个目录和子目录中。
修改后的Makefile如下所示

TARGET=analyser
CC=arm-none-eabi-gcc
OBJCPY=arm-none-eabi-objcopy
CFLAGS= -DNDEBUG -DCPU_MK64FN1M0VLL12 -DUSE_RTOS=1 -DPRINTF_ADVANCED_ENABLE=1 \
-DFRDM_K64F -DFREEDOM -DFSL_RTOS_FREE_RTOS -Os -Wall -fno-common \
-ffunction-sections -fdata-sections -ffreestanding -fno-builtin \
-mthumb -mapcs -std=gnu99 -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=fpv4-sp-d16 -MMD -MP \
--specs=nano.specs --specs=nosys.specs -Wall -fno-common -ffunction-sections \
-fdata-sections -ffreestanding -fno-builtin -mthumb -mapcs -Xlinker --gc-sections \
-Xlinker -static -Xlinker -z -Xlinker muldefs -Xlinker -Map=output.map -mcpu=cortex-m4 \
-mfloat-abi=hard -mfpu=fpv4-sp-d16 -Xlinker --defsym=__stack_size__=2048 -Xlinker \
--defsym=__heap_size__=25600 -Wall

LDSCRIPT=linker/MK64FN1M0xxx12_flash.ld

ASM_SOURCES := $(shell find . -name "*.S")
GCC_SOURCES := $(shell find . -name "*.c")

ASM_DIRNAME := $(shell dirname $(ASM_SOURCES))
GCC_DIRNAME := $(shell dirname $(GCC_SOURCES))

ASM_OBJECTS := $(patsubst $(ASM_DIRNAME)/%.S, $(ASM_DIRNAME)/%.o, $(ASM_SOURCES))
GCC_OBJECTS := $(patsubst $(GCC_DIRNAME)/%.c, $(GCC_DIRNAME)/%.o, $(GCC_SOURCES))

INCLUDES=\
    -Iapp/include \
    -ICMSIS \
    -Ifsl \
    -IFreeRTOS/include \
    -IFreeRTOS/include/private \
    -IFreeRTOS/src/portable/GCC/ARM_CM4F \
    -Iinclude \
    -Idrivers/uart \
    -Idrivers/serial_manager \
    -Idrivers/lists \
    -Ilwip/port \
    -Ilwip/src \
    -Ilwip/src/include \

all:
    $(CC) $(INCLUDES) $(GCC_SOURCES) $(ASM_SOURCES) $(CFLAGS) -T $(LDSCRIPT) -o $(TARGET).elf
    $(OBJCPY) $(TARGET).elf $(TARGET).bin -O binary

bad: $(GCC_OBJECTS) $(ASM_OBJECTS)                  \
    $(GCC) $(CFLAGS) -T $(LDSCRIPT) $^ -o $(TARGET) \

$(ASM_DIRNAME)/%.o:$(ASM_DIRNAME)/%.S               \
    $(GCC) $(INCLUDES) $(CFLAGS) -c $< -o $@        \

$(GCC_DIRNAME)/%.o:$(GCC_DIRNAME)/%.c               \
    $(GCC) $(INCLUDES) $(CFLAGS) -c $< -o $@        \

clean:
    rm $(TARGET).elf $(TARGET).bin $(TARGET).d output.map

当我执行“make”命令时,编译过程
但当我执行“使坏”我收到错误
对于有经验的编码Maven来说,这可能很简单,但我在Makefile世界中还是个新手。
有谁能帮我弄明白这个问题吗?
修改前的原始Makefile如下所示:

TARGET=analyser
CC=arm-none-eabi-gcc
OBJCPY=arm-none-eabi-objcopy
CFLAGS= -DNDEBUG -DCPU_MK64FN1M0VLL12 -DUSE_RTOS=1 -DPRINTF_ADVANCED_ENABLE=1 \
-DFRDM_K64F -DFREEDOM -DFSL_RTOS_FREE_RTOS -Os -Wall -fno-common \
-ffunction-sections -fdata-sections -ffreestanding -fno-builtin \
-mthumb -mapcs -std=gnu99 -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=fpv4-sp-d16 -MMD -MP \
--specs=nano.specs --specs=nosys.specs -Wall -fno-common -ffunction-sections \
-fdata-sections -ffreestanding -fno-builtin -mthumb -mapcs -Xlinker --gc-sections \
-Xlinker -static -Xlinker -z -Xlinker muldefs -Xlinker -Map=output.map -mcpu=cortex-m4 \
-mfloat-abi=hard -mfpu=fpv4-sp-d16 -Xlinker --defsym=__stack_size__=2048 -Xlinker \
--defsym=__heap_size__=25600 -Wall

LDSCRIPT=linker/MK64FN1M0xxx12_flash.ld
SRCS=\
    startup/startup_MK64F12.S \
    startup/startup_MK64F12.c \
    app/network_analyser.c \
    app/config/pin_mux.c \
    app/config/board.c \
    app/config/clock_config.c \
    fsl/fsl_sim.c \
    fsl/fsl_clock.c \
    fsl/fsl_enet.c \
    fsl/fsl_ftfx_cache.c \
    fsl/fsl_ftfx_controller.c \
    fsl/fsl_ftfx_flash.c \
    fsl/fsl_ftfx_flexnvm.c \
    fsl/fsl_gpio.c \
    fsl/fsl_debug_console.c \
    fsl/fsl_str.c \
    fsl/fsl_uart.c \
    fsl/fsl_smc.c \
    fsl/fsl_common.c \
    fsl/fsl_assert.c \
    fsl/fsl_sbrk.c \
    fsl/fsl_phy.c \
    fsl/fsl_dspi.c \
    fsl/fsl_dspi_freertos.c \
    fsl/fsl_uart_freertos.c \
    FreeRTOS/src/portable/MemMang/heap_3.c \
    FreeRTOS/src/event_groups.c \
    FreeRTOS/src/list.c \
    FreeRTOS/src/portable/GCC/ARM_CM4F/port.c \
    FreeRTOS/src/queue.c \
    FreeRTOS/src/stream_buffer.c \
    FreeRTOS/src/tasks.c \
    FreeRTOS/src/timers.c \
    drivers/uart/uart_adapter.c \
    drivers/serial_manager/serial_manager.c \
    drivers/serial_manager/serial_port_uart.c \
    drivers/lists/generic_list.c \
    lwip/port/enet_ethernetif_kinetis.c \
    lwip/port/enet_ethernetif.c \
    lwip/port/sys_arch.c \
    lwip/src/api/api_lib.c \
    lwip/src/api/api_msg.c \
    lwip/src/api/err.c \
    lwip/src/api/if_api.c \
    lwip/src/api/netbuf.c \
    lwip/src/api/netdb.c \
    lwip/src/api/netifapi.c \
    lwip/src/api/sockets.c \
    lwip/src/api/tcpip.c \
    lwip/src/core/altcp.c \
    lwip/src/core/altcp_alloc.c \
    lwip/src/core/altcp_tcp.c \
    lwip/src/core/def.c \
    lwip/src/core/dns.c \
    lwip/src/core/inet_chksum.c \
    lwip/src/core/init.c \
    lwip/src/core/ip.c \
    lwip/src/core/ipv4/autoip.c \
    lwip/src/core/ipv4/dhcp.c \
    lwip/src/core/ipv4/etharp.c \
    lwip/src/core/ipv4/icmp.c \
    lwip/src/core/ipv4/igmp.c \
    lwip/src/core/ipv4/ip4.c \
    lwip/src/core/ipv4/ip4_addr.c \
    lwip/src/core/ipv4/ip4_frag.c \
    lwip/src/core/ipv6/dhcp6.c \
    lwip/src/core/ipv6/ethip6.c \
    lwip/src/core/ipv6/icmp6.c \
    lwip/src/core/ipv6/inet6.c \
    lwip/src/core/ipv6/ip6.c \
    lwip/src/core/ipv6/ip6_addr.c \
    lwip/src/core/ipv6/ip6_frag.c \
    lwip/src/core/ipv6/mld6.c \
    lwip/src/core/ipv6/nd6.c \
    lwip/src/core/mem.c \
    lwip/src/core/memp.c \
    lwip/src/core/netif.c \
    lwip/src/core/pbuf.c \
    lwip/src/core/raw.c \
    lwip/src/core/stats.c \
    lwip/src/core/sys.c \
    lwip/src/core/tcp.c \
    lwip/src/core/tcp_in.c \
    lwip/src/core/tcp_out.c \
    lwip/src/core/timeouts.c \
    lwip/src/core/udp.c \
    lwip/src/netif/bridgeif.c \
    lwip/src/netif/bridgeif_fdb.c \
    lwip/src/netif/ethernet.c \
    lwip/src/netif/lowpan6.c \
    lwip/src/netif/lowpan6_ble.c \
    lwip/src/netif/lowpan6_common.c \
    lwip/src/netif/ppp/auth.c \
    lwip/src/netif/ppp/ccp.c \
    lwip/src/netif/ppp/chap-md5.c \
    lwip/src/netif/ppp/chap-new.c \
    lwip/src/netif/ppp/chap_ms.c \
    lwip/src/netif/ppp/demand.c \
    lwip/src/netif/ppp/eap.c \
    lwip/src/netif/ppp/eui64.c \
    lwip/src/netif/ppp/fsm.c \
    lwip/src/netif/ppp/ipcp.c \
    lwip/src/netif/ppp/ipv6cp.c \
    lwip/src/netif/ppp/lcp.c \
    lwip/src/netif/ppp/lwip_ecp.c \
    lwip/src/netif/ppp/magic.c \
    lwip/src/netif/ppp/mppe.c \
    lwip/src/netif/ppp/multilink.c \
    lwip/src/netif/ppp/ppp.c \
    lwip/src/netif/ppp/pppapi.c \
    lwip/src/netif/ppp/pppcrypt.c \
    lwip/src/netif/ppp/pppoe.c \
    lwip/src/netif/ppp/pppol2tp.c \
    lwip/src/netif/ppp/pppos.c \
    lwip/src/netif/ppp/upap.c \
    lwip/src/netif/ppp/utils.c \
    lwip/src/netif/ppp/vj.c \
    lwip/src/netif/slipif.c \
    lwip/src/netif/zepif.c \

INCLUDES=\
    -Iapp/include \
    -ICMSIS \
    -Ifsl \
    -IFreeRTOS/include \
    -IFreeRTOS/include/private \
    -IFreeRTOS/src/portable/GCC/ARM_CM4F \
    -Iinclude \
    -Idrivers/uart \
    -Idrivers/serial_manager \
    -Idrivers/lists \
    -Ilwip/port \
    -Ilwip/src \
    -Ilwip/src/include \

all:
    $(CC) $(INCLUDES) $(SRCS) $(CFLAGS) -T $(LDSCRIPT) -o $(TARGET).elf
    $(OBJCPY) $(TARGET).elf $(TARGET).bin -O binary

clean:
    rm $(TARGET).elf $(TARGET).bin $(TARGET).d output.map
xe55xuns

xe55xuns1#

1.在原始Makefile中,对象文件列表是使用patsubst生成的。相反,您可以使用:=赋值运算符直接将源文件扩展名替换为对象文件扩展名。
1.对对象文件使用模式规则。不要使用$(ASM_DIRNAME)/%.o:$(ASM_DIRNAME)/%.S$(GCC_DIRNAME)/%.o:$(GCC_DIRNAME)/%.c规则,这会导致“混合隐式和普通规则”错误,而应该使用%.o: %.S%.o: %.c等模式规则。这将匹配任何.S.c文件,并生成相应的.o文件。
1.您应该为构建TARGET.elfTARGET.bin文件创建单独的规则。TARGET.elf规则应该依赖于$(GCC_OBJECTS)$(ASM_OBJECTS),并使用$(CC)命令编译和链接它们。TARGET.bin规则应该依赖于TARGET.elf文件,并使用$(OBJCPY)命令生成二进制文件。
1.此外,clean规则还应该删除生成的目标文件。使用rm -f命令,然后使用目标文件列表$(ASM_OBJECTS)$(GCC_OBJECTS)

mf98qq94

mf98qq942#

为什么要用find来替换那个漂亮的源代码列表呢?把列表拿回来!
特别是为什么find用于 * 确切的一个 * 汇编源代码?当然,将其从C源文件中分离出来可能是一个很好的举措,但我认为find没有任何优势。
这...

ASM_DIRNAME := $(shell dirname $(ASM_SOURCES))

...没有用(见下文)。
这...

GCC_DIRNAME := $(shell dirname $(GCC_SOURCES))

...不仅没有用,而且肯定会有问题。因为你有多个C源代码,这个变量将得到一个完整的目录名列表,而不仅仅是一个。还有一堆重复的目录名,尽管这部分在实践中不会给你带来问题。
这些

ASM_OBJECTS := $(patsubst $(ASM_DIRNAME)/%.S, $(ASM_DIRNAME)/%.o, $(ASM_SOURCES))
GCC_OBJECTS := $(patsubst $(GCC_DIRNAME)/%.c, $(GCC_DIRNAME)/%.o, $(GCC_SOURCES))

...使$(patsubst)的使用变得轻浮和轻微错误。轻微错误是因为逗号后的空格是数据,而不是分隔符。轻浮,因为普通的替换引用更清晰,更短,更常规,而且更便于移植,只需将一个文件扩展名转换为另一个。显式地表达目录名而不是只将它们包含在%中也有些轻浮。推荐:

ASM_OBJECTS := $(ASM_SOURCES:.S=.o)
GCC_OBJECTS := $(GCC_SOURCES:.c=.o)

您的all目标没有先决条件,它的配方构建了所有内容。我知道您从原始的makefile继承了这一点,但显然您没有意识到这是需要修改以实现目标的首要内容。如果您想使用all目标,请单击此处。(这是一个常见的约定,但不是必需的),那么它应该将所有顶级构建输出作为先决条件,而不是配方。
尽可能地,每个配方都应该构建配方的目标,而不是其他后果。根据这个原则,你应该有单独的规则来构建$(TARGET).bin$(TARGET).elf
每个规则都应该表达其目标的所有先决条件,因此$(TARGET).bin的规则应该将$(TARGET).elf作为先决条件,而$(TARGET).elf的规则应该将所有对象文件以及链接器脚本作为先决条件。
但当我执行“使坏”我收到错误
这是因为该规则中的尾随反斜杠使其无效。您可以通过在最后一个先决条件和第一个反斜杠之间放置分号(;)来修复它,但您应该通过删除反斜杠并确保配方缩进文字制表符(而不是空格)来修复它。
你也应该对你的两个模式规则应用同样的修正,因为如果你正确地进行了前面描述的一些修改,那么这些规则实际上就会被使用。$(ASM_DIRNAME)变量。您不需要这些,因为模式词干将很好地匹配目录。您不需要前者,因为它使你的规则沿着这些路线:

dir1 dir1 dir2 dir3/%.o: dir1 dir1 dir2 dir3/%.c
        # ... recipe ...

这样做会有各种各样的问题,尤其是它只匹配你想要构建的一些.o文件。更简单的规则会做得更好,比如:

%.o: %.S
        $(GCC) $(INCLUDES) $(CFLAGS) -c $< -o $@

%.o: %.c
        $(GCC) $(INCLUDES) $(CFLAGS) -c $< -o $@

如果您将INCLUDES重命名为CPPFLAGS,那么您甚至可以完全丢弃后一个规则,而支持用于编译.c文件的内置规则。
此外,clean目标并不清理目标文件,如果目标的规则实际上并不构建具有目标名称的文件,则应该将目标声明为phony。
总的来说,我的建议是这样的:

TARGET = analyser
CC = arm-none-eabi-gcc
OBJCPY = arm-none-eabi-objcopy
CFLAGS = # flags ...

LDSCRIPT = linker/MK64FN1M0xxx12_flash.ld
ASM_SOURCES = startup/startup_MK64F12.S
GCC_SOURCES = \
  # long/list/of/sources.c ... \
  # last/source.c

ASM_OBJECTS = $(ASM_SOURCES:.S=.o)
GCC_OBJECTS = $(GCC_SOURCES:.c=.o)

CPPFLAGS = \
  # -Ithe/includes ... \
  # -Ilast/include

all: $(TARGET).bin

$(TARGET).bin: $(TARGET).elf
        $(OBJCPY) $< $@ -O binary

$(TARGET).elf: $(GCC_OBJECTS) $(ASM_OBJECTS)
        $(CC) -o $@ $(CFLAGS) -T $(LDSCRIPT) $^

%.o: %.S
        $(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $<

clean:
    rm -f $(TARGET).elf $(TARGET).bin $(TARGET).d output.map $(GCC_OBJECTS) $(ASM_OBJECTS)

.PHONY: all clean
4bbkushb

4bbkushb3#

你的all:规则说“每次编译所有内容”:

all:
    $(CC) $(INCLUDES) $(GCC_SOURCES) $(ASM_SOURCES) $(CFLAGS) -T $(LDSCRIPT) -o $(TARGET).elf
    $(OBJCPY) $(TARGET).elf $(TARGET).bin -O binary

如果文件all不存在,那么在所有源代码上运行C编译器并创建可执行文件(但不包括文件all)。如果文件all存在,则什么也不会发生-all不依赖于任何东西。
如果你不想编译所有的东西,你需要告诉make可执行文件依赖于目标文件,并链接目标文件。
我觉得你需要更像这样的东西:

PROGRAM = $(TARGET).elf

all: $(PROGRAM)

$(PROGRAM): $(GCC_OBJECTS) $(ASM_OBJECTS)
    $(CC) $(INCLUDES) $(GCC_OBJECTS) $(ASM_OBJECTS) $(CFLAGS) -T $(LDSCRIPT) -o $(PROGRAM)
    $(OBJCPY) $(PROGRAM) $(TARGET).bin -O binary

你可能需要做一些工作来构建目标文件,但是这里说“all依赖于程序,程序依赖于目标文件,当目标文件都是最新的,你就像这样构建程序”。你可能想指定.PHONY: all,因为它不是一个“真正的”目标-它不是你创建的文件。
你应该使用宏来重复像PROGRAM = $(TARGET).elf这样的元素(可能还有$(TARGET).bin)。如果你必须不止一次地写出它,它可能应该是一个宏。

cczfrluj

cczfrluj4#

感谢大家参与这项任务
主要的问题不仅是语法,还有“Sublime 3”软件生成的TAB符号
我已经将我的工作区更改为“vscode”,现在我没有任何不需要的TAB的问题
Makefile正在工作

TARGET=analyser
CC=arm-none-eabi-gcc
OBJCPY=arm-none-eabi-objcopy
CFLAGS= -DNDEBUG -DCPU_MK64FN1M0VLL12 -DUSE_RTOS=1 -DPRINTF_ADVANCED_ENABLE=1 \
-DFRDM_K64F -DFREEDOM -DFSL_RTOS_FREE_RTOS -Os -Wall -fno-common \
-ffunction-sections -fdata-sections -ffreestanding -fno-builtin \
-mthumb -mapcs -std=gnu99 -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=fpv4-sp-d16 -MMD -MP \
--specs=nano.specs --specs=nosys.specs -Wall -fno-common -ffunction-sections \
-fdata-sections -ffreestanding -fno-builtin -mthumb -mapcs -Xlinker --gc-sections \
-Xlinker -static -Xlinker -z -Xlinker muldefs -Xlinker -Map=output.map -mcpu=cortex-m4 \
-mfloat-abi=hard -mfpu=fpv4-sp-d16 -Xlinker --defsym=__stack_size__=2048 -Xlinker \
--defsym=__heap_size__=25600 -Wall

LDSCRIPT=linker/MK64FN1M0xxx12_flash.ld

ASM_SOURCES = startup/startup_MK64F12.S
GCC_SOURCES = $(shell find . -name "*.c")

INCLUDES=\
    -Iapp \
    -ICMSIS \
    -Ifsl \
    -IFreeRTOS/include \
    -IFreeRTOS/include/private \
    -IFreeRTOS/src/portable/GCC/ARM_CM4F \
    -Iinclude \
    -Idrivers/uart \
    -Idrivers/serial_manager \
    -Idrivers/lists \
    -Ilwip/port \
    -Ilwip/src \
    -Ilwip/src/include \

GCC_OBJECTS = $(GCC_SOURCES:.c=.o)
DEP_OBJECTS = $(GCC_SOURCES:.c=.d)

all: $(TARGET).bin

$(TARGET).bin: $(TARGET).elf
    $(OBJCPY) $< $@ -O binary

$(TARGET).elf: $(GCC_OBJECTS)
    $(CC) -o $@ $(CFLAGS) $(ASM_SOURCES) -T $(LDSCRIPT) $^

%.o: %.c
    $(CC) -I $(INCLUDES) $(CFLAGS) -c -o $@ $<

clean:
    rm -f $(TARGET).elf $(TARGET).bin $(TARGET).d output.map $(GCC_OBJECTS) $(DEP_OBJECTS)

.PHONY: all clean

非常感谢您的帮助!

相关问题