Makefile 将自身添加为目标

发布于 2024-10-04 03:33:23 字数 1939 浏览 0 评论 0原文

我有一个使用自动依赖项生成的 C++ 程序的 Makefile。 %.d 配方取自 GNU Make 手册。

问题在于,以某种方式将“Makefile”添加为目标,然后隐式规则导致它假设它是可执行文件并使用我的 src/%.cpp 规则尝试编译 src/Makefile.cpp。查看调试信息时,这总是在执行包含之后立即发生。

    No need to remake target `build/Sprite.d'.
 Considering target file `Makefile'.
  Looking for an implicit rule for `Makefile'.
  ...
  Trying pattern rule with stem `Makefile'.
  Trying implicit prerequisite `Makefile.o'.
  Looking for a rule with intermediate file `Makefile.o'.

我知道 include 会导致在必要时重建给定的 Makefiles。它是否也尝试重建当前的 Makefile?如果是这样,我该如何阻止它,如果不是,那么为什么将“Makefile”添加为目标?

此外,即使我在命令行上指定了目标(例如 make clean),包含也会被执行,从而导致 .d 文件被重新制作。有什么办法可以阻止这种情况发生吗?



# $(call setsuffix,newsuffix,files)
# Replaces all the suffixes of the given list of files.
setsuffix = $(foreach file,$2,$(subst $(suffix $(file)),$1,$(file)))

# $(call twinfile,newdir,newsuffix,oldfile)
# Turns a path to one file into a path to a corresponding file in a different
# directory with a different suffix.
twinfile = $(addprefix $1,$(call setsuffix,$2,$(notdir $3)))

MAIN = main

SOURCE_DIR = src/
INCLUDE_DIR = include/
BUILD_DIR = build/

SOURCES = $(wildcard $(SOURCE_DIR)*.cpp)
OBJECTS = $(call twinfile,$(BUILD_DIR),.o,$(SOURCES))
DEPENDENCIES = $(call twinfile,$(BUILD_DIR),.d,$(SOURCES))

CXX = g++
LIBS = -lpng
CXXFLAGS = -I $(INCLUDE_DIR)


.PHONY: all
all: $(MAIN)

$(MAIN): $(OBJECTS)
 $(CXX) $(LIBS) $^ -o $(MAIN)

include $(DEPENDENCIES)

%.o: $(BUILD_DIR)stamp
 $(CXX) $(CXXFLAGS) -c $(call twinfile,$(SOURCE_DIR),.cpp,$@) -o $@

$(BUILD_DIR)%.d: $(SOURCE_DIR)%.cpp $(BUILD_DIR)stamp
 @ echo Generate dependencies for $ $@.$$$$; \
 sed 's,\($*\)\.o[ :]*,$(BUILD_DIR)\1.o $@ : ,g'  $@; \
 rm -f $@.$$$$

$(BUILD_DIR)stamp:
 mkdir -p $(BUILD_DIR)
 touch $@

.PHONY: clean
clean:
 rm -rf $(BUILD_DIR)

.PHONY: printvars
printvars:
 @ echo $(SOURCES)
 @ echo $(OBJECTS)
 @ echo $(DEPENDENCIES)


I have a Makefile for a C++ program that uses automatic dependency generation. The %.d recipe is taken from the GNU Make manual.

The problem is that somehow "Makefile" is being added as a target and then an implicit rule is causing it to assume it's an executable and using my src/%.cpp rule to try to compile src/Makefile.cpp. When looking at the debug info, this always happens right after the include is executed.

    No need to remake target `build/Sprite.d'.
 Considering target file `Makefile'.
  Looking for an implicit rule for `Makefile'.
  ...
  Trying pattern rule with stem `Makefile'.
  Trying implicit prerequisite `Makefile.o'.
  Looking for a rule with intermediate file `Makefile.o'.

I know include causes the given Makefiles to be rebuilt if necessary. Does it also try to rebuild the current Makefile? If so how do I stop it, and if not, then why is "Makefile" being added as a target?

Also, the include is executed, causing the .d files to be remade even if I specify a target on the command line, such as make clean. Is there any way to stop that from happening?



# $(call setsuffix,newsuffix,files)
# Replaces all the suffixes of the given list of files.
setsuffix = $(foreach file,$2,$(subst $(suffix $(file)),$1,$(file)))

# $(call twinfile,newdir,newsuffix,oldfile)
# Turns a path to one file into a path to a corresponding file in a different
# directory with a different suffix.
twinfile = $(addprefix $1,$(call setsuffix,$2,$(notdir $3)))

MAIN = main

SOURCE_DIR = src/
INCLUDE_DIR = include/
BUILD_DIR = build/

SOURCES = $(wildcard $(SOURCE_DIR)*.cpp)
OBJECTS = $(call twinfile,$(BUILD_DIR),.o,$(SOURCES))
DEPENDENCIES = $(call twinfile,$(BUILD_DIR),.d,$(SOURCES))

CXX = g++
LIBS = -lpng
CXXFLAGS = -I $(INCLUDE_DIR)


.PHONY: all
all: $(MAIN)

$(MAIN): $(OBJECTS)
 $(CXX) $(LIBS) $^ -o $(MAIN)

include $(DEPENDENCIES)

%.o: $(BUILD_DIR)stamp
 $(CXX) $(CXXFLAGS) -c $(call twinfile,$(SOURCE_DIR),.cpp,$@) -o $@

$(BUILD_DIR)%.d: $(SOURCE_DIR)%.cpp $(BUILD_DIR)stamp
 @ echo Generate dependencies for $ $@.$$; \
 sed 's,\($*\)\.o[ :]*,$(BUILD_DIR)\1.o $@ : ,g'  $@; \
 rm -f $@.$$

$(BUILD_DIR)stamp:
 mkdir -p $(BUILD_DIR)
 touch $@

.PHONY: clean
clean:
 rm -rf $(BUILD_DIR)

.PHONY: printvars
printvars:
 @ echo $(SOURCES)
 @ echo $(OBJECTS)
 @ echo $(DEPENDENCIES)


如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

岛歌少女 2024-10-11 03:33:23

Make 在执行 Makefile 之前总是会尝试重新生成 Makefile。为此,make 将查找可用于重新创建 Makefile 的规则。 Make 将寻找相当多的隐式规则和其他晦涩的方法来(重新)创建 Makefile。

在您的情况下, make 以某种方式决定应该使用模式规则 %.o: $(BUILD_DIR)/stamp 来重新创建 Makefile,但失败了。

为了防止 make 重新创建 Makefile,您可以使用空配方编写规则:

Makefile: ;

阅读章节 在 make 手册中重新制作 Makefiles 以获取更多说明。

关于包含的 Makefile:无论目标如何,包含的 Makefile 将始终包含在内。如果包含的 makefile 丢失(或早于其先决条件),那么将首先(重新)创建它们。这意味着 make clean 将首先生成 .d Makefile,然后再次删除它们。

您可以通过将 include 指令包装在条件中来防止包含特定目标:

ifneq ($(MAKECMDGOALS),clean)
include $(DEPENDENCIES)
endif

这是经过一些修复的整个 Makefile。我标记了我改变过的地方。

# Makefile

# $(call setsuffix,newsuffix,files)
# Replaces all the suffixes of the given list of files.
setsuffix = $(foreach file,$2,$(subst $(suffix $(file)),$1,$(file)))

# $(call twinfile,newdir,newsuffix,oldfile)
# Turns a path to one file into a path to a corresponding file in a different
# directory with a different suffix.
twinfile = $(addprefix $1/,$(call setsuffix,$2,$(notdir $3)))

MAIN = main

SOURCE_DIR = src
INCLUDE_DIR = include
BUILD_DIR = build

SOURCES = $(wildcard $(SOURCE_DIR)/*.cpp)
OBJECTS = $(call twinfile,$(BUILD_DIR),.o,$(SOURCES))
DEPENDENCIES = $(call twinfile,$(BUILD_DIR),.d,$(SOURCES))

CXX = g++
LIBS = -lpng
CXXFLAGS = -I $(INCLUDE_DIR)


.PHONY: all
all: $(MAIN)

$(MAIN): $(OBJECTS)
    $(CXX) $(LIBS) $^ -o $(MAIN)

# -------> only include if goal is not clean <---------
ifneq ($(MAKECMDGOALS),clean)
include $(DEPENDENCIES)
endif

# ---------> fixed this target <--------------
$(BUILD_DIR)/%.o: $(SOURCE_DIR)/%.cpp $(BUILD_DIR)/stamp
    $(CXX) $(CXXFLAGS) -c $(call twinfile,$(SOURCE_DIR),.cpp,$@) -o $@

# ---------> and this target <---------------
$(BUILD_DIR)/%.d: $(SOURCE_DIR)/%.cpp $(BUILD_DIR)/stamp
    @ echo Generate dependencies for $@;
    @set -e; rm -f $@; \
    $(CC) -M $(CPPFLAGS) 
lt; > $@.$$; \
    sed 's,\($*\)\.o[ :]*,$(BUILD_DIR)\1.o $@ : ,g' < $@.$$ > $@; \
    rm -f $@.$$

$(BUILD_DIR)/stamp:
    mkdir -p $(BUILD_DIR)
    touch $@

.PHONY: clean
clean:
    rm -rf $(BUILD_DIR)

.PHONY: printvars
printvars:
    @ echo $(SOURCES)
    @ echo $(OBJECTS)
    @ echo $(DEPENDENCIES)

Make will always try to remake the Makefile before executing the Makefile. To do so, make will look for rules which can be used to recreate the Makefile. Make will look for quite a few implicit rules and other obscure methods to (re)create the Makefile.

In your case, make somehow decided that the pattern rule %.o: $(BUILD_DIR)/stamp should be used to recreate the Makefile, which failed.

To prevent make from remaking the Makefile you can write a rule with an empty recipe:

Makefile: ;

Read the chapter Remaking Makefiles in the make manual for more explanation.

About the included Makefiles: Included Makefiles will always be included, regardless of the target. If the included makefiles are missing (or older than their prerequisites) then they will first be (re)created. That means a make clean will first generate the .d Makefiles, only to delete them again.

You can prevent the including for specific goals by wraping the include directive in a conditional:

ifneq ($(MAKECMDGOALS),clean)
include $(DEPENDENCIES)
endif

Here is your entire Makefile with some fixes. I marked the places where I changed something.

# Makefile

# $(call setsuffix,newsuffix,files)
# Replaces all the suffixes of the given list of files.
setsuffix = $(foreach file,$2,$(subst $(suffix $(file)),$1,$(file)))

# $(call twinfile,newdir,newsuffix,oldfile)
# Turns a path to one file into a path to a corresponding file in a different
# directory with a different suffix.
twinfile = $(addprefix $1/,$(call setsuffix,$2,$(notdir $3)))

MAIN = main

SOURCE_DIR = src
INCLUDE_DIR = include
BUILD_DIR = build

SOURCES = $(wildcard $(SOURCE_DIR)/*.cpp)
OBJECTS = $(call twinfile,$(BUILD_DIR),.o,$(SOURCES))
DEPENDENCIES = $(call twinfile,$(BUILD_DIR),.d,$(SOURCES))

CXX = g++
LIBS = -lpng
CXXFLAGS = -I $(INCLUDE_DIR)


.PHONY: all
all: $(MAIN)

$(MAIN): $(OBJECTS)
    $(CXX) $(LIBS) $^ -o $(MAIN)

# -------> only include if goal is not clean <---------
ifneq ($(MAKECMDGOALS),clean)
include $(DEPENDENCIES)
endif

# ---------> fixed this target <--------------
$(BUILD_DIR)/%.o: $(SOURCE_DIR)/%.cpp $(BUILD_DIR)/stamp
    $(CXX) $(CXXFLAGS) -c $(call twinfile,$(SOURCE_DIR),.cpp,$@) -o $@

# ---------> and this target <---------------
$(BUILD_DIR)/%.d: $(SOURCE_DIR)/%.cpp $(BUILD_DIR)/stamp
    @ echo Generate dependencies for $@;
    @set -e; rm -f $@; \
    $(CC) -M $(CPPFLAGS) 
lt; > $@.$$; \
    sed 's,\($*\)\.o[ :]*,$(BUILD_DIR)\1.o $@ : ,g' < $@.$$ > $@; \
    rm -f $@.$$

$(BUILD_DIR)/stamp:
    mkdir -p $(BUILD_DIR)
    touch $@

.PHONY: clean
clean:
    rm -rf $(BUILD_DIR)

.PHONY: printvars
printvars:
    @ echo $(SOURCES)
    @ echo $(OBJECTS)
    @ echo $(DEPENDENCIES)
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文