How to call a bash script from cmake and pass a generator dependent string as argument?

Viewed 19

While using the Unix Makefiles generator I have added the following to a CMakeLists.txt file:

add_custom_target(maintainer-clean
  # The current directory is CMAKE_CURRENT_BINARY_DIR.
  COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/cwm4/scripts/cmake_maintainer_clean.sh $(MAKE) \"${GITACHE_PACKAGES}\"
)

This cmake_maintainer_clean.sh script is make specific, and it needs to use $(MAKE) in the generated Makefile when calling the script.

However, when switching to the generator Ninja this custom command is put as-is in the build.ninja file, causing the $ of the $(MAKE) to cause problems (ninja refuses to run any target, failing to parse build.ninja).

Therefore, I wish to make this generator-specific. How can I use $(MAKE) as first argument to the script when the generator is Unix Makefiles and something else, without a $ - e.g. "ninja" - when the generator is Ninja?

Can I do something like:

COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/cwm4/scripts/cmake_maintainer_clean.sh $<UNIX:$(MAKE),ninja> \"${GITACHE_PACKAGES}\"

?

1 Answers

I would make separate presets for each generator, so you can associate a particular environment variable with it. Here is how the CMakePresets.json file may look like:

{
  "version": 2,
  "cmakeMinimumRequired": {
    "major": 3,
    "minor": 20,
    "patch": 0
  },
  "configurePresets": [
    {
      "name": "base",
      "binaryDir": "${sourceDir}/build",
      "hidden": true
    },
    {
      "name": "Ninja",
      "inherits": "base",
      "displayName": "Ninja Config",
      "generator": "Ninja",
      "environment": {
        "SCRIPT_ARG": "Ninja"
      }
    },
    {
      "name": "Make",
      "inherits": "base",
      "displayName": "Make Config",
      "generator": "Unix Makefiles",
      "environment": {
        "SCRIPT_ARG": "Make"
      }
    }
  ]
}

Where the SCRIPT_ARG can be read later inside of the CMakeLists.txt configuration:

cmake_minimum_required(VERSION 3.20)
project(Hello)
add_executable(Hello main.cpp)

add_custom_target(my-script
    COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/script.sh $ENV{SCRIPT_ARG}
)

add_dependencies(Hello my-script)

Providing script.sh is as simple as this:

#!/bin/sh
echo "Hello from script, $1!"

You will end up with the following output during build phase:

% cmake --preset Make
...
% cmake --build build
Hello from script, Make!
[  0%] Built target my-script
...

Corresponding output for Ninja generator would be:

% cmake --preset Ninja
...
% cmake --build build
Hello from script, Ninja!
[  0%] Built target my-script
...

If you need something more complex than an environment variable, you can introduce condition statement based on value of CMAKE_GENERATOR:

cmake_minimum_required(VERSION 3.20)
project(Hello)
add_executable(Hello main.cpp)

if(CMAKE_GENERATOR STREQUAL Ninja)
  add_custom_target(my-script COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/script.sh $ENV{SCRIPT_ARG})
elseif(CMAKE_GENERATOR STREQUAL "Unix Makefiles")
  add_custom_target(my-script COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/another_script.sh)
endif()


add_dependencies(Hello my-script)

The provided solutions work at build step, and for the part whether it's possible to distinguish between generators during build system generation phase (e.g. with use of generator expressions) i don't think it's possible, because the configuration is generator-agnostic at this point.

Related