我希望我的 makefile 更加独立于顺序!
这与我之前的问题有关: 为什么 .PHONY 在此不起作用情况如何?。
我编写了一个 makefile 系统,以便不熟悉 make 的开发人员可以轻松地完成他们的任务。简而言之,有一个对于所有项目都相同的通用部分,以及一组特定于给定项目的 makefile。项目特定的包括通用的。由于某种原因,它在 make 3.80 上运行得很好,但是当我在 make 3.81 上尝试它时,我遇到了一些问题。这迫使我做出上面帖子中提到的更改。现在我遇到了一些新问题,所以我决定再发一篇文章。就像在那篇文章中一样,我制作了一组更小、更简单的 makefile 来显示问题。不幸的是,“简单”案例由 6 个文件组成。对此感到抱歉。首先,我将从“特定于项目”的文件开始(这些文件很简单):
makefile:
TARGETS:=\
Lib1.mk \
Lib2.mk \
my_prog.mk \
include generic/top.mk
Lib1.mk:
BINARY:=Lib1
TYPE:=LIB
LOCATION:=a/location
include generic/rules.mk
Lib2.mk:
BINARY:=Lib2
TYPE:=LIB
LOCATION:=another/location
LIBS:=Lib1
include generic/rules.mk
my_prog.mk:
BINARY:=my_prog
TYPE:=EXE
LOCATION:=some/location
LIBS:=Lib1 Lib2
include generic/rules.mk
简单描述:Makefile 简单列出了所有目标的名称。目标是可执行文件或库。 BINARY 是库或可执行文件的名称(扩展名由通用部分添加)。 TYPE 是 EXE 或 LIB。 LOCATION 是二进制文件应该存放的位置。 LIBS 是该二进制文件所依赖的任何库。真正的处理为用户创建所有 -L、rpath 等内容(以及 Visual Studio 的等效项)。现在是通用的(这些确实起作用):
generic/top.mk:
ALL_BINS:=
.PHONY: all
all:
include $(TARGETS)
all: $(ALL_BINS)
%.so %.exe:
mkdir -p $(dir $@)
touch $@
clean:
rm -rf out
最后..
generic/rules.mk:
ifeq (EXE,$(TYPE))
$(BINARY).FULL_FILE_NAME:=out/$(LOCATION)/$(BINARY).exe
else
$(BINARY).FULL_FILE_NAME:=out/$(LOCATION)/lib$(BINARY).so
endif
$(BINARY).DEP_LIBS:=$(foreach a,$(LIBS),$($(a).FULL_FILE_NAME))
ALL_BINS+=$(BINARY)
$(BINARY): $($(BINARY).FULL_FILE_NAME)
$($(BINARY).FULL_FILE_NAME): $($(BINARY).DEP_LIBS)
BINARY:=
LOCATION:=
LIBS:=
好吧,在这种状态下,事情工作正常。 Make 正确处理所有依赖项,如果我触摸任何文件,它只会正确地“构建”它必须的文件,仅此而已。当您从 makefile 中取出 m_prog.mk 行并将其移动到列表顶部时,就会出现问题,如下所示:
TARGETS:=\
my_prog.mk \
Lib1.mk \
Lib2.mk \
问题似乎是在它经过 rules.mk 时 对于 my_prog.mk 它还不知道 Lib1 和 Lib2 的完整库路径(它们是空字符串)。所以最后,它认为 my_prog 不依赖于任何东西,并尝试无序地构建它。在此示例中,您只是看到它首先“触摸”my_prog,然后是其他 2 个。当然,当我在那里有真正的编译器和链接器命令时,它会抛出错误。
当我只是让 .PHONY 目标相互依赖时(因此 my_prog 依赖于 Lib1 和 Lib2),生活是轻松和谐的。现在我无法做到这一点,生活变得更加困难。
你可能会说,“哎呀,只要按正确的顺序排列就可以了!”。到目前为止,这已经通过 make 为最终用户自动处理。事实上,大多数客户都是按字母顺序排列的。他们不知道也不关心彼此依赖的顺序。如果现在不得不告诉他们重新订购所有这些,那就太糟糕了。抱歉这篇文章的长度。如果有任何答案,我将不胜感激!
This is related to my previous question: Why does .PHONY not work in this situation?.
I have a makefile system that I wrote to make it easy for developers who are not familiar with make, to do their tasks. In short, there is a generic portion which would be the same for all projects, and a set of makefiles that are specific for a given project. The project specific ones include the generic ones. It worked great on make 3.80 for some reason, but when I tried it out on make 3.81 I ran into a few problems. That forced me to make changes that are mentioned in the above post. Now I have some new problems, so I decided to make another post. Like in that post, I made a much smaller and simpler set of makefiles that show the problem. Unfortunatly, the "simple" case consists of 6 files. Sorry about that. First I'll start with the "project specific" ones (these are meant to be simple):
makefile:
TARGETS:=\
Lib1.mk \
Lib2.mk \
my_prog.mk \
include generic/top.mk
Lib1.mk:
BINARY:=Lib1
TYPE:=LIB
LOCATION:=a/location
include generic/rules.mk
Lib2.mk:
BINARY:=Lib2
TYPE:=LIB
LOCATION:=another/location
LIBS:=Lib1
include generic/rules.mk
my_prog.mk:
BINARY:=my_prog
TYPE:=EXE
LOCATION:=some/location
LIBS:=Lib1 Lib2
include generic/rules.mk
A quick description: Makefile simply lists the names of all the targets. A target is either a executable or a library. BINARY is the name of the library or executable (extensions are added by the generic part). TYPE is either EXE or LIB. LOCATION is where the binary should go. LIBS is whatever libraries this binary depends on. The real ones handles creating all the -L, rpath, etc. stuff for the user (as well as their equivalents for visual studio). Now for the generic ones (these do the REAL work):
generic/top.mk:
ALL_BINS:=
.PHONY: all
all:
include $(TARGETS)
all: $(ALL_BINS)
%.so %.exe:
mkdir -p $(dir $@)
touch $@
clean:
rm -rf out
and finally..
generic/rules.mk:
ifeq (EXE,$(TYPE))
$(BINARY).FULL_FILE_NAME:=out/$(LOCATION)/$(BINARY).exe
else
$(BINARY).FULL_FILE_NAME:=out/$(LOCATION)/lib$(BINARY).so
endif
$(BINARY).DEP_LIBS:=$(foreach a,$(LIBS),$($(a).FULL_FILE_NAME))
ALL_BINS+=$(BINARY)
$(BINARY): $($(BINARY).FULL_FILE_NAME)
$($(BINARY).FULL_FILE_NAME): $($(BINARY).DEP_LIBS)
BINARY:=
LOCATION:=
LIBS:=
Ok, in this state, things work fine. Make handles all the dependencies correctly, and if I touch any of the files, it will correctly "build" only the ones that it has to, and nothing more. The problem happens when you take the m_prog.mk line from makefile and move it to the top of the list, like so:
TARGETS:=\
my_prog.mk \
Lib1.mk \
Lib2.mk \
The problem seems to be that while its is going through rules.mk for my_prog.mk it does not yet know what the full library path for Lib1 and Lib2 (they are empty strings). So in the end, it considers my_prog to be dependent on nothing and it tries to build it out of order. In this example, you just see it "touch" my_prog first and then the other 2. Of course, when I have real compiler and linker commands in there, it throws an error.
Back when I simply had the .PHONY targets depend on each other (so my_prog depended on Lib1 and Lib2) life was easy and harmonious. Now that I can't do that, life became more difficult.
You may say, "heck just put it in the right order!". Well up to now, this has been handled automatically through make for the end users. In fact, most customers have been putting things in alphabetical order. They don't know or care what order they depend on each other. It would stink to have to tell them to re-order all of that now. Sorry for the length of this post. I'd appreciate any answers!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
如果使用 := 赋值运算符设置变量,则会立即计算赋值。
如果您仅使用 = 作为赋值运算符来设置变量,则会尽可能晚地(在实际使用时)延迟评估它们。
请参阅 http://www.gnu.org/software/automake/manual/ make/Flavors.html。
If you set variables using the := assignment operator, the assignment is evaluated immediately.
If you set variables using just = as the assignment operator, they're evaluated lazily, as late as possible (at the time of actual use).
See http://www.gnu.org/software/automake/manual/make/Flavors.html.
有几种方法可以做你想做的事。最干净的可能是使用
vpath
。只需修改rules.mk
:There are several ways to do what you want. The cleanest is probably by using
vpath
. Just modifyrules.mk
: