tfussell / xlnt

:bar_chart: Cross-platform user-friendly xlsx library for C++11+
Other
1.48k stars 418 forks source link

CMake Module support #412

Open johnco3 opened 5 years ago

johnco3 commented 5 years ago

CMake support is pretty good in this library (supporting install and even uninstall, however it does not currently supportfind_package(xlnt CONFIG) orfind_package(xlnt MODULE). I am writing a Modern CMake script for my application and it would be great if I could import the xlnt target info. Are there any plans to do so or are you aware of any 3rd party Findxlnt.cmake script.

In my CMakeLists.txt I have

# Excel C++ library - this does not work well
find_package(xlnt CONFIG)

which results in -- Could NOT find xlnt (missing: xlnt_DIR)

Alternatively:

# Excel C++ library - this does not work well
find_package(xlnt MODULE)

which results in

CMake Warning (dev) at CMakeLists.txt:34 (find_package):
  Findxlnt.cmake must either be part of this project itself, in this case
  adjust CMAKE_MODULE_PATH so that it points to the correct location inside
  its source tree.

  Or it must be installed by a package which has already been found via
  find_package().  In this case make sure that package has indeed been found
  and adjust CMAKE_MODULE_PATH to contain the location where that package has
  installed Findxlnt.cmake.  This must be a location provided by that
  package.  This error in general means that the buildsystem of this project
  is relying on a Find-module without ensuring that it is actually available.

This warning is for project developers.  Use -Wno-dev to suppress it.
johnco3 commented 5 years ago

I was able to make this sort of work as follows. I placed this in a folder cmake/Modules within my project and I run cmake as follows on my windows platform. Note that I previously installed (via ninja install) the xlnt library to C:\tools\cmake_install_prefix where I install my dependent packages.

cmake -GNinja -DCMAKE_BUILD_TYPE=Debug -DCMAKE_EXPORT_COMPILE_COMMANDS:BOOL=ON -DCMAKE_VERBOSE_MAKEFILE:BOOL=ON -DCMAKE_MODULE_PATH=cmake/Modules -DCMAKE_INSTALL_PREFIX:PATH=/tools/cmake_install_prefix ../..

This results in the following console output from my Findxlnt.cmake:

xlnt_INCLUDE_DIR=/tools/cmake_install_prefix/include
xlnt_LIBRARY=optimized;C:/main/extlibs/xlnt/build/release/source/xlnt.lib;debug;C:/main/extlibs/xlnt/build/debug/source/xlntd.lib

My Module Finder is as follows.

# Findxlnt.cmake
#
# Finds the xlnt library
#
# This will define the following variables
#
#    xlnt_FOUND
#    xlnt_INCLUDE_DIR
#    xlnt_LIBRARY
#
# and the following imported targets
#
#     xlnt::xlnt
#
# Author: John Coffey - johnco3@gmail.com
find_path(xlnt_INCLUDE_DIR NAMES xlnt/xlnt.hpp)
# find_library(xlnt_LIBRARY NAMES xlnt xlntd DOC "C++ excel library")
if (NOT xlnt_LIBRARIES)
        find_library(xlnt_LIBRARY_RELEASE NAMES xlnt DOC "xlnt release library")
        find_library(xlnt_LIBRARY_DEBUG NAMES xlntd DOC "xlnt debug library")
        include(SelectLibraryConfigurations)
        select_library_configurations(xlnt)
endif()

include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(xlnt
        REQUIRED_VARS xlnt_INCLUDE_DIR xlnt_LIBRARY)
mark_as_advanced(
        xlnt_INCLUDE_DIR
        xlnt_LIBRARY)

if(xlnt_FOUND AND NOT (TARGET xlnt::xlnt))
        message(STATUS "xlnt_INCLUDE_DIR=${xlnt_INCLUDE_DIR}")
        message(STATUS "xlnt_LIBRARY=${xlnt_LIBRARY}")
        message(STATUS "xlnt_LIBRARIES=${xlnt_LIBRARIES}")
        message(STATUS "xlnt_LIBRARY_DEBUG=${xlnt_LIBRARY_DEBUG}")
        message(STATUS "xlnt_LIBRARY_RELEASE=${xlnt_LIBRARY_RELEASE}")
        add_library(xlnt::xlnt UNKNOWN IMPORTED)
        set_target_properties(xlnt::xlnt PROPERTIES
                INTERFACE_INCLUDE_DIRECTORIES "${xlnt_INCLUDE_DIR}"
                IMPORTED_LINK_INTERFACE_LANGUAGES "CXX"
                IMPORTED_LOCATION "${xlnt_LIBRARY}")
endif()

Running the build through ninja results in an error parsing the paths (I can see that the :

Build files have been written to: C:/main/tcdu/build/debug
ninja: error: FindFirstFileExA(optimized;c:/main/extlibs/xlnt/build/release/source/xlnt.lib;debug;c:/main/extlibs/xlnt/build/debug/source): The filename, directory name, or volume label syntax is incorrect.

Looking at the generated build output it appears that the cmake helper script SelectLibraryConfigurations.cmake that is part of cmake inserts this into the build. I needed to use that to separately use a release and debug lib as part of my imported target dependencies.

#############################################
# Link the executable tcdu.exe

build tcdu.exe: CXX_EXECUTABLE_LINKER__tcdu CMakeFiles\tcdu.dir\src\tcdu\main.cpp.obj CMakeFiles\tcdu.dir\src\tcdu\TCDUTester.cpp.obj CMakeFiles\tcdu.dir\src\tcdu\TCDUEvent.cpp.obj | 429.lib 702.lib 739.lib util.lib png.lib apex.lib subsys.lib vcdu.lib model.lib cdu.lib ateserver.lib ..\..\..\extlibs\ansys\OGLX\SDYOGLX67\lib64\OGLX_debug.lib C$:\main\extlibs\freeglut-3.0.0\build\release\lib\freeglutd.lib optimized;C$:\main\extlibs\xlnt\build\release\source\xlnt.lib;debug;C$:\main\extlibs\xlnt\build\debug\source\xlntd.lib \tools\cmake_install_prefix\lib\fmtd.lib C$:\main\extlibs\boost_1_71_0\lib64-msvc-14.2\boost_log-vc142-mt-gd-x64-1_71.lib \tools\cmake_install_prefix\lib\Geographic_d.lib 429.lib util.lib C$:\main\extlibs\boost_1_71_0\lib64-msvc-14.2\boost_log-vc142-mt-gd-x64-1_71.lib C$:\main\extlibs\boost_1_71_0\lib64-msvc-14.2\boost_log_setup-vc142-mt-gd-x64-1_71.lib C$:\main\extlibs\boost_1_71_0\lib64-msvc-14.2\boost_filesystem-vc142-mt-gd-x64-1_71.lib C$:\main\extlibs\boost_1_71_0\lib64-msvc-14.2\boost_thread-vc142-mt-gd-x64-1_71.lib C$:\main\extlibs\boost_1_71_0\lib64-msvc-14.2\boost_date_time-vc142-mt-gd-x64-1_71.lib C$:\main\extlibs\boost_1_71_0\lib64-msvc-14.2\boost_regex-vc142-mt-gd-x64-1_71.lib C$:\main\extlibs\boost_1_71_0\lib64-msvc-14.2\boost_chrono-vc142-mt-gd-x64-1_71.lib C$:\main\extlibs\boost_1_71_0\lib64-msvc-14.2\boost_atomic-vc142-mt-gd-x64-1_71.lib png.lib apex.lib C$:\main\extlibs\boost_1_71_0\lib64-msvc-14.2\boost_program_options-vc142-mt-gd-x64-1_71.lib || 429.lib 702.lib 739.lib apex.lib ateserver.lib cdu.lib model.lib png.lib subsys.lib util.lib vcdu.lib
  FLAGS = /DWIN32 /D_WINDOWS /GR /EHsc /Zi /Ob0 /Od /RTC1 -MDd
  LINK_FLAGS = /machine:x64 /debug /INCREMENTAL /subsystem:console
  LINK_LIBRARIES = 429.lib 702.lib 739.lib util.lib png.lib apex.lib subsys.lib vcdu.lib model.lib cdu.lib ateserver.lib ..\..\..\extlibs\ansys\OGLX\SDYOGLX67\lib64\OGLX_debug.lib C:\main\extlibs\freeglut-3.0.0\build\release\lib\freeglutd.lib optimized;C:\main\extlibs\xlnt\build\release\source\xlnt.lib;debug;C:\main\extlibs\xlnt\build\debug\source\xlntd.lib \tools\cmake_install_prefix\lib\fmtd.lib C:\main\extlibs\boost_1_71_0\lib64-msvc-14.2\boost_log-vc142-mt-gd-x64-1_71.lib \tools\cmake_install_prefix\lib\Geographic_d.lib 429.lib util.lib C:\main\extlibs\boost_1_71_0\lib64-msvc-14.2\boost_log-vc142-mt-gd-x64-1_71.lib C:\main\extlibs\boost_1_71_0\lib64-msvc-14.2\boost_log_setup-vc142-mt-gd-x64-1_71.lib C:\main\extlibs\boost_1_71_0\lib64-msvc-14.2\boost_filesystem-vc142-mt-gd-x64-1_71.lib C:\main\extlibs\boost_1_71_0\lib64-msvc-14.2\boost_thread-vc142-mt-gd-x64-1_71.lib C:\main\extlibs\boost_1_71_0\lib64-msvc-14.2\boost_date_time-vc142-mt-gd-x64-1_71.lib C:\main\extlibs\boost_1_71_0\lib64-msvc-14.2\boost_regex-vc142-mt-gd-x64-1_71.lib C:\main\extlibs\boost_1_71_0\lib64-msvc-14.2\boost_chrono-vc142-mt-gd-x64-1_71.lib C:\main\extlibs\boost_1_71_0\lib64-msvc-14.2\boost_atomic-vc142-mt-gd-x64-1_71.lib png.lib apex.lib C:\main\extlibs\boost_1_71_0\lib64-msvc-14.2\boost_program_options-vc142-mt-gd-x64-1_71.lib kernel32.lib user32.lib gdi32.lib winspool.lib shell32.lib ole32.lib oleaut32.lib uuid.lib comdlg32.lib advapi32.lib
  OBJECT_DIR = CMakeFiles\tcdu.dir
  POST_BUILD = cd .
  PRE_LINK = cd .
  TARGET_COMPILE_PDB = CMakeFiles\tcdu.dir\
  TARGET_FILE = tcdu.exe
  TARGET_IMPLIB = tcdu.lib
  TARGET_PDB = tcdu.pdb
johnco3 commented 5 years ago

OK here is a working fix, the problem was that I needed finer grained control over the target property settings. I include the Finxlnt.cmake - you can check it in somewhere if you wish.

# Findxlnt.cmake
#
# Finds the xlnt library
#
# This will define the following variables
#
#   xlnt_FOUND
#   xlnt_LIBRARY
#   xlnt_LIBRARIES
#   xlnt_LIBRARY_DEBUG
#   xlnt_LIBRARY_RELEASE
#
# and the following imported targets
#
#   xlnt::xlnt
#
# Author: John Coffey - johnco3@gmail.com
#

find_path(xlnt_INCLUDE_DIR NAMES xlnt/xlnt.hpp)

if (NOT xlnt_LIBRARIES)
    find_library(xlnt_LIBRARY_RELEASE NAMES xlnt DOC "xlnt release library")
    find_library(xlnt_LIBRARY_DEBUG NAMES xlntd DOC "xlnt debug library")
    include(SelectLibraryConfigurations)
    select_library_configurations(xlnt)
endif()

include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(xlnt
    REQUIRED_VARS xlnt_INCLUDE_DIR xlnt_LIBRARY)
mark_as_advanced(
    xlnt_INCLUDE_DIR
    xlnt_LIBRARY)

if(xlnt_FOUND AND NOT (TARGET xlnt::xlnt))
    # debug output showing the located libraries
    message(STATUS "xlnt_INCLUDE_DIR=${xlnt_INCLUDE_DIR}")
    message(STATUS "xlnt_LIBRARY=${xlnt_LIBRARY}")
    message(STATUS "xlnt_LIBRARIES=${xlnt_LIBRARIES}")
    message(STATUS "xlnt_LIBRARY_DEBUG=${xlnt_LIBRARY_DEBUG}")
    message(STATUS "xlnt_LIBRARY_RELEASE=${xlnt_LIBRARY_RELEASE}")
    # Add a blank imported library
    add_library(xlnt::xlnt UNKNOWN IMPORTED)

    # add the transitive includes property
    set_target_properties(xlnt::xlnt PROPERTIES
        INTERFACE_INCLUDE_DIRECTORIES "${xlnt_INCLUDE_DIR}")

    # Optimized library
    if(xlnt_LIBRARY_RELEASE)
        set_property(TARGET xlnt::xlnt APPEND PROPERTY
            IMPORTED_CONFIGURATIONS RELEASE)
        set_target_properties(xlnt::xlnt PROPERTIES
            IMPORTED_LOCATION_RELEASE "${xlnt_LIBRARY_RELEASE}")
    endif()

    # Debug library
    if(xlnt_LIBRARY_DEBUG)
        set_property(TARGET xlnt::xlnt APPEND PROPERTY
            IMPORTED_CONFIGURATIONS DEBUG)
        set_target_properties(xlnt::xlnt PROPERTIES
            IMPORTED_LOCATION_DEBUG "${xlnt_LIBRARY_DEBUG}")
    endif()

    # some other configuration
    if(NOT xlnt_LIBRARY_RELEASE AND NOT xlnt_LIBRARY_DEBUG)
        set_property(TARGET xlnt::xlnt APPEND PROPERTY
            IMPORTED_LOCATION "${xlnt_LIBRARY}")
    endif()
endif()