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")