让我们假设一个简单的项目,包括两个静态源和三个动态源,它们都是生成的。 makefile如下所示:
SRC_DIR = src
OUTPUT = output
TARGET = $(OUTPUT)/lib.so
GEN_SRCS = dynamic_1.c dynamic_2.c dynamic_3.c
SRCS = static_1.c static_2.c $(GEN_SRCS)
OBJS = $(SRCS:%.c=$(OUTPUT)/%.o)
all: $(TARGET)
$(TARGET): $(OBJS)
gcc -shared -o $@ $^
$(OUTPUT)/%.o: $(SRC_DIR)/$*%.c | $(OUTPUT)
@echo "Creating object $@ from $^"
gcc -c -o $@ $<
$(SRC_DIR)/dynamic_%.c: definition_for_generator.xml
@echo "---------------------------------------"
@echo "Generating dynamic Cs"
@echo "---------------------------------------"
./generate.sh $<
$(OUTPUT):
mkdir -p $(OUTPUT)
.PHONY: clean
clean:
rm -rf $(OUTPUT) $(SRC_DIR)/dynamic_1.c $(SRC_DIR)/dynamic_2.c $(SRC_DIR)/dynamic_3.c
虽然只使用一个核心构建,但它构建得很好:
mkdir -p output
Creating object output/static_1.o from src/static_1.c
gcc -c -o output/static_1.o src/static_1.c
Creating object output/static_2.o from src/static_2.c
gcc -c -o output/static_2.o src/static_2.c
---------------------------------------
Generating dynamic Cs
---------------------------------------
./generate.sh definition_for_generator.xml
Creating object output/dynamic_1.o from src/dynamic_1.c
gcc -c -o output/dynamic_1.o src/dynamic_1.c
Creating object output/dynamic_2.o from src/dynamic_2.c
gcc -c -o output/dynamic_2.o src/dynamic_2.c
Creating object output/dynamic_3.o from src/dynamic_3.c
gcc -c -o output/dynamic_3.o src/dynamic_3.c
gcc -shared -o output/lib.so output/static_1.o output/static_2.o output/dynamic_1.o output/dynamic_2.o output/dynamic_3.o
rm src/dynamic_1.c src/dynamic_2.c src/dynamic_3.c
但是,对于并行构建(-j 2),dynamic_ *文件会生成两次:
mkdir -p output
---------------------------------------
Generating dynamic Cs
---------------------------------------
./generate.sh definition_for_generator.xml
---------------------------------------
Generating dynamic Cs
---------------------------------------
./generate.sh definition_for_generator.xml
---------------------------------------
Generating dynamic Cs
---------------------------------------
./generate.sh definition_for_generator.xml
Creating object output/static_1.o from src/static_1.c
gcc -c -o output/static_1.o src/static_1.c
Creating object output/static_2.o from src/static_2.c
gcc -c -o output/static_2.o src/static_2.c
Creating object output/dynamic_1.o from src/dynamic_1.c
gcc -c -o output/dynamic_1.o src/dynamic_1.c
Creating object output/dynamic_2.o from src/dynamic_2.c
gcc -c -o output/dynamic_2.o src/dynamic_2.c
Creating object output/dynamic_3.o from src/dynamic_3.c
gcc -c -o output/dynamic_3.o src/dynamic_3.c
gcc -shared -o output/lib.so output/static_1.o output/static_2.o output/dynamic_1.o output/dynamic_2.o output/dynamic_3.o
rm src/dynamic_1.c src/dynamic_2.c src/dynamic_3.c
我应该如何更改makefile以仅生成一个文件? 一条线索可以在这里,但我还没有找到使用它的方法: multiple targets from one recipe and parallel execution
要下载的项目位于:http://stuff.pitris.info/make.tar.gz
谢谢!
答案 0 :(得分:1)
这个相关答案就是答案。你的规则:
$(SRC_DIR)/dynamic_%.c: definition_for_generator.xml
@echo "---------------------------------------"
@echo "Generating dynamic Cs"
@echo "---------------------------------------"
./generate.sh $<
告诉make每个$(SRC_DIR)/dynamic_%.c
的调用都会创建一个generate.sh
文件。但是,运行该脚本实际上会生成多个文件(但make不知道)。
因此,当您运行make
时,通常make按顺序进行操作,当它看到第一个dynamic_*.c
文件时,它会运行脚本来生成该单个文件(但生成所有文件)。当make然后确定是否需要生成下一个dynamic_*.c
文件时,它会看到该文件已经存在并跳过生成它的规则。
当您运行make -j
生成并行进程时,那些恰好同时发生dynamic_*.c
文件,因此他们所有运行脚本(即使只有其中一个需要)。
这就是为什么你看到它运行N
次。更改目标订单/等。并且你可能会看到更少generate.sh
次运行,因为并行性的运行方式不同(你甚至可以设法让它只运行一次),但这都是巧合。
正确的解决方案,就是在链接的答案中,通过列出该规则的多个模式来教授通过运行脚本来创建所有文件。或者,也可以将generate.sh
修改为一次只生成一个文件(并始终运行N
次)。
最简单(虽然有点难看)的方式来做多个模式的事情可能就是取代
GEN_SRCS = dynamic_1.c dynamic_2.c dynamic_3.c
与
GEN_SRCS = %ynamic_1.c %ynamic_2.c %ynamic_3.c
或类似的东西,然后替换
$(SRC_DIR)/dynamic_%.c: definition_for_generator.xml
与
$(GEN_SRCS): definition_for_generator.xml
答案 1 :(得分:0)
您的代码还有另一个问题:依赖于$(OUTPUT)目录。它永远不会按预期工作。
下面的makefile按预期工作,请尝试:
SRC_DIR = src
OUTPUT = output
TARGET = $(OUTPUT)/lib.so
GEN_SRCS = dynamic_1.c dynamic_2.c dynamic_3.c
SRCS = static_1.c static_2.c $(GEN_SRCS)
ifeq ($(filter clean,$(MAKECMDGOALS)),)
-include $(OUTPUT)/.timestamp
endif
OBJS = $(SRCS:%.c=$(OUTPUT)/%.o)
mkdir_@ = $(if $(wildcard $(@D)),,mkdir -p $(@D))
all: $(TARGET)
$(TARGET): $(OBJS)
gcc -shared -o $@ $^
$(OUTPUT)/%.o: $(SRC_DIR)/$*%.c
$(mkdir_@)
@echo "Creating object $@ from $^"
gcc -c -o $@ $<
$(OUTPUT)/.timestamp: definition_for_generator.xml
$(mkdir_@)
@echo "---------------------------------------"
@echo "Generating dynamic Cs"
@echo "---------------------------------------"
./generate.sh $<
touch $@
.PHONY: clean
clean:
rm -rf $(OUTPUT) $(SRC_DIR)/dynamic_1.c $(SRC_DIR)/dynamic_2.c $(SRC_DIR)/dynamic_3.c
这个想法是为生成的空makefile注入依赖 - $(OUTPUT)/ .timestamp。这是为了强制GNU Make重读你的Makefile。生成$(OUTPUT)/。时间戳的规则运行源生成器。因此,当GNU Make第二次开始处理Makefile时,生成的源已经存在。因此,您不需要为它们定义任何依赖项,即它们就像您的常规源文件一样。
提示:将生成的源写入源代码树不是一个好主意,因为不能同时阻止多个构建使用此源代码树。这是这种方法最明显的问题。
另请参阅 mkdir _ @ 函数:如果目标不存在,它将有效地为目标创建目录,首先尝试使用GNU Make builtin在生成 mkdir之前检查目录是否存在过程。与Cygwin一起使用时会有所不同,因为与第一类UNIX系统相比,在此环境中创建进程非常耗时。
输出:
$ make -j
mkdir -p output
---------------------------------------
Generating dynamic Cs
---------------------------------------
./generate.sh definition_for_generator.xml
touch output/.timestamp
Creating object output/static_1.o from src/static_1.c
gcc -c -o output/static_1.o src/static_1.c
Creating object output/static_2.o from src/static_2.c
gcc -c -o output/static_2.o src/static_2.c
Creating object output/dynamic_1.o from src/dynamic_1.c
Creating object output/dynamic_2.o from src/dynamic_2.c
gcc -c -o output/dynamic_1.o src/dynamic_1.c
gcc -c -o output/dynamic_2.o src/dynamic_2.c
Creating object output/dynamic_3.o from src/dynamic_3.c
gcc -c -o output/dynamic_3.o src/dynamic_3.c
gcc -shared -o output/lib.so output/static_1.o output/static_2.o output/dynamic_1.o output/dynamic_2.o output/dynamic_3.o