在跨平台桌面应用开发中,C++ 结合 Qt 框架是经典且强大的选择。然而,传统的 Qt 项目创建和配置,尤其是涉及 CMake 构建系统时,往往需要手动编写复杂的CMakeLists.txt文件,配置 Qt 模块、链接库、资源文件等,过程繁琐且容易出错。对于需要快速原型验证或频繁创建新项目的开发者来说,这无疑增加了时间成本。
本文将介绍一种高效的方法:使用 Python 脚本一键生成一个结构完整、可直接编译运行的 C++ Qt 跨平台 CMake 项目。这种方法不仅能自动化重复劳动,还能确保项目结构规范统一,特别适合团队协作和 CI/CD 流程的集成。无论你是 Qt 新手,还是希望优化工作流的资深开发者,都能从中获益。
1. 核心概念与工具链解析
在开始自动化之前,我们需要理解整个工具链中各个组件的作用及其相互关系。
1.1 Qt 与 C++ 跨平台开发
Qt 是一个功能强大的 C++ 跨平台应用程序开发框架。它提供了丰富的 GUI 组件、网络、数据库、多媒体等模块,其“一次编写,到处编译”的特性使其成为桌面、嵌入式甚至移动端开发的热门选择。一个基础的 Qt Widgets 应用程序通常包含.pro(qmake) 或CMakeLists.txt(CMake) 项目文件、C++ 源文件、头文件以及可能的界面文件 (.ui)、资源文件 (.qrc) 等。
1.2 CMake:现代构建系统的领导者
CMake 是一个跨平台的自动化构建系统生成器。它不直接构建软件,而是根据一个名为CMakeLists.txt的配置文件,生成对应平台(如 Windows 的 Visual Studio 项目、Linux 的 Makefile、macOS 的 Xcode 项目)的原生构建文件。使用 CMake 管理 Qt 项目已成为社区主流,因为它更灵活、更强大,且与 Qt 自身的 CMake 模块集成良好。
1.3 Python 作为自动化胶水
Python 以其简洁的语法和强大的标准库(如os,shutil,argparse,pathlib)而闻名,是自动化任务的绝佳选择。我们将编写一个 Python 脚本,其核心功能是:根据用户输入的项目名称、Qt 版本等信息,在指定目录下创建标准的文件夹结构,并生成所有必要的、内容正确的项目文件。
三者协作流程:
- Python 脚本:接收参数 -> 创建目录和文件 -> 填充模板内容。
- 生成的项目:包含
CMakeLists.txt,main.cpp,MainWindow类等。 - 开发者/CI:使用 CMake 配置项目 -> 生成构建系统 -> 编译运行。
2. 环境准备与版本说明
为了成功运行本文的示例,你需要准备以下环境。请注意,版本号是示例,请根据你的实际环境进行调整。
- 操作系统:Windows 10/11, Ubuntu 20.04/22.04 LTS, 或 macOS Monterey/Ventura。本文方法本身是跨平台的。
- Python 环境:Python 3.8 或更高版本。确保
python和pip命令可用。- 验证:在终端输入
python --version或python3 --version。
- 验证:在终端输入
- CMake:版本 3.16 或更高(Qt 6 推荐 3.16+, Qt 5 推荐 3.5+)。需要将其添加到系统 PATH。
- 验证:在终端输入
cmake --version。
- 验证:在终端输入
- Qt 开发环境:
- Qt 库:Qt 5.15 或 Qt 6.2 及以上版本。可以通过 Qt 官方安装程序 或系统包管理器安装。
- 编译器:
- Windows: MSVC (Visual Studio 2019/2022) 或 MinGW。
- Linux: GCC (g++)。
- macOS: Clang (Xcode Command Line Tools)。
- 验证:确保
qmake(Qt 5) 或cmake能找到 Qt 的 CMake 包。对于 Qt 6,通常通过CMAKE_PREFIX_PATH环境变量指向 Qt 安装目录。
- 构建目录:建议在一个空目录或新文件夹中运行脚本,避免文件冲突。
关键环境变量(Qt 6 CMake 用户特别注意): 为了让 CMake 自动找到 Qt,通常需要设置CMAKE_PREFIX_PATH。例如,在 Windows 上 Qt 安装在C:\Qt\6.5.0\msvc2019_64,你需要将此路径添加到该环境变量中,或者在 CMake 命令中通过-DCMAKE_PREFIX_PATH=...指定。
3. Python 脚本设计与核心函数拆解
我们的脚本将是一个独立的.py文件。它将包含以下几个核心功能模块。
3.1 参数解析:使用argparse
脚本需要从命令行接收一些关键参数,例如项目名称和路径。argparse模块可以优雅地处理这项工作。
# 文件:create_qt_cmake_project.py import argparse def parse_arguments(): """ 解析命令行参数 """ parser = argparse.ArgumentParser( description='一键创建基于 CMake 的 C++ Qt 跨平台项目。' ) parser.add_argument( 'project_name', type=str, help='项目的名称(将用于可执行文件名和项目标识)' ) parser.add_argument( '-p', '--path', type=str, default='.', help='项目创建的根目录路径,默认为当前目录' ) parser.add_argument( '-qt', '--qt_version', type=int, choices=[5, 6], default=6, help='目标 Qt 主版本 (5 或 6),默认为 6' ) parser.add_argument( '-ns', '--namespace', type=str, default='', help='可选的 C++ 命名空间,用于包装生成的类' ) return parser.parse_args() if __name__ == "__main__": args = parse_arguments() print(f"项目名称: {args.project_name}") print(f"创建路径: {args.path}") print(f"Qt 版本: {args.qt_version}") print(f"命名空间: {args.namespace if args.namespace else '无'}")3.2 目录结构创建
一个典型的 Qt CMake 项目具有清晰的结构。我们将使用os和pathlib模块来创建它们。
import os from pathlib import Path def create_project_structure(base_path, project_name): """ 创建标准的项目目录结构 """ project_root = Path(base_path) / project_name dirs = [ project_root, project_root / 'src', project_root / 'include', project_root / 'resources', project_root / 'ui', # 存放 .ui 文件 project_root / 'build', # 构建目录,通常不提交到版本控制 ] for dir_path in dirs: dir_path.mkdir(parents=True, exist_ok=True) print(f"创建目录: {dir_path}") return project_root3.3 文件内容模板
这是脚本的核心——定义每个需要生成的文件的模板内容。我们将使用 Python 的三引号字符串来定义多行模板,并使用字符串的.format()方法或 f-string 进行变量替换。
1. 顶级 CMakeLists.txt 模板这个文件定义了项目的基本信息、CMake 最低版本、查找 Qt 包以及添加子目录。
def generate_cmakelists_txt(project_root, project_name, qt_version, namespace): content = f"""cmake_minimum_required(VERSION 3.16) # 项目名称和版本 project({project_name} VERSION 1.0.0 LANGUAGES CXX) # 设置 C++ 标准 set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) # 自动处理 Qt 的 MOC、UIC、RCC set(CMAKE_AUTOMOC ON) set(CMAKE_AUTOUIC ON) set(CMAKE_AUTORCC ON) # 查找 Qt 包 find_package(QT NAMES Qt6 Qt5 COMPONENTS Core Widgets REQUIRED) find_package(Qt${{QT_VERSION_MAJOR}} COMPONENTS Core Widgets REQUIRED) # 设置可执行文件输出目录(可选) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${{CMAKE_BINARY_DIR}}/bin) # 添加子目录 add_subdirectory(src) """ file_path = project_root / 'CMakeLists.txt' file_path.write_text(content, encoding='utf-8') print(f"生成文件: {file_path}")2. 源代码目录下的 CMakeLists.txt这个文件负责将当前目录下的源文件添加到目标中。
def generate_src_cmakelists_txt(src_dir, project_name, namespace): # 定义源文件列表 sources = ['main.cpp', 'mainwindow.cpp'] headers = ['mainwindow.h'] ui_files = ['ui/mainwindow.ui'] if (src_dir.parent / 'ui' / 'mainwindow.ui').exists() else [] resources = ['resources/resources.qrc'] if (src_dir.parent / 'resources' / 'resources.qrc').exists() else [] all_files = sources + headers + ui_files + resources # 使用相对路径 sources_str = ' '.join(sources) headers_str = ' '.join(headers) if headers else '' ui_files_str = ' '.join(ui_files) if ui_files else '' resources_str = ' '.join(resources) if resources else '' content = f"""# 添加可执行目标 add_executable(${{PROJECT_NAME}} {sources_str} {headers_str} {ui_files_str} {resources_str} ) # 链接 Qt 库 target_link_libraries(${{PROJECT_NAME}} PRIVATE Qt${{QT_VERSION_MAJOR}}::Core Qt${{QT_VERSION_MAJOR}}::Widgets) # 设置目标属性:包含头文件目录 target_include_directories(${{PROJECT_NAME}} PRIVATE ${{CMAKE_CURRENT_SOURCE_DIR}}/../include) # 如果有命名空间,可以在这里添加一个编译定义(示例) if(DEFINED PROJECT_NAMESPACE) target_compile_definitions(${{PROJECT_NAME}} PRIVATE -DPROJECT_NAMESPACE=${{PROJECT_NAMESPACE}}) endif() """ file_path = src_dir / 'CMakeLists.txt' file_path.write_text(content, encoding='utf-8') print(f"生成文件: {file_path}")3. main.cpp 模板这是应用程序的入口点。
def generate_main_cpp(src_dir, project_name, namespace): namespace_begin = f'namespace {namespace} {{\n' if namespace else '' namespace_end = f'\n}} // namespace {namespace}' if namespace else '' class_prefix = f'{namespace}::' if namespace else '' content = f"""#include "mainwindow.h" #include <QApplication> {namespace_begin} int main(int argc, char *argv[]) {{ QApplication a(argc, argv); {class_prefix}MainWindow w; w.show(); return a.exec(); }} {namespace_end} """ file_path = src_dir / 'main.cpp' file_path.write_text(content, encoding='utf-8') print(f"生成文件: {file_path}")4. MainWindow 类文件模板生成一个基本的带有菜单栏和状态栏的主窗口类。
def generate_mainwindow_h(include_dir, namespace): guard = namespace.upper() + '_' if namespace else '' guard += 'MAINWINDOW_H' namespace_begin = f'namespace {namespace} {{\n' if namespace else '' namespace_end = f'\n}} // namespace {namespace}' if namespace else '' content = f"""#ifndef {guard} #define {guard} #include <QMainWindow> QT_BEGIN_NAMESPACE namespace Ui {{ class MainWindow; }} QT_END_NAMESPACE {namespace_begin} class MainWindow : public QMainWindow {{ Q_OBJECT public: explicit MainWindow(QWidget *parent = nullptr); ~MainWindow() override; private slots: void on_actionExit_triggered(); void on_actionAbout_triggered(); private: Ui::MainWindow *ui; void setupUi(); void setupConnections(); }}; {namespace_end} #endif // {guard} """ file_path = include_dir / 'mainwindow.h' file_path.write_text(content, encoding='utf-8') print(f"生成文件: {file_path}") def generate_mainwindow_cpp(src_dir, include_dir, namespace): namespace_begin = f'namespace {namespace} {{\n' if namespace else '' namespace_end = f'\n}} // namespace {namespace}' if namespace else '' class_prefix = f'{namespace}::' if namespace else '' include_path = (include_dir / 'mainwindow.h').relative_to(src_dir.parent) content = f"""#include "{include_path}" #include "ui_mainwindow.h" #include <QMessageBox> {namespace_begin} MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) {{ ui->setupUi(this); setupUi(); setupConnections(); }} MainWindow::~MainWindow() {{ delete ui; }} void MainWindow::setupUi() {{ // 设置窗口标题 setWindowTitle(tr("My Qt Application")); // 初始化状态栏 statusBar()->showMessage(tr("Ready")); }} void MainWindow::setupConnections() {{ // 连接菜单动作的信号槽 connect(ui->actionExit, &QAction::triggered, this, &QWidget::close); connect(ui->actionAbout, &QAction::triggered, this, &MainWindow::on_actionAbout_triggered); }} void MainWindow::on_actionExit_triggered() {{ close(); }} void MainWindow::on_actionAbout_triggered() {{ QMessageBox::about(this, tr("About"), tr("This is a Qt application generated by an automated Python script.")); }} {namespace_end} """ file_path = src_dir / 'mainwindow.cpp' file_path.write_text(content, encoding='utf-8') print(f"生成文件: {file_path}")5. UI 文件模板 (mainwindow.ui)使用 XML 格式定义一个简单的 UI。
def generate_mainwindow_ui(ui_dir): content = """<?xml version="1.0" encoding="UTF-8"?> <ui version="4.0"> <class>MainWindow</class> <widget class="QMainWindow" name="MainWindow"> <property name="geometry"> <rect> <x>0</x> <y>0</y> <width>800</width> <height>600</height> </rect> </property> <property name="windowTitle"> <string>MainWindow</string> </property> <widget class="QWidget" name="centralwidget"> <layout class="QVBoxLayout" name="verticalLayout"> <item> <widget class="QLabel" name="label"> <property name="text"> <string>Hello, Qt CMake Project!</string> </property> <property name="alignment"> <set>Qt::AlignCenter</set> </property> </widget> </item> </layout> </widget> <widget class="QMenuBar" name="menubar"> <property name="geometry"> <rect> <x>0</x> <y>0</y> <width>800</width> <height>22</height> </rect> </property> <widget class="QMenu" name="menuFile"> <property name="title"> <string>File</string> </property> <addaction name="actionExit"/> </widget> <widget class="QMenu" name="menuHelp"> <property name="title"> <string>Help</string> </property> <addaction name="actionAbout"/> </widget> <addaction name="menuFile"/> <addaction name="menuHelp"/> </widget> <widget class="QStatusBar" name="statusbar"/> <action name="actionExit"> <property name="text"> <string>Exit</string> </property> </action> <action name="actionAbout"> <property name="text"> <string>About</string> </property> </action> </widget> <resources/> <connections/> </ui> """ file_path = ui_dir / 'mainwindow.ui' file_path.write_text(content, encoding='utf-8') print(f"生成文件: {file_path}")6. 资源文件模板 (resources.qrc)定义一个资源文件,虽然初始为空,但结构已准备好。
def generate_resources_qrc(resources_dir): content = """<!DOCTYPE RCC> <RCC version="1.0"> <qresource> <!-- 在此处添加你的资源文件,例如: <file>images/icon.png</file> <file>styles/style.qss</file> --> </qresource> </RCC> """ file_path = resources_dir / 'resources.qrc' file_path.write_text(content, encoding='utf-8') print(f"生成文件: {file_path}")3.4 主流程整合
最后,我们将所有函数整合到main函数中,形成完整的脚本。
def main(): args = parse_arguments() # 1. 创建项目结构 project_root = create_project_structure(args.path, args.project_name) # 2. 生成各个文件 generate_cmakelists_txt(project_root, args.project_name, args.qt_version, args.namespace) generate_main_cpp(project_root / 'src', args.project_name, args.namespace) generate_mainwindow_h(project_root / 'include', args.namespace) generate_mainwindow_cpp(project_root / 'src', project_root / 'include', args.namespace) generate_mainwindow_ui(project_root / 'ui') generate_resources_qrc(project_root / 'resources') # 注意:src/CMakeLists.txt 需要在 UI 和资源文件生成后调用,因为它会检查这些文件 generate_src_cmakelists_txt(project_root / 'src', args.project_name, args.namespace) print(f"\n✅ 项目 '{args.project_name}' 已成功创建于: {project_root}") print("\n下一步:") print(f" cd {project_root}") print(" mkdir build && cd build") print(f" cmake .. -DCMAKE_PREFIX_PATH=<你的Qt安装路径> # 例如 -DCMAKE_PREFIX_PATH=C:/Qt/6.5.0/msvc2019_64") print(" cmake --build .") print(" # 在 Windows 上,可执行文件在 build/bin/ 下;Linux/macOS 在 build/ 下") if __name__ == "__main__": main()4. 完整实战:从脚本到可运行应用
现在,让我们使用上面编写的完整脚本,一步步创建一个名为MyApp的项目。
4.1 获取并运行脚本
- 将前面所有代码块整合到一个名为
create_qt_cmake_project.py的文件中。 - 打开终端(命令行),导航到存放该脚本的目录。
- 运行以下命令查看帮助:
python create_qt_cmake_project.py --help输出应类似于:
usage: create_qt_cmake_project.py [-h] [-p PATH] [-qt {5,6}] [-ns NAMESPACE] project_name 一键创建基于 CMake 的 C++ Qt 跨平台项目。 positional arguments: project_name 项目的名称(将用于可执行文件名和项目标识) optional arguments: -h, --help show this help message and exit -p PATH, --path PATH 项目创建的根目录路径,默认为当前目录 -qt {5,6}, --qt_version {5,6} 目标 Qt 主版本 (5 或 6),默认为 6 -ns NAMESPACE, --namespace NAMESPACE 可选的 C++ 命名空间,用于包装生成的类4.2 创建项目
假设我们想在D:\Projects目录下创建一个使用 Qt 6、位于MyNamespace命名空间中的MyApp项目。
python create_qt_cmake_project.py MyApp -p "D:\Projects" -qt 6 -ns MyNamespace运行后,终端会输出创建的文件和目录:
创建目录: D:\Projects\MyApp 创建目录: D:\Projects\MyApp\src ... 生成文件: D:\Projects\MyApp\CMakeLists.txt 生成文件: D:\Projects\MyApp\src\main.cpp ... ✅ 项目 'MyApp' 已成功创建于: D:\Projects\MyApp4.3 生成的项目结构
进入D:\Projects\MyApp,你会看到如下结构:
MyApp/ ├── CMakeLists.txt # 顶级 CMake 配置 ├── build/ # 构建目录(空) ├── include/ │ └── mainwindow.h # MainWindow 头文件 ├── resources/ │ └── resources.qrc # Qt 资源文件 ├── src/ │ ├── CMakeLists.txt # 源代码 CMake 配置 │ ├── main.cpp # 程序入口 │ └── mainwindow.cpp # MainWindow 实现 └── ui/ └── mainwindow.ui # Qt Designer UI 文件4.4 配置与构建项目
进入构建目录:
cd D:\Projects\MyApp mkdir build cd build运行 CMake 配置: 你需要告诉 CMake Qt 的安装位置。将
<你的Qt安装路径>替换为实际路径,例如C:\Qt\6.5.0\msvc2019_64或/home/user/Qt/6.5.0/gcc_64。# Windows (MSVC) 或 Linux/macOS 通用命令 cmake .. -DCMAKE_PREFIX_PATH=<你的Qt安装路径>如果 CMake 成功找到 Qt,你将看到配置成功的输出,并生成 Visual Studio 解决方案 (.sln)、Makefile 等。
编译项目:
# 通用编译命令 cmake --build . # 或者在 Windows 上使用生成的 .sln 文件在 Visual Studio 中打开编译 # 或者在 Linux/macOS 上直接 `make`
4.5 运行应用程序
编译成功后,可执行文件通常位于build目录下(根据CMAKE_RUNTIME_OUTPUT_DIRECTORY设置,可能在build/bin)。
- Windows:在
build\bin\Debug或build\bin\Release中找到MyApp.exe,双击运行。 - Linux/macOS:在
build目录下找到MyApp可执行文件,在终端中运行./MyApp。
运行后,一个标题为 “My Qt Application”、中央显示 “Hello, Qt CMake Project!”、带有 File/Help 菜单和状态栏的窗口应该会弹出。点击 Help -> About,会弹出一个关于对话框。
5. 常见问题与排查思路
在自动化创建和手动构建过程中,你可能会遇到以下问题。
| 问题现象 | 常见原因 | 解决思路 |
|---|---|---|
| CMake 错误:找不到 Qt | 1.CMAKE_PREFIX_PATH未设置或设置错误。2. Qt 未安装或版本不匹配。 3. 系统 PATH 中缺少 qmake(Qt 5) 或 CMake 未找到 Qt 的 CMake 配置。 | 1. 确认-DCMAKE_PREFIX_PATH指向正确的 Qt 安装目录(包含lib/cmake子目录)。2. 运行 qmake --version(Qt 5) 或检查 Qt 安装。3. 对于 Qt 6,确保安装时包含了对应编译器的开发组件。 |
| CMake 错误:未知的 Qt 模块 | CMakeLists.txt 中find_package指定的组件(如Widgets,Core)在安装的 Qt 版本中不存在或未安装。 | 1. 检查 Qt 安装程序,确保安装了Qt Widgets等必需模块。2. 对于 Qt 5,模块名可能是 Qt5Widgets;脚本已做兼容处理。3. 如果使用 Qt 6 但需要 Charts 等模块,需在脚本和 find_package中添加。 |
编译错误:找不到ui_mainwindow.h | 1.CMAKE_AUTOUIC未设置为ON。2. .ui文件未添加到目标的源文件列表中。3. 构建目录残留了旧配置。 | 1. 确保顶级CMakeLists.txt中设置了set(CMAKE_AUTOUIC ON)。2. 检查 src/CMakeLists.txt,确保ui/mainwindow.ui在add_executable的文件列表中。3. 尝试清理 build目录并重新运行 CMake。 |
| 链接错误:未定义的引用 | 1.target_link_libraries未链接必要的 Qt 模块。2. 使用了 Qt 模块但未 #include对应头文件。3. 编译器与 Qt 库的编译器不匹配(如 MSVC 链接了 MinGW 的 Qt 库)。 | 1. 确认target_link_libraries包含了所有用到的 Qt 模块,如Qt6::Core,Qt6::Widgets。2. 检查代码中的 #include语句。3. 确保使用的编译器套件与构建 Qt 时的一致。 |
| Python 脚本运行错误 | 1. Python 版本过低。 2. 路径包含中文或特殊字符。 3. 对目标目录没有写权限。 | 1. 使用python --version确认版本 >= 3.8。2. 避免在项目路径中使用中文或空格。 3. 以管理员/root权限运行,或更改目标目录权限。 |
生成的项目无法打开.ui文件 | Qt Creator 或 Designer 未安装,或者.ui文件关联错误。 | 安装 Qt Creator,它内置了 Designer。也可以单独安装qttools组件。在 Qt Creator 中打开项目即可编辑.ui文件。 |
6. 脚本扩展与工程化最佳实践
基础的脚本已经可以工作,但要用于真实项目或团队,还需要考虑更多。
6.1 增强脚本功能
- 支持更多 Qt 模块:修改脚本,允许用户通过参数选择额外的模块(如
Charts,Network,Sql),并动态更新CMakeLists.txt中的find_package和target_link_libraries。 - 自定义项目类型:除了主窗口应用,还可以支持控制台应用、动态库、静态库等模板。
- 集成
.gitignore:自动生成一个针对 Qt CMake 项目的.gitignore文件,忽略build/,*.user,*.autosave等。 - 许可证与作者信息:在文件头部添加注释模板,包含许可证、作者、创建日期等信息。
- 参数验证:增加对项目名称(是否合法标识符)、路径是否存在等的验证。
6.2 工程化与团队协作建议
- 版本控制:将
create_qt_cmake_project.py脚本本身纳入团队的版本控制(如 Git),作为项目模板工具。 - 模板仓库:可以创建一个标准的“项目模板” Git 仓库。新项目通过
git clone模板仓库并运行一个初始化脚本(或直接使用本 Python 脚本)来创建,确保所有项目起点一致。 - CMake 模块化:对于大型项目,将
CMakeLists.txt拆分为多个模块(如FindDependencies.cmake,ConfigureCompiler.cmake),使主文件更清晰。脚本可以生成这种模块化结构。 - 依赖管理:考虑集成
vcpkg,conan或FetchContent来管理第三方 C++ 依赖。脚本可以初始化这些工具的配置文件。 - CI/CD 集成:在脚本生成的项目中,预先配置好 GitHub Actions、GitLab CI 或 Jenkins 的流水线文件(如
.github/workflows/build.yml),实现提交后自动构建和测试。
6.3 安全与健壮性
- 路径处理:始终使用
pathlib或os.path处理路径,确保跨平台兼容性。 - 错误处理:在文件读写、目录创建等操作周围添加
try-except块,给用户友好的错误提示,而不是 Python 栈跟踪。 - 幂等性:运行脚本多次不应破坏已有文件。可以通过检查文件是否存在并提示用户来决定是覆盖、跳过还是备份。
- 输入清理:对用户输入的
project_name和namespace进行清理,移除不合法的字符,防止用于生成 C++ 代码时导致编译错误。
通过以上步骤,你不仅拥有一个一键创建项目的工具,更建立起一个标准化、可扩展的项目初始化流程,能显著提升个人和团队的开发效率,减少项目初期的配置错误。