实用makefile最佳实践

Ubuntu换源全攻略:从系统源到开发工具镜像配置 软件包管理器是Linux系统的核心组件,负责软件的安装、更新和依赖管理。其工作原理是通过访问远程软件仓库获取软件包列表和二进制文件。对于国内开发者而言,直接访问海外官方源常因网络延迟导致下载速度缓慢,影响开发效率。通过配置国内镜像源,可以显著提升软件包下载速度,保障开发环境的快速搭建。这一技术实践在Python、Node.js、Docker等开发工具链的部署中尤为重要。本文聚焦于Ubuntu系统,详细解析如何为apt包管理器及pip、Docker等常用开发工具配置国内镜像源,其中清华大学TUNA镜像和阿里云 阅读详情

一、前言

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配置可以参考注释
原文链接:http://www.cnblogs.com/ggjucheng/archive/2011/12/14/2288055.html
Pangolin安装报错:make: *** 没有规则可制作目标“pypangolin_pip_install”。 停止。 Pangolin安装报错:make: *** 没有规则可制作目标“pypangolin_pip_install”。 停止。 按照github上的步骤安装,到$ cmake --build . -t pypangolin_pip_install这步就报错 make: *** 没有规则可制作目标“pypangolin_pip_install”。 停止。 阅读详情

相关推荐

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

qq_44503950的博客 1万+

make 学习 make 学习

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

告别安装包下载困境:包管理器与镜像站构建高效安全开发环境

软件分发是软件开发与系统运维中的基础环节,其核心在于如何高效、安全地获取可信的二进制文件或源代码。传统的软件获取方式依赖用户自行搜索官网或第三方站点,常面临下载速度慢、版本混乱及安全风险等挑战。其技术原理在于通过集中式的软件仓库和依赖解析算法,将分散的软件资源标准化管理。包管理器(如 apt、Homebrew、Winget)的价值在于实现了软件安装的自动化与依赖管理,极大提升了环境配置的可靠性和可重复性。结合开源镜像站(如清华源、阿里云镜像)提供的国内加速服务,这一方案能有效解决网络延迟问题,广泛应用于个人

weixin_30896511的博客 275

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

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

weixin_39754831的博客 1099

Redis安装和配置

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

qq_44327755的博客 3307

各编程语言的国内镜像源切换

在学习编程语言过程中,每次都要寻找国内镜像源的切换方法,比较麻烦,所以这次做了汇总,后期有新的语言切换可以进行补充,也欢迎多多交流呀!!

VirusScanLog的博客 1141

编程语言认知框架:从核心要素到技术选型的系统性指南

编程语言是软件开发的基石,其核心在于通过语法、类型系统和执行模型等抽象机制,将人类意图转化为机器可执行的指令。理解编程语言的原理,关键在于把握其设计哲学与核心能力,这直接决定了开发效率、程序性能和应用边界。从技术价值看,掌握多语言认知框架能帮助开发者构建系统性的技术视野,提升技术选型与架构设计能力。在实际应用场景中,无论是Web开发、数据科学还是系统编程,都需要根据项目需求在**语法与表达能力**、**生态与工具链**等维度进行权衡。本文以搭建多语言实验台为例,通过对比Python、Go等语言的特性,自然收

weixin_30654583的博客 387

nodejs@npm安装以及镜像加速@npm代替方案

文章目录下载并抽取二进制文件创建软连接安装yarnclone egg.js 测试demo进入指定的egg项目执行install 下载并抽取二进制文件 执行: mkdir \usr\node 通过tar xvf node-v.....(your package name).tar.xz -C \usr\node 将文件抽取到\usr\node目录下 创建软连接 分别为node 和 npm 创建在/usr/local/bin 下的软连接(还是叫符号链接?) 现在进入到对应的bin目录下:(shell提示符中

xuchaoxin1375的博客 769

校园网加速秘籍:手把手教你配置高校开源镜像站(附常见问题解决)

本文针对校园网环境,详细介绍了如何配置高校开源镜像站以大幅提升软件下载速度。文章以清华大学TUNA等主流镜像站为例,手把手指导Ubuntu、CentOS等系统及Python、Docker等开发工具的换源配置,并提供了镜像站选择策略、性能测试方法及常见问题解决方案,帮助师生彻底告别下载慢的困扰。

flower的专栏 525

Python与PyCharm环境配置全攻略:从安装、配置到深度卸载

在软件开发中,环境配置是项目成功运行的基础,它直接关系到代码的执行效率和开发体验。其核心原理在于正确设置系统路径、依赖管理和工具链协同,确保开发工具能够准确识别和调用所需的运行时组件。对于Python开发而言,合理配置环境的价值在于避免版本冲突、依赖混乱,并提升包管理效率,这是构建稳定、可复现开发环境的关键。在实际应用场景中,无论是进行数据分析、Web开发还是自动化脚本编写,一个配置得当的环境都能显著提升开发效率。本文聚焦于Python开发环境的核心搭建,详细解析了Python解释器版本选择、PyCharm

weixin_34122810的博客 483

记录一下备忘

pm2 start C:\Users\Administrator\AppData\Roaming\npm\node_modules\verdaccio\bin\verdaccio pm2 stop C:\Users\Administrator\AppData\Roaming\npm\node_modules\verdaccio\bin\verdaccio route add 172.16.0.0 mask 255.255.0.0 192.168.2.3

yishifanchen的博客 440

包管理器大揭秘:从 pip 到 yum,一行安装命令背后的 “神仙操作”

本文深度拆解语言级(pip/npm/Maven 等)与系统级(yum/apt/brew 等)包管理器核心逻辑,揭示其 “仓库查询→依赖解析→自动化安装” 的共性范式,对比两类工具在管理范围、适用场景的核心差异。结合实战案例讲解工具用法与避坑技巧,帮助开发者理清工具选择逻辑,轻松搞定软件安装配置,提升开发部署效率。

进一步有进一步的欢喜~ 1272

开发环境配置全攻略:从Arduino到Python,掌握高效搭建与避坑技巧

环境配置是软件开发与嵌入式项目启动的关键第一步,其核心在于构建一个稳定、可复现的工具链。其原理涉及依赖管理、环境隔离和路径解析,旨在解决版本冲突与工具链对齐问题。这项技术的价值在于为高效开发奠定基础,避免后续因环境问题导致的调试困境。应用场景广泛,从Arduino IDE配置第三方开发板、Python多版本环境管理,到Java开发环境(JDK+Maven)搭建,均需遵循清晰的配置逻辑。本文聚焦于Arduino IDE和Python环境等高频配置场景,通过解析核心需求与通用方法论,提供一套从调研、安装、配置到

weixin_30875157的博客 339

【硬科普】太奶学 IT:一文读懂镜像源的底层逻辑

本文深入解析了镜像源的概念与应用,重点介绍了清华镜像源的使用方法。文章首先阐述了镜像源作为原始软件仓库异地副本的核心价值:提升下载速度、保障访问稳定性和缓解上游源压力。随后详细讲解了从个人搭建小型PyPI镜像到专业运营大型镜像源的技术要点,并提供了Python pip、Anaconda、Ubuntu、Docker和npm等常见开发场景配置清华镜像的完整指南。最后对比分析了国内主流镜像源的特点,解答了用户最关心的同步时效性、安全性等实际问题,帮助开发者根据需求选择最适合的镜像服务方案。全文兼顾理论知识与实操指

民乐团扒谱机的博客 1951

国内主流开源镜像站推荐与使用指南

本文推荐了国内六大主流开源镜像站(清华大学、阿里云、华为、南京大学、北京大学、浙江大学),详细对比了它们的特色与优势,并提供了Ubuntu/Debian、CentOS/RHEL、Python pip、Node.js等常见开发环境的镜像源配置指南,帮助开发者大幅提升软件下载速度与工作效率。

weixin_30338481的博客 460

高效开发必备:国内主流工具与镜像源一站式指南

本文为开发者提供了一份详尽的国内主流工具与镜像源一站式配置指南。针对国内网络环境,重点介绍了如何高效配置Python的Pip与Conda、Java的Maven与Gradle、Node.js的npm、Docker以及各类系统包管理器的国内镜像源,旨在解决下载缓慢和连接超时问题,显著提升开发环境搭建与依赖安装效率。

weixin_29224589的博客 560

镜像源对比评测:阿里云、清华、华为云哪个更适合你?

本文对国内三大主流镜像源阿里云、清华TUNA和华为云进行了深度评测,从下载速度、资源覆盖范围和稳定性三个维度提供详细对比数据。通过实测分析,帮助开发者根据项目需求选择最佳镜像源解决方案,提升软件开发效率。

weixin_29313547的博客 541

Linux零基础入门:从系统安装、核心命令到环境配置实战指南

这次我们来看一个面向零基础学习者的 Linux 系统完整入门攻略。对于很多想进入运维、开发或安全领域的新手来说,Linux 是一道绕不开的坎,但面对复杂的命令、陌生的文件系统和各种发行版,往往不知从何下手。这篇文章的目标很直接:提供一个从零开始、手把手带你掌握 Linux 核心知识、完成系统安装与激活,并建立正确学习方法的实战指南。 本文的重点不是空谈概念,而是提供一套可立即执行的操作流。无论你是想在虚拟机里体验,还是在真机上安装,或是需要激活某些开发环境,我们都会覆盖。你会了解到学习 Linux 的高效路

weixin_30918633的博客 427
上一篇: 关于Linux动态库与静态库的分析
下一篇: Makefile中的wildcard函数(用来获取指定目录下的文件)
usbdrivers
博客等级 码龄14年 26粉丝 57原创
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值