ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

CH55xDuino编译报错sdcc.sh syntax error的排查与修复

CH55xDuino编译报错sdcc.sh syntax error的排查与修复 写这篇排障记录之前我先说个背景。前阵子我在arduino环境里折腾CH55xDuino想在CH552这颗国产USB单片机上跑点小项目——这芯片便宜、带原生USB关键是能用arduino的口吻写代码社区方案叫CH55xDuino。结果板子选好、示例代码打开一点编译IDE直接甩给我一行sdcc.sh: syntax error: unexpected (。当时脑子是懵的因为我代码还没写几行编译链直接挂了。网上搜了一圈发现踩这个坑的人不少但靠谱的答案被埋在各种论坛回复里。我自己翻编译日志、拆shell脚本、改platform.txt折腾了一个晚上才把问题彻底解决。今天把整个排查过程和修复方案完整记录下来给同样被CH55xDuino折磨的人一个可以直接抄作业的参考。不管你是刚接触CH55x系列还是已经在arduino里编译过几个板子这个报错的解决思路基本通用关键是你得先理解它到底在哪一步炸的。1. 先搞清楚CH55xDuino和它背后的SDCC工具链1.1 CH55xDuino到底做了什么CH55x系列是沁恒出的8位USB单片机CH552、CH554这几颗芯片的性价比很能打几块钱就能拿到带USB控制器、ADC、PWM、触摸按键的小MCU。但它的原生开发环境是TI IDE配SDCC那个IDE放今天看确实不太好用所以社区里有人做了CH55xDuino把CH55x系列塞进arduino生态让你能用Arduino IDE写代码、选板子、点编译。CH55xDuino的原理不复杂它借用arduino的硬件抽象层标准把CH55x的寄存器操作、USB库、启动代码全部封装成arduino风格API同时自己带了一套完整工具链。这套工具链的核心就是SDCCSmall Device C Compiler一个开源的8051系列C编译器。CH55x是增强型8051内核所以底层编译必须靠SDCC而不是arduino常用的AVR-GCC或ARM-GCC。问题恰恰出在SDCC工具链的集成方式上。CH55xDuino在arduino的hardware目录下定义了一块“板卡”里面有platform.txt描述编译规则有boards.txt定义板型tools目录下则是编译器的可执行文件和一堆辅助脚本。其中sdcc.sh就是那个用来封装SDCC命令行的shell脚本它本意是想在不同操作系统之间做路径和参数的适配结果在Windows环境里反而成了第一个引爆点。1.2 报错出现的典型时机和完整形态这个报错不会在安装阶段出现而是在你真正点编译的那一刻才炸。具体时机一般是Arduino IDE里选好板子比如CH55xDuino Board下的CH552写一个最简单的Blink示例然后点“上传”或“编译”。IDE先编译核心库然后编译sketch当编译器前端准备调用SDCC时sdcc.sh脚本被执行shell解析器读取脚本时直接语法报错。不同环境下的报错形态不完全一样在Windows的Arduino IDE里常见的是exec: sdcc.sh: 未找到或者/bin/sh: sdcc.sh: syntax error: unexpected (在Git Bash里手动运行arduino-cli时你会看到更完整的上下文$ arduino-cli compile -b ch55xduino:ch55xduino:ch552 ... /bin/sh: /c/Users/xxx/AppData/Local/Arduino15/packages/ch55xduino/hardware/ch55xduino/1.3.x/tools/sdcc/bin/sdcc.sh: syntax error: unexpected ( Error during build: exit status 2注意最后那行exit status 2它只是结果不是原因。真正的原因是shell脚本文件本身没有被正确解析。换句话说SDCC编译器可能根本没开始跑脚本在执行前就被shell判定为“语法不合法”。这里有个很关键的认知这个报错和你的C代码、Arduino语法、板卡配置都没有关系纯粹是工具链里的shell脚本在当前环境里“水土不服”。所以排查方向应该锁定脚本本身而不是去改代码或换库。2. 为什么一个好好的脚本会在你的电脑上报语法错误2.1 头号元凶CRLF行尾符这是我在CH55xDuino报错里遇到的最常见原因没有之一。Windows底下做开发Git的core.autocrlf默认行为很坑。如果你直接git clone了CH55xDuino的仓库或者在Windows下用某些工具解压源码包很多文件的换行符会被自动转成CRLF\r\n而原本这些脚本在Linux和Mac环境下是LF\n结尾。shell脚本对行尾符极其敏感。#!/bin/sh这类脚本运行时解析器按行读取内容如果行尾多了一个\r字符它不会把这个\r当作空白忽略掉而是当作参数的一部分。这会导致你在脚本里明明写得正常的语句在sh眼里变成了奇怪的形态。为什么报错偏偏是unexpected (这里有个很典型的场景。很多shell脚本里会定义函数比如#!/bin/sh foo() { echo hello }如果行尾变成CRLFsh实际读到的可能是foo() \r{或者\r把行的语法结构搞乱最终解析器在某个位置遇到了一个不期望的左括号直接判定syntax error。sdcc.sh里面大概率有类似get_tool_path() { ... }这种函数定义CRLF会把整个函数定义行拆得支离破碎于是报错位置就指向了括号。你可以在Git Bash里用一行命令验证是不是CRLFcat -A sdcc.sh | head -n 20如果看到行尾有^M$那基本实锤了。^M就是\r的可视化表示$代表行尾。正常的LF行尾只会显示$CRLF则是^M$。另外一个判断方法是看文件大小同样一个脚本CRLF版本比LF版本会大一些每个换行多一个字节。但这个方法不够准不如cat -A直观。2.2 解释器不匹配Bash专属语法被POSIX sh解析第二种常见原因是解释器问题。这类问题在Linux/Ubuntu上尤其明显Windows的Git Bash里偶尔也会遇到。sdcc.sh脚本开头通常写的解释器是#!/bin/sh。但/bin/sh在不同系统上指向的解析器不一样Ubuntu/Debian上/bin/sh默认指向dash一个很严格的POSIX shell。很多发行版上/bin/sh才是bash的兼容模式。Windows的Git Bash里sh和bash之间的关系还需要看具体配置。问题在于脚本原文可能是给bash写的用了bash专属语法但执行时被/bin/shdash解析于是炸了。最典型的bash专属语法就是function关键字#!/bin/sh function foo() { echo hello }在bash里这样写没问题但dash不认直接报syntax error: unexpected (这是个很著名的坑。很多开源脚本作者默认开发环境是bash写完没在dash下测试结果用户一旦被/bin/sh指向dash就暴露了。CH55xDuino的sdcc.sh是不是也有这个问题我在排障时看到有些版本的脚本确实用了bash风格的写法。如果你确认行尾已经是LF了还报同样的错那大概率就是解释器问题。可以用下面命令确认正在用哪个shell解析ls -l /bin/sh如果输出指向dash那你可以临时用bash直接执行脚本试试bash sdcc.sh --version如果bash执行正常而sh执行报错那就是解释器不匹配解决方案是改脚本首行为#!/bin/bash或者把function关键字去掉改成POSIX兼容语法。2.3 其他潜在根因BOM、路径、权限除了上面两个大头还有一些不太常见但确实会踩到的坑。UTF-8 BOM头。有些Windows编辑器保存脚本时会自动加上BOM字节序标记sh解析器看到开头的\xef\xbb\xbf会把它当成命令的一部分第一行就直接syntax error。检查方法是用xxd或hexdump看文件头几个字节。解决方法是把BOM去掉用sed -i 1s/^\xef\xbb\xbf// sdcc.sh或者用支持“无BOM保存”的编辑器重新存一遍。路径里有空格或括号。Windows下你的用户目录如果是C:\Users\John Doe\Documents\Arduino那CH55xDuino的完整路径里就带空格。在shell脚本里路径没加引号或者用了反斜杠空格和括号就会被shell解释成特殊字符引发各种妖孽报错。这个问题的麻烦之处在于它有时候表现为syntax error有时候表现为找不到文件跟其他原因混在一起很难分辨。排查方法就是打印完整命令路径看有没有被shell错误切分。可执行权限缺失。Windows下没这个概念但在Linux/macOS下.sh文件没有x权限时你直接./sdcc.sh会报Permission denied但如果是通过sh sdcc.sh调用的则不涉及权限问题。CH55xDuino在Linux下如果脚本权限不对报错会是“Permission denied”而不是syntax error所以这个优先级不高但顺手chmod x *.sh也没坏处。MSYS/Git Bash的路径转换问题。Windows的Git Bash里/c/Users/xxx和C:\Users\xxx两种风格混用脚本内部如果用了/bin/cp这种路径在Windows下可能被MSYS转换出奇怪的结果导致脚本里某些分支逻辑走到了错误位置。这个问题较深一般到后面才排查。3. 三个可落地的修复方案按优先级排序3.1 方案一把脚本行尾转成LF顺手净化语法这是我最推荐的起点操作简单、覆盖面广而且无论你是CRLF问题还是function语法问题这个方案都能处理大半。先说一行命令搞定CRLF转换。在Git Bash里进入CH55xDuino工具链的sdcc目录执行find . -name *.sh -exec sed -i s/\r$// {} 这行命令会把当前目录下所有.sh文件的CRLF行尾统一换成LF。如果系统装了dos2unix也可以find . -name *.sh -exec dos2unix {} 转完之后用bash -n做语法检查bash -n sdcc.sh如果没有任何输出说明脚本语法已经被解析器接受。这一步通过后再到Arduino IDE里重新编译大概率问题已经消失。如果bash -n还是报错并且报错点指向function关键字或者某个括号位置那说明脚本里真的有bash专属语法。打开脚本找到报错行把function foo()改成foo()把function关键字删掉。如果你不确定脚本里有多少处用grep -n function *.sh批量改完后再次bash -n验证。操作完成后我个人的建议是把用户目录下整个ch55xduino硬件包里的所有.sh文件全转一遍因为你不知道后面还有哪个脚本会炸。特别是toolchain的bin目录下那堆辅助脚本比如mkbom、srec_cat相关脚本它们可能都有同样的问题。注意sed -i s/\r$//只处理行尾的\r不会动文件其他内容。在Git Bash里执行时如果文件原本是LF这行命令重复跑也不会造成损伤所以放心用。3.2 方案二绕过shell脚本直接用sdcc.exe如果你是在Windows上开发更彻底的做法是绕过脚本让arduino编译链直接调用SDCC的exe可执行文件。因为sdcc.sh只是一个封装层它里面主要是定位工具路径、转路径格式、调用sdcc可执行程序。在Windows下直接调用sdcc.exe完全可行省掉shell这一层一了百了。具体操作分两步。第一步找到platform.txt。在Arduino IDE里板卡包一般位于C:\Users\你的用户名\AppData\Local\Arduino15\packages\ch55xduino\hardware\ch55xduino\版本号\如果是你自己手动安装的硬件包可能在Arduino安装目录\hardware\ch55xduino\platform.txt就是arduino的编译规则描述文件里面定义了编译器怎么调用。第二步打开platform.txt搜索sdcc.sh。你大概率会看到类似这样的行## Compile process recipe.cpp.o.pattern{tool.path}/bin/sdcc.sh -c ...把其中sdcc.sh换成sdcc.exerecipe.cpp.o.pattern{tool.path}/bin/sdcc.exe -c ...如果有多个模式匹配比如c.o和cpp.o两个recipe全部替换。改完保存然后重新编译。如果报错变成参数格式问题比如SDCC不识别某个参数那就说明脚本里原本还做了参数适配此时你需要继续看platform.txt里那些参数是否能被sdcc.exe原生支持。这里插一句sdcc.sh的作用不只是调用exe它可能还做了路径格式转换比如把反斜杠转成正斜杠或者自动追加include路径。如果直接调exe导致找不到头文件你需要在platform.txt的recipe里手动补上-I参数指到CH55xDuino的cores目录。典型命令recipe.cpp.o.pattern{tool.path}/bin/sdcc.exe -I{runtime.platform.path}/cores/ch55x -I{runtime.platform.path}/libs {compiler.flags} ...具体include路径以你的平台包目录结构为准打开看看就知道。这个方案的优点是彻底摆脱shell依赖缺点是要动platform.txt对不熟悉arduino编译规则的人有点门槛。但一劳永逸改完以后再也不会被脚本问题烦。3.3 方案三调整运行环境让脚本待在它该待的地方如果你不想改platform.txt也不想逐个去改脚本那就改变工具运行的环境。思路是让sdcc.sh在被调用时能遇到一个它认识的shell同时文件本身以LF行尾存在。在Windows上我推荐用Git Bash跑arduino-cli而不是在CMD或PowerShell里跑。Git Bash自带的bash环境对shell脚本处理更友好环境变量PATH的转换方式也更接近Linux。实际操作从[arduino-cli官方地址]下载Windows版解压到任意目录。打开Git Bash把arduino-cli放到PATH里。用arduino-cli而不是图形IDE编译。在Git Bash里/bin/sh的行为更偏向bash很多在CMD里会炸的脚本在Git Bash里就能正常跑。但我实测下来如果文件本身是CRLFGit Bash照样会报syntax error——所以方案一的转LF步骤还是省不掉。如果你在Linux下开发可以把系统默认的/bin/sh从dash切回bashsudo dpkg-reconfigure dash选择“否”让系统的/bin/sh指向bash。这是一个全局修改能解决一批dash不兼容的脚本但副作用是你系统里其他依赖dash行为的脚本可能会有细微变化不建议在所有机器上无脑操作自己开发机无所谓。另外一个从源头避免问题的方法是大佬们都在用的在clone CH55xDuino仓库之前先把Git的autocrlf关掉git config --global core.autocrlf false这样clone下来的文件就会保持仓库原始的行尾格式不会自动给你换成CRLF。已经clone到本地的仓库也可以执行git config core.autocrlf false git rm --cached -r . git reset --hard强制把工作区的文件重新按原始行尾刷一遍。这个方法对已经踩坑的人尤其有效一刷全干净。4. 常见问题与排查技巧实录4.1 问题速查表我整理了一张表覆盖了CH55xDuino编译报错最常见的一批问题形态、根因和修复方式你可以直接对着找。错误形态根因快速定位修复方法syntax error: unexpected (CRLF行尾 或 bash函数语法被sh解析cat -A 脚本看行尾sed -i s/\r$// *.sh删function关键字syntax error: unexpected end of file脚本某行被CRLF破坏花括号配对错乱bash -n定位最后有效行统一转LF检查括号配对syntax error: word unexpectedBOM头 或 路径空格xxd看文件头sed -i 1s/^\xef\xbb\xbf//脚本exec: sdcc.sh: 未找到platform.txt调用脚本但脚本不在预期位置查看编译日志完整路径用sdcc.exe替换或修正路径Permission deniedLinux下脚本没有执行权限ls -l查看权限位chmod x sdcc.shNo such file or directory脚本首行解释器路径在当前系统不存在比如写了#!/bin/bash但系统无bashhead -1 脚本改为#!/bin/sh或安装bash这张表不敢说覆盖100%但CH55xDuino用户遇到shell层面报错时90%都能在表里找到对应项。如果表里没有你的情况那你得把完整编译日志拉出来自己分析了。4.2 我的三条实操心得心得一永远先开详细编译日志。在Arduino IDE里文件-首选项-勾选“显示详细编译输出”或者arduino-cli加--verbose参数。日志会显示实际执行的命令包括脚本所在的完整路径、传给脚本的参数。很多时候问题答案就在日志里你根本不用猜。我看到网上很多人对着一个IDE弹窗里的简短报错瞎改代码其实毫无意义第一件事永远是拉日志。心得二改platform.txt之前先备份。platform.txt是arduino编译链的核心文件改坏了整个板卡都用不了。我的习惯是先复制一份platform.txt.bak再动手。另外platform.txt对行尾也敏感改完之后确认是LF行尾别在Windows记事本里编辑然后保存记事本默认CRLF可能把platform.txt本身也弄出问题。建议用VS Code或Notepad设置LF和UTF-8无BOM。心得三在Windows上与其跟一堆.sh脚本搏斗不如直接把脚本链绕过去。我自己最后选的方案就是方案二直接调sdcc.exeplatform.txt改完那一版再也没报过这个错。如果你的项目长期用CH55x开发这个投入是值得的。偶尔玩玩的话方案一改行尾就够了。另外补充一个排查技巧如果你在Windows上搞不定不妨用WSL起一个Ubuntu环境在里面装arduino-cli和CH55xDuino。Linux环境对shell脚本的友好程度远超Windows在WSL里这条编译链几乎是一条命令直接通到底。我之前在WSL里编译同一个CH552项目完全没碰到Windows上那些妖孽问题脚本一发入魂。4.3 收个尾我现在的固定开发习惯踩过这个坑之后我现在在Windows上做CH55xDuino开发已经形成固定流程Git Bash作为终端arduino-cli命令行编译工具链目录下一律LF行尾platform.txt里的脚本调用统一改成sdcc.exe。新clone的板卡包第一件事就是执行find . -name *.sh -exec sed -i s/\r$// {} 。这套习惯让我后来再碰到别的板卡包报shell错误时也能三两下定位。最后说一句遇到sdcc.sh: syntax error: unexpected (这种报错别慌它说明你的开发环境离能用还差一步。按我上面给的顺序排查先看行尾再看解释器最后绕脚本直接调exe基本上半小时内解决。搞定之后再回头看CH55x这颗芯片你会发现它其实很好用几块钱的USB单片机配合arduino生态做小玩具小工具很顺手。
返回列表