From d9b5f4ae3b9fade5a7f5b0cb4cda5d53ba91c5a0 Mon Sep 17 00:00:00 2001 From: Philippe Leduc Date: Wed, 22 Jul 2026 14:27:50 +0200 Subject: [PATCH] Add conan recipe Add release-on-tag CI: build Conan package and publish GitHub release --- .github/workflows/ci.yml | 75 ++++++++++++++++++++++++++++ conan/all/conanfile.py | 102 +++++++++++++++++++++++++++++++++++++++ conanfile.py | 77 ----------------------------- core/CMakeLists.txt | 2 +- engine/CMakeLists.txt | 2 +- 5 files changed, 179 insertions(+), 79 deletions(-) create mode 100644 conan/all/conanfile.py delete mode 100644 conanfile.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 81b3845..3fa9aae 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -91,3 +91,78 @@ jobs: name: wheels-${{ matrix.os }} path: wheelhouse/*.whl if-no-files-found: error + + # Build the core+engine Conan package from conan/all. + conan-package: + name: Build Conan package + needs: build-test + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install Conan + run: pip install "conan>=2.10.0" + + - name: Determine version + run: | + if [[ "$GITHUB_REF" == refs/tags/* ]]; then + version="${GITHUB_REF#refs/tags/}" + echo "PKG_VERSION=${version#v}" >> "$GITHUB_ENV" + else + echo "PKG_VERSION=0.0.0-${GITHUB_SHA::8}" >> "$GITHUB_ENV" + fi + + - name: Conan create + run: | + conan profile detect --force + conan create conan/all --version "$PKG_VERSION" \ + --build=missing -s compiler.cppstd=20 + + - name: Export Conan package + run: | + conan cache save "piper/${PKG_VERSION}:*" \ + --file "piper-${PKG_VERSION}-linux-x86_64.tgz" + + - uses: actions/upload-artifact@v4 + with: + name: conan-package + path: piper-*.tgz + if-no-files-found: error + + # Attach the wheels and the Conan package to a GitHub release. Tag pushes only. + release: + name: Release + needs: [wheels, conan-package] + if: startsWith(github.ref, 'refs/tags/') + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - uses: actions/checkout@v4 + + - name: Download wheels + uses: actions/download-artifact@v4 + with: + pattern: wheels-* + path: wheelhouse + merge-multiple: true + + - name: Download Conan package + uses: actions/download-artifact@v4 + with: + name: conan-package + path: conan-package + + - name: Create release + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + tag: ${{ github.ref_name }} + run: | + gh release create "$tag" wheelhouse/*.whl conan-package/*.tgz \ + --repo="$GITHUB_REPOSITORY" \ + --title="${GITHUB_REPOSITORY#*/} ${tag#v}" \ + --generate-notes diff --git a/conan/all/conanfile.py b/conan/all/conanfile.py new file mode 100644 index 0000000..28ff851 --- /dev/null +++ b/conan/all/conanfile.py @@ -0,0 +1,102 @@ +import os + +from conan import ConanFile +from conan.tools.build import check_min_cppstd +from conan.tools.cmake import CMake, CMakeToolchain, CMakeDeps, cmake_layout +from conan.tools.files import copy, get + +required_conan_version = ">=2.10.0" + + +class PiperRecipe(ConanFile): + name = "piper" + description = "Visual designer for control-system pipelines (core + engine libraries)" + license = "CECILL-C" + url = "https://github.com/conan-io/conan-center-index" + homepage = "https://github.com/leducp/Piper" + topics = ("control-systems", "node-editor", "dataflow", "visual-programming") + package_type = "library" + + settings = "os", "compiler", "build_type", "arch" + options = {"shared": [True, False], "fPIC": [True, False]} + default_options = {"shared": False, "fPIC": True} + + def export_sources(self): + # Local development: copy the sources from the project root (two levels + # up from conan/all/) into the recipe. Released builds fetch a tagged + # tarball via source() instead. + root = os.path.abspath(os.path.join(self.recipe_folder, "../..")) + copy(self, "CMakeLists.txt", src=root, dst=self.export_sources_folder) + copy(self, "cmake/*", src=root, dst=self.export_sources_folder) + copy(self, "core/*", src=root, dst=self.export_sources_folder, + excludes=["**/tests/**", "**/examples/**", "**/py_bindings/**"]) + copy(self, "engine/*", src=root, dst=self.export_sources_folder, + excludes=["**/tests/**", "**/examples/**", "**/py_bindings/**"]) + copy(self, "LICENCE", src=root, dst=self.export_sources_folder) + + def source(self): + # Fetches a tagged source tarball when building a released version. + # get(self, **self.conan_data["sources"][self.version], strip_root=True) + pass + + def config_options(self): + if self.settings.os == "Windows": + del self.options.fPIC + + def configure(self): + if self.options.get_safe("shared"): + self.options.rm_safe("fPIC") + + def layout(self): + cmake_layout(self) + + def validate(self): + if self.settings.compiler.get_safe("cppstd"): + check_min_cppstd(self, 20) + + def requirements(self): + self.requires("nlohmann_json/3.11.3") + + def generate(self): + tc = CMakeToolchain(self) + tc.cache_variables["BUILD_APP"] = False + tc.cache_variables["BUILD_PY_BINDINGS"] = False + tc.cache_variables["BUILD_TESTS"] = False + tc.cache_variables["BUILD_EXAMPLES"] = False + tc.cache_variables["CMAKE_CI_BUILD"] = True + tc.generate() + CMakeDeps(self).generate() + + def build(self): + cmake = CMake(self) + cmake.configure() + cmake.build() + + def package(self): + # No install() rules exist for core/engine, so hand-copy the public + # headers and the static archives. + for subdir in ("core/include", "engine/include"): + copy(self, "*.h", + os.path.join(self.source_folder, subdir), + os.path.join(self.package_folder, "include")) + for pattern in ("*.a", "*.so", "*.so.*", "*.dylib"): + copy(self, pattern, + self.build_folder, + os.path.join(self.package_folder, "lib"), + keep_path=False) + copy(self, "LICENCE", + src=self.source_folder, + dst=os.path.join(self.package_folder, "licenses")) + + def package_info(self): + self.cpp_info.set_property("cmake_file_name", "piper") + + c = self.cpp_info.components + + c["piper_core"].libs = ["piper_core"] + c["piper_core"].requires = ["nlohmann_json::nlohmann_json"] + c["piper_core"].set_property("cmake_target_name", "piper::piper_core") + + c["piper_engine"].libs = ["piper_engine"] + c["piper_engine"].requires = ["piper_core"] + c["piper_engine"].set_property("cmake_target_name", "piper::piper_engine") diff --git a/conanfile.py b/conanfile.py deleted file mode 100644 index 4f5d5e8..0000000 --- a/conanfile.py +++ /dev/null @@ -1,77 +0,0 @@ -import os - -from conan import ConanFile -from conan.tools.cmake import CMakeToolchain, CMake, cmake_layout, CMakeDeps -from conan.tools.files import copy - - -class PiperPackage(ConanFile): - name = "piper" - description = "Visual designer for control-system pipelines (core + engine libraries)" - license = "CECILL-C" - settings = "os", "compiler", "build_type", "arch" - options = {"fPIC": [True, False]} - default_options = {"fPIC": True} - - def export_sources(self): - root = self.recipe_folder - copy(self, "CMakeLists.txt", root, self.export_sources_folder) - copy(self, "cmake/*", root, self.export_sources_folder) - copy(self, "core/*", root, self.export_sources_folder, - excludes=["**/tests/**", "**/examples/**", "**/py_bindings/**"]) - copy(self, "engine/*", root, self.export_sources_folder, - excludes=["**/tests/**", "**/examples/**", "**/py_bindings/**"]) - - def config_options(self): - if self.settings.os == "Windows": - del self.options.fPIC - - def layout(self): - cmake_layout(self) - - def requirements(self): - self.requires("nlohmann_json/3.11.3") - - def generate(self): - tc = CMakeToolchain(self) - tc.cache_variables["BUILD_APP"] = False - tc.cache_variables["BUILD_PY_BINDINGS"] = False - tc.cache_variables["BUILD_TESTS"] = False - tc.cache_variables["BUILD_EXAMPLES"] = False - tc.cache_variables["CMAKE_CI_BUILD"] = True - tc.cache_variables["CONAN_PACKAGE_BUILD"] = True - tc.generate() - CMakeDeps(self).generate() - - def build(self): - cmake = CMake(self) - cmake.configure() - cmake.build() - - def package(self): - # No install() rules exist for core/engine, so hand-copy the - # public headers and the static archives - for subdir in ("core/include", "engine/include"): - copy(self, "*.h", - os.path.join(self.source_folder, subdir), - os.path.join(self.package_folder, "include")) - copy(self, "*.tpp", - os.path.join(self.source_folder, subdir), - os.path.join(self.package_folder, "include")) - copy(self, "*.a", - self.build_folder, - os.path.join(self.package_folder, "lib"), - keep_path=False) - - def package_info(self): - self.cpp_info.set_property("cmake_file_name", "piper") - - c = self.cpp_info.components - - c["piper_core"].libs = ["piper_core"] - c["piper_core"].requires = ["nlohmann_json::nlohmann_json"] - c["piper_core"].set_property("cmake_target_name", "piper::piper_core") - - c["piper_engine"].libs = ["piper_engine"] - c["piper_engine"].requires = ["piper_core"] - c["piper_engine"].set_property("cmake_target_name", "piper::piper_engine") diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index b126c9a..ce4dbc1 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -15,7 +15,7 @@ set(PIPER_CORE_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/src/vec.cc ) -add_library(piper_core STATIC ${PIPER_CORE_SRCS}) +add_library(piper_core ${PIPER_CORE_SRCS}) target_include_directories(piper_core PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include) target_link_libraries(piper_core PRIVATE nlohmann_json::nlohmann_json) set_target_properties(piper_core PROPERTIES POSITION_INDEPENDENT_CODE ON) diff --git a/engine/CMakeLists.txt b/engine/CMakeLists.txt index 157c9bb..1df0bdd 100644 --- a/engine/CMakeLists.txt +++ b/engine/CMakeLists.txt @@ -6,7 +6,7 @@ set(PIPER_ENGINE_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/src/builtin_steps.cc ) -add_library(piper_engine STATIC ${PIPER_ENGINE_SRCS}) +add_library(piper_engine ${PIPER_ENGINE_SRCS}) target_include_directories(piper_engine PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include) target_include_directories(piper_engine PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src) target_link_libraries(piper_engine PUBLIC piper_core)