众所周知,C语言项目需要构建,而GNU make可谓是GNU/Linux下的正统构建管理工具了。不过由于咱之前写的Makefile太烂,所以这篇文章一直到最近有空重写ruri的Makefile后才出来。 本篇文章可能需要一定GCC/Clang基础,建议配合在Linux下优雅的调试C语言 与Clang/GCC安全编译与代码优化选项(合集) 食用。 本篇文章仅适用于GNU/Linux或Termux环境。
源码结构: 这里咱用的还是比较经典的结构,src放置源代码,src/include放置头文件,out用于放置生成的中间文件。最后将BIN_TARGET复制到源码根目录下。 clang-format文件需自备。
1 2 3 4 5 6 7 8 9 10 11 12 13 . ├── .clang-format ├── LICENSE ├── Makefile ├── hello ├── out │ ├── hello.o │ └── hello ├── README.md └── src ├── include │ └── hello.h └── hello.c
实现的功能:
代码优化与加固
静态编译支持
代码静态检测
代码格式化
GDB与ASAN调试工具支持
针对Termux(bionic)的特殊修复
命令详解: 1 2 3 4 5 6 7 8 9 make all #编译程序 make install #将程序安装到$PREFIX make static #静态编译 make static-bionic #在Termux下静态编译 make clean #清理 make dev #构建GDB调试版本 make asan #构建内置ASAN的GDB调试版本 make check #运行静态检测 make format #格式化代码
代码: C语言规范默认设置为GNU99,可视情况修改。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 .NOTPARALLEL: CCCOLOR = \033[1;38;2;254;228;208m LDCOLOR = \033[1;38;2;254;228;208m STRIPCOLOR = \033[1;38;2;254;228;208m BINCOLOR = \033[34;1m ENDCOLOR = \033[0m CC_LOG = @printf ' $(CCCOLOR) CC$(ENDCOLOR) $(BINCOLOR) %b$(ENDCOLOR)\n ' LD_LOG = @printf ' $(LDCOLOR) LD$(ENDCOLOR) $(BINCOLOR) %b$(ENDCOLOR)\n ' STRIP_LOG = @printf ' $(STRIPCOLOR) STRIP$(ENDCOLOR) $(BINCOLOR) %b$(ENDCOLOR)\n ' CLEAN_LOG = @printf ' $(CCCOLOR) CLEAN$(ENDCOLOR) $(BINCOLOR) %b$(ENDCOLOR)\n ' CC = clang STRIP = strip LTO = -flto PIE = -fPIE NX = -z noexecstack RELRO = -z now CANARY = -fstack-protector-all CLASH_PROTECT = -fstack-clash-protection SHADOW_STACK = -mshstk FORTIFY = -D_FORTIFY_SOURCE=3 -Wno-unused-result OPTIMIZE = -O2 DEBUGGER = -ggdb NO_OPTIMIZE = -O0 -fno-omit-frame-pointer NO_RELRO = -z norelro NO_NX = -z execstack NO_PIE = -no-pie NO_CANARY = -fno-stack-protector WALL = -Wall -Wextra -pedantic -Wconversion -Wno-newline-eof OPTIMIZE_CFLAGS = $(LTO) $(PIE) $(CANARY) $(CLASH_PROTECT) $(SHADOW_STACK) $(AUTO_VAR_INIT) $(FORTIFY) $(OPTIMIZE) $(STANDARD) STATIC_CFLAGS = $(OPTIMIZE_CFLAGS) -static DEV_CFLAGS = $(DEBUGGER) $(NO_OPTIMIZE) $(NO_CANARY) $(WALL) $(STANDARD) ASAN_CFLAGS = $(DEV_CFLAGS) -fsanitize=address,leak -fsanitize-recover=address,all SRC = src/*.c HEADER = src/include /*.h BIN_TARGET = hello STANDARD = -std=gnu99 -Wno-gnu-zero-variadic-macro-arguments FORMATER = clang-format -i CHECKER = clang-tidy --use-color CHECKER_FLAGS = --checks=*,-clang-analyzer-security.insecureAPI.strcpy,-altera-unroll-loops,-cert-err33-c,-concurrency-mt-unsafe,-clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling,-readability-function-cognitive-complexity,-cppcoreguidelines-avoid-magic-numbers,-readability-magic-numbers,-bugprone-easily-swappable-parameters,-cert-err34-c LD_FLAGS = $(NX) $(RELRO) -Wl,--build-id=sha1 DEV_LD_FLAGS = $(NO_RELRO) $(NO_NX) $(NO_PIE) -Wl,--build-id=sha1 BIONIC_FIX = -ffunction-sections -fdata-sections BIONIC_CFLAGS = $(OPTIMIZE_CFLAGS) $(BIONIC_FIX) BIONIC_LD_FLAGS = $(LD_FLAGS) -Wl,--gc-sections -Wl,--build-id=sha1 objects = hello.o O = out .ONESHELL: all :CFLAGS=$(OPTIMIZE_CFLAGS) all :build_dir $(objects) @cd $(O) @$(CC) $(CFLAGS) -o $(BIN_TARGET) $(objects) $(LD_FLAGS) $(LD_LOG) $(BIN_TARGET) @$(STRIP) $(BIN_TARGET) $(STRIP_LOG) $(BIN_TARGET) @cp $(BIN_TARGET) ../ dev :CFLAGS=$(DEV_CFLAGS) dev :build_dir $(objects) @cd $(O) $(LD_LOG) $(BIN_TARGET) @$(CC) $(CFLAGS) -o $(BIN_TARGET) $(objects) $(DEV_LD_FLAGS) @cp $(BIN_TARGET) ../ asan :CFLAGS=$(ASAN_CFLAGS) asan :build_dir $(objects) @cd $(O) $(LD_LOG) $(BIN_TARGET) @$(CC) $(CFLAGS) -o $(BIN_TARGET) $(objects) $(DEV_LD_FLAGS) @cp $(BIN_TARGET) ../ static :CFLAGS=$(STATIC_CFLAGS) static :build_dir $(objects) @cd $(O) $(LD_LOG) $(BIN_TARGET) @$(CC) $(CFLAGS) -o $(BIN_TARGET) $(objects) $(LD_FLAGS) $(STRIP_LOG) $(BIN_TARGET) @$(STRIP) $(BIN_TARGET) @cp $(BIN_TARGET) ../ static-bionic :CFLAGS=$(BIONIC_CFLAGS) static-bionic :build_dir $(objects) @cd $(O) $(LD_LOG) $(BIN_TARGET) @$(CC) $(CFLAGS) -o $(BIN_TARGET) $(objects) $(BIONIC_LD_FLAGS) $(STRIP_LOG) $(BIN_TARGET) @$(STRIP) $(BIN_TARGET) @cp $(BIN_TARGET) ../ build_dir: @mkdir -p $(O) $(objects) :%.o:src/%.c $(build_dir) @cd $(O) @$(CC) $(CFLAGS) -c ../$< -o $@ $(CC_LOG) $@ install :all install -m 777 $(BIN_TARGET) ${PREFIX}/bin/$(BIN_TARGET) check : @printf "\033[1;38;2;254;228;208mCheck list:\n" @sleep 1.5s @$(CHECKER) $(CHECKER_FLAGS) --list-checks $(SRC) -- $(DEV_CFLAGS) @printf ' \033[1;38;2;254;228;208mCHECK\033[0m \033[34;1m%b\033[0m\n' $(SRC) @$(CHECKER) $(CHECKER_FLAGS) $(SRC) -- $(DEV_CFLAGS) @printf ' \033[1;38;2;254;228;208mDONE.\n' format : $(FORMATER) $(SRC) $(HEADER) clean : $(CLEAN_LOG) $(O) @rm -f $(BIN_TARGET) ||true @rm -rf $(O) ||true help : @printf "\033[1;38;2;254;228;208mUsage:\n" @echo " make all compile" @echo " make install install program to \$$PREFIX" @echo " make static static compile,with musl or glibc" @echo " make static-bionic static compile,with bionic" @echo " make clean clean" @echo "Only for developers:" @echo " make dev compile without optimizations, enable gdb debug information and extra logs." @echo " make asan enable ASAN" @echo " make check run clang-tidy" @echo " make format format code" @echo "*Premature optimization is the root of all evil."
后记: 又水了一篇文章喵~