diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..22159b2 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,153 @@ +cmake_minimum_required(VERSION 3.15) + +project(OBUParse VERSION 1.0.0 LANGUAGES C) + +# --- Set Standard Output Directories --- +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) + +# --- Build Options --- +# On MSVC, we only build static to avoid DLL export complexity and naming collisions. +# On other platforms (including MinGW on Windows), we can build both. +if(MSVC) + option(BUILD_SHARED_LIBS "Build the shared library (disabled on MSVC)" OFF) + option(BUILD_STATIC_LIBS "Build the static library" ON) + set(BUILD_SHARED_LIBS OFF) # Force OFF for MSVC +else() + option(BUILD_SHARED_LIBS "Build the shared library" ON) + option(BUILD_STATIC_LIBS "Build the static library" ON) +endif() + +if(NOT BUILD_SHARED_LIBS AND NOT BUILD_STATIC_LIBS) + message(FATAL_ERROR "You must choose to build at least one library type.") +endif() + +# --- Library Targets --- + +if(BUILD_SHARED_LIBS) # This block will now be skipped on MSVC + add_library(obuparse_shared SHARED obuparse.c) + set_target_properties(obuparse_shared PROPERTIES + OUTPUT_NAME "obuparse" + VERSION ${PROJECT_VERSION} + SOVERSION 1 + EXPORT_NAME "shared" + ) + target_include_directories(obuparse_shared PUBLIC + $ + $ + ) + add_library(OBUParse::shared ALIAS obuparse_shared) +endif() + +if(BUILD_STATIC_LIBS) + add_library(obuparse_static STATIC obuparse.c) + # The naming collision only happens with MSVC, not MinGW. + if(MSVC) + set_target_properties(obuparse_static PROPERTIES OUTPUT_NAME "obuparses") + else() + set_target_properties(obuparse_static PROPERTIES OUTPUT_NAME "obuparse") + endif() + set_target_properties(obuparse_static PROPERTIES EXPORT_NAME "static") + target_include_directories(obuparse_static PUBLIC + $ + $ + ) + add_library(OBUParse::static ALIAS obuparse_static) +endif() + +# --- Tool Target (obudump) --- +add_executable(obudump + tools/obudump.c + tools/json.c +) +target_include_directories(obudump PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) +if(TARGET OBUParse::shared) + target_link_libraries(obudump PRIVATE OBUParse::shared) +else() + target_link_libraries(obudump PRIVATE OBUParse::static) +endif() + +# --- RPATH Handling for obudump tool --- +if(UNIX AND TARGET obudump AND TARGET OBUParse::shared) + # This handles the build tree. The executable is in build/bin, the library is in build/lib. + # The RPATH needs to point to the library directory. + set_property(TARGET obudump PROPERTY + BUILD_RPATH "${CMAKE_BINARY_DIR}/lib" + ) + + # This handles the install tree. The path is relative from the installed bin/ directory + # to the lib/ directory. + set_property(TARGET obudump PROPERTY + INSTALL_RPATH "\$ORIGIN/../lib" + ) +endif() + +# --- Installation --- +include(GNUInstallDirs) +include(CMakePackageConfigHelpers) + +# Install the public header file +install(FILES obuparse.h + DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" +) + +set(BUILT_TARGETS "") +if(TARGET obuparse_static) + list(APPEND BUILT_TARGETS obuparse_static) +endif() +if(TARGET obuparse_shared) + list(APPEND BUILT_TARGETS obuparse_shared) +endif() + +# Install the library targets that were built, and the tool +install(TARGETS ${BUILT_TARGETS} obudump + EXPORT OBUParseTargets + ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" + LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" + RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" +) + +# Generate and install the obuparse-config.cmake file +# This file tells other projects where to find the targets we just installed. +write_basic_package_version_file( + "${CMAKE_CURRENT_BINARY_DIR}/obuparse-config-version.cmake" + VERSION ${PROJECT_VERSION} + COMPATIBILITY AnyNewerVersion +) + +configure_package_config_file( + "obuparse-config.cmake.in" + "${CMAKE_CURRENT_BINARY_DIR}/obuparse-config.cmake" + INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/obuparse" +) + +# Install the generated CMake package files +install( + FILES + "${CMAKE_CURRENT_BINARY_DIR}/obuparse-config.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/obuparse-config-version.cmake" + DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/obuparse" +) + +# Install the export set, which contains the actual target information +install( + EXPORT OBUParseTargets + FILE OBUParseTargets.cmake + NAMESPACE OBUParse:: + DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/obuparse" +) + +# --- Uninstall Target --- +if(NOT TARGET uninstall) + configure_file( + "${CMAKE_CURRENT_SOURCE_DIR}/cmake_uninstall.cmake.in" + "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" + IMMEDIATE @ONLY + ) + + add_custom_target(uninstall + COMMAND ${CMAKE_COMMAND} -P "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" + COMMENT "Uninstalling the project..." + ) +endif() diff --git a/cmake_uninstall.cmake.in b/cmake_uninstall.cmake.in new file mode 100644 index 0000000..83523c0 --- /dev/null +++ b/cmake_uninstall.cmake.in @@ -0,0 +1,45 @@ +# cmake_uninstall.cmake.in +# This script is configured by CMake and then executed to uninstall the project. +# It removes only the files listed in the manifest and the project's +# specific CMake package directory. + +if(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") + message(FATAL_ERROR "Cannot find install manifest: \"@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt\"") +endif() + +# --- Stage 1: Remove all files from the manifest --- +message(STATUS "--- Uninstalling files ---") +file(STRINGS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" installed_files) +foreach(file ${installed_files}) + message(STATUS "Removing: \"${file}\"") + if(EXISTS "${file}" OR IS_SYMLINK "${file}") + file(REMOVE "${file}") + else() + message(STATUS "File does not exist, skipping: \"${file}\"") + endif() +endforeach() + +# --- Stage 2: Remove the project's specific CMake directory --- +# This path is taken directly from the install() command in CMakeLists.txt +set(package_dir "@CMAKE_INSTALL_PREFIX@/@CMAKE_INSTALL_LIBDIR@/cmake/obuparse") + +if(EXISTS "${package_dir}") + message(STATUS "--- Cleaning up package directory ---") + # Check if the directory is now empty + file(GLOB children LIST_DIRECTORIES false "${package_dir}/*") + if(NOT children) + message(STATUS "Removing empty directory: \"${package_dir}\"") + execute_process( + COMMAND "@CMAKE_COMMAND@" -E rm -rf "${package_dir}" + RESULT_VARIABLE res + ) + + if(NOT res EQUAL 0) + message(WARNING "Could not remove directory: \"${package_dir}\". It may be locked by another process.") + endif() + else() + message(STATUS "Package directory is not empty, skipping: \"${package_dir}\"") + endif() +endif() + +message(STATUS "Uninstallation complete.") diff --git a/obuparse-config.cmake.in b/obuparse-config.cmake.in new file mode 100644 index 0000000..ffb8b40 --- /dev/null +++ b/obuparse-config.cmake.in @@ -0,0 +1,8 @@ +# obuparse-config.cmake.in +# This file is configured by CMake and installed with the library. +# It helps other projects find the OBUParse targets. + +@PACKAGE_INIT@ + +# Include the file that contains the imported target definitions. +include("${CMAKE_CURRENT_LIST_DIR}/OBUParseTargets.cmake")