C++源码生成·第二章·构建项目

C++源码生成·第二章·构建项目

1 概述

书接上回,在「C++源码生成·第一章·初试模板」中展示了通过复制模板文件进行源码生成的方案,同时也描述了使用模板文件在源码生成工具中的作用,包括:提高代码一致性、减少重复劳动、易于维护和更新、增强可扩展性、促进最佳实践等优点。在本章将继续完善 Python 脚本,通过代替模板中的关键字来发挥模板文件的真正作用,实现源码的真正动态生成功能。

2 创建项目

2.1 命令行接收项目名称

在命令行中接收项目名称作为参数可以自动化项目的创建过程,命令行接口允许用户根据需要输入项目名称,这提供了极大的灵活性。在「Python 处理命令行参数」一文中,展示了在 Python 脚本中如何使用 argparse 模块来处理命令行参数。

在本项目中我们使用 argparse 模块来处理命令行参数,命令行的用法如下:

./cppgen <project_path>

其中的 <project_path> 指项目名称的路径,是必填参数。Python 脚本根据项目路径创建目录,并且使用路径的文件名作为项目名称,比如:./cppgen /path/to/hello,则项目名称为 hello

以下 Python 脚本展示了如何获取命令行中的项目名称:

cppgen

#!/usr/bin/python3

import argparse

def main():
    # Create an ArgumentParser object
    parser = argparse.ArgumentParser(description="cpp source code generator")

    # Add positional arguments
    parser.add_argument('project_path', type=str, help='to be generated project path')

    # Parse the command-line arguments
    args = parser.parse_args()

    # Handle args.project_path
    print(f"project path: {args.project_path}")

if __name__ == "__main__":
    main()

在以上 Python 脚本中:

  1. 我们创建了一个 ArgumentParser 对象,并为其提供了描述信息。
  2. 使用 add_argument() 方法添加了一个参数:
    • project_path 项目路径,这是一个必填参数,必须由用户输入。
  3. 调用 parse_args() 方法解析命令行参数,并将结果存储在 args 对象中。
  4. 解析后的参数存储在 args.project_path 中,示例中我们将其打印出来。

argparse 模块自动提供了命令行处理方法的帮助信息与错误处理逻辑,可以使用 -h 参数查看:

./cppgen -h

结果显示如下:

usage: cppgen [-h] project_path

cpp source code generator

positional arguments:
  project_path  to be generated project path

optional arguments:
  -h, --help    show this help message and exit

如果输入项目名称,可以使用以下命令:

./cppgen hello

这将输出:

project path: hello

2.2 创建项目目录

通过 {args.project_path} 我们可以获取命令行参数输入进来的项目路径名称,我们创建一个 generate_source_code() 方法根据项目路径名称创建项目的完整目录结构。

import re
from pathlib import Path

def is_valid_name(name):
    # Define the regular expression pattern for validation  
    pattern = r'^[A-Za-z][A-Za-z0-9_.-]*$'
    match = re.match(pattern, name)
    return match is not None

def generate_source_code(project_path):
    project_name = Path(project_path).name

    if (not is_valid_name(project_name)):
        print(f"failed: '{project_name}' is invalid project name")
        sys.exit(1)
    
    try:
        os.mkdir(project_path)
        print(f"project '{project_path}' created")

        source_path = project_path + '/src'
        os.mkdir(source_path)
        print(f"'{source_path}' created")
    except OSError as e:
        print(f"failed: {e}")
        sys.exit(1)

在以上 Python 脚本中:

  1. 创建一个表示由 project_name 变量指定的路径的 Path 对象,并获取这个路径中的最后一部分(文件名或目录名)。
  2. 我们需要先从 pathlib 模块中导入 Path 类,例如通过 from pathlib import Path
  3. is_valid_name 方法中定义了一个正则表达式,校验一个项目名称是否符合特定的规则(包含字母与数字,以及特定的符号如 ‘-’ 和 ‘_’ 以及 ‘.’,并且首字符必须为大小写字母)。如 “hello_2.7” 为正常的项目名称,而 “1a^@#” 包含非法字符,则判定为不可用名称。
  4. 使用 re.match 来检查字符串是否符合模式,如果 match 不是 None,则表示字符串符合规则。需要使用 re 模块来实现,通过 import re 导入。
  5. 使用 os 模块,os 模块提供了与操作系统交互的功能,例如创建目录。我们使用os.mkdir()函数创建一个单独的目录,避免覆盖已有的项目目录。
  6. 创建完项目目录后,接着创建 src 目录用于存放源码文件,比如 main.cpp 文件。

2.3 目录结构

项目一般会包含多个文件或目录,比如项目的说明文档 README.md,或者 LICENSE 文件,存储源码的 src 目录,存储项目文档资料的 doc 目录,又或者是存储测试源码的 test 目录等。

在这里我提供了一个简单的目录结构示例:

hello  
  ├── src/               # 存放开发源码
  │   └── main.cpp       # 主程序文件
  ├── README.md          # 项目情况说明文档
  └── CMakeList.txt      # CMake 编译系统配置文件

结合「C++源码生成·第一章·初试模板」提到的模板的用法,为了实现以上目录结构中展示的文件,需要有 main.cpp.tmplREADME.md.tmplCMakeList.txt.tmpl 三个模板文件。

由于我们现在已经能够获取到项目名称,同时 Python 提供了字符串替换的方法 string.replace(),我们可以通过字符串替换的方法将模板中的关键字进行替换,达成项目的实际需求。

2.4 替换模板文件关键字

如果模板文件中有多个需要替换的字符或字符串,可以通过定义一个替换项的列表,然后遍历这个列表来逐一替换文件中的内容。以下是一个扩展的 Python 脚本示例,它展示了如何替换模板文件中的多个字符或字符串:

def replace_multiple_in_file(file_path, replacements, output_file_path=None):
    try:
        # Read the contents of the file
        with open(file_path, 'r', encoding='utf-8') as file:
            file_contents = file.read()

        # Apply each replacement in sequence
        for old, new in replacements:
            file_contents = file_contents.replace(old, new)

        # Determine the output file path
        if output_file_path is None:
            output_file_path = file_path

        # Write the modified contents to the output file
        with open(output_file_path, 'w', encoding='utf-8') as file:
            file.write(file_contents)

        print(f"saved to '{output_file_path}'")

    except FileNotFoundError:
        print(f"error: The file {file_path} was not found.")
    except Exception as e:
        print(f"an error occurred: {e}")

在上文提到的 generate_source_code 后面调用 replace_multiple_in_file 方法:

def generate_source_code(project_path)
    ...
    templates = [
        ('template/CMakeLists.txt.tmpl', project_path + '/CMakeLists.txt'),
        ('template/README.md.tmpl', project_path + '/README.md'),
        ('template/main.cpp.tmpl', source_path + '/main.cpp')
    ]
    replacements = [
        ('${project}', project_name)
    ]
    for input_template, output_template in templates:
        replace_multiple_in_file(input_template, replacements, output_template)

在这段 Python 脚本中,replacements 是一个包含元组的列表,每个元组都包含要替换的旧字符串和新字符串。脚本会遍历这个列表,并逐一应用替换操作。在 replace_multiple_in_file 方法中需要指定源文件与目标文件,templates 也是一个包含元组的列表,每个元组都包含了模板文件与目标文件的路径,在脚本的末尾,for 语句会遍历 templates 并逐一调用替换方法。

最后在 main() 函数中调用 generate_source_code() 完成替换模板文件中的关键字实现模板应用的效果。

3 生成项目

以下是项目生成与编译的过程:

  1. 将以上的 Python 脚本保存成 cppgen

  2. 通过 chmod +x cppgen 修改脚本权限为可执行权限。

  3. 运行命令行时带上项目名称作为参数:

    ./cppgen hello
    
  4. 此时会生成 hello 项目目录。

  5. 执行 cd 命令进入 hello 目录会看到如 2.3 节介绍的目录结构。

  6. 创建临时构建文件夹 build

  7. build 目录中执行 cmake .. 指令生成 Makefile 文件。

    cmake ..
    

    此步骤要求系统中已提交安装好 CMake 构建系统,如果在 Ubuntu 中可通过以下指令安装 CMake:

    sudo apt install -y cmake
    
  8. cmake 会根据 CMakeList.txt 文件生成 Makefile 文件,然后可以使用 make 指令进行编译:

    make
    
  9. 以上步骤的完整指令如下:

    ./cppgen hello
    cd hello
    mkdir build
    cmake ..
    make
    

4 运行结果

执行 make 指令将编译 src 目录下的所有源码文件并生成名为 hello 的可执行文件,运行该文件会输出一段带有项目名称的信息:

$ ./hello
Demonstrate using template for hello

程序运行后会进入循环休眠状态直到收到退出信号。如果按下 Ctrl-C 终止运行,进程将捕获信号并打印以下内容:

^CCaught signal number 2 (Interrupt)

如果使用 kill <pid><pid> 为 main 的进程号,可通过 pgrep main 获取)指令,进程将捕获 SIGTERM 信号(值为 15),并打印如下信息:

Caught signal number 15 (Terminated)

5 总结

在本文中展示了通过设计模板文件与使用项目名称替换其中关键字的方法,实现动态生成项目源码与项目说明文档的功能,展示了自动化源码生成工具的强大作用。简化了项目开发流程,提升了项目开发速度。

本章内容与 github 是联动的,对应的 tag 是 v1.2.2。关于 git tag 的用法与说明可以参考我的另一篇文章「git tag 用法」。

在下一章会介绍通过增强项目模板的方式,将 C++ 开发过程中的最佳实践进行沉淀,自动生成优秀的源码,提升 C++ 开发能力,让自动化源码生成工具发挥更大作用。敬请关注!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

iBlackAngel

您的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值