A0400 Eclipse Cygwin GCC开发简介 - matianfu/arabesque GitHub Wiki

前言

本文档介绍在Windows上使用Eclipse和Cygwin开发C/C++程序。

不使用arm交叉编译工具链,程序生成的可执行文件在Windows上运行;这样做的好处是可以熟悉Eclipse功能,GNU工具链特性,包括GCC参数和GDB使用,熟悉测试框架等等。

在向STM32 Eclipse/GNU ARM工具链的开发环境中加入新特性之前,应该先在本文档所述的环境下,尝试在Cygwin GCC下实现该特性,成功之后移植到STM32开发工具链和开发环境下。

另外一些简单的算法调试,与平台无关的代码,在Host PC上调试也比交叉编译开发环境和目标硬件上调试来得方便。

安装

Java Runtime

没有特别的版本要求,可以直接访问http://java.com安装。

如果在线安装程序下载太慢无法成功,可以在java.com首页上点击顶栏上的“下载”,在下载页面左侧找到“脱机安装程序”链接,用性能稳定的下载器下载之后完成安装。

alt text

alt text

安装Eclipse CDT

推荐版本是Eclipse Luna SR1,这个版本稳定性尚可但有些小Bug,通常可重新启动Eclipse解决;我也试过Kepler SR2版本,也行,但界面有些不一样。

下载页面:

http://www.eclipse.org/downloads/packages/eclipse-ide-cc-developers/lunasr1

根据操作系统选择32bit或者64bit版本。

Eclipse无须安装,可以解压在任何地方,在同一台电脑上可以安装多个副本,之间没有干扰。

我的安装位置是:

c:\eclipse

可执行文件是:

c:\eclipse\eclipse.exe

在文件管理器里双击运行。

alt text

安装Cygwin

在前面讲过了。

用Eclipse开发C/C++程序必要的包是:

  1. GCC

  2. GDB

  3. make

Hello Cygwin!

创建项目

首次运行Eclipse会弹出一个对话框,要求用户选择workspace目录。

alt text

Eclipse支持多项目开发,可以在一个workspace里建立多个依赖性项目(例如库和应用),或者是相关项目;Eclipse可以在workspace之间切换,每个workspace内的项目和其他workspace无关。

我们会用这个Eclipse安装实例开发两类程序,一类是Cygwin GCC工具链的Windows应用;另一个STM32目标平台和GNU ARM交叉编译工具链的嵌入式项目;比较好的办法就是针对不同平台的开发,建立不同的workspace;因为使用Cygwin GCC工具链,文件路径遵循Cygwin/Unix习惯辉比较方便,我把这个workspace放在了Cygwin的用户HOME目录下。

Windows路径是:

c:\cygwin\home\<username>\workspace

对应的Cygwin路径是:

/home/<username>/workspace (~/workspace )

进入Eclipse后,关闭欢迎页面。创建一个C语言项目。

alt text

选择Empty Project,Toolchain选择Cygwin GCC,输入项目名称,hello-cygwin,点击Next

alt text

选择Configuration,这里的Configuration确切的说应该叫做Build Configuration,或者用make的习惯,称为Target。习惯Keil/IAR的开发者应该对此并不陌生;不同的Build Configuration/Target可以有不同的文件配置和编译配置,最常见的,在Debug Configuration禁止编译器优化,在Release Configuration里打开编译器优化。

点击Finish

alt text

Project Explorer里出现了一个新项目,Includes是一个虚拟文件夹(磁盘上并未真实存在),里面包含include文件的路径;显示为Windows路径;Eclipse CDT了解Cygwin GCC和Unix的include文件路径规范,所以自动加入到项目中。

注意图中的四个路径,其中前两个是工具链路径(即C语言标准库),后两个是(伪Unix)系统的库文件路径,其中一个是win32api的头文件路径。

alt text

鼠标选中项目名称,右键,New -> Source File,创建一个新的源文件;

alt text

命名为main.c;注意在Eclipse中文件后缀名直接关联对应的编译器,错误的后缀名会导致文件编译错误;

alt text

得到一个添加了注释的空文件;

alt text

添加图示中的代码;其中setbuf()语句关闭了标准输出的buffer;

alt text

Build

鼠标选中项目名称,右键,Build Project;或者,鼠标选中项目名称,菜单Project -> Build All

alt text

编译完成,Project Explorer中出现了Debug目录,里面包含编译完的可执行文件,Object文件,dependency文件,和makefile。这些文件是Eclipse CDT自动生成的make文件,省去用户手写make文件的工作,所有生成文件可读但不可编辑,每次build会部分和全部重新生成,用户的修改没有效果;

Project Explorer中还出现了一个Binaries虚拟目录,方便用户看到所有生成的可执行文件;

alt text

Debug

选中项目名称,菜单Run -> Debug Configurations

alt text

看到Debug Configurations界面。

alt text

Eclipse充满了Configuration的概念;前面遇到的Debug/Release Configuration,指的是Build Configuration;如果把Build过程抽象的理解成类的话,Debug/Release Configuration可以理解为实例化的对象;

而这里的Debug Configuration——我们的目的当然是要启动Debug——它的行为是更抽象的说,叫做Launch;

Eclipse中有四种Launch:Debug,Run,Profile和External Tool;

Debug Launch,是启动调试过程;当然需要外部的Debugger或者Debug Server程序配合;

Run Launch,是启动程序运行;也许是和用户交互,也许是在跑单元测试,也许是和外部命令构成自动化测试,等等;与Debug无关;

Profile Launch,是启动程序运行,并且配合Profiler工具,完成对程序的Profiling,例如代码覆盖测试(下面有例子),性能测试,内存使用测试,等等;GNU工具链和开源工具中有大量的Profiling工具,其中一小部分可以用于STM32的嵌入式开发;

External Tool Launch,是运行一个外部程序,仅仅是为了方便;

如前面图示,选中C/C++ Application,右键,New;或者点击上面一排图标中最左侧的一个;创建一个新的Debug (Launch) Configuration。

alt text

Eclipse会自动为你填入几乎所有的配置选项;在这个例子里不需要任何修改就可以启动Debug过程;

在Arguments Tab页,可以为程序提供启动的命令行参数;

在Environment Tab页,可以为启动程序的Sub-Shell添加环境变量;

在Debugger Tab页,可以设置Debugger/Debug Server选项;

在Source Tab页,可以设置源文件路径,路径映射等等;

在Common Tab页,可以选择把这个Debug Configuration添加到全局快捷启动按钮的下拉列表中;

点击Debug

alt text

和很多IDE一样,Eclipse提供在不同工作状态下,提供不同界面布局的能力,在Eclipse中称为Perspective;点击Yes,同意跳转到Debug Perspective;

alt text

首先,注意到界面的右上角,C/C++是刚刚编辑源码的界面布局(Perspective),现在是Debug Perspective,在任何时候都可以在这里点击切换Perspective;C/C++ 按钮左侧的按钮,可以打开未列出的Perspective,不需要的Perspective可以在Perspective按钮上点击右键选择close

其次,遇到了一个错误;错误说找不到源代码文件;先解决这个错误;点击Locate File...按钮,在弹出的文件对话框里,找到目录:

c:\cygwin\home\<username>\workspace\hello-cygwin

选中main.c文件;

alt text

和其他IDE一样,Debug时界面上提供一排Debug操作按钮,包括:run/resume,pause,stop,step into,step over,step out;

Eclipse没有分开源码的单步和汇编指令的单步,它提供了一个按钮(带有i符号的),按下去时单步是汇编,弹起来时是源码;

Eclipse提供了所有其他IDE所具有的View功能,包括寄存器、断点、变量、内存、watchpoint、stack frame、反汇编等等;没有显示在界面上的View可以在菜单Windows -> Show View里打开;注意在Show View子菜单最下方有一个Other,点击后可以看到Eclipse(及其插件)能提供的所有的View,有上百个;

所有View在Perspective中的位置都是可以移动的,Eclipse可以自动保存其当前位置;在界面布局上,Eclipse是高度可用户定制化的;

点击Step Over或按F6键,让程序走到结尾的位置,可以看到printf语句的打印输出在底部的Console View里;

alt text

结束这次Debug;切换回C/C++ Perspective;

选中项目名称,菜单Run -> Debug Configuration,选中hello-cygwin Debug,选中Source Tab;

在这里出现了一个Path Mapping。

解释一下刚刚Debugger出现找不到文件的错误原因;Cygwin GCC工具,编译到目标文件里的符号表中,使用的路径是Cygwin路径,比如main.c文件,它记录的是:

\home\ma\workspace\hello-cygwin\main.c

Debug时,GDB使用的路径是Windows路径,它找不到main.c文件,所以这里需要创建一个如图所示的路径映射,把Cygwin路径全部翻译成Windows路径;

这个功能并不是Eclipse实现的,而是GDB内置的功能,因为在实际开发过程中,一个程序在A计算机上开发,然后交付给另一个开发者在B计算机上调试的情况是常见的;要求两个开发者必须保持绝对路径或者相对路径一致都是不可能的;所以GDB提供了这个映射功能;

Eclipse CDT聪明的地方是,它没有强制用户手工创建这个映射,而是让用户通过文件对话框找到文件,然后自动创建了这个映射,同时设置GDB;

当然也可以在启动Debug前手工创建这个映射;

事实上在Eclipse上开发的所有Cygwin GCC程序都会有这个问题,你可以选择手工创建路径映射或者选择文件让Eclipse自动创建,均可。

alt text

Build选项

选中项目名称,点击菜单Project -> Properties;或者,选中项目名称,右键,选择最下面的Properties

alt text

左侧选中C/C++ Build;这里设置的是Build Configuration;

右侧界面中,Configuration栏可以选择设置Debug,或者Release;在Manager Configuration中可以创建和删除Build Configuration;[Active]表示当前使用的Build Configuration;

alt text

Eclipse CDT提供三种方式Build项目,或者说,三个Builder;

缺省的设置是Builder Type为External builder,同时Makefile generation选项中勾选了Generate Makefiles automatially。

在这种方式下,Eclipse CDT自动为项目生成Makefile和Dependency文件;调用外部Make工具(Cygwin里的GNU Make)Build目标可执行文件;

当前项目使用的就是这种方式;

第二种方式,Builder Type仍然选择External builder,但用户去除Generate Makefiles automatically勾选;这种配置下,用户需要自己手工书写和维护makefile;makefile里可以任意调用外部命令和脚本程序;

在规模不大的项目中,这是最灵活的方式,但是学习makefile书写有一定的学习曲线,适合熟悉make的用户使用;对于STM32的C语言嵌入式开发而言,这种方式是最推荐的方式,因为make文件很容易维护,修改也远比在IDE里输入编译和链接参数方便;

第三种方式,是对希望尽可能利用IDE设置编译链接过程的用户而言最友好的,即在Builder type里选择Internal Builder,使用Eclipse CDT内置的Build系统,它仍然外部依赖GNU make和coreutils包里的rm和echo命令;但在Build过程中不生成make文件给用户,后面我们会使用这种方式;

在左侧栏选择Environment;

alt text

make过程是工作在一个独立的Shell会话(进程)里,每个Shell进程都会有自己的环境变量;Environment里设置的就是启动make时,设置的环境变量;

Build Variables也是一种变量,但它是直接传递给make的,make内部有自己的变量表;但是如果make要和它调用的其他命令或者脚本传递参数的话,环境变量是一种方式;

在这个列表中出现的环境变量,都是在创建项目时,选择了Cygwin GCC Toolchain之后,Eclipse CDT自动设置的;

alt text

在左侧栏选择Tool Chian Editor;这里列出了当前的Toolchain是Cygwin GCC,builder是Gnu Make Builder;这些设置都是在创建项目时自动设置,一般不需要修改,通常会改乱掉,除非用户确实是需要更改工具链,例如项目从Windows电脑复制到Linux电脑,需要把Cygwin GCC修改成Linux GCC;或者从一台Windows PC复制到另一台Windows PC,可能需要把Cygwin GCC修改成MinGW GCC,等等。

Select Tools...弹出的对话框列出了所有和项目配置可兼容的工具链,但Eclipse CDT并不会为用户自动安装外部工具链,用户必须自己安装且清楚自己修改或替换工具链的目的或原因;

更换Current builder相对自由,这里的选项和C/C++ Build里的选项是一样,修改其中一个另一个会一起更改;

然后来看Settings。

alt text

Tools Settings Tab里的内容,就是设置编译、汇编和链接程序的运行参数;

All options里汇总了最终用于编译指令参数,但它是一个结果,用户需要通过修改其他选项设置来修改这个结果,但不能直接修改;

这里用户最常修改的选项是Optimization和Debugging,特殊功能选项一般增加在miscellaneous里;

Build Steps里,用户可以设置编译前预处理的命令或脚本程序;例如有需要自动生成的文件,就可以把命令写在Prebuild Steps里;编译后的处理,在嵌入式方面是很常见的,至少需要有把Elf文件strip掉的过程,生成可烧录的二进制问题。

Build Artifact里,是对生成文件类型、文件名、和扩展名的设置;文件名和扩展名都是相当自由的;虽然在大部分商用IDE里,他们看起来都在遵循一些约定,但事实上随便什么名字都可以;

Binary Parsers是与目标平台紧密相关的;它用于解析生成文件中的符号表,在IDE里显示的二进制文件中显示其中的符号、指令集、Endian等信息;例如在Project Explorer中,.exe文件显示[x86/le],意思就是指令集是x86(32bit),little endian;展开之后能看到它包含的头文件和源文件名称;

每个目标平台对可执行程序的格式(包括符号表)都有不同约定;针对不同的格式应该使用对应的Parser才能获得正确的信息;Binary Parsers一般是创建项目时自动设置的,不要修改;

Error Parser的功能是:在编译过程中,解析工具链输出的错误和警告信息,然后反馈到编辑器内,标注错误或警告的位置,类型,给出建议的解决方法;这里的设置一般无须改动,除非某个Parser发疯了给出大量不正确的信息,可以在这里禁用它。

alt text

再来看Paths and Symbols。

Paths

实际的编译过程中,包括头文件目录路径、库文件路径、以及指定链接哪些库文件,都是通过编译和链接参数传递给编译器和链接器的;分别对应这里的Includes、Library Paths、和Libraries设置;

Eclipse缺省并不是把项目目录里的所有源文件都送给编译器编译,只有在Source Location里列出的目录(及其子目录)下的的文件才会被编译;

当前项目创建时是空项目(没有源文件,也推荐这种创建方式),Eclipse CDT自动把项目的根目录加入到Source Location里;这样后来添加或者导入的源文件都会被作为目标编译;

但如果创建项目的时候使用了某种项目模板,例如创建了src目录,且只添加了src目录到Source Location里,那么未来创建了和src目录同级的目录,如果内部要包含源文件,用户需要手工在Source Location里添加该目录。

对于头文件;新建的头文件目录必须手工添加到Includes页面里;要么就只能在源文件里使用#include时,使用头文件的相对路径;

Symbols

Symbol相当于宏定义,GCC接受-D option;在Symbols里定义的Symbol会在编译时全局生效;这样做的好处是无须修改源码文件即可实现宏开关;尤其是那些和Debug相关的开关,可以在Debug Configuration里定义这些Symbol,在Release里不定义;免去反复修改源文件的麻烦,也减少错误发生;

再强调一下:Build系统最终传递给GCC编译器和链接器的,只有命令行参数,在Paths & Symbols里的设置,最终都会转化成命令行参数,在C/C++ Build -> Settings里通过界面做的设置,最终也会转化成命令行参数;最终这些设置的含义,权威文档是工具链的文档,而不是Eclipse的说明;如果编译过程出现不理解的错误,首先在Console窗口中查看完整的编译命令输出,检查参数,然后查阅相关文档。

References页面里允许引用Workspace内的其他项目;例如模块化很好的开发结构,一个SDIO驱动或者一个FATFS文件系统,可以独立成一个项目,其文件被同一个Workspace里的其他项目引用。

  • 题外话:TI的Startware和自家的Code Composer Studio(一个基于Eclipse的定制IDE)提供的大量DEMO是非常好的例子;

alt text

下面把Builder type修改成CDT Internal Builder;

alt text

然后在Project Explorer里,选中Debug目录下的所有文件,删除;

重新编译(Ctrl + B);

alt text

这样在Debug目录下就没有makefile和dependency文件了。

Profile

GCC支持很多扩展特性,这里我们以Gcov作为一个例子。

Gcov是一个Coverage Test工具;Coverage的意思是,在程序的一次运行过程中,有那些代码分支被运行过;

在一次测试过程中,没有被运行过的代码分支,意味着用户需要添加测例或者测试代码,来保证这部分代码分支是被执行过的,这不意味着这部分代码就没问题,但至少跑过比没跑过让人放心;

首先给代码加一个条件分支语句;

alt text

然后检查编译选项,编译器的Optimization必须关掉,Level设置为None;

alt text

Debugging选项里,Debug Level设置为Maximum;勾选Generate gcov information选项;这会给编译器添加如下参数:

-ftest-coverage -fprofile-arcs

alt text

检查Linker选项,在All options里包含上述两个参数;

即Linker也需要这两个参数,在Eclipse Luna SR1中勾选前者会给Linker自动添加这两个参数;在Eclipse Kepler SR2中,这两个参数都只能手工添加到编译器和Linker的Miscellaneous里;

alt text

重新编译一次;注意到在Debug目录下出现了一个新的文件,叫做main.gcno;这个文件是gcov note数据;标注了源代码内的所有分支点;

alt text

然后点击Debug按钮右侧的向下的小箭头,选择hello-cygwin Debug,即之前创建的Debug (Launch) Configuratoin;

alt text

Eclipse的C/C++ Perspective的快捷工具栏中有快速启动Debug, Run, Profile, 和External Tools的按钮,直接点击这几个按钮的逻辑是“启动最近一次同类Launch的Configuration”;如果只是在反复调试一个项目程序,对应每种Launch只有一个Configuration,那么直接点击快捷按钮启动即可,但如果同时打开了多个项目,或者配置了几种不同的Debug Configuration,那么建议的操作方式是上述的方式,保证启动的Configuration是正确的;

让程序完整运行完,然后切换回C/C++ Perspective;选中Debug目录,按F5刷新一下,这时能看到多了一个文件,main.gcda;这是程序运行后自动生成的gcov data文件;

alt text

鼠标双击main.gcda或者main.gcno;在弹出的对话框里点击OK;

alt text

在底部能看到gcov view出现;但是结果不正确;Eclipse CDT的Gcov功能没能正确解析gcov数据;

alt text

Eclipse Luna的Gcov功能集成来自于集成的一个插件,这个项目的名称是Linux Tools;它在Linux上工作很完美但是在Windows上有个要求,就是它要用到的binutils必须在Windows的路径里;Eclipse CDT的其他部分功能并不需要这一点(前面的整个编译调试过程均不需要Windows全局路径包含Cygwin的bin路径);

设置Windows环境变量:

alt text

在Path里添加Cygwin的bin目录路径:

C:\cygwin\bin

alt text

保存环境变量之后,关闭Eclipse;


重启Windows.... rebooting ....


重启完成后,重新打开Eclipse,鼠标双击gcda文件或者gcno文件;

弹出一个不可理喻的对话框,选择main.gcno文件;

alt text

最终终于看到了正确的结果(almost);

标绿色的代码是执行过的部分,没标绿色的代码(实际上应该标红色,Gcov插件有bug)是没有被执行过的;

gcov view里的Coverage %显示100%,指的是这个文件里的函数被100%执行过了。

alt text

CLI方式

如果不用Eclipse的Gcov插件集成,可以在Cygwin Shell里直接用gcov命令查看结果;需要main.c main.gcda. main.gcno等文件在同一个目录下;

alt text

运行gcov main.c,得到一个main.c.gcov文件;

可以用less查看这个文件:

less main.c.gcov

显示如下,左侧的1表示此行代码被运行过1次;按q退出less。

alt text

应该的方式

前面介绍的在Eclipse上使用Gcov的方式其实是workaround的办法;Gcov只要编译链接选项打开,编译后执行一次,即可获得两个文件;

在Linux系统下跑Eclipse(同样的软件),只需下述两种操作方式之一即可得到正确的Gcov View图表;

  1. 直接右键菜单里选择Profiling Tools -> Profile Code Coverage

alt text

  1. 菜单Run -> Profile Configuration,然后在Profiler Tab中选择Gcov,点击Profile

alt text

alt text

alt text

但很可惜在Windows上,这两个操作都只能得到一个启动出错的对话框;所以目前只能按照前面的临时办法得到Gcov数据和结果分析;

How it works?

-ftest-coverage -fprofile-arcs既是编译开关,也是链接开关;对编译器而言,它在所有的分支语句部分加入了特殊代码,对链接器而言,它让项目自动链接libgcov库,同时在链接完成时生成.gcno文件;库里的函数在程序退出时把测试数据写入.gcda文件;

这种测试方式称为Instrumentation。

除了Coverage Test,常用的测试还包括性能测试(gprof),可以获得每个函数的运行时间和运行次数;

Valgrind则是一个丰富和全面的Instrumentation框架,包括堆内存和栈内存使用检查(Massif),内存使用错误检查(Memcheck),线程错误检查(Helgrind),CPU Cache使用检查(Cachegrind)等等。

开源工具链 vs 商用IDE

和Keil/IAR提供的内置工具链不同,GNU工具链和开源工具强调开发者功能,他们提供了丰富的扩展功能让开发者易于Debug问题,易于测试代码,易于检查潜在威胁和改善程序性能。

商用IDE更多的优势在使用便利方面,他们通常提供特殊语言特性,优化的编译器和C语言标准库性能,但就性能而言,目前的GNU ARM工具链和newlib,相比Keil/IAR提供的编译器和库,并未有显著的性能劣势。

Keep Tuned.

⚠️ **GitHub.com Fallback** ⚠️