xz/CMakeLists.txt

2097 lines
76 KiB
CMake
Raw Normal View History

# SPDX-License-Identifier: 0BSD
#############################################################################
#
# CMake support for building XZ Utils
#
# The complete CMake-based build hasn't been tested much yet and
# thus it's still slightly experimental. Testing this especially
# outside GNU/Linux and Windows would be great now.
#
# A few things are still missing compared to the Autotools-based build:
#
# - A few tests aren't CMake compatible yet and thus aren't run!
#
# - 32-bit x86 assembly code for CRC32 and CRC64 isn't used.
#
# - External SHA-256 code isn't supported but it's disabled by
# default in the Autotools build too (--enable-external-sha256).
#
# - Extra compiler warning flags aren't added by default.
#
# About CMAKE_BUILD_TYPE:
#
# - CMake's standard choices are fine to use for production builds,
# including "Release" and "RelWithDebInfo".
#
# NOTE: While "Release" uses -O3 by default with some compilers,
# this file overrides -O3 to -O2 for "Release" builds if
# CMAKE_C_FLAGS_RELEASE is not defined by the user. At least
# with GCC and Clang/LLVM, -O3 doesn't seem useful for this
# package as it can result in bigger binaries without any
# improvement in speed compared to -O2.
#
# - Empty value (the default) is handled slightly specially: It
# adds -DNDEBUG to disable debugging code (assert() and a few
# other things). No optimization flags are added so an empty
# CMAKE_BUILD_TYPE is an easy way to build with whatever
# optimization flags one wants, and so this method is also
# suitable for production builds.
#
# If debugging is wanted when using empty CMAKE_BUILD_TYPE,
# include -UNDEBUG in the CFLAGS environment variable or
# in the CMAKE_C_FLAGS CMake variable to override -DNDEBUG.
# With empty CMAKE_BUILD_TYPE, the -UNDEBUG option will go
# after the -DNDEBUG option on the compiler command line and
# thus NDEBUG will be undefined.
#
# - Non-standard build types like "None" aren't treated specially
# and thus won't have -DNEBUG. Such non-standard build types
# SHOULD BE AVOIDED FOR PRODUCTION BUILDS. Or at least one
# should remember to add -DNDEBUG.
#
# If building from xz.git instead of a release tarball, consider
# the following *before* running cmake:
#
# - To get translated messages, install GNU gettext tools (the
# command msgfmt is needed). Alternatively disable translations
# by setting ENABLE_NLS=OFF.
#
# - To get translated man pages, run po4a/update-po which requires
# the po4a tool. The build works without this step too.
#
# - To get Doxygen-generated liblzma API docs in HTML format,
# run doxygen/update-doxygen which requires the doxygen tool.
# The build works without this step too.
#
# This file provides the following installation components (if you only
# need liblzma, install only its components!):
# - liblzma_Runtime (shared library only)
# - liblzma_Development
2024-02-17 16:10:40 +00:00
# - liblzma_Documentation (examples and Doxygen-generated API docs as HTML)
2024-02-19 10:20:59 +00:00
# - xz_Runtime (xz, the symlinks, and possibly translation files)
# - xz_Documentation (xz man pages and the symlinks)
# - xzdec_Runtime
# - xzdec_Documentation (xzdec *and* lzmadec man pages)
# - lzmadec_Runtime
# - lzmainfo_Runtime
# - lzmainfo_Documentation (lzmainfo man pages)
# - scripts_Runtime (xzdiff, xzgrep, xzless, xzmore)
# - scripts_Documentation (their man pages)
# - Documentation (generic docs like README and licenses)
#
# To find the target liblzma::liblzma from other packages, use the CONFIG
# option with find_package() to avoid a conflict with the FindLibLZMA module
# with case-insensitive file systems. For example, to require liblzma 5.2.5
# or a newer compatible version:
#
# find_package(liblzma 5.2.5 REQUIRED CONFIG)
# target_link_libraries(my_application liblzma::liblzma)
#
#############################################################################
#
# Author: Lasse Collin
#
#############################################################################
# NOTE: Translation support is disabled with CMake older than 3.20.
cmake_minimum_required(VERSION 3.14...3.28 FATAL_ERROR)
include(CMakePushCheckState)
liblzma: Add fast CRC64 for 32/64-bit x86 using SSSE3 + SSE4.1 + CLMUL. It also works on E2K as it supports these intrinsics. On x86-64 runtime detection is used so the code keeps working on older processors too. A CLMUL-only build can be done by using -msse4.1 -mpclmul in CFLAGS and this will reduce the library size since the generic implementation and its 8 KiB lookup table will be omitted. On 32-bit x86 this isn't used by default for now because by default on 32-bit x86 the separate assembly file crc64_x86.S is used. If --disable-assembler is used then this new CLMUL code is used the same way as on 64-bit x86. However, a CLMUL-only build (-msse4.1 -mpclmul) won't omit the 8 KiB lookup table on 32-bit x86 due to a currently-missing check for disabled assembler usage. The configure.ac check should be such that the code won't be built if something in the toolchain doesn't support it but --disable-clmul-crc option can be used to unconditionally disable this feature. CLMUL speeds up decompression of files that have compressed very well (assuming CRC64 is used as a check type). It is know that the CLMUL code is significantly slower than the generic code for tiny inputs (especially 1-8 bytes but up to 16 bytes). If that is a real-world problem then there is already a commented-out variant that uses the generic version for small inputs. Thanks to Ilya Kurdyukov for the original patch which was derived from a white paper from Intel [1] (published in 2009) and public domain code from [2] (released in 2016). [1] https://www.intel.com/content/dam/www/public/us/en/documents/white-papers/fast-crc-computation-generic-polynomials-pclmulqdq-paper.pdf [2] https://github.com/rawrunprotected/crc
2022-11-14 19:34:57 +00:00
include(CheckIncludeFile)
include(CheckSymbolExists)
include(CheckStructHasMember)
liblzma: Add fast CRC64 for 32/64-bit x86 using SSSE3 + SSE4.1 + CLMUL. It also works on E2K as it supports these intrinsics. On x86-64 runtime detection is used so the code keeps working on older processors too. A CLMUL-only build can be done by using -msse4.1 -mpclmul in CFLAGS and this will reduce the library size since the generic implementation and its 8 KiB lookup table will be omitted. On 32-bit x86 this isn't used by default for now because by default on 32-bit x86 the separate assembly file crc64_x86.S is used. If --disable-assembler is used then this new CLMUL code is used the same way as on 64-bit x86. However, a CLMUL-only build (-msse4.1 -mpclmul) won't omit the 8 KiB lookup table on 32-bit x86 due to a currently-missing check for disabled assembler usage. The configure.ac check should be such that the code won't be built if something in the toolchain doesn't support it but --disable-clmul-crc option can be used to unconditionally disable this feature. CLMUL speeds up decompression of files that have compressed very well (assuming CRC64 is used as a check type). It is know that the CLMUL code is significantly slower than the generic code for tiny inputs (especially 1-8 bytes but up to 16 bytes). If that is a real-world problem then there is already a commented-out variant that uses the generic version for small inputs. Thanks to Ilya Kurdyukov for the original patch which was derived from a white paper from Intel [1] (published in 2009) and public domain code from [2] (released in 2016). [1] https://www.intel.com/content/dam/www/public/us/en/documents/white-papers/fast-crc-computation-generic-polynomials-pclmulqdq-paper.pdf [2] https://github.com/rawrunprotected/crc
2022-11-14 19:34:57 +00:00
include(CheckCSourceCompiles)
include(cmake/tuklib_large_file_support.cmake)
include(cmake/tuklib_integer.cmake)
include(cmake/tuklib_cpucores.cmake)
include(cmake/tuklib_physmem.cmake)
include(cmake/tuklib_progname.cmake)
include(cmake/tuklib_mbstr.cmake)
set(PACKAGE_NAME "XZ Utils")
set(PACKAGE_BUGREPORT "xz@tukaani.org")
2024-01-19 15:08:14 +00:00
set(PACKAGE_URL "https://xz.tukaani.org/xz-utils/")
# Get the package version from version.h into PACKAGE_VERSION variable.
file(READ src/liblzma/api/lzma/version.h PACKAGE_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" PACKAGE_VERSION "${PACKAGE_VERSION}")
# With several compilers, CMAKE_BUILD_TYPE=Release uses -O3 optimization
# which results in bigger code without a clear difference in speed. If
# no user-defined CMAKE_C_FLAGS_RELEASE is present, override -O3 to -O2
# to make it possible to recommend CMAKE_BUILD_TYPE=Release.
if(NOT DEFINED CMAKE_C_FLAGS_RELEASE)
set(OVERRIDE_O3_IN_C_FLAGS_RELEASE ON)
endif()
# Among other things, this gives us variables xz_VERSION and xz_VERSION_MAJOR.
project(xz VERSION "${PACKAGE_VERSION}" LANGUAGES C)
if(OVERRIDE_O3_IN_C_FLAGS_RELEASE)
# Looking at CMake's source, there aren't any _FLAGS_RELEASE_INIT
# entries where "-O3" would appear as part of some other option,
# thus a simple search and replace should be fine.
string(REPLACE -O3 -O2 CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE}")
# Update the cache value while keeping its docstring unchanged.
set_property(CACHE CMAKE_C_FLAGS_RELEASE
PROPERTY VALUE "${CMAKE_C_FLAGS_RELEASE}")
endif()
# We need a compiler that supports enough C99 or newer (variable-length arrays
# aren't needed, those are optional in C17). Setting CMAKE_C_STANDARD here
# makes it the default for all targets. It doesn't affect the INTERFACE so
# liblzma::liblzma won't end up with INTERFACE_COMPILE_FEATURES "c_std_99"
# (the API headers are C89 and C++ compatible).
set(CMAKE_C_STANDARD 99)
set(CMAKE_C_STANDARD_REQUIRED ON)
# On Apple OSes, don't build executables as bundles:
set(CMAKE_MACOSX_BUNDLE OFF)
# Set CMAKE_INSTALL_LIBDIR and friends. This needs to be done before
# the LOCALEDIR_DEFINITION workaround below.
include(GNUInstallDirs)
# windres from GNU binutils can be tricky with command line arguments
# that contain spaces or other funny characters. Unfortunately we need
# a space in PACKAGE_NAME. Using \x20 to encode the US-ASCII space seems
# to work in both cmd.exe and /bin/sh.
#
# However, even \x20 isn't enough in all situations, resulting in
# "syntax error" from windres. Using --use-temp-file prevents windres
# from using popen() and this seems to fix the problem.
#
# llvm-windres from Clang/LLVM 16.0.6 and older: The \x20 results
# in "XZx20Utils" in the compiled binary. The option --use-temp-file
# makes no difference.
#
# llvm-windres 17.0.0 and later: It emulates GNU windres more accurately, so
# the workarounds used with GNU windres must be used with llvm-windres too.
#
# CMake 3.27 doesn't have CMAKE_RC_COMPILER_ID so we rely on
# CMAKE_C_COMPILER_ID.
if((MINGW OR CYGWIN OR MSYS) AND (
NOT CMAKE_C_COMPILER_ID STREQUAL "Clang" OR
CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL "17"))
# Use workarounds with GNU windres and llvm-windres >= 17.0.0. The \x20
# in PACKAGE_NAME_DEFINITION works with gcc and clang too so we don't need
# to worry how to pass different flags to windres and the C compiler.
# Keep the original PACKAGE_NAME intact for generation of liblzma.pc.
string(APPEND CMAKE_RC_FLAGS " --use-temp-file")
string(REPLACE " " "\\x20" PACKAGE_NAME_DEFINITION "${PACKAGE_NAME}")
# Use octal because "Program Files" would become \x20F.
string(REPLACE " " "\\040" LOCALEDIR_DEFINITION
"${CMAKE_INSTALL_FULL_LOCALEDIR}")
else()
# Elsewhere a space is safe. This also keeps things compatible with
# EBCDIC in case CMake-based build is ever done on such a system.
set(PACKAGE_NAME_DEFINITION "${PACKAGE_NAME}")
set(LOCALEDIR_DEFINITION "${CMAKE_INSTALL_FULL_LOCALEDIR}")
endif()
# Definitions common to all targets:
add_compile_definitions(
# Package info:
PACKAGE_NAME="${PACKAGE_NAME_DEFINITION}"
PACKAGE_BUGREPORT="${PACKAGE_BUGREPORT}"
PACKAGE_URL="${PACKAGE_URL}"
# Standard headers and types are available:
HAVE_STDBOOL_H
HAVE__BOOL
HAVE_STDINT_H
HAVE_INTTYPES_H
# Always enable CRC32 since liblzma should never build without it.
HAVE_CHECK_CRC32
# Disable assert() checks when no build type has been specified. Non-empty
# build types like "Release" and "Debug" handle this by default.
$<$<CONFIG:>:NDEBUG>
)
######################
# System definitions #
######################
# _GNU_SOURCE and such definitions. This specific macro is special since
# it also adds the definitions to CMAKE_REQUIRED_DEFINITIONS.
tuklib_use_system_extensions(ALL)
# Check for large file support. It's required on some 32-bit platforms and
# even on 64-bit MinGW-w64 to get 64-bit off_t. This can be forced off on
# the CMake command line if needed: -DLARGE_FILE_SUPPORT=OFF
tuklib_large_file_support(ALL)
# This is needed by liblzma and xz.
tuklib_integer(ALL)
# This is used for liblzma.pc generation to add -lrt if needed.
set(LIBS)
# Check for clock_gettime(). Do this before checking for threading so
# that we know there if CLOCK_MONOTONIC is available.
check_symbol_exists(clock_gettime time.h HAVE_CLOCK_GETTIME)
if(NOT HAVE_CLOCK_GETTIME)
# With glibc <= 2.17 or Solaris 10 this needs librt.
# Add librt for the next check for HAVE_CLOCK_GETTIME. If it is
# found after including the library, we know that librt is required.
list(INSERT CMAKE_REQUIRED_LIBRARIES 0 rt)
check_symbol_exists(clock_gettime time.h HAVE_CLOCK_GETTIME_LIBRT)
# If it was found now, add librt to all targets and keep it in
# CMAKE_REQUIRED_LIBRARIES for further tests too.
if(HAVE_CLOCK_GETTIME_LIBRT)
link_libraries(rt)
set(LIBS "-lrt") # For liblzma.pc
else()
list(REMOVE_AT CMAKE_REQUIRED_LIBRARIES 0)
endif()
endif()
if(HAVE_CLOCK_GETTIME OR HAVE_CLOCK_GETTIME_LIBRT)
add_compile_definitions(HAVE_CLOCK_GETTIME)
# Check if CLOCK_MONOTONIC is available for clock_gettime().
check_symbol_exists(CLOCK_MONOTONIC time.h HAVE_CLOCK_MONOTONIC)
tuklib_add_definition_if(ALL HAVE_CLOCK_MONOTONIC)
endif()
# Translation support requires CMake 3.20 because it added the Intl::Intl
# target so we don't need to play with the individual variables.
#
2024-02-25 13:41:55 +00:00
# The definition ENABLE_NLS is added only to those targets that use it, thus
# it's not done here. (xz has translations, xzdec doesn't.)
if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.20")
find_package(Intl)
find_package(Gettext)
if(Intl_FOUND)
option(ENABLE_NLS "Native Language Support (translated messages)" ON)
# If translation support is enabled but neither gettext tools or
# pre-generated .gmo files exist, translation support cannot be
# enabled.
#
# The detection of pre-generated .gmo files is done by only
# checking for the existence of a single .gmo file; Ukrainian
# is one of many translations that gets regular updates.
if(ENABLE_NLS AND NOT GETTEXT_FOUND AND
NOT EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/po/uk.gmo")
# This only sets the variable, not the cache variable!
set(ENABLE_NLS OFF)
# This message is shown only when new enough CMake is used and
# library support for translations was found. The assumptions is
# that in this situation the user might have interest in the
# translations. This also keeps this code simpler.
message(WARNING "Native language support (NLS) has been disabled. "
"NLS support requires either gettext tools or "
"pre-generated .gmo files. The latter are only "
"available in distribution tarballs. "
"To avoid this warning, NLS can be explicitly "
"disabled by passing -DENABLE_NLS=OFF to cmake.")
endif()
# Warn if NLS is enabled but translated man pages are missing.
if(UNIX AND ENABLE_NLS AND
NOT EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/po4a/man")
message(WARNING "Native language support (NLS) has been enabled "
"but pre-generated translated man pages "
"were not found and thus they won't be installed. "
"Run 'po4a/update-po' to generate them.")
endif()
# The *installed* name of the translation files is "xz.mo".
set(TRANSLATION_DOMAIN "xz")
endif()
endif()
# Options for new enough GCC or Clang on any arch or operating system:
if(CMAKE_C_COMPILER_ID MATCHES GNU|Clang)
# configure.ac has a long list but it won't be copied here:
add_compile_options(-Wall -Wextra)
endif()
#############################################################################
# liblzma
#############################################################################
option(BUILD_SHARED_LIBS "Build liblzma as a shared library instead of static")
add_library(liblzma
src/common/mythread.h
src/common/sysdefs.h
src/common/tuklib_common.h
src/common/tuklib_config.h
src/common/tuklib_integer.h
src/common/tuklib_physmem.c
src/common/tuklib_physmem.h
src/liblzma/api/lzma.h
src/liblzma/api/lzma/base.h
src/liblzma/api/lzma/bcj.h
src/liblzma/api/lzma/block.h
src/liblzma/api/lzma/check.h
src/liblzma/api/lzma/container.h
src/liblzma/api/lzma/delta.h
src/liblzma/api/lzma/filter.h
src/liblzma/api/lzma/hardware.h
src/liblzma/api/lzma/index.h
src/liblzma/api/lzma/index_hash.h
src/liblzma/api/lzma/lzma12.h
src/liblzma/api/lzma/stream_flags.h
src/liblzma/api/lzma/version.h
src/liblzma/api/lzma/vli.h
src/liblzma/check/check.c
src/liblzma/check/check.h
src/liblzma/check/crc_common.h
liblzma: Avoid extern lzma_crc32_clmul() and lzma_crc64_clmul(). A CLMUL-only build will have the crcxx_clmul() inlined into lzma_crcxx(). Previously a jump to the extern lzma_crcxx_clmul() was needed. Notes about shared liblzma on ELF platforms: - On platforms that support ifunc and -fvisibility=hidden, this was silly because CLMUL-only build would have that single extra jump instruction of extra overhead. - On platforms that support neither -fvisibility=hidden nor linker version script (liblzma*.map), jumping to lzma_crcxx_clmul() would go via PLT so a few more instructions of overhead (still not a big issue but silly nevertheless). There was a downside with static liblzma too: if an application only needs lzma_crc64(), static linking would make the linker include the CLMUL code for both CRC32 and CRC64 from crc_x86_clmul.o even though the CRC32 code wouldn't be needed, thus increasing code size of the executable (assuming that -ffunction-sections isn't used). Also, now compilers are likely to inline crc_simd_body() even if they don't support the always_inline attribute (or MSVC's __forceinline). Quite possibly all compilers that build the code do support such an attribute. But now it likely isn't a problem even if the attribute wasn't supported. Now all x86-specific stuff is in crc_x86_clmul.h. If other archs The other archs can then have their own headers with their own is_clmul_supported() and crcxx_clmul(). Another bonus is that the build system doesn't need to care if crc_clmul.c is needed. is_clmul_supported() stays as inline function as it's not needed when doing a CLMUL-only build (avoids a warning about unused function).
2023-10-20 20:35:10 +00:00
src/liblzma/check/crc_x86_clmul.h
src/liblzma/check/crc32_arm64.h
src/liblzma/common/block_util.c
src/liblzma/common/common.c
src/liblzma/common/common.h
src/liblzma/common/easy_preset.c
src/liblzma/common/easy_preset.h
src/liblzma/common/filter_common.c
src/liblzma/common/filter_common.h
src/liblzma/common/hardware_physmem.c
src/liblzma/common/index.c
src/liblzma/common/index.h
src/liblzma/common/memcmplen.h
src/liblzma/common/stream_flags_common.c
src/liblzma/common/stream_flags_common.h
src/liblzma/common/string_conversion.c
src/liblzma/common/vli_size.c
)
target_include_directories(liblzma PRIVATE
src/liblzma/api
src/liblzma/common
src/liblzma/check
src/liblzma/lz
src/liblzma/rangecoder
src/liblzma/lzma
src/liblzma/delta
src/liblzma/simple
src/common
)
######################
# Size optimizations #
######################
option(ENABLE_SMALL "Reduce code size at expense of speed. \
This may be useful together with CMAKE_BUILD_TYPE=MinSizeRel.")
if(ENABLE_SMALL)
add_compile_definitions(HAVE_SMALL)
endif()
##########
# Checks #
##########
set(ADDITIONAL_SUPPORTED_CHECKS crc64 sha256)
set(ADDITIONAL_CHECK_TYPES "${ADDITIONAL_SUPPORTED_CHECKS}" CACHE STRING
"Additional check types to support (crc32 is always built)")
foreach(CHECK IN LISTS ADDITIONAL_CHECK_TYPES)
if(NOT CHECK IN_LIST ADDITIONAL_SUPPORTED_CHECKS)
message(FATAL_ERROR "'${CHECK}' is not a supported check type")
endif()
endforeach()
if(ENABLE_SMALL)
target_sources(liblzma PRIVATE src/liblzma/check/crc32_small.c)
else()
target_sources(liblzma PRIVATE
src/liblzma/check/crc32_fast.c
src/liblzma/check/crc32_table.c
src/liblzma/check/crc32_table_be.h
src/liblzma/check/crc32_table_le.h
)
endif()
if("crc64" IN_LIST ADDITIONAL_CHECK_TYPES)
add_compile_definitions("HAVE_CHECK_CRC64")
if(ENABLE_SMALL)
target_sources(liblzma PRIVATE src/liblzma/check/crc64_small.c)
else()
target_sources(liblzma PRIVATE
src/liblzma/check/crc64_fast.c
src/liblzma/check/crc64_table.c
src/liblzma/check/crc64_table_be.h
src/liblzma/check/crc64_table_le.h
)
endif()
endif()
if("sha256" IN_LIST ADDITIONAL_CHECK_TYPES)
add_compile_definitions("HAVE_CHECK_SHA256")
target_sources(liblzma PRIVATE src/liblzma/check/sha256.c)
endif()
#################
# Match finders #
#################
set(SUPPORTED_MATCH_FINDERS hc3 hc4 bt2 bt3 bt4)
set(MATCH_FINDERS "${SUPPORTED_MATCH_FINDERS}" CACHE STRING
"Match finders to support (at least one is required for LZMA1 or LZMA2)")
foreach(MF IN LISTS MATCH_FINDERS)
if(MF IN_LIST SUPPORTED_MATCH_FINDERS)
string(TOUPPER "${MF}" MF_UPPER)
add_compile_definitions("HAVE_MF_${MF_UPPER}")
else()
message(FATAL_ERROR "'${MF}' is not a supported match finder")
endif()
endforeach()
#############
# Threading #
#############
# Supported threading methods:
# ON - autodetect the best threading method. The autodetection will
# prefer Windows threading (win95 or vista) over posix if both are
# available. vista threads will be used over win95 unless it is a
# 32-bit build.
# OFF - Disable threading.
# posix - Use posix threading (pthreads), or throw an error if not available.
# win95 - Use Windows win95 threading, or throw an error if not available.
# vista - Use Windows vista threading, or throw an error if not available.
set(SUPPORTED_THREADING_METHODS ON OFF posix win95 vista)
set(ENABLE_THREADS ON CACHE STRING
"Threading method: Set to 'ON' to autodetect, 'OFF' to disable threading.")
# Create dropdown in CMake GUI since only 1 threading method is possible
# to select in a build.
set_property(CACHE ENABLE_THREADS
PROPERTY STRINGS "${SUPPORTED_THREADING_METHODS}")
# This is a flag variable set when win95 threads are used. We must ensure
# the combination of enable_small and win95 threads is not used without a
# compiler supporting attribute __constructor__.
set(USE_WIN95_THREADS OFF)
# This is a flag variable set when posix threads (pthreads) are used.
# It's needed when creating liblzma-config.cmake where dependency on
# Threads::Threads is only needed with pthreads.
set(USE_POSIX_THREADS OFF)
if(NOT ENABLE_THREADS IN_LIST SUPPORTED_THREADING_METHODS)
message(FATAL_ERROR "'${ENABLE_THREADS}' is not a supported "
"threading method")
endif()
if(ENABLE_THREADS)
# Also set THREADS_PREFER_PTHREAD_FLAG since the flag has no effect
# for Windows threading.
set(THREADS_PREFER_PTHREAD_FLAG TRUE)
find_package(Threads REQUIRED)
# If both Windows and posix threading are available, prefer Windows.
# Note that on Cygwin CMAKE_USE_WIN32_THREADS_INIT is false.
if(CMAKE_USE_WIN32_THREADS_INIT AND NOT ENABLE_THREADS STREQUAL "posix")
if(ENABLE_THREADS STREQUAL "win95"
OR (ENABLE_THREADS STREQUAL "ON"
AND CMAKE_SIZEOF_VOID_P EQUAL 4))
# Use Windows 95 (and thus XP) compatible threads.
# This avoids use of features that were added in
# Windows Vista. This is used for 32-bit x86 builds for
# compatibility reasons since it makes no measurable difference
# in performance compared to Vista threads.
set(USE_WIN95_THREADS ON)
add_compile_definitions(MYTHREAD_WIN95)
else()
add_compile_definitions(MYTHREAD_VISTA)
endif()
elseif(CMAKE_USE_PTHREADS_INIT)
if(ENABLE_THREADS STREQUAL "posix" OR ENABLE_THREADS STREQUAL "ON")
# The threading library only needs to be explicitly linked
# for posix threads, so this is needed for creating
# liblzma-config.cmake later.
set(USE_POSIX_THREADS ON)
target_link_libraries(liblzma Threads::Threads)
add_compile_definitions(MYTHREAD_POSIX)
# Check if pthread_condattr_setclock() exists to
# use CLOCK_MONOTONIC.
if(HAVE_CLOCK_MONOTONIC)
list(INSERT CMAKE_REQUIRED_LIBRARIES 0
"${CMAKE_THREAD_LIBS_INIT}")
check_symbol_exists(pthread_condattr_setclock pthread.h
HAVE_PTHREAD_CONDATTR_SETCLOCK)
tuklib_add_definition_if(ALL HAVE_PTHREAD_CONDATTR_SETCLOCK)
endif()
else()
message(SEND_ERROR
"Windows threading method was requested but a compatible "
"library could not be found")
endif()
else()
message(SEND_ERROR "No supported threading library found")
endif()
target_sources(liblzma PRIVATE
src/common/tuklib_cpucores.c
src/common/tuklib_cpucores.h
src/liblzma/common/hardware_cputhreads.c
src/liblzma/common/outqueue.c
src/liblzma/common/outqueue.h
)
endif()
############
# Encoders #
############
set(SIMPLE_FILTERS
x86
arm
armthumb
arm64
powerpc
ia64
sparc
riscv
)
# The SUPPORTED_FILTERS are shared between Encoders and Decoders
# since only lzip does not appear in both lists. lzip is a special
# case anyway, so it is handled separately in the Decoders section.
set(SUPPORTED_FILTERS
lzma1
lzma2
delta
"${SIMPLE_FILTERS}"
)
set(ENCODERS "${SUPPORTED_FILTERS}" CACHE STRING "Encoders to support")
# If LZMA2 is enabled, then LZMA1 must also be enabled.
if(NOT "lzma1" IN_LIST ENCODERS AND "lzma2" IN_LIST ENCODERS)
message(FATAL_ERROR "LZMA2 encoder requires that LZMA1 is also enabled")
endif()
# If LZMA1 is enabled, then at least one match finder must be enabled.
if(MATCH_FINDERS STREQUAL "" AND "lzma1" IN_LIST ENCODERS)
message(FATAL_ERROR "At least 1 match finder is required for an "
"LZ-based encoder")
endif()
set(HAVE_DELTA_CODER OFF)
set(SIMPLE_ENCODERS OFF)
set(HAVE_ENCODERS OFF)
foreach(ENCODER IN LISTS ENCODERS)
if(ENCODER IN_LIST SUPPORTED_FILTERS)
set(HAVE_ENCODERS ON)
if(NOT SIMPLE_ENCODERS AND ENCODER IN_LIST SIMPLE_FILTERS)
set(SIMPLE_ENCODERS ON)
endif()
string(TOUPPER "${ENCODER}" ENCODER_UPPER)
add_compile_definitions("HAVE_ENCODER_${ENCODER_UPPER}")
else()
message(FATAL_ERROR "'${ENCODER}' is not a supported encoder")
endif()
endforeach()
if(HAVE_ENCODERS)
add_compile_definitions(HAVE_ENCODERS)
target_sources(liblzma PRIVATE
src/liblzma/common/alone_encoder.c
src/liblzma/common/block_buffer_encoder.c
src/liblzma/common/block_buffer_encoder.h
src/liblzma/common/block_encoder.c
src/liblzma/common/block_encoder.h
src/liblzma/common/block_header_encoder.c
src/liblzma/common/easy_buffer_encoder.c
src/liblzma/common/easy_encoder.c
src/liblzma/common/easy_encoder_memusage.c
src/liblzma/common/filter_buffer_encoder.c
src/liblzma/common/filter_encoder.c
src/liblzma/common/filter_encoder.h
src/liblzma/common/filter_flags_encoder.c
src/liblzma/common/index_encoder.c
src/liblzma/common/index_encoder.h
src/liblzma/common/stream_buffer_encoder.c
src/liblzma/common/stream_encoder.c
src/liblzma/common/stream_flags_encoder.c
src/liblzma/common/vli_encoder.c
)
if(ENABLE_THREADS)
target_sources(liblzma PRIVATE
src/liblzma/common/stream_encoder_mt.c
)
endif()
if(SIMPLE_ENCODERS)
target_sources(liblzma PRIVATE
src/liblzma/simple/simple_encoder.c
src/liblzma/simple/simple_encoder.h
)
endif()
if("lzma1" IN_LIST ENCODERS)
target_sources(liblzma PRIVATE
src/liblzma/lzma/lzma_encoder.c
src/liblzma/lzma/lzma_encoder.h
src/liblzma/lzma/lzma_encoder_optimum_fast.c
src/liblzma/lzma/lzma_encoder_optimum_normal.c
src/liblzma/lzma/lzma_encoder_private.h
src/liblzma/lzma/fastpos.h
src/liblzma/lz/lz_encoder.c
src/liblzma/lz/lz_encoder.h
src/liblzma/lz/lz_encoder_hash.h
src/liblzma/lz/lz_encoder_hash_table.h
src/liblzma/lz/lz_encoder_mf.c
src/liblzma/rangecoder/price.h
src/liblzma/rangecoder/price_table.c
src/liblzma/rangecoder/range_encoder.h
)
if(NOT ENABLE_SMALL)
target_sources(liblzma PRIVATE src/liblzma/lzma/fastpos_table.c)
endif()
endif()
if("lzma2" IN_LIST ENCODERS)
target_sources(liblzma PRIVATE
src/liblzma/lzma/lzma2_encoder.c
src/liblzma/lzma/lzma2_encoder.h
)
endif()
if("delta" IN_LIST ENCODERS)
set(HAVE_DELTA_CODER ON)
target_sources(liblzma PRIVATE
src/liblzma/delta/delta_encoder.c
src/liblzma/delta/delta_encoder.h
)
endif()
endif()
############
# Decoders #
############
set(DECODERS "${SUPPORTED_FILTERS}" CACHE STRING "Decoders to support")
set(SIMPLE_DECODERS OFF)
set(HAVE_DECODERS OFF)
foreach(DECODER IN LISTS DECODERS)
if(DECODER IN_LIST SUPPORTED_FILTERS)
set(HAVE_DECODERS ON)
if(NOT SIMPLE_DECODERS AND DECODER IN_LIST SIMPLE_FILTERS)
set(SIMPLE_DECODERS ON)
endif()
string(TOUPPER "${DECODER}" DECODER_UPPER)
add_compile_definitions("HAVE_DECODER_${DECODER_UPPER}")
else()
message(FATAL_ERROR "'${DECODER}' is not a supported decoder")
endif()
endforeach()
if(HAVE_DECODERS)
add_compile_definitions(HAVE_DECODERS)
target_sources(liblzma PRIVATE
src/liblzma/common/alone_decoder.c
src/liblzma/common/alone_decoder.h
src/liblzma/common/auto_decoder.c
src/liblzma/common/block_buffer_decoder.c
src/liblzma/common/block_decoder.c
src/liblzma/common/block_decoder.h
src/liblzma/common/block_header_decoder.c
src/liblzma/common/easy_decoder_memusage.c
src/liblzma/common/file_info.c
src/liblzma/common/filter_buffer_decoder.c
src/liblzma/common/filter_decoder.c
src/liblzma/common/filter_decoder.h
src/liblzma/common/filter_flags_decoder.c
src/liblzma/common/index_decoder.c
src/liblzma/common/index_decoder.h
src/liblzma/common/index_hash.c
src/liblzma/common/stream_buffer_decoder.c
src/liblzma/common/stream_decoder.c
src/liblzma/common/stream_flags_decoder.c
src/liblzma/common/stream_decoder.h
src/liblzma/common/vli_decoder.c
)
if(ENABLE_THREADS)
target_sources(liblzma PRIVATE
src/liblzma/common/stream_decoder_mt.c
)
endif()
if(SIMPLE_DECODERS)
target_sources(liblzma PRIVATE
src/liblzma/simple/simple_decoder.c
src/liblzma/simple/simple_decoder.h
)
endif()
if("lzma1" IN_LIST DECODERS)
target_sources(liblzma PRIVATE
src/liblzma/lzma/lzma_decoder.c
src/liblzma/lzma/lzma_decoder.h
src/liblzma/rangecoder/range_decoder.h
src/liblzma/lz/lz_decoder.c
src/liblzma/lz/lz_decoder.h
)
endif()
if("lzma2" IN_LIST DECODERS)
target_sources(liblzma PRIVATE
src/liblzma/lzma/lzma2_decoder.c
src/liblzma/lzma/lzma2_decoder.h
)
endif()
if("delta" IN_LIST DECODERS)
set(HAVE_DELTA_CODER ON)
target_sources(liblzma PRIVATE
src/liblzma/delta/delta_decoder.c
src/liblzma/delta/delta_decoder.h
)
endif()
endif()
# Some sources must appear if the filter is configured as either
# an encoder or decoder.
if("lzma1" IN_LIST ENCODERS OR "lzma1" IN_LIST DECODERS)
target_sources(liblzma PRIVATE
src/liblzma/rangecoder/range_common.h
src/liblzma/lzma/lzma_encoder_presets.c
src/liblzma/lzma/lzma_common.h
)
endif()
if(HAVE_DELTA_CODER)
target_sources(liblzma PRIVATE
src/liblzma/delta/delta_common.c
src/liblzma/delta/delta_common.h
src/liblzma/delta/delta_private.h
)
endif()
if(SIMPLE_ENCODERS OR SIMPLE_DECODERS)
target_sources(liblzma PRIVATE
src/liblzma/simple/simple_coder.c
src/liblzma/simple/simple_coder.h
src/liblzma/simple/simple_private.h
)
endif()
foreach(SIMPLE_CODER IN LISTS SIMPLE_FILTERS)
if(SIMPLE_CODER IN_LIST ENCODERS OR SIMPLE_CODER IN_LIST DECODERS)
target_sources(liblzma PRIVATE "src/liblzma/simple/${SIMPLE_CODER}.c")
endif()
endforeach()
#############
# MicroLZMA #
#############
option(MICROLZMA_ENCODER
"MicroLZMA encoder (needed by specific applications only)" ON)
option(MICROLZMA_DECODER
"MicroLZMA decoder (needed by specific applications only)" ON)
if(MICROLZMA_ENCODER)
if(NOT "lzma1" IN_LIST ENCODERS)
message(FATAL_ERROR "The LZMA1 encoder is required to support the "
"MicroLZMA encoder")
endif()
target_sources(liblzma PRIVATE src/liblzma/common/microlzma_encoder.c)
endif()
if(MICROLZMA_DECODER)
if(NOT "lzma1" IN_LIST DECODERS)
message(FATAL_ERROR "The LZMA1 decoder is required to support the "
"MicroLZMA decoder")
endif()
target_sources(liblzma PRIVATE src/liblzma/common/microlzma_decoder.c)
endif()
if (MICROLZMA_ENCODER OR MICROLZMA_DECODER)
add_compile_definitions(HAVE_MICROLZMA)
endif()
#############################
# lzip (.lz) format support #
#############################
option(LZIP_DECODER "Support lzip decoder" ON)
if(LZIP_DECODER)
# If lzip decoder support is requested, make sure LZMA1 decoder is enabled.
if(NOT "lzma1" IN_LIST DECODERS)
message(FATAL_ERROR "The LZMA1 decoder is required to support the "
"lzip decoder")
endif()
add_compile_definitions(HAVE_LZIP_DECODER)
target_sources(liblzma PRIVATE
src/liblzma/common/lzip_decoder.c
src/liblzma/common/lzip_decoder.h
)
endif()
##############
# Sandboxing #
##############
# ON Use sandboxing if a supported method is available in the OS.
# OFF Disable sandboxing.
# capsicum Require Capsicum (FreeBSD >= 10.2) and fail if not found.
# pledge Require pledge(2) (OpenBSD >= 5.9) and fail if not found.
# landlock Require Landlock (Linux >= 5.13) and fail if not found.
set(SUPPORTED_SANDBOX_METHODS ON OFF capsicum pledge landlock)
set(ENABLE_SANDBOX ON CACHE STRING
2024-02-17 13:35:35 +00:00
"Sandboxing method to use in 'xz', 'xzdec', and 'lzmadec'")
set_property(CACHE ENABLE_SANDBOX
PROPERTY STRINGS "${SUPPORTED_SANDBOX_METHODS}")
if(NOT ENABLE_SANDBOX IN_LIST SUPPORTED_SANDBOX_METHODS)
message(FATAL_ERROR "'${ENABLE_SANDBOX}' is not a supported "
"sandboxing method")
endif()
# When autodetecting, the search order is fixed and we must not find
# more than one method.
if(ENABLE_SANDBOX STREQUAL "OFF")
set(SANDBOX_FOUND ON)
else()
set(SANDBOX_FOUND OFF)
endif()
# Since xz and xzdec can both use sandboxing, the compile definition needed
# to use the sandbox must be added to both targets.
set(SANDBOX_COMPILE_DEFINITION OFF)
# Sandboxing: Capsicum
if(NOT SANDBOX_FOUND AND ENABLE_SANDBOX MATCHES "^ON$|^capsicum$")
check_symbol_exists(cap_rights_limit sys/capsicum.h
HAVE_CAP_RIGHTS_LIMIT)
if(HAVE_CAP_RIGHTS_LIMIT)
set(SANDBOX_COMPILE_DEFINITION "HAVE_CAP_RIGHTS_LIMIT")
set(SANDBOX_FOUND ON)
endif()
endif()
# Sandboxing: pledge(2)
if(NOT SANDBOX_FOUND AND ENABLE_SANDBOX MATCHES "^ON$|^pledge$")
check_symbol_exists(pledge unistd.h HAVE_PLEDGE)
if(HAVE_PLEDGE)
set(SANDBOX_COMPILE_DEFINITION "HAVE_PLEDGE")
set(SANDBOX_FOUND ON)
endif()
endif()
# Sandboxing: Landlock
if(NOT SANDBOX_FOUND AND ENABLE_SANDBOX MATCHES "^ON$|^landlock$")
# A compile check is done here because some systems have
# linux/landlock.h, but do not have the syscalls defined
# in order to actually use Linux Landlock.
check_c_source_compiles("
#include <linux/landlock.h>
#include <sys/syscall.h>
#include <sys/prctl.h>
.
void my_sandbox(void)
{
(void)prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
(void)SYS_landlock_create_ruleset;
(void)SYS_landlock_restrict_self;
(void)LANDLOCK_CREATE_RULESET_VERSION;
return;
}
int main(void) { return 0; }
"
HAVE_LINUX_LANDLOCK)
if(HAVE_LINUX_LANDLOCK)
set(SANDBOX_COMPILE_DEFINITION "HAVE_LINUX_LANDLOCK")
set(SANDBOX_FOUND ON)
# Of our three sandbox methods, only Landlock is incompatible
# with -fsanitize. FreeBSD 13.2 with Capsicum was tested with
# -fsanitize=address,undefined and had no issues. OpenBSD (as
# of version 7.4) has minimal support for process instrumentation.
# OpenBSD does not distribute the additional libraries needed
# (libasan, libubsan, etc.) with GCC or Clang needed for runtime
# sanitization support and instead only support
# -fsanitize-minimal-runtime for minimal undefined behavior
# sanitization. This minimal support is compatible with our use
# of the Pledge sandbox. So only Landlock will result in a
# build that cannot compress or decompress a single file to
# standard out.
if(CMAKE_C_FLAGS MATCHES "-fsanitize=")
message(SEND_ERROR
"CMAKE_C_FLAGS or the environment variable CFLAGS "
"contains '-fsanitize=' which is incompatible "
"with Landlock sandboxing. Use -DENABLE_SANDBOX=OFF "
"as an argument to 'cmake' when using '-fsanitize'.")
endif()
endif()
endif()
if(NOT SANDBOX_FOUND AND NOT ENABLE_SANDBOX MATCHES "^ON$|^OFF$")
message(SEND_ERROR "ENABLE_SANDBOX=${ENABLE_SANDBOX} was used but "
"support for the sandboxing method wasn't found.")
endif()
###
# Put the tuklib functions under the lzma_ namespace.
target_compile_definitions(liblzma PRIVATE TUKLIB_SYMBOL_PREFIX=lzma_)
tuklib_cpucores(liblzma)
tuklib_physmem(liblzma)
# While liblzma can be built without tuklib_cpucores or tuklib_physmem
# modules, the liblzma API functions lzma_cputhreads() and lzma_physmem()
# will then be useless (which isn't too bad but still unfortunate). Since
# I expect the CMake-based builds to be only used on systems that are
# supported by these tuklib modules, problems with these tuklib modules
# are considered a hard error for now. This hopefully helps to catch bugs
# in the CMake versions of the tuklib checks.
if(NOT TUKLIB_CPUCORES_FOUND OR NOT TUKLIB_PHYSMEM_FOUND)
# Use SEND_ERROR instead of FATAL_ERROR. If someone reports a bug,
# seeing the results of the remaining checks can be useful too.
message(SEND_ERROR
"tuklib_cpucores() or tuklib_physmem() failed. "
"Unless you really are building for a system where these "
"modules are not supported (unlikely), this is a bug in the "
"included cmake/tuklib_*.cmake files that should be fixed. "
"To build anyway, edit this CMakeLists.txt to ignore this error.")
endif()
# Check for __attribute__((__constructor__)) support.
# This needs -Werror because some compilers just warn
# about this being unsupported.
cmake_push_check_state()
set(CMAKE_REQUIRED_FLAGS "-Werror")
check_c_source_compiles("
__attribute__((__constructor__))
static void my_constructor_func(void) { return; }
int main(void) { return 0; }
"
HAVE_FUNC_ATTRIBUTE_CONSTRUCTOR)
cmake_pop_check_state()
tuklib_add_definition_if(liblzma HAVE_FUNC_ATTRIBUTE_CONSTRUCTOR)
# The Win95 threading lacks a thread-safe one-time initialization function.
# The one-time initialization is needed for crc32_small.c and crc64_small.c
# create the CRC tables. So if small mode is enabled, the threading mode is
# win95, and the compiler does not support attribute constructor, then we
# would end up with a multithreaded build that is thread-unsafe. As a
# result this configuration is not allowed.
if(USE_WIN95_THREADS AND ENABLE_SMALL AND NOT HAVE_FUNC_ATTRIBUTE_CONSTRUCTOR)
message(SEND_ERROR "Threading method win95 and ENABLE_SMALL "
"cannot be used at the same time with a compiler "
"that doesn't support "
"__attribute__((__constructor__))")
endif()
# Check for __attribute__((__ifunc__())) support.
# Supported values for USE_ATTR_IFUNC:
#
# auto (default) - Detect ifunc support with a compile test.
# ON - Always enable ifunc.
# OFF - Disable ifunc usage.
set(USE_ATTR_IFUNC "auto" CACHE STRING "Use __attribute__((__ifunc__())).")
set(SUPPORTED_USE_ATTR_IFUNC auto ON OFF)
if(NOT USE_ATTR_IFUNC IN_LIST SUPPORTED_USE_ATTR_IFUNC)
message(FATAL_ERROR "'${USE_ATTR_IFUNC}' is not a supported value for"
"USE_ATTR_IFUNC")
endif()
# When USE_ATTR_IFUNC is 'auto', allow the use of __attribute__((__ifunc__()))
# if compiler support is detected and we are building for GNU/Linux (glibc)
# or FreeBSD. uClibc and musl don't support ifunc in their dynamic linkers
# but some compilers still accept the attribute when compiling for these
# C libraries, which results in broken binaries. That's why we need to
# check which libc is being used.
if(USE_ATTR_IFUNC STREQUAL "auto")
cmake_push_check_state()
set(CMAKE_REQUIRED_FLAGS "-Werror")
check_c_source_compiles("
/*
* Force a compilation error when not using glibc on Linux
* or if we are not using FreeBSD. uClibc will define
* __GLIBC__ but does not support ifunc, so we must have
* an extra check to disable with uClibc.
*/
#if defined(__linux__)
# include <features.h>
# if !defined(__GLIBC__) || defined(__UCLIBC__)
compile error
# endif
#elif !defined(__FreeBSD__)
compile error
#endif
static void func(void) { return; }
/*
* The attribute __no_profile_instrument_function__ is
* needed with GCC to prevent improper instrumentation in
* the ifunc resolver.
*/
__attribute__((__no_profile_instrument_function__))
2023-06-27 14:24:49 +00:00
static void (*resolve_func(void)) (void) { return func; }
void func_ifunc(void)
__attribute__((__ifunc__(\"resolve_func\")));
int main(void) { return 0; }
/*
* 'clang -Wall' incorrectly warns that resolve_func is
* unused (-Wunused-function). Correct assembly output is
* still produced. This problem exists at least in Clang
* versions 4 to 17. The following silences the bogus warning:
*/
void make_clang_quiet(void);
void make_clang_quiet(void) { resolve_func()(); }
"
SYSTEM_SUPPORTS_IFUNC)
cmake_pop_check_state()
endif()
if(USE_ATTR_IFUNC STREQUAL "ON" OR SYSTEM_SUPPORTS_IFUNC)
tuklib_add_definitions(liblzma HAVE_FUNC_ATTRIBUTE_IFUNC)
if(CMAKE_C_FLAGS MATCHES "-fsanitize=")
message(SEND_ERROR
"CMAKE_C_FLAGS or the environment variable CFLAGS "
"contains '-fsanitize=' which is incompatible "
"with ifunc. Use -DUSE_ATTR_IFUNC=OFF "
"as an argument to 'cmake' when using '-fsanitize'.")
endif()
endif()
liblzma: Add fast CRC64 for 32/64-bit x86 using SSSE3 + SSE4.1 + CLMUL. It also works on E2K as it supports these intrinsics. On x86-64 runtime detection is used so the code keeps working on older processors too. A CLMUL-only build can be done by using -msse4.1 -mpclmul in CFLAGS and this will reduce the library size since the generic implementation and its 8 KiB lookup table will be omitted. On 32-bit x86 this isn't used by default for now because by default on 32-bit x86 the separate assembly file crc64_x86.S is used. If --disable-assembler is used then this new CLMUL code is used the same way as on 64-bit x86. However, a CLMUL-only build (-msse4.1 -mpclmul) won't omit the 8 KiB lookup table on 32-bit x86 due to a currently-missing check for disabled assembler usage. The configure.ac check should be such that the code won't be built if something in the toolchain doesn't support it but --disable-clmul-crc option can be used to unconditionally disable this feature. CLMUL speeds up decompression of files that have compressed very well (assuming CRC64 is used as a check type). It is know that the CLMUL code is significantly slower than the generic code for tiny inputs (especially 1-8 bytes but up to 16 bytes). If that is a real-world problem then there is already a commented-out variant that uses the generic version for small inputs. Thanks to Ilya Kurdyukov for the original patch which was derived from a white paper from Intel [1] (published in 2009) and public domain code from [2] (released in 2016). [1] https://www.intel.com/content/dam/www/public/us/en/documents/white-papers/fast-crc-computation-generic-polynomials-pclmulqdq-paper.pdf [2] https://github.com/rawrunprotected/crc
2022-11-14 19:34:57 +00:00
# cpuid.h
check_include_file(cpuid.h HAVE_CPUID_H)
tuklib_add_definition_if(liblzma HAVE_CPUID_H)
# immintrin.h:
check_include_file(immintrin.h HAVE_IMMINTRIN_H)
if(HAVE_IMMINTRIN_H)
target_compile_definitions(liblzma PRIVATE HAVE_IMMINTRIN_H)
# SSE2 intrinsics:
check_c_source_compiles("
#include <immintrin.h>
int main(void)
{
__m128i x = { 0 };
_mm_movemask_epi8(x);
return 0;
}
"
HAVE__MM_MOVEMASK_EPI8)
tuklib_add_definition_if(liblzma HAVE__MM_MOVEMASK_EPI8)
liblzma: Add fast CRC64 for 32/64-bit x86 using SSSE3 + SSE4.1 + CLMUL. It also works on E2K as it supports these intrinsics. On x86-64 runtime detection is used so the code keeps working on older processors too. A CLMUL-only build can be done by using -msse4.1 -mpclmul in CFLAGS and this will reduce the library size since the generic implementation and its 8 KiB lookup table will be omitted. On 32-bit x86 this isn't used by default for now because by default on 32-bit x86 the separate assembly file crc64_x86.S is used. If --disable-assembler is used then this new CLMUL code is used the same way as on 64-bit x86. However, a CLMUL-only build (-msse4.1 -mpclmul) won't omit the 8 KiB lookup table on 32-bit x86 due to a currently-missing check for disabled assembler usage. The configure.ac check should be such that the code won't be built if something in the toolchain doesn't support it but --disable-clmul-crc option can be used to unconditionally disable this feature. CLMUL speeds up decompression of files that have compressed very well (assuming CRC64 is used as a check type). It is know that the CLMUL code is significantly slower than the generic code for tiny inputs (especially 1-8 bytes but up to 16 bytes). If that is a real-world problem then there is already a commented-out variant that uses the generic version for small inputs. Thanks to Ilya Kurdyukov for the original patch which was derived from a white paper from Intel [1] (published in 2009) and public domain code from [2] (released in 2016). [1] https://www.intel.com/content/dam/www/public/us/en/documents/white-papers/fast-crc-computation-generic-polynomials-pclmulqdq-paper.pdf [2] https://github.com/rawrunprotected/crc
2022-11-14 19:34:57 +00:00
# CLMUL intrinsic:
option(ALLOW_CLMUL_CRC "Allow carryless multiplication for CRC \
calculation if supported by the system" ON)
if(ALLOW_CLMUL_CRC)
check_c_source_compiles("
#include <immintrin.h>
#if defined(__e2k__) && __iset__ < 6
# error
#endif
#if (defined(__GNUC__) || defined(__clang__)) \
&& !defined(__EDG__)
__attribute__((__target__(\"ssse3,sse4.1,pclmul\")))
#endif
__m128i my_clmul(__m128i a)
{
const __m128i b = _mm_set_epi64x(1, 2);
return _mm_clmulepi64_si128(a, b, 0);
}
int main(void) { return 0; }
"
HAVE_USABLE_CLMUL)
liblzma: Avoid extern lzma_crc32_clmul() and lzma_crc64_clmul(). A CLMUL-only build will have the crcxx_clmul() inlined into lzma_crcxx(). Previously a jump to the extern lzma_crcxx_clmul() was needed. Notes about shared liblzma on ELF platforms: - On platforms that support ifunc and -fvisibility=hidden, this was silly because CLMUL-only build would have that single extra jump instruction of extra overhead. - On platforms that support neither -fvisibility=hidden nor linker version script (liblzma*.map), jumping to lzma_crcxx_clmul() would go via PLT so a few more instructions of overhead (still not a big issue but silly nevertheless). There was a downside with static liblzma too: if an application only needs lzma_crc64(), static linking would make the linker include the CLMUL code for both CRC32 and CRC64 from crc_x86_clmul.o even though the CRC32 code wouldn't be needed, thus increasing code size of the executable (assuming that -ffunction-sections isn't used). Also, now compilers are likely to inline crc_simd_body() even if they don't support the always_inline attribute (or MSVC's __forceinline). Quite possibly all compilers that build the code do support such an attribute. But now it likely isn't a problem even if the attribute wasn't supported. Now all x86-specific stuff is in crc_x86_clmul.h. If other archs The other archs can then have their own headers with their own is_clmul_supported() and crcxx_clmul(). Another bonus is that the build system doesn't need to care if crc_clmul.c is needed. is_clmul_supported() stays as inline function as it's not needed when doing a CLMUL-only build (avoids a warning about unused function).
2023-10-20 20:35:10 +00:00
tuklib_add_definition_if(liblzma HAVE_USABLE_CLMUL)
endif()
endif()
# ARM64 C Language Extensions define CRC32 functions in arm_acle.h.
# These are supported by at least GCC and Clang which both need
# __attribute__((__target__("+crc"))), unless the needed compiler flags
# are used to support the CRC instruction.
option(ALLOW_ARM64_CRC32 "Allow ARM64 CRC32 instruction if supported by \
the system" ON)
if(ALLOW_ARM64_CRC32)
check_c_source_compiles("
#include <stdint.h>
#ifndef _MSC_VER
#include <arm_acle.h>
#endif
#if (defined(__GNUC__) || defined(__clang__)) && !defined(__EDG__)
__attribute__((__target__(\"+crc\")))
#endif
uint32_t my_crc(uint32_t a, uint64_t b)
{
return __crc32d(a, b);
}
int main(void) { return 0; }
"
HAVE_ARM64_CRC32)
if(HAVE_ARM64_CRC32)
target_compile_definitions(liblzma PRIVATE HAVE_ARM64_CRC32)
# Check for ARM64 CRC32 instruction runtime detection.
# getauxval() is supported on Linux.
check_symbol_exists(getauxval sys/auxv.h HAVE_GETAUXVAL)
tuklib_add_definition_if(liblzma HAVE_GETAUXVAL)
# elf_aux_info() is supported on FreeBSD.
check_symbol_exists(elf_aux_info sys/auxv.h HAVE_ELF_AUX_INFO)
tuklib_add_definition_if(liblzma HAVE_ELF_AUX_INFO)
# sysctlbyname("hw.optional.armv8_crc32", ...) is supported on Darwin
# (macOS, iOS, etc.). Note that sysctlbyname() is supported on FreeBSD,
# NetBSD, and possibly others too but the string is specific to
# Apple OSes. The C code is responsible for checking
# defined(__APPLE__) before using
# sysctlbyname("hw.optional.armv8_crc32", ...).
check_symbol_exists(sysctlbyname sys/sysctl.h HAVE_SYSCTLBYNAME)
tuklib_add_definition_if(liblzma HAVE_SYSCTLBYNAME)
endif()
endif()
# Symbol visibility support:
#
# The C_VISIBILITY_PRESET property takes care of adding the compiler
# option -fvisibility=hidden (or equivalent) if and only if it is supported.
#
# HAVE_VISIBILITY should always be defined to 0 or 1. It tells liblzma
# if __attribute__((__visibility__("default")))
# and __attribute__((__visibility__("hidden"))) are supported.
# Those are useful only when the compiler supports -fvisibility=hidden
# or such option so HAVE_VISIBILITY should be 1 only when both option and
# the attribute support are present. HAVE_VISIBILITY is ignored on Windows
# and Cygwin by the liblzma C code; __declspec(dllexport) is used instead.
#
# CMake's GenerateExportHeader module is too fancy since liblzma already
# has the necessary macros. Instead, check CMake's internal variable
# CMAKE_C_COMPILE_OPTIONS_VISIBILITY (it's the C-specific variant of
# CMAKE_<LANG>_COMPILE_OPTIONS_VISIBILITY) which contains the compiler
# command line option for visibility support. It's empty or unset when
# visibility isn't supported. (It was added to CMake 2.8.12 in the commit
# 0e9f4bc00c6b26f254e74063e4026ac33b786513 in 2013.) This way we don't
# set HAVE_VISIBILITY to 1 when visibility isn't actually supported.
if(BUILD_SHARED_LIBS AND CMAKE_C_COMPILE_OPTIONS_VISIBILITY)
set_target_properties(liblzma PROPERTIES C_VISIBILITY_PRESET hidden)
target_compile_definitions(liblzma PRIVATE HAVE_VISIBILITY=1)
else()
target_compile_definitions(liblzma PRIVATE HAVE_VISIBILITY=0)
endif()
if(WIN32)
if(BUILD_SHARED_LIBS)
# Add the Windows resource file for liblzma.dll.
target_sources(liblzma PRIVATE src/liblzma/liblzma_w32res.rc)
set_target_properties(liblzma PROPERTIES
LINK_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/src/common/common_w32res.rc"
)
# Export the public API symbols with __declspec(dllexport).
target_compile_definitions(liblzma PRIVATE DLL_EXPORT)
if(NOT MSVC)
# Create a DEF file. The linker puts the ordinal numbers there
# too so the output from the linker isn't our final file.
target_link_options(liblzma PRIVATE
"-Wl,--output-def,liblzma.def.in")
# Remove the ordinal numbers from the DEF file so that
# no one will create an import library that links by ordinal
# instead of by name. We don't maintain a DEF file so the
# ordinal numbers aren't stable.
add_custom_command(TARGET liblzma POST_BUILD
COMMAND "${CMAKE_COMMAND}"
-DINPUT_FILE=liblzma.def.in
-DOUTPUT_FILE=liblzma.def
-P
"${CMAKE_CURRENT_SOURCE_DIR}/cmake/remove-ordinals.cmake"
BYPRODUCTS "liblzma.def"
VERBATIM)
endif()
else()
# Disable __declspec(dllimport) when linking against static liblzma.
target_compile_definitions(liblzma INTERFACE LZMA_API_STATIC)
endif()
elseif(BUILD_SHARED_LIBS AND CMAKE_SYSTEM_NAME STREQUAL "Linux" AND
NOT CMAKE_SYSTEM_PROCESSOR MATCHES "[Mm]icro[Bb]laze")
# GNU/Linux-specific symbol versioning for shared liblzma.
# This includes a few extra compatibility symbols for RHEL/CentOS 7
# which are pointless on non-glibc non-Linux systems.
#
# As a special case, GNU/Linux on MicroBlaze gets the generic
# symbol versioning because GCC 12 doesn't support the __symver__
# attribute on MicroBlaze. On Linux, CMAKE_SYSTEM_PROCESSOR comes
# from "uname -m" for native builds (should be "microblaze") or from
# the CMake toolchain file (not perfectly standardized but it very
# likely has "microblaze" in lower case or mixed case somewhere in
# the string).
#
# FIXME? Avoid symvers on Linux with non-glibc like musl?
#
# Note that adding link options doesn't affect static builds
# but HAVE_SYMBOL_VERSIONS_LINUX must not be used with static builds
# because it would put symbol versions into the static library which
# can cause problems. It's clearer if all symver related things are
# omitted when not building a shared library.
#
# NOTE: Set it explicitly to 1 to make it clear that versioning is
# done unconditionally in the C files.
target_compile_definitions(liblzma PRIVATE HAVE_SYMBOL_VERSIONS_LINUX=1)
target_link_options(liblzma PRIVATE
"-Wl,--version-script=${CMAKE_CURRENT_SOURCE_DIR}/src/liblzma/liblzma_linux.map"
)
set_target_properties(liblzma PROPERTIES
LINK_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/src/liblzma/liblzma_linux.map"
)
elseif(BUILD_SHARED_LIBS AND (CMAKE_SYSTEM_NAME STREQUAL "FreeBSD" OR
CMAKE_SYSTEM_NAME STREQUAL "Linux"))
# Generic symbol versioning for shared liblzma is used on FreeBSD and
# also on GNU/Linux on MicroBlaze.
target_link_options(liblzma PRIVATE
"-Wl,--version-script=${CMAKE_CURRENT_SOURCE_DIR}/src/liblzma/liblzma_generic.map"
)
set_target_properties(liblzma PROPERTIES
LINK_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/src/liblzma/liblzma_generic.map"
)
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.so or liblzma.dll, not libliblzma.so or lzma.dll.
# Avoid the name lzma.dll because it would conflict with LZMA SDK.
PREFIX ""
IMPORT_PREFIX ""
)
# Create liblzma-config-version.cmake.
#
# FIXME: SameMajorVersion is correct for stable releases but it is wrong
# for development releases where each release may have incompatible changes.
include(CMakePackageConfigHelpers)
write_basic_package_version_file(
CMake: Try to improve compatibility with the FindLibLZMA module. The naming conflict with FindLibLZMA module gets worse. Not avoiding it in the first place was stupid. Normally find_package(LibLZMA) will use the module and find_package(liblzma 5.2.5 REQUIRED CONFIG) will use the config file even with a case insensitive file system. However, if CMAKE_FIND_PACKAGE_PREFER_CONFIG is TRUE and the file system is case insensitive, find_package(LibLZMA) will find our liblzma config file instead of using FindLibLZMA module. One big problem with this is that FindLibLZMA uses LibLZMA::LibLZMA and we use liblzma::liblzma as the target name. With target names CMake happens to be case sensitive. To workaround this, this commit adds add_library(LibLZMA::LibLZMA ALIAS liblzma::liblzma) to the config file. Then both spellings work. To make the behavior consistent between case sensitive and insensitive file systems, the config and related files are renamed from liblzmaConfig.cmake to liblzma-config.cmake style. With this style CMake looks for lowercase version of the package name so find_package(LiBLzmA 5.2.5 REQUIRED CONFIG) will work to find our config file. There are other differences between our config file and FindLibLZMA so it's still possible that things break for reasons other than the spelling of the target name. Hopefully those situations aren't too common. When the config file is available, it should always give as good or better results as FindLibLZMA so this commit doesn't affect the recommendation to use find_package(liblzma 5.2.5 REQUIRED CONFIG) which explicitly avoids FindLibLZMA. Thanks to Markus Rickert.
2021-01-30 16:36:04 +00:00
"${CMAKE_CURRENT_BINARY_DIR}/liblzma-config-version.cmake"
VERSION "${liblzma_VERSION}"
COMPATIBILITY SameMajorVersion)
# Create liblzma-config.cmake. We use this spelling instead of
# liblzmaConfig.cmake to make find_package work in case insensitive
# manner even with case sensitive file systems. This gives more consistent
# behavior between operating systems. This optionally includes a dependency
# on a threading library, so the contents are created in two separate parts.
# The "second half" is always needed, so create it first.
set(LZMA_CONFIG_CONTENTS
"include(\"\${CMAKE_CURRENT_LIST_DIR}/liblzma-targets.cmake\")
if(NOT TARGET LibLZMA::LibLZMA)
# Be compatible with the spelling used by the FindLibLZMA module. This
# doesn't use ALIAS because it would make CMake resolve LibLZMA::LibLZMA
# to liblzma::liblzma instead of keeping the original spelling. Keeping
# the original spelling is important for good FindLibLZMA compatibility.
add_library(LibLZMA::LibLZMA INTERFACE IMPORTED)
set_target_properties(LibLZMA::LibLZMA PROPERTIES
INTERFACE_LINK_LIBRARIES liblzma::liblzma)
endif()
")
if(USE_POSIX_THREADS)
set(LZMA_CONFIG_CONTENTS
"include(CMakeFindDependencyMacro)
set(THREADS_PREFER_PTHREAD_FLAG TRUE)
find_dependency(Threads)
${LZMA_CONFIG_CONTENTS}
")
endif()
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/liblzma-config.cmake"
"${LZMA_CONFIG_CONTENTS}")
# Create liblzma.pc.
set(prefix "${CMAKE_INSTALL_PREFIX}")
set(exec_prefix "${CMAKE_INSTALL_PREFIX}")
set(libdir "${CMAKE_INSTALL_FULL_LIBDIR}")
set(includedir "${CMAKE_INSTALL_FULL_INCLUDEDIR}")
set(PTHREAD_CFLAGS "${CMAKE_THREAD_LIBS_INIT}")
configure_file(src/liblzma/liblzma.pc.in liblzma.pc
@ONLY
NEWLINE_STYLE LF)
# 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}"
COMPONENT liblzma_Runtime
LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}"
COMPONENT liblzma_Runtime
NAMELINK_COMPONENT liblzma_Development
ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}"
COMPONENT liblzma_Development
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 src/liblzma/api/
COMPONENT liblzma_Development
DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
FILES_MATCHING PATTERN "*.h")
# Install the CMake files that other packages can use to find liblzma.
set(liblzma_INSTALL_CMAKEDIR
"${CMAKE_INSTALL_LIBDIR}/cmake/liblzma"
CACHE STRING "Path to liblzma's .cmake files")
install(EXPORT liblzmaTargets
NAMESPACE liblzma::
CMake: Try to improve compatibility with the FindLibLZMA module. The naming conflict with FindLibLZMA module gets worse. Not avoiding it in the first place was stupid. Normally find_package(LibLZMA) will use the module and find_package(liblzma 5.2.5 REQUIRED CONFIG) will use the config file even with a case insensitive file system. However, if CMAKE_FIND_PACKAGE_PREFER_CONFIG is TRUE and the file system is case insensitive, find_package(LibLZMA) will find our liblzma config file instead of using FindLibLZMA module. One big problem with this is that FindLibLZMA uses LibLZMA::LibLZMA and we use liblzma::liblzma as the target name. With target names CMake happens to be case sensitive. To workaround this, this commit adds add_library(LibLZMA::LibLZMA ALIAS liblzma::liblzma) to the config file. Then both spellings work. To make the behavior consistent between case sensitive and insensitive file systems, the config and related files are renamed from liblzmaConfig.cmake to liblzma-config.cmake style. With this style CMake looks for lowercase version of the package name so find_package(LiBLzmA 5.2.5 REQUIRED CONFIG) will work to find our config file. There are other differences between our config file and FindLibLZMA so it's still possible that things break for reasons other than the spelling of the target name. Hopefully those situations aren't too common. When the config file is available, it should always give as good or better results as FindLibLZMA so this commit doesn't affect the recommendation to use find_package(liblzma 5.2.5 REQUIRED CONFIG) which explicitly avoids FindLibLZMA. Thanks to Markus Rickert.
2021-01-30 16:36:04 +00:00
FILE liblzma-targets.cmake
DESTINATION "${liblzma_INSTALL_CMAKEDIR}"
COMPONENT liblzma_Development)
CMake: Try to improve compatibility with the FindLibLZMA module. The naming conflict with FindLibLZMA module gets worse. Not avoiding it in the first place was stupid. Normally find_package(LibLZMA) will use the module and find_package(liblzma 5.2.5 REQUIRED CONFIG) will use the config file even with a case insensitive file system. However, if CMAKE_FIND_PACKAGE_PREFER_CONFIG is TRUE and the file system is case insensitive, find_package(LibLZMA) will find our liblzma config file instead of using FindLibLZMA module. One big problem with this is that FindLibLZMA uses LibLZMA::LibLZMA and we use liblzma::liblzma as the target name. With target names CMake happens to be case sensitive. To workaround this, this commit adds add_library(LibLZMA::LibLZMA ALIAS liblzma::liblzma) to the config file. Then both spellings work. To make the behavior consistent between case sensitive and insensitive file systems, the config and related files are renamed from liblzmaConfig.cmake to liblzma-config.cmake style. With this style CMake looks for lowercase version of the package name so find_package(LiBLzmA 5.2.5 REQUIRED CONFIG) will work to find our config file. There are other differences between our config file and FindLibLZMA so it's still possible that things break for reasons other than the spelling of the target name. Hopefully those situations aren't too common. When the config file is available, it should always give as good or better results as FindLibLZMA so this commit doesn't affect the recommendation to use find_package(liblzma 5.2.5 REQUIRED CONFIG) which explicitly avoids FindLibLZMA. Thanks to Markus Rickert.
2021-01-30 16:36:04 +00:00
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/liblzma-config.cmake"
"${CMAKE_CURRENT_BINARY_DIR}/liblzma-config-version.cmake"
DESTINATION "${liblzma_INSTALL_CMAKEDIR}"
COMPONENT liblzma_Development)
if(NOT MSVC)
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/liblzma.pc"
DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig"
COMPONENT liblzma_Development)
endif()
#############################################################################
# Helper functions for installing files
#############################################################################
# For each non-empty element in the list LINK_NAMES, creates symbolic links
# ${LINK_NAME}${LINK_SUFFIX} -> ${TARGET_NAME} in the directory ${DIR}.
# The target file should exist because on Cygwin and MSYS2 symlink creation
# can fail under certain conditions if the target doesn't exist.
function(my_install_symlinks COMPONENT DIR TARGET_NAME LINK_SUFFIX LINK_NAMES)
install(CODE "set(D \"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${DIR}\")
foreach(L ${LINK_NAMES})
file(CREATE_LINK \"${TARGET_NAME}\"
\"\${D}/\${L}${LINK_SUFFIX}\"
SYMBOLIC)
endforeach()"
COMPONENT "${COMPONENT}")
endfunction()
# Installs a man page file of a given language ("" for the untranslated file)
# and optionally its alternative names as symlinks. This is a helper function
# for my_install_man() below.
function(my_install_man_lang COMPONENT SRC_FILE MAN_LANG LINK_NAMES)
# Get the man page section from the filename suffix.
string(REGEX REPLACE "^.*\.([^/.]+)$" "\\1" MAN_SECTION "${SRC_FILE}")
# A few man pages might be missing from translations.
# Don't attempt to install them or create the related symlinks.
if(NOT MAN_LANG STREQUAL "" AND NOT EXISTS "${SRC_FILE}")
return()
endif()
# Installing the file must be done before creating the symlinks
# due to Cygwin and MSYS2.
install(FILES "${SRC_FILE}"
DESTINATION "${CMAKE_INSTALL_MANDIR}/${MAN_LANG}/man${MAN_SECTION}"
COMPONENT "${COMPONENT}")
# Get the basename of the file to be used as the symlink target.
get_filename_component(BASENAME "${SRC_FILE}" NAME)
# LINK_NAMES don't contain the man page filename suffix (like ".1")
# so it needs to be told to my_install_symlinks.
my_install_symlinks("${COMPONENT}"
"${CMAKE_INSTALL_MANDIR}/${MAN_LANG}/man${MAN_SECTION}"
"${BASENAME}" ".${MAN_SECTION}" "${LINK_NAMES}")
endfunction()
# Installs a man page file and optionally its alternative names as symlinks.
# Does the same for translations if ENABLE_NLS.
function(my_install_man COMPONENT SRC_FILE LINK_NAMES)
my_install_man_lang("${COMPONENT}" "${SRC_FILE}" "" "${LINK_NAMES}")
if(ENABLE_NLS)
# Find the translated versions of this man page.
get_filename_component(BASENAME "${SRC_FILE}" NAME)
file(GLOB MAN_FILES "po4a/man/*/${BASENAME}")
foreach(F ${MAN_FILES})
get_filename_component(MAN_LANG "${F}" DIRECTORY)
get_filename_component(MAN_LANG "${MAN_LANG}" NAME)
my_install_man_lang("${COMPONENT}" "${F}" "${MAN_LANG}"
"${LINK_NAMES}")
endforeach()
endif()
endfunction()
#############################################################################
# libgnu (getopt_long)
#############################################################################
# This mirrors how the Autotools build system handles the getopt_long
# replacement, calling the object library libgnu since the replacement
# version comes from Gnulib.
add_library(libgnu OBJECT)
# CMake requires that even an object library must have at least once source
# file. So we give it a header file that results in no output files.
#
# NOTE: Using a file outside the lib directory makes it possible to
# delete lib/*.h and lib/*.c and still keep the build working if
# getopt_long replacement isn't needed. It's convenient if one wishes
# to be certain that no GNU LGPL code gets included in the binaries.
target_sources(libgnu PRIVATE src/common/sysdefs.h)
# The Ninja Generator requires setting the linker language since it cannot
# guess the programming language of just a header file. Setting this
# property avoids needing an empty .c file or an non-empty unnecessary .c
# file.
set_target_properties(libgnu PROPERTIES LINKER_LANGUAGE C)
# Create /lib directory in the build directory and add it to the include path.
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/lib")
target_include_directories(libgnu PUBLIC "${CMAKE_CURRENT_BINARY_DIR}/lib")
2023-09-25 16:09:53 +00:00
# Include /lib from the source directory. It does no harm even if none of
# the Gnulib replacements are used.
target_include_directories(libgnu PUBLIC lib)
# The command line tools need getopt_long in order to parse arguments. If
# the system does not have a getopt_long implementation we can use the one
# from Gnulib instead.
check_symbol_exists(getopt_long getopt.h HAVE_GETOPT_LONG)
if(NOT HAVE_GETOPT_LONG)
# Set the __GETOPT_PREFIX definition to "rpl_" (replacement) to avoid
# name conflicts with libc symbols. The same prefix is set if using
# the Autotools build (m4/getopt.m4).
target_compile_definitions(libgnu PUBLIC "__GETOPT_PREFIX=rpl_")
# Create a custom copy command to copy the getopt header to the build
# directory and re-copy it if it is updated. (Gnulib does it this way
# because it allows choosing which .in.h files to actually use in the
# build. We need just getopt.h so this is a bit overcomplicated for
# a single header file only.)
add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/lib/getopt.h"
COMMAND "${CMAKE_COMMAND}" -E copy
"${CMAKE_CURRENT_SOURCE_DIR}/lib/getopt.in.h"
"${CMAKE_CURRENT_BINARY_DIR}/lib/getopt.h"
MAIN_DEPENDENCY "${CMAKE_CURRENT_SOURCE_DIR}/lib/getopt.in.h"
VERBATIM)
target_sources(libgnu PRIVATE
lib/getopt1.c
lib/getopt.c
lib/getopt_int.h
lib/getopt-cdefs.h
lib/getopt-core.h
lib/getopt-ext.h
lib/getopt-pfx-core.h
lib/getopt-pfx-ext.h
"${CMAKE_CURRENT_BINARY_DIR}/lib/getopt.h"
)
endif()
#############################################################################
2024-02-17 13:35:35 +00:00
# xzdec and lzmadec
#############################################################################
if(HAVE_DECODERS AND (NOT MSVC OR MSVC_VERSION GREATER_EQUAL 1900))
2024-02-17 13:35:35 +00:00
foreach(XZDEC xzdec lzmadec)
add_executable("${XZDEC}"
src/common/sysdefs.h
src/common/tuklib_common.h
src/common/tuklib_config.h
src/common/tuklib_exit.c
src/common/tuklib_exit.h
src/common/tuklib_gettext.h
src/common/tuklib_progname.c
src/common/tuklib_progname.h
src/xzdec/xzdec.c
)
2024-02-17 13:35:35 +00:00
target_include_directories("${XZDEC}" PRIVATE
src/common
src/liblzma/api
)
2024-02-17 13:35:35 +00:00
target_link_libraries("${XZDEC}" PRIVATE liblzma libgnu)
2024-02-17 13:35:35 +00:00
if(WIN32)
# Add the Windows resource file for xzdec.exe or lzmadec.exe.
target_sources("${XZDEC}" PRIVATE src/xzdec/xzdec_w32res.rc)
set_target_properties("${XZDEC}" PROPERTIES
LINK_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/src/common/common_w32res.rc"
)
endif()
2024-02-17 13:35:35 +00:00
if(SANDBOX_COMPILE_DEFINITION)
target_compile_definitions("${XZDEC}" PRIVATE
"${SANDBOX_COMPILE_DEFINITION}")
endif()
tuklib_progname("${XZDEC}")
2024-02-17 13:35:35 +00:00
install(TARGETS "${XZDEC}"
RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}"
2024-02-19 10:20:59 +00:00
COMPONENT "${XZDEC}_Runtime")
2024-02-17 13:35:35 +00:00
endforeach()
2024-02-17 13:35:35 +00:00
# This is the only build-time difference with lzmadec.
target_compile_definitions(lzmadec PRIVATE "LZMADEC")
if(UNIX)
2024-02-19 10:20:59 +00:00
# NOTE: This puts the lzmadec.1 symlinks into xzdec_Documentation.
# This isn't great but doing them separately with translated
# man pages would require extra code. So this has to suffice for now.
my_install_man(xzdec_Documentation src/xzdec/xzdec.1 lzmadec)
endif()
endif()
2024-02-17 13:35:35 +00:00
#############################################################################
# lzmainfo
#############################################################################
if(HAVE_DECODERS AND (NOT MSVC OR MSVC_VERSION GREATER_EQUAL 1900))
add_executable(lzmainfo
src/common/sysdefs.h
src/common/tuklib_common.h
src/common/tuklib_config.h
src/common/tuklib_exit.c
src/common/tuklib_exit.h
src/common/tuklib_gettext.h
src/common/tuklib_progname.c
src/common/tuklib_progname.h
src/lzmainfo/lzmainfo.c
)
target_include_directories(lzmainfo PRIVATE
src/common
src/liblzma/api
)
target_link_libraries(lzmainfo PRIVATE liblzma libgnu)
if(WIN32)
# Add the Windows resource file for lzmainfo.exe.
target_sources(lzmainfo PRIVATE src/lzmainfo/lzmainfo_w32res.rc)
set_target_properties(lzmainfo PROPERTIES
LINK_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/src/common/common_w32res.rc"
)
endif()
tuklib_progname(lzmainfo)
# NOTE: The translations are in the "xz" domain and the .mo files are
# installed as part of the "xz" target.
if(ENABLE_NLS)
target_link_libraries(lzmainfo PRIVATE Intl::Intl)
2024-02-17 13:35:35 +00:00
target_compile_definitions(lzmainfo PRIVATE
ENABLE_NLS
PACKAGE="${TRANSLATION_DOMAIN}"
LOCALEDIR="${LOCALEDIR_DEFINITION}"
2024-02-17 13:35:35 +00:00
)
endif()
install(TARGETS lzmainfo
RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}"
2024-02-19 10:20:59 +00:00
COMPONENT lzmainfo_Runtime)
2024-02-17 13:35:35 +00:00
if(UNIX)
2024-02-19 10:20:59 +00:00
my_install_man(lzmainfo_Documentation src/lzmainfo/lzmainfo.1 "")
2024-02-17 13:35:35 +00:00
endif()
endif()
#############################################################################
# xz
#############################################################################
if(NOT MSVC OR MSVC_VERSION GREATER_EQUAL 1900)
add_executable(xz
src/common/mythread.h
src/common/sysdefs.h
src/common/tuklib_common.h
src/common/tuklib_config.h
src/common/tuklib_exit.c
src/common/tuklib_exit.h
src/common/tuklib_gettext.h
src/common/tuklib_integer.h
src/common/tuklib_mbstr.h
src/common/tuklib_mbstr_fw.c
src/common/tuklib_mbstr_width.c
src/common/tuklib_open_stdxxx.c
src/common/tuklib_open_stdxxx.h
src/common/tuklib_progname.c
src/common/tuklib_progname.h
src/xz/args.c
src/xz/args.h
src/xz/coder.c
src/xz/coder.h
src/xz/file_io.c
src/xz/file_io.h
src/xz/hardware.c
src/xz/hardware.h
src/xz/main.c
src/xz/main.h
src/xz/message.c
src/xz/message.h
src/xz/mytime.c
src/xz/mytime.h
src/xz/options.c
src/xz/options.h
src/xz/private.h
src/xz/sandbox.c
src/xz/sandbox.h
src/xz/signals.c
src/xz/signals.h
src/xz/suffix.c
src/xz/suffix.h
src/xz/util.c
src/xz/util.h
)
target_include_directories(xz PRIVATE
src/common
src/liblzma/api
)
if(HAVE_DECODERS)
target_sources(xz PRIVATE
src/xz/list.c
src/xz/list.h
)
endif()
target_link_libraries(xz PRIVATE liblzma libgnu)
target_compile_definitions(xz PRIVATE ASSUME_RAM=128)
if(WIN32)
# Add the Windows resource file for xz.exe.
target_sources(xz PRIVATE src/xz/xz_w32res.rc)
set_target_properties(xz PROPERTIES
LINK_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/src/common/common_w32res.rc"
)
endif()
if(SANDBOX_COMPILE_DEFINITION)
target_compile_definitions(xz PRIVATE "${SANDBOX_COMPILE_DEFINITION}")
endif()
tuklib_progname(xz)
tuklib_mbstr(xz)
check_symbol_exists(optreset getopt.h HAVE_OPTRESET)
tuklib_add_definition_if(xz HAVE_OPTRESET)
check_symbol_exists(posix_fadvise fcntl.h HAVE_POSIX_FADVISE)
tuklib_add_definition_if(xz HAVE_POSIX_FADVISE)
# How to get file time:
check_struct_has_member("struct stat" st_atim.tv_nsec
"sys/types.h;sys/stat.h"
HAVE_STRUCT_STAT_ST_ATIM_TV_NSEC)
if(HAVE_STRUCT_STAT_ST_ATIM_TV_NSEC)
tuklib_add_definitions(xz HAVE_STRUCT_STAT_ST_ATIM_TV_NSEC)
else()
check_struct_has_member("struct stat" st_atimespec.tv_nsec
"sys/types.h;sys/stat.h"
HAVE_STRUCT_STAT_ST_ATIMESPEC_TV_NSEC)
if(HAVE_STRUCT_STAT_ST_ATIMESPEC_TV_NSEC)
tuklib_add_definitions(xz HAVE_STRUCT_STAT_ST_ATIMESPEC_TV_NSEC)
else()
check_struct_has_member("struct stat" st_atimensec
"sys/types.h;sys/stat.h"
HAVE_STRUCT_STAT_ST_ATIMENSEC)
tuklib_add_definition_if(xz HAVE_STRUCT_STAT_ST_ATIMENSEC)
endif()
endif()
# How to set file time:
check_symbol_exists(futimens "sys/types.h;sys/stat.h" HAVE_FUTIMENS)
if(HAVE_FUTIMENS)
tuklib_add_definitions(xz HAVE_FUTIMENS)
else()
check_symbol_exists(futimes "sys/time.h" HAVE_FUTIMES)
if(HAVE_FUTIMES)
tuklib_add_definitions(xz HAVE_FUTIMES)
else()
check_symbol_exists(futimesat "sys/time.h" HAVE_FUTIMESAT)
if(HAVE_FUTIMESAT)
tuklib_add_definitions(xz HAVE_FUTIMESAT)
else()
check_symbol_exists(utimes "sys/time.h" HAVE_UTIMES)
if(HAVE_UTIMES)
tuklib_add_definitions(xz HAVE_UTIMES)
else()
check_symbol_exists(_futime "sys/utime.h" HAVE__FUTIME)
if(HAVE__FUTIME)
tuklib_add_definitions(xz HAVE__FUTIME)
else()
check_symbol_exists(utime "utime.h" HAVE_UTIME)
tuklib_add_definition_if(xz HAVE_UTIME)
endif()
endif()
endif()
endif()
endif()
if(ENABLE_NLS)
target_link_libraries(xz PRIVATE Intl::Intl)
target_compile_definitions(xz PRIVATE
ENABLE_NLS
PACKAGE="${TRANSLATION_DOMAIN}"
LOCALEDIR="${LOCALEDIR_DEFINITION}"
)
file(STRINGS po/LINGUAS LINGUAS)
# Where to find .gmo files. If msgfmt is available, the .po files
# will be converted as part of the build. Otherwise we will use
# the pre-generated .gmo files which are included in XZ Utils
# tarballs by Autotools.
set(GMO_DIR "${CMAKE_CURRENT_SOURCE_DIR}/po")
if(GETTEXT_FOUND)
# NOTE: gettext_process_po_files' INSTALL_DESTINATION is
# incompatible with how Autotools requires the .po files to
# be named. CMake would require each .po file to be named with
# the translation domain and thus each .po file would need its
# own language-specific directory (like "po/fi/xz.po"). On top
# of this, INSTALL_DESTINATION doesn't allow specifying COMPONENT
# and thus the .mo files go into "Unspecified" component. So we
# can use gettext_process_po_files to convert the .po files but
# installation needs to be done with our own code.
#
# Also, the .gmo files will go to root of the build directory
# instead of neatly into a subdirectory. This is hardcoded in
# CMake's FindGettext.cmake.
foreach(LANG IN LISTS LINGUAS)
gettext_process_po_files("${LANG}" ALL
PO_FILES "${CMAKE_CURRENT_SOURCE_DIR}/po/${LANG}.po")
endforeach()
set(GMO_DIR "${CMAKE_CURRENT_BINARY_DIR}")
endif()
foreach(LANG IN LISTS LINGUAS)
install(
FILES "${GMO_DIR}/${LANG}.gmo"
DESTINATION "${CMAKE_INSTALL_LOCALEDIR}/${LANG}/LC_MESSAGES"
RENAME "${TRANSLATION_DOMAIN}.mo"
2024-02-19 10:20:59 +00:00
COMPONENT xz_Runtime)
endforeach()
endif()
# This command must be before the symlink creation to keep things working
# on Cygwin and MSYS2 in all cases.
#
# - Cygwin can encode symlinks in multiple ways. This can be
# controlled via the environment variable "CYGWIN". If it contains
# "winsymlinks:nativestrict" then symlink creation will fail if
# the link target doesn't exist. This mode isn't the default though.
# See: https://cygwin.com/faq.html#faq.api.symlinks
#
# - MSYS2 supports the same winsymlinks option in the environment
# variable "MSYS" (not "MSYS2). The default in MSYS2 is to make
# a copy of the file instead of any kind of symlink. Thus the link
# target must exist or the creation of the "symlink" (copy) will fail.
#
# Our installation order must be such that when a symbolic link is created
# its target must already exists. There is no race condition for parallel
# builds because the generated cmake_install.cmake executes serially.
install(TARGETS xz
RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}"
2024-02-19 10:20:59 +00:00
COMPONENT xz_Runtime)
if(UNIX)
option(CREATE_XZ_SYMLINKS "Create unxz and xzcat symlinks" ON)
option(CREATE_LZMA_SYMLINKS "Create lzma, unlzma, and lzcat symlinks"
ON)
set(XZ_LINKS)
if(CREATE_XZ_SYMLINKS)
list(APPEND XZ_LINKS "unxz" "xzcat")
endif()
if(CREATE_LZMA_SYMLINKS)
list(APPEND XZ_LINKS "lzma" "unlzma" "lzcat")
endif()
# On Cygwin, don't add the .exe suffix to the symlinks.
#
# FIXME? Does this make sense on MSYS & MSYS2 where "ln -s"
# by default makes copies? Inside MSYS & MSYS2 it is possible
# to execute files without the .exe suffix but not outside
# (like in Command Prompt). Omitting the suffix matches
# what configure.ac has done for many years though.
2024-02-19 10:20:59 +00:00
my_install_symlinks(xz_Runtime "${CMAKE_INSTALL_BINDIR}"
"xz${CMAKE_EXECUTABLE_SUFFIX}" "" "${XZ_LINKS}")
# Install the man pages and (optionally) their symlinks
# and translations.
2024-02-19 10:20:59 +00:00
my_install_man(xz_Documentation src/xz/xz.1 "${XZ_LINKS}")
endif()
endif()
#############################################################################
# Scripts
#############################################################################
if(UNIX)
# NOTE: This isn't as sophisticated as in the Autotools build which
# uses posix-shell.m4 but hopefully this doesn't need to be either.
# CMake likely won't be used on as many (old) obscure systems as the
# Autotools-based builds are.
if(CMAKE_SYSTEM_NAME STREQUAL "SunOS" AND EXISTS "/usr/xpg4/bin/sh")
set(POSIX_SHELL_DEFAULT "/usr/xpg4/bin/sh")
else()
set(POSIX_SHELL_DEFAULT "/bin/sh")
endif()
set(POSIX_SHELL "${POSIX_SHELL_DEFAULT}" CACHE STRING
"Shell to use for scripts (xzgrep and others)")
# Guess the extra path to add from POSIX_SHELL. Autotools-based build
# has a separate option --enable-path-for-scripts=PREFIX but this is
# enough for Solaris.
set(enable_path_for_scripts)
get_filename_component(POSIX_SHELL_DIR "${POSIX_SHELL}" DIRECTORY)
if(NOT POSIX_SHELL_DIR STREQUAL "/bin" AND
NOT POSIX_SHELL_DIR STREQUAL "/usr/bin")
set(enable_path_for_scripts "PATH=${POSIX_SHELL_DIR}:\$PATH")
endif()
set(XZDIFF_LINKS xzcmp)
set(XZGREP_LINKS xzegrep xzfgrep)
set(XZMORE_LINKS)
set(XZLESS_LINKS)
if(CREATE_LZMA_SYMLINKS)
list(APPEND XZDIFF_LINKS lzdiff lzcmp)
list(APPEND XZGREP_LINKS lzgrep lzegrep lzfgrep)
list(APPEND XZMORE_LINKS lzmore)
list(APPEND XZLESS_LINKS lzless)
endif()
set(xz "xz")
foreach(S xzdiff xzgrep xzmore xzless)
configure_file("src/scripts/${S}.in" "${S}"
@ONLY
NEWLINE_STYLE LF)
install(PROGRAMS "${CMAKE_CURRENT_BINARY_DIR}/${S}"
DESTINATION "${CMAKE_INSTALL_BINDIR}"
2024-02-19 10:20:59 +00:00
COMPONENT scripts_Runtime)
endforeach()
# file(CHMOD ...) would need CMake 3.19 so use execute_process instead.
# Using +x is fine even if umask was 077. If execute bit is set at all
# then "make install" will set it for group and other access bits too.
execute_process(COMMAND chmod +x xzdiff xzgrep xzmore xzless
WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}")
unset(xz)
unset(POSIX_SHELL)
unset(enable_path_for_scripts)
2024-02-19 10:20:59 +00:00
my_install_symlinks(scripts_Runtime "${CMAKE_INSTALL_BINDIR}" xzdiff ""
"${XZDIFF_LINKS}")
2024-02-19 10:20:59 +00:00
my_install_symlinks(scripts_Runtime "${CMAKE_INSTALL_BINDIR}" xzgrep ""
"${XZGREP_LINKS}")
2024-02-19 10:20:59 +00:00
my_install_symlinks(scripts_Runtime "${CMAKE_INSTALL_BINDIR}" xzmore ""
"${XZMORE_LINKS}")
2024-02-19 10:20:59 +00:00
my_install_symlinks(scripts_Runtime "${CMAKE_INSTALL_BINDIR}" xzless ""
"${XZLESS_LINKS}")
2024-02-19 10:20:59 +00:00
my_install_man(scripts_Documentation src/scripts/xzdiff.1 "${XZDIFF_LINKS}")
my_install_man(scripts_Documentation src/scripts/xzgrep.1 "${XZGREP_LINKS}")
my_install_man(scripts_Documentation src/scripts/xzmore.1 "${XZMORE_LINKS}")
my_install_man(scripts_Documentation src/scripts/xzless.1 "${XZLESS_LINKS}")
endif()
2024-02-17 16:10:40 +00:00
#############################################################################
# Documentation
#############################################################################
# Use OPTIONAL because doc/api might not exist. The liblzma API docs
# can be generated by running "doxygen/update-doxygen".
install(DIRECTORY doc/api doc/examples
DESTINATION "${CMAKE_INSTALL_DOCDIR}"
COMPONENT liblzma_Documentation
OPTIONAL)
# GPLv2 applies to the scripts. If GNU getopt_long is used then
# LGPLv2.1 applies to the command line tools but, using the
# section 3 of LGPLv2.1, GNU getopt_long can be handled as GPLv2 too.
# Thus GPLv2 should be enough here.
install(FILES AUTHORS
COPYING
COPYING.0BSD
COPYING.GPLv2
NEWS
README
THANKS
doc/faq.txt
doc/history.txt
doc/lzma-file-format.txt
doc/xz-file-format.txt
DESTINATION "${CMAKE_INSTALL_DOCDIR}"
2024-02-19 10:20:59 +00:00
COMPONENT Documentation)
2024-02-17 16:10:40 +00:00
#############################################################################
# Tests
#############################################################################
include(CTest)
if(BUILD_TESTING)
set(LIBLZMA_TESTS
test_bcj_exact_size
test_block_header
test_check
test_filter_flags
test_filter_str
test_hardware
test_index
test_index_hash
test_lzip_decoder
test_memlimit
test_microlzma
test_stream_flags
test_vli
)
foreach(TEST IN LISTS LIBLZMA_TESTS)
add_executable("${TEST}" "tests/${TEST}.c")
target_include_directories("${TEST}" PRIVATE
src/common
src/liblzma/api
src/liblzma
)
target_link_libraries("${TEST}" PRIVATE liblzma)
# Put the test programs into their own subdirectory so they don't
# pollute the top-level dir which might contain xz and xzdec.
set_target_properties("${TEST}" PROPERTIES
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/tests_bin"
)
add_test(NAME "${TEST}"
COMMAND "${CMAKE_CURRENT_BINARY_DIR}/tests_bin/${TEST}"
)
# Set srcdir environment variable so that the tests find their
# input files from the source tree.
#
# Set the return code for skipped tests to match Automake convention.
set_tests_properties("${TEST}" PROPERTIES
ENVIRONMENT "srcdir=${CMAKE_CURRENT_SOURCE_DIR}/tests"
SKIP_RETURN_CODE 77
)
endforeach()
if(UNIX AND HAVE_DECODERS)
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/test_scripts")
add_test(NAME test_scripts.sh
COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/tests/test_scripts.sh" ".."
WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/test_scripts"
)
set_tests_properties(test_scripts.sh PROPERTIES
ENVIRONMENT "srcdir=${CMAKE_CURRENT_SOURCE_DIR}/tests"
SKIP_RETURN_CODE 77
)
endif()
endif()