Consuming ZLib in Boost using Visual Studio (MSBuild) project reference system - ...

:

Introduction

This is a third article in series about integrating third party tools and libraries into Visual Studio (MSBuild). I am using Boost C++ open source library to demonstrate how tool or a library could be integrated into MSBuild environment. In first article I've discussed how to create custom build and integrate with configuration UI of Visual Studio. Second article covers requirements for a project to be consumed by other projects vis project references. In this article I will explain how custom build could reference other projects. I will be using ZLib C++ open source library required by Boost to demonstrate necessary steps.

Background

ZLib is a free, open source library availabe as download as well as GitHub repository. Boost requires it to enable some of the compression features. It could consume it as a link to a source directory or as DLL or static link library. I will explain how ZLib could be built in Visual Studio and added as project reference to Boost build to enable the integration. At the time writing this article ZLib supports Visual Studio up to VC11. We will add support for VC12 ans VC14 and enable some UI configuration while at it.

Project

Standard ZLib distribution or clone of the repository has Visual Studio projects located at contrib\vstudio folder. It has sub-folder for each of the supported version of Visual Studio: VC9, VC10, VC11. 

Attempt at building latest version with Visual Studio 13 failed and instead of trying to fix it I've decided to create new project with integrating all of the enhancements we've learned so far. I've decided to add Property page with only valid settings for the project such as DLL/LIB, calling convention and such. For details on how it should be done see this artcile.

After adding all the necessary fields I've got these options:

Normally Visual Studio project file is located at contrib\vstudio\vcXX directory. But sometimes it is beneficial to have only one copy of ZLib source on the computer and use just references to it in multiple projects. To enable that and prevent overriding settings in this file in multiple projects I've added extra parameter pointing to source's root. Now project file could be copied to working solution directory, pointed to the installed source and changed and built without affection other projects.

I've tryed to add only options that are applicable and valid for the library. For example Configuration Type could be only Dynamic or Static library and can not be EXE or Makefile as in standard template. Target Name is initially assigned as zlib1 (well know name for this library) but if WINAPI option is enabled it could be changed to zlibwapi or old zlib.

By default the build uses assembly implementation from contrib directory. To disable assembly and fallback to C code set Use ASM implementation as No.

Implementation

The project is implemented in zlib.vcxproj file and targets newer zlib1 library. It has been changed from zlibvc.vcxfroj to prevent compatability issues. In case you might require zlibvc.vcxproj or zlibstat.vcxproj, just set correct options in the project UI and save it as either zlibvc.vcxproj or zlibstat.vcxproj.

Project ID

ZLib has a well known project ID: {8FD826F8-3739-44E6-8CC8-997122E53B8D}. It is important because this ID could be used to identify this library to consuming project to uniquely distinguish it from other projects. Boost will be using it to resolve the reference to the library.

Source Files

Reference to source files has been changed to be relative to root folder:

<ItemGroup>
  <ClInclude Include="$(ZLibDir)deflate.h" />
  <ClInclude Include="$(ZLibDir)infblock.h" />
  <ClInclude Include="$(ZLibDir)infcodes.h" />
  <ClInclude Include="$(ZLibDir)inffast.h" />
  <ClInclude Include="$(ZLibDir)inftrees.h" />
  <ClInclude Include="$(ZLibDir)infutil.h" />
  <ClInclude Include="$(ZLibDir)zconf.h" />
  <ClInclude Include="$(ZLibDir)zlib.h" />
  <ClInclude Include="$(ZLibDir)zutil.h" />
</ItemGroup>

Assembly Files

Original project used custom build step to compile assembly files before it could be linked into project. 
Latest Visual Studio distributions come with MASM tool built-in and support building of .ASM files natively. It requires adding MASM targets to the project and is done by adding Build Customisation:

  1. Right click on project name in Project Explorer window
  2. Click on Project Dependencies
  3. Click on Build Customizations
  4. Select MASM(.target .props)
  5. Save and you are done.

Now add the .ASM files to the project:

<ItemGroup>
  <MASM Include="$(ZLibDir)contrib\masmx86\inffas32.asm" />
  <MASM Include="$(ZLibDir)contrib\masmx86\match686.asm" />
  <MASM Include="$(ZLibDir)contrib\masmx64\gvmat64.asm" />
  <MASM Include="$(ZLibDir)contrib\masmx64\inffasx64.asm" />
</ItemGroup>

After adding the build conditions to properly select which file is built we can run the compile.

Issues with Assembly modules

It was brought to my attention that these modules have few issues. If you are planning to work on these you might enable ASM modules otherwise stay with the C++ implementation.

Module Definition File

To create export link library we have to specify module definition file. It is done by assigning path to win32\zlib.def file to ModuleDefinitionFile element of Link item.

<ItemDefinitionGroup Label="Globals">
  <Link>
    <ModuleDefinitionFile>$(ZLibDir)win32\zlib.def</ModuleDefinitionFile>
  </Link>
</ItemDefinitionGroup>

Preprocessor Definitions

Project creates following definitions: _CRT_NONSTDC_NO_DEPRECATE,_CRT_SECURE_NO_DEPRECATE_CRT_NONSTDC_NO_WARNINGS

By default project uses assembly implementations and defines ASMV and ASMINF. If Use ASM Implementations is set to No, these are not defined. If WINAPI Calling Convention option set to Yes project defines: ZLIB_WINAPI.For more information about this option see readme.txt file at contrib\vstudio folder.

Outputs

Building the project creates zlib1.lib and if Configuration Type is set to Dynamic Library file zlib1.dll as well.

Referencing ZLib

ZLib project uses default implementation of build process defined in: 

<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<Import Project="$(VCTargetsPath)\BuildCustomizations\masm.targets

and does not require any customization. It is a normal C++ project with all the default targets and tasks. To reference ZLib we have to do following:

  1.  Add zlib project to Boost solution
  2. Right click on a Boost project and select Properties and open Properties dialog
  3. Go to Common Properties and click on References
  4. Click on Add Reference button
  5. Select zlib project and click OK

Boost

As we discussed in this article consuming of the dependency library comes down to linking to proper link library file and copying appropriate DLLs to the right directory. 

Normal applications relay on Linker to do all the object manipulation but boost is different. It uses its own build system to do compilation and linking (well, it still uses the linker but it does it on its own). So instead of passing references to libraries to the Linker we have to pass it to the Jam Build tool. It is done either as an environment variable or as command line attribute when the tool is called. It is done outside of MSBuild sequence of target calls so we have to resolve it and pass it to the tool separately.

I am going to use command line attribute to pass this information to Jam Build Tool.

Referencing

Information about dependency references is stored in Reference Item in the project file. It is added and modified by Visual Studio and does not require any customization on our part. All we have to do is to get the name of the referenced project, call GetResolvedLinkLibs to get list of link libraries, select one that has reference to zlib1.lib and pass it to b2.exe.

We could approach it two different ways:

  • we could  use MSBuild task to call GetResolvedLinkLibs target on zlib project directly or
  • call GetResolvedLinkLibs on own project to get all of the references and select one that belongs to zlib

Returned values of targets are cashed in MSBuild and GetResolvedLinkLibs will be called anyway so it is cheaper to call our own target. We will be using ZLIB_LIBPATH switch to pass this information alone to the b2.exe.

<Target Name="PrepareForBoostBuild" Returns="@(boost-options)" >
    ...
  <MSBuild Targets="GetResolvedLinkLibs" Projects="$(MSBuildProjectFullPath)" >
    <Output ItemName="Dependencies" TaskParameter="TargetOutputs"/>
  </MSBuild>

  <ItemGroup Label="Dependency Libraries">
    <boost-options Condition="'%(Project)'=='{8fd826f8-3739-44e6-8cc8-997122e53b8d}'" 

                   Include="-sZLIB_LIBPATH=&#34;%(Dependencies.Identity)&#34;" />
  </ItemGroup>
  ...  

As you could see in this code sample we call GetResolvedLinkLibs target to get list of all link libraries and filter one that belongs to ZLib project based on project ID.

Support for other dependencies will be added the same manner.

Precedence

Boost library allows specifying of ZLib source code path as well as binary file to link to. Selection of which one has priority is left to Boost build.

Using the code

I've provided two downloads: Sample and Source.

Source download contains five files (project, targets and three xml) required to build Boost library. Copy it into a directory. Specify location of Boost library's root and build.

Sample download contains a Solution with three projects: Boost-MSBuild and Sample. When built and executed these will allow you to see the functionality described it this article in action.

History

03/20/2015 - Published.