Monday 3 October 2011

NMAKE and its environment

nmake is a Microsoft's command-line tool for building C/C++ applications. nmake comes with Visual Studio and Windows Driver Development Kit (DDK) and it comes in two versions: one for building 32-bit and another for building 64-bit binaries. Here is the list of its locations, depending on which Microsoft product is installed on your machine:

Visual Studio 2010:
C:\Program Files\Microsoft Visual Studio 10.0\VC\bin\nmake.exe
(32-bit binary; depends on 32-bit advapi32.dll, kernel32.dll and msvcr100.dll)

C:\Program Files\Microsoft Visual Studio 10.0\VC\bin\amd64\nmake.exe
(64-bit binary; depends on 64-bit advapi32.dll, kernel32.dll and msvcr100.dll)

Visual Studio 2008:
C:\Program Files\Microsoft Visual Studio 9.0\VC\bin\nmake.exe
(32-bit binary; depends on 32-bit advapi32.dll, kernel32.dll and msvcr90.dll)

C:\Program Files\Microsoft Visual Studio 9.0\VC\bin\amd64\nmake.exe
(64-bit binary; depends on 64-bit advapi32.dll, kernel32.dll and msvcr90.dll)

Windows DDK (I have some pretty old installed on my machine):
C:\WinDDK\6001.18001\bin\x86\nmake.exe
(32-bit binary; depends on 32-bit advapi32.dll, kernel32.dll and msvcr80.dll)

C:\WinDDK\6001.18001\bin\ia64\nmake.exe
(64-bit binary; depends on 64-bit advapi32.dll, kernel32.dll and msvcr80.dll)

Binaries from amd64/ia64 directories are 64-bit and cannot be run on 32-bit machines.

msvcrXX.dll is Microsoft Visual C Runtime library. XX is its version, specific for each version of Visual Studio: 80 for VS2005, 90 for VS2008 and 100 for VS2010. Binary linked against some msvcr library requires existence of that library on the machine it runs on. If it does not exist, it can be installed with Microsoft Visual C++ VS20XX Redistributable Package (x86 or x64).

If you want to run some particular nmake on your machine, make sure its path is listed in Paths environment variable. If there are more versions of nmake present on your machine, put path to desired version before paths to the other versions. E.g. put path "C:\Program Files\Microsoft Visual Studio 10.0\VC\bin" before "C:\Program Files\Microsoft Visual Studio 9.0\VC\bin" in Paths if you want to run VS2010 nmake. You can always check which nmake will be called from some arbitrary location if executing (from a command line):

where nmake

If nmake resides in multiple directories, all those directories will be listed but only instance from the first listed will be called,
nmake reads instructions from a file called makefile (full name of this file usually does not contain a dot and extension). Instructions of how to build a specific binary (or binaries) are grouped into named targets. Those instructions contain compiler and linker calls, provided with source files, paths to headers, dependencies, object file names and compiler and linker options. Tags for groups of targets or sets of instructions that don't make binaries (like creating or deleting files or directories) are pseudo-targets.

You need to provide target or pseudo-target name when calling nmake. You can provide path to makefile; without it nmake looks for it in the current directory.

makefile contains instruction to call Microsoft compiler, cl.exe and linker, link.exe. Just like nmake, they come with Visual Studio and Windows DDK.

Visual Studio 2010:
c:\Program Files\Microsoft Visual Studio 10.0\VC\bin\amd64\cl.exe
c:\Program Files\Microsoft Visual Studio 10.0\VC\bin\amd64\link.exe
(64-bit compiler and linker for AMD64 architecture)

c:\Program Files\Microsoft Visual Studio 10.0\VC\bin\cl.exe
c:\Program Files\Microsoft Visual Studio 10.0\VC\bin\link.exe
(32-bit compiler and linker for building 32-bit applications)

c:\Program Files\Microsoft Visual Studio 10.0\VC\bin\x86_amd64\cl.exe
(depends on advapi32.dll, kernel32.dll, msvcr100.dll, version.dll, psapi.dll and shell32.dll from C:\Windows\System32 and mspdb100.dll from c:\program files\microsoft visual studio 10.0\common7\ide)

c:\Program Files\Microsoft Visual Studio 10.0\VC\bin\x86_amd64\link.exe
(depends on advapi32.dll, kernel32.dll, msvcr100.dll, psapi.dll and user32.dll from C:\Windows\System32 and mspdb100.dll from c:\program files\microsoft visual studio 10.0\common7\ide) (32-bit cross-compiler and linker for building 64-bit applications for AMD64 architectures)

c:\Program Files\Microsoft Visual Studio 10.0\VC\bin\x86_ia64\cl.exe
c:\Program Files\Microsoft Visual Studio 10.0\VC\bin\x86_ia64\link.exe
(32-bit cross-compiler and linker for building 64-bit applications for Itanium architectures)

Visual Studio 2008:
c:\Program Files\Microsoft Visual Studio 9.0\VC\bin\amd64\cl.exe
c:\Program Files\Microsoft Visual Studio 9.0\VC\bin\amd64\link.exe

c:\Program Files\Microsoft Visual Studio 9.0\VC\bin\cl.exe
c:\Program Files\Microsoft Visual Studio 9.0\VC\bin\link.exe

c:\Program Files\Microsoft Visual Studio 9.0\VC\bin\x86_amd64\cl.exe
c:\Program Files\Microsoft Visual Studio 9.0\VC\bin\x86_amd64\link.exe

c:\Program Files\Microsoft Visual Studio 9.0\VC\ce\bin\x86_arm\cl.exe
c:\Program Files\Microsoft Visual Studio 9.0\VC\ce\bin\x86_arm\link.exe

c:\Program Files\Microsoft Visual Studio 9.0\VC\ce\bin\x86_mips\cl.exe
c:\Program Files\Microsoft Visual Studio 9.0\VC\ce\bin\x86_mips\link.exe

c:\Program Files\Microsoft Visual Studio 9.0\VC\ce\bin\x86_sh\cl.exe
c:\Program Files\Microsoft Visual Studio 9.0\VC\ce\bin\x86_sh\link.exe

Windows DDK:
c:\WinDDK\6001.18001\bin\ia64\ia64\cl.exe
c:\WinDDK\6001.18001\bin\ia64\ia64\link.exe
c:\WinDDK\6001.18001\bin\x86\amd64\cl.exe
c:\WinDDK\6001.18001\bin\x86\amd64\link.exe
c:\WinDDK\6001.18001\bin\x86\ia64\cl.exe
c:\WinDDK\6001.18001\bin\x86\ia64\link.exe
c:\WinDDK\6001.18001\bin\x86\x86\cl.exe
c:\WinDDK\6001.18001\bin\x86\x86\link.exe

Again, which compiler and linker will be called depends on paths listed in Path environment variable. Paths used can be checked by calling (from a command line):

where cl.exe
where link.exe

For example, if we want to build some 64-bit application for AMD architecture (which is usually named x64 and is more common than Itanium one) and we we want to build it on 32-bit machine, using VS2010, we need to use cross-compiler and so will add following paths to Path environment variable:
C:\Windows\System32; (path to dlls used by cl.exe and link.exe)
c:\program files\microsoft visual studio 10.0\common7\ide; (path to dlls used by cl.exe and link.exe)
C:\Program Files\Microsoft Visual Studio 10.0\VC\bin\x86_amd64; (path to cl.exe and link.exe)
C:\Program Files\Microsoft Visual Studio 10.0\VC\bin; (path to nmake.exe)

If you try to run 64-bit compiler on 32-bit machine, you'll get message:
c:\Program Files\Microsoft Visual Studio 10.0\VC\bin\amd64>cl.exe /?
This version of c:\Program Files\Microsoft Visual Studio 10.0\VC\bin\amd64\cl.ex
e is not compatible with the version of Windows you're running. Check your compu
ter's system information to see whether you need a x86 (32-bit) or x64 (64-bit)
version of the program, and then contact the software publisher.

You can run 32-bit compiler or 32-bit cross-compiler on 32-bit machine only:
c:\Program Files\Microsoft Visual Studio 10.0\VC\bin\x86_amd64>cl.exe /?
Microsoft (R) C/C++ Optimizing Compiler Version 16.00.30319.01 for x64
Copyright (C) Microsoft Corporation. All rights reserved.

This is not enough for nmake build to be successful. Compiler reads paths to Visual C++ and Windows SDK (Windows Framework) headers from INCLUDE environment variable. These headers are coming with each version of Visual Studio and we need to make sure cl.exe will be using correct ones. For example, if we want to use VS2010, we will set INCLUDE as:
c:\Program Files\Microsoft Visual Studio 10.0\VC\include; (Visual C++ headers - part of Microsoft's implementation of C++ standard)
c:\Program Files\Microsoft Visual Studio 10.0\VC\atlmfc\include; (ATL/MFC headers - add this path only for this type of applications)
c:\Program Files\Microsoft SDKs\Windows\v7.0A\include; (Windows SDK headers)

Furthermore, linker reads paths to Visual C++ and Windows Framework libraries from LIB environment variable. These libraries are specific for each version of Visual Studio and target architecture. If using VS2010 and building binary for AMD64 architecture, we would set LIB as:
c:\Program Files\Microsoft Visual Studio 10.0\VC\lib\amd64; (Visual C++ libraries)
c:\Program Files\Microsoft Visual Studio 10.0\VC\atlmfc\lib\amd64; (ATL/MFC libraries- add this path only for this type of applications)
c:\Program Files\Microsoft SDKs\Windows\v7.0A\lib\x64; (Windows SDK libraries)

Of course, if your application depends on some other framework or package, INCLUDE needs to contain path to its headers and LIB path to its libraries.

I wrote one short batch file for setting Visual Studio environment (environmental variables) for x86 or x64 builds:
setenv.bat (caret character is used to break long lines):
@rem B.Komazec Setting environment for using Microsoft Visual Studio 2010 x86/x64 tools.
@echo off

@if "%1"=="x86" goto set_x86
@if "%1"=="x64" goto set_x64
@if "%1"=="" goto error

:set_x86
@echo Setting environment for using Microsoft Visual Studio 2010 x86 tools.

set INCLUDE=^
c:\Program Files\Microsoft Visual Studio 10.0\VC\include;^
c:\Program Files\Microsoft SDKs\Windows\v7.0A\include;

set LIB=^
c:\Program Files\Microsoft Visual Studio 10.0\VC\lib;^
c:\Program Files\Microsoft SDKs\Windows\v7.0A\lib;

set PATH=^
%SystemRoot%\system32;^
c:\Program Files\Microsoft Visual Studio 10.0\VC\bin;^
c:\program files\microsoft visual studio 10.0\common7\ide;

goto test_bin_locations

:set_x64
@echo Setting environment for using Microsoft Visual Studio 2010 x64 tools.

set INCLUDE=^
c:\Program Files\Microsoft Visual Studio 10.0\VC\include;^
c:\Program Files\Microsoft SDKs\Windows\v7.0A\include;

set LIB=^
c:\Program Files\Microsoft Visual Studio 10.0\VC\lib\amd64;^
c:\Program Files\Microsoft SDKs\Windows\v7.0A\lib\x64;

set PATH=^
%SystemRoot%\system32;^
C:\Program Files\Microsoft Visual Studio 10.0\VC\bin\x86_amd64;^
C:\Program Files\Microsoft Visual Studio 10.0\VC\bin;^
C:\Program Files\Microsoft Visual Studio 10.0\Common7\ide;

goto test_bin_locations

:test_bin_locations
@echo on
where nmake
where cl.exe
where link.exe
@echo off
goto:eof

:error
@echo Usage: setenv.bat [x86^|x64]

goto:eof

For example, to set environment for building x64 (AMD64) application with VS2010, call this script providing "x64" as an argument:

setenv x64

Note that changes made with SET will remain only for the duration of the current CMD session. To check the value of some environment variable in command prompt window use ECHO command followed by environment variable surrounded with percent signs:

echo %Path%

You can also use batch files that come with Visual Studio:
Visual Studio 2010:
C:\Program Files\Microsoft Visual Studio 10.0\VC\bin\vcvars32.bat
C:\Program Files\Microsoft Visual Studio 10.0\VC\bin\x86_amd64\vcvarsx86_amd64.bat
C:\Program Files\Microsoft Visual Studio 10.0\VC\bin\x86_ia64\vcvarsx86_ia64.bat
C:\Program Files\Microsoft Visual Studio 10.0\VC\vcvarsall.bat

Visual Studio 2008:
C:\Program Files\Microsoft Visual Studio 9.0\VC\bin\amd64\vcvarsamd64.bat
C:\Program Files\Microsoft Visual Studio 9.0\VC\bin\vcvars32.bat
C:\Program Files\Microsoft Visual Studio 9.0\VC\bin\x86_amd64\vcvarsx86_amd64.bat
C:\Program Files\Microsoft Visual Studio 9.0\VC\vcvarsall.bat

Links and references:
NMAKE Reference (MSDN)
An introduction to Makefiles
Nmake Makefile Tutorial and Example
CL Environment Variables
LINK Environment Variables
Setting the Path and Environment Variables for Command-Line Builds

No comments: