Makefile递归包含目标文件列表

时间:2019-05-06 08:42:14

标签: c assembly makefile

我已经使用Linux内核已有一段时间了,我对适应在那里使用的Makefile结构很感兴趣。我想通过在每个子目录中创建一组Makefile来实现类似的目的,这些Makefile只包含我在构建项目时真正想要编译的文件的名称。在Linux内核中实现的一个典型的类似示例如下所示:

obj-y += file1.o
obj-y += file2.o
obj-y += file3.o
obj-y += file4.o
# ...
obj-d += somesubdir

现在,在构建项目时,我使用根目录中的Makefile将每个Makefile递归地包含在各个子目录中,并将其附加到obj-y变量的列表中。我目前的方法是定义一个递归函数,该函数处理包括Makefile在内的文件,并自动遍历每个子目录:

OBJS :=

objtree  := .
srctree  := .

# ...

define build_subdir
objtree := $$(objtree)/$(1)
srctree := $$(srctree)/$(1)

obj-y :=
obj-d :=

include $$(srctree)/Makefile

OBJS += $$(patsubst %,$$(objtree)/%,$$(obj-y))

$$(foreach subdir,$$(obj-d),$$(eval $$(call build_subdir,$$(subdir))))

srctree := $$(patsubst %/$(1),%,$$(srctree))
objtree := $$(patsubst %/$(1),%,$$(objtree))
endef

# ...

$(eval $(call build_subdir,src))
$(eval $(call build_subdir,src/subdir))

遍历所有子目录时,我将各个文件添加到OBJS变量中,然后使用该变量来编译该文件。 但是,OBJS变量仅包含目标文件(即目标)的名称,而不包含实际源文件的名称。这是有问题的,因为我项目中的源文件不仅由.c组成,还包括某些程序集文件(.S)。因此,我无法定义如下所示的配方:

define compile_file
$(1): $$(patsubst %.o,%.c,$(1))
        $(CC) $< -o $@ $(CFLAGS)
endef

就我而言,编译器始终是相同的,因此将$(CC)变量保持不变没有任何问题。 $(CFLAGS)变量也是如此。

有没有一种类似于Linux内核的方法?

这是我的Makefile的当前相关内容:

objtree  := .
srctree  := .

.PHONY: all
all: real-all

OBJS     :=

define build_subdir
objtree := $$(objtree)/$(1)
srctree := $$(srctree)/$(1)

obj-y :=
obj-d :=

include $$(srctree)/Makefile

OBJS += $$(patsubst %,$$(objtree)/%,$$(obj-y))

$$(foreach subdir,$$(obj-d),$$(eval $$(call build_subdir,$$(subdir))))

srctree := $$(patsubst %/$(1),%,$$(srctree))
objtree := $$(patsubst %/$(1),%,$$(objtree))
endef

# $(eval $(call build_subdir, src))
$(eval $(call build_subdir,src/arch/$(ARCH)))

define compile_file
$(1): $$(patsubst %.o,%.S,$(1))
    @echo "Compiling file $$< to file $$@"
endef

$(foreach file,$(OBJS),$(eval $(call compile_file,$(file))))

.PHONY: real-all
real-all: $(OBJS)
    @echo "real-all"

当前输出:

Compiling file src/arch/x86/a20.S to file src/arch/x86/a20.o
make: *** No rule to make target 'src/arch/x86/acpi.S', needed by 'src/arch/x86/acpi.o'.  Stop.

最后一行显然失败了,因为没有src/arch/x86/acpi.S文件。相反,实际文件为src/arch/x86/acpi.c。那就是我之前针对源文件问题而解决的问题,可以是.c.S文件。

1 个答案:

答案 0 :(得分:0)

我通过以下方式使用$(wildcard)函数解决了这个问题:

define compile_file

srcbase := $$(basename $(1))
srcfile := $$(wildcard $$(srcbase).*)

$(1): $$(srcfile)
    @echo "[ CC ] $$@"
endef

由于$(1)包含文件的路径,因此srcfile通过使用$(wildcard $$(basename $(1)).*)查找文件来匹配文件。

现在,我可以轻松地编译所有文件了。