Code View

fson / source / fson-1.1.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 FsonRecipe(ConanFile):
    name = "fson"
    package_type = "shared-library"

    license = "MIT"
    author = "Fehmi Demiralp <demiralp@fedem.eu>"
    url = "https://fson.fedem.eu"
    description = ("FSON — a JSON superset (comments, bare keys, --disabled members) "
                  "with a comment-preserving round-trip C++ model, parser and writer")
    topics = ("fson", "json", "json5", "parser", "config")

    # Topic 135 packaging redesign: manifest.fson generation shared with
    # every other FFS-family package via ffs-packaging-tools — see that
    # repo's README for the API (augment_manifest here; ffs itself, being
    # multi-component, uses augment_component_manifest instead).
    python_requires = "ffs-packaging-tools/[>=1.0 <2]"

    settings = "os", "compiler", "build_type", "arch"
    # libfson is always built SHARED by the CMakeLists; only fPIC is a knob.
    options = {
        "fPIC": [True, False],
        "with_tools": [True, False],
        "with_tests": [True, False],
        # with_docs: also build the CMake `docs` target (Doxygen → the HTML
        # tree at build/<cfg>/docs/html/) during `conan build`. Off by
        # default — the library package never needs it. Requires system
        # Doxygen, and Graphviz `dot` for the class / call / include graphs.
        "with_docs": [True, False],
    }
    default_options = {
        "fPIC": True,
        "with_tools": True,
        "with_tests": True,
        "with_docs": False,
    }

    exports_sources = (
        "CMakeLists.txt",
        "LICENSE",
        "fson.pc.in",
        "cmake/*",
        "apps/*",
        "tools/*",
        "libs/*",
        "tests/*",
        "examples/*",
        # Just the files the `docs` (Doxygen) target needs — it scans
        # libs/ + apps/, not the docs/ markdown or the railroad sources.
        # Present only so `-o with_docs=True` can build; CMakeLists guards
        # add_subdirectory(docs) on docs/CMakeLists.txt existing.
        "docs/CMakeLists.txt",
        "docs/Doxyfile.in",
        "manifest.fson",
    )

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

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

    def requirements(self):
        # cparse was a nested git submodule until the conan migration.
        # Public fson headers include "cparse/Parser.hh", so it is a
        # transitive (visible) requirement.
        self.requires("cparse/[>=1.0 <2]", transitive_headers=True, transitive_libs=True)

    def layout(self):
        cmake_layout(self)

    def generate(self):
        tc = CMakeToolchain(self)
        tc.cache_variables["FSON_BUILD_TOOLS"] = bool(self.options.with_tools)
        tc.cache_variables["TESTING"] = bool(self.options.with_tests)
        tc.cache_variables["EXAMPLE"] = False
        tc.generate()
        CMakeDeps(self).generate()

    def build(self):
        cmake = CMake(self)
        cmake.configure()
        cmake.build()
        if self.options.with_docs:
            # docs/CMakeLists.txt creates the `docs` target only when it is
            # the top-level project and Doxygen is installed.
            cmake.build(target="docs")
            html = os.path.join(self.build_folder, "docs", "html", "index.html")
            self.output.success("Doxygen HTML tree: " + html)
        # CMakeLists.txt silently turns its own TESTING off (and never adds
        # tests/, so no "test" target exists) when GTest isn't found, even
        # with this option True — trusting the option alone makes `cmake
        # --build --target test` fail with "No rule to make target 'test'".
        # CTestTestfile.cmake is only generated when enable_testing() ran,
        # so its presence is the reliable signal that a test target exists.
        ctest_file = os.path.join(self.build_folder, "CTestTestfile.cmake")
        if self.options.with_tests and os.path.exists(ctest_file) and not self.conf.get(
                "tools.build:skip_test", default=False):
            cmake.test()

    def package(self):
        CMake(self).install()
        copy(self, "LICENSE", self.source_folder,
             os.path.join(self.package_folder, "licenses"))
        self.python_requires["ffs-packaging-tools"].module.augment_manifest(self)

    def package_info(self):
        self.cpp_info.libs = ["fson"]
        self.cpp_info.requires = ["cparse::cparse"]
        self.cpp_info.set_property("cmake_file_name", "fson")
        self.cpp_info.set_property("cmake_target_name", "fson::fson")
        self.cpp_info.set_property("pkg_config_name", "fson")