From 836617bd9c02937009a3e19f9d57841984b569be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florin=20Cri=C8=99an?= Date: Mon, 14 Jun 2021 19:46:07 +0300 Subject: [PATCH] Add option to use external GTest in CMake 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. --- cmake/README.md | 176 ++++++++++++++++++++++++++++++---------------- cmake/tests.cmake | 64 ++++++++++------- 2 files changed, 153 insertions(+), 87 deletions(-) diff --git a/cmake/README.md b/cmake/README.md index 89d00c1b6f4f..f0974e180121 100644 --- a/cmake/README.md +++ b/cmake/README.md @@ -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 @@ -52,29 +56,25 @@ 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 =================== @@ -82,71 +82,119 @@ 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. + +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. -To make a *Visual Studio* file for Visual Studio 16 2019, create the *Visual Studio* -solution file above and edit the CMakeCache file. +[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.) - C:Path\to\protobuf\cmake\build\solution\CMakeCache +For example: -Then create the *Visual Studio* solution file again + 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 `. + + +Note that if your generator supports multiple configurations, you will probably want to specify which one to build: - C:\Path\to\protobuf\cmake\build\release>nmake + 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. @@ -164,11 +212,15 @@ Testing To run unit-tests, first you must compile protobuf as described above. Then run: - C:\Path\to\protobuf\cmake\build\release>nmake check + C:\Path\to\build\protobuf\release>cmake --build . --target check + +Or you may want to use the tool you've configured: + + C:\Path\to\build\protobuf\release>nmake check 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. @@ -185,7 +237,7 @@ You should see output similar to: To run specific tests: - 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. @@ -210,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. @@ -280,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. @@ -308,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= - -DZLIB_LIB= + -DZLIB_INCLUDE_DIR= + -DZLIB_LIB= Build and testing protobuf as usual. diff --git a/cmake/tests.cmake b/cmake/tests.cmake index 4a54b70d7424..eb6295f39617 100644 --- a/cmake/tests.cmake +++ b/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 @@ -226,7 +236,7 @@ if(MINGW) endif() add_executable(tests ${all_tests_files} ${common_test_files} ${tests_proto_files} ${lite_test_proto_files}) -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 @@ -236,19 +246,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