当前位置: 首页 > article >正文

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

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 PathImport("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.clibx/mem/b.c

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

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

import sys
import fnmatch
from pathlib import PathImport("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 = Nonelib_path = Nonefor l_p in lib_parent:if (l_p / lib_name).exists():lib_path = l_p / lib_namesrc_path = l_p / fpbreakassert (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_pathif 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_filenew_node = build_filefor callback, pattern in middlewares:if pattern and not fnmatch.fnmatch(node.srcnode().get_path(), pattern):continueif callback.__code__.co_argcount == 2:new_node = callback(env, new_node)else:new_node = callback(new_node)if not new_node:breakif new_node:build_file = new_nodeenv.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 = Nonelib_path = Nonefor l_p in lib_parent:if (l_p / lib_name).exists():lib_path = l_p / lib_namesrc_path = l_p / fpbreak

找到路径以后,如果是要添加文件夹,就调用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_filenew_node = build_filefor callback, pattern in middlewares:if pattern and not fnmatch.fnmatch(node.srcnode().get_path(), pattern):continueif callback.__code__.co_argcount == 2:new_node = callback(env, new_node)else:new_node = callback(new_node)if not new_node:breakif new_node:build_file = new_node

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

在这里插入图片描述

相关文章:

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

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

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 提供了 面向文档 的存储方式,操作起来比较简单和容易,支持“无模式”的数据建模,可以存储比较复杂…...

java基础 迭代Iterable接口以及迭代器Iterator

Itera迭代 Iterable < T>迭代接口(1) Iterator iterator()(2) forEach(Consumer<? super T> action)forEach结合Consumer常见场景forEach使用注意细节 (3)Spliterator spliterator() Iterator< T>迭代器接口如何“接收” Iterator<T>核心方法迭代器的…...

CentOS禁用nouveau驱动

1、验证 nouveau 是否在运行 lsmod | grep nouveau如果命令返回结果&#xff0c;说明 nouveau 驱动正在运行。 2、编辑黑名单文件 通过编辑黑名单配置文件来禁用 nouveau 驱动&#xff0c;这样在系统启动时不会加载它。 vi /etc/modprobe.d/blacklist-nouveau.conf修改以下…...

Linux 时间同步工具 Chrony 简介与使用

一、Chrony 是什么&#xff1f; chrony 是一个开源的网络时间同步工具&#xff0c;主要由两个组件组成&#xff1a; chronyd&#xff1a;后台服务进程&#xff0c;负责与时间服务器交互&#xff0c;同步系统时钟。chronyc&#xff1a;命令行工具&#xff0c;用于手动查看或修…...

C语言:字符串处理函数strstr分析

在 C 语言中&#xff0c;strstr 函数用于查找一个字符串中是否存在另一个字符串。它的主要功能是搜索指定的子字符串&#xff0c;并返回该子字符串在目标字符串中第一次出现的位置的指针。如果没有找到子字符串&#xff0c;则返回 NULL。 详细说明&#xff1a; 头文件&#xf…...

28--当路由器开始“宫斗“:设备控制面安全配置全解

当路由器开始"宫斗"&#xff1a;设备控制面安全配置全解 引言&#xff1a;路由器的"大脑保卫战" 如果把网络世界比作一座繁忙的城市&#xff0c;那么路由器就是路口执勤的交通警察。而控制面&#xff08;Control Plane&#xff09;就是警察的大脑&#xf…...

Vue知识点(5)-- 动画

CSS 动画是 Vue3 中实现组件动画效果的高效方式&#xff0c;主要通过 CSS transitions 和 keyframes 动画 CSS Keyframes&#xff08;关键帧动画&#xff09; 用来创建复杂的动画序列&#xff0c;可以精确控制动画的各个阶段。 核心语法&#xff1a; keyframes animationNa…...

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

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

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

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

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

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

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…...

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

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

Python监控网站更新则推送到企业微信

import requests from lxml import etree import redis r redis.Redis(host"localhost", port6379, db0)def get_page_content(url):# 获取指定网页中的标题和链接url_lists []headers {"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)…...

下一代智能爬虫框架:ScrapeGraphAI 详解

更多内容请见: 爬虫和逆向教程-专栏介绍和目录 文章目录 一、ScrapeGraphAI 概述1.1 ScrapeGraphAI介绍1.2 核心特点1.3 工作流程1.4 关键模块1.5 对比传统爬虫框架1.6 安装二、基础操作2.1 自定义解析规则2.2 数据后处理2.3 分布式爬取三、高级功能3.1 多步骤交互采集3.2 动态…...

Dubbo(30)如何配置Dubbo的服务分片?

配置Dubbo的服务分片&#xff08;也称为服务分组&#xff09;可以帮助你将不同的服务实例分组&#xff0c;以实现隔离和管理。通过服务分片&#xff0c;可以在同一个注册中心中注册多个相同接口的服务&#xff0c;但它们属于不同的分组&#xff0c;消费者可以根据需要选择特定分…...

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

Qt 事件系统负载测试&#xff1a;深入理解 Qt 事件处理机制 文章目录 Qt 事件系统负载测试&#xff1a;深入理解 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.答…...

《深度探秘:SQL助力经典Apriori算法实现》

在数据的广袤世界里&#xff0c;隐藏着无数有价值的信息&#xff0c;等待着我们去挖掘和发现。关联规则挖掘算法&#xff0c;作为数据挖掘领域的关键技术&#xff0c;能够从海量数据中找出事物之间潜在的关联关系&#xff0c;为商业决策、学术研究等诸多领域提供有力支撑。其中…...

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一、…...

Nginx 499 错误的原因及解决方法

Nginx 499 错误的原因及解决方法 原因 客户端超时&#xff1a; 客户端在等待服务器响应时超时&#xff0c;导致连接被关闭。 解决方法&#xff1a;优化服务端响应时间&#xff0c;或调大客户端的连接超时时间。 服务端响应过慢&#xff1a; 后端服务处理请求时间过长。 解决方法…...

Linux网络多进程并发服务器和多线程并发服务器

多进程 还是以大小写转换为例子 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <pthread.h> #include <sys/socket.h> #include <arpa/inet.h> #include "wrap.h" #include…...

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", …...

Kubernetes 集群搭建(二):搭建k8s集群 (1.28版本)

&#xff08;一&#xff09;虚拟环境准备 名称ip备注m1192.168.101.131mastern1192.168.101.132workern2192.168.101.133worker &#xff08;二&#xff09;所有主机统一配置 2.1 关闭防火墙和selinux systemctl stop firewalld systemctl disable firewalld sed -i s/enfo…...

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

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

Python爬取数据(二)

一.example2包下的 1.re模块的compile函数使用 import repatternre.compile(r\d) print(pattern) 2.match的方法使用 import re patternre.compile(r\d) # m1pattern.match(one123twothree345four) #参数2&#xff1a;指定起始位置(包含),参数3&#xff1a;终止位置(包含),…...

前端用用jsonp的方式解决跨域问题

前端用用jsonp的方式解决跨域问题 前端用用jsonp的方式解决跨域问题 前端用用jsonp的方式解决跨域问题限制与缺点&#xff1a;前端后端测试使用示例 限制与缺点&#xff1a; 不安全、只能使用get方式、后台需要相应jsonp方式的传参 前端 function jsonp(obj) {// 动态生成唯…...