Linux Makefile之优化

server/2024/9/25 13:25:37/

1 概述

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

2 优化

优化列表:

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

Makefile_9">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.cpp7 directories, 24 files

Makefile_48">3.1 Makefile

all:@make -C src@make -C testclean:@make -C src clean@make -C test cleanrun:@./bin/test.PHNOY: all clean test

说明:

Makefile_65">3.2 src/Makefile

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

PROJECT_NAME ?= cppcmdPWD := $(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).aCFLAGS := 
C++FLAGS := -std=c++11include $(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 ?= makeLIBFLAGS := -rcDCSRC := $(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 ?= makeLIBFLAGS := -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文件。

Makefile_209">3.4 test/Makefile

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

PROJECT_NAME := testPWD := $(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 ?= echoCSRC := $(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'

http://www.ppmy.cn/server/24093.html

相关文章

发送json 发送对象

文章目录 发送jsonlogin.htmlUserController 发送对象login.htmlUserController 发送json login.html <!-- 登录 --> <!DOCTYPE html> <html lang"ch"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compati…

一款pdf工具

下载链接&#xff1a;点击跳转&#xff1b; 它是一个installer&#xff0c;下好它之后&#xff0c;把网断掉&#xff0c;然后双击它&#xff0c;他会默认安装在C盘&#xff0c;安装时&#xff0c;浏览器可能会有一个弹窗&#xff0c;直接关掉并进入任务管理器杀掉所有smallerp…

k8s复制pod里的文件到宿主机

要在Kubernetes中复制Pod里的文件到宿主机&#xff0c;可以使用kubectl cp命令。以下是一个基本的命令格式和示例&#xff1a; 命令格式: kubectl cp <namespace>/<pod-name>:/path/to/file/in/pod /path/to/target/location/on/host -c <container-name> …

盲人手机导航:科技之光引领无障碍出行新纪元

在这个日新月异的数字时代&#xff0c;科技不仅改变了我们获取信息的方式&#xff0c;更在无声中拓宽了视障人士的生活半径。盲人手机导航这一创新技术&#xff0c;正逐步成为他们探索世界、实现独立出行的重要伙伴。 对于大多数人而言&#xff0c;日常出行或许只是一次…

面向对象编程三大特征:封装、继承、多态

封装、继承、多态 1. 封装 1.1 介绍 封装(encapsulation)就是把抽象出的数据 [属性] 和对数据的操作 [方法] 封装在一起,数据被保护在内部,程序的其它部分只有通过被授权的操作 [方法] ,才能对数据进行操作。 1.2 封装的理解和好处 1) 隐藏实现细节:方法(连接数据库)<…

Zotero导入参考文献详细步骤

步骤一&#xff1a; 下载Zotero 步骤二&#xff1a;安装浏览器插件 下载安装Zotero Connector插件 步骤三&#xff1a;将需要的文献导入Zotero 1、在谷歌学术或者知网等搜索需要的文献 2、点击文章标题进入详情&#xff0c;在安装好Zotero插件后&#xff0c;同时保持Zotero打…

“科技让广告更精彩”四川迈瑞斯文化传媒有限公司 行业领先的一站式媒体采购供应平台

国际数字影像产业园与园区企业一同推动数字影像技术的创新与发展&#xff0c;为数字影像产业注入新的活力。其中&#xff0c;四川迈瑞斯文化传媒有限公司&#xff08;906&#xff09;作为数字媒体行业的优秀企业&#xff0c;坚持“科技让广告更精彩”的理念&#xff0c;致力于为…

Linux学习之Tcp与Udp

目录 UDP Udp协议的格式 UDP的传输特性 UDP的缓冲区 基于UDP的应用层协议 TCP协议 TCP的报文格式 1.ACK确认应答机制 2.超时重传 3.TCP的链接管理机制 为什么要三次握手呢&#xff1f; 理解TIME_WAIT状态 流量控制&#xff08;可靠性效率&#xff09; 滑动窗口 拥塞…