Linux Makefile之优化

news/2024/9/25 13:22:09/

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/news/1450292.html

相关文章

Matlab|二阶锥松弛在配电网最优潮流计算中的应用

目录 一、主要内容 二、部分代码 三、程序代码 四、下载链接 一、主要内容 最优潮流计算是电网规划、优化运行的重要基础。首先建立了配电网全天有功损耗最小化的最优潮流计算模型&#xff1b;其次结合辐射型配电网潮流特点建立支路潮流约束&#xff0c;并考虑配电网中的可…

TCP/IP网络模型各层简介

第一张图是OSI参考模型&#xff0c;由国际标准组织ISO定义的理论模型。 第二张则是TCP/IP协议的网络模型&#xff0c;实际互联网所采用的网络协议族的基础架构&#xff0c;工作中也一般都是接触这个。 第三章是学习时一般使用的网络模型&#xff0c;尤其是《计算机网络-自顶向…

【多维动态规划】Leetcode 72. 编辑距离【中等】

编辑距离 给你两个单词 word1 和 word2&#xff0c; 请返回将 word1 转换成 word2 所使用的最少操作数 。 你可以对一个单词进行如下三种操作&#xff1a; 插入一个字符删除一个字符替换一个字符 示例 1&#xff1a; 输入&#xff1a;word1 “horse”, word2 “ros” 输…

Linux 学习 --- 编辑 vi 命令

1、vi 基本概念&#xff08;了解&#xff09; 基本上 vi 可以分为三种状态&#xff0c;分别是命令模式 (command mode)、插入模式 (Insert mode) 和底行模式 (last line mode)&#xff0c;各模式的功能区分如下: 命令行模式 command mode&#xff09;  控制屏幕光标的移动&a…

如何从0开始创建一个python+Pdm+Django项目

1、安装pdm pip3.10 install pdm或者 pip install pdm2、初始化python项目的配置和环境 pdm init3、在项目中添加 Django 框架 pdm add django4、当前目录创建一个叫做Tesla的Django项目 pdm run django-admin startproject Tesla ./如图 5、编辑pyproject.toml文件&#xff…

LPO vs CPO:谁是数据中心光互连的领跑者?

在不断扩大的数据中心领域&#xff0c;速度和效率至关重要&#xff0c;光互连的主导地位之争已经到了关键时刻。激光相控振荡器&#xff08;LPO&#xff09;和相干相控振荡器&#xff08;CPO&#xff09;这两项强大的技术已成为彻底改变数据中心光互连竞赛的主要竞争者。本文深…

C语言 | Leetcode C语言题解之第64题最小路径和

题目&#xff1a; 题解&#xff1a; int minPathSum(int** grid, int gridSize, int* gridColSize) {int rows gridSize, columns gridColSize[0];if (rows 0 || columns 0) {return 0;}int dp[rows][columns];dp[0][0] grid[0][0];for (int i 1; i < rows; i) {dp[i…

Spark-机器学习(8)分类学习之随机森林

在之前的文章中&#xff0c;我们学习了分类学习之支持向量机决策树支持向量机&#xff0c;并带来简单案例&#xff0c;学习用法。想了解的朋友可以查看这篇文章。同时&#xff0c;希望我的文章能帮助到你&#xff0c;如果觉得我的文章写的不错&#xff0c;请留下你宝贵的点赞&a…