Skip to content

Potential CMake build recipe refresh? #789

@halleysfifthinc

Description

@halleysfifthinc

I have a CMake project which depends on Simbody using the FetchContent module, which adds Simbody as a subproject.

I had some difficulties with that approach that led me to look at the Simbody CMake build system.

While I was familiarizing myself with the Simbody CMake, I noticed a number of things that can be natively done by (recent) CMake's, some outdated/deprecated CMake commands, and a number of configuration options whose purpose/usecase I don't understand and/or don't appear to be used anymore. So in the spirit of not surprising y'all with a giant CMake rewrite PR, here is a list of comments and questions about the CMake build recipe:

  • Playing nice as a subproject:

    • Bump Allow building simbody as cmake subproject #721

      • The CMAKE_SOURCE_DIR => CMAKE_CURRENT_SOURCE_DIR replacement only affects how simbody is built when it is a subproject (i.e. no functional change for regular builds). When Simbody is a subproject, CMAKE_SOURCE_DIR is the directory of the top-level project, and paths in simbody that assume CMAKE_SOURCE_DIR is the simbody source directory will be incorrect.
    • CACHE'ed variables are global

      • The CMakeCache is global to a CMake project, so subprojects setting CACHE variables can overwrite settings in the top-level project. Overwriting can happen by either force setting, or setting CACHE variables before parent projects if they don't FORCE set. For example, the INSTALL_DOCS, BUILD_EXAMPLES, and BUILD_TESTING1 variables (all fairly common in software built with CMake) conflict with variables of the same name in opensim-core
      • Solution:
        • Best practice is that most CACHE'ed variables in a project should be prefixed with the project name, e.g. SIMBODY_INSTALL_DOCS etc.
        • Check if the project is top level (requires CMake >=3.21) when setting un-prefixed cache variables
          • Reconsider default values for variables when Simbody is a dependency/subproject (aka not the top-level project), e.g. if Simbody is a dependency, Simbody's tests probably shouldn't be built
  • Purpose and maintenance cost of simultaneous shared and static builds, simultaneous versioned and unversioned builds

    • There are multiple untested (by CI) configuration options, and the target definitions for the static/shared, versioned/unversioned targets are mostly the same, with only minor differences. What are the intended use cases for these options and who are the users?
      • Versioning:
        • What is the functional/intended difference between the version suffix and builtin library versions (e.g. SOVERSION, etc)?
        • Why/when/who would need to generate (from the same build config) versioned and unversioned libraries?
      • How often are these simultaneous builds needed, and are separate builds (i.e. create separate build directories for each option) inconvenient enough to be worth the added complexity/configuration duplication?
        • There is no build de-duplication afaik, so no advantage to multiple targets with the same sources within the same build directory/configuration (but c.f. ccache)
    • Solutions: remove or rewrite to reduce duplication:
      • Remove simultaneous static/shared builds and add examples of the functional equivalent separate builds to CONTRIBUTING.md, CMakePresets.josn and/or SampleCMakeLists.txt
      • Keep simultaneous builds but rewrite to simplify (I have ideas [as of yet, untested])
      • Versioning suffixes can be added more idiomatically using <$CONFIG>_POSTFIX and potentially other options
      • Add better docstrings to configurations, with examples of why they might be used
  • Accessibility to newcomers

    • There are 7 CACHE variables2 shown in the GUI that "can't be changed here/from the GUI". None of these are relevant to a new/potential contributor (imo), and should be hidden (marked "INTERNAL" in CMakeLists.txt) to reduce visual noise and mental load for new contributors.
      • If the intent is to inform users what values these variables are being set to, messages can be printed when cmake is configured
    • Another 7 CACHE variables3 are only changed (from defaults) in special, advanced situations, and should be marked as such in CMake (i.e. mark_as_advanced), again to reduce visual noise and mental load
  • CMake style/idioms. I'm not sure if some of these things are intentional stylistic choices vs. leftover from earlier versions of CMake

    • Text inside else and endif parentheses is ignored/unused by CMake.
      • Adds visual noise with no beneficial impacts on clarity, imo
    • Manual SIMBODY_(MAJOR|MINOR|PATCH)_VERSION declaration versus defining in project declaration and using CMAKE_PROJECT_VERSION_(MAJOR|MINOR|PATCH) everywhere else (same for subprojects)
      • Defining version in the project declaration automatically defines CMAKE_PROJECT_VERSION_(MAJOR|MINOR|PATCH) variables
    • Provide common alternative build configs with CMakePresets.json (requires CMake >= 3.19)
      • Define variants for supported, but less common builds (e.g. static, versioned, namespaced, etc)
      • Define a standard/multi-platform CI build config

If it helps, I can break these questions/concerns out into separate issues. Looking forward to discussing with y'all and hopefully submitting some PR's!

Footnotes

  1. BUILD_TESTING is somewhat special because it is checked/set by the Dart/CTest module.

  2. BUILD_PLATFORM, LAPACK_BEING_USED, SIMBODY_SONAME_VERSION, SIMBODY_VERSION, SimTKCOMMON_LIBRARY_NAME, SimTKMATH_LIBRARY_NAME, SimTKSIMBODY_LIBRARY_NAME

  3. BUILD_STATIC_LIBRARIES, BUILD_UNVERSIONED_LIBRARIES, BUILD_USING_NAMESPACE, BUILD_USING_OTHER_LAPACK, BUILD_VERSIONED_LIBRARIES, SIMBODY_COVERAGE, WINDOWS_USE_EXTERNAL_LIBS

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions