PlatformIO 自定义脚本选择编译库源文件 - 设置只用于C++ 的编译选项

news2025/4/18 5:53:18

PlatformIO 只支持以文件夹为单位选择要编译的源文件,不像Keil 或者CMake,可以手动控制每一个源文件。而且默认只会将库的src 文件夹下的源文件全部加入编译。比如,某个库的文件结构如下:

+ libx
    + src
    + include
    + mem
    |  a.c
    |  b.c
    |  c.c

PlaformIO 默认会将src 文件夹下的所有源文件加入编译,并且将include 文件夹加入包含路径,但是没有简单的方法把mem 文件夹加入编译;要只选择mem 里面的某一个文件编译,其他的排除,这就更复杂了。而FreeRTOS 就恰好是这样的结构,需要在五个heap_x.c 文件里选择一个。一般有两种常规的方法:

  1. 直接修改库文件,把不想要的源文件删掉,剩下的源文件放在库的src 文件夹里;
  2. 创建或编辑库的配置文件library.json,想办法用文件过滤器把要包含的源文件过滤出来;

这两种方法的操作和原理可以参考:

  1. library.json
  2. How to add source directories and include directories for libraries?

但是修改库文件的话,就等于要自己维护一个库的版本,以后如果原来的库升级了,还得费劲把升级的内容合并到自己修改后的库里。而且如果以后有别的需求,想更改成其他源文件,那就又得从头改个新版本。所以最好还是折腾一下PlatformIO 的脚本吧,或者别用PlaformIO 了。脚本的基本功能参考官方文档:Adavanced Scripting,用的是python。下面把脚本功能分成三个难度等级。

一、用脚本添加C++ 编译选项

某些C++ 的编译选项和C 不兼容,如果直接在platformio.inibuild_flags 里添加,选项也会应用在C 文件上,编译时就会冒出大片的警告,说这个选项不能用于C,挺烦的。比如,喜欢追求流行的大伙们或许会发现,如果指定了C++20 标准,本来正常的寄存器操作代码突然会报大量编译警告。这是C++20 关于volatile 关键字的新规定,而寄存器都是定义成volatile 的,写嵌入式的又不配用C++ 了……解决方法是添加个编译选项,关掉和volatile 有关的警告。

build_flags = 
	-Wno-volatile

但是这个选项不能用于C 文件。解决方法就是用脚本,只给C++ 文件添加这个选项。

要使用脚本,先要在platformio.ini 添加一行:

extra_scripts = pre:extra_flags.py

意思是要在编译时先执行一个python 脚本,脚本文件名extra_flags.py,和platformio.ini 一样放在项目根目录。pre: 表示脚本是在编译前执行的。脚本的内容是:

Import("env")  # type: ignore
env = env  # type: ignore
# 别管上面这行,留着就行

print(" ========== Script Start ======== ")

# 只用于C++ 的参数
env.Append(
    CXXFLAGS=[
        "-Wno-volatile",
    ]
)

print(" ========== Script Ended ======== ")

CXXFLAGS 就是类似makefile 的编译器选项,PlatformIO 把这些底层的东西都隐藏了,只能靠脚本打洞。详细的API 文档是没有的,好像是和scons 编译系统有关,这东西应该和cmake 类似,懒得去深究,参考示例,连蒙带猜吧。

二、选择任意文件夹加入编译

这个可以参考官方文档:Build external sources。假如要将之前那个libx 库里的mem 文件夹里的所有源文件加入编译,脚本里可以这么写:

from pathlib import Path

Import("env")  # type: ignore
env = env  # type: ignore
# 别管上面这行,留着就行

build_dir = Path('$BUILD_DIR')
project_dir = Path('$PROJECT_DIR')

# 把mem 文件夹整体加入编译
env.BuildSources(
    str(build_dir / 'libx' / 'mem'),
    str(project_dir / 'lib' / 'libx' / 'mem')  # 假设libx 是放在项目的lib 目录下
)

就不详细解释了,毕竟我也是靠猜的,理解的不一定准确。

三、选择单个文件加入编译

终于到重头戏了。假如我希望只把mem 下面的a.cb.c 加入编译,没有直接能用的API,只能自己在PlatformIO 编译系统上打洞实现。

为了方便易用,不要每次加入个文件都得修改脚本,可以利用自定义选项,在platformio.ini 里添加:

; 将任意库中的任意源文件导入build,库可以在lib 或libdeps 文件夹下
custom_lib_src =
    libx/mem/a.c
    libx/mem/b.c

这就跟写makefile 一样了。custom 开头的选项就是自定义选项,可以传递给脚本。这个地方不需要写出库文件的绝对路径,之后会在脚本里查找libx 库到底在哪个位置,然后补全路径。PlatformIO 默认会把自动下载的库放在libdeps 文件夹下;如果自己添加库,则要放在lib 文件夹下;所以脚本之后在这两个位置找就行。

脚本文件还是之前的extra_flags.py,想试用的话直接复制粘贴过去就行。全部内容如下:

import sys
import fnmatch
from pathlib import Path

Import("env")  # type: ignore
env = env  # type: ignore
# 别管上面这行,留着就行

print(" ========== Script Start ======== ")

# 只用于C++ 编译器的参数
env.Append(
    CXXFLAGS=[
        "-Wno-volatile",
    ]
)

# ###################

# 查找并添加自定义库源文件
# 自定义源文件的定义是相对库文件夹得路径,比如ioxx/ioxx.c
# ioxx 是库文件夹名,ioxx.c 是要包含得源文件。
# 脚本将自动从几个库文件夹可能的位置中查找到具体的路径。
# 比如,ioxx 库可能位于lib 文件夹里,也可能在libdeps 里,
# 如果脚本在lib 中找到了库,就会把相对路径变成绝对路径 $PROJECT_DIR/lib/ioxx/ioxx.c,
# 然后将该源文件加入构建

extra_src = env.GetProjectOption("custom_lib_src")
extra_src = env.Split(extra_src)

lib_parent = env.subst('$LIBSOURCE_DIRS')
lib_parent = list(map(lambda x: Path(x), env.Split(lib_parent)))

if len(extra_src) > 0:
    print("Extra Sources Added From Lib:")

    for f in extra_src:
        print(f'\t{f}')
        fp = Path(f)
        lib_name = fp.parts[0]

        # 查找子文件夹
        src_path = None
        lib_path = None
        for l_p in lib_parent:
            if (l_p / lib_name).exists():
                lib_path = l_p / lib_name
                src_path = l_p / fp
                break

        assert (src_path is not None) and (lib_path is not None)
        # print('!> Lib: {lib_name}, not found, skip.')

        src_path = src_path.absolute()
        lib_path = lib_path.absolute()

        assert src_path.exists()
        # print('!> Lib src: {src_path.basename}, not found, skip.')

        variant = Path(f'$BUILD_DIR/extra/{lib_name}')

        if src_path != lib_path:
            rel_path = src_path.relative_to(lib_path)
            variant = variant / rel_path

        if src_path.is_dir():
            # 将文件夹加入构建
            env.BuildSources(str(variant), str(src_path))
        else:
            # 将单个文件加入构建,这部分用了没有文档的接口,
            # 功能是连蒙带猜的
            build_file = env.File(str(variant))

            middlewares = env.get("__PIO_BUILD_MIDDLEWARES")
            if middlewares:
                node = build_file
                new_node = build_file
                for callback, pattern in middlewares:
                    if pattern and not fnmatch.fnmatch(node.srcnode().get_path(), pattern):
                        continue
                    if callback.__code__.co_argcount == 2:
                        new_node = callback(env, new_node)
                    else:
                        new_node = callback(new_node)
                    if not new_node:
                        break
                if new_node:
                    build_file = new_node

            env.Append(PIOBUILDFILES=[env.Object(build_file)])
            env.VariantDir(str(variant.parent), str(src_path.parent))

print(" ========== Script Ended ======== ")

开头几行就是获取自定义选项extra_src,然后获取当前项目的几个库路径lib_parent

extra_src = env.GetProjectOption("custom_lib_src")
extra_src = env.Split(extra_src)

lib_parent = env.subst('$LIBSOURCE_DIRS')
lib_parent = list(map(lambda x: Path(x), env.Split(lib_parent)))

循环里先要根据库的名字查找它的位置,比如根据libx 找到lib 下的libx 文件夹。

        print(f'\t{f}')
        fp = Path(f)
        lib_name = fp.parts[0]

        # 查找子文件夹
        src_path = None
        lib_path = None
        for l_p in lib_parent:
            if (l_p / lib_name).exists():
                lib_path = l_p / lib_name
                src_path = l_p / fp
                break

找到路径以后,如果是要添加文件夹,就调用BuildSources 接口;如果是单个源文件,则只能打洞。

        if src_path.is_dir():
            # 将文件夹加入构建
            env.BuildSources(str(variant), str(src_path))
        else:
            # 将单个文件加入构建,这部分用了没有文档的接口,
            # 功能是连蒙带猜的
            build_file = env.File(str(variant))

下面这一大块代码是从PlatformIO 内部代码里复制出来的,不确定有什么用,但是似乎有用。

            middlewares = env.get("__PIO_BUILD_MIDDLEWARES")
            if middlewares:
                node = build_file
                new_node = build_file
                for callback, pattern in middlewares:
                    if pattern and not fnmatch.fnmatch(node.srcnode().get_path(), pattern):
                        continue
                    if callback.__code__.co_argcount == 2:
                        new_node = callback(env, new_node)
                    else:
                        new_node = callback(new_node)
                    if not new_node:
                        break
                if new_node:
                    build_file = new_node

总之就这样,我简单试了一下,貌似没问题,不知道有没有什么隐藏BUG。大伙可以拿去试试看,反正就算有BUG 也不会让电脑爆炸。

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2331306.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

dolphinscheduler单机部署链接oracle

部署成功请给小编一个赞或者收藏激励小编 1、安装准备 JDK版本:1.8或者1.8oracle版本:19Coracle驱动版本:8 2、安装jdk 下载地址:https://www.oracle.com/java/technologies/downloads/#java8 下载后上传到/tmp目录下。 然后执行下面命…

MongoDB常见面试题总结(上)

MongoDB 基础 MongoDB 是什么? MongoDB 是一个基于 分布式文件存储 的开源 NoSQL 数据库系统,由 C 编写的。MongoDB 提供了 面向文档 的存储方式,操作起来比较简单和容易,支持“无模式”的数据建模,可以存储比较复杂…

MATLAB2024a超详细图文安装教程(2025最新版保姆级教程)附安装钥

目录 前言 一、MATLAB下载 二、MATLAB安装 二、MATLAB启动 前言 MATLAB(Matrix Laboratory)是由MathWorks公司开发的一款高性能的编程语言和交互式环境,主要用于数值计算、数据分析和算法开发。内置数学函数和工具箱丰富,开发…

基于 Spring Boot 瑞吉外卖系统开发(二)

基于 Spring Boot 瑞吉外卖系统开发(二) 员工登录功能实现 员工登录页面login.html存放在/resources/backend/page/login目录下。 启动项目,在浏览器中通过地址“http://localhost:8080/backend/page/login/login.html”访问员工登录页面。…

软考系统架构设计师之大数据与人工智能笔记

一、大数据架构设计 1. 核心概念与挑战 大数据特征:体量大(Volume)、多样性(Variety)、高速性(Velocity)、价值密度低(Value)。传统数据库问题:数据过载、性…

146. LRU 缓存 带TTL的LRU缓存实现(拓展)

LRU缓存 方法一:手动实现双向链表 哈希表 struct Node{int val;int key;Node* prev;Node* next;Node(int a, int b): key(a), val(b), prev(nullptr), next(nullptr) {}Node():key(0), val(0), prev(nullptr), next(nullptr) {} }; class LRUCache { private:Node* removeTai…

浅层神经网络:全面解析(扩展)

浅层神经网络:全面解析(扩展) 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,可以分享一下给大家。点击跳转到网站。 https://www.captainbed.cn/ccc 一、神经网络架构演进图谱 #mermaid-svg-…

Qt 事件系统负载测试:深入理解 Qt 事件处理机制

Qt 事件系统负载测试:深入理解 Qt 事件处理机制 文章目录 Qt 事件系统负载测试:深入理解 Qt 事件处理机制摘要引言实现原理1. 自定义事件类型2. 事件队列管理3. 性能指标监控4. 事件发送机制 性能监控实现1. 负载计算2. 内存监控3. 延迟计算 使用效果优化…

Unity3D仿星露谷物语开发33之光标位置可视化

1、目标 当从道具栏中拖出一个道具到地面的时候&#xff0c;光标区域会显示是否可放置物体的可视化显示。绿色表示可以放置物体&#xff0c;红色表示不可以放置物体。 2、优化InventoryManager脚本 添加2个方法&#xff1a; /// <summary>/// Returns the itemDetails&…

蓝桥杯冲刺题单--二分

二分 知识点 二分&#xff1a; 1.序列二分&#xff1a;在序列中查找&#xff08;不怎么考&#xff0c;会比较难&#xff1f;&#xff09; 序列二分应用的序列必须是递增或递减&#xff0c;但可以非严格 只要r是mid-1&#xff0c;就对应mid&#xff08;lr1&#xff09;/2 2.答…

MySQL原理(一)

目录 一、理解MySQL的服务器与客户端关系 1&#xff1a;MySQL服务器与客户端 2&#xff1a;服务器处理客户端请求 3&#xff1a;常见的存储引擎 二、字符集和比较规则 1&#xff1a;字符集和比较规则简介 2&#xff1a;字符集和比较规则应用 3&#xff1a;乱码原因&…

Docker+Jenkins+Gitee自动化项目部署

前置条件 docker安装成功 按照下面配置加速 sudo mkdir -p /etc/docker sudo tee /etc/docker/daemon.json <<-EOF {"registry-mirrors": ["https://register.librax.org"] } EOF sudo systemctl daemon-reload sudo systemctl restart docker一、…

VScode 画时序图(FPGA)

1、先安装插件&#xff1a; 2、然后就可以编写一个.js文件&#xff0c;如下&#xff1a; {signal: [{name: clk, wave: p.......|..},{name: rstn, wave: 01......|..},{name: din_vld, wave: 0.1.0...|..},{name: din, wave: "x.x...|..", data: ["D0", …

一文详解OpenCV环境搭建:Windows使用CLion配置OpenCV开发环境

在计算机视觉和图像处理领域&#xff0c;OpenCV 是一个不可或缺的工具。其为开发者提供了一系列广泛的算法和实用工具&#xff0c;支持多种编程语言&#xff0c;并且可以在多个平台上运行。对于希望在其项目中集成先进视觉功能的开发者来说&#xff0c;掌握如何配置和使用OpenC…

计算机网络 3-2 数据链路层(流量控制与可靠传输机制)

3.4 流量控制与可靠传输机制 流量控制&#xff1a;指由接收方控制发送方的发送速率&#xff0c;使接收方有足够的缓冲空间来接收每个帧 滑动窗口流量控制:一种更高效的流量控制方法。 在任意时刻&#xff0c;发送方都维持一组连续的允许发送帧的序号&#xff0c;称为发送窗口…

Jenkins配置的JDK,Maven和Git

1. 前置 在配置前&#xff0c;我们需要先把JDK&#xff0c;Maven和Git安装到Jenkins的服务器上。 &#xff08;1&#xff09;需要进入容器内部&#xff0c;执行命令&#xff1a;docker exec -u root -it 容器号/容器名称&#xff08;2选1&#xff09; bash -- 容器名称 dock…

有效压缩 Hyper-v linux Centos 的虚拟磁盘 VHDX

参考&#xff1a; http://www.360doc.com/content/22/0505/16/67252277_1029878535.shtml VHDX 有个不好的问题就是&#xff0c;如果在里面存放过文件再删除&#xff0c;那么已经使用过的空间不会压缩&#xff0c;导致空间一直被占用。那么就需要想办法压缩空间。 还有一点&a…

网络空间安全(53)XSS

一、定义与原理 XSS&#xff08;Cross Site Scripting&#xff09;&#xff0c;全称为跨站脚本攻击&#xff0c;是一种网站应用中的安全漏洞攻击。其原理是攻击者利用网站对用户输入内容校验不严格等漏洞&#xff0c;将恶意脚本&#xff08;通常是JavaScript&#xff0c;也可以…

Spring MVC 框架 的核心概念、组件关系及流程的详细说明,并附表格总结

以下是 Spring MVC 框架 的核心概念、组件关系及流程的详细说明&#xff0c;并附表格总结&#xff1a; 1. 核心理念 Spring MVC 是基于 MVC&#xff08;Model-View-Controller&#xff09;设计模式 的 Web 框架&#xff0c;其核心思想是 解耦&#xff1a; Model&#xff1a;数…

金融数据分析(Python)个人学习笔记(6):安装相关软件

python环境的安装请查看Python个人学习笔记&#xff08;1&#xff09;&#xff1a;Python软件的介绍与安装 一、pip 在windows系统中检查是否安装了pip 打开命令提示符的快捷键&#xff1a;winR&#xff0c;然后输入cmd 在命令提示符中执行如下命令 python -m pip --version…