我最近尝试使用 C 预处理器从包含预处理器符号的模板 Makefile.cc 生成可移植的 Makefile。到目前为止,它的效果出奇的好。第一个观察结果是 NMAKE 将预扫描一个 Tools.ini 文件,我在同一目录中提供该文件
[NMAKE]
MAKECONFIG=-D_NMAKE
然后我旁边有一个“真正的”Makefile,它只用 GNU Make 和 NMAKE 的通用子语言编写。
MAKEFILE=Makefile.mk
TEMPLATE=Makefile.cc
all: $(MAKEFILE)
$(MAKE) -f $(MAKEFILE)
clean: $(MAKEFILE)
$(MAKE) -f $(MAKEFILE) clean
$(MAKEFILE): $(TEMPLATE)
$(CXX) $(MAKECONFIG) -E $(TEMPLATE) > $(MAKEFILE)
请注意,-E 开关对于仅用于预处理文件的编译器(至少我使用的三大编译器:GCC、Clang 和 CL)非常常见。使用 GNU Make,$(MAKECONFIG) 扩展为空,但在 NMAKE 中,它提供了声明自身的预处理器变量。由于您的模板 Makefile.cc 可以使用 #ifdef 检查它,以及检查编译器用来声明自身的公共变量,因此您可以为“make”程序、您的操作系统和您正在使用的编译器。
如果你有任何“make”,你可能也已经有一个 C 编译器;无需安装其他软件,如 CMake 或 autotools。它使用旧的机制,因此很可能在很多环境中工作。据我目前所知,它真的很快。至少比在 autotools 中运行配置步骤快。我面临的唯一缺点是它将 Make 规则的样式限制在同一行,因为预处理器会更改代码的缩进。预处理器也会吐出带有 # 标记的行,但由于这些在 Makefile 中开始注释,因此它们无论如何都会被忽略。
A 有一个有点小的 C++ 项目,其 Makefile.cc 看起来像下面的 sn-p。它使用 GCC、Clang 或 CL 在 GNU Make 或 NMAKE 上以及在 Windows 或 POSIX 环境中编译。不过,我还没有支持 BSD Make 或测试任何其他编译器。
// Make Version
#ifdef _NMAKE
# define ifdef !ifdef
# define ifndef !ifndef
# define else !else
# define endif !endif
# define err(x) !error x
# define cat(x, y) x=$(x) y
#else // GNU Make
# define err(x) $(error x)
# define cat(x, y) x += y
#endif
// System Commands
ifdef SHELL
RM=rm -f
else
ifdef COMSPEC
RM=del /f
else
err("Cannot determine your system commands.")
endif // COMSPEC
endif // SHELL
// Project Variables
STD=c++17
SRC=test.cpp dbg.cpp dir.cpp dll.cpp env.cpp err.cpp fifo.cpp file.cpp shm.cpp sig.cpp socket.cpp sys.cpp xdg.cpp
BIN=test
.SUFFIXES: .cpp .hpp .o .d .obj .pdb .lib .exp .ilk .log .i .db
// Operating system
#ifdef _WIN32
cat(CFLAGS, -D_WIN32)
EXE=$(BIN).exe
#else
cat(CFLAGS, -D_POSIX_C_SOURCE)
cat(LDFLAGS, -ldl -lrt -lpthread)
EXE=$(BIN)
#endif
// Make Targets
all: $(EXE)
clean: ; $(RM) $(EXE) *.o *.d *.obj *.pdb *.lib *.exp *.ilk *.log *.i
// Compiler Options
#ifdef _MSC_VER
cat(CFLAGS, -nologo -std:$(STD) -W4 -DNOMINMAX -D_CRT_SECURE_NO_WARNINGS -EHsc -permissive-)
ifndef NDEBUG
cat(CFLAGS, -Zi)
endif
cat(LDFLAGS, -nologo)
OBJ=$(SRC:.cpp=.obj)
$(EXE): $(OBJ); $(CXX) $(LDFLAGS) $(OBJ) -Fe$@
.cpp.obj: ; $(CXX) $(CFLAGS) -c $<
#elif defined(__GNUC__) || defined(__llvm__) || defined(__clang__)
cat(CFLAGS, -std=$(STD) -Wall -Wextra -Wpedantic -MP -MMD)
ifndef NDEBUG
cat(CFALGS, -g)
endif
cat(LDFLAGS, -rdynamic)
OBJ=$(SRC:.cpp=.o)
$(EXE): $(OBJ); $(CXX) $(LDFLAGS) $(OBJ) -o $@
.cpp.o: ; $(CXX) $(CFLAGS) -c $<
# ifndef _NMAKE
-include $(SRC:.cpp=.d)
# endif
#else
# error "Cannot determine your compiler."
#endif