Skip to content

Windows recipe patterns

Mike Sarahan edited this page Jan 24, 2016 · 12 revisions

Building blocks for Windows recipes

Building things on Windows can be a little bit tricky and annoying because there are 3 different compilers to contend with (VS 2008 for Py 2.7-Py3.2, VS 2010 for Py 3.3-Py3.4, and VS 2015 for Py 3.5+). Hopefully the patterns here help you get started faster.

Checking for 32-bit or 64-bit

This is often used to feed the correct platform to msbuild, or to adjust configuration steps.

if "%ARCH%" == "64" (
  set ARCH=x64
) else (
  set ARCH=Win32
)

Using CMake with Visual Studio

You have at least 2 options using CMake with Visual Studio.

NMake makefiles

Visual Studio includes a tool called NMake that is much like Make on Linux/Mac. CMake can output NMake makefiles. These makefiles are compiled with whatever compiler is active for conda-build. This means no additional configuration for you to support multiple Python platforms.

cmake -G "NMake Makefiles" -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=%LIBRARY_PREFIX% ..\
cmake --build . --target INSTALL --config Release

Version-specific solution files

The downside of NMake is that it does not build in parallel, and there are occasionally strange corner cases that prevent it from working. If you're having problems with it, try generating version-specific solution files instead. This is more complicated in terms of bld.bat file, but perhaps simpler from the standpoint of taking the path more traveled.

REM write a temporary batch file to map cl.exe version to visual studio version
echo @echo 15=9 2008> msvc_versions.bat
echo @echo 16=10 2010>> msvc_versions.bat
echo @echo 19=14 2015>> msvc_versions.bat

REM Run cl.exe to find which version our compiler is
for /f "delims=" %%A in ('cl /? 2^>^&1 ^| findstr /C:"Version"') do set "CL_TEXT=%%A"
FOR /F "tokens=1,2 delims==" %%i IN ('msvc_versions.bat') DO echo %CL_TEXT% | findstr /C:"Version %%i" > nul && set VSTRING=%%j && goto FOUND
EXIT 1
:FOUND

if "%ARCH%" == "64" (
   set "VSTRING=%VSTRING%Win64"
)

REM Trim trailing whitespace that may prevent CMake from finding which generator to use
call :TRIM VSTRING %VSTRING%

cd build

cmake -G "Visual Studio %VSTRING%" -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=%LIBRARY_PREFIX% ..
cmake --build . --target INSTALL --config Release
 
:TRIM
  SetLocal EnableDelayedExpansion
  set Params=%*
  for /f "tokens=1*" %%a in ("!Params!") do EndLocal & set %1=%%b
  exit /B

Updating older .sln files

You don't actually need solution files for each different VS version you want. Instead, you can take a VS 2008 solution and update it as necessary.

devenv.exe FreeImage.2008.sln /Upgrade
msbuild FreeImage.2008.sln /property:Configuration=Release /property:Platform=%ARCH%

Passing data from Python to your build script

It is sometimes helpful to pass characteristics about your Python environment that Conda-build does not make available as an environment variable. You can obtain these values by executing print statements:

for /f "delims=" %%A in ('%PREFIX%\python -c "import sys; print(sys.version_info.major)"') DO SET PY_MAJOR=%%A

for /f "delims=" %%A in ('%PREFIX%\python -c "import sys; print(sys.version_info.minor)"') DO SET PY_MINOR=%%A

Missing stdint.h

Visual Studio 2008 did not include stdint.h. You can download them using:

    curl -O http://msinttypes.googlecode.com/svn/trunk/stdint.h

Note that you need curl as a build-time requirement for this to work.

Finally, copy this downloaded file wherever your recipe looks for include files. For example, if your source code working folder has an include folder:

    copy stdint.h %SRC_DIR%\include\stdint.h

Duplicate stdint.h symbols

Very related to above. Many recipes compensate for the lack of stdint.h by including their own definitions. Where recipes do not limit the scope correctly, these symbols conflict with those that Microsoft provides, and you need to fix the includes to avoid duplicate symbols.

The preprocessor lines where stdint.h is should be:

#if defined(_MSC_VER) && _MSC_VER >= 1600
#include "stdint.h"
#else
#include "<internal library stdint definitions>"
#endif
Clone this wiki locally