Skip to content

Core install rules use a hardcoded absolute DESTINATION /usr/local and are not gated by BENCHMARK_ENABLE_INSTALL. #51

Description

@jenskeiner

Summary

I noticed this issue while embedding the benchmarks library in a project.

When a project embeds via FetchContent, running cmake --install on the parent project fails or silently writes outside its prefix because core/CMakeLists.txt installs headers to a hardcoded absolute path /usr/local instead of a prefix-relative CMAKE_INSTALL_* destination.

Also, the two install() rules are not guarded by BENCHMARK_ENABLE_INSTALL, so an embedding project that sets BENCHMARK_ENABLE_INSTALL=OFF still cannot opt out.

Environment

  • codspeed-cpp v2.3.0
  • CMake 3.28.3, Linux.

Reproduction

To reproduce, create a test parent project, consisting of two files, that embeds the library.

CMakeLists.txt:

make_minimum_required(VERSION 3.20)
project(codspeed_install_repro LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)

include(FetchContent)

# Embedder turns these OFF, as the docs suggest for projects embedding benchmark.
set(BENCHMARK_ENABLE_TESTING OFF CACHE BOOL "" FORCE)
set(BENCHMARK_ENABLE_INSTALL OFF CACHE BOOL "" FORCE)

FetchContent_Declare(codspeed
  GIT_REPOSITORY https://github.com/CodSpeedHQ/codspeed-cpp
  GIT_TAG        v2.3.0
  GIT_SUBMODULES_RECURSE TRUE
  SOURCE_SUBDIR  google_benchmark)
FetchContent_MakeAvailable(codspeed)

add_executable(bench bench.cpp)
target_link_libraries(bench PRIVATE benchmark::benchmark)

bench.cpp:

#include <benchmark/benchmark.h>
static void BM_Noop(benchmark::State& state) {
  for (auto _ : state) benchmark::DoNotOptimize(0);
} 
BENCHMARK(BM_Noop);
BENCHMARK_MAIN();

Then run:

cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
cmake --install build --prefix "$PWD/_install"

The install errors with Operation not permitted on any environment where /usr/local isn't writable. Where it is writable, it silently pollutes /usr/local instead of the requested prefix.

Expected: An embedded benchmark contributes no install rules when BENCHMARK_ENABLE_INSTALL=OFF. When it does install, it honors CMAKE_INSTALL_PREFIX/--prefix.

Suggested Fix

  1. Use a prefix-relative destination via GNUInstallDirs:
include(GNUInstallDirs)
install(
    DIRECTORY "${PROJECT_SOURCE_DIR}/include/" "${PROJECT_BINARY_DIR}/include/"
    DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
    FILES_MATCHING PATTERN "*.h" PATTERN "*.hpp"
)
  1. Guard both install() blocks behind the existing BENCHMARK_ENABLE_INSTALL option so embedders can opt out:
if(BENCHMARK_ENABLE_INSTALL)
  install(DIRECTORY ... )
  install(TARGETS codspeed instrument_hooks EXPORT codspeed-targets ... )
endif()

As a workaround, one can add EXCLUDE_FROM_ALL to FetchContent_Declare(...) in a project that embeds the library, so the sub-project's install rules don't participate in the parent's install.

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