Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Modernize and clean up CMakeLists.txt for streets_service_base #237

Draft
wants to merge 15 commits into
base: develop
Choose a base branch
from

Conversation

adamlm
Copy link
Contributor

@adamlm adamlm commented Oct 15, 2022

PR Details

Description

This PR addresses issue #236 by modernizing and cleaning up the project's CMake configuration.

The biggest change is architecture. Package dependencies have been moved into a dependencies.cmake file to clean up the top-level CMakeLists.txt file. All commands related to building the streets_service_base library have been moved to src/CMakeLists.txt, again to clean up the top-level file. Finally, all test-related commands have been moved to test/CMakeLists.txt. The architectural changes are based on the recommended project structure in Craig Scott's Professional CMake book (Section 34.2 in the 13th edition).

Other general changes include moving global configurations to target-based ones to avoid unintended and undesirable behaviors. For example, setting compiler flags.

The CMake version is updated to 3.16, which is the version available in Ubuntu 20.04's apt repos. With an upgraded version, we can better utilize new CMake features. Configuration options were added to allow consumers to decided on building tests and setting the spdlog logging level. Additionally, tests will not build by default if the project is consumed as a subproject into another project. Consumers can override this if they desire.

If there is a design decision that seems unclear, please check my commit messages. I tried to explain my reasoning/justification in them.

Related Issue

Closes #236

Motivation and Context

CMake brings a lot of historical baggage and complexities that developers are trying to address in newer releases. Therefore, it is good project hygiene to periodically review and clean out old CMake usages for better practices. This PR is an attempt to do that.

For a non-technical motivation, I wanted to work on a non-trivial CMake project while also contributing to an open-source project.

How Has This Been Tested?

I built the project in a clean environment and ran the unit tests. Everything seemed to work fine.

I built some consumer projects, and those seemed to build without issues. I have not tested final _service packages due to other dependency/build challenges. However, direct consumers of streets_service_base seem to build fine with the new changes.

I took care to ensure the unit test executable is put where the CI scripts expect it to be, so it should not affect that process.

Types of changes

  • Defect fix (non-breaking change that fixes an issue)
  • New feature (non-breaking change that adds functionality)
  • Breaking change (fix or feature that cause existing functionality to change)

Checklist:

  • I have added any new packages to the sonar-scanner.properties file
  • My change requires a change to the documentation.
  • I have updated the documentation accordingly.
  • I have read the CONTRIBUTING document.
    CARMA Contributing Guide
  • I have added tests to cover my changes.
  • All new and existing tests passed.

adamlm added 14 commits October 11, 2022 22:18
For repeatability, dependency versions should be explicitly listed.
Exact dependency versions are listed now, but they could be relaxed in
the future to allow for compatible major versions. However, this
assumes dependency developers strictly adhere to semver conventions. The
spdlog package, for example, had API breaking changes between versions
1.5.0 and 1.10.0.

streets_utils/streets_service_base/CMakeLists.txt
* add version for Boost
* add version for spdlog
* add RapidJSON as required package since project links to it
* add version for RapidJSON
The streets_service_base package requires code from the RapidJSON and
spdlog packages that are not available in the Ubuntu 20.04 repositories.
For RapidJSON, the streets_service_base package requires code not
available in any release version. Therefore, both of these dependencies
are vendored through CMake's FetchContent feature and built from source
as subprojects.

Additonally, the GTest dependency is explicitly added and linked through
the provided import target.

streets_utils/streets_service_base/CMakeLists.txt
* explicitly add GTest dependency
* include dependencies.cmake file
* link to GTest::GTest

streets_utils/streets_service_base/dependencies.cmake
* add RapidJSON repo version locked to a commit hash
* add spdlog repo version locked to latest release
The previous version used ${PROJECT_NAME} all over the project's
CMakeLists.txt file, which made reading and understanding challenging.
Craig Scott in his Professional CMake book discourages using
${PROJECT_NAME} for this very reason.

streets_utils/streets_service_base/CMakeLists.txt
* replace ${PROJECT_NAME} with the actual name
The spdlog library will not generate export targets if it is a
subproject, so it needs to be added to the top-level project's export
target set.

streets_utils/streets_service_base/CMakeLists.txt
* add spdlog target to export set for top-level project
The previous formatting was inconsistent and difficult to read. The new
formatting has tabs sizes of 2, among other small changes.

streets_utils/streets_service_base/CMakeLists.txt
* update formatting
External dependencies and vendored dependencies all now live in the
dependencies.cmake file. The top-level CMakeLists.txt file includes
dependencies.cmake, a project organization recommended in the Professional
CMake book.

The spdlog dependency was changed from a source dependency to a binary
one. There was really no need to build the project from source. The only
requirement was a minimum version.

streets_utils/streets_service_base/CMakeLists.txt
* move find_package calls to dependencies.cmake
* remove spdlog install target since it is no longer a subproject

streets_utils/streets_service_base/dependencies.cmake
* add find_package calls for Boost, spdlog, and GTest
* remove FetchContent call for spdlog. It is now the developer's
  responsibility to acquire it
Move compile definitions (spdlog level) and compiler flags to target
specific commands. This avoids polluting the global project space.

streets_utils/streets_service_base/CMakeLists.txt
* move global compile definitions to target specific scopes
* move global compile flags to target specific scopes
* elevate spdlog level to ERROR on unit tests. The TRACE level before
  was cluttering the output, and in my opinion, trace-level outputs
  on unit tests are unnecessary.
Install commands were using hard-coded installation paths, which while
not technically wrong, would make it harder for consuming packages to
change the install location. The install commands were changed to use
the recommended GNUInstallDirs variables.

streets_utils/streets_service_base/CMakeLists.txt
* update install commands to use the install variables defined in
  GNUInstallDirs
* replace header and template file installation to use DIRECTORY
  variant because it is a bit easier to read and doesn't involve
  globbing files
The RapidJSON library was previously built from source as a subproject
using FetchContent. However, while testing installation to non-standard
locations, it was discovered that overriding RapidJSON's install
location is not easy (if you want to do it properly). RapidJSON build
architecture does not seem conducive to subprojects, so it is probably
better to let the developer handle its building/installation.

Note: We should add a notice to builders that they need to install
RapidJSON from source using a version beyond a specific commit. This is
because the streets_service_base uses functionality not found in the
latest release.
Clean up and modernize the unit testing section of the CMakeLists.txt
file. The file globbing was removed, and target includes and links were
made private since there's no need for anything to link against the
unit tests. Additionally, the manual unit test commands were replaced
with the modern alternative gtest_discover_tests

streets_utils/streets_service_base/CMakeListst.txt
* remove source file globbing. There were only three files, so it seemed
  excessive
* make target includes and links private since we're building an
  executable, not a library
* replace add_test with gtest_discover_tests so GTest can automatically
  find unit tests
The streets_service_base project's CMake layout has been broken up and
cleaned up. The top-level CMakeLists.txt file now simply calls
add_subdirectory on the rest of the project. It is more like a table of
contents, a structure recommended in the Professional CMake book.

streets_utils/streets_service_base/CMakeLists.txt
* library building has been moved to src directory
* library install targets have been moved to src directory
* unit testing targets have been moved to test directory
* an option to build unit tests has been added, and unit tests will only
  be built if the project is top level or explicitly enabled. This new
  structure makes the streets_service_base project more amenable to
  being consumed as a subproject.

streets_utils/streets_service_base/cmake/streets_service_base_libConfig.cmake.in
* file has been deleted in place of a fully manually created one. Using
  the CMakePackageConfigHelpers features didn't really make sense in
  this context because the package has a simple structure and dependency
  hierarchy.

streets_utils/streets_service_base/cmake/streets_service_base_libConfig.cmake
* This file was added to replace the one used with
  CMakePackageConfigHelper. It has the same structure.
* add required dependency for RapidJSON
* add optional dependency for GTest

streets_utils/streets_service_base/src/CMakeLists.txt
* move library building and installation to this file. It is included in
  the main build from the top-level CMakeLists.txt file

streets_utils/streets_service_base/test/CMakeLists.txt
* move unit test configuration to this file. It is included in the main
  build from the top-level CMakeLists.txt file
* replace manual add_test command with gtest_discover_tests because it
  is the recommended modern alternative
Instead of hard coding the spdlog log level, it has been moved to a
configuration option. The configuration option defaults to a WARN level.

streets_utils/streets_service_base/src/CMakeLists.txt
* add spdlog logging level configuration option
* set list of allowable log levels to make the configuration option
  appear as a listbox in ccmake/cmake-gui
* set default logging level to WARN instead of TRACE. This is likely the
  desired level for release builds

streets_utils/streets_service_base/test/CMakeLists.txt
* remove spdlog level definition because it is not used directly in the
  test code and it is a compile-time definition for the library code.
  Therefore, there is no way to change it after the library has been
  built.
If the package does not build unit tests, then GTest is not actually
required. However, the CMake configuration previously required GTest in
all instances. Now, requiring GTest is conditioned on tests being built.

streets_utils/streets_service_base/dependencies.cmake
* make GTest being required conditioned on unit tests being built
Remove include directories for unit tests because those header files are
provided by linking the streets_service_base library.

streets_utils/streets_service_base/test/CMakeLists.txt
* remove target_include_directories because those header files are
  included when linking with the streets_service_base_lib library. There
  are no test-specific headers
@paulbourelly999
Copy link
Collaborator

@adamlm currently our docker images are built using ubuntu:bionic-20210702 as a base. I suspect this may be an issue for the version of CMake you are using. You could test this by attempting to build the scheduling_service using docker build -t <image:tag> -f scheduling_service/Dockerfile . from the root directory.

In either case we would probably wait to merge any non-functional PRs like this one until after our up coming release. Super helpful though, will look through the changes in detail to understand a little more.

CARMA Streets uses Ubuntu 18.04 Docker containers, which only have CMake
version 3.10.2 and Boost version 1.65. Therefore, the versions
previously required need to be reverted back so that they are compatible
with the production environment.

streets_utils/streets_service_base/CMakeLists.txt
* revert CMake version to 3.10.2 instead of 3.16

streets_utils/streets_service_base/dependencies.cmake
* remove Boost version requirement. Some minimum version should be
  established in the future.
@adamlm
Copy link
Contributor Author

adamlm commented Oct 18, 2022

I reverted the CMake version back to 3.10.2. As far as I can see, none of the change I made used features introduced in the newer releases.

I successfully built the scheduling_service Docker container, but I did not run anything. Since these changes are only related to the package build process, I doubt they would affect runtime behavior, but let me know if I should run any test programs.

I also built the streets_service_base package by itself using the reverted CMake (3.10.2) and Boost (1.65) versions. All unit tests for the package passed.

I have similar changes for other CARMA Streets packages, specifically those under streets_utils. Do you want me to wait to submit those until after the next release, or should I submit them whenever they're ready? I can also do draft PRs if that is more appropriate.

@paulbourelly999
Copy link
Collaborator

e until after the next release, or should I submit them wheneve

I reverted the CMake version back to 3.10.2. As far as I can see, none of the change I made used features introduced in the newer releases.

I successfully built the scheduling_service Docker container, but I did not run anything. Since these changes are only related to the package build process, I doubt they would affect runtime behavior, but let me know if I should run any test programs.

I also built the streets_service_base package by itself using the reverted CMake (3.10.2) and Boost (1.65) versions. All unit tests for the package passed.

I have similar changes for other CARMA Streets packages, specifically those under streets_utils. Do you want me to wait to submit those until after the next release, or should I submit them whenever they're ready? I can also do draft PRs if that is more appropriate.

You can submit the PRs whenever. I think we will just hold of on merging any PRs that are not related to new release functionality until after our release to avoid any potential bugs/issues resulting from non functional changes. For oversight it might be helpful if we leave PRs like this in draft to avoid any miscommunication about when they will be merged. I do think this is a necessary improvement for our CARMA-Streets packages in general. Another CMake related change that would be nice to have is to install all the streets_utils libraries/packages under a common namespace in CMake so that we could include them more like this instead of including each library as if it were a completely independent dependency. I am still a novice with CMake but I am learning.

find_package( streets_utils COMPONENTS streets_signal_phase_and_timing streets_vehicle_list)

@adamlm
Copy link
Contributor Author

adamlm commented Oct 26, 2022

For oversight it might be helpful if we leave PRs like this in draft to avoid any miscommunication about when they will be merged.

I will make this one a draft until it's ready for review.

Another CMake related change that would be nice to have is to install all the streets_utils libraries/packages under a common namespace in CMake

I was thinking about this too. I think Boost has a good components mechanism implemented. I will give it more though and submit an issue/PR when ready.

@adamlm adamlm marked this pull request as draft October 26, 2022 01:39
@paulbourelly999
Copy link
Collaborator

@adamlm When we get some time it would be nice to revisit these CMake improvements.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Modernize and clean up CMakeLists.txt for streets_service_base
2 participants