PRE-Ninja Bash-Command

Hi

I work on a big Cortex-M project with arm-none-eabi-gcc, cmake and ninja-build.

The current build process is a bash script which consists of 4 parts:

  • Execute pre-cmake-build bash instruction (also here, some files are generated EVERY TIME like git_version_info.h etc)
  • Execute cmake …
  • Execute ninja
  • Execute post-cmake-build bash instructions

Now what i want to achieve is moving part 1 and part 4 into a pre and post build bash script, and remove the other one, so i can make it work with CLion.

Im terribly failing to find a command for cmake that lets me execute a bash script:
A.) every time a build, even if there is no change in coding
B.) that actually PRE_BUILD executes.

add_custom_command does not work, as PRE_BUILD is equal to PRE_LINK, therefore the build fails since header files are missing.

Can someone help me write this cmakelists file so the bash script will actually execute BEFORE cmake generates files and ninja executes the compilation?

ok

1 Like

haha, β€œok” ?? :smiley:

You can make a command run β€œalways” by listing an output that never gets created. Something like this:

add_custom_command(
  OUTPUT
    "${CMAKE_CURRENT_BINARY_DIR}/version.h"
    "${CMAKE_CURRENT_BINARY_DIR}/version.h.noexist"
  COMMAND
    mkversion)
# "Attach" a single target to this command. Directly depending on
# version.h from multiple targets is not supported in Visual Studio.
add_custom_target(version.h
  DEPENDS
    "${CMAKE_CURRENT_BINARY_DIR}/version.h")

# For each direct consumer of the header.
add_library(foo foo.c)
add_dependencies(foo version.h)

But then you can put the command in the version.h target and mark the output file as GENERATED and list the file as BYPRODUCT as well

BYPRODUCTS only works with the Ninja generator.

You trick the Ninja tool with the non-existent output. What’s the point compared to the custom target?

You wanted it to run unconditionally. That makes that happen. Compared to a plain custom target? It’s probably similar, but it’s easier to make chains of commands with add_custom_command, so I just do that then β€œcap” it off with a user-facing custom target.

Okay i got it working as follows:

CMakeLists.txt

cmake_minimum_required(VERSION 3.16)
project(test001)
set(CMAKE_CXX_STANDARD 20)
add_custom_command(
  OUTPUT
    "${PROJECT_SOURCE_DIR}/version.h"
    "${PROJECT_SOURCE_DIR}/version.h.noexist"
  COMMAND
    ${PROJECT_SOURCE_DIR}/version.sh)
add_custom_target(test001_prebuild
  DEPENDS
  "${PROJECT_SOURCE_DIR}/version.h")
add_executable(test001 main.cpp)
add_dependencies(test001 test001_prebuild)

main.cpp

#include <iostream>
#include "version.h"
int main() {
  #ifndef DUMMY_VERSION
    #warning no DUMMY_VERSION
    std::cout << "Hello, World! NOT DEFINED" << std::endl;
  #else
    #warning DUMMY_VERSION
    std::cout << "Hello, World! DEFINED" << std::endl;
  #endif
  return 0;
}

version.sh

#!/bin/env bash
echo ">>>>>>>>>>>>>>> VERSION.SH BEGIN"
echo "#pragma once" >> ../version.h
echo "#define DUMMY_VERSION" >> ../version.h
echo ">>>>>>>>>>>>>>> VERSION.SH END"

tree

.
β”œβ”€β”€ cmake-build-debug
β”œβ”€β”€ CMakeLists.txt
β”œβ”€β”€ main.cpp
β”œβ”€β”€ version.h
└── version.sh

You should never generate files during build in the source directory!