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:
- Right click on project name in Project Explorer window
- Click on Project Dependencies
- Click on Build Customizations
- Select MASM(.target .props)
- 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:
- Add zlib project to Boost solution
- Right click on a Boost project and select Properties and open Properties dialog
- Go to Common Properties and click on References
- Click on Add Reference button
- 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="%(Dependencies.Identity)"" />
</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.