Code View

plugin / plugin-2.2.0.0 / conanfile.py
import os

from conan import ConanFile
from conan.tools.cmake import CMake, CMakeDeps, CMakeToolchain, cmake_layout
from conan.tools.files import copy


def cmake_project_version(recipe_folder):
    """The single source of truth for this repo's version: the number on the
    ``project(... VERSION x.y.z[.w] ...)`` line in CMakeLists.txt. Bump it
    there — and only there — and this package's version follows. Set
    ``CI_PROJECT_VERSION`` in the environment to override (for CI that stamps
    its own)."""
    import os
    import re
    text = open(os.path.join(recipe_folder, "CMakeLists.txt"),
                encoding="utf-8").read()
    m = re.search(r"\bproject\s*\((?:[^)]*?\s)?VERSION\s+"
                  r"([0-9]+(?:\.[0-9]+){0,3})\b",
                  text, re.IGNORECASE | re.DOTALL)
    if not m:
        raise Exception("conanfile: no literal 'project(... VERSION ...)' "
                        "found in CMakeLists.txt")
    return os.getenv("CI_PROJECT_VERSION") or m.group(1)


class PluginRecipe(ConanFile):
    name = "plugin"
    # Headline artefact is libdsold.so; plugin-sign ships as a static archive
    # inside the same package.
    package_type = "shared-library"

    license = "MIT"
    author = "Fehmi Demiralp <demiralp@fedem.eu>"
    url = "https://plugin.fedem.eu"
    description = ("DSO plugin toolkit: runtime shared-object loader (dsold) and an "
                  "Ed25519 sign/verify library (plugin-sign) for trusted plugins")
    topics = ("plugin", "dso", "dlopen", "ed25519", "code-signing")

    settings = "os", "compiler", "build_type", "arch"
    # dsold is SHARED, plugin-sign is STATIC — both fixed by the CMakeLists.
    options = {"fPIC": [True, False]}
    default_options = {"fPIC": True}

    exports_sources = (
        "CMakeLists.txt",
        "CMakePresets.json",
        "LICENSE",
        "plugin.pc.in",
        "src/*",
        "apps/*",
        "tools/*",
        "docs/*",
    )

    def set_version(self):
        self.version = self.version or cmake_project_version(self.recipe_folder)

    def requirements(self):
        # OpenSSL is a private implementation detail of plugin-sign (only .cpp
        # includes <openssl/*>), but it propagates as a link requirement to
        # anything that links the static plugin-sign archive.
        self.requires("openssl/[>=3.0 <4]")

    def config_options(self):
        if self.settings.os == "Windows":
            self.options.rm_safe("fPIC")

    def layout(self):
        cmake_layout(self)

    def generate(self):
        tc = CMakeToolchain(self)
        # dso-keygen / dso-sign / dso-verify — always built and packaged so a
        # consumer (FFS) gets them straight from the Conan cache, with no
        # separate "install the tools" step.
        tc.cache_variables["APPS"] = True
        tc.cache_variables["EXAMPLE"] = False
        tc.cache_variables["TESTING"] = False
        tc.generate()
        CMakeDeps(self).generate()

    def build(self):
        cmake = CMake(self)
        cmake.configure()
        cmake.build()

    def package(self):
        CMake(self).install()
        copy(self, "LICENSE", self.source_folder,
             os.path.join(self.package_folder, "licenses"))

    def package_info(self):
        self.cpp_info.set_property("cmake_file_name", "plugin")
        # dso-keygen / dso-sign / dso-verify are packaged in bin/ (the default
        # cpp_info.bindirs). That is what puts them on PATH through a
        # consumer's VirtualRunEnv / VirtualBuildEnv and into
        # <pkg>_BIN_DIRS_<CONFIG> for CMakeDeps — no explicit setting needed.

        # dsold — runtime DSO loader (SHARED). Signed-load API is compiled in
        # because plugin-sign is always built here (OpenSSL is a hard require).
        dso = self.cpp_info.components["dso"]
        dso.libs = ["dsold"]
        dso.system_libs = ["dl"]
        dso.requires = ["sign", "headers"]
        # include/     -> <dso/DSOLoader.hh>, <plugin/...>, <sign/...>
        # include/dso/ -> DSOExceptions.hh's bare `#include <Exception.hh>`
        dso.includedirs = ["include", os.path.join("include", "dso")]
        dso.set_property("cmake_target_name", "plugin::dsold")
        dso.set_property("pkg_config_name", "plugin")

        # plugin-sign — Ed25519 manifest sign/verify (STATIC).
        sign = self.cpp_info.components["sign"]
        sign.libs = ["plugin-sign"]
        sign.requires = ["openssl::openssl"]
        sign.set_property("cmake_target_name", "plugin::plugin-sign")

        # plugin/ — header-only catalog/register API (no compiled artefact).
        headers = self.cpp_info.components["headers"]
        headers.libs = []
        headers.includedirs = ["include"]
        headers.set_property("cmake_target_name", "plugin::plugin")