生成make文件的最佳实践

Dav*_*ski 2 makefile

背景:

我对Make还是比较陌生,因此我开始为我最近制作的Othello游戏构建makefile。我当前的makefile比较简单。但是,我希望进一步完善它并添加构建和存档功能。

问题1:为什么不打扫工作?

问题2:对于归档,我打算使用

# tar cvzf *.o Othello
Run Code Online (Sandbox Code Playgroud)

存档时,将编译时生成.o和可执行文件。这是一种好的标准做法还是有更好的方法呢?

问题3:对于构建部分,我计划使用./Othello运行可执行文件,这是在Makefile中进行构建的最佳实践吗?

git存储库发布在下面,谢谢您的时间

全部:奥赛罗

Othello: main.o Myothello.o space.o game.o
        clang++ main.o Myothello.o space.o game.o -o Othello

main.o: main.cc
        clang++ -c main.cc

game.o: game.cc
        clang++ -c game.cc

Myothello.o: Myothello.cc
            clang++ -c Myothello.cc

space.o: space.cc
        clang++ -c space.cc

clean:
rm -f *.o core *.core
rm MyOthello
Run Code Online (Sandbox Code Playgroud)

Max*_*kin 5

这是为您提供的示例makefile,已在github项目上进行了测试。

特征:

  1. 自动依赖项生成。
  2. Makefile更改时自动重建。
  3. 调试/发布建立在不同的目录中。调试版本是默认版本,make BUILD=release用于发布版本。
  4. 支持gccclanggcc是默认设置,make COMPILER=clang用于clang
  5. clean 目标通过删除整个构建目录来工作。
  6. run以构建和运行为目标,例如make run_othello

局限性:

  1. 假定所有源文件都在同一目录中。在更大的项目中,将有多个目录,每个目录具有多个构建目标(可执行,静态或共享库)。需要一个同构的构建目录结构来支持此功能。这完全可行,但在Makefile下面的示例中需要更多的复杂性,因此为简洁起见,将其省略。

归档时,您可能只希望归档源文件和makefile。无需包含任何构建工件(例如目标文件,库和可执行文件)。


# ==== Begin prologue boilerplate.
all : # The canonical default target.
BUILD := debug
build_dir := ${CURDIR}/${BUILD}
exes := # Executables to build.
# ==== End prologue boilerplate.

# ==== Begin define executable othello.
exes += othello
objects.othello = main.o game.o Myothello.o space.o
-include ${objects.othello:%.o=${build_dir}/%.d} # Include auto-generated dependencies.
# ==== End define executable othello.

# ==== Begin define another executable.
# ... as for othello
# ==== End define another executable.

# ==== Begin rest of boilerplate.
SHELL := /bin/bash
COMPILER=gcc

CXX.gcc := /bin/g++
CC.gcc := /bin/gcc
LD.gcc := /bin/g++
AR.gcc := /bin/ar

CXX.clang := /bin/clang++
CC.clang := /bin/clang
LD.clang := /bin/clang++
AR.clang := /bin/ar

CXX := ${CXX.${COMPILER}}
CC := ${CC.${COMPILER}}
LD := ${LD.${COMPILER}}
AR := ${AR.${COMPILER}}

CXXFLAGS.gcc.debug := -Og -fstack-protector-all
CXXFLAGS.gcc.release := -O3 -march=native -DNDEBUG
CXXFLAGS.gcc := -pthread -std=gnu++14 -march=native -W{all,extra,error} -g -fmessage-length=0 ${CXXFLAGS.gcc.${BUILD}}

CXXFLAGS.clang.debug := -O0 -fstack-protector-all
CXXFLAGS.clang.release := -O3 -march=native -DNDEBUG
CXXFLAGS.clang := -pthread -std=gnu++14 -march=native -W{all,extra,error} -g -fmessage-length=0 ${CXXFLAGS.clang.${BUILD}}

CXXFLAGS := ${CXXFLAGS.${COMPILER}}
CFLAGS := ${CFLAGS.${COMPILER}}

LDFLAGS.debug :=
LDFLAGS.release :=
LDFLAGS := -fuse-ld=gold -pthread -g ${LDFLAGS.${BUILD}}
LDLIBS := -ldl

COMPILE.CXX = ${CXX} -c -o $@ ${CPPFLAGS} -MD -MP ${CXXFLAGS} $(abspath $<)
PREPROCESS.CXX = ${CXX} -E -o $@ ${CPPFLAGS} ${CXXFLAGS} $(abspath $<)
COMPILE.C = ${CC} -c -o $@ ${CPPFLAGS} -MD -MP ${CFLAGS} $(abspath $<)
LINK.EXE = ${LD} -o $@ $(LDFLAGS) $(filter-out Makefile,$^) $(LDLIBS)
LINK.SO = ${LD} -shared -o $@ $(LDFLAGS) $(filter-out Makefile,$^) $(LDLIBS)
LINK.A = ${AR} rsc $@ $(filter-out Makefile,$^)

all : ${exes:%=${build_dir}/%} # Build all exectuables.

.SECONDEXPANSION:
# Build an executable.
${exes:%=${build_dir}/%} : ${build_dir}/% : $$(addprefix ${build_dir}/,$${objects.$$*}) Makefile | ${build_dir}
    $(strip ${LINK.EXE})

# Run an executable. E.g. make run_othello
${exes:%=run_%} : run_% : ${build_dir}/%
    @echo "---- running $< ----"
    /usr/bin/time --verbose $<

# Create the build directory on demand.
${build_dir} :
    mkdir $@

# Compile a C++ source into .o.
# Most importantly, generate header dependencies.
${build_dir}/%.o : %.cc Makefile | ${build_dir}
    $(strip ${COMPILE.CXX})

# Compile a C source into .o.
# Most importantly, generate header dependencies.
${build_dir}/%.o : %.c Makefile | ${build_dir}
    $(strip ${COMPILE.C})

clean :
    rm -rf ${build_dir}

.PHONY : clean all run_%

# ==== End rest of boilerplate.
Run Code Online (Sandbox Code Playgroud)