cmake_minimum_required(VERSION 3.18)

file(READ "VERSION" VERSION)
string(STRIP ${VERSION} VERSION)
if(${VERSION} MATCHES [[([0-9]+)\.([0-9]+)\.([0-9]+)]])
    set(LIBXML_MAJOR_VERSION ${CMAKE_MATCH_1})
    set(LIBXML_MINOR_VERSION ${CMAKE_MATCH_2})
    set(LIBXML_MICRO_VERSION ${CMAKE_MATCH_3})
endif()

project(libxml2 VERSION ${VERSION} LANGUAGES C)

set(CMAKE_C_STANDARD 11)

include(CheckCSourceCompiles)
include(CheckFunctionExists)
include(CheckIncludeFiles)
include(CheckLibraryExists)
include(CheckStructHasMember)
include(CheckSymbolExists)
include(CMakeDependentOption)
include(CMakePackageConfigHelpers)
include(CMakePushCheckState)
include(FindPkgConfig)
include(GNUInstallDirs)

option(BUILD_SHARED_LIBS "Build shared libraries" ON)
option(LIBXML2_WITH_CATALOG "Add the Catalog support" ON)
option(LIBXML2_WITH_DEBUG "Add the debugging module" ON)
option(LIBXML2_WITH_DOCS "Build documentation" OFF)
option(LIBXML2_WITH_HTML "Add the HTML support" ON)
option(LIBXML2_WITH_HTTP "ABI compatibility for removed HTTP support" OFF)
option(LIBXML2_WITH_ICONV "Add ICONV support" ON)
option(LIBXML2_WITH_ICU "Add ICU support" OFF)
option(LIBXML2_WITH_ISO8859X "Add ISO8859X support if no iconv" ON)
option(LIBXML2_WITH_LEGACY "Add deprecated APIs for compatibility" OFF)
option(LIBXML2_WITH_MODULES "Add the dynamic modules support" ON)
option(LIBXML2_WITH_OUTPUT "Add the serialization support" ON)
option(LIBXML2_WITH_PATTERN "Add the xmlPattern selection interface" ON)
option(LIBXML2_WITH_PROGRAMS "Build programs" ON)
option(LIBXML2_WITH_PUSH "Add the PUSH parser interfaces" ON)
option(LIBXML2_WITH_PYTHON "Build Python bindings" OFF)
option(LIBXML2_WITH_READLINE "readline support for xmllint shell" OFF)
option(LIBXML2_WITH_REGEXPS "Add Regular Expressions support" ON)
option(LIBXML2_WITH_SAX1 "Add the older SAX1 interface" ON)
option(LIBXML2_WITH_TESTS "Build tests" ON)
option(LIBXML2_WITH_THREADS "Add multithread support" ON)
option(LIBXML2_WITH_TLS "Enable thread-local storage" OFF)
option(LIBXML2_WITH_VALID "Add the DTD validation support" ON)
option(LIBXML2_WITH_XINCLUDE "Add the XInclude support" ON)
option(LIBXML2_WITH_XPATH "Add the XPATH support" ON)

cmake_dependent_option(
    LIBXML2_WITH_ZLIB "Use libz" OFF
    "NOT LIBXML2_WITH_LEGACY" ON)

cmake_dependent_option(
    LIBXML2_WITH_C14N "Add the Canonicalization support" ON
    "LIBXML2_WITH_OUTPUT;LIBXML2_WITH_XPATH" OFF)
cmake_dependent_option(
    LIBXML2_WITH_HISTORY "history support for xmllint shell" OFF
    "LIBXML2_WITH_READLINE" OFF)
cmake_dependent_option(
    LIBXML2_WITH_READER "Add the xmlReader parsing interface" ON
    "LIBXML2_WITH_PUSH" OFF)
cmake_dependent_option(
    LIBXML2_WITH_SCHEMAS "Add XML Schemas 1.0 support" ON
    "LIBXML2_WITH_PATTERN;LIBXML2_WITH_REGEXPS" OFF)
cmake_dependent_option(
    LIBXML2_WITH_SCHEMATRON "Add Schematron support" OFF
    "LIBXML2_WITH_PATTERN;LIBXML2_WITH_XPATH" OFF)
cmake_dependent_option(
    LIBXML2_WITH_THREAD_ALLOC "Add per-thread malloc hooks" OFF
    "LIBXML2_WITH_THREADS" OFF)
cmake_dependent_option(
    LIBXML2_WITH_WRITER "Add the xmlWriter saving interface" ON
    "LIBXML2_WITH_OUTPUT;LIBXML2_WITH_PUSH" OFF)
cmake_dependent_option(
    LIBXML2_WITH_XPTR "Add the XPointer support" ON
    "LIBXML2_WITH_XPATH" OFF)

cmake_dependent_option(
    LIBXML2_WITH_RELAXNG "Add Relax-NG support" ON
    "LIBXML2_WITH_REGEXPS;LIBXML2_WITH_SCHEMAS" OFF)

if(LIBXML2_WITH_PYTHON)
    find_package(Python3 COMPONENTS Interpreter Development REQUIRED)
    #set(LIBXML2_PYTHON_INSTALL_DIR ${Python3_SITEARCH} CACHE PATH "Python bindings install directory")
    set(LIBXML2_PYTHON_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/python"
        CACHE PATH "Python bindings install directory")
endif()

foreach(VARIABLE IN ITEMS WITH_C14N WITH_CATALOG WITH_DEBUG WITH_HTML WITH_HTTP WITH_ICONV WITH_ICU WITH_ISO8859X WITH_MODULES WITH_OUTPUT WITH_PATTERN WITH_PUSH WITH_READER WITH_REGEXPS WITH_RELAXNG WITH_SAX1 WITH_SCHEMAS WITH_SCHEMATRON WITH_THREADS WITH_THREAD_ALLOC WITH_VALID WITH_WRITER WITH_XINCLUDE WITH_XPATH WITH_XPTR WITH_ZLIB)
    if(LIBXML2_${VARIABLE})
        set(${VARIABLE} 1)
    else()
        set(${VARIABLE} 0)
    endif()
endforeach()

set(LIBXML_VERSION ${VERSION})
set(LIBXML_VERSION_EXTRA "")
math(EXPR LIBXML_VERSION_NUMBER "
    ${LIBXML_MAJOR_VERSION} * 10000 +
    ${LIBXML_MINOR_VERSION} * 100 +
    ${LIBXML_MICRO_VERSION}
")

set(MODULE_EXTENSION "${CMAKE_SHARED_LIBRARY_SUFFIX}")

if(LIBXML2_WITH_ICONV)
    find_package(Iconv REQUIRED)
endif()

if(LIBXML2_WITH_ICU)
    find_package(ICU REQUIRED COMPONENTS uc)
endif()

if(LIBXML2_WITH_THREADS)
    find_package(Threads REQUIRED)
    set(THREAD_LIBS ${CMAKE_THREAD_LIBS_INIT})
    list(APPEND CMAKE_REQUIRED_LIBRARIES Threads::Threads)
endif()

if(LIBXML2_WITH_ZLIB)
    find_package(ZLIB REQUIRED)
endif()

check_c_source_compiles("
    void __attribute__((destructor))
    f(void) {}
    int main(void) { return 0; }
" HAVE_FUNC_ATTRIBUTE_DESTRUCTOR)
check_symbol_exists(getentropy "sys/random.h" HAVE_DECL_GETENTROPY)
check_symbol_exists(glob "glob.h" HAVE_DECL_GLOB)
check_symbol_exists(mmap "sys/mman.h" HAVE_DECL_MMAP)
check_include_files(stdint.h HAVE_STDINT_H)

if(LIBXML2_WITH_READLINE)
    check_library_exists(readline readline "" HAVE_LIBREADLINE)
    if (LIBXML2_WITH_HISTORY)
        check_library_exists(history append_history "" HAVE_LIBHISTORY)
    endif()
endif()

if(LIBXML2_WITH_TLS)
    check_c_source_compiles(
        "_Thread_local int v; int main(){return 0;}"
        XML_THREAD_LOCAL_C11
    )
    if (XML_THREAD_LOCAL_C11)
        set(XML_THREAD_LOCAL "_Thread_local")
    else()
        check_c_source_compiles(
            "__thread int v; int main(){return 0;}"
            XML_THREAD_LOCAL_THREAD
        )
        if (XML_THREAD_LOCAL_THREAD)
            set(XML_THREAD_LOCAL "__thread")
        else()
            check_c_source_compiles(
                "__declspec(thread) int v; int main(){return 0;}"
                XML_THREAD_LOCAL_DECLSPEC
            )
            if (XML_THREAD_LOCAL_DECLSPEC)
                set(XML_THREAD_LOCAL "__declspec(thread)")
            endif()
        endif()
    endif()
endif()

# Headers which are private.
set(
    LIBXML2_PRIVATE_HDRS
    include/private/buf.h
    include/private/cata.h
    include/private/dict.h
    include/private/enc.h
    include/private/entities.h
    include/private/error.h
    include/private/globals.h
    include/private/html.h
    include/private/io.h
    include/private/lint.h
    include/private/memory.h
    include/private/parser.h
    include/private/regexp.h
    include/private/save.h
    include/private/string.h
    include/private/threads.h
    include/private/tree.h
    include/private/xinclude.h
    include/private/xpath.h
)

set(
    LIBXML2_HDRS
    include/libxml/c14n.h
    include/libxml/catalog.h
    include/libxml/chvalid.h
    include/libxml/debugXML.h
    include/libxml/dict.h
    include/libxml/encoding.h
    include/libxml/entities.h
    include/libxml/globals.h
    include/libxml/hash.h
    include/libxml/HTMLparser.h
    include/libxml/HTMLtree.h
    include/libxml/list.h
    include/libxml/nanoftp.h
    include/libxml/nanohttp.h
    include/libxml/parser.h
    include/libxml/parserInternals.h
    include/libxml/pattern.h
    include/libxml/relaxng.h
    include/libxml/SAX.h
    include/libxml/SAX2.h
    include/libxml/schemasInternals.h
    include/libxml/schematron.h
    include/libxml/threads.h
    include/libxml/tree.h
    include/libxml/uri.h
    include/libxml/valid.h
    include/libxml/xinclude.h
    include/libxml/xlink.h
    include/libxml/xmlIO.h
    include/libxml/xmlautomata.h
    include/libxml/xmlerror.h
    include/libxml/xmlexports.h
    include/libxml/xmlmemory.h
    include/libxml/xmlmodule.h
    include/libxml/xmlreader.h
    include/libxml/xmlregexp.h
    include/libxml/xmlsave.h
    include/libxml/xmlschemas.h
    include/libxml/xmlschemastypes.h
    include/libxml/xmlstring.h
    include/libxml/xmlunicode.h
    include/libxml/xmlwriter.h
    include/libxml/xpath.h
    include/libxml/xpathInternals.h
    include/libxml/xpointer.h
)

set(
    LIBXML2_SRCS
    buf.c
    chvalid.c
    dict.c
    encoding.c
    entities.c
    error.c
    globals.c
    hash.c
    list.c
    parser.c
    parserInternals.c
    SAX2.c
    threads.c
    tree.c
    uri.c
    valid.c
    xmlIO.c
    xmlmemory.c
    xmlstring.c
)

if(LIBXML2_WITH_C14N)
    list(APPEND LIBXML2_SRCS c14n.c)
endif()
if(LIBXML2_WITH_CATALOG)
    list(APPEND LIBXML2_SRCS catalog.c)
endif()
if(LIBXML2_WITH_DEBUG)
    list(APPEND LIBXML2_SRCS debugXML.c)
endif()
if(LIBXML2_WITH_HTML)
    list(APPEND LIBXML2_SRCS HTMLparser.c HTMLtree.c)
endif()
if(LIBXML2_WITH_HTTP)
    list(APPEND LIBXML2_SRCS nanohttp.c)
endif()
if(LIBXML2_WITH_MODULES)
    list(APPEND LIBXML2_SRCS xmlmodule.c)
endif()
if(LIBXML2_WITH_OUTPUT)
    list(APPEND LIBXML2_SRCS xmlsave.c)
endif()
if(LIBXML2_WITH_PATTERN)
    list(APPEND LIBXML2_SRCS pattern.c)
endif()
if(LIBXML2_WITH_READER)
    list(APPEND LIBXML2_SRCS xmlreader.c)
endif()
if(LIBXML2_WITH_REGEXPS)
    list(APPEND LIBXML2_SRCS xmlregexp.c)
endif()
if(LIBXML2_WITH_RELAXNG)
    list(APPEND LIBXML2_SRCS relaxng.c)
endif()
if(LIBXML2_WITH_SCHEMAS)
    list(APPEND LIBXML2_SRCS xmlschemas.c xmlschemastypes.c)
endif()
if(LIBXML2_WITH_SCHEMATRON)
    list(APPEND LIBXML2_SRCS schematron.c)
endif()
if(LIBXML2_WITH_WRITER)
    list(APPEND LIBXML2_SRCS xmlwriter.c)
endif()
if(LIBXML2_WITH_XINCLUDE)
    list(APPEND LIBXML2_SRCS xinclude.c)
endif()
if(LIBXML2_WITH_XPATH)
    list(APPEND LIBXML2_SRCS xpath.c)
endif()
if(LIBXML2_WITH_XPTR)
    list(APPEND LIBXML2_SRCS xlink.c xpointer.c)
endif()

if(WIN32)
    list(APPEND LIBXML2_SRCS win32/libxml2.rc)
    file(
        WRITE
        ${CMAKE_CURRENT_BINARY_DIR}/rcVersion.h
        "#define LIBXML_MAJOR_VERSION ${LIBXML_MAJOR_VERSION}\n"
        "#define LIBXML_MINOR_VERSION ${LIBXML_MINOR_VERSION}\n"
        "#define LIBXML_MICRO_VERSION ${LIBXML_MICRO_VERSION}\n"
        "#define LIBXML_DOTTED_VERSION \"${VERSION}\"\n"
    )
endif()

add_library(LibXml2 ${LIBXML2_HDRS} ${LIBXML2_SRCS} ${LIBXML2_PRIVATE_HDRS})
add_library(LibXml2::LibXml2 ALIAS LibXml2)

target_include_directories(
    LibXml2
    PUBLIC
    $<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}>
    $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
    $<INSTALL_INTERFACE:$<INSTALL_PREFIX>/${CMAKE_INSTALL_INCLUDEDIR}/libxml2>
)

if(LIBXML2_WITH_MODULES)
    cmake_push_check_state(RESET)
        set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_DL_LIBS})
        check_symbol_exists(dlopen "dlfcn.h" HAVE_DLOPEN)
    cmake_pop_check_state()
    if(HAVE_DLOPEN)
        target_link_libraries(LibXml2 PRIVATE ${CMAKE_DL_LIBS})
        list(TRANSFORM CMAKE_DL_LIBS PREPEND "-l" REGEX "^[^-]" OUTPUT_VARIABLE MODULE_LIBS)
    else()
        check_library_exists(dld shl_load "" HAVE_SHLLOAD)
        if(HAVE_SHLLOAD)
            target_link_libraries(LibXml2 PRIVATE dld)
            set(MODULE_LIBS "-ldld")
        endif()
    endif()
endif()

if(UNIX)
    target_link_libraries(LibXml2 PRIVATE m)
    set(LIBM "-lm")
endif()

if(WIN32)
    target_link_libraries(LibXml2 PRIVATE bcrypt)
    set(CRYPTO_LIBS "-lbcrypt")
endif()

if(LIBXML2_WITH_ICONV)
    target_link_libraries(LibXml2 PRIVATE Iconv::Iconv)
    if(NOT Iconv_IS_BUILT_IN)
        set(ICONV_LIBS "-liconv")
    endif()
endif()

if(LIBXML2_WITH_ICU)
    target_link_libraries(LibXml2 PRIVATE ICU::uc)
    set(ICU_LDFLAGS "-licuuc")
    list(APPEND XML_PRIVATE_LIBS "${ICU_LDFLAGS}")
    pkg_check_modules(ICU_PC IMPORTED_TARGET icu-uc)
    if(ICU_PC_FOUND)
        list(APPEND XML_PC_REQUIRES icu-uc)
    else()
        list(APPEND XML_PC_LIBS "${ICU_LDFLAGS}")
    endif()
endif()

if(LIBXML2_WITH_THREADS)
    target_link_libraries(LibXml2 PRIVATE Threads::Threads)
endif()

if(LIBXML2_WITH_ZLIB)
    target_link_libraries(LibXml2 PRIVATE ZLIB::ZLIB)
    set(ZLIB_LDFLAGS "-lz")
    list(APPEND XML_PRIVATE_LIBS "${ZLIB_LDFLAGS}")
    pkg_check_modules(ZLIB_PC IMPORTED_TARGET zlib)
    if(ZLIB_PC_FOUND)
        list(APPEND XML_PC_REQUIRES zlib)
    else()
        list(APPEND XML_PC_LIBS "${ZLIB_LDFLAGS}")
    endif()
endif()

if(CMAKE_C_COMPILER_ID MATCHES "Clang" OR CMAKE_C_COMPILER_ID STREQUAL "GNU")
    # These compiler flags can break the checks above so keep them here.
    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pedantic -Wall -Wextra -Wshadow \
-Wpointer-arith -Wcast-align -Wwrite-strings \
-Wstrict-prototypes -Wmissing-prototypes \
-Wno-long-long -Wno-format-extra-args -Wno-array-bounds")
endif()

set(LIBXML_MINOR_COMPAT 14)
math(EXPR LIBXML_SOVERSION "${LIBXML_MAJOR_VERSION} + ${LIBXML_MINOR_COMPAT}")
math(EXPR LIBXML_AGE "${LIBXML_MINOR_VERSION} - ${LIBXML_MINOR_COMPAT}")
math(EXPR LIBXML_MACHO_COMPAT "${LIBXML_MAJOR_VERSION} + ${LIBXML_MINOR_VERSION} + 1")
set_target_properties(
    LibXml2
    PROPERTIES
    IMPORT_PREFIX lib
    OUTPUT_NAME xml2
    POSITION_INDEPENDENT_CODE ON
    PREFIX lib
    VERSION "${LIBXML_SOVERSION}.${LIBXML_AGE}.${LIBXML_MICRO_VERSION}"
    SOVERSION ${LIBXML_SOVERSION}
    MACHO_COMPATIBILITY_VERSION ${LIBXML_MACHO_COMPAT}
    MACHO_CURRENT_VERSION "${LIBXML_MACHO_COMPAT}.${LIBXML_MICRO_VERSION}"
)

if(MSVC)
    if(BUILD_SHARED_LIBS)
        set_target_properties(
            LibXml2
            PROPERTIES
            DEBUG_POSTFIX d
        )
    else()
        set_target_properties(
            LibXml2
            PROPERTIES
            DEBUG_POSTFIX sd
            MINSIZEREL_POSTFIX s
            RELEASE_POSTFIX s
            RELWITHDEBINFO_POSTFIX s
        )
    endif()
endif()

set(XML_SYSCONFDIR "${CMAKE_INSTALL_FULL_SYSCONFDIR}")

install(FILES ${LIBXML2_HDRS} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/libxml2/libxml COMPONENT development)

install(
    TARGETS    LibXml2
    EXPORT LibXml2
    ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT development
    LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT runtime NAMELINK_COMPONENT development
    RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT runtime
)

if(MSVC AND BUILD_SHARED_LIBS)
    install(FILES $<TARGET_PDB_FILE:LibXml2> DESTINATION ${CMAKE_INSTALL_BINDIR} CONFIGURATIONS Debug RelWithDebInfo COMPONENT debug)
endif()

if(LIBXML2_WITH_PROGRAMS)
    add_executable(xmllint xmllint.c shell.c lintmain.c)
    set(PROGRAMS xmllint)
    if(LIBXML2_WITH_CATALOG AND LIBXML2_WITH_OUTPUT)
        add_executable(xmlcatalog xmlcatalog.c)
        list(APPEND PROGRAMS xmlcatalog)
    endif()
    foreach(PROGRAM ${PROGRAMS})
        add_executable(LibXml2::${PROGRAM} ALIAS ${PROGRAM})
        target_link_libraries(${PROGRAM} LibXml2)
        if(HAVE_LIBHISTORY)
            target_link_libraries(${PROGRAM} history)
        endif()
        if(HAVE_LIBREADLINE)
            target_link_libraries(${PROGRAM} readline)
        endif()
        install(TARGETS    ${PROGRAM} EXPORT LibXml2 RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT programs)
    endforeach()
    if(LIBXML2_WITH_ZLIB)
        target_link_libraries(xmllint ZLIB::ZLIB)
    endif()
endif()

if(LIBXML2_WITH_TESTS)
    enable_testing()
    set(
        TESTS
        runtest
        runxmlconf
        runsuite
        testapi
        testcatalog
        testchar
        testdict
        testModule
        testlimits
        testparser
        testrecurse
    )
    foreach(TEST ${TESTS})
        add_executable(${TEST} ${TEST}.c)
        target_link_libraries(${TEST} LibXml2)
    endforeach()
    if(Threads_FOUND)
        foreach(TEST runtest)
        if(LIBXML2_WITH_ZLIB)
            target_link_libraries(${TEST} ZLIB::ZLIB Threads::Threads)
        else()
            target_link_libraries(${TEST} Threads::Threads)
        endif()
        endforeach()
    endif()
    add_test(NAME runtest COMMAND runtest --out ${CMAKE_CURRENT_BINARY_DIR} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
    add_test(NAME runsuite COMMAND runsuite WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
    if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/xmlconf/xmlconf.xml)
        add_test(NAME runxmlconf COMMAND runxmlconf WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
    endif()
    add_test(NAME testapi COMMAND testapi)
    add_test(NAME testcatalog COMMAND testcatalog)
    add_test(NAME testchar COMMAND testchar)
    add_test(NAME testdict COMMAND testdict)
    add_test(NAME testparser COMMAND testparser WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
    add_test(NAME testrecurse COMMAND testrecurse WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})


    function(ADD_SUITE test_dir program)
        file(GLOB_RECURSE files_to_process CONFIGURE_DEPENDS "./test/${test_dir}/*.script")

        foreach(file_path ${files_to_process})
            file(RELATIVE_PATH file_relative "${CMAKE_CURRENT_SOURCE_DIR}" "${file_path}")
            get_filename_component(dir_path ${file_relative} DIRECTORY)
            get_filename_component(base_name ${file_relative} NAME_WE)
            get_filename_component(extension ${file_relative} EXT)

            set(actual_stdout   "${CMAKE_CURRENT_BINARY_DIR}/result/${test_dir}/${base_name}")
            set(actual_stderr   "${CMAKE_CURRENT_BINARY_DIR}/result/${test_dir}/${base_name}.err")
            set(expected_stdout "${CMAKE_CURRENT_SOURCE_DIR}/result/${test_dir}/${base_name}")
            set(expected_stderr "${CMAKE_CURRENT_SOURCE_DIR}/result/${test_dir}/${base_name}.err")

            set(xml_path  "${CMAKE_CURRENT_SOURCE_DIR}/${dir_path}/${base_name}.xml")
            set(sgml_path "${CMAKE_CURRENT_SOURCE_DIR}/${dir_path}/${base_name}.sgml")

            if(EXISTS "${xml_path}")
                set(input_file "./${dir_path}/${base_name}.xml")
            elseif(EXISTS "${sgml_path}")
                set(input_file "./${dir_path}/${base_name}.sgml")
            else()
                message(FATAL_ERROR "Neither XML nor SGML exists for: ${dir_path}/${base_name}")
            endif()

            add_test(
                NAME ${test_dir}_${base_name}
                COMMAND ${CMAKE_COMMAND}
                    -DPROG=$<TARGET_FILE:${program}>
                    -DSTDIN=./${dir_path}/${base_name}.script
                    -DXML=${input_file}
                    -DSTDOUT=${actual_stdout}
                    -DSTDERR=${actual_stderr}
                    -DEXPECTED_STDOUT=${expected_stdout}
                    -DEXPECTED_STDERR=${expected_stderr}
                    -DWORKDIR=${CMAKE_CURRENT_SOURCE_DIR}
                    -P ${CMAKE_CURRENT_SOURCE_DIR}/run_and_diff.cmake
            )

        endforeach()
    endfunction()

    add_suite("scripts" xmllint)
    add_suite("catalogs" xmlcatalog)

    add_test(
        NAME catalog_add_del
        COMMAND ${CMAKE_COMMAND}
                -DXMLCAT=$<TARGET_FILE:xmlcatalog>
                -DEXPECTED_FULL=${CMAKE_CURRENT_SOURCE_DIR}/result/catalogs/mycatalog.full
                -DEXPECTED_EMPTY=${CMAKE_CURRENT_SOURCE_DIR}/result/catalogs/mycatalog.empty
                -P ${CMAKE_CURRENT_SOURCE_DIR}/test_xmlcatalog_add_del.cmake
    )

endif()

if(LIBXML2_WITH_DOCS OR LIBXML2_WITH_PYTHON)
    set(DOXYFILE ${CMAKE_CURRENT_SOURCE_DIR}/doc/Doxyfile)
    add_custom_command(
        OUTPUT doc/html doc/xml
        COMMAND ${CMAKE_COMMAND} -E env
            SOURCE_ROOT=${CMAKE_CURRENT_SOURCE_DIR}/
            BUILD_ROOT=${CMAKE_CURRENT_BINARY_DIR}/
            doxygen -q ${DOXYFILE}
        MAIN_DEPENDENCY ${DOXYFILE}
        DEPENDS ${LIBXML2_HDRS} ${LIBXML2_SRCS}
    )
    add_custom_target(Doxygen ALL DEPENDS doc/html doc/xml)
endif()

if(LIBXML2_WITH_PYTHON)
    add_custom_command(
        OUTPUT
            libxml2-py.c
            libxml2-py.h
            libxml2.py
        COMMAND
            ${Python3_EXECUTABLE}
            ${CMAKE_CURRENT_SOURCE_DIR}/python/generator.py
            ${CMAKE_CURRENT_BINARY_DIR}
        DEPENDS
            python/generator.py
            Doxygen
    )

    Python3_add_library(
        LibXml2Mod MODULE WITH_SOABI
        libxml2-py.c
        libxml2-py.h
        python/libxml.c
        python/libxml_wrap.h
        python/types.c
    )
    target_include_directories(
        LibXml2Mod
        PRIVATE
        $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/python>
    )
    target_link_libraries(LibXml2Mod PRIVATE LibXml2)
    set_target_properties(
        LibXml2Mod
        PROPERTIES
        IMPORT_PREFIX lib
        OUTPUT_NAME xml2mod
        PREFIX lib
    )
    install(
        TARGETS    LibXml2Mod
        ARCHIVE DESTINATION ${LIBXML2_PYTHON_INSTALL_DIR} COMPONENT development
        LIBRARY DESTINATION ${LIBXML2_PYTHON_INSTALL_DIR} COMPONENT runtime NAMELINK_COMPONENT development
        RUNTIME DESTINATION ${LIBXML2_PYTHON_INSTALL_DIR} COMPONENT runtime
    )
    if(MSVC AND BUILD_SHARED_LIBS)
        install(FILES $<TARGET_PDB_FILE:LibXml2Mod> DESTINATION ${LIBXML2_PYTHON_INSTALL_DIR} CONFIGURATIONS Debug RelWithDebInfo COMPONENT debug)
    endif()
    install(FILES python/drv_libxml2.py DESTINATION ${LIBXML2_PYTHON_INSTALL_DIR} COMPONENT runtime)
    install(FILES ${CMAKE_CURRENT_BINARY_DIR}/libxml2.py DESTINATION ${LIBXML2_PYTHON_INSTALL_DIR} COMPONENT runtime)
endif()

if (LIBXML2_WITH_DOCS)
    set(MAN_PAGES doc/xml2-config.1)

    if(LIBXML2_WITH_PROGRAMS)
        set(PROGNAMES xmllint)
        if(LIBXML2_WITH_CATALOG AND LIBXML2_WITH_OUTPUT)
            list(APPEND PROGNAMES xmlcatalog)
        endif()
        foreach(PROG IN ITEMS ${PROGNAMES})
            set(XML_SRC ${CMAKE_CURRENT_SOURCE_DIR}/doc/${PROG}.xml)

            add_custom_command(
                OUTPUT ${PROG}.1
                COMMAND xsltproc
                    --nonet --novalid
                    --param man.output.quietly 1
                    -o ${PROG}.1
                    http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl
                    ${XML_SRC}
                MAIN_DEPENDENCY ${XML_SRC}
            )
            list(APPEND MAN_PAGES ${CMAKE_CURRENT_BINARY_DIR}/${PROG}.1)

            add_custom_command(
                OUTPUT ${PROG}.html
                COMMAND xsltproc
                    --nonet --novalid
                    -o ${PROG}.html
                    http://docbook.sourceforge.net/release/xsl/current/html/docbook.xsl
                    ${XML_SRC}
                MAIN_DEPENDENCY ${XML_SRC}
            )
            list(APPEND HTML_PAGES ${CMAKE_CURRENT_BINARY_DIR}/${PROG}.html)
        endforeach()

        add_custom_target(DocBook ALL DEPENDS ${MAN_PAGES} ${HTML_PAGES})

        install(
            FILES ${HTML_PAGES}
            DESTINATION ${CMAKE_INSTALL_DOCDIR}
            COMPONENT documentation
        )
    endif()

    install(
        FILES ${MAN_PAGES}
        DESTINATION ${CMAKE_INSTALL_MANDIR}/man1
        COMPONENT documentation
    )
    install(
        DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/doc/html
        DESTINATION ${CMAKE_INSTALL_DOCDIR}
        COMPONENT documentation
    )
endif()

configure_package_config_file(
    libxml2-config.cmake.cmake.in libxml2-config.cmake
    INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/libxml2
)

install(
    FILES ${CMAKE_CURRENT_BINARY_DIR}/libxml2-config.cmake
    DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/libxml2
    COMPONENT development
)

write_basic_package_version_file(
    ${CMAKE_CURRENT_BINARY_DIR}/libxml2-config-version.cmake
    VERSION ${PROJECT_VERSION}
    COMPATIBILITY SameMajorVersion
)

install(
    FILES ${CMAKE_CURRENT_BINARY_DIR}/libxml2-config-version.cmake
    DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/libxml2
    COMPONENT development
)

install(
    EXPORT LibXml2
    DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/libxml2
    NAMESPACE LibXml2::
    FILE libxml2-export.cmake
    COMPONENT development
)

configure_file(config.h.cmake.in config.h)
configure_file(include/libxml/xmlversion.h.in libxml/xmlversion.h)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/libxml/xmlversion.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/libxml2/libxml COMPONENT development)

source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR}
                  FILES ${LIBXML2_SRCS} ${LIBXML2_HDRS} ${LIBXML2_PRIVATE_HDRS})

if(LIBXML2_WITH_PYTHON)
    set(prefix "${CMAKE_INSTALL_PREFIX}")
    configure_file(python/setup.py.in setup.py @ONLY)
endif()

set(NON_PC_LIBS "${THREAD_LIBS} ${ICONV_LIBS} ${LIBM} ${WINSOCK_LIBS} ${CRYPTO_LIBS} ${MODULE_LIBS}")
list(APPEND XML_PC_LIBS "${NON_PC_LIBS}")
list(APPEND XML_PRIVATE_LIBS "${NON_PC_LIBS}")
list(REMOVE_DUPLICATES XML_PC_LIBS)
list(REMOVE_DUPLICATES XML_PRIVATE_LIBS)

list(JOIN XML_PC_REQUIRES " " XML_PC_REQUIRES)
list(JOIN XML_PC_LIBS " " XML_PC_LIBS)
list(JOIN XML_PRIVATE_LIBS " " XML_PRIVATE_LIBS)

set(XML_INCLUDEDIR "-I\${includedir}/libxml2")
set(XML_LIBDIR "-L\${libdir}")
set(XML_LIBS "-lxml2")

if(BUILD_SHARED_LIBS)
    set(XML_PC_PRIVATE ".private")
    set(XML_PC_LIBS_PRIVATE "
Libs.private:")
else()
    set(XML_PRIVATE_LIBS_NO_SHARED "${XML_PRIVATE_LIBS}")
endif()

if(WIN32)
    set(XML_STATIC_CFLAGS "-DLIBXML_STATIC")
    if (BUILD_SHARED_LIBS)
        set(XML_PC_CFLAGS_PRIVATE "
Cflags.private:")
    else()
        target_compile_definitions(LibXml2 PUBLIC LIBXML_STATIC)
        set(XML_CFLAGS "${XML_STATIC_CFLAGS}")
    endif()
endif()

file(RELATIVE_PATH PACKAGE_RELATIVE_PATH "${CMAKE_INSTALL_FULL_LIBDIR}/pkgconfig" "${CMAKE_INSTALL_PREFIX}")
string(REGEX REPLACE "/$" "" PACKAGE_RELATIVE_PATH "${PACKAGE_RELATIVE_PATH}")

if(WIN32)
    set(prefix "\${pcfiledir}/${PACKAGE_RELATIVE_PATH}")
else()
    set(prefix "${CMAKE_INSTALL_PREFIX}")
endif()
set(exec_prefix "\${prefix}")
set(libdir "\${prefix}/${CMAKE_INSTALL_LIBDIR}")
set(includedir "\${prefix}/${CMAKE_INSTALL_INCLUDEDIR}")
configure_file(libxml-2.0.pc.in libxml-2.0.pc @ONLY)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/libxml-2.0.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig COMPONENT development)

if(WIN32)
    set(prefix "\$(cd \"\$(dirname \"\$0\")\"; pwd -P)/..")
endif()
configure_file(xml2-config.in xml2-config @ONLY)
install(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/xml2-config DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT development)

set(XML_INCLUDEDIR "-I${CMAKE_INSTALL_FULL_INCLUDEDIR}/libxml2")
set(XML_LIBDIR "-L${CMAKE_INSTALL_FULL_LIBDIR}")

set(CPACK_COMPONENT_DEVELOPMENT_DEPENDS runtime)
set(CPACK_COMPONENT_PROGRAMS_DEPENDS runtime)
set(CPACK_DEB_COMPONENT_INSTALL ON)
set(CPACK_DEBIAN_DEVELOPMENT_PACKAGE_DEPENDS "${PACKAGE_TARNAME}")
set(CPACK_DEBIAN_DEVELOPMENT_PACKAGE_NAME "${PACKAGE_TARNAME}-dev")
set(CPACK_DEBIAN_DEVELOPMENT_PACKAGE_SECTION "libdevel")
set(CPACK_DEBIAN_PACKAGE_HOMEPAGE ${PACKAGE_URL})
set(CPACK_DEBIAN_PACKAGE_NAME ${PACKAGE_TARNAME})
set(CPACK_DEBIAN_PACKAGE_SECTION "devel")
set(CPACK_DEBIAN_PROGRAMS_PACKAGE_DEPENDS "${PACKAGE_TARNAME}")
set(CPACK_DEBIAN_PROGRAMS_PACKAGE_NAME "${PACKAGE_TARNAME}-utils")
set(CPACK_DEBIAN_PROGRAMS_PACKAGE_SECTION "utils")
set(CPACK_DEBIAN_RUNTIME_PACKAGE_NAME ${PACKAGE_TARNAME})
set(CPACK_DEBIAN_RUNTIME_PACKAGE_RECOMMENDS "${PACKAGE_TARNAME}-utils")
set(CPACK_DEBIAN_RUNTIME_PACKAGE_SECTION "libs")
set(CPACK_NSIS_PACKAGE_NAME ${PACKAGE_STRING})
set(CPACK_NSIS_URL_INFO_ABOUT ${PACKAGE_URL})
set(CPACK_PACKAGE_DISPLAY_NAME ${PACKAGE_STRING})
set(CPACK_PACKAGE_INSTALL_DIRECTORY "${PACKAGE_TARNAME}-${PACKAGE_VERSION}")
set(CPACK_PACKAGE_NAME ${PACKAGE_TARNAME})
set(CPACK_PACKAGE_VERSION ${PACKAGE_VERSION})
set(CPACK_PACKAGE_VERSION_MAJOR ${LIBXML_MAJOR_VERSION})
set(CPACK_PACKAGE_VERSION_MINOR ${LIBXML_MINOR_VERSION})
set(CPACK_PACKAGE_VERSION_PATCH ${LIBXML_MICRO_VERSION})
set(CPACK_RESOURCE_FILE_LICENSE ${CMAKE_CURRENT_SOURCE_DIR}/Copyright)
set(CPACK_RPM_COMPONENT_INSTALL ON)
set(CPACK_RPM_development_PACKAGE_NAME "${PACKAGE_NAME}-devel")
set(CPACK_RPM_development_PACKAGE_REQUIRES "${PACKAGE_NAME}")
set(CPACK_RPM_PACKAGE_GROUP "Development/Libraries")
set(CPACK_RPM_PACKAGE_NAME ${PACKAGE_TARNAME})
set(CPACK_RPM_PACKAGE_URL ${PACKAGE_URL})
set(CPACK_RPM_programs_PACKAGE_NAME "${PACKAGE_NAME}-utils")
set(CPACK_RPM_programs_PACKAGE_REQUIRES "${PACKAGE_NAME}")
set(CPACK_RPM_runtime_PACKAGE_NAME "${PACKAGE_NAME}")
set(CPACK_RPM_runtime_PACKAGE_SUGGESTS "${PACKAGE_NAME}-utils")

include(CPack)
