【问题标题】:Makefile - wildcard and recipe buildMakefile - 通配符和配方构建
【发布时间】:2018-03-24 19:00:12
【问题描述】:

我有以下生成文件

# See gcc/clang manual to understand all flags
CFLAGS += -std=c99 # Define which version of the C standard to use
CFLAGS += -Wall # Enable the 'all' set of warnings
CFLAGS += -Werror # Treat all warnings as error
CFLAGS += -Wshadow # Warn when shadowing variables
CFLAGS += -Wextra # Enable additional warnings
CFLAGS += -O2 -D_FORTIFY_SOURCE=2 # Add canary code, i.e. detect buffer overflows
CFLAGS += -fstack-protector-all # Add canary code to detect stack smashing

# We have no libraries to link against except libc, but we want to keep
# the symbols for debugging
LDFLAGS = -rdynamic

# external libs
# par défaut les chemins classiques

LDFLAGS += -I$(HOME)/local/include
LDFLAGS += -L$(HOME)/local/lib

# Default compiler
CC=gcc

# folders
SOURCE_FOLDER=src
TESTS_FOLDER=tests
PAQUET_FOLDER=paquet
SEND_RECEIVE_DATA_FOLDER=sendAndReceiveData
CLIENT_FOLDER=client
SERVER_FOLDER=server

# folder of sources
PAQUET_FULL_PATH=$(SOURCE_FOLDER)/$(PAQUET_FOLDER)
SEND_RECEIVE_DATA_FULL_PATH=$(SOURCE_FOLDER)/$(SEND_RECEIVE_DATA_FOLDER)
CLIENT_FULL_PATH=$(SOURCE_FOLDER)/$(CLIENT_FOLDER)
SERVER_FULL_PATH=$(SOURCE_FOLDER)/$(SERVER_FOLDER)

# sources files
# On prend tout
PACKET_SOURCES = $(wildcard $(PAQUET_FULL_PATH)/*.c)
SEND_RECEIVE_DATA_SOURCES = $(wildcard $(SEND_RECEIVE_DATA_FULL_PATH)/*.c)
CLIENT_SOURCES = $(wildcard $(CLIENT_FULL_PATH)/*.c)
SERVER_SOURCES = $(wildcard $(SERVER_FULL_PATH)/*.c)

# objects

PACKET_OBJECTS=$(PACKET_SOURCES:.c=.o)
SEND_RECEIVE_DATA_OBJECTS=$(SEND_RECEIVE_DATA_SOURCES:.c=.o)
CLIENT_OBJECTS=$(CLIENT_SOURCES:.c=.o)
SERVER_OBJECTS=$(SERVER_SOURCES:.c=.o)

# another things

# Default target
all: clean server client

client: $(CLIENT_OBJECTS) $(PACKET_OBJECTS); \
        $(CC) $(CFLAGS) $(CLIENT_OBJECTS) $(LDFLAGS);

server: $(SERVER_OBJECTS) $(PACKET_OBJECTS); \
        $(CC) $(CFLAGS) $(SERVER_OBJECTS) $(LDFLAGS);

$(PACKET_OBJECTS): $(PACKET_OBJECTS); \
        $(CC) $(CFLAGS) -lz $(PACKET_OBJECT) $(LDFLAGS);

tests: $(PACKET_OBJECTS) $(TESTS_OBJECTS); \
        $(CC) $(CFLAGS) -lcunit $(LDFLAGS);

.PHONY: clean

clean:
    @rm -f *.o

我收到了这条消息:

make: 循环 src/paquet/packet_implem.o _start': (.text+0x20): undefined reference tomain' collect2: 错误: ld 返回 1 退出状态 Makefile:71: recipe for 目标 'src/paquet/packet_implem.o' 失败 make: *** [src/paquet/packet_implem.o] 错误 1

我想要什么:

1) 构建依赖项 (PACKET_SOURCES) 和 (SEND_RECEIVE_DATA_SOURCES)
2) 使用步骤 1 中的依赖项构建客户端和服务器
3) 可执行的“客户端”将位于文件夹的根目录。

如何纠正我的错误?

【问题讨论】:

  • 除非CFLAGS包含参数g,否则不会有任何调试符号。如果使用gdb 调试器,则使用g 而不是ggdb
  • 关于:CC=gcc 1) 使用:= 而不是=,因此宏只被评估一次,而不是每次被引用。 2) 对 shell 函数使用宏的想法是准确指定要使用的程序名称的哪个实例。即使用:CC := /usr/bin/gcc
  • 此语句:“编译”配方中的LDFLAGS += -I$(HOME)/local/include is something you want at the end of the gcc` 命令。不在link 配方中。建议:INCLUDE += -I$(HOME)/local/include
  • all目标之前,应该有一行:.PHONY : all
  • 您(通常)不想在尝试构建时运行“干净”目标。所以这一行:all: clean server client 最好写成:all: server client

标签: c linux gcc makefile


【解决方案1】:

问题出现在这条规则中:

$(PACKET_OBJECTS): $(PACKET_OBJECTS); \
        $(CC) $(CFLAGS) -lz $(PACKET_OBJECT) $(LDFLAGS);

这表示$(PACKET_OBJECTS) 扩展中的每个 目标都依赖于$(PACKET_OBJECTS) 扩展中的每个 目标。这提供了大量的依赖循环,特别是它为您提供了依赖于自身的目标文件的单元素循环。

事实上,这条规则在其他几个方面存在问题:

  • 配方实际上并没有创建任何规则的指定目标
  • 相反,它尝试将目标文件(见下文)链接到一个可执行文件中,并为结果分配默认名称(可能是a.out)。
  • 配方引用了一个未定义的变量$(PACKET_OBJECT)。我想这可能是$(PACKET_OBJECTS) 的拼写错误。相反,如果您指的是$(PACKET_OBJECTS) 中给出的当前目标,则拼写为$@
  • 不仅目标文件肯定不是每个都有自己作为先决条件,它们也没有彼此作为先决条件。每个对象的依赖项应该是它们对应的来源。
  • 如果您确实打算使用此规则来构建您的目标文件,那么配方不需要指定要链接的库(例如 -lz),并且可能不应该这样做。

很有可能您可以简单地完全删除该规则,从而获得更好的结果。您可能想将-lz 添加到您的LDFLAGS;否则它只会丢失。

另外,

  • 您可能想要移动 -I$(HOME)/local/include 选项:它不属于 LDFLAGS,并且不会在那里发挥其作用。如果您需要它,请将其放入您的 CPPFLAGS,或者可能是 CFLAGS 甚至 INCLUDES

  • 构建可执行目标的各种规则应提供指定要构建的目标名称的选项。您可以将其拼写为“-o $@”,

  • clean 目标作为all 的先决条件是非常规的。如果您在不指定目标的情况下运行make,它将始终从头开始重建所有内容。大多数人更愿意只在需要时这样做。

  • 您的all 目标没有构建名为“all”的目标,因此也应将其声明为.PHONY

很可能还有更多,但我会留给你解决。

【讨论】:

  • 感谢您的解释。问题是没有这个规则:我得到了这个 (src/paquet/packet_implem.c:6:26: fatal error: zlib.h: No such file or directory #include //crc32)
  • @jacquesy,有问题的规则对您没有任何用处。没有它,make 将尝试实际构建目标文件,这是您需要它来完成的,而以前它不会这样做。这揭示了一个不相关的错误。如果尚未安装 libz 及其开发组件,则需要安装它们。如果 zlib.h 不在默认的包含文件搜索路径中,那么您需要添加另一个 -I 选项来告诉编译器在哪里找到它。
  • 已经是这种情况,我现在使用: INCLUDES += -I$(HOME)/local/include INCLUDES += -L$(HOME)/local/lib 和 LDFLAGS += -lz LDFLAGS += -lcunit 。不明白为什么会阻塞?
  • @jacquesy, make 将打印它执行的每个命令。检查未能确保包含您需要的所有选项的选项。您甚至可以使用该行并尝试直接从命令行运行它的变体。恐怕我不能确切地告诉你你需要什么,因为这取决于你机器上的安装位置。
  • 没问题:它可以在另一台机器上运行^^。我想我会在 XD 之后销毁我的 xubuntu 和 Bash Linux for Windows。只是 Cunit 是复杂的
【解决方案2】:

使用 -c 生成对象而不尝试生成可执行文件,这样你就不需要 main

$(PACKET_OBJECTS): $(PACKET_OBJECTS); \
        $(CC) -c $(CFLAGS) -lz $(PACKET_OBJECT) $(LDFLAGS);

我认为您的 SERVER_OBJECTS 可能还需要这样的规则

.c.o:
        $(CC) -c $(CFLAGS) -lz $< -o $@

【讨论】:

  • 谢谢。您对丢弃的循环依赖以及纠正此问题的方法有任何想法吗?
  • 你的 PACKET_OBJECTS 规则对自身有依赖性。如果你使用 .c.o 规则,也许你根本不需要这条规则。
  • 从未听说过 .c.o 规则。我正在测试通配符,我已经后悔了 XD
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-11-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-12-10
相关资源
最近更新 更多