【问题标题】:How I strip path of source file while writing a compilation rule in makefile?在makefile中编写编译规则时如​​何剥离源文件的路径?
【发布时间】:2013-03-11 14:10:29
【问题描述】:

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

例如,我有文件:

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 找不到从../../lib1/boot.c 构建boot.o 的规则,它只能找到从../../lib1/boot.c 构建../../lib1/boot.o 的规则。

我试过用这个:

%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失败了。我浏览了我们的项目,发现了很多这样的案例,涉及到很多不同的目录。使用我的方法,我必须为每个这样的目录编写一个单独的规则,并使用相同的编译行。这对我来说似乎不太好。

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

谢谢。

【问题讨论】:

  • 你考虑过使用VPATH吗?
  • Idelic,不幸的是,不,我没有尝试过。另请参阅此处的 eriktous 答案。

标签: makefile


【解决方案1】:

可以使用$(dir ...)CSRC 变量中提取目录,然后可以在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
    ...

【讨论】:

  • 我怀疑这可以通过这种方式完成,但我找不到确切的解决方案,因为我不明白vpath 是如何工作的。所以我选择了另一种解决方案。但是谢谢你,如果我的解决方案需要一些潜在的未来扩展任务,我会考虑你的解决方案。
  • notdir 正是我想要的。
【解决方案2】:

尝试像这样使用makefile函数notdir

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

$@ 必须等于完整路径,例如:../../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/obj 目录是源和对象之间的分隔符。

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

【讨论】:

  • Sirttas,我知道“notdir”,但我的问题不在编译行(即“gcc ...”)。它在规则中(即“%.o: %.c”)。而且我看不出您的示例如何解决问题。如果您能解释一下,我将不胜感激,因为如果我理解正确,在您的回复中,目标文件的结构将重复源文件的结构;您只需将“src”和“obj”作为上层目录添加到所有路径。
  • 我的例子是不正确的,你是真的,我认为你必须编译lib1lib2common的递归makefile。我会编辑。
  • 抱歉,我无法递归执行此操作,因为我不知道我的用户将使用哪些目录。我需要它来处理源目录,其数量、嵌套和名称事先都是未知的。带有完整路径的源文件列表作为make 的参数提供。
  • ok 所以只要使用notdir的规则,它会改变编译行的内容并为所有人制定一个规则。
  • 我不能,因为notdir 在编译行中起作用,但在规则行中不起作用。例如$(notdir %.o): %.c 对我不起作用。
【解决方案3】:

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

# 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 这样的非通配符规则(上述宏的结果)可以替换默认目标,以防它们之前编写。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-10-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多