Daily Archives: June 22, 2013

Building non-Windows platforms from Visual Studio

To build on Windows I am using the makefile. I have a batch file that will call make with the platform I want to build. For Windows this simply calls  make after setting up the environment. In Visual Studio I have set up an external tool on the menu for building. This tool uses the command output window and therefore parses the errors. So I can hit a button on the toolbar and build using make. Any errors are easily navigable into the source. This isn’t surprising as its using the MS compiler so the format is going to be correct. I like to be able to build on all platforms just as easily.

I do most of the development on Windows, and use VS to edit the files, even when working on OSX or Linux as I like its editor. I can’t directly compile on Windows though as I need to compile in either OSX or Linux. My setup is an OSX computer with a Windows VM and a Linux VM (using Vmware). I keep all the files I’m working on on OSX. They Linux and Windows VMs share those same folders using Vmware Shared Folders features (I could also use networking shares). I also have SSH setup from the Windows VM to OSX and Linux. To set off a build on Linux or OSX I use SSH to run make on the appropriate platform. They are all sharing the same files. The directories are simliar but have different bases.

For example on Windows the files are visible from Z:\. This maps to “~” (home directory) on OSX, and on Linux to /mnt/hgfs/osxhome. I have my Build.bat file take a command line parameter to determine which build to do. For example “Windows” to do the Windows build. “OSX” to do the OSX build. If OSX is selected it takes the directory name, and modifies it to the one used by OSX. By removing the drive letter and replacing back slashes with forward slashes. For example Z:\Code\WaterJuice\makefile becomes ~/Code/WaterJuice/makefile. In Linux it would be compe /mnt/hghs/osxhome/Code/WaterJuice/makefile.

So from Visual Studio I can press a button and call Build.bat with Windows, OSX, or Linux. When its OSX or Linux it changes the base name and then calls ssh to run make. The output is displayed in the Visual Studio command output window. This is almost perfect. I do not have to switch VMs, I stay with in Visual Studio and can build on any platform. However the output of GCC is slightly different from Visual studio so it is unable to detect the errors directly. I have a small filter I wrote in Perl that modifies the output to be Visual Studio compatible.

First the file names need to be modified. gcc will obviously output the filenames that it sees. In order for VS to open the correct file they need to be converted to the name the Windows sees. So for example when gcc outputs the file name ./lib/Md5.c (it uses relative paths), the Perl script converts this to Z:\Code\WaterJuice\lib\Md5.c. The other thing it does it change the line output of errors and warnings so that Visual studio recognises them.

Visual studio uses a line format such as:

lib\LibMd5.c(76): error C2065: 'variable' : undeclared identifier

Where as GCC would display the same error as

lib/LibMd5.c:76: error: ‘variable’ undeclared (first use in this function)

So a simple RegEx is used to convert the :nn: part to (nn):  Now with the corrected path Visual Studio will bring up the file at the line number when I press F4.

Here is the Perl script I use. I call it GccToVss.pl

$| = 1;
( $actPath ) = @ARGV;

while ( <STDIN> )
    # Replace UTF-8 quote marks with plain ascii

	# Convert error format and paths from gcc to vss
	s/([\w.\/]+):(\d+):(.*)/$actPath$1($2) : $3/;

    print $_;

It takes a single parameter which is the actual Windows directory where the root of the project is (the directory containing the makefile).

The following is Build.bat, the batch file that sets off the builds. It also caters for building in Cygwin and an x86 Windows XP build.

@echo off

set RELPATH=%~p0
set ACTPATH=%~d0%~p0


set LINUXBASE=/mnt/hgfs/osxdev

SET PROJECT=%2 %3 %4 %5 %6 %7 %8 %9

cd %~p0

if /I "%1" == "Windows" goto Windows
if /I "%1" == "WindowsXP" goto WindowsXP
if /I "%1" == "Cygwin" goto Cygwin
if /I "%1" == "OSX" goto OSX
if /I "%1" == "Linux" goto Linux
if /I "%1" == "All" goto All

echo Syntax:
echo    Build ^<target^>
echo      ^<target^> options:
echo        Windows   - Windows x64 Vista Build
echo        WindowsXP - Windows x86 XP Build
echo        Cygwin    - Cygwin Build
echo        OSX       - Remote OSX Build
echo        Linux     - Remote Linux Build
echo        All       - Builds ALL the above
goto :eof

set ALL=1

call "c:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\vcvarsall.bat" amd64
set CL=/link /LIBPATH:C:\WinDdk\7600.16385.1\lib\wlh\amd64 /LIBPATH:C:\WinDdk\7600.16385.1\lib\crt\amd64 /subsystem:console,6.0
echo ::::::::::::: Building for Windows x64 Vista :::::::::::::::::
echo ---- Build complete for Windows x64 Vista
if "%ALL%" == "" goto :eof

call "c:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\vcvarsall.bat" x86
set CL=/link /LIBPATH:C:\WinDdk\7600.16385.1\lib\wxp\i386 /LIBPATH:C:\WinDdk\7600.16385.1\lib\crt\i386 /subsystem:console,5.1
echo ::::::::::::: Building for Windows x86 XP :::::::::::::::::
echo ---- Build complete for Windows x86 XP
if "%ALL%" == "" goto :eof

echo ::::::::::::: Building for Cygwin :::::::::::::::::
make 2>&1 | tools\GccToVss.pl %ACTPATH%
echo ---- Build complete for Cygwin
if "%ALL%" == "" goto :eof

echo ::::::::::::: Building for OSX :::::::::::::::::
ssh %OSXHOST% "cd %OSXBASE%%RELPATH%; make %PROJECT%" 2>&1 | tools\GccToVss.pl %ACTPATH%
echo ---- Build complete for OSX
if "%ALL%" == "" goto :eof

echo ::::::::::::: Building for Linux :::::::::::::::::
ssh %LINUXHOST% "cd %LINUXBASE%%RELPATH%; make %PROJECT%" 2>&1 | tools\GccToVss.pl %ACTPATH%
echo ---- Build complete for Linux
if "%ALL%" == "" goto :eof

The batch file contains a few hardcoded paths. They should be easy enough to modify to suit your environment.

This is as close as I can get to building directly from Windows itself. There is the possibility of using cross platform compilers on Cygwin to produce OSX and Linux binaries, but they are very complicated to setup. Plus I prefer to build on the native platform itself using the compilers provided.

Hopefully this article helps other people build Linux and OSX binaries from within Visual Studio.