Skip to content

Commit

Permalink
Add option to use external GTest in CMake (#8736)
Browse files Browse the repository at this point in the history
If `-Dprotobuf_USE_EXTERNAL_GTEST=ON` is passed to CMake, it will use an external Google Test copy (i.e. by calling `find_package(GTest REQUIRED)`) rather than the one provided as a submodule.

This makes sense for larger projects that might already include Google Test and want to use a more standard CMake approach.

Also updated build instructions with this information, and more idiomatic usage.

Co-authored-by: Adam Cozzette <acozzette@google.com>
  • Loading branch information
florin-crisan and acozzette committed Feb 8, 2022
1 parent 18c951e commit 9ebb317
Show file tree
Hide file tree
Showing 2 changed files with 148 additions and 86 deletions.
170 changes: 111 additions & 59 deletions cmake/README.md
Expand Up @@ -36,6 +36,10 @@ If *git* command is not available from *Command Prompt*, add it to system *PATH*

C:\Path\to>set PATH=%PATH%;C:\Program Files\Git\cmd

Optionally, you will want to download [ninja](https://ninja-build.org/) and add it to your *PATH* variable.

C:\Path\to>set PATH=%PATH%;C:\tools\ninja

Good. Now you are ready to continue.

Getting Sources
Expand All @@ -52,101 +56,145 @@ download `protobuf-all-[VERSION].tar.gz`.

Or you can use git to clone from protobuf git repository.

C:\Path\to> git clone -b [release_tag] https://github.com/protocolbuffers/protobuf.git
C:\Path\to> mkdir src & cd src
C:\Path\to\src> git clone -b [release_tag] https://github.com/protocolbuffers/protobuf.git

Where *[release_tag]* is a git tag like *v3.0.0-beta-1* or a branch name like *master*
if you want to get the latest code.

Go to the project folder:

C:\Path\to>cd protobuf
C:\Path\to\protobuf>
C:\Path\to\src> cd protobuf
C:\Path\to\src\protobuf>

Remember to update any submodules if you are using git clone (you can skip this
step if you are using a release .tar.gz or .zip package):

```console
C:\Path\to> git submodule update --init --recursive
C:\Path\to\src\protobuf> git submodule update --init --recursive
```

Now go to *cmake* folder in protobuf sources:

C:\Path\to\protobuf>cd cmake
C:\Path\to\protobuf\cmake>

Good. Now you are ready to *CMake* configuration.
Good. Now you are ready for *CMake* configuration.

CMake Configuration
===================

*CMake* supports a lot of different
[generators](http://www.cmake.org/cmake/help/latest/manual/cmake-generators.7.html)
for various native build systems.
We are only interested in
[Makefile](http://www.cmake.org/cmake/help/latest/manual/cmake-generators.7.html#makefile-generators)
and
[Visual Studio](http://www.cmake.org/cmake/help/latest/manual/cmake-generators.7.html#visual-studio-generators)
generators.

We will use shadow building to separate the temporary files from the protobuf source code.
Of most interest to Windows programmers are the following:

* [Makefile](http://www.cmake.org/cmake/help/latest/manual/cmake-generators.7.html#makefile-generators).
This generates NMake Makefiles for Visual Studio. These work, but they are rather slow.

* [Visual Studio](http://www.cmake.org/cmake/help/latest/manual/cmake-generators.7.html#visual-studio-generators)
This generates a Visual Studio solution for the project.

* [Ninja](https://cmake.org/cmake/help/latest/manual/cmake-generators.7.html#ninja-generator)
This uses the external tool [Ninja](https://ninja-build.org/) to build. It is the fastest solution available.

Note that as of Visual Studio 2015, Visual Studio includes
[support for opening directly CMake-based projects](https://docs.microsoft.com/en-us/cpp/build/cmake-projects-in-visual-studio).

It is considered good practice not to build CMake projects in the source tree but in a separate folder.

Create a temporary *build* folder and change your working directory to it:

C:\Path\to\protobuf\cmake>mkdir build & cd build
C:\Path\to\protobuf\cmake\build>
mkdir C:\Path\to\build\protobuf
cd C:\Path\to\build\protobuf
C:\Path\to\build\protobuf>

The *Makefile* generator can build the project in only one configuration, so you need to build
The *Makefile* and *Ninja* generators can build the project in only one configuration, so you need to build
a separate folder for each configuration.

To start using a *Release* configuration:
To start using a *Release* configuration via the *NMmake* generator:

C:\Path\to\protobuf\cmake\build>mkdir release & cd release
C:\Path\to\protobuf\cmake\build\release>cmake -G "NMake Makefiles" ^
C:\Path\to\build\protobuf>mkdir release & cd release
C:\Path\to\build\protobuf\release>cmake -G "NMake Makefiles" ^
-DCMAKE_BUILD_TYPE=Release ^
-DCMAKE_INSTALL_PREFIX=../../../../install ^
../..
-DCMAKE_INSTALL_PREFIX=C:\Path\to\install ^
C:\Path\to\src\protobuf

It will generate *nmake* *Makefile* in current directory.
It will generate a *NMake* *Makefile* in the current directory.

To use *Debug* configuration:
To use *Debug* configuration using *Ninja*:

C:\Path\to\protobuf\cmake\build>mkdir debug & cd debug
C:\Path\to\protobuf\cmake\build\debug>cmake -G "NMake Makefiles" ^
C:\Path\to\build\protobuf>mkdir debug & cd debug
C:\Path\to\build\protobuf\debug>cmake -G "Ninja" ^
-DCMAKE_BUILD_TYPE=Debug ^
-DCMAKE_INSTALL_PREFIX=../../../../install ^
../..
-DCMAKE_INSTALL_PREFIX=C:\Path\to\install ^
C:\Path\to\src\protobuf

It will generate *nmake* *Makefile* in current directory.
It will generate *Ninja* build scripts in current directory.

To create *Visual Studio* solution file:
The *Visual Studio* generator is multi-configuration: it will generate a single *.sln* file that can be used for both *Debug* and *Release*:

C:\Path\to\protobuf\cmake\build>mkdir solution & cd solution
C:\Path\to\protobuf\cmake\build\solution>cmake -G "Visual Studio 16 2019" ^
-DCMAKE_INSTALL_PREFIX=../../../../install ^
../..
C:\Path\to\build\protobuf>mkdir solution & cd solution
C:\Path\to\build\protobuf\solution>cmake -G "Visual Studio 16 2019" ^
-DCMAKE_INSTALL_PREFIX=C:\Path\to\install ^
C:\Path\to\src\protobuf

It will generate *Visual Studio* solution file *protobuf.sln* in current directory.

If the *gmock* directory does not exist, and you do not want to build protobuf unit tests,
you need to add *cmake* command argument `-Dprotobuf_BUILD_TESTS=OFF` to disable testing.
Unit Tests
----------

Unit tests are being built along with the rest of protobuf. The unit tests require Google Mock (now a part of Google Test).

A copy of [Google Test](https://github.com/google/googletest) is included as a Git submodule in the `third-party/googletest` folder.
(You do need to initialize the Git submodules as explained above.)

Alternately, you may want to use protobuf in a larger set-up, you may want to use that standard CMake approach where
you build and install a shared copy of Google Test.

To make a *Visual Studio* file for Visual Studio 16 2019, create the *Visual Studio*
solution file above and edit the CMakeCache file.
After you've built and installed your Google Test copy, you need add the following definition to your *cmake* command line
during the configuration step: `-Dprotobuf_USE_EXTERNAL_GTEST=ON`.
This will cause the standard CMake `find_package(GTest REQUIRED)` to be used.

C:Path\to\protobuf\cmake\build\solution\CMakeCache
[find_package](https://cmake.org/cmake/help/latest/command/find_package.html) will search in a default location,
which on Windows is *C:\Program Files*. This is most likely not what you want. You will want instead to search for
Google Test in your project's root directory (i.e. the same directory you've passed to `CMAKE_INSTALL_PREFIX` when
building Google Test). For this, you need to set the `CMAKE_PREFIX_PATH` CMake variable. (There are other ways in CMake,
see the [manual](https://cmake.org/cmake/help/latest/command/find_package.html) for details.)

Then create the *Visual Studio* solution file again
For example:

C:\Path\to\build\protobuf>mkdir solution & cd solution
C:\Path\to\build\protobuf\solution>cmake -G "Visual Studio 16 2019" ^
-DCMAKE_INSTALL_PREFIX=C:\Path\to\install ^
-DCMAKE_PREFIX_PATH=C:\Path\to\my_big_project ^
-Dprotobuf_USE_EXTERNAL_GTEST=ON ^
C:\Path\to\src\protobuf

In most cases, `CMAKE_PREFIX_PATH` and `CMAKE_INSTALL_PREFIX` will point to the same directory.

To disable testing completely, you need to add the following argument to you *cmake* command line: `-Dprotobuf_BUILD_TESTS=OFF`.

For example:

C:\Path\to\build\protobuf\solution>cmake -G "Visual Studio 16 2019" ^
-DCMAKE_INSTALL_PREFIX=C:\Path\to\install ^
-Dprotobuf_BUILD_TESTS=OFF ^
C:\Path\to\src\protobuf

Compiling
=========

To compile protobuf:
The standard way to compile a *CMake* project is `cmake --build <directory>`.


C:\Path\to\protobuf\cmake\build\release>nmake
Note that if your generator supports multiple configurations, you will probably want to specify which one to build:

cmake --build C:\Path\to\build\protobuf\solution --config Release

You can also run directly the build tool you've configured:

C:\Path\to\build\protobuf\release>nmake

or

C:\Path\to\protobuf\cmake\build\debug>nmake
C:\Path\to\build\protobuf\debug>ninja

And wait for the compilation to finish.

Expand All @@ -172,7 +220,7 @@ You can also build the `check` target (not idiomatic CMake usage, though):

or

C:\Path\to\protobuf\cmake\build\debug>nmake check
C:\Path\to\build\protobuf\release>ninja check

You can also build project *check* from Visual Studio solution.
Yes, it may sound strange, but it works.
Expand All @@ -189,7 +237,7 @@ You should see output similar to:

To run specific tests, you need to pass some command line arguments to the test program itself:

C:\Path\to\protobuf>cmake\build\release\tests.exe --gtest_filter=AnyTest*
C:\Path\to\build\protobuf\release>tests.exe --gtest_filter=AnyTest*
Running main() from gmock_main.cc
Note: Google Test filter = AnyTest*
[==========] Running 3 tests from 1 test case.
Expand All @@ -214,13 +262,17 @@ If all tests are passed, safely continue.
Installing
==========

To install protobuf to the specified *install* folder:
To install protobuf to the *install* folder you've specified in the configuration step, you need to build the `install` target:

cmake --build C:\Path\to\build\protobuf\solution --config Release --target install

Or if you prefer:

C:\Path\to\protobuf\cmake\build\release>nmake install
C:\Path\to\build\protobuf\release>nmake install

or

C:\Path\to\protobuf\cmake\build\debug>nmake install
C:\Path\to\build\protobuf\debug>ninja install

You can also build project *INSTALL* from Visual Studio solution.
It sounds not so strange and it works.
Expand Down Expand Up @@ -284,16 +336,16 @@ You can also compile it from source by yourself.

Getting sources:

C:\Path\to>git clone -b v1.2.8 https://github.com/madler/zlib.git
C:\Path\to>cd zlib
C:\Path\to\src>git clone -b v1.2.8 https://github.com/madler/zlib.git
C:\Path\to\src>cd zlib

Compiling and Installing:

C:\Path\to\zlib>mkdir build & cd build
C:\Path\to\zlib\build>mkdir release & cd release
C:\Path\to\zlib\build\release>cmake -G "NMake Makefiles" -DCMAKE_BUILD_TYPE=Release ^
-DCMAKE_INSTALL_PREFIX=../../../install ../..
C:\Path\to\zlib\build\release>nmake & nmake install
C:\Path\to\src\zlib>mkdir C:\Path\to\build\zlib & cd C:\Path\to\build\zlib
C:\Path\to\build\zlib>mkdir release & cd release
C:\Path\to\build\zlib\release>cmake -G "Ninja" -DCMAKE_BUILD_TYPE=Release ^
-DCMAKE_INSTALL_PREFIX=C:\Path\to\install C:\Path\to\src\zlib
C:\Path\to\src\zlib\build\release>cmake --build . --target install

You can make *debug* version or use *Visual Studio* generator also as before for the
protobuf project.
Expand All @@ -312,8 +364,8 @@ the headers or the .lib file in the right directory.

If you already have ZLIB library and headers at some other location on your system then alternatively you can define following configuration flags to locate them:

-DZLIB_INCLUDE_DIR=<path to dir containing zlib headers>
-DZLIB_LIB=<path to dir containing zlib>
-DZLIB_INCLUDE_DIR=<path to dir containing zlib headers>
-DZLIB_LIB=<path to dir containing zlib>

Build and testing protobuf as usual.

Expand Down
64 changes: 37 additions & 27 deletions cmake/tests.cmake
@@ -1,32 +1,42 @@
if (NOT EXISTS "${PROJECT_SOURCE_DIR}/../third_party/googletest/CMakeLists.txt")
message(FATAL_ERROR
"Cannot find third_party/googletest directory that's needed to "
"build tests. If you use git, make sure you have cloned submodules:\n"
" git submodule update --init --recursive\n"
"If instead you want to skip tests, run cmake with:\n"
" cmake -Dprotobuf_BUILD_TESTS=OFF\n")
endif()
option(protobuf_USE_EXTERNAL_GTEST "Use external Google Test (i.e. not the one in third_party/googletest)" OFF)

option(protobuf_ABSOLUTE_TEST_PLUGIN_PATH
"Using absolute test_plugin path in tests" ON)
mark_as_advanced(protobuf_ABSOLUTE_TEST_PLUGIN_PATH)

set(googlemock_source_dir "${protobuf_source_dir}/third_party/googletest/googlemock")
set(googletest_source_dir "${protobuf_source_dir}/third_party/googletest/googletest")
include_directories(
${googlemock_source_dir}
${googletest_source_dir}
${googletest_source_dir}/include
${googlemock_source_dir}/include
)
if (protobuf_USE_EXTERNAL_GTEST)
find_package(GTest REQUIRED)
else()
if (NOT EXISTS "${PROJECT_SOURCE_DIR}/../third_party/googletest/CMakeLists.txt")
message(FATAL_ERROR
"Cannot find third_party/googletest directory that's needed to "
"build tests. If you use git, make sure you have cloned submodules:\n"
" git submodule update --init --recursive\n"
"If instead you want to skip tests, run cmake with:\n"
" cmake -Dprotobuf_BUILD_TESTS=OFF\n")
endif()

set(googlemock_source_dir "${protobuf_source_dir}/third_party/googletest/googlemock")
set(googletest_source_dir "${protobuf_source_dir}/third_party/googletest/googletest")
include_directories(
${googlemock_source_dir}
${googletest_source_dir}
${googletest_source_dir}/include
${googlemock_source_dir}/include
)

add_library(gmock STATIC
"${googlemock_source_dir}/src/gmock-all.cc"
"${googletest_source_dir}/src/gtest-all.cc"
)
target_link_libraries(gmock ${CMAKE_THREAD_LIBS_INIT})
add_library(gmock_main STATIC "${googlemock_source_dir}/src/gmock_main.cc")
target_link_libraries(gmock_main gmock)

add_library(GTest::gmock ALIAS gmock)
add_library(GTest::gmock_main ALIAS gmock_main)
endif()

add_library(gmock STATIC
"${googlemock_source_dir}/src/gmock-all.cc"
"${googletest_source_dir}/src/gtest-all.cc"
)
target_link_libraries(gmock ${CMAKE_THREAD_LIBS_INIT})
add_library(gmock_main STATIC "${googlemock_source_dir}/src/gmock_main.cc")
target_link_libraries(gmock_main gmock)

set(lite_test_protos
google/protobuf/map_lite_unittest.proto
Expand Down Expand Up @@ -227,7 +237,7 @@ if (MSVC)
/wd4146 # unary minus operator applied to unsigned type, result still unsigned
)
endif()
target_link_libraries(tests libprotoc libprotobuf gmock_main)
target_link_libraries(tests libprotoc libprotobuf GTest::gmock_main)

set(test_plugin_files
${protobuf_source_dir}/src/google/protobuf/compiler/mock_code_generator.cc
Expand All @@ -237,19 +247,19 @@ set(test_plugin_files
)

add_executable(test_plugin ${test_plugin_files})
target_link_libraries(test_plugin libprotoc libprotobuf gmock)
target_link_libraries(test_plugin libprotoc libprotobuf GTest::gmock)

set(lite_test_files
${protobuf_source_dir}/src/google/protobuf/lite_unittest.cc
)
add_executable(lite-test ${lite_test_files} ${common_lite_test_files} ${lite_test_proto_files})
target_link_libraries(lite-test libprotobuf-lite gmock_main)
target_link_libraries(lite-test libprotobuf-lite GTest::gmock_main)

set(lite_arena_test_files
${protobuf_source_dir}/src/google/protobuf/lite_arena_unittest.cc
)
add_executable(lite-arena-test ${lite_arena_test_files} ${common_lite_test_files} ${lite_test_proto_files})
target_link_libraries(lite-arena-test libprotobuf-lite gmock_main)
target_link_libraries(lite-arena-test libprotobuf-lite GTest::gmock_main)

add_custom_target(check
COMMAND tests
Expand Down

0 comments on commit 9ebb317

Please sign in to comment.