13

简而言之:我想编译来自不同目录的源代码,并将目标文件放入当前目录。

例如,我有文件:

test.c
../../lib1/boot.c
../../lib2/startup.c
../common/utils.c

(还有一些文件.s(程序集)和.cpp,但我希望这不重要)。

我想在当前目录中的所有目标文件:

test.o
boot.o
startup.o
utils.o

而且我不知道如何在我的makefile中编写这样的规则。

例如,

%o.: %.c

现在不起作用,因为make找不到要构建的规则boot.o../../lib1/boot.c它只能找到要构建../../lib1/boot.o的规则../../lib1/boot.c

我试着用这个:

%o.: %.c
 (my compilation line, for example "gcc -c $^ -o $@")
%o.: ../../lib1/%.c
 (my compilation line)
%o.: ../../lib2/%.c
 (my compilation line)
%o.: ../common/%.c
 (my compilation line)

它有效。但这显然不够通用,另外,今天有用户来找我说他的应用程序也有一些../../some_other_lib/common_things.c,因此我的makefile失败了。我浏览了我们的项目,发现了很多这样的案例,涉及到很多不同的目录。使用我的方法,我必须为每个这样的目录编写一个单独的规则,并使用相同的编译行。这对我来说似乎不太好。

所以我的问题是:如何制定一些通用的编译规则,将目标文件放在当前目录中(并检查),同时使用不同目录中的源进行操作?

谢谢你。

4

3 回答 3

9

可以从CSRC变量 with中提取目录$(dir ...),然后可以在vpath指令中使用此列表。

vpath %.c $(sort $(dir $(CSRC)))
vpath %.s $(sort $(dir $(SSRC)))
vpath %.cpp $(sort $(dir $(CPPSRC)))

(我已经加入了sort删除重复项的功能,但这不是绝对必要的。)

现在规则可以保持简单,make并将在目录列表中搜索源文件。

$(COBJ) := $(notdir $(CSRC))
$(SOBJ) := $(notdir $(SSRC))
$(CPPOBJ) := $(notdir $(CPPSRC))

.PHONY: all
all: $(EXECUTABLE)

$(EXECUTABLE): $(COBJ) $(SOBJ) $(CPPOBJ)
    ....

$(COBJ): %.o: %.c
    ...

$(SOBJ): %.o: %.s
    ...

$(CPPOBJ): %.o: %.cpp
    ...
于 2013-03-12T11:41:05.520 回答
7

尝试notdir像这样使用makefile函数:

%.o:    %.c
    gcc -c $< -o $(notdir $@)

$@必须等于完整路径 ex:../../lib2/startup.o广告notdir会将其中继到: startup.o

使用此规则,您将能够在当前目录中编译所有源代码。

实际上,您的示例是这样的:

.
└── common
    ├── lib1
    │   └── boot.c
    ├── lib2
    │   └── startup.c
    ├── test
    │   ├── Makefile
    │   └── test.c
    └── utils.c

我想我会变得更好:

.
├── common
│   ├── lib1
│   │   ├── Makefile
│   │   ├── obj
│   │   └── src
│   │       └── boot.c
│   ├── lib2
│   │   ├── Makefile
│   │   ├── obj
│   │   └── src
│   │       └── startup.c
│   ├── Makefile
│   ├── obj
│   ├── src
│   │   └── utils.c
│   └── test
│       ├── Makefile
│       ├── obj
│       └── src
│           └── test.c
└── Makefile

为此,您需要所有 Makefile 来调用子目录 Makefile。并且src/ objdirs 是您的源和对象之间的分隔符。

SRC := utils.c

OBJ := $(SRC:%.c=%.o)

NAME := project

SRC_D := src
OBJ_D := obj

SUBDIRS := lib1/ \
           lib2/ \
           test/

all: $(NAME) $(SUBDIRS)
    @for dir in $(SUBDIRS); \
    do \
    $(MAKE) -C $$dir; \
    done

$(NAME):    $(OBJ:%.o=$(OBJ_D)/%.o)

$(OBJ_D)/%.o :  $(SRC_D)/%.c
    gcc -c $< -o $@
于 2013-03-11T15:08:10.743 回答
2

好的,花了我一些时间,但最后我找到了解决方案(顺便在这个网站上使用了一些线程):

# Defining compilation rules in a way that object files will be produced in current directory, and not in the directory of source files:
all: <List of my targets>

define my_c_rule
$(subst .c,.o,$(notdir $(1))): $(1)
    $(CC) $(CFLAGS) $(CDEFINES) $$^ -o $$@
endef
$(foreach f, $(CSRC), $(eval $(call my_c_rule, $(f))))

$(CSRC)包含源文件列表及其路径。

只需要考虑到,如果早些时候我有这样的事情:

.c.o:
    $(CC) $(CFLAGS) $(CDEFINES) $^ -o $@

all: <List of my targets>

...现在我必须把all句子放在我在程序中描述的规则之上my_c_rule。如果我不这样做,make编译第一个源文件后停止。这是因为旧的“通配符”规则喜欢.c.o%.o: %.c不替换all为默认目标(甚至更早编写),但非通配符规则boot.o: ../../lib1/boot.c(上述宏的结果)确实会替换默认目标,以防它们更早编写。

于 2013-03-11T17:43:48.410 回答