• Linux Makefile编写之静态库


    1 概述

      编译工具有很多(make/cmake/BJam)。如果不考虑跨平台的话,还是make比较方便。使用make编译需要编写Makefile。本文编写Makefile来生成C/C++静态库。

    2 Makefile文件命名

    Makefile文件首先是一个文本文件,Linux下默认有两种命名方式:

    • Makefile 这是最常用的命名方式
    • makefile 这是优先级高的命名方式

    在工程目录下运行make命令,make程序先找makefile,如果没有makefile再找Makefile文件。也就是说如果makefile和Makefile两个文件都存在默认使用makefile。

    其实Makefile的文件名可以是任意的,例如Buildfile,可以使用下面命令编译:

    make -f BuildFile
    
    • 1

    本文使用make程序版本:

    $make --version
    GNU Make 4.2.1
    Built for x86_64-pc-linux-gnu
    Copyright (C) 1988-2016 Free Software Foundation, Inc.
    License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
    This is free software: you are free to change and redistribute it.
    There is NO WARRANTY, to the extent permitted by law.
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    3 MakeFile实例

    这里以CppTest库代码为例,代码目录结构:

    cpptest$ tree
    .
    ├── Makefile
    ├── inc
    │   ├── 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
    └── src
        ├── collectoroutput.cpp
        ├── compileroutput.cpp
        ├── config.h
        ├── htmloutput.cpp
        ├── missing.cpp
        ├── missing.h
        ├── source.cpp
        ├── suite.cpp
        ├── textoutput.cpp
        ├── time.cpp
        ├── utils.cpp
        ├── utils.h
        └── winconfig.h
    
    2 directories, 24 files
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    Makefile文件如下:

    PROJECT_NAME ?= cpptest
    
    CC ?= gcc
    CXX ?= g++
    AR  ?= ar
    
    CFLAGS := 
    C++FLAGS := -std=c++11
    LIBFLAGS := -rcD
    
    PWD := $(shell pwd)
    INCS :=  -I$(PWD)/inc
    SRCDIR := $(PWD)/src
    LIBDIR := $(PWD)/lib
    LIBNAME := $(LIBDIR)/lib$(PROJECT_NAME).a
    
    CSRC := $(wildcard $(SRCDIR)/*.c)
    OBJS := $(patsubst %.c,%.o,$(CSRC))
    
    CPPS := $(wildcard $(SRCDIR)/*.cpp)
    CPPOBJS := $(patsubst %.cpp,%.o,$(CPPS))
    
    all: $(OBJS) $(CPPOBJS) $(LIBDIR)
    	$(AR) $(LIBFLAGS) $(LIBNAME) $(OBJS) $(CPPOBJS)  
    
    $(OBJS): %.o:%.c
    	$(CC) -c $(CFLAGS) $(INCS) $< -o $@
    
    $(CPPOBJS): %.o:%.cpp
    	$(CXX)  -c $(C++FLAGS) $(INCS) $< -o $@
    
    $(LIBDIR):
    	@mkdir $(LIBDIR) -p
    
    .PHNOY:clean
    clean:
    	@rm -f $(OBJS) $(CPPOBJS)
    	@rm -f $(LIBNAME)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    编译结果:

    cpptest$ make
    g++  -c -std=c++11 -I/home/james/git/cpptest/inc /home/james/git/cpptest/src/utils.cpp -o /home/james/git/cpptest/src/utils.o
    g++  -c -std=c++11 -I/home/james/git/cpptest/inc /home/james/git/cpptest/src/source.cpp -o /home/james/git/cpptest/src/source.o
    g++  -c -std=c++11 -I/home/james/git/cpptest/inc /home/james/git/cpptest/src/time.cpp -o /home/james/git/cpptest/src/time.o
    g++  -c -std=c++11 -I/home/james/git/cpptest/inc /home/james/git/cpptest/src/collectoroutput.cpp -o /home/james/git/cpptest/src/collectoroutput.o
    g++  -c -std=c++11 -I/home/james/git/cpptest/inc /home/james/git/cpptest/src/textoutput.cpp -o /home/james/git/cpptest/src/textoutput.o
    g++  -c -std=c++11 -I/home/james/git/cpptest/inc /home/james/git/cpptest/src/compileroutput.cpp -o /home/james/git/cpptest/src/compileroutput.o
    g++  -c -std=c++11 -I/home/james/git/cpptest/inc /home/james/git/cpptest/src/htmloutput.cpp -o /home/james/git/cpptest/src/htmloutput.o
    g++  -c -std=c++11 -I/home/james/git/cpptest/inc /home/james/git/cpptest/src/suite.cpp -o /home/james/git/cpptest/src/suite.o
    g++  -c -std=c++11 -I/home/james/git/cpptest/inc /home/james/git/cpptest/src/missing.cpp -o /home/james/git/cpptest/src/missing.o
    ar -rcD /home/james/git/cpptest/lib/libcpptest.a  /home/james/git/cpptest/src/utils.o /home/james/git/cpptest/src/source.o /home/james/git/cpptest/src/time.o /home/james/git/cpptest/src/collectoroutput.o /home/james/git/cpptest/src/textoutput.o /home/james/git/cpptest/src/compileroutput.o /home/james/git/cpptest/src/htmloutput.o /home/james/git/cpptest/src/suite.o /home/james/git/cpptest/src/missing.o
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    说明:

    • 编译生成静态库libcpptes.a文件放在lib目录下
    • 编译生成.o与源码在同一目录

    3 代码分析

    3.1 定义变量

    PROJECT_NAME ?= cpptest
    
    CC ?= gcc
    CXX ?= g++
    AR  ?= ar
    
    • 1
    • 2
    • 3
    • 4
    • 5

    说明:

    • 定义工程名,C/C++编译器名称和生成库程序名称。
    • ?=格式定义变量可以通过环境变量修改,例如工程名修改为CppTest需要在命令上执行命令: export PROJECT_NAME=CppTest
    • 编译器名称也可以修改,例如:export CC=arm-xilinx-linux-gnueabi-gcc,这样就可交叉编译了。

    3.2 定义编译选项

    CFLAGS := 
    C++FLAGS := -std=c++11
    LIBFLAGS := -rcD
    
    • 1
    • 2
    • 3

    说明:

    • 定义C/C++编译选项,C++使用C++11标准。
    • 定义生成库选项

    3.3 定义路径

    PWD := $(shell pwd)
    INCS :=  -I$(PWD)/inc
    SRCDIR := $(PWD)/src
    LIBDIR := $(PWD)/lib
    LIBNAME := $(LIBDIR)/lib$(PROJECT_NAME).a
    
    • 1
    • 2
    • 3
    • 4
    • 5

    说明:

    • 调用shell命令pwd获取当前路径PWD
    • 利用PWD定义include/src/lib路径
    • 定义生成库名称
    • 注意这里定义变量是通过:=来定义的,这种变量没法通过环境变量修改。

    3.4 自动选择译源文件

    CSRC := $(wildcard $(SRCDIR)/*.c)
    OBJS := $(patsubst %.c,%.o,$(CSRC))
    
    CPPS := $(wildcard $(SRCDIR)/*.cpp)
    CPPOBJS := $(patsubst %.cpp,%.o,$(CPPS))
    
    • 1
    • 2
    • 3
    • 4
    • 5

    说明:

    • 调用函数wildcard扫描src下所有.c/.cpp文件
    • 调用函数patsubst通过源文件生成.o目标文件

    3.5 编译依赖项

    all: $(OBJS) $(CPPOBJS) $(LIBDIR)
    	$(AR) $(LIBFLAGS) $(LIBNAME) $(OBJS) $(CPPOBJS)  
    
    $(OBJS): %.o:%.c
    	$(CC) -c $(CFLAGS) $(INCS) $< -o $@
    
    $(CPPOBJS): %.o:%.cpp
    	$(CXX)  -c $(C++FLAGS) $(INCS) $< -o $@
    
    $(LIBDIR):
    	@mkdir $(LIBDIR) -p
    
    .PHNOY:clean
    clean:
    	@rm -f $(OBJS) $(CPPOBJS)
    	@rm -f $(LIBNAME)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    说明:

    • $(OBJS)依赖项编译.c文件为.o文件
    • $(CPPOBJS)依赖项编译.cpp文件为.o文件
    • $(LIBDIR)依赖项创建目录lib
    • all依赖项将.o文件生成lib文件。
    • clean依赖项删除编译生成.o和.a文件。
  • 相关阅读:
    【虹科分享】不要使用Windows解决方案来保护Linux服务器
    【跟小嘉学 Rust 编程】二十四、内联汇编(inline assembly)
    目标检测—YOLO系列(二 ) 全面解读复现YOLOv1 PyTorch
    Element-UI 动态设置form验证规则
    springMvc57-简单的拦截器
    白盒测试的各种方法
    机器学习与数据挖掘第三、四周
    【无标题】
    WEB前端网页设计 HTML CSS 网页设计参数 - 【浏览器背景图片】
    带您聚焦第十四届中国航展新看点
  • 原文地址:https://blog.csdn.net/flysnow010/article/details/138185330