这是我目前的makefile:
# Compiler #
CXX = g++
DEBUG = -g
LFLAGS =
CXXFLAGS = -Wall
# Directories #
SRCDIR = src/
INCDIR = include/
BUILDDIR = build/
BINDIR = bin/
# Objects #
OBJ_NAMES = main.o dfa.o dfaException.o state.o
OBJS = $(addprefix $(BUILDDIR), $(OBJ_NAMES))
# Output #
NAME = pract3
TARGET = $(BINDIR)pract3
# Clean #
ifeq ($(OS),Windows_NT)
RM = del /q /s $(BUILDDIR:/=\)*.o $(BINDIR:/=\)$(NAME)*
else
RM = rm -rf $(BUILDDIR)*.o $(TARGET)*
endif
# Files #
$(TARGET): $(OBJS)
$(CXX) $(CXXFLAGS) $(LFLAGS) $(OBJS) -o $(TARGET)
$(BUILDDIR)%.o: $(SRCDIR)%.cpp
$(CXX) $(CXXFLAGS) $(LFLAGS) -c $< -o $@
clean:
$(RM)
这是我的项目树:
Porject/
bin/
build/
doc/
...
include/
connection.hpp
dfa.hpp
dfaException.hpp
state.hpp
src/
dfa.cpp
dfaException.cpp
main.cpp
state.cpp
Makefile
Readme
现在,我有三个“问题”。
首先,我希望我的makefile创建bin并构建目录,以防万一。我想我只需要使用:
mkdir $(BUILDDIR)
mkdir $(BINDIR)
但我应该把它们放在哪里?而且,我怎样才能阻止mkdir和rm(或windows中的del)消息,例如“找不到......”或“x目录已经存在......”
其次,我猜我可以从src /中读取对象名称(将.cpp转换为.o),但是如何从目录中读取文件名?
最后,我有一个模板类:connection.hpp
(所有函数都在标题中)。此文件包含在state.hpp
中,使用:#include "../include/connection.hpp"
。我一次运行make
一次正确,然后故意在connection.hpp
中出现语法错误。然后我再次运行make
,但它只使用构建中的.o
文件编译目标文件而没有任何错误。每当我想要修改connection.hpp
时,我必须使用make clean
然后make
。有没有更好的方法呢?
答案 0 :(得分:1)
如果您需要一个目录才能继续,只需输入
即可mkdir -p ${DIRECTORY}
在您的规则中需要它之前。如果目录已经存在,mkdir -p
将很乐意无所事事。
同样,如果您使用rm -f FILE
,如果首先不存在FILE
,则不应该抱怨。
没有可移植的方法来创建一个包含目录中所有文件名称的变量。但是你已经在使用GNU Make功能了,所以你可以使用
SOURCES = $(wildcard ${SRCDIR}/*.cpp)
然后
OBJECTS = $(SOURCES:.cpp=.o)
将它们转换为目标文件名。我猜你也可能想要替换主要目录名称。
您没有在make文件中列出任何*.hpp
个文件作为先决条件。您可以手动添加它们,如
foo.o: foo.cpp bar.hpp baz.hpp
但这很快就会变得不愉快。另一个技巧是使用编译器通过文件告诉你标题(传递)#include
d。如果您使用的是GCC,则可以运行
gcc -MM foo.cpp
将其输出到make-file代码段上方。您可以设置类似以下
的模式规则%.deps: %.cpp
${CXX} -MM ${CPPFLAGS} $< > $@
到您的make文件中,然后include
生成的*.deps
个文件。
include $(SOURCES:.cpp=.deps)
GNU Make将足够智能,首先解析make文件,认识到*.deps
文件不存在,因此不能include
d但是要知道有一个规则要生成他们。因此它将执行该规则,然后继续解析make文件。
我从彼得·米勒的精彩文章Recursive Make Considered Harmful中学到了这个技巧,如果你想学习如何编写好的制作文件,这是一个很好的阅读。