Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to "install" Python code from CMake?

I have a mainly c++ project that I use CMake to manage. After setting cmake_install_prefix and configuring, it generates makefiles which can then be used to build and install with the very standard:

make
make install

At this point, my binaries end up in cmake_install_prefix, and they can be executed with no additional work. Recently I've added some Python scripts to a few places in the source tree, and some of them depend on others. I can use CMake to copy the Python files+directory structure to the cmake_install_prefix, but if I go into that path and try to use one of the scripts, Python cannot find the other scripts used as imports because PYTHONPATH does not contain cmake_install_prefix. I know you can set an environment variable with CMake, but it doesn't persist across shells, so it's not really "setup" for the user for more than the current terminal session.

The solution seems to be to add a step to your software build instructions that says "set your PYTHONPATH". Is there any way to avoid this? Is this the standard practice for "installing" Python scripts as part of a bigger project? It seems to really complicate things like setting up continuous integration for the project, as something like Jenkins has to be manually configured to inject environment variables, whereas nothing special was required for it to build and execute executables built from c++ code.

like image 548
David Doria Avatar asked Jan 19 '17 03:01

David Doria


1 Answers

Python provides sys.path list, which is used for search modules with import directives. You may adjust this list before include your modules:

script1.py:

# Do some things useful for other scripts

script2.py.in:

# Uses script1.py.
...
sys.path.insert(1, "@SCRIPT1_INSTALL_PATH@")
import script1
...

CMakeLists.txt:

...
# Installation path for script1. Depends from CMAKE_INSTALL_PREFIX.
set(SCRIPT1_INSTALL_PATH ${CMAKE_INSTALL_PREFIX}/<...>)

install(FILES script1.py DESTINATION ${SCRIPT1_INSTALL_PATH}

# Configure 'sys.path' in script2.py, so it may find script1.py.
configure_file("script2.py.in" "script2.py" @ONLY)

set(SCRIPT2_INSTALL_PATH ${CMAKE_INSTALL_PREFIX}/<...>)
install(FILES script2.py DESTINATION ${SCRIPT2_INSTALL_PATH}
...

If you want script2.py to work both in build tree and in install tree, you need to have two instances of it, one which works in build tree, and one which works after being installed. Both instances may be configured from single .in file.


In case of compiled executables and libraries, similar mechanism is uses for help binaries to find libraries in non-standard locations. It is known as RPATH.

Because CMake

  • knows every binary created (it tracks add_executable and add_library calls),

  • knows linkage between binaries (target_link_libraries call is also tracked),

  • has full control over linking procedure,

CMake is able to automatically adjust RPATH when install binaries.

In case of Python scripts CMake doesn't have such information, so adjusting linkage path should be performed manually.

like image 174
Tsyvarev Avatar answered Sep 18 '22 15:09

Tsyvarev