Introduction
Life would
be so much easier if some things were more compatible. Like having no need for
a power plug adapter when flying over the Atlantic. Like not caring which battery
type you need for your gadget. Like using your favorite IDE with any compiler
you might need for the embedded project...
Wait a
minute, we're software developers, we are supposed to know how to make things
compatible. This article describes how to make Visual Studio
"understand" the error messages produced by GCC using a Makefile
project and a simple PERL script.
Although
Visual Studio is a complex IDE with numerous functions, the way it compiles
C/C++ code is pretty straight-forward: it simply executes Microsoft C++ Compiler
(cl.exe) and parses its output to get the error/warning list. So does it mean we
need to fully emulate the behavior of cl.exe version 15 (with the enormous
amount of command-line switches so typical for a version 15 of a tool)?
Fortunately, there is an easier way.
Makefile projects
Visual Studio
supports a special project type - makefile project. It is originally intended
to be used with the Microsoft make tool (NMake), but in fact it will simply run
a given command to build or clean the solution; and what's more important, it
will expect it to report errors in a way compatible with cl.exe.
So let's
try to create a Visual Studio project to build an ARM firmware file using GCC. Before
we begin, make a batch file (we call it build.bat) that either invokes GNU
make, or just calls GCC to compile your code. To keep things simple, I'll use a
tiny "LED blink" demo for the AT91SAM7S256 microcontroller to
illustrate. In this case, the built.bat file will just contain 1 line:
C:\SysGCC\arm-eabi\bin\arm-eabi-g++.exe testled.cpp ../shared/board_cstartup.o ../shared/board_lowlevel.o -nostartfiles -DAT91SAM7S256 -I../shared/at91sam7s256 -T../shared/at91sam7s256/flash_noremap.lds -o testled.elf
If
testled.cpp contains errors, arm-eabi-g++.exe
will report them. So let's make Visual Studio understand its output and help us
navigate among the error list:
1. Open
Visual Studio.
2. Choose
File->New->Project and then select Visual C++->General->Makefile
project.
3. Specify
the directory where you have your embedded project.
4. Specify
"build.bat" as your command line.
5. Add your
source files to the project, so that Visual Studio can recognize them
So what
happens when we make a mistake in the source file and try to build the program?
Visual Studio will run build.bat, build.bat will run GCC, GCC will report the
errors and Visual Studio will show its output.
The problem
here is that those error messages are not clickable. You can see them as clear
text, but Visual Studio won't open the source file when you double-click on
them. They also won't appear in the error list window. This happens due to
different error message formats used by Visual Studio and GCC. GCC error
messages look like this:
<file_name>:row:column: error: <text>
In turn, Visual
Studio, expects something like that:
<file_name>(row,column): error: <text>
A tiny
difference, but just enough for Visual Studio to fully ignore those messages
and make you search line numbers manually.
The script
The way to
fix the problem is pretty straight-forward. We'll make a script that takes the
output produced by built.bat and converts the error messages. Furthermore, there
is a bit more trickery besides just inserting brackets and converting colon to
comma:
- We
need to convert relative paths to absolute.
- We
need to convert cygwin-style (or mingw-style) paths to normal Windows paths.
- We
need to convert the "c:/xxx" paths to "c:\xxx"
One of the quickest
ways to make such a script is using the PERL language. It has its pros and
cons, but it's probably one of the easiest ways to make simple scripts below
100 lines of code that have to do some simple text processing.
First, we
need to change the build command line in the Make Project settings:
build.bat 2>&1 | E:\Perl\bin\perl.exe gccfilt.pl
If you don't have perl.exe installed, you can get it by installing cygwin environment.
The "2>&1"
construct means "join STDERR together with STDOUT" or in simple words
"pass error messages through the script as well".
Second,
let's make the script. We'll detect the GCC error messages using 2 regular
expressions and then try to transform the paths using the unix2winpath() function that we'll define later:
$mydir = `cmd /c cd`;
chomp $mydir;
foreach (<STDIN>)
{
if (/^([^ ]+):([0-9]+):([0-9]+): (.*)$/)
{
print unix2winpath($1)."($2,$3) : $4\n";
}
elsif (/^(In file included from )([^ ]+):([0-9]+):([0-9]+):$/)
{
print unix2winpath($2)."($3,$4) : <==== Included from here (double-click to go to line)\n";
}
else
{
print "$_";
}
}
Now let's
define the path transformation function:
sub unix2winpath
{
my $fp = $_[0];
#Handle "c:/xxx" paths
if (substr($fp,1,1) eq ':')
{
$fp =~ s/\ return $fp;
}
#Handle relative paths
if (substr($fp,0,1) ne '/')
{
$fp =~ s/\ return "$mydir\\$fp";
}
}
Finally,
we need to convert the absolute paths to the Windows format. Instead of hardcoding
Cygwin/MinGW path templates, we'll use the "mount" command to extract
them:
foreach(`mount`)
{
if (/^([^ \t]+) on ([^ \t]+) /)
{
if ($2 eq '/')
{
$ROOTMOUNT = $1;
}
else
{
$MOUNTS{$2} = $1;
}
}
}
Finally,
let's use this information in the unix2winpath() function:
foreach(keys %MOUNTS)
{
if ($fp =~ /^$_\/(.*)$/)
{
my $suffix = $1;
$suffix =~ s/\ return "$MOUNTS{$_}\\$suffix";
}
}
$fp =~ s/\ return $ROOTMOUNT.$fp;
Here it
goes. The GCC messages are transformed on-the-fly and Visual Studio handles
them the same way it would handle the messages from cl.exe. Double-click on a
message to navigate to a line, or use the error list. And, of course, don't
forget about the F8 shortcut that quickly jumps you to the next error.

You can download the gccfilt.pl file described in this article here.