From 84e9a319bc8cdfcd0e32a077c6ba5e79fbcc74b2 Mon Sep 17 00:00:00 2001 From: Martin Valgur Date: Tue, 3 Oct 2023 13:45:37 +0300 Subject: [PATCH] nanobind: add new recipe --- recipes/nanobind/all/conandata.yml | 4 + recipes/nanobind/all/conanfile.py | 152 ++++++++++++++++++ .../nanobind/all/test_package/CMakeLists.txt | 10 ++ .../nanobind/all/test_package/conanfile.py | 35 ++++ .../all/test_package/test_package.cpp | 8 + recipes/nanobind/config.yml | 3 + 6 files changed, 212 insertions(+) create mode 100644 recipes/nanobind/all/conandata.yml create mode 100644 recipes/nanobind/all/conanfile.py create mode 100644 recipes/nanobind/all/test_package/CMakeLists.txt create mode 100644 recipes/nanobind/all/test_package/conanfile.py create mode 100644 recipes/nanobind/all/test_package/test_package.cpp create mode 100644 recipes/nanobind/config.yml diff --git a/recipes/nanobind/all/conandata.yml b/recipes/nanobind/all/conandata.yml new file mode 100644 index 00000000000000..555cdff3409da3 --- /dev/null +++ b/recipes/nanobind/all/conandata.yml @@ -0,0 +1,4 @@ +sources: + "1.6.1": + url: "https://github.com/wjakob/nanobind/archive/refs/tags/v1.6.1.tar.gz" + sha256: "6acf2cfef781b8b7d82a8c298c1bfe7ccb4dd0de06626b69ed1c99a71a93e78d" diff --git a/recipes/nanobind/all/conanfile.py b/recipes/nanobind/all/conanfile.py new file mode 100644 index 00000000000000..ee86b81bd3adee --- /dev/null +++ b/recipes/nanobind/all/conanfile.py @@ -0,0 +1,152 @@ +import os + +from conan import ConanFile +from conan.errors import ConanInvalidConfiguration +from conan.tools.build import check_min_cppstd +from conan.tools.cmake import CMake, CMakeToolchain, cmake_layout, CMakeDeps +from conan.tools.files import get, copy, rm, replace_in_file, save, rename +from conan.tools.scm import Version + +required_conan_version = ">=1.53.0" + + +class PackageConan(ConanFile): + name = "nanobind" + description = "Tiny and efficient C++/Python bindings" + license = "BSD-3-Clause" + url = "https://github.com/conan-io/conan-center-index" + homepage = "https://github.com/wjakob/nanobind" + topics = ("python", "bindings", "pybind11") + + package_type = "library" + settings = "os", "arch", "compiler", "build_type" + options = { + "shared": [True, False], + "fPIC": [True, False], + "stable_abi": [True, False], + "domain": [None, "ANY"], + } + default_options = { + "shared": False, + "fPIC": True, + "stable_abi": False, # FIXME: should be True by default + "domain": None, + } + + @property + def _min_cppstd(self): + return 17 + + @property + def _compilers_minimum_version(self): + return { + "gcc": "7", + "clang": "5", + "apple-clang": "10", + "msvc": "192", + "Visual Studio": "15", + } + + def config_options(self): + if self.settings.os == "Windows": + del self.options.fPIC + + def configure(self): + if self.options.shared: + self.options.rm_safe("fPIC") + + def layout(self): + cmake_layout(self, src_folder="src") + + def requirements(self): + self.requires("tsl-robin-map/1.2.1") + # FIXME: add cpython dependency + # self.requires("cpython/3.12.0") + + def validate(self): + if self.settings.compiler.cppstd: + check_min_cppstd(self, self._min_cppstd) + minimum_version = self._compilers_minimum_version.get(str(self.settings.compiler), False) + if minimum_version and Version(self.settings.compiler.version) < minimum_version: + raise ConanInvalidConfiguration( + f"{self.ref} requires C++{self._min_cppstd}, which your compiler does not support." + ) + # FIXME: need to check that we are using CPython 3.12+ for self.options.stable_abi + + def source(self): + get(self, **self.conan_data["sources"][self.version], strip_root=True) + + def generate(self): + tc = CMakeToolchain(self) + tc.cache_variables["NB_TEST"] = False + tc.generate() + deps = CMakeDeps(self) + deps.generate() + + @property + def _libname(self): + libname = "nanobind" + if not self.options.shared: + libname += "-static" + if self.options.stable_abi: + libname += "-abi3" + if self.options.domain and self.options.shared: + libname += f"-{self.options.domain}" + return libname + + def build(self): + self._patch_sources() + cmake = CMake(self) + cmake.configure() + cmake.build(target=self._libname) + + def _patch_sources(self): + cmakelists = os.path.join(self.source_folder, "CMakeLists.txt") + nb_config = os.path.join(self.source_folder, "cmake", "nanobind-config.cmake") + # tsl-robin-map has been unvendored, skip a check for its presence + replace_in_file(self, cmakelists, "if (NOT IS_DIRECTORY", "if (FALSE AND NOT IS_DIRECTORY") + # Force building of nanobind lib + save(self, cmakelists, f"\nnanobind_build_library({self._libname})", append=True) + # Link against tsl-robin-map and install the lib and headers + replace_in_file( + self, + nb_config, + "target_include_directories(${TARGET_NAME} PRIVATE\n ${NB_DIR}/ext/robin_map/include)\n", + ( + "find_package(tsl-robin-map REQUIRED CONFIG)\n" + "target_link_libraries(${TARGET_NAME} PRIVATE tsl::robin_map)\n" + "include(GNUInstallDirs)\n" + "install(TARGETS ${TARGET_NAME})\n" + "install(DIRECTORY ${NB_DIR}/include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})\n" + ), + ) + # No need to build the lib inside nanobind_add_module() call + replace_in_file(self, nb_config, "nanobind_build_library(${libname})\n", "") + # Link against previously built nanobind and Python + # FIXME: the CPython include dirs should be added by Conan automatically + replace_in_file( + self, + nb_config, + "target_link_libraries(${name} PRIVATE ${libname})\n", + ( + "target_link_libraries(${name} PRIVATE nanobind::nanobind)\n" + "target_include_directories(${name} PUBLIC ${Python_INCLUDE_DIRS})\n" + ), + ) + + def package(self): + copy(self, "LICENSE", dst=os.path.join(self.package_folder, "licenses"), src=self.source_folder) + cmake = CMake(self) + cmake.install() + copy(self, "nanobind-config.cmake", + src=os.path.join(self.source_folder, "cmake"), + dst=os.path.join(self.package_folder, "lib", "cmake")) + rename(self, + os.path.join(self.package_folder, "lib", "cmake", "nanobind-config.cmake"), + os.path.join(self.package_folder, "lib", "cmake", "nanobind-conan-config.cmake")) + rm(self, "*.pdb", self.package_folder, recursive=True) + + def package_info(self): + self.cpp_info.libs = [self._libname] + self.cpp_info.builddirs = [os.path.join(self.package_folder, "lib", "cmake")] + self.cpp_info.set_property("cmake_build_modules", [os.path.join("lib", "cmake", "nanobind-conan-config.cmake")]) diff --git a/recipes/nanobind/all/test_package/CMakeLists.txt b/recipes/nanobind/all/test_package/CMakeLists.txt new file mode 100644 index 00000000000000..850e51b926ba07 --- /dev/null +++ b/recipes/nanobind/all/test_package/CMakeLists.txt @@ -0,0 +1,10 @@ +cmake_minimum_required(VERSION 3.15) +project(test_package CXX) + +find_package(Python 3.8 + REQUIRED COMPONENTS Interpreter Development.Module + OPTIONAL_COMPONENTS Development.SABIModule) + +find_package(nanobind CONFIG REQUIRED) + +nanobind_add_module(test_package STABLE_ABI NB_STATIC test_package.cpp) diff --git a/recipes/nanobind/all/test_package/conanfile.py b/recipes/nanobind/all/test_package/conanfile.py new file mode 100644 index 00000000000000..da3d283bee2674 --- /dev/null +++ b/recipes/nanobind/all/test_package/conanfile.py @@ -0,0 +1,35 @@ +import importlib +import sys + +from conan import ConanFile +from conan.tools.build import can_run +from conan.tools.cmake import cmake_layout, CMake + + +class TestPackageConan(ConanFile): + settings = "os", "arch", "compiler", "build_type" + generators = "CMakeDeps", "CMakeToolchain", "VirtualRunEnv" + test_type = "explicit" + + def requirements(self): + self.requires(self.tested_reference_str) + + def layout(self): + cmake_layout(self) + + def build(self): + cmake = CMake(self) + cmake.configure() + cmake.build() + + def _test_nanobind_module(self): + sys.path.append(self.build_folder) + # Cannot use 'import test_package' due to pylint + test_package = importlib.import_module("test_package") + assert test_package.add(2, 3) == 5 + self.output.info("test_package.add(2, 3) ran successfully") + sys.path.pop() + + def test(self): + if can_run(self): + self._test_nanobind_module() diff --git a/recipes/nanobind/all/test_package/test_package.cpp b/recipes/nanobind/all/test_package/test_package.cpp new file mode 100644 index 00000000000000..83b94735082a0c --- /dev/null +++ b/recipes/nanobind/all/test_package/test_package.cpp @@ -0,0 +1,8 @@ +#include + +namespace nb = nanobind; +using namespace nb::literals; + +NB_MODULE(test_package, m) { + m.def("add", [](int a, int b) { return a + b; }, "a"_a, "b"_a); +} diff --git a/recipes/nanobind/config.yml b/recipes/nanobind/config.yml new file mode 100644 index 00000000000000..bd3f43d241a52a --- /dev/null +++ b/recipes/nanobind/config.yml @@ -0,0 +1,3 @@ +versions: + "1.6.1": + folder: all