'Generate script during build with cmake

I have a cmake project with a single source file called main.c. I want to additionally provide a wrapper script which calls main with specific parameters.

My CMakeLists.txt looks as follows:

cmake_minimum_required(VERSION 3.1...3.16)

file(WRITE  ${CMAKE_BINARY_DIR}/wrapper  "#!/usr/bin/env bash\n")
file(APPEND ${CMAKE_BINARY_DIR}/wrapper "./main options\n")

add_executable(main main.c)

add_custom_target(wrapper_target
    ALL DEPENDS wrapper)
add_custom_target(main_target
    ALL DEPENDS main wrapper_target)

add_dependencies(main wrapper_target)

install(
    TARGETS main
    RUNTIME DESTINATION bin/)
install(
    PROGRAMS wrapper
    DESTINATION bin/)

If I run cmake --install ., the script wrapper is installed together with the binary main. Running cmake --build . produces the script wrapper, but it is not marked as executable (on Linux).

How can I tell cmake to also generate wrapper during build and mark it as executable?

Note: I need this for an automated build system which runs build and not install, and expects a specific file to be available on build.



Solution 1:[1]

Try:

file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/wrapper.tmp
"#!/usr/bin/env bash
# Note that './main' is relative from whatever directory you are in
# Use just main assuming the install prefix is in your bath
# Or use $<TARGET_FILE:main> 
# Or maybe ${CMAKE_INSTALL_PREFIX}/bin/main
./main options
")

# add execute permissions
file(
    COPY ${CMAKE_CURRENT_BINARY_DIR}/wrapper.tmp
    DESTINATION ${CMAKE_CURRENT_BINARY_DIR}
    FILE_PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE
)

# Rename the file
file(RENAME 
    ${CMAKE_CURRENT_BINARY_DIR}/wrapper.tmp
    ${CMAKE_CURRENT_BINARY_DIR}/wrapper
)

How can I tell cmake to also generate wrapper during build and mark it as executable?

file( is a in a cmake script - it is executed during configuration phase, when cmake is executed. To generate the file during build use add_custom_command, the most "portable" way in cmake sense would be to run a cmake script inside add_custom_command:

add_custom_command
     OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/wrapper
     COMMAND $(CMAKE_COMMAND)
          -D CMAKE_CURRENT_BINARY_DIR=${CMAKE_CURRENT_BINARY_DIR}
          -P ${CMAKE_CURRENT_SOURCE_DIR}/the_cmake_script.cmake 
     DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/the_cmake_script.cmake 
     COMMAND Generating wrapper script...
     VERBATIM
 )

then inside the_cmake_script.cmake you could do the script above - add_custom_command will execute the command cmake -P <the script> during build of you project. That way you can DEPEND properly on the wrapper script.

Solution 2:[2]

CMake 3.20 added support for FILE_PERMISSIONS attribute to the FILE command. So one could simply:

FILE(GENERATE OUTPUT ${CMAKE_BINARY_DIR}/wrapper
     CONTENT 
"#!/usr/bin/env bash
./main options
"
     FILE_PERMISSIONS OWNER_EXECUTE OWNER_WRITE OWNER_READ)

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 lets_go_surfing