From 95646591d62966043f0bc25364211594adc8d963 Mon Sep 17 00:00:00 2001 From: Christian Pfeiffer Date: Wed, 22 Nov 2017 17:53:54 +0100 Subject: FindIconv: Add the FindIconv module. This module provides abstraction over the various ways POSIX platforms handle the iconv calls defined in POSIX.1-2001 and later versions. --- Help/manual/cmake-modules.7.rst | 1 + Help/module/FindIconv.rst | 1 + Help/release/dev/FindIconv.rst | 4 ++ Modules/FindIconv.cmake | 133 ++++++++++++++++++++++++++++++++++++ Tests/CMakeLists.txt | 4 ++ Tests/FindIconv/CMakeLists.txt | 10 +++ Tests/FindIconv/Test/CMakeLists.txt | 14 ++++ Tests/FindIconv/Test/main.cxx | 52 ++++++++++++++ 8 files changed, 219 insertions(+) create mode 100644 Help/module/FindIconv.rst create mode 100644 Help/release/dev/FindIconv.rst create mode 100644 Modules/FindIconv.cmake create mode 100644 Tests/FindIconv/CMakeLists.txt create mode 100644 Tests/FindIconv/Test/CMakeLists.txt create mode 100644 Tests/FindIconv/Test/main.cxx diff --git a/Help/manual/cmake-modules.7.rst b/Help/manual/cmake-modules.7.rst index 9fd92ec..694bae5 100644 --- a/Help/manual/cmake-modules.7.rst +++ b/Help/manual/cmake-modules.7.rst @@ -130,6 +130,7 @@ All Modules /module/FindIcotool /module/FindICU /module/FindImageMagick + /module/FindIconv /module/FindIntl /module/FindITK /module/FindJasper diff --git a/Help/module/FindIconv.rst b/Help/module/FindIconv.rst new file mode 100644 index 0000000..c1f3ed0 --- /dev/null +++ b/Help/module/FindIconv.rst @@ -0,0 +1 @@ +.. cmake-module:: ../../Modules/FindIconv.cmake diff --git a/Help/release/dev/FindIconv.rst b/Help/release/dev/FindIconv.rst new file mode 100644 index 0000000..98f2591 --- /dev/null +++ b/Help/release/dev/FindIconv.rst @@ -0,0 +1,4 @@ +FindIconv +--------- + +* A :module:`FindIconv` module was added to locate iconv support. diff --git a/Modules/FindIconv.cmake b/Modules/FindIconv.cmake new file mode 100644 index 0000000..bf20f6f --- /dev/null +++ b/Modules/FindIconv.cmake @@ -0,0 +1,133 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +#[=======================================================================[.rst: +FindIconv +--------- + +This module finds the ``iconv()`` POSIX.1 functions on the system. +These functions might be provided in the regular C library or externally +in the form of an additional library. + +The following variables are provided to indicate iconv support: + +.. variable:: Iconv_FOUND + + Variable indicating if the iconv support was found. + +.. variable:: Iconv_INCLUDE_DIRS + + The directories containing the iconv headers. + +.. variable:: Iconv_LIBRARIES + + The iconv libraries to be linked. + +.. variable:: Iconv_IS_BUILT_IN + + A variable indicating whether iconv support is stemming from the + C library or not. Even if the C library provides `iconv()`, the presence of + an external `libiconv` implementation might lead to this being false. + +Additionally, the following :prop_tgt:`IMPORTED` target is being provided: + +.. variable:: Iconv::Iconv + + Imported target for using iconv. + +The following cache variables may also be set: + +.. variable:: Iconv_INCLUDE_DIR + + The directory containing the iconv headers. + +.. variable:: Iconv_LIBRARY + + The iconv library (if not implicitly given in the C library). + +.. note:: + On POSIX platforms, iconv might be part of the C library and the cache + variables ``Iconv_INCLUDE_DIR`` and ``Iconv_LIBRARY`` might be empty. + +#]=======================================================================] + +include(${CMAKE_CURRENT_LIST_DIR}/CMakePushCheckState.cmake) +if(CMAKE_C_COMPILER_LOADED) + include(${CMAKE_CURRENT_LIST_DIR}/CheckCSourceCompiles.cmake) +elseif(CMAKE_CXX_COMPILER_LOADED) + include(${CMAKE_CURRENT_LIST_DIR}/CheckCXXSourceCompiles.cmake) +else() + # If neither C nor CXX are loaded, implicit iconv makes no sense. + set(Iconv_IS_BUILT_IN FALSE) +endif() + +# iconv can only be provided in libc on a POSIX system. +# If any cache variable is already set, we'll skip this test. +if(NOT DEFINED Iconv_IS_BUILT_IN) + if(UNIX AND NOT DEFINED Iconv_INCLUDE_DIR AND NOT DEFINED Iconv_LIBRARY) + cmake_push_check_state(RESET) + # We always suppress the message here: Otherwise on supported systems + # not having iconv in their C library (e.g. those using libiconv) + # would always display a confusing "Looking for iconv - not found" message + set(CMAKE_FIND_QUIETLY TRUE) + # The following code will not work, but it's sufficient to see if it compiles. + # Note: libiconv will define the iconv functions as macros, so CheckSymbolExists + # will not yield correct results. + set(Iconv_IMPLICIT_TEST_CODE + " + #include + #include + int main() { + char *a, *b; + size_t i, j; + iconv_t ic; + ic = iconv_open(\"to\", \"from\"); + iconv(ic, &a, &i, &b, &j); + iconv_close(ic); + } + " + ) + if(CMAKE_C_COMPILER_LOADED) + check_c_source_compiles("${Iconv_IMPLICIT_TEST_CODE}" Iconv_IS_BUILT_IN) + else() + check_cxx_source_compiles("${Iconv_IMPLICIT_TEST_CODE}" Iconv_IS_BUILT_IN) + endif() + cmake_pop_check_state() + else() + set(Iconv_IS_BUILT_IN FALSE) + endif() +endif() + +if(NOT Iconv_IS_BUILT_IN) + find_path(Iconv_INCLUDE_DIR + NAMES "iconv.h" + DOC "iconv include directory") + set(Iconv_LIBRARY_NAMES "iconv" "libiconv") +else() + set(Iconv_INCLUDE_DIR "" CACHE FILEPATH "iconv include directory") + set(Iconv_LIBRARY_NAMES "c") +endif() + +find_library(Iconv_LIBRARY + NAMES ${Iconv_LIBRARY_NAMES} + DOC "iconv library (potentially the C library)") + +mark_as_advanced(Iconv_INCLUDE_DIR) +mark_as_advanced(Iconv_LIBRARY) + +include(${CMAKE_CURRENT_LIST_DIR}/FindPackageHandleStandardArgs.cmake) +if(NOT Iconv_IS_BUILT_IN) + find_package_handle_standard_args(Iconv REQUIRED_VARS Iconv_LIBRARY Iconv_INCLUDE_DIR) +else() + find_package_handle_standard_args(Iconv REQUIRED_VARS Iconv_LIBRARY) +endif() + +if(Iconv_FOUND) + set(Iconv_INCLUDE_DIRS "${Iconv_INCLUDE_DIR}") + set(Iconv_LIBRARIES "${Iconv_LIBRARY}") + if(NOT TARGET Iconv::Iconv) + add_library(Iconv::Iconv INTERFACE IMPORTED) + endif() + set_property(TARGET Iconv::Iconv PROPERTY INTERFACE_INCLUDE_DIRECTORIES "${Iconv_INCLUDE_DIRS}") + set_property(TARGET Iconv::Iconv PROPERTY INTERFACE_LINK_LIBRARIES "${Iconv_LIBRARIES}") +endif() diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt index 4a7b8c9..a61864f 100644 --- a/Tests/CMakeLists.txt +++ b/Tests/CMakeLists.txt @@ -1433,6 +1433,10 @@ ${CMake_BINARY_DIR}/bin/cmake -DDIR=dev -P ${CMake_SOURCE_DIR}/Utilities/Release add_subdirectory(GoogleTest) endif() + if(CMake_TEST_FindIconv) + add_subdirectory(FindIconv) + endif() + if(CMake_TEST_FindICU) add_subdirectory(FindICU) endif() diff --git a/Tests/FindIconv/CMakeLists.txt b/Tests/FindIconv/CMakeLists.txt new file mode 100644 index 0000000..b205b80 --- /dev/null +++ b/Tests/FindIconv/CMakeLists.txt @@ -0,0 +1,10 @@ +add_test(NAME FindIconv.Test COMMAND + ${CMAKE_CTEST_COMMAND} -C $ + --build-and-test + "${CMake_SOURCE_DIR}/Tests/FindIconv/Test" + "${CMake_BINARY_DIR}/Tests/FindIconv/Test" + ${build_generator_args} + --build-project TestFindIconv + --build-options ${build_options} + --test-command ${CMAKE_CTEST_COMMAND} -V -C $ + ) diff --git a/Tests/FindIconv/Test/CMakeLists.txt b/Tests/FindIconv/Test/CMakeLists.txt new file mode 100644 index 0000000..c59adb3 --- /dev/null +++ b/Tests/FindIconv/Test/CMakeLists.txt @@ -0,0 +1,14 @@ +cmake_minimum_required(VERSION 3.10) +project(TestFindIconv CXX) +include(CTest) + +find_package(Iconv REQUIRED) + +add_executable(test_iconv_tgt main.cxx) +target_link_libraries(test_iconv_tgt Iconv::Iconv) +add_test(NAME test_iconv_tgt COMMAND test_iconv_tgt) + +add_executable(test_iconv_var main.cxx) +target_include_directories(test_iconv_var PRIVATE ${Iconv_INCLUDE_DIRS}) +target_link_libraries(test_iconv_var PRIVATE ${Iconv_LIBRARIES}) +add_test(NAME test_iconv_var COMMAND test_iconv_var) diff --git a/Tests/FindIconv/Test/main.cxx b/Tests/FindIconv/Test/main.cxx new file mode 100644 index 0000000..415ee37 --- /dev/null +++ b/Tests/FindIconv/Test/main.cxx @@ -0,0 +1,52 @@ +extern "C" { +#include +} +#include +#include +#include +#include +#include +#include + +class iconv_desc +{ +private: + iconv_t iconvd_; + +public: + iconv_desc(const std::string& tocode, const std::string& fromcode) + { + iconvd_ = iconv_open(tocode.c_str(), fromcode.c_str()); + if (iconvd_ == reinterpret_cast(-1)) + throw std::system_error(errno, std::system_category()); + } + + ~iconv_desc() { iconv_close(iconvd_); } + + operator iconv_t() { return this->iconvd_; } +}; + +int main() +{ + try { + auto conv_d = iconv_desc{ "ISO-8859-1", "UTF-8" }; + auto from_str = std::array{ u8"a\xC3\xA4o\xC3\xB6u\xC3\xBC" }; + auto to_str = std::array{}; + + auto from_str_ptr = from_str.data(); + auto from_len = from_str.size(); + auto to_str_ptr = to_str.data(); + auto to_len = to_str.size(); + const auto iconv_ret = + iconv(conv_d, &from_str_ptr, &from_len, &to_str_ptr, &to_len); + if (iconv_ret == static_cast(-1)) + throw std::system_error(errno, std::system_category()); + std::cout << '\'' << from_str.data() << "\' converted to \'" + << to_str.data() << '\'' << std::endl; + return EXIT_SUCCESS; + } catch (const std::system_error& ex) { + std::cerr << "ERROR: " << ex.code() << '\n' + << ex.code().message() << std::endl; + } + return EXIT_FAILURE; +} -- cgit v0.12