From 8b176951636fa2a1f3f18072a03466bb0dfbaa43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Montan=CC=83ana?= Date: Wed, 16 Jul 2025 17:49:20 +0200 Subject: [PATCH 1/3] Conan create fixed --- .claude/settings.local.json | 12 +++++ ArffFiles.hpp | 15 +++--- CHANGELOG.md | 8 ++++ CMakeLists.txt | 60 +++++++++++++++++++++--- CMakeLists_conan.txt | 11 +++++ CMakeUserPresets.json | 9 ++++ Makefile | 8 ++-- README.md | 4 +- cmake/ArffFilesConfig.cmake.in | 5 ++ conanfile.py | 84 +++++++++++++++++++++++++++++----- config/config.h.in | 9 ++-- tests/TestArffFiles.cc | 3 +- 12 files changed, 191 insertions(+), 37 deletions(-) create mode 100644 .claude/settings.local.json create mode 100644 CMakeLists_conan.txt create mode 100644 CMakeUserPresets.json create mode 100644 cmake/ArffFilesConfig.cmake.in diff --git a/.claude/settings.local.json b/.claude/settings.local.json new file mode 100644 index 0000000..f3132ed --- /dev/null +++ b/.claude/settings.local.json @@ -0,0 +1,12 @@ +{ + "permissions": { + "allow": [ + "Bash(find:*)", + "Bash(mkdir:*)", + "Bash(cmake:*)", + "Bash(make:*)", + "Bash(cat:*)" + ], + "deny": [] + } +} \ No newline at end of file diff --git a/ArffFiles.hpp b/ArffFiles.hpp index 1812e96..991fdb4 100644 --- a/ArffFiles.hpp +++ b/ArffFiles.hpp @@ -10,6 +10,8 @@ #include // std::isdigit #include // std::all_of std::transform #include // For file size checking +#include "arffFiles_config.h" + // Summary information structure for ARFF files struct ArffSummary { @@ -63,8 +65,7 @@ struct ArffSummary { * // Thread 2: globalArff.load("file2.arff"); // UNSAFE! */ class ArffFiles { - const std::string VERSION = "1.2.0"; - +private: // Memory usage limits (configurable via environment variables) static constexpr size_t DEFAULT_MAX_FILE_SIZE = 100 * 1024 * 1024; // 100 MB static constexpr size_t DEFAULT_MAX_SAMPLES = 1000000; // 1 million samples @@ -103,11 +104,11 @@ public: return *this; } - // Copy constructor (explicitly defaulted) - ArffFiles(const ArffFiles& other) = default; + // Copy constructor (explicitly delete) + ArffFiles(const ArffFiles& other) = delete; - // Copy assignment operator (explicitly defaulted) - ArffFiles& operator=(const ArffFiles& other) = default; + // Copy assignment operator (explicitly deleted) + ArffFiles& operator=(const ArffFiles& other) = delete; // Destructor (explicitly defaulted) ~ArffFiles() = default; @@ -252,7 +253,7 @@ public: } return result; } - std::string version() const { return VERSION; } + std::string version() const { return ARFFLIB_VERSION; } private: // Helper function to validate file path for security diff --git a/CHANGELOG.md b/CHANGELOG.md index 9b34e1b..eaf59a0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,14 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [1.2.1] 2025-07-15 Bug Fixes and Improvements + +### Added + +- Library version from CMake projecto to `ArffFiles.hpp` +- Library `catch2` as a conan test requirement +- Install target for CMake + ## [1.2.0] 2025-06-27 Refactoring and Improvements ### Added diff --git a/CMakeLists.txt b/CMakeLists.txt index a036144..eb28b6f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.20) project(ArffFiles - VERSION 1.2.0 + VERSION 1.2.1 DESCRIPTION "Library to read Arff Files and return STL vectors with the data read." HOMEPAGE_URL "https://github.com/rmontanana/ArffFiles" LANGUAGES CXX @@ -41,14 +41,60 @@ add_subdirectory(config) # ------- if (ENABLE_TESTING) MESSAGE("Testing enabled") - Include(FetchContent) - FetchContent_Declare(Catch2 - GIT_REPOSITORY https://github.com/catchorg/Catch2.git - GIT_TAG v3.3.2 - ) - FetchContent_MakeAvailable(Catch2) + find_package(Catch2 REQUIRED) include(CTest) add_subdirectory(tests) endif (ENABLE_TESTING) add_library(ArffFiles INTERFACE ArffFiles.hpp) + +target_include_directories(ArffFiles INTERFACE + $ + $ + $ +) +# Install +# ------- +install(TARGETS ArffFiles EXPORT ArffFilesTargets + INCLUDES DESTINATION include +) +install(EXPORT ArffFilesTargets + FILE ArffFilesTargets.cmake + NAMESPACE ArffFiles:: + DESTINATION lib/cmake/ArffFiles +) + +# Install the main header file +install(FILES ArffFiles.hpp + DESTINATION include +) + +# Install the generated configuration header +install(FILES "${CMAKE_CURRENT_BINARY_DIR}/configured_files/include/arffFiles_config.h" + DESTINATION include +) + +# Install documentation files +install(FILES LICENSE README.md + DESTINATION share/doc/ArffFiles +) + +# Create and install package configuration files +include(CMakePackageConfigHelpers) +write_basic_package_version_file( + "${CMAKE_CURRENT_BINARY_DIR}/ArffFilesConfigVersion.cmake" + VERSION ${PROJECT_VERSION} + COMPATIBILITY AnyNewerVersion +) + +configure_package_config_file( + "${CMAKE_CURRENT_SOURCE_DIR}/cmake/ArffFilesConfig.cmake.in" + "${CMAKE_CURRENT_BINARY_DIR}/ArffFilesConfig.cmake" + INSTALL_DESTINATION lib/cmake/ArffFiles +) + +install(FILES + "${CMAKE_CURRENT_BINARY_DIR}/ArffFilesConfig.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/ArffFilesConfigVersion.cmake" + DESTINATION lib/cmake/ArffFiles +) diff --git a/CMakeLists_conan.txt b/CMakeLists_conan.txt new file mode 100644 index 0000000..40b8a97 --- /dev/null +++ b/CMakeLists_conan.txt @@ -0,0 +1,11 @@ +cmake_minimum_required(VERSION 3.20) + +project(ArffFiles + VERSION 1.2.1 + DESCRIPTION "Library to read Arff Files and return STL vectors with the data read." + HOMEPAGE_URL "https://github.com/rmontanana/ArffFiles" + LANGUAGES CXX +) + +# Subdirectories +add_subdirectory(config) diff --git a/CMakeUserPresets.json b/CMakeUserPresets.json new file mode 100644 index 0000000..e994210 --- /dev/null +++ b/CMakeUserPresets.json @@ -0,0 +1,9 @@ +{ + "version": 4, + "vendor": { + "conan": {} + }, + "include": [ + "build_debug/build/Debug/generators/CMakePresets.json" + ] +} \ No newline at end of file diff --git a/Makefile b/Makefile index 0547324..64052f2 100644 --- a/Makefile +++ b/Makefile @@ -25,10 +25,12 @@ clean: ## Clean the tests info @echo ">>> Done"; build: ## Build a debug version of the project - @echo ">>> Building Debug ArffFiles..."; - @if [ -d ./$(f_debug) ]; then rm -rf ./$(f_debug); fi + @echo ">>> Building Debug Folding..."; + @if [ -d $(f_debug) ]; then rm -rf $(f_debug); fi @mkdir $(f_debug); - @cmake -S . -B $(f_debug) -D CMAKE_BUILD_TYPE=Debug -D ENABLE_TESTING=ON -D CODE_COVERAGE=ON + conan install . -of $(f_debug) -s build_type=Debug -b missing + cmake -B $(f_debug) -S . -DCMAKE_BUILD_TYPE=Debug -DCMAKE_TOOLCHAIN_FILE=$(f_debug)/conan_toolchain.cmake -DENABLE_TESTING=ON + cmake --build $(f_debug) -t $(test_targets) $(n_procs) @echo ">>> Done"; opt = "" diff --git a/README.md b/README.md index 39f9968..8e0556d 100644 --- a/README.md +++ b/README.md @@ -29,10 +29,10 @@ A modern C++17 header-only library to read **ARFF (Attribute-Relation File Forma ```bash # Add the package to your conanfile.txt [requires] -arff-files/1.2.0 +arff-files/1.2.1 # Or install directly -conan install arff-files/1.2.0@ +conan install arff-files/1.2.1@ ``` ### Manual Installation diff --git a/cmake/ArffFilesConfig.cmake.in b/cmake/ArffFilesConfig.cmake.in new file mode 100644 index 0000000..19c5aa6 --- /dev/null +++ b/cmake/ArffFilesConfig.cmake.in @@ -0,0 +1,5 @@ +@PACKAGE_INIT@ + +include("${CMAKE_CURRENT_LIST_DIR}/ArffFilesTargets.cmake") + +check_required_components(ArffFiles) \ No newline at end of file diff --git a/conanfile.py b/conanfile.py index 9dc8829..5685034 100644 --- a/conanfile.py +++ b/conanfile.py @@ -1,19 +1,22 @@ import re from conan import ConanFile from conan.tools.files import copy +from conan.tools.cmake import CMakeToolchain, CMakeDeps class ArffFilesConan(ConanFile): name = "arff-files" version = "X.X.X" - description = "Header-only library to read ARFF (Attribute-Relation File Format) files and return STL vectors with the data read." + description = "Header-only library to read ARFF (Attribute-Relation \ + File Format) files and return STL vectors with the data read." url = "https://github.com/rmontanana/ArffFiles" license = "MIT" homepage = "https://github.com/rmontanana/ArffFiles" topics = ("arff", "data-processing", "file-parsing", "header-only", "cpp17") no_copy_source = True - exports_sources = "ArffFiles.hpp", "LICENSE", "README.md" + exports_sources = ("ArffFiles.hpp", "LICENSE", "README.md", "CMakeLists.txt", "config/*") package_type = "header-library" + settings = "build_type", "compiler", "arch", "os" def init(self): # Read the CMakeLists.txt file to get the version @@ -26,15 +29,81 @@ class ArffFilesConan(ConanFile): if match: self.version = match.group(1) + def build_requirements(self): + self.tool_requires("cmake/[>=3.15]") + self.test_requires("catch2/3.8.1") + + def layout(self): + # Use cmake_layout for proper build folder structure + from conan.tools.cmake import cmake_layout + cmake_layout(self) + + def generate(self): + # Generate CMake toolchain file + tc = CMakeToolchain(self) + tc.generate() + + # Generate CMake dependencies file (needed for test requirements like catch2) + deps = CMakeDeps(self) + deps.generate() + + # Create a minimal CMakeLists.txt for conan build that only generates config + import os + minimal_cmake = """cmake_minimum_required(VERSION 3.20) + +project(ArffFiles + VERSION 1.2.1 + DESCRIPTION "Library to read Arff Files and return STL vectors with the data read." + HOMEPAGE_URL "https://github.com/rmontanana/ArffFiles" + LANGUAGES CXX +) + +# Subdirectories +add_subdirectory(config) +""" + with open(os.path.join(self.source_folder, "CMakeLists_conan.txt"), "w") as f: + f.write(minimal_cmake) + + def build(self): + # Use CMake to generate the config file through existing config system + from conan.tools.cmake import CMake + import os + + # Temporarily rename the files to use minimal CMakeLists.txt + original_cmake = os.path.join(self.source_folder, "CMakeLists.txt") + minimal_cmake = os.path.join(self.source_folder, "CMakeLists_conan.txt") + backup_cmake = os.path.join(self.source_folder, "CMakeLists_backup.txt") + + # Backup and replace + os.rename(original_cmake, backup_cmake) + os.rename(minimal_cmake, original_cmake) + + try: + cmake = CMake(self) + cmake.configure() + # No need to build anything, just configure to generate the config file + finally: + # Restore original files + os.rename(original_cmake, minimal_cmake) + os.rename(backup_cmake, original_cmake) + def package(self): - # Copy header file to include directory + # Copy header file copy( self, - "*.hpp", + "ArffFiles.hpp", src=self.source_folder, dst=self.package_folder, keep_path=False, ) + # Copy the generated config file from CMake build folder + copy( + self, + "arffFiles_config.h", + src=f"{self.build_folder}/configured_files/include", + dst=self.package_folder, + keep_path=False, + ) # Copy license and readme for package documentation copy( self, @@ -50,13 +119,6 @@ class ArffFilesConan(ConanFile): dst=self.package_folder, keep_path=False, ) - copy( - self, - "CMakeLists.txt", - src=self.source_folder, - dst=self.package_folder, - keep_path=False, - ) def package_info(self): # Header-only library configuration diff --git a/config/config.h.in b/config/config.h.in index d6c6362..0646c04 100644 --- a/config/config.h.in +++ b/config/config.h.in @@ -1,11 +1,10 @@ #pragma once -#include -#include +#define ARFFLIB_VERSION_MAJOR @PROJECT_VERSION_MAJOR@ +#define ARFFLIB_VERSION_MINOR @PROJECT_VERSION_MINOR@ +#define ARFFLIB_VERSION_PATCH @PROJECT_VERSION_PATCH@ -#define PROJECT_VERSION_MAJOR @PROJECT_VERSION_MAJOR @ -#define PROJECT_VERSION_MINOR @PROJECT_VERSION_MINOR @ -#define PROJECT_VERSION_PATCH @PROJECT_VERSION_PATCH @ +#define ARFFLIB_VERSION "@PROJECT_VERSION@" static constexpr std::string_view arffFiles_project_name = "@PROJECT_NAME@"; static constexpr std::string_view arffFiles_project_version = "@PROJECT_VERSION@"; diff --git a/tests/TestArffFiles.cc b/tests/TestArffFiles.cc index 0dbf53a..f2ea907 100644 --- a/tests/TestArffFiles.cc +++ b/tests/TestArffFiles.cc @@ -3,7 +3,6 @@ #include #include #include "ArffFiles.hpp" -#include "arffFiles_config.h" #include class Paths { @@ -28,7 +27,7 @@ public: TEST_CASE("Version Test", "[ArffFiles]") { ArffFiles arff; - REQUIRE(arff.version() == "1.1.0"); + REQUIRE(arff.version() == "1.2.1"); } TEST_CASE("Load Test", "[ArffFiles]") { From 332324a6c2601a9aa9e15680813bb9711e5aa208 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Montan=CC=83ana?= Date: Wed, 16 Jul 2025 17:52:57 +0200 Subject: [PATCH 2/3] Remove CMakeUserPresets --- CMakeUserPresets.json | 9 --------- conanfile.py | 33 +++++++++++++++++++++------------ 2 files changed, 21 insertions(+), 21 deletions(-) delete mode 100644 CMakeUserPresets.json diff --git a/CMakeUserPresets.json b/CMakeUserPresets.json deleted file mode 100644 index e994210..0000000 --- a/CMakeUserPresets.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "version": 4, - "vendor": { - "conan": {} - }, - "include": [ - "build_debug/build/Debug/generators/CMakePresets.json" - ] -} \ No newline at end of file diff --git a/conanfile.py b/conanfile.py index 5685034..fae8717 100644 --- a/conanfile.py +++ b/conanfile.py @@ -34,9 +34,15 @@ class ArffFilesConan(ConanFile): self.test_requires("catch2/3.8.1") def layout(self): - # Use cmake_layout for proper build folder structure - from conan.tools.cmake import cmake_layout - cmake_layout(self) + # Only use cmake_layout for conan packaging, not for development builds + # This can be detected by checking if we're in a conan cache folder + import os + if (hasattr(self, 'folders') and + hasattr(self.folders, 'base_build') and + self.folders.base_build and + ".conan2" in self.folders.base_build): + from conan.tools.cmake import cmake_layout + cmake_layout(self) def generate(self): # Generate CMake toolchain file @@ -46,10 +52,14 @@ class ArffFilesConan(ConanFile): # Generate CMake dependencies file (needed for test requirements like catch2) deps = CMakeDeps(self) deps.generate() + + def build(self): + # Use CMake to generate the config file through existing config system + from conan.tools.cmake import CMake + import os # Create a minimal CMakeLists.txt for conan build that only generates config - import os - minimal_cmake = """cmake_minimum_required(VERSION 3.20) + minimal_cmake_content = """cmake_minimum_required(VERSION 3.20) project(ArffFiles VERSION 1.2.1 @@ -61,19 +71,16 @@ project(ArffFiles # Subdirectories add_subdirectory(config) """ - with open(os.path.join(self.source_folder, "CMakeLists_conan.txt"), "w") as f: - f.write(minimal_cmake) - - def build(self): - # Use CMake to generate the config file through existing config system - from conan.tools.cmake import CMake - import os # Temporarily rename the files to use minimal CMakeLists.txt original_cmake = os.path.join(self.source_folder, "CMakeLists.txt") minimal_cmake = os.path.join(self.source_folder, "CMakeLists_conan.txt") backup_cmake = os.path.join(self.source_folder, "CMakeLists_backup.txt") + # Create minimal CMakeLists.txt + with open(minimal_cmake, "w") as f: + f.write(minimal_cmake_content) + # Backup and replace os.rename(original_cmake, backup_cmake) os.rename(minimal_cmake, original_cmake) @@ -86,6 +93,8 @@ add_subdirectory(config) # Restore original files os.rename(original_cmake, minimal_cmake) os.rename(backup_cmake, original_cmake) + # Clean up temporary file + os.remove(minimal_cmake) def package(self): # Copy header file From e2ac5fde1262d4c8b5aee21601981e9a85fc3727 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Montan=CC=83ana?= Date: Wed, 16 Jul 2025 18:34:33 +0200 Subject: [PATCH 3/3] Fix conan build and make build --- .gitignore | 3 ++- conanfile.py | 72 ++++++++++++++++++---------------------------------- 2 files changed, 27 insertions(+), 48 deletions(-) diff --git a/.gitignore b/.gitignore index ee40879..e597295 100644 --- a/.gitignore +++ b/.gitignore @@ -37,4 +37,5 @@ build_*/** cmake-build*/** .idea puml/** -.vscode/settings.json \ No newline at end of file +.vscode/settings.json +CMakeUserPresets.json \ No newline at end of file diff --git a/conanfile.py b/conanfile.py index fae8717..b158289 100644 --- a/conanfile.py +++ b/conanfile.py @@ -14,7 +14,14 @@ class ArffFilesConan(ConanFile): homepage = "https://github.com/rmontanana/ArffFiles" topics = ("arff", "data-processing", "file-parsing", "header-only", "cpp17") no_copy_source = True - exports_sources = ("ArffFiles.hpp", "LICENSE", "README.md", "CMakeLists.txt", "config/*") + exports_sources = ( + "ArffFiles.hpp", + "LICENSE", + "README.md", + "CMakeLists.txt", + "config/*", + "cmake/*", + ) package_type = "header-library" settings = "build_type", "compiler", "arch", "os" @@ -36,65 +43,36 @@ class ArffFilesConan(ConanFile): def layout(self): # Only use cmake_layout for conan packaging, not for development builds # This can be detected by checking if we're in a conan cache folder - import os - if (hasattr(self, 'folders') and - hasattr(self.folders, 'base_build') and - self.folders.base_build and - ".conan2" in self.folders.base_build): + if ( + hasattr(self, "folders") + and hasattr(self.folders, "base_build") + and self.folders.base_build + and ".conan2" in self.folders.base_build + ): from conan.tools.cmake import cmake_layout + cmake_layout(self) - + def generate(self): # Generate CMake toolchain file tc = CMakeToolchain(self) tc.generate() - + # Generate CMake dependencies file (needed for test requirements like catch2) deps = CMakeDeps(self) deps.generate() - + def build(self): # Use CMake to generate the config file through existing config system from conan.tools.cmake import CMake - import os - - # Create a minimal CMakeLists.txt for conan build that only generates config - minimal_cmake_content = """cmake_minimum_required(VERSION 3.20) -project(ArffFiles - VERSION 1.2.1 - DESCRIPTION "Library to read Arff Files and return STL vectors with the data read." - HOMEPAGE_URL "https://github.com/rmontanana/ArffFiles" - LANGUAGES CXX -) - -# Subdirectories -add_subdirectory(config) -""" - - # Temporarily rename the files to use minimal CMakeLists.txt - original_cmake = os.path.join(self.source_folder, "CMakeLists.txt") - minimal_cmake = os.path.join(self.source_folder, "CMakeLists_conan.txt") - backup_cmake = os.path.join(self.source_folder, "CMakeLists_backup.txt") - - # Create minimal CMakeLists.txt - with open(minimal_cmake, "w") as f: - f.write(minimal_cmake_content) - - # Backup and replace - os.rename(original_cmake, backup_cmake) - os.rename(minimal_cmake, original_cmake) - - try: - cmake = CMake(self) - cmake.configure() - # No need to build anything, just configure to generate the config file - finally: - # Restore original files - os.rename(original_cmake, minimal_cmake) - os.rename(backup_cmake, original_cmake) - # Clean up temporary file - os.remove(minimal_cmake) + cmake = CMake(self) + # Configure with minimal options - just enough to generate the config file + cmake.configure( + build_script_folder=None, + cli_args=["-DENABLE_TESTING=OFF", "-DCODE_COVERAGE=OFF"], + ) + # No need to build anything, just configure to generate the config file def package(self): # Copy header file