自从我了解了-j后,我一直使用-j8。前几天我正在编译一个地图集安装,但是make失败了。最终我将其追踪到了无序的事情 - 一旦我回到单线程制作,它就能正常工作。这让我很紧张。在编写自己的make文件时,我需要注意哪些条件,以避免使用make -j做出意外的事情?
答案 0 :(得分:39)
我认为make -j将尊重您在Makefile中指定的依赖项;即如果你指定objA依赖于objB和objC,那么在objB和objC完成之前make将不会开始处理objA。
很可能你的Makefile没有严格规定必要的操作顺序,而且运气恰好在单线程情况下适合你。
答案 1 :(得分:23)
简而言之 - 确保您的依赖关系正确且完整。
如果您使用的是单线程make,那么您可能会盲目地忽略目标之间的隐式依赖关系。 使用parallel make时,不能依赖隐式依赖。它们都应该明确。这可能是最常见的陷阱。特别是如果使用.phony目标作为依赖项。
This链接是关于并行make的一些问题的一个很好的入门。
答案 2 :(得分:9)
这是我开始使用并行构建时遇到的问题示例。我有一个名为“fresh”的目标,我用它从头开始重建目标(一个“新鲜的”构建)。在过去,我通过简单地指示“干净”然后“构建”作为依赖来编码“新鲜”目标。
build: ## builds the default target
clean: ## removes generated files
fresh: clean build ## works for -j1 but fails for -j2
在我开始使用并行构建之前,它工作正常,但是对于并行构建,它尝试同时执行“清理”和“构建”。所以我改变了“新鲜”的定义如下,以保证正确的操作顺序。
fresh:
$(MAKE) clean
$(MAKE) build
这基本上只是正确指定依赖关系的问题。诀窍在于并行构建比单线程构建更严格。我的示例演示了给定目标的依赖关系列表并不一定表示执行顺序。
答案 3 :(得分:7)
如果你有一个递归制作,事情很容易破裂。如果你没有做一个递归make,那么只要你的依赖是正确和完整的,你就不应该遇到任何问题(除了make中的bug)。有关递归make的问题的详细描述,请参阅Recursive Make Considered Harmful。
答案 4 :(得分:2)
最好有一个自动化测试来测试所有make文件的-j选项。即使是最好的开发人员也会遇到make的-j选项问题。最常见的问题是最简单的。
myrule: subrule1 subrule2
echo done
subrule1:
echo hello
subrule2:
echo world
在正常的制作中,你会看到你好 - >世界 - >完成。 使用make -j 4,你可能会看到世界 - >你好 - >完成
我发现这种情况最常见的是创建输出目录。例如:
build: $(DIRS) $(OBJECTS)
echo done
$(DIRS):
-@mkdir -p $@
$(OBJECTS):
$(CC) ...
答案 5 :(得分:1)
以为我会添加到subsetbrew的答案,因为它没有清楚地显示效果。但是添加一些睡眠命令可以。那么它适用于linux。
然后运行make显示差异:
all: toprule1
toprule1: botrule2 subrule1 subrule2
@echo toprule 1 start
@sleep 0.01
@echo toprule 1 done
subrule1: botrule1
@echo subrule 1 start
@sleep 0.08
@echo subrule 1 done
subrule2: botrule1
@echo subrule 2 start
@sleep 0.05
@echo subrule 2 done
botrule1:
@echo botrule 1 start
@sleep 0.20
@echo "botrule 1 done (good prerequiste in sub)"
botrule2:
@echo "botrule 2 start"
@sleep 0.30
@echo "botrule 2 done (bad prerequiste in top)"