如何在 makefile 中传递规则模式来过滤依赖项?
How can I pass a rule pattern in a makefile to filter dependencies?
我的项目中有一个规则,就是从源代码生成库。我已经拥有将 %.c
编译为 %.o
的功能,但我将我的库代码拆分为多个以相同前缀开头的源文件。我在同一个目录中有两个单独的库代码,但它们的源文件有不同的前缀,这就是为什么我试图为两个(或更多)库构建一个规则。但是我无法将代码库的前缀传递给依赖项来过滤所需的目标文件。
我的规则是在我的 Makefile 中:
# ...
BINDIR = bin
LIBDIR = lib
# ...
# These are all the libraries source files.
LIB_SOURCES = $(wildcard $(LIBDIR)/*.c)
# These are all the libraries "main" source files.
LIB_SRCS = $(filter-out $(wildcard $(LIBDIR)/*_*.c), $(LIB_SOURCES))
# These are all the source files to which I have exported some code from the "main" library source file.
LIB_CODE = $(filter-out $(LIB_SRCS), $(LIB_SOURCES))
# These are all the objects produced by compiling the c source files.
LIB_OBJS = $(patsubst %.c, %.o, $(LIB_CODE))
# ...
# These are all the libraries produced.
LIBS = $(patsubst $(LIBDIR)/%.c,$(BINDIR)/lib%.so,$(LIB_SRCS))
# ...
.PHONY: libs
# ...
%/:
mkdir -p $@
libs: $(BINDIR)/ $(LIBS)
.SECONDEXPANSION:
$(BINDIR)/lib%.so: $(LIBDIR)/%.o $(filter $(LIBDIR)/$*_%.o, $(LIB_OBJS))
@echo $(CC) $(CFLAGS) $(CLNKERFLAGS) -o $@ $^
$(LIBDIR)/%.o: $(LIBDIR)/%.c $(LIBDIR)/%.h
@echo $(CC) $(CFLAGS) -o $@ -c $<
目前我只打印了(不完整的)命令,但它仍然没有得到包含所有所需对象的正确输出。
在目录 lib
中,我有以下文件:
$ tree lib
lib
├── app.c
├── app_db.c
├── app_db.h
├── app.h
├── app_logic.c
├── app_logic.h
├── app_net.c
├── app_net.h
├── server.c
├── server.h
├── server_queue.c
└── server_queue.h
但它从未正确构建依赖关系。
$ make libs
gcc -Wpedantic -O3 -o lib/app.o -c lib/app.c
gcc -Wpedantic -O3 -o bin/libapp.so lib/app.o
gcc -Wpedantic -O3 -o lib/server.o -c lib/server.c
gcc -Wpedantic -O3 -o bin/libserver.so lib/server.o
我已经阅读了以下问题 How to use pattern-dependent variables in dependencies in make pattern rules,其中有一个提示,我认为它会帮助我,但它没有。
知道如何实现吗?
编辑 1:
我希望最后一条命令的输出是:
$ make libs
gcc -Wpedantic -O3 -o lib/app.o -c lib/app.c
gcc -Wpedantic -O3 -o lib/app_db.o -c lib/app_db.c
gcc -Wpedantic -O3 -o lib/app_logic.o -c lib/app_logic.c
gcc -Wpedantic -O3 -o lib/app_net.o -c lib/app_net.c
gcc -Wpedantic -O3 -o bin/libapp.so lib/app.o lib/app_db.o lib/app_logic.o lib/app_net.o
gcc -Wpedantic -O3 -o lib/server.o -c lib/server.c
gcc -Wpedantic -O3 -o lib/server_queue.o -c lib/server_queue.c
gcc -Wpedantic -O3 -o bin/libserver.so lib/server.o lib/server_queue.o
感谢您的关注。
Make 对通配符不是很灵巧,您的方法要求它在一行中处理两个不同的通配符。如果这可能的话,那将是一件可怕的事情。我建议采用不同的方法。
首先,你的变量是错误的。不清楚您的意图是什么,但以下是符合您期望结果的:
LIB_SOURCES := $(wildcard $(LIBDIR)/*.c)
LIB_OBJS := $(patsubst %.c,%.o,$(LIB_SOURCES))
现在,您希望您的 makefile 表现得好像它具有以下两个规则:
$(BINDIR)/libapp.so: $(filter $(LIBDIR)/app%,$(NEW_LIB_OBJS))
@echo build $@ somehow from $^
$(BINDIR)/libserver.so: $(filter $(LIBDIR)/server%,$(NEW_LIB_OBJS))
@echo build $@ somehow from $^
但您希望 Make 在 运行 时构建它们,而不是在 makefile 中拼出它们。所以我们将使用 "canned recipe":
define librule
$(BINDIR)/lib$(1).so: $(filter $(LIBDIR)/$(1)%,$(LIB_OBJS))
@echo building $$@ somehow from $$^
endef
$(eval $(call librule,app))
$(eval $(call librule,server))
然后我们可以让 Make 从 LIBS
:
中提取它们,而不是在 makefile 中写最后两行,指定 app
和 server
LIB_NAMES := $(patsubst $(BINDIR)/lib%.so,%,$(LIBS))
$(foreach libname,$(LIB_NAMES),$(eval $(call librule,$(libname))))
您添加了 .SECONDEXPANSION
,但您没有在先决条件列表中转义任何内容,因此它实际上没有执行任何操作:
$(BINDIR)/lib%.so: $(LIBDIR)/%.o $(filter $(LIBDIR)/$*_%.o, $(LIB_OBJS))
None个variables/functions被转义了,所以这里的一切都是在初始read-in时展开的,所以二次展开没什么可做的。
二次扩展功能由两部分组成:首先,您使用特殊目标启用它。其次,转义要延迟扩展的变量 and/or 函数。所以这可能是:
getobjs = $(filter $(LIBDIR)/$*_%.o, $(LIB_OBJS))
$(BINDIR)/lib%.so: $(LIBDIR)/%.o $$(getobjs)
@echo $(CC) $(CFLAGS) $(CLNKERFLAGS) -o $@ $^
请注意我们如何将 $(getobjs)
转义为 $$(getobjs)
,以便该变量在第二次传递之前不会展开。
我的项目中有一个规则,就是从源代码生成库。我已经拥有将 %.c
编译为 %.o
的功能,但我将我的库代码拆分为多个以相同前缀开头的源文件。我在同一个目录中有两个单独的库代码,但它们的源文件有不同的前缀,这就是为什么我试图为两个(或更多)库构建一个规则。但是我无法将代码库的前缀传递给依赖项来过滤所需的目标文件。
我的规则是在我的 Makefile 中:
# ...
BINDIR = bin
LIBDIR = lib
# ...
# These are all the libraries source files.
LIB_SOURCES = $(wildcard $(LIBDIR)/*.c)
# These are all the libraries "main" source files.
LIB_SRCS = $(filter-out $(wildcard $(LIBDIR)/*_*.c), $(LIB_SOURCES))
# These are all the source files to which I have exported some code from the "main" library source file.
LIB_CODE = $(filter-out $(LIB_SRCS), $(LIB_SOURCES))
# These are all the objects produced by compiling the c source files.
LIB_OBJS = $(patsubst %.c, %.o, $(LIB_CODE))
# ...
# These are all the libraries produced.
LIBS = $(patsubst $(LIBDIR)/%.c,$(BINDIR)/lib%.so,$(LIB_SRCS))
# ...
.PHONY: libs
# ...
%/:
mkdir -p $@
libs: $(BINDIR)/ $(LIBS)
.SECONDEXPANSION:
$(BINDIR)/lib%.so: $(LIBDIR)/%.o $(filter $(LIBDIR)/$*_%.o, $(LIB_OBJS))
@echo $(CC) $(CFLAGS) $(CLNKERFLAGS) -o $@ $^
$(LIBDIR)/%.o: $(LIBDIR)/%.c $(LIBDIR)/%.h
@echo $(CC) $(CFLAGS) -o $@ -c $<
目前我只打印了(不完整的)命令,但它仍然没有得到包含所有所需对象的正确输出。
在目录 lib
中,我有以下文件:
$ tree lib
lib
├── app.c
├── app_db.c
├── app_db.h
├── app.h
├── app_logic.c
├── app_logic.h
├── app_net.c
├── app_net.h
├── server.c
├── server.h
├── server_queue.c
└── server_queue.h
但它从未正确构建依赖关系。
$ make libs
gcc -Wpedantic -O3 -o lib/app.o -c lib/app.c
gcc -Wpedantic -O3 -o bin/libapp.so lib/app.o
gcc -Wpedantic -O3 -o lib/server.o -c lib/server.c
gcc -Wpedantic -O3 -o bin/libserver.so lib/server.o
我已经阅读了以下问题 How to use pattern-dependent variables in dependencies in make pattern rules,其中有一个提示,我认为它会帮助我,但它没有。
知道如何实现吗?
编辑 1:
我希望最后一条命令的输出是:
$ make libs
gcc -Wpedantic -O3 -o lib/app.o -c lib/app.c
gcc -Wpedantic -O3 -o lib/app_db.o -c lib/app_db.c
gcc -Wpedantic -O3 -o lib/app_logic.o -c lib/app_logic.c
gcc -Wpedantic -O3 -o lib/app_net.o -c lib/app_net.c
gcc -Wpedantic -O3 -o bin/libapp.so lib/app.o lib/app_db.o lib/app_logic.o lib/app_net.o
gcc -Wpedantic -O3 -o lib/server.o -c lib/server.c
gcc -Wpedantic -O3 -o lib/server_queue.o -c lib/server_queue.c
gcc -Wpedantic -O3 -o bin/libserver.so lib/server.o lib/server_queue.o
感谢您的关注。
Make 对通配符不是很灵巧,您的方法要求它在一行中处理两个不同的通配符。如果这可能的话,那将是一件可怕的事情。我建议采用不同的方法。
首先,你的变量是错误的。不清楚您的意图是什么,但以下是符合您期望结果的:
LIB_SOURCES := $(wildcard $(LIBDIR)/*.c)
LIB_OBJS := $(patsubst %.c,%.o,$(LIB_SOURCES))
现在,您希望您的 makefile 表现得好像它具有以下两个规则:
$(BINDIR)/libapp.so: $(filter $(LIBDIR)/app%,$(NEW_LIB_OBJS))
@echo build $@ somehow from $^
$(BINDIR)/libserver.so: $(filter $(LIBDIR)/server%,$(NEW_LIB_OBJS))
@echo build $@ somehow from $^
但您希望 Make 在 运行 时构建它们,而不是在 makefile 中拼出它们。所以我们将使用 "canned recipe":
define librule
$(BINDIR)/lib$(1).so: $(filter $(LIBDIR)/$(1)%,$(LIB_OBJS))
@echo building $$@ somehow from $$^
endef
$(eval $(call librule,app))
$(eval $(call librule,server))
然后我们可以让 Make 从 LIBS
:
app
和 server
LIB_NAMES := $(patsubst $(BINDIR)/lib%.so,%,$(LIBS))
$(foreach libname,$(LIB_NAMES),$(eval $(call librule,$(libname))))
您添加了 .SECONDEXPANSION
,但您没有在先决条件列表中转义任何内容,因此它实际上没有执行任何操作:
$(BINDIR)/lib%.so: $(LIBDIR)/%.o $(filter $(LIBDIR)/$*_%.o, $(LIB_OBJS))
None个variables/functions被转义了,所以这里的一切都是在初始read-in时展开的,所以二次展开没什么可做的。
二次扩展功能由两部分组成:首先,您使用特殊目标启用它。其次,转义要延迟扩展的变量 and/or 函数。所以这可能是:
getobjs = $(filter $(LIBDIR)/$*_%.o, $(LIB_OBJS))
$(BINDIR)/lib%.so: $(LIBDIR)/%.o $$(getobjs)
@echo $(CC) $(CFLAGS) $(CLNKERFLAGS) -o $@ $^
请注意我们如何将 $(getobjs)
转义为 $$(getobjs)
,以便该变量在第二次传递之前不会展开。