Qt编程:qmake - .pri详解

.pri 文件详解

.pri 文件是 qmake 的包含文件(Include File),用于在多个 .pro 项目文件中共享配置和设置,类似于 C/C++ 中的头文件。以下是关于 .pri 文件的全面解析。

基本概念

主要特点

  • 共享配置:可以在多个 .pro 文件中复用相同的配置

  • 模块化管理:将公共设置、编译器标志、路径定义等集中管理

  • 层次结构:支持嵌套包含,一个 .pri 可以包含其他 .pri

  • 平台特定配置:可以在单个文件中管理多平台设置

命名约定

  • 通常以 .pri 为扩展名

  • 常见命名:common.priconfig.prisettings.pri 等

  • 可以按功能模块命名:qt_settings.prithirdparty.pri 等

基本语法

包含方式

在 .pro 文件中包含 .pri 文件:

include(path/to/file.pri)

或使用相对路径:

include(../common/config.pri)

示例结构

# common.pri 示例

# 定义变量
MY_PROJECT_ROOT = $$PWD/..

# 公共编译选项
CONFIG += c++17 warn_on debug_and_release

# Qt 模块配置
QT += core gui network

# 公共包含路径
INCLUDEPATH += \
    $$MY_PROJECT_ROOT/include \
    $$MY_PROJECT_ROOT/thirdparty

# 平台特定设置
win32 {
    LIBS += -lws2_32
}

unix:!macx {
    LIBS += -lpthread
}

核心功能

1. 变量定义与使用

# 定义变量
MY_LIB_PATH = $$PWD/../libs

# 使用变量
LIBS += -L$$MY_LIB_PATH -lmylib

# 条件变量赋值
!defined(MY_DEBUG_LEVEL, var): MY_DEBUG_LEVEL = 1

2. 函数定义与使用

# 定义函数
defineTest(addDebug) {
    CONFIG += debug
    DEFINES += DEBUG_MODE=$$1
    return(true)
}

# 使用函数
!addDebug(3) {
    message("Debug configuration failed")
}

3. 条件配置

# 根据变量值配置
contains(QT, network) {
    DEFINES += HAVE_NETWORK
}

# 平台判断
macx {
    ICON = myapp.icns
} else {
    DEFINES += NO_RESOURCE_ICON
}

4. 文件操作

# 检查文件是否存在
exists($$MY_PROJECT_ROOT/custom.pri) {
    include($$MY_PROJECT_ROOT/custom.pri)
}

# 遍历目录
for(file, $$files($$PWD/*.cpp)) {
    SOURCES += $$file
}

高级用法

1. 配置继承与覆盖

# base.pri
CONFIG += feature_base
DEFINES += BASE_DEFINE=1

# derived.pri
include(base.pri)
CONFIG -= feature_base
CONFIG += feature_derived
DEFINES += DERIVED_DEFINE=1

2. 参数化包含

# config.pri
!defined(DEBUG_LEVEL, var): DEBUG_LEVEL = 1
DEFINES += DEBUG_LEVEL=$$DEBUG_LEVEL

# project.pro
include(config.pri DEBUG_LEVEL=3)

3. 错误处理与验证

# 检查必需变量
isEmpty(MY_PROJECT_ROOT) {
    error("MY_PROJECT_ROOT must be defined")
}

# 版本检查
versionAtLeast(QT_VERSION, 5.15.0) {
    DEFINES += QT_NEW_FEATURES
} else {
    warning("Requires Qt 5.15.0 or higher")
}

4. 生成文件

# 生成配置文件
version.target = version.h
version.commands = echo "#define APP_VERSION \\"$$VERSION\\"" > $$version.target
QMAKE_EXTRA_TARGETS += version
PRE_TARGETDEPS += version.h

实际应用示例

场景1:管理第三方库

# thirdparty.pri

# Boost 配置
BOOST_ROOT = $$MY_PROJECT_ROOT/thirdparty/boost
INCLUDEPATH += $$BOOST_ROOT
win32 {
    LIBS += -L$$BOOST_ROOT/lib -lboost_system
}

# OpenCV 配置
!defined(OPENCV_DIR): OPENCV_DIR = /usr/local/opencv
exists($$OPENCV_DIR) {
    INCLUDEPATH += $$OPENCV_DIR/include
    LIBS += -L$$OPENCV_DIR/lib -lopencv_core -lopencv_highgui
} else {
    error("OpenCV not found at $$OPENCV_DIR")
}

场景2:跨平台配置

# platform.pri

# 编译器标志
QMAKE_CXXFLAGS += -Wall -Wextra

# 平台特定设置
win32 {
    # Windows 配置
    DEFINES += WIN32_LEAN_AND_MEAN
    RC_FILE = resources.rc
}

macx {
    # macOS 配置
    QMAKE_MACOSX_DEPLOYMENT_TARGET = 10.15
    ICON = app.icns
}

linux {
    # Linux 配置
    DEFINES += LINUX
    QMAKE_LFLAGS += -Wl,--as-needed
}

场景3:功能模块化

# features.pri

# 可选功能开关
contains(CONFIG, feature_audio) {
    QT += multimedia
    DEFINES += WITH_AUDIO
    SOURCES += audiomanager.cpp
    HEADERS += audiomanager.h
}

contains(CONFIG, feature_network) {
    QT += network
    DEFINES += WITH_NETWORK
    include(network_config.pri)
}

示例分析

!isEmpty(CONFIG_PRI_INCLUDED):error("config.pri already included")
CONFIG_PRI_INCLUDED = 1

CONFIG_VERSION = 1.0.0

QT += core gui widgets
CONFIG += no_batch c++14 no_moc_predefs

CONFIG += force_debug_info

defineReplace(qtLibraryTargetName) {
   unset(LIBRARY_NAME)
   LIBRARY_NAME = $$1
   CONFIG(debug, debug|release) {
      !debug_and_release|build_pass {
            win32:RET = $$member(LIBRARY_NAME, 0)d
      }
   }
   isEmpty(RET):RET = $$LIBRARY_NAME
   return($$RET)
}

defineTest(qtCopy) {
    source = $$system_path($$1)
    target = $$system_path($$2)
    win32:system(xcopy $$source $$target  /S /H /D /F /Y)
    return(true)
}

exists($$PWD/../userconfig.pri) {
    include($$PWD/../userconfig.pri)
    isEmpty(CONFIG_OUTPUT_PATH):CONFIG_OUTPUT_PATH = $$PWD/../bin
}


isEmpty(APP_TARGET):APP_TARGET = Application

isEmpty(CONFIG_SOURCE_TREE):CONFIG_SOURCE_TREE = $$PWD

isEmpty(CONFIG_BUILD_TREE) {
    sub_dir = $$_PRO_FILE_PWD_
    sub_dir ~= s,^$$re_escape($$PWD),,
    CONFIG_BUILD_TREE = $$clean_path($$OUT_PWD)
    CONFIG_BUILD_TREE ~= s,$$re_escape($$sub_dir)$,,
}

exists($$PWD/../userconfig.pri) {
    include($$PWD/../userconfig.pri)
    isEmpty(CONFIG_OUTPUT_PATH):CONFIG_OUTPUT_PATH = $$PWD/../bin
}


isEmpty(CONFIG_OUTPUT_PATH){
CONFIG(release, debug|release):CONFIG_OUTPUT_PATH     = $$CONFIG_SOURCE_TREE/bin
CONFIG(  debug, debug|release):CONFIG_OUTPUT_PATH     = $$CONFIG_SOURCE_TREE/bin
}


CONFIG(release, debug|release):CONFIG_APP_PATH     = $$CONFIG_OUTPUT_PATH/application
CONFIG(  debug, debug|release):CONFIG_APP_PATH     = $$CONFIG_OUTPUT_PATH/application_debug

CONFIG(release, debug|release):CONFIG_LIBRARY_PATH = $$CONFIG_OUTPUT_PATH/application
CONFIG(  debug, debug|release):CONFIG_LIBRARY_PATH = $$CONFIG_OUTPUT_PATH/application_debug

CONFIG(release, debug|release):CONFIG_PLUGIN_PATH  = $$CONFIG_OUTPUT_PATH/plugins

CONFIG_3RDPARTY_PATH = $$CONFIG_OUTPUT_PATH/3rdparty

CONFIG_3RD_ROOT = $$(CONFIG_3RD_ROOT)
isEmpty(CONFIG_3RD_ROOT):error("can't find CONFIG_3RD_ROOT in system environment variable!")

CONFIG_LINK_LIBRARY_PATH = $$CONFIG_LIBRARY_PATH

INSTALL_ROOT = E:/

CONFIG_PREFIX = qframework

INSTALL_BIN_PATH     = $$INSTALL_ROOT/$$CONFIG_PREFIX/bin
INSTALL_APP_PATH     = $$INSTALL_ROOT/$$CONFIG_PREFIX/bin
INSTALL_LIB_PATH     = $$INSTALL_ROOT/$$CONFIG_PREFIX/lib

INCLUDEPATH += \
    $$CONFIG_SOURCE_TREE \
    $$CONFIG_SOURCE_TREE/libs \
    $$CONFIG_SOURCE_TREE/shared

LIBS *= -L$$CONFIG_LINK_LIBRARY_PATH # libraries
exists($$CONFIG_LIBRARY_PATH): LIBS *= -L$$CONFIG_LIBRARY_PATH  # library path from output path

CONFIG_PLUGIN_DIRS_FROM_ENVIRONMENT = $$(CONFIG_PLUGIN_DIRS)
CONFIG_PLUGIN_DIRS += $$split(CONFIG_PLUGIN_DIRS_FROM_ENVIRONMENT, $$QMAKE_DIRLIST_SEP)
CONFIG_PLUGIN_DIRS += $$CONFIG_SOURCE_TREE/plugins

for(dir, CONFIG_PLUGIN_DIRS) {
    INCLUDEPATH += $$dir
}

CONFIG += \
    depend_includepath \
    no_include_pwd

# recursively resolve library deps
done_libs =
for(ever) {
    isEmpty(CONFIG_LIB_DEPENDS): \
        break()
    done_libs += $$CONFIG_LIB_DEPENDS
    for(dep, CONFIG_LIB_DEPENDS) {
        include($$PWD/libs/$$dep/$${dep}_dependencies.pri)
        LIBS += -l$$qtLibraryTargetName($$CONFIG_LIB_NAME)
    }
    CONFIG_LIB_DEPENDS = $$unique(CONFIG_LIB_DEPENDS)
    CONFIG_LIB_DEPENDS -= $$unique(done_libs)
}

config.pri 文件

这个文件是一个 Qt 项目配置的共享文件(.pri),用于定义项目构建的通用设置、路径和依赖关系管理。逐一详细分析如下:

!isEmpty(CONFIG_PRI_INCLUDED):error("config.pri already included")
CONFIG_PRI_INCLUDED = 1
  • 防止重复包含的保护机制,如果已经包含过则报错

QT += core gui widgets
CONFIG += no_batch c++14 no_moc_predefs
  • 添加 Qt 模块依赖

  • 配置项目选项:禁用批处理模式、启用 C++14、禁用 moc 预定义

CONFIG += force_debug_info
  • 强制生成调试信息,即使是在 release 模式下

defineReplace(qtLibraryTargetName) {
   unset(LIBRARY_NAME)
   LIBRARY_NAME = $$1
   CONFIG(debug, debug|release) {
      !debug_and_release|build_pass {
            win32:RET = $$member(LIBRARY_NAME, 0)d
      }
   }
   isEmpty(RET):RET = $$LIBRARY_NAME
   return($$RET)
}
  • 定义替换函数,用于生成库文件名

  • 在 Windows 平台 debug 模式下,给库名添加 'd' 后缀

defineTest(qtCopy) {
    source = $$system_path($$1)
    target = $$system_path($$2)
    win32:system(xcopy $$source $$target  /S /H /D /F /Y)
    return(true)
}
  • 定义测试函数,用于文件复制

  • Windows 下使用 xcopy 命令

exists($$PWD/../userconfig.pri) {
    include($$PWD/../userconfig.pri)
    isEmpty(CONFIG_OUTPUT_PATH):CONFIG_OUTPUT_PATH = $$PWD/../bin
}
  • 检查并包含上级目录中的 userconfig.pri(用户自定义配置)

  • 如果用户配置中没有定义输出路径,则设置为 ../bin

isEmpty(APP_TARGET):APP_TARGET = Application
  • 设置默认应用程序名称

isEmpty(CONFIG_SOURCE_TREE):CONFIG_SOURCE_TREE = $$PWD
  • 设置源代码基础路径为当前目录

isEmpty(CONFIG_BUILD_TREE) {
    sub_dir = $$_PRO_FILE_PWD_
    sub_dir ~= s,^$$re_escape($$PWD),,
    CONFIG_BUILD_TREE = $$clean_path($$OUT_PWD)
    CONFIG_BUILD_TREE ~= s,$$re_escape($$sub_dir)$,,
}
  • 计算构建目录路径,基于输出目录和当前项目相对路径

# 重复的 userconfig.pri 检查(前面已经出现过)
exists($$PWD/../userconfig.pri) {
    include($$PWD/../userconfig.pri)
    isEmpty(CONFIG_OUTPUT_PATH):CONFIG_OUTPUT_PATH = $$PWD/../bin
}

isEmpty(CONFIG_OUTPUT_PATH){
CONFIG(release, debug|release):CONFIG_OUTPUT_PATH     = $$CONFIG_SOURCE_TREE/bin
CONFIG(  debug, debug|release):CONFIG_OUTPUT_PATH     = $$CONFIG_SOURCE_TREE/bin
}
  • 设置默认输出路径为源代码目录下的 bin 目录

#应用程序路径
CONFIG(release, debug|release):CONFIG_APP_PATH     = $$CONFIG_OUTPUT_PATH/application
CONFIG(  debug, debug|release):CONFIG_APP_PATH     = $$CONFIG_OUTPUT_PATH/application_debug
#库路径
CONFIG(release, debug|release):CONFIG_LIBRARY_PATH = $$CONFIG_OUTPUT_PATH/application
CONFIG(  debug, debug|release):CONFIG_LIBRARY_PATH = $$CONFIG_OUTPUT_PATH/application_debug
  • 设置应用程序和库的输出路径,区分 debug/release

CONFIG(release, debug|release):CONFIG_PLUGIN_PATH  = $$CONFIG_OUTPUT_PATH/plugins
  • 设置插件输出路径

CONFIG_3RDPARTY_PATH = $$CONFIG_OUTPUT_PATH/3rdparty
  • 设置第三方库路径

CONFIG_3RD_ROOT = $$(CONFIG_3RD_ROOT)
isEmpty(CONFIG_3RD_ROOT):error("can't find CONFIG_3RD_ROOT in system environment variable!")
  • 从环境变量读取第三方库根目录,如果没有设置则报错

CONFIG_LINK_LIBRARY_PATH = $$CONFIG_LIBRARY_PATH
  • 设置链接库路径

INSTALL_ROOT = E:/

CONFIG_PREFIX = qframework

INSTALL_BIN_PATH     = $$INSTALL_ROOT/$$CONFIG_PREFIX/bin
INSTALL_APP_PATH     = $$INSTALL_ROOT/$$CONFIG_PREFIX/bin
INSTALL_LIB_PATH     = $$INSTALL_ROOT/$$CONFIG_PREFIX/lib
  • 定义安装路径结构,默认安装到 E:/qframework/

INCLUDEPATH += \
    $$CONFIG_SOURCE_TREE \
    $$CONFIG_SOURCE_TREE/libs \
    $$CONFIG_SOURCE_TREE/shared
  • 添加默认包含路径

LIBS *= -L$$CONFIG_LINK_LIBRARY_PATH # libraries
exists($$CONFIG_LIBRARY_PATH): LIBS *= -L$$CONFIG_LIBRARY_PATH  # library path from output path
  • 添加库搜索路径

CONFIG_PLUGIN_DIRS_FROM_ENVIRONMENT = $$(CONFIG_PLUGIN_DIRS)
CONFIG_PLUGIN_DIRS += $$split(CONFIG_PLUGIN_DIRS_FROM_ENVIRONMENT, $$QMAKE_DIRLIST_SEP)
CONFIG_PLUGIN_DIRS += $$CONFIG_SOURCE_TREE/plugins
  • 从环境变量获取插件目录并添加到搜索路径

for(dir, CONFIG_PLUGIN_DIRS) {
    INCLUDEPATH += $$dir
}
  • 将所有插件目录添加到包含路径

CONFIG += \
    depend_includepath \
    no_include_pwd
  • 添加配置选项:依赖包含路径、不自动包含当前目录

# recursively resolve library deps
done_libs =
for(ever) {
    isEmpty(CONFIG_LIB_DEPENDS): \
        break()
    done_libs += $$CONFIG_LIB_DEPENDS
    for(dep, CONFIG_LIB_DEPENDS) {
        include($$PWD/libs/$$dep/$${dep}_dependencies.pri)
        LIBS += -l$$qtLibraryTargetName($$CONFIG_LIB_NAME)
    }
    CONFIG_LIB_DEPENDS = $$unique(CONFIG_LIB_DEPENDS)
    CONFIG_LIB_DEPENDS -= $$unique(done_libs)
}
  • 递归解析库依赖关系,自动包含依赖项并添加链接指令

这个配置文件提供了一个高度可配置的项目构建框架,特别适合大型 Qt 项目

实践建议

  1. 合理组织目录结构

    project/
    ├── config/
    │   ├── config.pri
    │   ├── platform.pri
    │   └── features.pri
    ├── modules/
    │   ├── module1/
    │   └── module2/
    └── app.pro
  2. 分层设计

    • 基础层:config.pri (编译器设置)

    • 平台层:platform.pri (平台差异)

    • 功能层:features.pri (功能模块)

    • 项目层:项目特定配置

  3. 文档注释

    ###############################################
    # Common Configuration
    #
    # Variables:
    #   MY_PROJECT_ROOT - 项目根目录
    #   BUILD_MODE      - 构建模式(debug/release)
    #
    # Usage:
    #   include(../config/config.pri)
    ###############################################
  4. 避免过度复杂

    • 保持 .pri 文件专注单一功能

    • 避免深层嵌套包含

    • 限制条件判断的复杂度

常见问题解决

1. 变量作用域问题

  • 问题:变量在不同 .pri 中意外覆盖

  • 解决:使用唯一变量名前缀,如 MYPROJ_PATHS

2. 路径问题

  • 问题:相对路径在不同包含层级下解析不同

  • 解决:始终基于 $$PWD 或项目根变量构建路径

3. 循环包含

  • 问题:A.pri 包含 B.pri,B.pri 又包含 A.pri

  • 解决:重构设计,引入第三方的公共 .pri

4. 调试困难

  • 问题:难以追踪变量定义位置

  • 解决:使用 message() 调试:

    message("Current value: $$VAR_NAME (defined in $$_FILE_)")

总结

.pri 文件是 qmake 项目中强大的配置管理工具,合理使用可以:

  • 大幅减少 .pro 文件中的重复配置

  • 集中管理平台差异和编译选项

  • 实现模块化、可复用的项目配置

  • 提高大型项目的可维护性

掌握 .pri 文件的使用技巧,能够使 Qt 项目的构建系统更加清晰、灵活和易于维护。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值