How can I create a python3 venv in cmake install?

Question:

I have a medium sized project composed by many parts, mostly in C++, but testing and configuration relies on Python3 scripts.

The project buildsystem is generated using CMake and installed (by CMake rules) in a "deploy" directory.

I would like to create a Python venv to segregate changes.
I have the following CMake fragment:

set(VENV ${CMAKE_INSTALL_PREFIX}/venv)
set(REQUIREMENTS ${CMAKE_SOURCE_DIR}/tools/testing/scripts/requirements.txt)
set(BIN_DIR ${VENV}/bin)
set(PYTHON ${BIN_DIR}/python)
set(OUTPUT_FILE ${VENV}/environment.txt)
add_custom_command(
    OUTPUT ${OUTPUT_FILE}
    COMMAND ${Python3_EXECUTABLE} -m venv ${VENV}
    COMMAND ${BIN_DIR}/pip install -U pip wheel
    COMMAND ${BIN_DIR}/pip install -r ${REQUIREMENTS}
    COMMAND ${BIN_DIR}/pip freeze > ${VENV}/environment.txt
    DEPENDS ${REQUIREMENTS}
)
add_custom_target(venv DEPENDS ${PYTHON})

… but I don’t know how to trigger the venv target while doing installation.

I am unsure if this is the right approach (comments welcome, of course), but need should be quite clear: I need to be able to run my scripts from the "deploy" directory using something like:

venv/bin/python test-script.py

or use a custom: #!venv/bin/python line (I am working under Linux).

UPDATE:

My somewhat working code, based on @user Answer, is as follows:

find_package(Python3 REQUIRED COMPONENTS Interpreter)
set(VENV "${CMAKE_INSTALL_PREFIX}/venv")
set(REQUIREMENTS "${CMAKE_SOURCE_DIR}/tools/testing/scripts/requirements.txt")
set(BIN_DIR "${VENV}/bin")
install(CODE "
      MESSAGE("Creating VENV from ${Python3_EXECUTABLE} to ${VENV}")
      execute_process(COMMAND_ECHO STDOUT COMMAND ${Python3_EXECUTABLE} -m venv ${VENV} )
      execute_process(COMMAND_ECHO STDOUT COMMAND ${BIN_DIR}/pip install -U pip wheel )
      execute_process(COMMAND_ECHO STDOUT COMMAND ${BIN_DIR}/pip install -r ${REQUIREMENTS} )
")

Equivalent code with set(... and find_package(... inside install(CODE... did not work; also quoting seems wrong.

I will accept this answer, but I would like to clarify the above.

Asked By: ZioByte

||

Answers:

You should be able to do this using install(SCRIPT) or install(CODE):

The SCRIPT form will invoke the given CMake script files during installation. If the script file name is a relative path it will be interpreted with respect to the current source directory. The CODE form will invoke the given CMake code during installation. Code is specified as a single argument inside a double-quoted string.

I believe it would be done something like this (using the CODE form here to make it easier to do variable substitution and use some of the variables that wouldn’t be available outside the context of the configuration phase (i.e. if you were to use the SCRIPT mode) without explicitly passing them):

install(CODE "
    set(VENV "${CMAKE_INSTALL_PREFIX}/venv")
    set(REQUIREMENTS "${CMAKE_SOURCE_DIR}/tools/testing/scripts/requirements.txt")
    set(BIN_DIR "${VENV}/bin")
    set(OUTPUT_FILE "${VENV}/environment.txt")
    execute_process(COMMAND "${Python3_EXECUTABLE}" -m venv "${VENV}")
    execute_process(COMMAND "${BIN_DIR}/pip" install -U pip wheel)
    execute_process(COMMAND "${BIN_DIR}/pip" install -r "${REQUIREMENTS}")
    execute_process(COMMAND "${BIN_DIR}/pip" freeze > "${VENV}/environment.txt")
    execute_process(COMMAND "${REQUIREMENTS}")
")

See also the docs for execute_process if you want to do things like handle command failures, etc.

Answered By: user
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.