The Eclipse CDT (C/C++ Development Tooling) has been around for awhile. While it has some usefulness as a generic IDE I prefer it because it uses external makefiles and cross compilation toolchains. This means I don't need to maintain two separate project structures and anything I do on the commandline is the same as what the CDT does when using built-in commands. Also, it works on OSX where most vendor supplied IDEs only support Windows.
While powerful the Eclipse CDT isn't for the faint-of-heart. It most definitely doesn't work without some manual configuration and can be quite frustrating when it doesn't do what you expect. This blog post will take you through one particular use-case: setting up the Eclipse CDT to build and index native source using only an existing set of Makefiles and an external toolchain. Specifically we'll build the blinky example in the Nordic Semiconductor nRF5 SDK using the GNU ARM Embedded Toolchain (arm-none-eabi).
Prerequisites
Mostly this post is about getting setup with the CDT indexer so it'll be useful across a wide range of SDKs and toolchains. If you want to follow along though here's what you'll need:
- Eclipse CDT – I'm using the Neon release 9.0.0 but these steps should apply to 8.8.1/Mars as well
- nRF5-SDK – If you want to actually run the code you'll need to buy one of the evaluation boards supported by their SDK but that's not germane to this blog post. You only need to download this free SDK to try the techniques we'll discuss.
- GNU Arm Embedded Toolchain – I think you can get this through brew on OSx otherwise you'll have to download it from launchpad.net or build it from source. Try apt-get on ubuntu. Given ARM's dominance these days it's not a hard toolchain to locate.
Setup
- Install the Eclipse CDT.
- Unpack the nRF5 SDK.
- Open an Eclipse workspace.
A brief digression: I always use this bash script to launch eclipse. It starts an Eclipse instance with the workspace set as the directory the shell script is located within and uses JAVA_HOME to find a jre (if set).
#!/usr/bin/env bash # From http://stackoverflow.com/questions/59895/can-a-bash-script-tell-what-directory-its-stored-in SOURCE="${BASH_SOURCE[0]}" while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )" SOURCE="$(readlink "$SOURCE")" [[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE" # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located done DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )" if [ -z ${ENV_ECLIPSE_PATH+x} ]; then if [[ "$OSTYPE" == "darwin"* ]]; then ENV_ECLIPSE_PATH=/Applications/Eclipse.app/Contents/MacOS/eclipse else ENV_ECLIPSE_PATH=eclipse fi fi echo "Opening eclipse workspace at ${DIR}" echo "(${ENV_ECLIPSE_PATH})" if [ -z ${JAVA_HOME+x} ]; then JAVA_VM_ARG= else JAVA_VM_ARG="-vm ${JAVA_HOME}/jre/bin" fi ${ENV_ECLIPSE_PATH} -data ${DIR} ${JAVA_VM_ARG} &> /dev/null &
I'll start my example by opening a new workspace directly under the nRF5 SDK root folder.
Blinky
Now start a new project in the workspace you just opened: File > New > New Makefile Project with Existing Code
We'll create the project right in the blinky directory. In the nRF5 SDK this is found under examples/peripheral/blinky.
We select the Cross GCC toolchain here because we're going to modify it in a later step but note that we don't actually build using this toolchain.
Next open the project properties (right click on the project or cmd+i) and then C/C++ Build. Here you'll see the Build Command the CDT uses when building. For many projects you'll need to tweak this command. For the nRF5 SDK we need two additional parameters:
make GNU_INSTALL_ROOT=/usr/local/ VERBOSE=1
- GNU_INSTALL_ROOT=[path to your gcc – This is needed to provide the makefile with the basepath for the gcc arm embedded toolchain. On my system this is /usr/local
- VERBOSE=1 – This one is really important. Many makefiles suppress compiler output for aesthetic reasons but in the next step we're going to tell the indexer to read this output. Luckily the Nordic makefiles provide this VERBOSE override. Some other makefiles will have to be manually modified to emit the gcc commands. This usually involves removing prepended @ directives in object rules.
- You also need to modify the build directory to where the Makefile is located. For the Nordic SDK this will be, from the workspace root, [board]/blank/armgcc where [board] is the name of one of the Nordic evaluation boards (Use pca10040 if you just want to build something and don't have a board).
Now when you build (cmd+B) you should see a bunch of scrollback in the console window and the blinky example should build. You'll see a binary size report at the end of the console if the build succeeded.
Indexer
If you open the main.c in our blinky project you'll see a sad state of affairs. Lots of little beetles and red squiggly lines. If the build succeeded then why are there so many errors in the source? The CDT build and indexer are independent systems and it's the indexer that reports these errors. To get the indexer to use our external makefiles and toolchain we need to modify two "providers": the CDT GCC Build Output Parser and the CDT Cross GCC Build-In Compiler Settings. To get to the list of providers open the project properties (right click or cmd+i) and go to C/C++ General > Preprocessor Include Paths > Providers.
GCC Build Output Parser
This little gem reads the console output from your build and parses and -I or -D directives it finds. The trick is it only looks for arguments to known compiler aliases. If you are using a compiler like avr-gcc or, as in this example, arm-none-eabi-gcc you'll need to modify the default pattern appropriately. It takes a regular expression so you can get fancy with this if you like. For this example I've just added "or arm-none-eabi-gcc": |(arm-none-eabi-gcc)
CDT Cross GCC Built-in Compiler Settings
The next provider we are going to use is a bit of a hack but it seems to work brilliantly. The Built-in Compiler provider is supposed to provide information about system headers and defines for the Cross GCC toolchain. We're not really using this per-se but we can aim it at our compiler to provide the indexer with the same information. Again I've just hard-coded in arm-none-eabi-gcc but you can do this with whatever compiler you are using. Just be sure to supply the right parameters to get it to dump its system include paths and defines to standard out. Open a console and try this manually first if you aren't sure what the options are. If you are still unsure if this is working for check the "Allocate console in the Console View" box for this provider. When the indexer runs you should see all the system paths and -D defines dumped to one of your CDT consoles.
Unicorns and Rainbows
So that's it. You should now have fully indexed source for this blinky example. If you don't you can try coercing the indexer by right clicking on the project and selecting Index > Rebuild. Now you can simply cmd+click on any type, define, or include to hyperlink right into the source. You should also see the elf output for this example in your Project Explorer pane. This expands to reveal all the source files that went into building it. With more work you can get the Eclipse CDT to debug, write hex images to boards, manage your SCM (i.e. git), etc. All this and it's all open-source, free, and cross-platform.