On 2020-02-15 Mario Emmenlauer wrote:
> This has been my foremost reason for cmake. While not everything is
> perfect, it has proven to work very well for generating native build
> instructions with well defined settings on quite a number of
> platforms.

CMake can be used like GNU Autotools to make portable builds but in this
case it's not practical to move the whole project to CMake. It should
be enough to have something that does as little as possible to produce
usable output for MSVC and ignore the portability features of CMake...

> I would start with the instructions from vcpkg (see
> https://github.com/microsoft/vcpkg/tree/master/ports/liblzma) but
> unify their multiple patches into a single, concise CMakeLists.txt.

...and these files seem to do that, and they only build liblzma. Thanks!

There are several details that I don't understand (yet). I see some
small minor problems though. liblzma_w32res.rc is missing from from
Windows DLL output. The "NOT WIN32" part looks a bit broken even though
it might compile. For example:

  - The definition of SIZEOF_SIZE_T is wrong as it's not understandable
    by the preprocessor but it's often not needed either so a compiler
    may not care.

  - Some #defines are missing that are needed for a good build on
    GNU/Linux.

I wonder why enable-uwp-builds.patch is needed. Is GetModuleHandle a
problem on UWP? If so, it can be changed to make things simpler.
Compatibility with Windows 98 isn't that important... ;-)

Since I would be maintaining the CMakeLists.txt file, I thought I should
understand it too. After reading some CMake docs and the linked vcpkg
files I ended up with the attached file. It does a little more than
just MSVC support so I can test it on GNU/Linux too. It only works
against xz.git, but if this can be finished quickly, a version can be
included in the upcoming XZ Utils 5.2.5.

Some known issues with my CMakeLists.txt:

  - The Windows parts aren't tested at all. The file header says
    32/64-bit x86/ARM but I tried only x86-64 GNU/Linux.

  - It obviously doesn't attempt to be properly portable since it
    assumes little endian etc. but that isn't the goal for now either.

  - I don't have much clue about the generated .cmake files. E.g.
    is the single-line liblzmaConfig.cmake fine or does it need
    something extra to handle the dependency on Threads::Theads?

  - The default installation paths come from GNUInstallDirs.
    The .cmake files are installed into a different directory
    than in the vcpkg files.

  - All #defines are passed as command line arguments. That is, it
    doesn't create or use config.h. The number of #defines isn't
    huge so it the command line lengths don't get too long, but
    is it bad/ugly on Windows?

  - NDEBUG shouldn't be #defined for debug builds.

Thoughts, fixes, suggestions etc. are welcome.

-- 
Lasse Collin  |  IRC: Larhzu @ IRCnet & Freenode
#############################################################################
#
# This provides very limited CMake support:
#   - Static or shared liblzma only (no command line tools or tests)
#   - GNU/Linux with GCC or Clang, or Windows with MSVC
#   - 32/64-bit x86 and 32/64-bit little endian ARM
#
# For now, this is indented to be useful on Windows. The GNU/Linux support
# is just for testing and shouldn't normally be used.
#
#############################################################################
#
# Author: Lasse Collin
#
# This file has been put into the public domain.
# You can do whatever you want with this file.
#
#############################################################################

cmake_minimum_required(VERSION 3.1)

# Get the package version from version.h into XZ_VERSION variable.
file(READ ${CMAKE_CURRENT_LIST_DIR}/src/liblzma/api/lzma/version.h XZ_VERSION)
string(REGEX REPLACE
"^.*\n\
#define LZMA_VERSION_MAJOR ([0-9]+)\n\
#define LZMA_VERSION_MINOR ([0-9]+)\n\
#define LZMA_VERSION_PATCH ([0-9]+)\n\
.*$"
       "\\1.\\2.\\3" XZ_VERSION ${XZ_VERSION})

# While this file only builds liblzma, name the project xz still in case
# some day this file builds more than liblzma. Among other things, this
# gives us variables xz_VERSION, xz_VERSION_MAJOR, and xz_HOMEPAGE_URL.
project(xz
        VERSION ${XZ_VERSION}
        DESCRIPTION "XZ Utils (liblzma only)"
        HOMEPAGE_URL "https://tukaani.org/xz/";
        LANGUAGES C
)

add_library(liblzma
    ${CMAKE_CURRENT_LIST_DIR}/src/common/tuklib_cpucores.c
    ${CMAKE_CURRENT_LIST_DIR}/src/common/tuklib_physmem.c
    ${CMAKE_CURRENT_LIST_DIR}/src/liblzma/check/check.c
    ${CMAKE_CURRENT_LIST_DIR}/src/liblzma/check/crc32_fast.c
    ${CMAKE_CURRENT_LIST_DIR}/src/liblzma/check/crc32_table.c
    ${CMAKE_CURRENT_LIST_DIR}/src/liblzma/check/crc64_fast.c
    ${CMAKE_CURRENT_LIST_DIR}/src/liblzma/check/crc64_table.c
    ${CMAKE_CURRENT_LIST_DIR}/src/liblzma/check/sha256.c
    ${CMAKE_CURRENT_LIST_DIR}/src/liblzma/common/alone_decoder.c
    ${CMAKE_CURRENT_LIST_DIR}/src/liblzma/common/alone_encoder.c
    ${CMAKE_CURRENT_LIST_DIR}/src/liblzma/common/auto_decoder.c
    ${CMAKE_CURRENT_LIST_DIR}/src/liblzma/common/block_buffer_decoder.c
    ${CMAKE_CURRENT_LIST_DIR}/src/liblzma/common/block_buffer_encoder.c
    ${CMAKE_CURRENT_LIST_DIR}/src/liblzma/common/block_decoder.c
    ${CMAKE_CURRENT_LIST_DIR}/src/liblzma/common/block_encoder.c
    ${CMAKE_CURRENT_LIST_DIR}/src/liblzma/common/block_header_decoder.c
    ${CMAKE_CURRENT_LIST_DIR}/src/liblzma/common/block_header_encoder.c
    ${CMAKE_CURRENT_LIST_DIR}/src/liblzma/common/block_util.c
    ${CMAKE_CURRENT_LIST_DIR}/src/liblzma/common/common.c
    ${CMAKE_CURRENT_LIST_DIR}/src/liblzma/common/easy_buffer_encoder.c
    ${CMAKE_CURRENT_LIST_DIR}/src/liblzma/common/easy_decoder_memusage.c
    ${CMAKE_CURRENT_LIST_DIR}/src/liblzma/common/easy_encoder.c
    ${CMAKE_CURRENT_LIST_DIR}/src/liblzma/common/easy_encoder_memusage.c
    ${CMAKE_CURRENT_LIST_DIR}/src/liblzma/common/easy_preset.c
    ${CMAKE_CURRENT_LIST_DIR}/src/liblzma/common/file_info.c
    ${CMAKE_CURRENT_LIST_DIR}/src/liblzma/common/filter_buffer_decoder.c
    ${CMAKE_CURRENT_LIST_DIR}/src/liblzma/common/filter_buffer_encoder.c
    ${CMAKE_CURRENT_LIST_DIR}/src/liblzma/common/filter_common.c
    ${CMAKE_CURRENT_LIST_DIR}/src/liblzma/common/filter_decoder.c
    ${CMAKE_CURRENT_LIST_DIR}/src/liblzma/common/filter_encoder.c
    ${CMAKE_CURRENT_LIST_DIR}/src/liblzma/common/filter_flags_decoder.c
    ${CMAKE_CURRENT_LIST_DIR}/src/liblzma/common/filter_flags_encoder.c
    ${CMAKE_CURRENT_LIST_DIR}/src/liblzma/common/hardware_cputhreads.c
    ${CMAKE_CURRENT_LIST_DIR}/src/liblzma/common/hardware_physmem.c
    ${CMAKE_CURRENT_LIST_DIR}/src/liblzma/common/index.c
    ${CMAKE_CURRENT_LIST_DIR}/src/liblzma/common/index_decoder.c
    ${CMAKE_CURRENT_LIST_DIR}/src/liblzma/common/index_encoder.c
    ${CMAKE_CURRENT_LIST_DIR}/src/liblzma/common/index_hash.c
    ${CMAKE_CURRENT_LIST_DIR}/src/liblzma/common/outqueue.c
    ${CMAKE_CURRENT_LIST_DIR}/src/liblzma/common/stream_buffer_decoder.c
    ${CMAKE_CURRENT_LIST_DIR}/src/liblzma/common/stream_buffer_encoder.c
    ${CMAKE_CURRENT_LIST_DIR}/src/liblzma/common/stream_decoder.c
    ${CMAKE_CURRENT_LIST_DIR}/src/liblzma/common/stream_encoder.c
    ${CMAKE_CURRENT_LIST_DIR}/src/liblzma/common/stream_encoder_mt.c
    ${CMAKE_CURRENT_LIST_DIR}/src/liblzma/common/stream_flags_common.c
    ${CMAKE_CURRENT_LIST_DIR}/src/liblzma/common/stream_flags_decoder.c
    ${CMAKE_CURRENT_LIST_DIR}/src/liblzma/common/stream_flags_encoder.c
    ${CMAKE_CURRENT_LIST_DIR}/src/liblzma/common/vli_decoder.c
    ${CMAKE_CURRENT_LIST_DIR}/src/liblzma/common/vli_encoder.c
    ${CMAKE_CURRENT_LIST_DIR}/src/liblzma/common/vli_size.c
    ${CMAKE_CURRENT_LIST_DIR}/src/liblzma/delta/delta_common.c
    ${CMAKE_CURRENT_LIST_DIR}/src/liblzma/delta/delta_decoder.c
    ${CMAKE_CURRENT_LIST_DIR}/src/liblzma/delta/delta_encoder.c
    ${CMAKE_CURRENT_LIST_DIR}/src/liblzma/lz/lz_decoder.c
    ${CMAKE_CURRENT_LIST_DIR}/src/liblzma/lz/lz_encoder.c
    ${CMAKE_CURRENT_LIST_DIR}/src/liblzma/lz/lz_encoder_mf.c
    ${CMAKE_CURRENT_LIST_DIR}/src/liblzma/lzma/fastpos_table.c
    ${CMAKE_CURRENT_LIST_DIR}/src/liblzma/lzma/lzma2_decoder.c
    ${CMAKE_CURRENT_LIST_DIR}/src/liblzma/lzma/lzma2_encoder.c
    ${CMAKE_CURRENT_LIST_DIR}/src/liblzma/lzma/lzma_decoder.c
    ${CMAKE_CURRENT_LIST_DIR}/src/liblzma/lzma/lzma_encoder.c
    ${CMAKE_CURRENT_LIST_DIR}/src/liblzma/lzma/lzma_encoder_optimum_fast.c
    ${CMAKE_CURRENT_LIST_DIR}/src/liblzma/lzma/lzma_encoder_optimum_normal.c
    ${CMAKE_CURRENT_LIST_DIR}/src/liblzma/lzma/lzma_encoder_presets.c
    ${CMAKE_CURRENT_LIST_DIR}/src/liblzma/rangecoder/price_table.c
    ${CMAKE_CURRENT_LIST_DIR}/src/liblzma/simple/arm.c
    ${CMAKE_CURRENT_LIST_DIR}/src/liblzma/simple/armthumb.c
    ${CMAKE_CURRENT_LIST_DIR}/src/liblzma/simple/ia64.c
    ${CMAKE_CURRENT_LIST_DIR}/src/liblzma/simple/powerpc.c
    ${CMAKE_CURRENT_LIST_DIR}/src/liblzma/simple/simple_coder.c
    ${CMAKE_CURRENT_LIST_DIR}/src/liblzma/simple/simple_decoder.c
    ${CMAKE_CURRENT_LIST_DIR}/src/liblzma/simple/simple_encoder.c
    ${CMAKE_CURRENT_LIST_DIR}/src/liblzma/simple/sparc.c
    ${CMAKE_CURRENT_LIST_DIR}/src/liblzma/simple/x86.c
)

target_include_directories(liblzma PRIVATE
    ${CMAKE_CURRENT_LIST_DIR}/src/liblzma/api
    ${CMAKE_CURRENT_LIST_DIR}/src/liblzma/common
    ${CMAKE_CURRENT_LIST_DIR}/src/liblzma/check
    ${CMAKE_CURRENT_LIST_DIR}/src/liblzma/lz
    ${CMAKE_CURRENT_LIST_DIR}/src/liblzma/rangecoder
    ${CMAKE_CURRENT_LIST_DIR}/src/liblzma/lzma
    ${CMAKE_CURRENT_LIST_DIR}/src/liblzma/delta
    ${CMAKE_CURRENT_LIST_DIR}/src/liblzma/simple
    ${CMAKE_CURRENT_LIST_DIR}/src/common
)

target_compile_definitions(liblzma PRIVATE
    # liblzma configuration:
    PACKAGE_NAME=XZ\ Utils
    PACKAGE_URL=${xz_HOMEPAGE_URL}
    TUKLIB_SYMBOL_PREFIX=lzma_
    ASSUME_RAM=128
    HAVE_CHECK_CRC32
    HAVE_CHECK_CRC64
    HAVE_CHECK_SHA256
    HAVE_DECODERS
    HAVE_DECODER_ARM
    HAVE_DECODER_ARMTHUMB
    HAVE_DECODER_DELTA
    HAVE_DECODER_IA64
    HAVE_DECODER_LZMA1
    HAVE_DECODER_LZMA2
    HAVE_DECODER_POWERPC
    HAVE_DECODER_SPARC
    HAVE_DECODER_X86
    HAVE_ENCODERS
    HAVE_ENCODER_ARM
    HAVE_ENCODER_ARMTHUMB
    HAVE_ENCODER_DELTA
    HAVE_ENCODER_IA64
    HAVE_ENCODER_LZMA1
    HAVE_ENCODER_LZMA2
    HAVE_ENCODER_POWERPC
    HAVE_ENCODER_SPARC
    HAVE_ENCODER_X86
    HAVE_MF_BT2
    HAVE_MF_BT3
    HAVE_MF_BT4
    HAVE_MF_HC3
    HAVE_MF_HC4

    # Standard headers and types are available:
    HAVE_STDBOOL_H
    HAVE__BOOL
    HAVE_STDINT_H
    HAVE_INTTYPES_H

    # Disable assert() checks.
    NDEBUG

    # Unaligned access is fast on x86(-64) and usually on 32/64-bit ARM too.
    TUKLIB_FAST_UNALIGNED_ACCESS
)

# Threading support:
set(THREADS_PREFER_PTHREAD_FLAG TRUE)
include(FindThreads)
target_link_libraries(liblzma Threads::Threads)
if(CMAKE_USE_WIN32_THREADS_INIT)
    target_compile_definitions(liblzma PRIVATE MYTHREAD_VISTA)
else()
    target_compile_definitions(liblzma PRIVATE MYTHREAD_POSIX)
endif()

# immintrin.h:
include(CheckIncludeFile)
check_include_file(immintrin.h HAVE_IMMINTRIN_H)
if(HAVE_IMMINTRIN_H)
    target_compile_definitions(liblzma PRIVATE HAVE_IMMINTRIN_H)

    # SSE2 intrinsics:
    include(CheckCSourceCompiles)
    check_c_source_compiles("
            #include <immintrin.h>
            int main(void)
            {
                __m128i x = { 0 };
                _mm_movemask_epi8(x);
                return 0;
            }
        "
        HAVE__MM_MOVEMASK_EPI8)
    if(HAVE__MM_MOVEMASK_EPI8)
        target_compile_definitions(liblzma PRIVATE HAVE__MM_MOVEMASK_EPI8)
    endif()
endif()

# Support -fvisiblity=hidden. These lines do nothing on Windows (even under
# Cygwin) so it's safe to enable this unconditionally.
set_target_properties(liblzma PROPERTIES C_VISIBILITY_PRESET hidden)
target_compile_definitions(liblzma PRIVATE HAVE_VISIBILITY=1)

if(WIN32)
    if(BUILD_SHARED_LIBS)
        # Add the Windows resource file for liblzma.dll.
        target_sources(liblzma PRIVATE
            ${CMAKE_CURRENT_LIST_DIR}/src/liblzma/liblzma_w32res.rc
        )

        # Export the public API symbols with __declspec(dllexport).
        target_compile_definitions(liblzma PRIVATE DLL_EXPORT)
    else()
        # Disable __declspec(dllimport) when linking against static liblzma.
        target_compile_definitions(liblzma INTERFACE LZMA_API_STATIC)
    endif()
else()
    # These are specific to GNU/Linux:
    target_compile_definitions(liblzma PRIVATE
        _GNU_SOURCE
        TUKLIB_CPUCORES_SCHED_GETAFFINITY
        TUKLIB_PHYSMEM_SYSCONF
        HAVE_CLOCK_GETTIME
        HAVE_DECL_CLOCK_MONOTONIC
        HAVE_PTHREAD_CONDATTR_SETCLOCK
    )

    # Symbol versioning for shared liblzma. This doesn't affect static builds.
    target_link_options(liblzma PRIVATE
        -Wl,--version-script=${CMAKE_CURRENT_LIST_DIR}/src/liblzma/liblzma.map
    )
    set_target_properties(liblzma PROPERTIES
        LINK_DEPENDS ${CMAKE_CURRENT_LIST_DIR}/src/liblzma/liblzma.map
    )
endif()

# Options for new enough GCC or Clang on any arch or operating system:
if(NOT MSVC)
    target_compile_definitions(liblzma PRIVATE
        HAVE___BUILTIN_ASSUME_ALIGNED
        HAVE___BUILTIN_BSWAPXX
    )

    # configure.ac has a long list but it won't be copied here:
    target_compile_options(liblzma PRIVATE -Wall -Wextra)
endif()

set_target_properties(liblzma PROPERTIES
    # At least for now the package versioning matches the rules used for
    # shared library versioning (excluding development releases) so it is
    # fine to use the package version here.
    SOVERSION ${xz_VERSION_MAJOR}
    VERSION ${xz_VERSION}

    # It's liblzma.dll, liblzma.lib, liblzma.a, ... but not libliblzma.a:
    PREFIX ""
)

# Create liblzmaConfigVersion.cmake.
include(CMakePackageConfigHelpers)
write_basic_package_version_file(
    ${CMAKE_CURRENT_BINARY_DIR}/liblzmaConfigVersion.cmake
    VERSION ${liblzma_VERSION}
    COMPATIBILITY SameMajorVersion)

# Create liblzmaConfig.cmake.
file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/liblzmaConfig.cmake
     "include(\"\${CMAKE_CURRENT_LIST_DIR}/liblzmaTargets.cmake\")")

# Set CMAKE_INSTALL_LIBDIR and friends.
include(GNUInstallDirs)

# Install the library binary. The INCLUDES specifies the include path that
# is exported for other projects to use but it doesn't install any files.
install(TARGETS liblzma EXPORT liblzmaTargets
        RUNTIME  DESTINATION ${CMAKE_INSTALL_BINDIR}
        LIBRARY  DESTINATION ${CMAKE_INSTALL_LIBDIR}
        ARCHIVE  DESTINATION ${CMAKE_INSTALL_LIBDIR}
        INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})

# Install the liblzma API headers. These use a subdirectory so
# this has to be done as a separate step.
install(DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/src/liblzma/api/
        DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
        FILES_MATCHING PATTERN "*.h")

# Install the CMake files that other packages can use to find liblzma.
install(EXPORT liblzmaTargets
        NAMESPACE liblzma::
        FILE liblzmaTargets.cmake
        DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/liblzma)

install(FILES ${CMAKE_CURRENT_BINARY_DIR}/liblzmaConfig.cmake
              ${CMAKE_CURRENT_BINARY_DIR}/liblzmaConfigVersion.cmake
        DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/liblzma)

Reply via email to