Create your first Conan package with Visual Studio/MSBuild

In the Create your first Conan package tutorial CMake was used as the build system. If you haven’t read that section, read it first to familiarize yourself with the conanfile.py and test_package concepts, then come back to read about the specifics of the Visual Studio package creation.

Use the conan new command to create a “Hello World” C++ library example project:

  1. $ conan new msbuild_lib -d name=hello -d version=1.0

This will create a Conan package project with the following structure.

  1. .
  2. ├── conanfile.py
  3. ├── hello.sln
  4. ├── hello.vcxproj
  5. ├── include
  6. └── hello.h
  7. ├── src
  8. └── hello.cpp
  9. └── test_package
  10. ├── conanfile.py
  11. ├── test_hello.sln
  12. ├── test_hello.vcxproj
  13. └── src
  14. └── test_hello.cpp

The structure and files are very similar to the previous CMake example:

  • conanfile.py: On the root folder, there is a conanfile.py which is the main recipe file, responsible for defining how the package is built and consumed.

  • hello.sln: A Visual Studio solution file that can be opened with the IDE.

  • hello.vcxproj: A Visual Studio C/C++ project, part of the solution above.

  • src and include folders: the folders that contains the simple C++ “hello” library.

  • test_package folder: contains an example application that will require and link with the created package. In this case the test_package also contains a Visual Studio solution and project, but it is possible to have the test_package using other build system as CMake if desired. It is not mandatory that the test_package is using the same build system as the package.

Let’s have a look at the package recipe conanfile.py (only the relevant new parts):

  1. # Sources are located in the same place as this recipe, copy them to the recipe
  2. exports_sources = "hello.sln", "hello.vcxproj", "src/*", "include/*"
  3. def layout(self):
  4. vs_layout(self)
  5. def generate(self):
  6. tc = MSBuildToolchain(self)
  7. tc.generate()
  8. def build(self):
  9. msbuild = MSBuild(self)
  10. msbuild.build("hello.sln")
  11. def package(self):
  12. copy(self, "*.h", os.path.join(self.source_folder, "include"),
  13. dst=os.path.join(self.package_folder, "include"))
  14. copy(self, "*.lib", src=self.build_folder, dst=os.path.join(self.package_folder, "lib"),
  15. keep_path=False)

Let’s explain the different sections of the recipe briefly:

  • Note there are no options like the shared option in this recipe. The current project always builds a static library, so it is not optional.

  • The layout() defines a typical VS layout, this is less flexible than a CMake one, so it doesn’t allow any parametrization.

  • The generate() method calls MSBuildToolchain to generate a conantoolchain.props file, that the project must add to its properties. If the project had dependencies with Conan requires, it should add MSBuildDeps too and add the relevant generated files property sheets.

  • The build() method uses the MSBuild() helper to drive the build of the solution

  • As the project doesn’t have any “install” functionality in the build scripts, the package() method can manually define which files must be copied.

The hello.vcxproj project file adds the generated property sheets like conantoolchain.props to the project, so the build can receive the Conan input settings and act accordingly.

hello.vcxproj

  1. <ImportGroup Label="PropertySheets">
  2. <Import Project="conan\conantoolchain.props" />
  3. </ImportGroup>

If the project had dependencies, it should add the dependencies generated .props files too.

The test_package folder also contains a test_hello.vcxproj file, that includes both the toolchain and the dependencies property sheets:

test_package/test_hello.vcxproj

  1. <ImportGroup Label="PropertySheets">
  2. <Import Project="conan\conantoolchain.props" />
  3. <Import Project="conan\conandeps.props" />
  4. </ImportGroup>

Note the test_package/conanfile.py contains also a generators="MSBuildDeps".

Let’s build the package from sources with the current default configuration, and then let the test_package folder test the package:

  1. $ conan create .
  2. ...
  3. ======== Testing the package: Executing test ========
  4. hello/1.0 (test package): Running test()
  5. hello/1.0 (test package): RUN: x64\Release\test_hello
  6. hello/1.0: Hello World Release!
  7. hello/1.0: _M_X64 defined
  8. hello/1.0: MSVC runtime: MultiThreadedDLL
  9. hello/1.0: _MSC_VER1939
  10. hello/1.0: _MSVC_LANG201402
  11. hello/1.0: __cplusplus199711
  12. hello/1.0 test_package

We can now validate that the recipe and the package binary are in the cache:

  1. $ conan list hello/1.0:*
  2. Local Cache:
  3. hello
  4. hello/1.0
  5. revisions
  6. 856c535669f78da11502a119b7d8a6c9 (2024-03-04 17:52:39 UTC)
  7. packages
  8. c13a22a41ecd72caf9e556f68b406569547e0861
  9. info
  10. settings
  11. arch: x86_64
  12. build_type: Release
  13. compiler: msvc
  14. compiler.cppstd: 14
  15. compiler.runtime: dynamic
  16. compiler.runtime_type: Release
  17. compiler.version: 193
  18. os: Windows

See also