From 4a3f15d24d46f6906779a1adf4e1dfbac57e888f Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Thu, 5 Aug 2021 15:44:21 +0200 Subject: [PATCH] Added Conan support - Added conanfile.py - Set C++17 standard - Added .gitignore (always handy) - Changed behaviour of finding clipper It will first try to find polyclipping the target name used by Conan and polyclipping itself. if that fails. It will default back to the original behaviour of the require_package module which looks for the target Clipper::Clipper. This target is then linked to an interface library polyclipping::polyclipping allowing it to link by both use cases. --- .gitignore | 123 ++++++++++++++ CMakeLists.txt | 6 +- conanfile.py | 150 ++++++++++++++++++ .../libnest2d/backends/clipper/CMakeLists.txt | 11 +- .../backends/clipper/clipper_polygon.hpp | 2 +- test_package/CMakeLists.txt | 10 ++ test_package/conanfile.py | 35 ++++ test_package/main.cpp | 6 + 8 files changed, 338 insertions(+), 5 deletions(-) create mode 100644 .gitignore create mode 100644 conanfile.py create mode 100644 test_package/CMakeLists.txt create mode 100644 test_package/conanfile.py create mode 100644 test_package/main.cpp diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e5af4d8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,123 @@ +### C++ template +# Prerequisites +*.d + +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod +*.smod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app + +### CMake template +CMakeLists.txt.user +CMakeCache.txt +CMakeFiles +CMakeScripts +Testing +Makefile +cmake_install.cmake +install_manifest.txt +compile_commands.json +CTestTestfile.cmake +_deps + +### JetBrains template +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +.idea/ +tmp/* +test_package/build/* \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 82bde89..c778375 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,8 +2,10 @@ cmake_minimum_required(VERSION 3.1) project(Libnest2D) -set(CMAKE_CXX_STANDARD 11) -set(CMAKE_CXX_STANDARD_REQUIRED) +# Use C++17 Standard +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_EXTENSIONS OFF) +set(CMAKE_CXX_STANDARD_REQUIRED ON) # Add our own cmake module path. list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake_modules/) diff --git a/conanfile.py b/conanfile.py new file mode 100644 index 0000000..40b9d37 --- /dev/null +++ b/conanfile.py @@ -0,0 +1,150 @@ +from conans import ConanFile, tools +from conan.tools.cmake import CMakeToolchain, CMakeDeps, CMake + +class libnest2dConan(ConanFile): + name = "libnest2d" + version = "4.10.0" + license = "LGPL-3.0" + author = "Ultimaker B.V." + url = "https://github.com/Ultimaker/libnest2d" + description = "2D irregular bin packaging and nesting library written in modern C++" + topics = ("conan", "cura", "prusaslicer", "nesting", "c++", "bin packaging") + settings = "os", "compiler", "build_type", "arch" + exports = "LICENSE.txt" + options = { + "shared": [True, False], + "fPIC": [True, False], + "tests": [True, False], + "header_only": [True, False], + "geometries": ["clipper", "boost", "eigen"], + "optimizer": ["nlopt", "optimlib"], + "threading": ["std", "tbb", "omp", "none"], + "python_version": "ANY" + } + default_options = { + "shared": True, + "tests": False, + "fPIC": True, + "header_only": False, + "geometries": "clipper", + "optimizer": "nlopt", + "threading": "std", + "python_version": "3.9" + } + scm = { + "type": "git", + "subfolder": ".", + "url": "auto", + "revision": "auto" + } + + # TODO: Move lib naming logic to python_requires project + _ext = None + + @property + def ext(self): + if self._ext: + return self._ext + self._ext = "lib" if self.settings.os == "Windows" else "a" + if self.options.shared: + if self.settings.os == "Windows": + self._ext = "dll" + elif self.settings.os == "Macos": + self._ext = "dylib" + else: + self._ext = "so" + return self._ext + + _lib_name = None + + @property + def lib_name(self): + if self._lib_name: + return self._lib_name + ext = "d" if self.settings.build_type == "Debug" else "" + ext += "" if self.settings.os == "Windows" else f".{self.ext}" + self._lib_name = f"{self.name}_{self.options.geometries}_{self.options.optimizer}{ext}" + return self._lib_name + + def configure(self): + if self.options.shared or self.settings.compiler == "Visual Studio": + del self.options.fPIC + if self.options.geometries == "clipper": + self.options["clipper"].shared = self.options.shared + self.options["boost"].header_only = True + self.options["boost"].python_version = self.options.python_version + self.options["boost"].shared = self.options.shared + if self.options.optimizer == "nlopt": + self.options["nlopt"].shared = self.options.shared + + def build_requirements(self): + self.build_requires("cmake/[>=3.16.2]") + if self.options.tests: + self.build_requires("catch2/[>=2.13.6]", force_host_context=True) + + def requirements(self): + if self.options.geometries == "clipper": + self.requires("clipper/[>=6.4.2]") + self.requires("boost/1.70.0") + elif self.options.geometries == "eigen": + self.requires("eigen/[>=3.3.7]") + if self.options.optimizer == "nlopt": + self.requires("nlopt/[>=2.7.0]") + + def validate(self): + if self.settings.compiler.get_safe("cppstd"): + tools.check_min_cppstd(self, 17) + + def generate(self): + cmake = CMakeDeps(self) + cmake.generate() + + tc = CMakeToolchain(self) + + # FIXME: This shouldn't be necessary (maybe a bug in Conan????) + if self.settings.compiler == "Visual Studio": + tc.blocks["generic_system"].values["generator_platform"] = None + tc.blocks["generic_system"].values["toolset"] = None + + tc.variables["LIBNEST2D_HEADER_ONLY"] = self.options.header_only + if self.options.header_only: + tc.variables["BUILD_SHARED_LIBS"] = False + else: + tc.variables["BUILD_SHARED_LIBS"] = self.options.shared + tc.variables["LIBNEST2D_BUILD_UNITTESTS"] = self.options.tests + tc.variables["LIBNEST2D_GEOMETRIES"] = self.options.geometries + tc.variables["LIBNEST2D_OPTIMIZER"] = self.options.optimizer + tc.variables["LIBNEST2D_THREADING"] = self.options.threading + tc.generate() + + _cmake = None + + def configure_cmake(self): + if self._cmake: + return self._cmake + self._cmake = CMake(self) + self._cmake.configure() + return self._cmake + + def build(self): + cmake = self.configure_cmake() + cmake.build() + + def package(self): + cmake = self.configure_cmake() + cmake.install() + + def package_info(self): + self.cpp_info.includedirs = ["include"] + if self.in_local_cache: + self.cpp_info.libdirs = ["lib"] + else: + self.cpp_info.libdirs = [f"cmake-build-{self.settings.build_type}".lower()] + self.cpp_info.libs = [self.lib_name] + self.cpp_info.defines.append(f"LIBNEST2D_GEOMETRIES_{self.options.geometries}") + self.cpp_info.defines.append(f"LIBNEST2D_OPTIMIZERS_{self.options.optimizer}") + self.cpp_info.defines.append(f"LIBNEST2D_THREADING_{self.options.threading}") + self.cpp_info.names["cmake_find_package"] = self.name + self.cpp_info.names["cmake_find_package_multi"] = self.name + if self.settings.os in ["Linux", "FreeBSD", "Macos"]: + self.cpp_info.system_libs.append("pthread") diff --git a/include/libnest2d/backends/clipper/CMakeLists.txt b/include/libnest2d/backends/clipper/CMakeLists.txt index 031a0a0..3dee7cb 100644 --- a/include/libnest2d/backends/clipper/CMakeLists.txt +++ b/include/libnest2d/backends/clipper/CMakeLists.txt @@ -1,7 +1,14 @@ add_library(clipperBackend INTERFACE) -require_package(Clipper 6.1 REQUIRED) -target_link_libraries(clipperBackend INTERFACE Clipper::Clipper) +find_package(polyclipping 6.1 QUIET) +if(NOT TARGET polyclipping::polyclipping) + message(STATUS "Using require_package to obtain Clipper") + require_package(Clipper 6.1 REQUIRED) + add_library(polyclipping::polyclipping INTERFACE IMPORTED) + target_link_libraries(polyclipping::polyclipping INTERFACE Clipper::Clipper) +endif() +target_link_libraries(clipperBackend INTERFACE polyclipping::polyclipping) + # Clipper backend is not enough on its own, it still need some functions # from Boost geometry diff --git a/include/libnest2d/backends/clipper/clipper_polygon.hpp b/include/libnest2d/backends/clipper/clipper_polygon.hpp index 6511fbb..5993221 100644 --- a/include/libnest2d/backends/clipper/clipper_polygon.hpp +++ b/include/libnest2d/backends/clipper/clipper_polygon.hpp @@ -1,7 +1,7 @@ #ifndef CLIPPER_POLYGON_HPP #define CLIPPER_POLYGON_HPP -#include +#include namespace ClipperLib { diff --git a/test_package/CMakeLists.txt b/test_package/CMakeLists.txt new file mode 100644 index 0000000..50a416a --- /dev/null +++ b/test_package/CMakeLists.txt @@ -0,0 +1,10 @@ +cmake_minimum_required(VERSION 3.13) + +project(Libnest2D_example) +set(CMAKE_CXX_STANDARD 17) +find_package(libnest2d REQUIRED) + +add_executable(test main.cpp) + +target_link_libraries(test PRIVATE libnest2d::libnest2d) +target_include_directories(test PRIVATE ${libnest2d_INCLUDE_DIRS}) \ No newline at end of file diff --git a/test_package/conanfile.py b/test_package/conanfile.py new file mode 100644 index 0000000..1077b94 --- /dev/null +++ b/test_package/conanfile.py @@ -0,0 +1,35 @@ +import os +from conans import ConanFile, CMake, tools +from conan.tools.cmake import CMakeToolchain, CMakeDeps, CMake + + +class LibNest2DTestConan(ConanFile): + settings = "os", "compiler", "build_type", "arch" + + def build_requirements(self): + self.build_requires("cmake/[>=3.16.2]") + + def requirements(self): + self.requires(f"libnest2d/4.10.0@ultimaker/testing") + + def generate(self): + cmake = CMakeDeps(self) + cmake.generate() + tc = CMakeToolchain(self) + if self.settings.compiler == "Visual Studio": + tc.blocks["generic_system"].values["generator_platform"] = None + tc.blocks["generic_system"].values["toolset"] = None + tc.generate() + + def build(self): + cmake = CMake(self) + cmake.configure() + cmake.build() + + def imports(self): + self.copy("*.dll", str(self.settings.build_type), "lib") + self.copy("*.dll", str(self.settings.build_type), "lib") + self.copy("*.dylib", str(self.settings.build_type), "lib") + + def test(self): + pass # only interested in compiling and linking \ No newline at end of file diff --git a/test_package/main.cpp b/test_package/main.cpp new file mode 100644 index 0000000..044700f --- /dev/null +++ b/test_package/main.cpp @@ -0,0 +1,6 @@ +#include + +int main(void /*int argc, char **argv*/) { + auto pi = libnest2d::Pi; + return 0; +} \ No newline at end of file