我正在尝试构建一个 makefile,以便在 Linux 下编译一个在 Windows 上使用 Code::Blocks 编写的程序(因此,我没有可用的 makefile 可供查看)。
项目很大,源文件位于各个子目录中,所以我这样制作了我的makefile:
# Main project folder
MyProject_PATH := ~/MyProject/
# Main Source Folder
SRC_PATH := $(MyProject_PATH)src
# All Source subfolders
SRC_DIRS := $(shell find $(SRC_PATH) -type d)
# - Output folders
OBJ_PATH := $(MyProject_PATH)LINUXmake/obj-arm
BIN_PATH := $(MyProject_PATH)LINUXmake/bin-arm
# - Final executable name
EXE := $(BIN_PATH)/MyProject-arm
# get all .c and .cpp files
SRC := $(foreach dir,$(SRC_DIRS),$(wildcard $(dir)/*.c) $(wildcard $(dir)/*.cpp))
# Define object files for each source file
OBJ := $(patsubst %.c,$(OBJ_PATH)/%.o,$(notdir $(SRC:.c=.o)))
OBJ := $(patsubst %.cpp,$(OBJ_PATH)/%.o,$(notdir $(OBJ:.cpp=.o)))
然后我有这些规则部分:
.PHONY: all show clean
# Rules for compiling .c files
%.o: %.c
@echo "Compiling C file: $<"
$(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@
# Rules for compiling .cpp files
%.o: %.cpp
@echo "Compiling C++ file: $<"
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $< -o $@
# Rule to build the final target
$(EXE): $(OBJ)
@echo "Linking objects to create target: $@"
$(CXX) $(LDLIBS) $^ -o $@
# Rule to create output directories
$(shell mkdir -p $(OBJ_PATH) $(BIN_PATH))
# Rule to clean object files and the target
clean:
$(RM) -r $(OBJ_PATH) $(BIN_PATH)
show:
@printf "\nPaths: $(OBJ_PATH) $(BIN_PATH)\n"
@printf "\nSource files:\n$(SRC)"
@printf "\n\nObject files:\n$(OBJ)"
@printf "\n\nCompiler:\n$(CC)"
@printf "\n\nC++ Compiler:\n$(CXX)"
@printf "\n\nC Flags:\n$(CFLAGS)"
@printf "\n\nC++ Flags:\n$(CXXFLAGS)"
@printf "\n\nInclude Flags:\n$(CPPFLAGS)"
@printf "\n\nLinked Libraries:\n$(LDLIBS)"
@echo ""
但我总是收到此错误:
make: *** No rule to make target 'cKeyboard.o', needed by '/home/buster/MyProject/LINUXmake/bin-arm/MyProject'. Stop.
如果我使用“show”目标,我会得到以下输出:
Paths: ~/MyProject/LINUXmake/obj-arm ~/MyProject/LINUXmake/bin-arm
Source files:
/home/buster/MyProject/src/cKeyboard.cpp /home/buster/MyProject/src/GUI_MainForm.cpp /home/buster/MyProject/src/MyProject_bitmaps.cpp /home/buster/MyProject/src/CommManager.cpp /home/buster/MyProject/src/cMSX.cpp /home/buster/MyProject/src/fMainForm.cpp /home/buster/MyProject/src/MSX_PG.cpp /home/buster/MyProject/src/MsgManager.cpp /home/buster/MyProject/src/ClassiEventi/cBaseObjWithEvents.cpp /home/buster/MyProject/src/cMSXTool/cMSXTool.cpp /home/buster/MyProject/src/Comm/X_OS_Socket.cpp /home/buster/MyProject/src/Comm/serialib.cpp /home/buster/MyProject/src/Comm/BBBSerial.cpp /home/buster/MyProject/src/CustomControls/cCCDCalibration/cCCDCal.cpp /home/buster/MyProject/src/CustomControls/TAntiAliasedGauge/TAntiAliasedGauge.cpp /home/buster/MyProject/src/CustomControls/TGradientButton/wxGradientButton.cpp /home/buster/MyProject/src/CustomControls/TGradientButton/wxGradientSwitch.cpp /home/buster/MyProject/src/CustomControls/TPacket/cPacchetto.cpp /home/buster/MyProject/src/CustomControls/TPacket/TPacket.cpp /home/buster/MyProject/src/CustomControls/TPacket/cSigaretta.cpp /home/buster/MyProject/src/CustomControls/TScannerImage/TScannerImage.cpp /home/buster/MyProject/src/CustomControls/TScannerImage/cResampler.cpp /home/buster/MyProject/src/CustomControls/TScannerImage/resampler.cpp /home/buster/MyProject/src/CustomControls/TSigaTollerance/TSigaTolerance.cpp /home/buster/MyProject/src/CustomControls/wxActiveArea/wxActiveArea.cpp /home/buster/MyProject/src/CustomControls/wxPanelSensorBar/wxPanelSensorBar.cpp /home/buster/MyProject/src/CustomControls/wxPngAnimator/wxpnganimator.cpp /home/buster/MyProject/src/GUI_MainformParts/GUI_XMLManager.cpp /home/buster/MyProject/src/GUI_MainformParts/GUI_MSXT.cpp /home/buster/MyProject/src/GUI_MainformParts/GUI_MSXT (2).cpp /home/buster/MyProject/src/GUI_MainformParts/GUI_UnusedControlEvents.cpp /home/buster/MyProject/src/GUI_MainformParts/GUI_GraphReader.cpp /home/buster/MyProject/src/GUI_MainformParts/GUI_TextBoxManagement.cpp /home/buster/MyProject/src/GUI_MainformParts/GUI_Charts.cpp /home/buster/MyProject/src/GUI_MainformParts/GUI_FileBrowser.cpp /home/buster/MyProject/src/GUI_MainformParts/GUI_RealTimeClock.cpp /home/buster/MyProject/src/GUI_MainformParts/GUI_SystemPages.cpp /home/buster/MyProject/src/GUI_MainformParts/GUI_KeysManagement.cpp /home/buster/MyProject/src/GUI_MainformParts/_DEBUG_STUFFS.cpp /home/buster/MyProject/src/I2C/24cXX.c /home/buster/MyProject/src/I2C/24cXX.cpp /home/buster/MyProject/src/I2C/i2cfunc.cpp /home/buster/MyProject/src/MsxData/MsxGraph.cpp /home/buster/MyProject/src/MSX_ClassParts/MSX_SendCommands.cpp /home/buster/MyProject/src/MSX_ClassParts/cTendenzaScarti.cpp /home/buster/MyProject/src/MSX_ClassParts/CCD_Averages.cpp /home/buster/MyProject/src/MSX_ClassParts/MSX_SalvaGrafici.cpp /home/buster/MyProject/src/Utilities/Utilities.cpp /home/buster/MyProject/src/Utilities/StringUtils.cpp /home/buster/MyProject/src/Utilities/cGraphFile.cpp
Object files:
cKeyboard.o GUI_MainForm.o MyProject_bitmaps.o CommManager.o cMSX.o fMainForm.o MSX_PG.o MsgManager.o cBaseObjWithEvents.o cMSXTool.o X_OS_Socket.o serialib.o BBBSerial.o cCCDCal.o TAntiAliasedGauge.o wxGradientButton.o wxGradientSwitch.o cPacchetto.o TPacket.o cSigaretta.o TScannerImage.o cResampler.o resampler.o TSigaTolerance.o wxActiveArea.o wxPanelSensorBar.o wxpnganimator.o GUI_XMLManager.o GUI_MSXT.o GUI_MSXT (2).o GUI_UnusedControlEvents.o GUI_GraphReader.o GUI_TextBoxManagement.o GUI_Charts.o GUI_FileBrowser.o GUI_RealTimeClock.o GUI_SystemPages.o GUI_KeysManagement.o _DEBUG_STUFFS.o 24cXX.o 24cXX.o i2cfunc.o MsxGraph.o MSX_SendCommands.o cTendenzaScarti.o CCD_Averages.o MSX_SalvaGrafici.o Utilities.o StringUtils.o cGraphFile.o
我显然错过了一些东西,但我无法排序。 我该怎么办?
此模式规则:
%.o: %.cpp
@echo "Compiling C file: $<"
$(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@
表示:对于匹配
<SOMETHING>.c
的文件,将文件 <SOMETHING>.o
制作为
运行每行的扩展:
@echo "Compiling C file: $<"
$(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@
作为 shell 命令。
您的第一个源文件,如您的
make show
输出所示,是:
/home/buster/MyProject/src/cKeyboard.cpp
因此模式规则告诉
make
如何制作:
/home/buster/MyProject/src/cKeyboard.o
但是您的
$(EXE)
目标所需的第一个目标文件,也如图所示
通过 make show
,是:
cKeyboard.o
(=
./cKeyboard.o
) 并且没有规则可以做到这一点。此外,
你不想成功。您要制作的文件:
/home/buster/MyProject/src/cKeyboard.cpp
显然是:
$(OBJ_PATH)/cKeyboard.o
= ~/MyProject/LINUXmake/obj-arm
= /home/buster/MyProject/LINUXmake/obj-arm/cKeyboard.o
所以至少实现这一点的模式规则是:
$(OBJ_PATH)/%.o: $(SRC_PATH)/%.cpp
@echo "Compiling C file: $<"
$(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@
显然,同样的错误会影响
%.o: %.c
的规则。
但是,你为什么要生成:
$(OBJ) = cKeyboard.o ...
而不是你需要的:
$(OBJ) = ~/MyProject/LINUXmake/obj-arm/cKeyboard.o ...
?这让我们想到:
OBJ := $(patsubst %.c,$(OBJ_PATH)/%.o,$(notdir $(SRC:.c=.o)))
OBJ := $(patsubst %.cpp,$(OBJ_PATH)/%.o,$(notdir $(OBJ:.cpp=.o)))
为了简单起见,我们假设:
/home/buster/MyProject/src/cKeyboard.cpp
是唯一的源文件。那么第一个
OBJ
作业意味着:找到展开式中的所有单词:
$(notdir $(SRC:.c=.o))
= $(notdir $(/home/buster/MyProject/src/cKeyboard.cpp:.c=.o)
= $(notdir /home/buster/MyProject/src/cKeyboard.cpp) # Because there was no `.c=.o` substitution
= cKeyboard.cpp
然后替换结果中的任何单词:
cKeyboard.cpp
将
<SOMETHING>.c
与 $(OBJ_PATH)/<SOMETHING>.o
相匹配。里面没有字:
cKeyboard.cpp
那场比赛
<SOMETHING>.c
,所以第一个 OBJ
作业的最终结果
仍然存在
cKeyboard.cpp
第二个
OBJ
作业的意思是:找到以下位置的所有单词:
$(notdir $(OBJ:.cpp=.o))
= $(notdir $(cKeyboard.cpp:.cpp=.o)
= $(notdir cKeyboard.o)
= cKeyboard.o
然后替换结果中的任何单词:
cKeyboard.o
将
<SOMETHING>.cpp
与 $(OBJ_PATH)/<SOMETHING>.o
相匹配。没有
中的单词:
cKeyboard.o
匹配
<SOMETHING>.cpp
,所以第二个 OBJ
作业的最终结果
剩下:
cKeyboard.o
错误。
获得正确结果的一种方法是:
COBJ := $(patsubst %.c,$(OBJ_PATH)/%.o,$(notdir $(filter %.c,$(SRC))))
CXXOBJ := $(patsubst %.cpp,$(OBJ_PATH)/%.o,$(notdir $(filter %.cpp,$(SRC))))
OBJ := $(COBJ) $(CXXOBJ)
顺便说一句,我建议您通常不要采取这种“抓住任何东西”的方法来
提供 make
应考虑并指定的源文件
他们分别:
SRCS := $(MyProject_PATH)src/cKeyboard.cpp \
$(MyProject_PATH)src/GUI_MainForm.cpp \
...
即使这看起来很艰难且乏味,如果你起步较晚的话。完成最初繁琐的列表后,添加新文件或删除多余的文件 很容易。构建系统应该明确并清楚地显示 对于维护人员来说,到底应该存在哪些源文件,而不是构建 任何程序可能由某些人捕获的任何源文件产生 某种拖网 - 或者未能实现这一目标。