如何在makefile中编写编译规则时删除源文件的路径?

时间:2013-03-11 14:10:30

标签: makefile

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

例如,我有文件:

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的规则,它只能从{{1}找到构建../../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) ,因此我的makefile失败了。我查看了我们的项目,发现很多这样的案例涉及很多不同的目录。根据我的方法,我将不得不为每个这样的目录编写一个单独的规则,具有相同的编译行。这对我来说似乎不太好。

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

谢谢。

3 个答案:

答案 0 :(得分:5)

可以使用CSRC$(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
    ...

答案 1 :(得分:4)

尝试使用makefile函数notdir

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

$@必须等于ex的完整路径:../../lib2/startup.o ad 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 / obj dirs是源和对象之间的分隔。

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 $@

答案 2 :(得分: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 (上述宏的结果)确实替换了以前写入的默认目标。