C ++ Makefile,是否可以将其分解为更多? [英] C++ Makefile, is it possible to factorize it even more?

查看:78
本文介绍了C ++ Makefile,是否可以将其分解为更多?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个学校项目,我想编写一个Makefile,我看到了一些示例,这些示例将Makefile与多个源目录和多个可执行文件一起使用,但仍无法正确地将其实现到我的Makefile中.

I have a project for school and I want to write a Makefile, I have seen some examples of using Makefile with multiple source directories and multiple executables but still could not implement it properly to my Makefile.

PS:我正在使用doctest进行单元测试(但无法更改).

这是项目结构(我无法更改):

Here is the project structure (and I can't change it):

.
├── bin
├── build
├── extern
│   └── doctest.h
├── include
│   ├── file1.hpp
│   └── file2.hpp
├── src
│   ├── file1.cpp
│   └── file2.cpp
├── tests
│   ├── file1-test.cpp
│   └── file2-test.cpp
└──  Makefile

我有以下目录:

  • bin :用于所有可执行文件.

  • bin: for all the executables.

build :用于所有对象( .o ).

build: for all the objects (.o).

extern :用于doctest标头(这是我将存储任何其他库的位置)

extern: for the doctest header (this is where I would have stored any other library)

include :用于所有标头( .hpp ).

include: for all the headers (.hpp).

src :用于所有类( .cpp ).

tests :用于所有单元测试(也是 .cpp )

tests: for all the unit tests (also .cpp)

您可以将 file1.cpp 作为类,将 file1.hpp 作为类头,将 file1-test.cpp 作为单元对课程进行测试.

You can see file1.cpp as a class, file1.hpp as the class header and file1-test.cpp as the unit tests for the class.

这是我的Makefile:

Here is my Makefile:

BIN_DIR := bin/
BUILD_DIR := build/
EXTERN_DIR := extern/
INCLUDE_DIR := include/
SOURCE_DIR := src/
TESTS_DIR := tests/
DEP_DIR := .dep/

DEPENDS := $(patsubst %.o, $(BUILD_DIR)$(DEP_DIR)%.d, $(notdir $(wildcard $(BUILD_DIR)*.o)))

EXE := $(addprefix $(BIN_DIR), file1-test file2-test)

OBJS_1 := $(addprefix $(BUILD_DIR), file1.o)
OBJS_2 := $(addprefix $(BUILD_DIR), file1.o file2.o)

CXX := clang++
CXXFLAGS := -Wall -std=c++11 -g -O3 -I$(INCLUDE_DIR) -I$(EXTERN_DIR)

vpath %.cpp $(SOURCE_DIR) $(TESTS_DIR)

.PHONY: all clean

all: $(EXE)

$(BUILD_DIR) $(BIN_DIR) $(BUILD_DIR)$(DEP_DIR):
    @mkdir -p $@

$(BUILD_DIR)%.o: %.cpp | $(BUILD_DIR) $(BUILD_DIR)$(DEP_DIR)
    @$(CXX) $(CXXFLAGS) -MMD -MP -MF $(BUILD_DIR)$(DEP_DIR)$(notdir $(basename $@).d) -c $< -o $@

$(BIN_DIR)%: $(BUILD_DIR)%.o | $(BIN_DIR)
    @$(CXX) -o $@ $^

$(BIN_DIR)file1-test: $(OBJS_1)
$(BIN_DIR)file2-test: $(OBJS_2)

.PRECIOUS: $(BUILD_DIR)%.o

-include $(DEPENDS)

clean:
    -rm -rf $(BIN_DIR) $(BUILD_DIR)

所以我的问题是:

  • 我的Makefile是否遵循良好习惯?

  • Is my Makefile following good practices ?

是否已优化?如果没有,我该如何做得更好?

Is it optimized ? If no, how can I make it even better ?

对于每个新的可执行文件,我必须添加一个 OBJS_X 变量和一个目标 $(BIN_DIR)fileX-test:$(OBJS_X),我可以摆脱它?如果可以,那么有人可以给我写一些通用规则,这样我就不需要每次都想要一个新的可执行文件时就指定一个变量和一个目标.

For every new executable I've to add a OBJS_X variable and a target $(BIN_DIR)fileX-test: $(OBJS_X), can i get rid of it ? If yes can someone write me some generic rule, so I don't have to specify a variable and a target every time I want a new executable.

如果只想编译一个可执行文件,则必须使用 make bin/fileX-test .是否可以仅运行 make fileX-test 而不是 make bin/fileX-test (但仍将其构建在 bin 目录中)?我试图实现这样的规则: fileX-test:$(BIN_DIR)fileX-test ,但是它没有按我的要求工作,在编译的最后,它开始执行内置规则,而我不这样做不知道为什么.有人可以解释吗?

If I want to compile only one executable I have to use make bin/fileX-test. Is it possible to run only make fileX-test instead of make bin/fileX-test (but still building it in the bin directory) ? I tried to implement a rule like this: fileX-test: $(BIN_DIR)fileX-test but it's not working as I want, at the very end of the compilation it starts executing builtin rules and I don't know why. Can someone explain ?

最终答案:

我认为这是一个很好的答案,如果它可以在以后对某人有所帮助:

This is what I considere a good answer, if it can help someone later:

BIN_DIR := bin/
BUILD_DIR := build/
EXTERN_DIR := extern/
INCLUDE_DIR := include/
SOURCE_DIR := src/
TESTS_DIR := tests/
DEP_DIR := $(BUILD_DIR).dep/

CXX := g++
CXXFLAGS := -Wall -std=c++11 -g -O3 -I$(INCLUDE_DIR) -I$(EXTERN_DIR)
DEPFLAGS := -MMD -MP -MF $(DEP_DIR)

vpath %.cpp $(SOURCE_DIR) $(TESTS_DIR)

file1-test_OBJECTS := $(addprefix $(BUILD_DIR), file1.o)
file2-test_OBJECTS := $(addprefix $(BUILD_DIR), file1.o file2.o)

EXE := $(patsubst %_OBJECTS, %, $(filter %_OBJECTS, $(.VARIABLES)))

.PHONY: all keep help check clean $(EXE)

all: $(EXE:%=$(BIN_DIR)%)

$(foreach E, $(EXE), $(eval $(BIN_DIR)$E: $($E_OBJECTS)))
$(foreach E, $(EXE), $(eval $E: $(BIN_DIR)$E ;))

$(BUILD_DIR) $(BIN_DIR) $(DEP_DIR):
    @mkdir -p $@

$(BUILD_DIR)%.o: %.cpp | $(BUILD_DIR) $(DEP_DIR) $(BIN_DIR)
    @$(CXX) $(CXXFLAGS) $(DEPFLAGS)$(@F:.o=.d) -c $< -o $@

$(BIN_DIR)%: $(BUILD_DIR)%.o
    @$(CXX) -o $@ $^

-include $(wildcard $(DEP_DIR)*.d)

keep: $(EXE:%=$(BUILD_DIR)%.o)

clean:
    -@rm -rf $(BIN_DIR)* $(BUILD_DIR)* $(DEP_DIR)*

推荐答案

主要是你的 makefile 很不错.您可以进行一些简化,但这只是语法而已,并不是真正的性能等.

Mostly your makefile is pretty good. There are some simplifications you can make, but they're just syntax and not really performance etc.:

DEP_DIR := .dep/

您永远不会单独使用它,因此,如果将其定义更改为:

You never use this by itself so if you change its definition to:

DEP_DIR := $(BUILD_DIR).dep/

您可以简化对其的引用.

you can simplify the references to it.

DEPENDS := $(patsubst %.o, $(BUILD_DIR)$(DEP_DIR)%.d, $(notdir $(wildcard $(BUILD_DIR)*.o)))

-include $(DEPENDS)

这似乎很复杂.为什么不摆脱DEPENDS而只写:

this seems complex. Why not get rid of DEPENDS and just write:

include $(wildcard $(DEP_DIR)*.d)

这个:

@$(CXX) $(CXXFLAGS) -MMD -MP -MF $(BUILD_DIR)$(DEP_DIR)$(notdir $(basename $@).d) -c $< -o $@

也很复杂.您可以将其编写为(如果只是 DEP_DIR )为:

is also complex. You can write it (if you simply DEP_DIR) as:

@$(CXX) $(CXXFLAGS) -MMD -MP -MF $(DEP_DIR)$(@F:.o=.d) -c $< -o $@

对于:

.PRECIOUS: $(BUILD_DIR)%.o

我绝对不会使用它. .PRECIOUS 应该很少使用(如果有的话).如果要避免将目标文件视为中间文件,则最好直接将它们作为先决条件列出,例如:

I would definitely NOT use this. .PRECIOUS should be rarely, if ever, used. If you're trying to avoid object files being considered intermediate it's best to just list them directly as prerequisites, such as:

keep : $(EXE:$(BIN_DIR)%=$(BUILD_DIR)%.o)

但除非您有特殊需要查看这些目标文件,否则让 make 删除它们也无妨.

But unless you have special need to look at these object files it doesn't hurt to let make delete them.

关于快捷方式的问题:看到您的行为的原因是您的目标定义:

Regarding your question about shortcuts: the reason you see the behavior you do is that your target definition:

fileX-test: $(BIN_DIR)fileX-test

没有附加配方,因此make会尝试使用隐式规则查找配方.它会找到%:%.c 的内置配方,并且由于您设置了 vpath ,因此它可以找到匹配的%.c 文件,所以它使用它.为了避免这种情况,您可以给一个空的食谱.将上面的内容替换为:

has no recipe attached to it, so make will try to find a recipe using an implicit rule. It finds built-in recipe for % : %.c, and because you set vpath it can find a %.c file that matches, so it uses it. To avoid this you can just give an empty recipe; replace the above with:

fileX-test: $(BIN_DIR)fileX-test ;

(请注意添加分号).

您的主要问题是如何简化此操作:

Your main question is how to simplify this:

EXE := $(addprefix $(BIN_DIR), file1-test file2-test)

OBJS_1 := $(addprefix $(BUILD_DIR), file1.o)
OBJS_2 := $(addprefix $(BUILD_DIR), file1.o file2.o)

all: $(EXE)

$(BIN_DIR)file1-test: $(OBJS_1)
$(BIN_DIR)file2-test: $(OBJS_2)

您可以自动执行此操作,但这样做需要了解GNU make的更深入的部分.您可能会发现这组博客文章很有趣: http://make.mad-scientist.net/category/metaprogramming/(从最底部/最旧的位置开始,然后向上移动).

You can do this automatically but doing so requires knowing the deeper parts of GNU make. You might find this set of blog posts interesting: http://make.mad-scientist.net/category/metaprogramming/ (start with the bottom / oldest and work your way up).

将以上内容替换为:

# Write one of these for each program you need:

file1-test_OBJECTS = file1.o
file2-test_OBJECTS = file1.o file2.o

# Now everything below here is boilerplate

EXE = $(patsubst %_OBJECTS,%,$(filter %_OBJECTS,$(.VARIABLES)))

all: $(EXE:%=$(BIN_DIR)%)

$(foreach E,$(EXE),$(eval $(BIN_DIR)$E: $($E_OBJECTS)))
$(foreach E,$(EXE),$(eval $E: $(BIN_DIR)$E ;))
.PHONY: $(EXE)

这篇关于C ++ Makefile,是否可以将其分解为更多?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆