Command Line Compilation

Introduction

The obvious way to create Windows programs with C and C++ (and C#, for that matter) is to use Visual Studio. And the obvious way to do the editing and compiling is to use the IDE. Whilst the IDE simplifies many aspects of the process of creating an executable, recent versions have become so complex, learning to use them beyond the basics can be an arduous task. Moreover, relying on the built-in defaults to convert one's source code into an executable program bypasses a useful opportunity to learn about this process. Another reason I tend not to use the IDE is so that I can use my own choice of editor (Vim, in my case).

There is educational merit in breaking things down into their component parts, and controlling processes as manually as possible. So, in that spirit, this page explains the steps required to compile a program from the command line.

This page will show you how to compile your source-code into machine-code using only the command-line (AKA DOS-box, console, command-prompt). In order to keep things simple, it provides only the smallest possible set of commands to achieve this. Advanced techniques are discussed in other pages.

Alas, the compilation procedure is not straightforward (so it's not surprising people just use the defaults). It's made more complicated by the difference between a 32 bit and a 64 bit host platform (the operating system on which you're writing and compiling the program), and the target platform (the one on which the program will run).

For these tutorials, I assume you've installed Visual Studio Express 2012, and version 8.1 of the Windows Software Development Kit, and that you're working on a 64 bit version of Windows 7. I also assume that you're creating 32 bit programs (32 bit programs will usually run on 64 bit Windows, but the reverse is not true).

Compilation

Compiling a program actually requires two main steps; compiling the source code, then linking the object code to create an executable program or dynamically-linked library (DLL). However, by default, the compiler program in Visual Studio (cl.exe) automatically calls the linker program (link.exe) so compiling (whether using the IDE or cl.exe at the command-prompt) can seem like a one-step process.

As mentioned, the commands given here are the least required to create a simple executable program. This means that they will probably be unable to compile anything but the most basic programs. Information on compiling more sophisticated programs is given in other tutorials; hopefully by the time you get on to those, you'll have learned enough to work out how to get it compiling.

Step One: Finding the complier

The first thing we need to do is tell the system where it can find the compiler program (cl.exe) and its various support files. The locations of these files are as follows:

C:\Program Files (x86)\Microsoft Visual Studio 11.0\Bin
C:\Program Files (x86)\Microsoft Visual Studio 11.0\Common7\IDE

GOTCHA: Annoyingly, if you try running cl without telling the system about the Common7\IDE directory, cl will run without any error message (or any other message, for that matter), but it won't do anything. The lack of any message leaves one baffled as to what went on, so be careful about that.

Note that Visual Studio Express does not include compiler programs that are 64 bit themselves. It does, however, contain 32 bit compiler programs that generate 32 bit, or 64 bit, object code. The 32-to-32 bit compiler is in the Bin directory, whilst the 32-to-64 bit compiler is in the x86_x64 subdirectory of Bin. The &lsquot;bitness’ of the compiler program is not important for our purposes; we'll just keep everything (i.e. the host and the target) 32 bit.

Step Two: Finding the Header Files

The compiler needs to be able to find any header files we #included in our source file. The header files for the standard C and C++ libraries are found in the following directory:

c:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\Include

If you're writing anything beyond the most basic programs, then you'll probably need Microsoft's Software Development Kit (SDK). We gain access to its features in our source code through its header files, which are installed in the following directories:

C:\Program Files (x86)\Windows Kits\8.1\Include\um

Step Three: Setting the Compiler Options

Now that we know where the relevant files are located, we can start writing the console commands for performing the compilation. As noted, the compiler program file is called cl.exe, so that's the first thing we need. Then we have to work out what options to give it. These are the options used for this tutorial:

/c
I want full control over the compilation process, so I'll issue the linking command myself. Therefore, I need to tell cl not to do the linking stage; this is achieved with the \c option.
/W4
This provides the highest level of warnings about potential problems and actual errors in my source code. Whilst some warnings can be ignored, the greater scrutiny provided by this option gives us more confidence that our program is correct.
/nologo
This just prevents cl from displaying its banner message, which clutters the display.
/EHsc
Controls the way that exceptions should be handled. “sc” means that only C++ exceptions should be caught, and that C functions (presumably included within extern do not throw C++ exceptions. A bit esoteric for a simple tutorial like this, and the main reason for including it is to silence the warning that cl otherwise generates about exception handlers and unwind semantics.
/I path
This tells cl where to find the header (I for Include) files that our program #includes. Note that the order in which header files are included can give rise to some puzzling errors.
filename.c
Obviously, we have to tell cl the name of our source code file. This should be the last argument.

The full list of options for cl (VS 2012) is available here.

Step Four: Writing the Compilation Commands

Putting all this information together, we come up with a few commands for our compiling batch script.

The first thing is to set up the PATH environment variable:

Set PATH=C:\Program Files (x86)\Microsoft Visual Studio 11.0\Common7\IDE;C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\Bin;%PATH%

The %PATH% at the end preserves the existing value of PATH, and puts our compiler paths at the front to give them priority (Windows stops searching as soon as it finds the first instance of the command name).

Now, given all the paths and arguments, the command line can become extremely long and unwieldy. To make the command shorter and more readable, we can break it down with environment variables. For instance, we can store the include paths in environment variables, thus:

set INC_VS=C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\Include
set INC_SDK_SH=C:\Program Files (x86)\Windows Kits\8.1\Include\shared
set INC_SDK_UM=C:\Program Files (x86)\Windows Kits\8.1\Include\um

We can now write our compiler command thus:

cl.exe /c /W4 /nologo /EHsc /I %INC_VS% /I %INC_SDK_SH% /I %INC_SDK_UM% prog.c

Linking

Step Five: Locating the Library Files

Just as the compiler needed to be able to find the header files, the linker needs to be able to find the library files for those headers. Note that one library (.lib) file might contain the code for more than one header file. Another important thing to note is that the library files must be compatible in terms of their bit-width (i.e. whether they contain 32 bit or 64 bit code). Also, in some cases, the library file might contain only statically or dynamically linked code. As with the header-file directories for the compiler, the library directories for the linker can be stored in environment variables:

set LIB_VS=C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\Lib
set LIB_SDK=C:\Program Files (x86)\Windows Kits\8.1\Lib\winv6.3\um\x86

Note that there is no directory called share for the library files (at least not on my systems).

Step Six: Setting the Linker Options

When run in compilation-only mode, cl produces an object (.obj) file, which contains the unlinked machine code. In order to link it and create the final executable program (presumably a .exe file, but it could be a .dll file), we run the link.exe program. It's arguments are thus:

/SUBSYSTEM:CONSOLE
Given that we're using the command-line to do our compilation, I'm assuming the program we're writing is also command-line based. This option tells the linker that we're linking an executable for the console (command-line).
/LIBPATH:path
This option tells the linker where to find the library files containing the functions provided in the #include files.
filename.obj
This is the name of the object-file that we're going to link and convert into our executable program; it's the file created by cl from our original source code file.

Step Seven: Writing the Linking Command

So, the command for linking is as follows:

link.exe /SUBSYSTEM:CONSOLE /LIBPATH:%LIB_VS% /LIBPATH:%LIB_SDK% prog.obj

I explicitly used the /SUBSYSTEM option here for the sake of clarity. However, it is not strictly necessary to do so because the linker can generally work out automatically which type of executable to produce from the input file. link can also work out the bitness.

Note that, unlike the cl command, which does not need the included files to be mentioned in its command line, the linker does need to be told about any object (but not library) files that it might need.

Step Eight: The Complete Batch Script

Putting all this information together, we can write a batch file that performs all the required commands, thus:

Set PATH=C:\Program Files (x86)\Microsoft Visual Studio 11.0\Common7\IDE;C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\Bin;%PATH%

set INC_VS=C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\Include
set INC_SDK_SH=C:\Program Files (x86)\Windows Kits\8.1\Include\shared
set INC_SDK_UM=C:\Program Files (x86)\Windows Kits\8.1\Include\um
cl.exe /c /W4 /nologo /EHsc /I %INC_VS% /I %INC_SDK_SH% /I %INC_SDK_UM% prog.c

set LIB_VS=C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\Lib
set LIB_SDK=C:\Program Files (x86)\Windows Kits\8.1\Lib\winv6.3\um\x86
link.exe /SUBSYSTEM:CONSOLE /LIBPATH:%LIB_VS% /LIBPATH:%LIB_SDK% prog.obj

The Next Step: Make

Once you're comfortable that you understand this tutorial, have a look at the next one, which explains how to use make to save having to recompile source code that hasn't changed.



Home About Me
Copyright © Neil Carter

Last updated: 2016-03-02