Linux Makefile之优化

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

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调用src和test目录下Makefile进行编译。

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文件。

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.mzph.cn/diannao/6091.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

免安装SQL管理工具HeidiSQL建库如何选Collation字符校对

免安装SQL管理工具HeidiSQL 文章目录 免安装SQL管理工具HeidiSQL一、安装二、建库因此&#xff0c;通常我们选择&#xff1a; 一、安装 到官方网址&#xff1a;https://www.heidisql.com/ 下载后按不同版本安装或解压&#xff0c;运行目录中的heidisql应用程序。 该工具可以对…

【Linux系统编程】第十二弹---编辑器gcc/g++使用

✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】【C详解】【Linux系统编程】 目录 1、什么是gcc/g 2、gcc/g编辑器的安装 3、gcc/g编译的四个步骤 2.1、预处理 2.2、编译 2.3、汇编 2.4、链接 4、函数库 …

WSL2连接Windows主机的Mysql

文章目录 需求查看主机IP防火墙设置Mysql设置允许远程连接WSL2连接Mysql 需求 在WSL2&#xff08;本机Ubuntu20.04&#xff09;运行的程序需要将数据写入到本机的Mysql服务器中 查看主机IP 两种办法&#xff1a; Windows主机输入 ipconfig&#xff0c;找到带有WSL后缀的部分…

pytorch 实现语义分割 PSPNet

语意分割是指一张图片上包含多个物体&#xff0c;通过语义分割可以识别物体分类、物体名称、像素识别的任务。和物体检测不同&#xff0c;他不会将物体框出来&#xff0c;而是根据像素的归属把物体标注出来。PSPNet 的输入是一张图片&#xff0c;例如300500&#xff0c;那么输出…

计算机毕业设计python在线交友系统django+vue

Flask 是一个轻量级的 Web 框架&#xff0c;使用 Python 语言编写&#xff0c;较其他同类型框架更为灵活、轻便且容易上手&#xff0c;小型团队在短时间内就可以完成功能丰富的中小型网站或 Web 服务的实现。 本在线交友系统管理员功能有个人中心&#xff0c;用户管理&#xff…

OpenCV4.9去运动模糊滤镜(68)

返回:OpenCV系列文章目录&#xff08;持续更新中......&#xff09; 上一篇:OpenCV4.9失焦去模糊滤镜(67) 下一篇 :OpenCV系列文章目录&#xff08;持续更新中......&#xff09; 目标 在本教程中&#xff0c;您将学习&#xff1a; 运动模糊图像的 PSF 是多少如何恢复运动模…

【千帆平台】使用AppBuilder三步手搓应用创建精准多轮对话agent之K12互动式练习题

欢迎来到《小5讲堂》 这是《千帆平台》系列文章&#xff0c;每篇文章将以博主理解的角度展开讲解。 温馨提示&#xff1a;博主能力有限&#xff0c;理解水平有限&#xff0c;若有不对之处望指正&#xff01; 目录 前言创建应用应用头像应用名称应用描述角色指令能力扩展开场白 …

【Web】CTFSHOW 新手杯 题解

目录 easy_eval 剪刀石头布 baby_pickle repairman easy_eval 用script标签来绕过 剪刀石头布 需要赢100轮&#x1f914; 右键查看源码拿到提示 一眼session反序列化 打PHP_SESSION_UPLOAD_PROGRESS 脚本 import requestsp1 a|O:4:"Game":1:{s:3:"log…

ubuntu与redhat的不同之处

华子目录 什么是ubuntu概述 ubuntu版本简介桌面版服务器版 安装部署部署后的设置设置root密码关闭防火墙启用允许root进行ssh登录更改apt源安装所需软件 网络配置Netplan概述配置详解配置文件DHCP静态IP设置设置 软件安装方法apt安装软件作用常用命令配置apt源 deb软件包安装概…

服务器分类

服务器可以根据不同的标准进行分类&#xff0c;主要分类方式包括但不限于以下几种&#xff1a; 按应用层次划分&#xff1a; 入门级服务器&#xff1a;适用于小型企业或个人&#xff0c;处理轻量级任务。工作组级服务器&#xff1a;适合中小型企业部门&#xff0c;支持更多用户…

EasyExcel 处理 Excel

序言 本文介绍在日常的开发中&#xff0c;如何使用 EasyExcel 高效处理 Excel。 一、EasyExcel 是什么 EasyExcel 是阿里巴巴开源的一个 Java Excel 操作类库&#xff0c;它基于 Apache POI 封装了简单易用的 API&#xff0c;使得我们能够方便地读取、写入 Excel 文件。Easy…

selenium 4.x 之验证码处理(python)

验证码处理 一般情况公司如果涉及web自动化测试需要对验证码进行处理的方式一般有一下几种&#xff1a; 关闭验证码功能&#xff08;开发处理&#xff09;设置万能验证码&#xff08;开发处理&#xff09;使用智能识别库进行验证 通过第三方打码平台识别验证码 1. 跳过验证功…

【目标检测】DEtection TRansformer (DETR)

一、前言 论文&#xff1a; End-to-End Object Detection with Transformers 作者&#xff1a; Facebook AI 代码&#xff1a; DEtection TRansformer (DETR) 特点&#xff1a; 无proposal&#xff08;R-CNN系列&#xff09;、无anchor&#xff08;YOLO系列&#xff09;、无NM…

C++入门基础(三)

这里写目录标题 引用引用概念例子1例子2例子3例子4 常引用引用的应用做参数做返回值野引用扩展 传值、传引用效率比较引用和指针的区别C对比C语言实现顺序表 内联函数概念特性 &#x1f412;&#x1f412;&#x1f412; 个人主页 &#x1f978;&#x1f978;&#x1f978; C语…

分布式八股文

什么是分布式系统? 集中式系统,可以理解为将一整个系统的所有功能,包括数据库各种都部署在一起,统一向外提供服务。分布式就是将集中式系统拆分成多个系统,每一个系统单独对外提供服务,整一个提供一整套服务。意味着能够采用更多的服务器,CPU、内存、存储资源增加,能够…

[方法] Unity 解决类《原神》角色移动方向问题

第三人称视角类的游戏有很多&#xff0c;比如《原神》、《崩坏:星穹铁道》、《剑星》、《绝地求生》等。这些游戏中&#xff0c;角色的移动方向取决于玩家的输入和相机的方向&#xff0c;例如玩家在键盘上按下D键&#xff0c;则角色会相对于相机方向向右移动&#xff0c;本篇文…

dnf游戏攻略:保姆级游戏攻略!

欢迎来到DNF&#xff0c;一个扣人心弦的2D横版格斗游戏世界&#xff01;无论你是新手还是老玩家&#xff0c;这篇攻略都将为你提供宝贵的游戏技巧和策略&#xff0c;助你在游戏中大展身手&#xff0c;成为一名强大的冒险者。 一、角色选择 在DNF中&#xff0c;角色的选择至关重…

K8S join 证书过期 节点报错:certificate has expired or is not yet valid

问题场景&#xff1a; 我是因为虚拟机&#xff0c;挂起了几天&#xff0c;再打开join节点的时候报错&#xff1a; 证书过期报错 ...其他输出 I0427 15:33:56.626776 93338 token.go:215] [discovery] Failed to request cluster-info, will try again: Get "https://…

量子城域网建设案例分析(一):广西量子通信技术试验平台

对量子城域网的讨论已经有一段时间了&#xff0c;经过近期系列文章的讨论&#xff0c;我们对城域网的整体情况、关键技术以及核心设备等都有了一些基本的认识&#xff0c;今天我计划对广西量子通信技术试验平台构建与应用研究服务采购项目进行讨论&#xff0c;通过对实际案例的…

IoTDB 入门教程⑤——数据模型和基础概念

文章目录 一、前文二、数据模型2.1 关系型数据库MySQL。2.2 时序数据库TDengine2.3 时序数据库InfluxDB2.4 时序数据库IoTDB&#xff08;本专栏的正主&#xff09; 三、基础概念3.1 数据库&#xff08;Database&#xff09;3.2 设备模板&#xff08;元数据模板&#xff09;3.3 …