qtcanpool 知 01:工程管理

文章目录

  • 1. 前言
  • 2. 演进
    • 2.1. 一个可执行程序
      • 2.1.1. 创建
      • 2.1.2. 编译
      • 2.1.3. 改进
      • 2.1.4. 思考
    • 2.2. 一个可执行程序、一个用户库
      • 2.2.1. 新建共享库
      • 2.2.2. 添加导出类
      • 2.2.3. 使用库
        • 2.2.3.1. 子目录
          • 2.2.3.1.1. 编译
          • 2.2.3.1.2. 运行
        • 2.2.3.2. 父目录
          • 2.2.3.2.1. 编译
      • 2.2.4. 思考
    • 2.3. 一个可执行程序和用户库
  • 3. 简介
    • 3.1. 工程结构
    • 3.2. 工程管理
      • 3.2.1. qtproject.pri
      • 3.2.2. library.pri
    • 3.3. 使用
      • 3.3.1. 库依赖关系
      • 3.3.2. 库添加
      • 3.3.3. 应用添加
      • 3.3.4. 库链接
  • 4. 后语

1. 前言

qtcanpool的工程管理来源于qtcreator源码,并进行了优化和改进,使之变为更通用的工程管理模板。

2. 演进

工程管理,不对,工程管理模板,这个有什么意义?

先来看看一个程序的主要组织结构:
qtcanpool 知 01:工程管理_第1张图片

在开发QT程序的时候,大概会有下面几种组织形式:

  1. 一个可执行程序
  2. 一个可执行程序、若干个用户库
  3. 一个可执行程序、若干个用户库、若干个用户插件

下面看看QT程序的工程是如何演进的。

2.1. 一个可执行程序

功能:实现一个窗口类HelloCanpool,其包含一个QPushButton,Button文本显示为Hello Canpool。HelloCanpool的实例作为QMainWindow的中心窗体,效果图如下:
qtcanpool 知 01:工程管理_第2张图片

2.1.1. 创建

如何创建工程可以参考:qtcreator 手册
qtcanpool 知 01:工程管理_第3张图片
主要代码如下

  • hellocanpool.pro
QT       += core gui

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

CONFIG += c++11

# You can make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000

SOURCES += \
    hellocanpool.cpp \
    main.cpp \
    mainwindow.cpp

HEADERS += \
    hellocanpool.h \
    mainwindow.h
  • hellocanpool.cpp
#include "hellocanpool.h"
#include 
#include 

HelloCanpool::HelloCanpool(QWidget *parent)
    : QWidget(parent)
{
    QPushButton *btn = new QPushButton("Hello Canpool");

    QHBoxLayout *lay = new QHBoxLayout();
    lay->addWidget(btn);

    setLayout(lay);
}
  • mainwindow.cpp
#include "mainwindow.h"
#include "hellocanpool.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    HelloCanpool *hc = new HelloCanpool();

    setCentralWidget(hc);

    resize(300, 100);
}

2.1.2. 编译

编译后,输出的可执行程序如下:
qtcanpool 知 01:工程管理_第4张图片
看破

  • 看:可执行程序为什么在debug目录,而不是release目录?
    破:因为qtcreator构建时选的是debug模式,所以默认在debug目录
  • 看:可执行程序一般不是在bin目录吗?
    破:是的,一般是在bin目录,可以在pro文件中通过DESTDIR设置
  • 看:可执行程序名称为什么是hellocanpool,可以叫HelloCanpool吗?
    破:可以在pro文件中通过TARGET设置,如果没有设置,默认就是工程文件名
  • 看:为什么生成的是可执行程序exe,而不是库呢?
    破:可以在pro文件中通过TEMPLATE = app设置,如果没有设置,默认就是生成app。

备注:更多的配置和使用可以参考 qmake手册

2.1.3. 改进

将可执行程序改名为HelloCanpool,并输出到bin目录下:

  • hellocanpool.pro
QT       += core gui

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

DESTDIR = bin
TARGET = HelloCanpool

CONFIG += c++11

qtcanpool 知 01:工程管理_第5张图片
这些就是一个可执行程序大致的开发内容了,之后可以不断的丰富这个可执行程序的功能。

2.1.4. 思考

一个可执行程序如果采用qmake构建方式,那么pro文件就是它的配置管理文件,通过DESTDIR、TARGET、TEMPLATE等完全可以管理好这个项目,这还需要什么工程管理模板吗?

思考一下:一个可执行程序可能存在哪些问题?

  • 随着软件功能的增加,这个可执行程序会越来越大,大好像也没啥问题。
  • 如果软件是个人开发的,也不用和别人协作,一个可执行程序也没啥问题。但有没有一种可能,一个人可能会开发多个软件,不同的软件之间可能有共用的功能,不过可以互相拷贝源文件,但文件数目较多呢,比如几十个,也来回拷贝吗?可以改为拷贝目录(模块)。
  • 如果软件是多人协作、分工开发,该怎么划分呢?一个可执行程序可以划分不同的类模块,不同的人负责不同的类模块,也可解决协作问题。
  • 有没有一种可能,开发的软件会提供给第三方做二次开发,此时,也把源代码拷贝给第三方吗?这对于开源软件来说,也不成问题。

把所谓的模块封装为库,提供开放接口的头文件,那么是不是可以更好的解决上面的问题呢?

2.2. 一个可执行程序、一个用户库

功能:将上一个可执行程序的窗口类HelloCanpool封装成hello库,提供给可执行程序使用。

2.2.1. 新建共享库

qtcanpool 知 01:工程管理_第6张图片
qtcanpool 知 01:工程管理_第7张图片
这里Type选择Shared Library,Qt module选择Widgets,类名和文件名保持不变。

主要代码如下

  • hello.pro
QT += widgets

TEMPLATE = lib
DEFINES += HELLO_LIBRARY

CONFIG += c++11

# You can make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0

SOURCES += \
    hello.cpp

HEADERS += \
    hello_global.h \
    hello.h

# Default rules for deployment.
unix {
    target.path = /usr/lib
}
!isEmpty(target.path): INSTALLS += target
  • hello_global.h
#ifndef HELLO_GLOBAL_H
#define HELLO_GLOBAL_H

#include 

#if defined(HELLO_LIBRARY)
#  define HELLO_EXPORT Q_DECL_EXPORT
#else
#  define HELLO_EXPORT Q_DECL_IMPORT
#endif

#endif // HELLO_GLOBAL_H
  • hello.h
#ifndef HELLO_H
#define HELLO_H

#include "hello_global.h"

class HELLO_EXPORT Hello
{
public:
    Hello();
};

#endif // HELLO_H

看破

  • 看:TEMPLATE = lib
    破:说明这是一个库工程
  • 看:DEFINES += HELLO_LIBRARY
    破:定义宏HELLO_LIBRARAY
  • 看:HELLO_EXPORT
    破:这是个导出宏,当定义HELLO_LIBRARAY时,该宏为Q_DECL_EXPORT;当未定义HELLO_LIBRARAY时,该宏为Q_DECL_IMPORT。从名字上可以看出,一个是定义导出,一个是定义导入,可以看看这两个Q_DECL_xxx宏是什么定义:
    qtcanpool 知 01:工程管理_第8张图片
    一个是动态库导出,一个是动态库导入
  • 看:class HELLO_EXPORT Hello
    破:如果定义HELLO_LIBRARAY,说明Hello是一个导出类,不然,Hello就是一个导入类。
    什么意思呢?简单理解,导出就是库提供了对外使用的类,导入就是应用将库提供的类导入使用。
    更多可以参考:
    https://doc.qt.io/qt-5/sharedlibrary.html
    https://wiki.qt.io/How_to_create_a_library_with_Qt_and_use_it_in_an_application

编译输出动态库和静态库如下:
qtcanpool 知 01:工程管理_第9张图片
延伸
新建一个静态库如下,静态库是通过CONFIG += staticlib配置,没有导出导入的概念,因为静态库最终会集成到可执行程序中。
qtcanpool 知 01:工程管理_第10张图片

2.2.2. 添加导出类

将类HelloCanpool从可执行程序中移到hello库中,主要修改如下:
qtcanpool 知 01:工程管理_第11张图片
hellocanpool.h中包含头文件hello_global.h,并在类定义前添加HELLO_EXPORT,将HelloCanpool定义为导出类,供应用程序使用。

编译结果如下:
qtcanpool 知 01:工程管理_第12张图片

2.2.3. 使用库

上面已经生成了动态库hello.dll和静态库libhello.a,该如何使用呢?

2.2.3.1. 子目录

在hellocanpool目录下新建子目录hello,然后拷贝hello.dll、hello_global.h、hellocanpool.h、libhello.a几个文件到hello目录,并修改mainwindow.cpp(hellocanpool.h前面增加hello路径)如下:
qtcanpool 知 01:工程管理_第13张图片qtcanpool 知 01:工程管理_第14张图片

2.2.3.1.1. 编译

编译hellocanpool,如下所示:
qtcanpool 知 01:工程管理_第15张图片
看破

  • 看:未定义__imp__ZN12HelloCanpoolC1EP7QWidget
    破:编译后的类,会进行符号重命名,这里实指:未定义HelloCanpool
  • 看:collect2.exe: error: ld returned 1 exit status
    破:链接错误

这里使用了HelloCanpool类,但是类的定义/实现却在库中,直接使用自然会报未定义和链接错误。
在hellocanpool.pro中配置LIBS如下,再次编译即可通过。
qtcanpool 知 01:工程管理_第16张图片
看破

  • 看:-L$$PWD/hello
    破:-L指定库所在的目录,即当前路径(pro文件所在的目录)下的hello目录
  • 看:-lhello
    破:-l链接库,有两个库,分别是libhello.a和hello.dll,那么到底链接的是哪个库呢?测试如下:
    qtcanpool 知 01:工程管理_第17张图片
    1)删除libhello.a,保留hello.dll,然后rebuild如下:
    qtcanpool 知 01:工程管理_第18张图片qtcanpool 知 01:工程管理_第19张图片
    2)删除hello.dll,保留libhello.a,然后rebuild如下:
    qtcanpool 知 01:工程管理_第20张图片
    qtcanpool 知 01:工程管理_第21张图片
    3)删除libhello.a和hello.dll,然后rebuild如下: 在这里插入图片描述
    qtcanpool 知 01:工程管理_第22张图片
    经过测试,动态库和静态库都可以进行链接
2.2.3.1.2. 运行

运行结果如下:
qtcanpool 知 01:工程管理_第23张图片
qtcanpool 知 01:工程管理_第24张图片
在这里插入图片描述
看破

  • 看:未拷贝动态库hello.dll到可执行程序目录下也可以运行?
    破:会不会是hello目录下存在hello.dll的原因,测试如下:
    qtcanpool 知 01:工程管理_第25张图片
    1)删除hello.dll,保留libhello.a,运行如下:
    qtcanpool 知 01:工程管理_第26张图片
    qtcanpool 知 01:工程管理_第27张图片
    2)保留hello.dll,删除libhello.a,运行如下:
    qtcanpool 知 01:工程管理_第28张图片
    qtcanpool 知 01:工程管理_第29张图片

结论
经过上面反复对静态库和动态库的测试,总结如下:

  • 静态库和动态库都可以用来编译
  • 动态库可用来运行

备注:如果发布软件时,动态库需要拷贝到可执行程序所在的目录

2.2.3.2. 父目录

使用hello库的时候,是在hellocanpool中新建子目录hello,需要拷贝开放头文件和相应的库。一般源码修改频繁,每次修改库后都拷贝头文件和库,不是一个好的处理方案。

hellocanpool和hello两个工程在同一目录,是否可以直接用hello目录,而不用频繁拷贝呢?
qtcanpool 知 01:工程管理_第30张图片
将hello的库文件(libhello.a和hello.dll)拷贝到hello目录,删除hellocanpool的子目录hello,同时修改hellocanpool工程的hello路径,如下:
qtcanpool 知 01:工程管理_第31张图片

2.2.3.2.1. 编译

qtcanpool 知 01:工程管理_第32张图片
看破

  • 看:hello/hellocanpool.h: No such file or directory
    破:找不到头文件hello/hellocanpool.h。之前存在子目录的时候,mainwindow.cpp中在当前路径的hello下可以找到hellocanpool.h,现在hellocanpool.h在父目录的hello下,可以继续修改hellocanpool.h的路径为…/hello/hellocanpool.h?这样显然不好,可以通过INCLUDEPATH来设置一些头文件的路径,如下:
    qtcanpool 知 01:工程管理_第33张图片

目前开放头文件不需要拷贝了,但是库文件还需要手动拷贝到hello目录,可以设置hello的库文件输出目录,这样就不需要每次都拷贝了,如下:
qtcanpool 知 01:工程管理_第34张图片
qtcanpool 知 01:工程管理_第35张图片
说明
上面的源码和输出库的目录结构没有进行规划(比如源码放在src目录,输出的库放在lib目录),因为程序功能相对较简单,如果功能复杂,建议组织好目录结构体(可以使用pri来管理),然后更改相应的DESTDIR,INCLUDEPATH、LIBS等路径信息。

2.2.4. 思考

目前,可执行程序hellocanpool和库hello是两个独立的工程,两个工程存在下面一些问题:

  • 编译过程比较麻烦,工程需要分别手动编译,如果有多个工程,操作变得很复杂。
  • 依赖关系比较弱,比如hello有更新,需要先手动编译hello,才能再编译hellocanpool;如果只编译hellocanpool,依赖的hello头文件定义和库内容可能存在差异(头文件更新了,库却没更新),增加程序出错率。
  • 文件管理不方便,两个工程目录加上编译输出目录,就是四个,比如用git等版本管理工具,文件整体性比较差。

现代的很多IDE都支持单工程多应用管理,就是一个工程/项目,支持多个应用/库,而且应用和库之间的依赖关系可以明确指定。当编译某个应用时,同时会自动编译依赖的库。

qmake通过TEMPLATE来指定工程是应用还是库,那么还有没有别的模板呢?如下所示:
qtcanpool 知 01:工程管理_第36张图片
TEMPLATE = subdirs可以使用单工程多应用模板,再来看看SUBDIRS变量如何使用?
qtcanpool 知 01:工程管理_第37张图片
SUBDIRS用来指定多个应用,.depends用来设置依赖关系。

下面将采用subdirs的模板来重新管理hellocanpool和hello。

2.3. 一个可执行程序和用户库

功能:新建一个subdirs模板工程mycanpool,添加可执行程序hellocanpool和库hello。

新建mycanpool目录和mycanpool.pro文件,并将hellocanpool和hello移到mycanpool目录,并做如下修改:
qtcanpool 知 01:工程管理_第38张图片
qtcanpool 知 01:工程管理_第39张图片
重新修改DESTDIR和LIBS的路径,编译输出如下:
qtcanpool 知 01:工程管理_第40张图片
至此,工程的演进过程就介绍完了,最后这种方式还有一些地方值得思考的:

  • 依赖关系的写法不好,实际上应该在hellocanpool中写明依赖hello,SUDIRS添加hellocanpool后应该自动识别出hellocanpool的依赖关系。不然移除hellocanpool时,还要手动移除其depends。
  • DESTDIR的路径需要修改多处,是否可以通过同一个变量来指明输出路径,然后DESTDIR引用这个变量,之后修改输出路径,只需要修改这个变量的值即可。
  • LIBS的-L指定的库路径,实际上和库的DESTDIR有关系,是否可以引用某个变量,这样就不需要频繁修改LIBS的路径了。
  • INCLUDEPATH中添加库头文件的路径,是否也可以引用某个变量。

这些问题,随着以后不断的完善,将会形成一套方便的工程管理。作者在阅读qtcreator的源码时,发现了这个工程管理,然后进行了优化和改进,整理出了一套通用的工程管理模板,这就是qtcanpool的工程管理。

3. 简介

接下来,将正式开始介绍qtcanpool的工程管理,下图中各种pri文件就是整个工程管理的核心。
qtcanpool 知 01:工程管理_第41张图片

3.1. 工程结构

整个qtcanpool主要由几大部分构成,如下表:

部件 说明
libs 功能独立的库模块
plugins 插件模块,基于QtCreator的插件系统
tools 工具模块,最终也是生成可执行程序,开发一个软件可能同时会伴随一些辅助的小工具,即多应用
modules 一些功能独立但又不具备库的规模的模块

3.2. 工程管理

工程管理到底管的是什么呢?其实,就是管理前面介绍的几大部件,即如何管理libs库、plugins插件、tools工具等。

管理文件 说明
qtproject.pri 整个工程的管理文件
library.pri libs库的管理文件
plugin.pri plugins插件的管理文件
tool.pri tools工具的管理文件

这几个文件分别对应QtCreator的qtcreator.pri、qtcreatorlibrary.pri、qtcreatorplugin.pri、qtcreatortool.pri,只是做了一些细微的处理。

3.2.1. qtproject.pri

qtproject.pri是整个工程的基础管理文件,定义了工程版本,通用函数,应用输出路径,共用头文件路径,库路径,库头文件路径,库链接等。

  • 工程目录
isEmpty(QTPROJECT_DIR):             QTPROJECT_DIR = $$PWD
isEmpty(QTPROJECT_OUT_PWD):         QTPROJECT_OUT_PWD = $$OUT_PWD
isEmpty(QTPROJECT_PRO_FILE_PWD):    QTPROJECT_PRO_FILE_PWD = $$_PRO_FILE_PWD_
isEmpty(QTPROJECT_PRO_FILE):        QTPROJECT_PRO_FILE = $$_PRO_FILE_
  • 引用库的函数
defineReplace(qtLibraryName) {
   RET = $$qtLibraryTargetName($$1)
   win32 {
      VERSION_LIST = $$split(QTPROJECT_VERSION, .)
      RET = $$RET$$first(VERSION_LIST)
   }
   return($$RET)
}

# eg: $$qtLibraryNameVersion(qcanpool, 1)
defineReplace(qtLibraryNameVersion) {
   RET = $$qtLibraryTargetName($$1)
   win32 {
      exists($$2) {
          VERSION_LIST = $$split(QTPROJECT_VERSION, .)
          RET = $$RET$$first(VERSION_LIST)
      } else {
          RET = $$RET$$2
      }
   }
   return($$RET)
}
  • 应用输出路径
# config IDE_SOURCE_TREE
IDE_SOURCE_TREE = $$QTPROJECT_DIR

isEmpty(IDE_BUILD_TREE) {
    sub_dir = $$QTPROJECT_PRO_FILE_PWD
    sub_dir ~= s,^$$re_escape($$IDE_SOURCE_TREE),,
    greaterThan(QT_MAJOR_VERSION, 4) {
        IDE_BUILD_TREE = $$clean_path($$QTPROJECT_OUT_PWD) # qt5
    } else {
        IDE_BUILD_TREE = $$QTPROJECT_OUT_PWD  # qt4
    }
    IDE_BUILD_TREE ~= s,$$re_escape($$sub_dir)$,,
}

IDE_APP_PATH = $$IDE_BUILD_TREE/bin
  • 头文件路径和库路径
INCLUDEPATH += \
    $$IDE_BUILD_TREE/src \ # for <app/app_version.h> in case of actual build directory
    $$IDE_SOURCE_TREE/src \ # for <app/app_version.h> in case of binary package with dev package
    $$IDE_SOURCE_TREE/src/libs \
    $$IDE_SOURCE_TREE/tools

win32:exists($$IDE_SOURCE_TREE/lib/qtproject) {
    # for .lib in case of binary package with dev package
    LIBS *= -L$$IDE_SOURCE_TREE/lib/qtproject
    LIBS *= -L$$IDE_SOURCE_TREE/lib/qtproject/plugins
}

QTC_PLUGIN_DIRS_FROM_ENVIRONMENT = $$(QTC_PLUGIN_DIRS)
QTC_PLUGIN_DIRS += $$split(QTC_PLUGIN_DIRS_FROM_ENVIRONMENT, $$QMAKE_DIRLIST_SEP)
QTC_PLUGIN_DIRS += $$IDE_SOURCE_TREE/src/plugins
!isEqual($$IDE_SOURCE_TREE, $$QTCANPOOL_ROOT) {
    QTC_PLUGIN_DIRS += $$QTCANPOOL_ROOT/src/plugins
}
for(dir, QTC_PLUGIN_DIRS) {
    INCLUDEPATH += $$dir
}

QTC_LIB_DIRS_FROM_ENVIRONMENT = $$(QTC_LIB_DIRS)
QTC_LIB_DIRS += $$split(QTC_LIB_DIRS_FROM_ENVIRONMENT, $$QMAKE_DIRLIST_SEP)
QTC_LIB_DIRS += $$IDE_SOURCE_TREE/src/libs
!isEqual($$IDE_SOURCE_TREE, $$QTCANPOOL_ROOT) {
    QTC_LIB_DIRS += $$QTCANPOOL_ROOT/src/libs
}
for(dir, QTC_LIB_DIRS) {
    INCLUDEPATH += $$dir
}
  • 库链接
# recursively resolve plugin deps
done_plugins =
for(ever) {
    isEmpty(QTC_PLUGIN_DEPENDS): \
        break()
    done_plugins += $$QTC_PLUGIN_DEPENDS
    for(dep, QTC_PLUGIN_DEPENDS) {
        dependencies_file =
        for(dir, QTC_PLUGIN_DIRS) {
            exists($$dir/$$dep/$${dep}_dependencies.pri) {
                dependencies_file = $$dir/$$dep/$${dep}_dependencies.pri
                break()
            }
        }
        isEmpty(dependencies_file): \
            error("Plugin dependency $$dep not found")
        include($$dependencies_file)
        LIBS += -l$$qtLibraryName($$QTC_PLUGIN_NAME)
    }
    QTC_PLUGIN_DEPENDS = $$unique(QTC_PLUGIN_DEPENDS)
    QTC_PLUGIN_DEPENDS -= $$unique(done_plugins)
}

# recursively resolve library deps
done_libs =
for(ever) {
    isEmpty(QTC_LIB_DEPENDS): \
        break()
    done_libs += $$QTC_LIB_DEPENDS
    for(dep, QTC_LIB_DEPENDS) {
        dependencies_file =
        for(dir, QTC_LIB_DIRS) {
            exists($$dir/$$dep/$${dep}_dependencies.pri) {
                dependencies_file = $$dir/$$dep/$${dep}_dependencies.pri
                break()
            }
        }
        isEmpty(dependencies_file): \
            error("Library dependency $$dep not found")
        include($$dependencies_file)
        LIBS += -l$$qtLibraryName($$QTC_LIB_NAME)
    }
    QTC_LIB_DEPENDS = $$unique(QTC_LIB_DEPENDS)
    QTC_LIB_DEPENDS -= $$unique(done_libs)
}

3.2.2. library.pri

library.pri是所有库的管理文件,统一设置了库的名称,输出目录等信息。

isEmpty(QTLIBRARY_PRO_FILE_PWD):    QTLIBRARY_PRO_FILE_PWD = $$_PRO_FILE_PWD_

include($$replace(QTLIBRARY_PRO_FILE_PWD, ([^/]+$), \\1/\\1_dependencies.pri))
TARGET = $$QTC_LIB_NAME

include(../qtproject.pri)

# use precompiled header for libraries by default
isEmpty(PRECOMPILED_HEADER):PRECOMPILED_HEADER = $$PWD/shared/qtproject_pch.h

win32 {
    DLLDESTDIR = $$IDE_APP_PATH
}

DESTDIR = $$IDE_LIBRARY_PATH

osx {
    QMAKE_LFLAGS_SONAME = -Wl,-install_name,@rpath/
    QMAKE_LFLAGS += -compatibility_version $$QTCREATOR_COMPAT_VERSION
}

RPATH_BASE = $$IDE_LIBRARY_PATH
include(rpath.pri)

TARGET = $$qtLibraryTargetName($$TARGET)

TEMPLATE = lib
CONFIG += shared dll

contains(QT_CONFIG, reduce_exports):CONFIG += hide_symbols

win32 {
    dlltarget.path = $$INSTALL_BIN_PATH
    INSTALLS += dlltarget
} else {
    target.path = $$INSTALL_LIBRARY_PATH
    INSTALLS += target
}

3.3. 使用

以应用fancydemo和库qcanpool为例进行介绍。

3.3.1. 库依赖关系

库的依赖关系和名字等写在xxx_dependencies.pri文件中,qcanpool_dependencies.pri如下:

QTC_LIB_NAME = qcanpool

qcanpoool库名字为qcanpool,暂时没有依赖别的库,所以没有依赖关系,依赖关系通过QTC_LIB_DEPENDS += xxx指明。

3.3.2. 库添加

每个库都是一个工程,在libs.pro的SUBDIRS中添加即可:
qtcanpool 知 01:工程管理_第42张图片
在SUBDIRS中添加库工程后,会自动解析其依赖关系。

3.3.3. 应用添加

fancydemo是一个可执行应用程序,在demos.pro的SUBDIRS中添加即可:
qtcanpool 知 01:工程管理_第43张图片

3.3.4. 库链接

库的引用通过函数qtLibraryName进行解析,因为库文件名会加调试或版本等信息,用函数可以屏蔽这些信息,只要关注库名字即可。
qtcanpool 知 01:工程管理_第44张图片
应用输出目录引用qtproject.pri的IDE_APP_PATH即可。

4. 后语

作者没使用过插件开发,所以没有讲解插件库,如果有需要,可以自行尝试。
其它pri,lib、plugin、module等请自行解锁,欢迎评论交流!

你可能感兴趣的:(qtcanpool,qt)