Linux Makefile之优化

11 篇文章 0 订阅
4 篇文章 0 订阅

1 概述

  前面写了两篇关于Makefile的文章Linux Makefile编写之静态库Linux Makefile编写之可执行程序.虽然编译没有问题,但还有优化的空间。

2 优化

优化列表:

  • 目标文件放入单独目录。
  • 隐藏编译命令。
  • 增加头文件依赖。
  • 增量编译,只编译修改部分。
  • 将生成lib和exe部分代码提取到单独文件,Makefile直接引用。

3 Makefile实例

这里以CppCmd(C++写的命令行系统)为例,代码结构:

cppcmd1.0.0$ tree
.
├── Makefile
├── inc
│   └── cppcmd.h
├── mkfiles
│   ├── exe.mk
│   └── lib.mk
├── src
│   ├── Makefile
│   ├── cmdhelper.h
│   ├── cmdio.cpp
│   ├── cmdio.h
│   └── cppcmd.cpp
└── test
    ├── Makefile
    ├── cmdtest.cpp
    ├── cmdtest.h
    ├── inc
    │   └── cpptest
    │       ├── cpptest-assert.h
    │       ├── cpptest-collectoroutput.h
    │       ├── cpptest-compileroutput.h
    │       ├── cpptest-htmloutput.h
    │       ├── cpptest-output.h
    │       ├── cpptest-source.h
    │       ├── cpptest-suite.h
    │       ├── cpptest-textoutput.h
    │       ├── cpptest-time.h
    │       └── cpptest.h
    ├── lib
    │   └── libcpptest.a
    └── test.cpp

7 directories, 24 files

3.1 Makefile

all:
	@make -C src
	@make -C test

clean:
	@make -C src clean
	@make -C test clean

run:
	@./bin/test

.PHNOY: all clean test

说明:

  • 根目录Makefile调用src和test目录下Makefile进行编译。

3.2 src/Makefile

这个Makefile编译src目录下文件并生成lib库。

PROJECT_NAME ?= cppcmd

PWD := $(shell pwd)
TOP := $(PWD)/..
LIBMKFILE := $(TOP)/mkfiles/lib.mk
INCS := -I$(TOP)/inc
SRCDIR := $(TOP)/src
LIBDIR := $(TOP)/lib
OBJDIR := $(PWD)/.obj
INCFILES := $(SRCDIR)/cmdhelper.h 
LIBNAME := $(LIBDIR)/lib$(PROJECT_NAME).a

CFLAGS := 
C++FLAGS := -std=c++11

include $(LIBMKFILE)

说明:

  • 定义lib.mk文件路径 LIBMKFILE
  • 定义include路径 INCS
  • 定义src路径 SRCDIR
  • 定义lib库存放路径 LIBDIR
  • 定义.o文件存放路径 OBJDIR
  • 定义依赖头文件cmdhelper.h,增加依赖头文件后,头文件修改后,make时会自动编译引用该头文件的源文件。
  • 定义生成库名称 LIBNAME
  • 定义C编译选项 CFLAGS
  • 定义C++编译选项 C++FLAGS
  • 引入生成lib库Makefile片段文件lib.mk

3.3 mkfiles/lib.mk

生成lib库文件的Makefile片段.

CC ?= gcc
CXX ?= g++
AR ?= ar
ECHO ?= echo
MAKE ?= make

LIBFLAGS := -rcD

CSRC := $(wildcard $(SRCDIR)/*.c)
OBJS := $(patsubst %.c, $(OBJDIR)/%.o, $(notdir $(CSRC)))

CPPS := $(wildcard $(SRCDIR)/*.cpp)
CPPOBJS := $(patsubst %.cpp, $(OBJDIR)/%.o, $(notdir $(CPPS)))

all: $(LIBNAME)
	@make -ts --no-print-directory

$(LIBNAME): $(OBJS) $(CPPOBJS) $(LIBDIR)
	@$(ECHO) Ar $(LIBNAME)
	@$(AR) $(LIBFLAGS) $(LIBNAME) $(OBJS) $(CPPOBJS) 

$(OBJS): $(OBJDIR)/%.o:$(SRCDIR)/%.c $(INCFILES) $(OBJDIR)
	@$(ECHO) Cc $@
	@$(CC) -c $(CFLAGS) $(INCS) $< -o $@

$(CPPOBJS): $(OBJDIR)/%.o:$(SRCDIR)/%.cpp $(INCFILES) $(OBJDIR) 
	@$(ECHO) C++ $@
	@$(CXX) -c $(C++FLAGS) $(INCS) $< -o $@

$(LIBDIR):
	@mkdir -p $(LIBDIR)

$(OBJDIR):
	@mkdir -p $(OBJDIR)

.PHNOY: clean
clean:
ifeq ($(wildcard $(OBJDIR)), $(OBJDIR))
	@rm -rf $(OBJDIR)
endif
	@rm -f $(LIBNAME)

3.3.1 代码分析

3.3.1.1 变量定义
CC ?= gcc
CXX ?= g++
AR ?= ar
ECHO ?= echo
MAKE ?= make

LIBFLAGS := -rcD

说明:

  • 定义编译器等程序名称。
  • 定义生成库选项。
3.3.1.2 自动选择译源文件
CSRC := $(wildcard $(SRCDIR)/*.c)
OBJS := $(patsubst %.c, $(OBJDIR)/%.o, $(notdir $(CSRC)))

CPPS := $(wildcard $(SRCDIR)/*.cpp)
CPPOBJS := $(patsubst %.cpp, $(OBJDIR)/%.o, $(notdir $(CPPS)))

说明:

  • 调用函数wildcard扫描src下所有.c/.cpp文件
  • 调用函数patsubst通过源文件生成.o目标文件,注意目标文件放在OBJDIR目录下
3.3.1.3 增量编译
all: $(LIBNAME)
	@make -ts --no-print-directory

说明:

  • 通过-t选项touch目标文件,更新目标文件日期,防止重新编译。
3.3.1.4 编译依赖项
$(LIBNAME): $(OBJS) $(CPPOBJS) $(LIBDIR)
	@$(ECHO) Ar $(LIBNAME)
	@$(AR) $(LIBFLAGS) $(LIBNAME) $(OBJS) $(CPPOBJS) 

$(OBJS): $(OBJDIR)/%.o:$(SRCDIR)/%.c $(INCFILES) $(OBJDIR)
	@$(ECHO) Cc $@
	@$(CC) -c $(CFLAGS) $(INCS) $< -o $@

$(CPPOBJS): $(OBJDIR)/%.o:$(SRCDIR)/%.cpp $(INCFILES) $(OBJDIR) 
	@$(ECHO) C++ $@
	@$(CXX) -c $(C++FLAGS) $(INCS) $< -o $@

$(LIBDIR):
	@mkdir -p $(LIBDIR)

$(OBJDIR):
	@mkdir -p $(OBJDIR)

.PHNOY: clean
clean:
ifeq ($(wildcard $(OBJDIR)), $(OBJDIR))
	@rm -rf $(OBJDIR)
endif
	@rm -f $(LIBNAME)

说明:

  • $(OBJS)依赖项编译.c文件为.o文件,同时依赖 $(INCFILES)和 $(OBJDIR)
  • $(CPPOBJS)依赖项编译.cpp文件为.o文件,同时依赖 $(INCFILES)和 $(OBJDIR)
  • $(LIBDIR)依赖项创建目录lib
  • $(OBJDIR)依赖项创建目录.obj
  • $(LIBNAME) 依赖项将.o文件生成lib文件。
  • clean依赖项删除编译生成.o和.a文件。

3.4 test/Makefile

这个Makefile编译test目录下文件并生成exe。

PROJECT_NAME := test

PWD := $(shell pwd)
TOP := $(PWD)/..
EXEMKFILE := $(TOP)/mkfiles/exe.mk
INCS := -I$(TOP)/inc -I$(PWD)/inc
SRCDIR := $(PWD)
OBJDIR := $(PWD)/.obj
BINDIR := $(TOP)/bin
INCFILES := $(SRCDIR)/cmdtest.h
LIBS :=  $(TOP)/lib/libcppcmd.a $(PWD)/lib/libcpptest.a
APPNAME := $(BINDIR)/$(PROJECT_NAME)

CFLAGS := 
C++FLAGS := -std=c++11
LINKFLAGS :=

include $(EXEMKFILE)
  • 定义exe.mk文件路径 EXEMKFILE
  • 定义include路径 INCS
  • 定义src路径 SRCDIR
  • 定义.o文件存放路径 OBJDIR
  • 定义exe文件存放路径 BINDIR
  • 定义依赖头文件cmdtest.h, 增加依赖头文件后,头文件修改后,make时会自动编译引该用头文件的源文件。
  • 定义生成exe名称 APPNAME
  • 定义C编译选项 CFLAGS
  • 定义C++编译选项 C++FLAGS
  • 定义链接选项 LINKFLAGS
  • 引入生成exe库Makefile片段文件exe.mk

3.5 mkfiles/exe.mk

生成exe文件的Makefile片段。

CC ?= gcc
CXX ?= g++
AR ?= ar
ECHO ?= echo

CSRC := $(wildcard $(SRCDIR)/*.c)
OBJS := $(patsubst %.c, $(OBJDIR)/%.o, $(notdir $(CSRC)))

CPPS := $(wildcard $(SRCDIR)/*.cpp)
CPPOBJS := $(patsubst %.cpp, $(OBJDIR)/%.o, $(notdir $(CPPS)))

all: $(APPNAME)
	@make -ts --no-print-directory

$(APPNAME): $(OBJS) $(CPPOBJS) $(LIBS) $(BINDIR)
	@$(ECHO) Link $(APPNAME)
	@$(CXX) $(OBJS) $(CPPOBJS) $(LIBS) $(LINKFLAGS) -o $(APPNAME)

$(OBJS): $(OBJDIR)/%.o:$(SRCDIR)/%.c $(INCFILES) $(OBJDIR)
	@$(ECHO) Cc $@
	@$(CC) -c $(CFLAGS) $(INCS) $< -o $@

$(CPPOBJS): $(OBJDIR)/%.o:$(SRCDIR)/%.cpp  $(INCFILES) $(OBJDIR)
	@$(ECHO) C++ $@
	@$(CXX) -c $(C++FLAGS) $(INCS) $< -o $@

$(OBJDIR):
	@mkdir -p $(OBJDIR)

$(BINDIR):
	@mkdir -p $(BINDIR)

.PHNOY: clean
clean:
ifeq ($(wildcard $(OBJDIR)), $(OBJDIR))
	@rm -rf $(OBJDIR)
endif
	@rm -f $(APPNAME)

说明:

  • 参考上面lib.mk说明。

4 运行

4.1 编译

4.1.1 编译代码隐藏编命令

cppcmd1.0.0$ make
make[1]: Entering directory '/home/james/git/cppcmd1.0.0/src'
C++ /home/james/git/cppcmd1.0.0/src/.obj/cppcmd.o
C++ /home/james/git/cppcmd1.0.0/src/.obj/cmdio.o
Ar /home/james/git/cppcmd1.0.0/src/../lib/libcppcmd.a
make[1]: Leaving directory '/home/james/git/cppcmd1.0.0/src'
make[1]: Entering directory '/home/james/git/cppcmd1.0.0/test'
C++ /home/james/git/cppcmd1.0.0/test/.obj/cmdtest.o
C++ /home/james/git/cppcmd1.0.0/test/.obj/test.o
Link /home/james/git/cppcmd1.0.0/test/../bin/test
make[1]: Leaving directory '/home/james/git/cppcmd1.0.0/test'

4.1.2 编译后重新make,由于代码没有修改多以没有修改。

cppcmd1.0.0$ make
make[1]: Entering directory '/home/james/git/cppcmd1.0.0/src'
make[1]: 'all' is up to date.
make[1]: Leaving directory '/home/james/git/cppcmd1.0.0/src'
make[1]: Entering directory '/home/james/git/cppcmd1.0.0/test'
make[1]: 'all' is up to date.
make[1]: Leaving directory '/home/james/git/cppcmd1.0.0/test'

4.1.3 修改头文件cmdhelper.h,相关联代码都编译了

cppcmd1.0.0$ make
make[1]: Entering directory '/home/james/git/cppcmd1.0.0/src'
C++ /home/james/git/cppcmd1.0.0/src/.obj/cppcmd.o
C++ /home/james/git/cppcmd1.0.0/src/.obj/cmdio.o
Ar /home/james/git/cppcmd1.0.0/src/../lib/libcppcmd.a
make[1]: Leaving directory '/home/james/git/cppcmd1.0.0/src'
make[1]: Entering directory '/home/james/git/cppcmd1.0.0/test'
Link /home/james/git/cppcmd1.0.0/test/../bin/test
make[1]: Leaving directory '/home/james/git/cppcmd1.0.0/test'

4.2 清理

cppcmd1.0.0$ make clean
make[1]: Entering directory '/home/james/git/cppcmd1.0.0/src'
make[1]: Leaving directory '/home/james/git/cppcmd1.0.0/src'
make[1]: Entering directory '/home/james/git/cppcmd1.0.0/test'
make[1]: Leaving directory '/home/james/git/cppcmd1.0.0/test'
  • 8
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

flysnow010

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值