Building a Python Project With Meson

I have already shown how to build a Vala program with Meson. In this post, I will show how to do the same for Python. You don’t really need to compile an interpreted language but Meson helps you to put the files in their correct installation directory.

Let’s say we have a program, called hello.py that we want to install using Meson. The first thing that we need to do is to make it executable without the python keyword. We can do it using something called shebang. Shebang is a character sequence on the first line of the file that tells the computer how to run the script. It starts with a #! and everything after that is the interpreter path.

#!/usr/bin/python3

if __name__ == '__main__':
    print('Hello World')

Now if we will set the executable bit on (using chmod +x hello.py) we will be able to run the file like elf (executable) files. But the file is not in the path yet and we don’t want to do it manually every time we change something so for that we will use Meson.

Like with our Vala example, we will first declare our project using the project function. After that, we will copy the script using the install_data function to the binary directory (called bindir in Meson).

project('hello',
        version: '1.0.0',
        meson_version: '>= 0.50.0',
)

install_data('hello.py', install_dir: get_option('bindir'))

Assuming your executable file is called hello.py and your build file is called meson.build you can put the script in the path by building and installing the project:

meson build
sudo ninja -C build install

That was easy, but let’s make it more portable by replacing our hardcoded python path with the correct python path on the system. We can use /usr/bin/env to do it but I want to use this opportunity to show you how to use configuration variables with Meson. To do it we will initialize a new configuration dictionary with the configuration_data function. To add a new value to the dictionary we will call the set method on the configuration dictionary with the key and the value.

To get the path of Python 3 on our system we can use the find_python function from the Python 3 module, just don’t forget to import it first.

Now to use the configuration data we will use the configure_file function instead of the install_data function. We will pass it the input file name, output file name, configuration, and install path. The result should look like this:

project('hello',
        version: '1.0.0',
        meson_version: '>= 0.50.0',
)

python3 = import('python3')
conf = configuration_data()
conf.set('PYTHON', python3.find_python().path())

configure_file(
  input: 'hello.in',
  output: 'hello',
  configuration: conf,
  install: true,
  install_dir: get_option('bindir')
)

Now we just need to add the PYTHON parameter in our code surrounded by @ and it will be replaced by the actual path. This is our final Python script:

#!@PYTHON@

if __name__ == '__main__':
    print('Hello World')

That’s it, now you have a Python script as the command hello.