小型C/C++项目的makefile编写

摘要:
在我所接触到的Linux嵌入式开发中,大多使用的是C语言,采用makefile文件对源文件进行编译后生成可执行文件。本文即从个人经历上介绍小型的C项目如何编写makefile文档。这就是Makefile的规则。也就是Makefile中最核心的内容。由于这三个文件都未生成,故针对每一个文件都需要进行编译生成,所以有三个.o文件为目标的makefile语句。

【前言】在我所接触到的Linux嵌入式开发中,大多使用的是C语言,采用makefile文件对源文件进行编译后生成可执行文件。本文即从个人经历上介绍小型的C项目如何编写makefile文档。

一、gcc命令

从目的上看,gcc命令和makefile的功能是一样,即是把源文件编译后生成可执行文件或.o二进制文件。gcc命令中有许多的额外的参数,本文仅介绍以下几种最简单和常用的方法:

有helloworld.c文件如下:

#include <stdio.h>
intmain()
{
        printf("helloworld!
");
        return 0;
}

使用如下gcc命令,将在同一文件目录下生成a.out可执行文件,在terminal中运行该文件即可在屏幕中打印出helloworld!:

$  gcc helloworld.c

使用如下gcc命令,将在同一文件目录下生成helloworld.o二进制文件,它可以被用来连接文件:

$    gcc -c helloworld.c

如果要生成特定名称的可执行文件,可以使用如下gcc命令,它将生成helloworld可执行文件,区别于a.out可执行文件,在termianl执行它可以打印出helloworld!:

$    gcc helloworld.c -o helloworld

以上三种gcc命令,主要区别是参数不同,-c和-o的区别在于,-c只编译不连接,故只生成.o的二进制文件,-o即指定名称输出可执行文件。对于单个或者几个的源文件,使用gcc命令即可,但是一旦文件具有依赖关系,gcc命令的执行顺序是需要规定的,否则无法顺序编译项目,这是gcc命令的缺点,而makefile区别于gcc命令的地方正在于它能规定好文件间的依赖关系,以及加入依赖的库文件和头文件。

如果要引用其它目录下的头文件,其gcc命令如下,采用-I的参数:

$    gcc -I /ycl/include/ helloworld.c -o helloworld

如果要引用其它的目录下的库文件,如libmlib.so,其gcc命令如下,采用-L的参数:

$    gcc -L /ycl/lib –lmlib helloworld.c -o helloworld

二、makefile简要语法

makefile的用处是告诉编辑器,本项目以怎样的规则进行编译和链接,运行make命令需要makfile文件作为支撑。

要弄明白makefile,其关键是理解它的核心语法(源自博文http://blog.csdn.net/liang13664759/article/details/1771246 ):

target ... : prerequisites ...
command
...
...
target也就是一个目标文件,可以是Object File,也可以是执行文件。还可以是一个标签(Label),对于标签这种特性,在后续的“伪目标”章节中会有叙述。
prerequisites就是,要生成那个target所需要的文件或是目标。
command也就是make需要执行的命令。(任意的Shell命令)
这是一个文件的依赖关系,也就是说,target这一个或多个的目标文件依赖于prerequisites中的文件,其生成规则定义在command中。说白一点就是说,prerequisites中如果有一个以上的文件比target文件要新的话,command所定义的命令就会被执行。这就是Makefile的规则。也就是Makefile中最核心的内容。

示例代码如下:

CXX = g++
CXXFLAGS =    -O2 -g -Wall -fmessage-length=0CFLAGS = `pkg-config --cflags lcm`
LDFLAGS=`pkg-config --libs lcm`
        -lpthread

OBJS =DSRC_module.o    
            LCMHandler.o    
            UDPHandler.o

TARGET =DSRC_module
$(TARGET):    $(OBJS)
    $(CXX) -o $@ $^ $(LDFLAGS)

DSRC_module.o: DSRC_module.cpp
$(CXX) $(CFLAGS) -I. -o $@ -c $<

LCMHandler.o: LCMHandler.cpp
$(CXX) $(CFLAGS) -I. -o $@ -c $<

UDPHandler.o: UDPHandler.cpp
$(CXX) $(CFLAGS) -I. -o $@ -c $<

all: $(TARGET)

clean:
rm -f $(OBJS) $(TARGET)

示例代码中出现了变量,makefile中变量采用$(变量)的方法使用,如$(TARGET)即表示DSRC_module。该makefile的目的是生成名为DSRC_ module的可执行文件,即$(TARGET),它的依赖文件是$(OBJS)所代表的DSRC_module.o、LCMHandler.o、UDPHandler.o三个文件。由于这三个文件都未生成,故针对每一个文件都需要进行编译生成,所以有三个.o文件为目标的makefile语句。

文中特殊字符说明如下:$@--目标文件,$^--所有的依赖文件,$<--第一个依赖文件。比如在如下的代码中,$@代表DSRC_module.cpp,$<代表第一个依赖文件,也就是DSRC_module.cpp。

DSRC_module.o: DSRC_module.cpp  $(CXX) $(CFLAGS) -I. -o $@ -c $<

三、makefile编写

使用装用CDT插件的eclipse可以直接生成helloworld项目的makefile项目,makefile内容如下:

CXXFLAGS =    -O2 -g -Wall -fmessage-length=0
OBJS =example.o

LIBS =
TARGET =example

$(TARGET):    $(OBJS)
    $(CXX) -o $(TARGET) $(OBJS) $(LIBS)

all:    $(TARGET)

clean:
    rm -f $(OBJS) $(TARGET)

其中CXXFLAGS是编译参数变量,CXX是makefile内置变量,CXX默认表示g++(C++编译器)。这个makefile的有两个功能:①编译代码,将example.c编译成example.o并链接成example可执行文件;②清除项目,当项目需要重新编译或整理时,使用make clean命令即可清除生的OBJS和TARGET变量中的名称。

在上述代码中,之所以依赖文件没有.c文件,这是因为此处使用了makefile的自动推导功能,只要依赖关系中有.o文件,它会自动添加同名称的.c文件作为依赖文件,并由$(CXX) -o example.o可以自动推导出$(CXX) -o example.c。

$(TARGET):    $(OBJS)
    $(CXX) -o $(TARGET) $(OBJS) $(LIBS)

基于上述的makefile文件,如果此时需要添加新的.c和.h文件,则改变OBJS的内容即可,比如填加test.c文件,则代码如下(注意test.o前的不能加空格,需用tab键):

CXXFLAGS =    -O2 -g -Wall -fmessage-length=0
OBJS =example.o
                   test.o

LIBS =
TARGET =example

$(TARGET):    $(OBJS)
    $(CXX) -o $(TARGET) $(OBJS) $(LIBS)

all:    $(TARGET)

clean:
    rm -f $(OBJS) $(TARGET)    

如果需要添加头文件,那么注意尽量不要使用makefile的自动推导功能,否则可能找不到头文件,则只需添加和修改如下代码:

CXXFLAGS =    -O2 -g -Wall -fmessage-length=0OBJS =example.cpp
                   test.cpp

HEADER_DIR = -I/home/duser/dot3 

LIBS =TARGET =example $(TARGET): $(OBJS) $(CXX) -o $(TARGET) $(OBJS) $(LIBS) all: $(TARGET) clean: rm -f $(TARGET)
$(TARGET):    $(OBJS)
    $(CXX) $(HEADER_DIR)$(OBJS) -o $(TARGET)  $(LIBS)

如果需要添加库文件,需要两个变量,LDFLAGS---相当于库所在路径,LIBS---要链接的库文件。则只需添加和修改如下代码:

LDFLAGS += -L/lib
LIB += -ltest
$(TARGET):    $(OBJS)
$(CXX) $(HEADER_DIR)$(OBJS) -o $(TARGET)  $(LIBS)$(LDFLGS)$(LIBS)

免责声明:文章转载自《小型C/C++项目的makefile编写》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇关于MYSQL字符集问题(一)ElasticSearch(三):ES单机版本基本操作之删除,修改,插入下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

GCC编译之后的代码信息

arm-none-eabi-gcc编译之后会显示一些信息,比如 下面就来说受每个字段的含义以及它们与bin文件大小的关系。text区是代码区,就是程序编译之后源码的区域,在烧录之后一直位于Flash ROM中。 data和bss都是指的全局变量以及函数内static的变量,区别是data是有初始值的而bss没有。data的初始值同样存在Rom里,当单片机启...

双指针应用一:碰撞指针

1、翻转数组中的元素:s = ['l', 'e', 'e', 't', 'c', 'o', 'd', 'e'] 算法思路:定义左指针 i=0, 右指针 r = len(s)-1。交换指针对应的元素,然后同时向中间移动指针,直到指针碰撞。在指针移动的同时,交换指针指代的元素。 def reverseStr(s): l=0 r=len(s)-1...

Redis——6.0集群安装部署

前言 redis集群化部署主要用于大型缓存架构,一般的小型架构,使用redis主从配置 + sentinel哨兵集群应付系统压力, 使用redis集群可以方便快捷地对集群进行动态扩容,动态的添加、删除节点,reshard、并带有自动故障恢复功能。 一般redis集群使用三主三从,并且尽量保证主服务器与从服务器不在同一台机器上,防止机器故障导致的集群瘫痪,每...

快速编译system.img和boot.img的方法

快速编译system.img,可以使用这个命令: #make systemimage  快速编译boot.img,可以使用以下命令: #make bootimage  快速编译userdata.img,可以使用以下命令: # make userdataimage-nodeps 但是,对于bootimage, 在不改变内存布局的情况下,可以使用以下命令: #...

glibcxx升级

经过测试“GLIBCXX3.4.21 not find”这篇博文解决了我的问题。 以下是安装步骤: 一、首先查看当前gcc版本 strings /usr/lib/x86_64_linux-gun/libstdc++.so.6|grep GLIBCXX 二、升级安装GCC 1. 下载最新版本gcc gcc当前最新版本是5.2,下面是我找到的两个下载镜像,...

Linux环境依赖库离线安装

离线源码安装包下载地址:http://www.rpmfind.net/linux/rpm2html/search.php?query=net-tools&submit=Search+...&system=&arch= gcc --version;  #检查gcc是否安装g++ --version;  #检查g++是否安装 未安装,...