实用make最佳实践(精)

Linux安装Redis单机版 1.环境准备redis安装在Linux上,redis是c语言开发的,需要依赖以下环境。 yum -y install cpp binutils glibc glibc-kernheadersglibc-common glibc-devel gcc make gcc-c++ libstdc++-devel tcl #如果安装过则不会执行(没有规则可以创建目标“gcc-c++”。 停止)make g... 阅读详情

http://www.cnblogs.com/ggjucheng/archive/2011/12/14/2288055.html

一、前言

Make工具最主要也是最基本的功能就是通过makefile文件来描述源程序之间的相互关系并自动维护编译工作。而makefile 文件需要按照某种语法进行编写,文件中需要说明如何编译各个源文件并连接生成可执行文件,并要求定义源文件之间的依赖关系。

然而make的命令"博客精深",对于初学者来说,真是望而生畏,这篇文章不是make详解,只是讲解实用makefile的编写和使用。

在linux上,如果用gcc一个个编译源码,实在很繁琐,尤其是随着源代码的增加,这种繁琐更是明显,很多人,包括我,其实要求很简单,只要把头文件和cpp或者c文件放在当前目录或者其他目录,直接一个命令就可以编译了,但是make的命令太多了,相当打击初学者的信心。

常用的make指令其实不多,够用就行了,接下来开始讲解实用makefile的编写,看完之后大家可以直接下载make工程直接一个make命令完成编译。

本文相关环境:redhat 5.5 + g++ version 4.1.2 + GNU Make 3.81

g++可以支持cpp文件编译,gcc编译cpp文件在链接时会出现点问题,所以这里统一使用g++。

 

二、Makefile的规则

target ... : prerequisites ...
    command
    ...
    ...

comman如果和target不是同一行,需要在第二行键入\t再键入command.

target也就是一个目标文件,可以是Object File,也可以是执行文件。还可以是一个标签(Label)

这是一个文件的依赖关系,也就是说,target这一个或多个的目标文件依赖于prerequisites中的文件,其生成规则定义在command中。

prerequisites中如果有一个以上的文件比target文件要新的话,command所定义的命令就会被执行。这就是Makefile的规则。也就是Makefile中最核心的内容。

 

三、使用make一个个编译源代码

 假设我们写一个简单的程序,源码位于/usr/make/main.cpp,代码如下:

复制代码
//main.cpp
#include <stdio.h>
int main(int argc, char** argv) {
    printf("app startup\n");
    printf("app stop\n");
    return 0;
}
复制代码

make的最大好处是自动化编译,于是我们新建/usr/make/makefile,makefile文件内容如下:

main:   main.o
    g++ main.o -o main
main.o: main.cpp
    g++ -c main.cpp -o main.o
clean:
    rm -rf *.o main

保存之后,直接执行make,就可以生成main.o目标文件,main可执行文件了。

然而我们这个程序实在单薄了点,于是我们需要加入一个App类,控制整个程序的启动和关闭周期。

新建/usr/make/app.h文件,代码如下:

复制代码
#ifndef APP_H
#define APP_H 

class App{
    public:
        static App& getInstance();
        bool start();
        bool shutdown();
        
    private:
        App();
        App(const App&);
        App& operator=(const App&);
        bool m_stopped;
};

#endif
复制代码



新建/usr/make/app.cpp文件,代码如下:

复制代码
#include "app.h"

#include <stdio.h>
#include <unistd.h>
App& App::getInstance() {
       static App app;
       return app;
}

App::App() {
       m_stopped = false;
}
   
bool App::start() {
       printf("app startup\n");
       while (!m_stopped) {
            printf("app run\n");
            sleep(5);
       }
       return true;
}

   bool App::shutdown() {
       if (m_stopped == false) {
           m_stopped = true; 
       }
       return true;
}
复制代码



修改/usr/make/main.cpp文件,代码如下:

复制代码
//main.cpp
#include <stdio.h>

#include "app.h"

int main(int argc, char** argv) {
    App& app = App::getInstance();
    
    if(!app.start()) {
        printf("app start fail\n");
    }
    
    app.shutdown();
    return 0;
}
复制代码

修改/usr/make/makefile,内容如下:

复制代码
main:    main.o app.o
    g++ main.o app.o -o main
main.o:    main.cpp
    g++ -c main.cpp -o main.o
app.o:    app.cpp
    g++ -c app.cpp -o app.o
clean:
    rm -rf *.o main
复制代码




执行make,./main,发现后台会每隔5秒打印出"app run",只能ctrl+c结束.

以后我们修改了main.cpp,app.cpp,app.h文件后,我们只要输入make,就可以自动编译了,不必每次一个个g++命令去进行繁杂的编译过程。

这种方式,以后每添加一个源码文件,我们就要手工修改makefile的内容,添加依赖和命令,仍然显得不自动。

makeprojectv1下载

 

四、使用make自动推导编译

 GNU的make很强大,它可以自动推导文件以及文件依赖关系后面的命令,于是我们就没必要去在每一个[.o]文件后都写上类似的命令,因为,我们的make会自动识别,并自己推导命令。

 我们的是新的makefile又出炉了:

复制代码
CPP_SOURCES = $(wildcard *.cpp)
CPP_OBJS = $(patsubst %.cpp, %.o, $(CPP_SOURCES))

default:compile

$(CPP_OBJS):%.o:%.cpp
    g++ -c $< -o $@

compile: $(CPP_OBJS)
    g++ $^ -o main

clean:
    rm -f $(CPP_OBJS)
    rm -f main
复制代码
下面来一步步解析这个makefie的语法.
初始化:
wildcard函数功能是展开成一列所有符合由其参数描述的文件名,文件间以空格间隔,本例是产生一个所有以 '.cpp' 结尾的文件的列表,然后存入变量 CPP_SOURCES.
patsubst函数是匹配替换的函数,有三个参数,第一个是一个需要匹配的式样,第二个表示用什么来替换它,第三个是一个需要被处理的由空格分隔的字列,本例是把
CPP_SOURCES的后缀为cpp文件列表,替换成后缀为o的文件列表。
生成目标文件
$(CPP_OBJS):%.o:%.cpp
    g++ -c $< -o $@

上面的例子中,指明了我们的目标从$(CPP_OBJS)中获取,"%.o"表明要所有以".o"结尾的目标,也就是"main.o app.o",也就是变量$(CPP_OBJS)集合的模式.
而依赖模式"%.cpp"则取模式"%.o"的"%",也就是"main app",再加上后缀"cpp".于是,我们的依赖目标就是"main.cpp app.cpp"。
而命令中的"$<"和"$@"则是自动化变量,"$<"表示所有的依赖目标集(也就是"main.cpp app.cpp"),"$@"表示目标集(也就是"main.o cpp.o")。

展开之后是:

main.o: main.cpp
    g++ -c main.cpp -o main.o
main.o: app.cpp
    g++ -c app.cpp -o app.o

链接

上述的default目标是是makefile的第一个目标,依赖compile,compile文件不存在,也不会出现,所以会执行compile目标。

default和compile只是makefile一个普通的规则而已,没有特殊之处。

compile: $(CPP_OBJS)
    g++ $^ -o main

"$<"表示所有的依赖目标集,表示main.o app.o

展开之后是

compile: main.o app.o
    g++ main.o app.o -o main


这种方式,当我们在当前目录添加cpp文件,不用再改makefile,只要键入命令make就可以完成整个编译过程,这个时候我们可以感受到make的自动化编译的方便。


makeprojectv2下载

 

五、实用makefile的完善

虽然我们已经使用了makefile的自动推导,但是这个makefile还有有些不足:

1.只能支持cpp文件,不支持c文件

2.生成的目标文件全放在当前目录,造成当前目录混乱

3.h文件和cpp文件或者c文件没有分离,造成目录里的源码文件2倍爆炸,而且也不支持多目录

4.不支持第三方的库文件,包括include文件和lib文件。

于是,有一个完善的makefile如下:

复制代码
TARGET = main
OBJ_PATH = objs

CC = g++
CFLAGS = -Wall -Werror -g
LINKFLAGS =

#INCLUDES = -I include/myinclude -I include/otherinclude1 -I include/otherinclude2
INCLUDES = -I include
#SRCDIR =src/mysrcdir src/othersrc1 src/othersrc2
SRCDIR = src
#LIBS = -Llib -lcurl -Llib -lmysqlclient -Llib -llog4cpp
LIBS =

C_SRCDIR = $(SRCDIR)
C_SOURCES = $(foreach d,$(C_SRCDIR),$(wildcard $(d)/*.c) )
C_OBJS = $(patsubst %.c, $(OBJ_PATH)/%.o, $(C_SOURCES))

CPP_SRCDIR = $(SRCDIR)
CPP_SOURCES = $(foreach d,$(CPP_SRCDIR),$(wildcard $(d)/*.cpp) )
CPP_OBJS = $(patsubst %.cpp, $(OBJ_PATH)/%.o, $(CPP_SOURCES))

default:init compile

$(C_OBJS):$(OBJ_PATH)/%.o:%.c
    $(CC) -c $(CFLAGS) $(INCLUDES) $< -o $@

$(CPP_OBJS):$(OBJ_PATH)/%.o:%.cpp
    $(CC) -c $(CFLAGS) $(INCLUDES) $< -o $@

init:
    $(foreach d,$(SRCDIR), mkdir -p $(OBJ_PATH)/$(d);)

compile:$(C_OBJS) $(CPP_OBJS)
    $(CC)  $^ -o $(TARGET) $(LINKFLAGS) $(LIBS)

clean:
    rm -rf $(OBJ_PATH)
    rm -f $(TARGET)

install: $(TARGET)
    cp $(TARGET) $(PREFIX_BIN)

uninstall:
    rm -f $(PREFIX_BIN)/$(TARGET)

rebuild: clean compile
复制代码



 这个makefile的功能是扫描src文件夹的c文件和cpp文件,进行编译,编译的命令是g++,编译选项是-Wall -Werror -g,表示尽可能打印出报错信息,有错误和警告就停止编译,并且生成调试信息。如果是生产环境,请把-g去掉。最后连接所有目标文件为main。如果所有的c文件和cpp文件没有main函数入口,链接会报错。

使用这个makefile,需要注意的几个变量是:

TARGET 表示最后生成的可执行文件的名字,默认生成的main可执行文件在当前工作目录。
OBJ_PATH  表示编译过程中产生的目标文件放在那个目录,默认生成放在在当前目录的objs目录下,无需手工mkdir,自动生成。
CC 默认是g++,如果是使用gcc的,请修改,但是gcc在编译cpp文件后,链接时会出现问题,请自行解决。
INCLUDES 配置头文件的目录,默认是include目录,如果放在当前目录,可以改为"INCLUDES = .",如果要配置多个include目录,可以按照注释,自行配置。
SRCDIR 配置c文件和cpp文件的目录,默认是src目录,如果喜欢放在当前目录,可以改为"SRCDIR= .",如果要配置多个源码目录,可以按照注释,自行配置。
LIBS 配置第三方的动态链接库和静态链接库,当程序使用了第三方库时,我们编译是要配置第三方的头文件目录,链接时就要配置链接的库文件,LIBS配置可以参考注释,如果不清楚,请看本人博客里的“gcc常用命令”文章

  makeprojectv3下载



make: *** 没有规则可制作目标“defconfig-iwlwifi-public”。 停止。 输入命令: sudo make defconfig-iwlwifi-public 报错提示:make: *** 没有规则可制作目标“defconfig-iwlwifi-public”。 停止。 提示原因:make menuconfig命令需要进入内核源码目录才能使用,否则会提示以上错误 接下来安装ubuntu内核源码: 1、下载安装内核源码 终端命令: sudo apt-get install linux-source 按照提示下载最新版本的内核源码 下载的源码在/usr/src目录中,进入该目录 cd 阅读详情

相关推荐

Windows下解决‘make‘命令不可用的3种实用方法(含MinGW配置技巧)

本文详细介绍了在Windows系统下解决'make'命令不可用的3种实用方法,包括直接安装GNU Make、配置MinGW环境以及使用WSL。特别提供了MinGW配置技巧和环境变量设置指南,帮助开发者快速恢复C/C++开发工作流程。

pz890123的博客 1203

linux中将文本中的单词换掉的指令_Linux实用程序——Make快速入门

1. Makemake 是 linux 系统的实用程序。它用于管理对于大型程序的自动编译任务,自动决定程序某一部分需要重新编译,并发出编译指令。虽然,我们最常见于 C 语言程序的编译。但是,make 不限于某一特定语言,凡是可以通过 shell 命令来运行编译器的语言都可以使用 make 。除此之外,你甚至可以用 make 描述任何构建任务,这些任务中,文件需要在其依赖的文件发生变动后自...

weixin_39754831的博客 1104

GNU make/Makefile 简明实用教程

GNU make/Makefile 简明实用教程 目录: 1. 简介 2. 示例项目 3. 为什么使用make 4. 使用变量 5. 模式规则 6. 使用函数 7. 总结 1. 简介 make(GNU make) 是一个项目构建工具,即方便地编译、链接多个源代码文件,自动决定哪些源文件需要重新编译,从而高效地构建自己地项目。本教程使用最广泛使用make的.c文件为例,但实际上make的使用并不限于 C 语言。 本教程目标: 根据多个源文件(示例为.c、.h文件),编译成多个中间文件(示例为.o文件)

wenxue_disi的博客 669

Redis安装和配置

Redis安装,配置和两种启动方式

qq_44327755的博客 3311

make 学习 make 学习

vmake 学习 make 学习 make 学习 make 学习

QT | QT打包生成单个EXE文件

1.以release方式运行程序 2.在release文件夹中找到.exe文件 如果没有找到exe,可能是因为在项目中勾选了shadow build,你可以在构建目录中找到exe 3.将exe放在一个新建的文件夹中,因为待会儿会生成很多dll文件 4.点击电脑左下角搜索QT,打开Qt 5.8 for Desktop (MinGQW 5.3.0 32bit) 5.进入刚刚新建的文件夹,输入wi...

⊳ideal world⊲~ヽ(゚︺゚)Hi 1238

实用make最佳实践

一、前言 Make工具最主要也是最基本的功能就是通过makefile文件来描述源程序之间的相互关系并自动维护编译工作。而makefile 文件需要按照某种语法进行编写,文件中需要说明如何编译各个源文件并连接生成可执行文件,并要求定义源文件之间的依赖关系。 然而make的命令"博客精深",对于初学者来说,真是望而生畏,这篇文章不是make详解,只是讲解实用makefile的编写和使用。

daemon69的专栏 1261

卫星监测“America”级#两#栖#攻#击#舰#(LHA-6)出港

卫星监测“America”级#两#栖#攻#击#舰#(LHA-6)出港 前脚刚走,后脚又来,山姆大叔又来刷存在感,2020年7月28日,USS Ronald Reagan流窜在我国东海海区被卫星拍了下来: 2020年8月1日,最新卫星图像显示USS Ronald Reagan已经返回母港日本横须贺:距离其出港(2020年6月8日)已经过去了快两个月 然而事情并没有到此结束,“America”级#两#栖#攻#击#舰#(LHA-6)选择在 “八.一” 这个特殊的日子前后出港颇具挑衅的味道。 根据2020年

qq_46071146的博客 1566

Linux实用程序——Make快速入门

1. Makemake 是 linux 系统的实用程序。它用于管理对于大型程序的自动编译任务,自动决定程序某一部分需要重新编译,并发出编译指令。虽然,我们最常见于 C 语...

2418

无线Mesh组网全攻略:从原理、部署到调优与排障

对于布线困难的大户型、复式楼或园区,传统的无线中继往往面临带宽减半和链路不稳定的问题。无线Mesh网络通过多跳转发与自组织路由,让每个节点无需网线即可协作扩展覆盖,并依靠动态路径选择实现自愈。其核心技术涉及802.11s、HWMP路由协议,以及802.11k/v/r协同的无缝漫游,能有效提升终端切换体验。在实际部署中,设备选型、回程频段规划与布点位置直接决定网络质量。围绕无线Mesh的组网原理、设备选型、配置调优与故障排查,提供一套工程实践参考,帮助用户在复杂环境下构建稳定高效的无缝覆盖。

weixin_34098209的博客 274

NXP MCU CAN波特率(位时间)配置详解

1. 概述 本文将会详细讲解如何设置NXP MCU的CAN波特率、位时间、采样点等属性。 波特率即CAN总线传输频率,位时间是波特率的倒数,例如波特率是500K,那么位时间 = 1 / 500000 = 0.000002s = 2000ns。 我们先来看一下S32K144单片机的EB中与CAN总线波特率相关的可配置参数有哪些: ...

weixin_42967006的博客 9691

Linux | makefile | make,make clean,make install,make dist,make distcheck,make distclean

文章目录转载来源makemake cleanmake installmake distmake distcheckmake distclean 转载来源 linux系统命令make.clean的用法讲解 - 卡饭网 make 根据Makefile文件编译源代码、连接、生成目标文件、可执行文件。 make clean 清除上次的make命令所产生的object文件(后缀为“.o”的文件)及可执行文件。 make install 将编译成功的可执行文件安装到系统目录中,一般为/usr/local/bin目录。

我是一块小石头 2105

【机器学习】推理时计算优化——思维链(Chain-of-Thought)与慢思考范式 第一章:基础理论与认知框架 第二章:计算优化技术与前沿方法

过程监督在数学推理中展现出显著优势,因其能够定位错误发生的具体步骤,而非仅在最终答案处提供稀疏反馈。然而,高质量过程标注的获取成本高昂,这催生了基于自动验证器的近似方法。

VectorShift的博客 73

Go 中 make 与 new:区别及最佳实践

Go 提供两种常用的内存分配方式:make 与 new。二者虽均可分配内存,但定位和使用场景截然不同。正确区分并合理使用,有助于编写高效且易维护的代码。

分享 Go、云原生、AI Infra 相关技术 867

基于V7-690T FPGA纯逻辑实现NVMe SSD盘的读写(不使用软核)

a.NVMe的Admin CQ与Admin SQ命令放在Host内存中;因此SQ的64B要用FPGA的DDR或开一个bram空间存放,同理NVME写到HOST的CQ命令也需要存放在FPGA的DDR或一个bram空间,host收到NVME传来的MSI/MSI-X中断后,去CQ存放空间取CQ指令;b. 发送identify命令后,需要发送tial doorbell通知NVME;(发送CNS01h获取4096字节的固件信息等;发送CNS02h获取1024字节的namaspace list)

宝沐熙的博客 835

RK3576 PCIE接口调试:基于RTL8111HS的网卡驱动移植

检查项方法电源电压复位时序检查设备树 reset-gpios链路速率设备识别lspci -vvv驱动加载。

小灰灰的博客 739

make&& make install的意思

/configure配置环境 make是编译的意思。就是把源码包编译成二进制可执行文件 make install 就是安装的意思。 make&& make install的意思是: make与makeinstall是两个命令,在你./configuration生成了Makefile之后执行编译安装; 与&&一起的还有||,不过意思不一样,&&是与,||是或; make && makeinstall的意思就是执行make如果没有发生错误就执行m

ainivip的专栏 1万+

Cesium 实战 28 - 三维视频融合(视频投影)

Cesium 实战 27 - 三维视频融合(视频投影)。

非科班Java出身GISer的博客 3371

Linux下 config/configure/Configure、make 、make test/make check、sudo make install 的作用

这些都是典型的使用GNU的AUTOCONF和AUTOMAKE产生的程序的安装步骤 config/configure/Configure 这个是用来检测你的安装平台的目标特征的。比如它会检测你是不是有CC或GCC,并不是需要CC或GCC,它是个shell脚本这一步一般用来生成 Makefile,为下一步的编译做准备,你可以通过在 configure 后加上参数来对安装进行控制,比如:

SecondJanuary 2万+

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

【Linux基础知识系列】第四十四篇 - 源码安装:configure、make、make install

文章介绍了在Linux系统中从源代码编译安装软件的标准流程。主要步骤包括下载源码包、解压、配置编译环境(configure)、编译源码(make)和安装软件(make install)。文章详细说明了每个阶段的操作方法,如使用--prefix指定安装路径、-j选项加速编译等,并提供了解决依赖问题、卸载软件等常见问题的解决方案。此外,还给出了检查系统依赖、阅读文档等实用建议,帮助用户顺利完成源码安装。掌握这些方法对于需要自定义软件配置的用户尤为重要。

望获实时Linux系统 718

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

上一篇: linux awk命令详解
下一篇: C++ Template
起风啰
博客等级 码龄16年 30粉丝 4原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值