Anthony
Anthony

Reputation: 71

How do I run a python script every time in a CMake build?

Currently I have a need to run a Python script every time in CMake which generates a .qrc file. I can't use Qt Designer and I have to use CMake. set(CMAKE_AUTORCC ON) is being used and fails whenever a resource is added or a name is changed, thus the reason for the python script. The script itself already generates output and everything works after that, so I don't need output from CMake itself. I have currently tried the following:

include(FindPythonInterp)
set (py_cmd "QRC_Updater.py")
execute_process(
                  COMMAND ${PYTHON_EXECUTABLE} ${py_cmd}
                  WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
                  RESULT_VARIABLE py_result
               )
message(STATUS "Python result: ${py_result})

and it works, but it does not execute every time. It only executes when CMakeLists.txt is modified.

So after some searching, people suggested using

add_custom_target(...)

and

add_custom_command(...)

which I have also tried with this:

add_custom_target(
   always_run_target ALL
   DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/__header.h
   )

add_custom_command(
    OUTPUT
        ${CMAKE_CURRENT_BINARY_DIR}/__header.h
        ${CMAKE_CURRENT_BINARY_DIR}/header.h
    COMMAND ${PYTHON_EXECUTABLE} ${py_cmd}
    )

which doesn't run the script at all. I know it's not running the script because I have the file open in Notepad++ and it doesn't ask if I want to switch to the newer version of the file like it does after execute_process() runs. Other variations of add_custom_command() also don't run the script. There are no errors associated with this running except when I don't include ${PYTHON_EXECUTABLE} which results in "%1 is not a valid Win32 command". So how can I effectively get a Python script to run every single time in CMake?

EDIT: The answers here do not work. How to always run command when building regardless of any dependency?

Upvotes: 7

Views: 31155

Answers (2)

Kcvin
Kcvin

Reputation: 5163

I have a solution if you are always building ALL targets, and for individual targets.

To run Python script when building all

I have a separate generate_header.cmake file and added it to the root-level directory of the project, and the file contains the following:

# File: generate_header.cmake
#############################

find_package(PythonInterp)
find_package(Python)

function(generate_headers target_name)
    if (PYTHON_FOUND)
        add_custom_target(${target_name} ALL
            COMMAND 
               ${PYTHON_EXECUTABLE} ${py_cmd}
            OUTPUT 
               ${CMAKE_CURRENT_BINARY_DIR}/__header.h
               ${CMAKE_CURRENT_BINARY_DIR}/header.h  
        )
    endif()
endfunction()

and in the root-level CMakeLists.txt I have added the following:

include(generate_header)

# ... other project stuff

generate_headers(always_run_target)

Now every time I build all, i.e. pass --target all when calling cmake.exe, my script runs.

Note: Building other individual targets does not regenerate the header files.

To run Python script when building individual targets

If you want to generate for individual targets, then you need to call add_custom_command for each target. Note that this will also get called when building all, so it will call the script multiple times if you have multiple targets:

add_custom_command(
    TARGET name_of_your_target
    PRE_BUILD # Call this command pre-build
    COMMAND ${PYTHON_EXECUTABLE} ${py_cmd}
    COMMENT "Generate Headers"
)    

Upvotes: 3

Noki
Noki

Reputation: 411

you need to add a dependency to custom command to inspect changes on OriginalHeader.h and regenerates __header.h and header.h if it changes.

add_executable(MyExe main.cpp ${CMAKE_CURRENT_BINARY_DIR}/__header.h)

add_custom_target(
        always_run_target ALL
        DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/__header.h
)

add_custom_command(
        OUTPUT
            ${CMAKE_CURRENT_BINARY_DIR}/__header.h
            ${CMAKE_CURRENT_BINARY_DIR}/header.h
        DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/OriginalHeader.h
        COMMAND python ${py_cmd}
)

I only changed the ${PYTHON_EXECUTABLE} with python. I was able to run this python script (below) that print, create a directory and generate two files __header.h and header.h every time a change is detected in OriginalHeader.h, or __header.h/header.h don't exists yet.

import os

print("TEST")

if not os.path.exists("TESTDIR"):
    os.makedirs("TESTDIR")
with open("header.h", 'w+'):
    os.utime("header.h", None)

Upvotes: 4

Related Questions