:::{default-domain} bzl :::

How to link to libpython

This guide explains how to use the Python toolchain to get the linker flags required for linking against libpython. This is often necessary when embedding Python in a C/C++ application.

Currently, the :current_py_cc_libs target does not include -lpython et al linker flags. This is intentional because it forces dynamic linking (via the dynamic linker processing DT_NEEDED entries), which prevents users who want to load it in some more custom way.

Exposing linker flags in a rule

You can create a rule that gets the Python version from the toolchain and constructs the correct linker flag. This rule can then provide the flag to other C/C++ rules via the CcInfo provider.

Here's an example of a rule that creates the -lpython<version> flag:

# python_libs.bzl
load("@bazel_tools//tools/cpp:toolchain_utils.bzl", "cc_common")

def _python_libs_impl(ctx):
    toolchain = ctx.toolchains["@rules_python//python:toolchain_type"]
    info = toolchain.py3_runtime.interpreter_version_info
    link_flag = "-lpython{}.{}".format(info.major, info.minor)

    cc_info = CcInfo(
        linking_context = cc_common.create_linking_context(
            user_link_flags = [link_flag],
        ),
    )
    return [cc_info]

python_libs = rule(
    implementation = _python_libs_impl,
    toolchains = ["@rules_python//python:toolchain_type"],
)

Using the rule

In your BUILD.bazel file, define a target using this rule and add it to the deps of your cc_binary or cc_library.

# BUILD.bazel
load(":python_libs.bzl", "python_libs")

python_libs(
    name = "py_libs",
)

cc_binary(
    name = "my_app",
    srcs = ["my_app.c"],
    deps = [
        ":py_libs",
        # Other dependencies
    ],
)