Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 59 additions & 1 deletion source/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -930,6 +930,30 @@ endif()
configure_file("${PROJECT_SOURCE_DIR}/x265_config.h.in"
"${PROJECT_BINARY_DIR}/x265_config.h")

# Declared before the multilib logic below, which reads it to decide whether
# the 10/12-bit archives should be merged into the static library.
option(ENABLE_SHARED "Build shared library" ON)

# Build a single library containing the 8-bit, 10-bit and 12-bit encoders,
# switching bit depths at runtime (see build/linux/multilib.sh). Toolchain
# settings that CMake cannot infer (e.g. a packaging toolchain's target
# triplet) can be passed through MULTILIB_CMAKE_ARGS.
option(ENABLE_MULTILIB "Build a single library containing all supported bit depths" OFF)
set(MULTILIB_CMAKE_ARGS "" CACHE STRING "Extra CMake arguments for the multilib sub-builds")
set(MULTILIB_ARCHIVER "" CACHE STRING "Archiver used to merge the multilib static libraries")
if(ENABLE_MULTILIB)
include(cmake/multilib.cmake)
elseif(X265_MULTILIB_CACHE_OWNED)
# ENABLE_MULTILIB was switched back OFF in an existing build tree; drop the
# cache entries that the ON configuration force-wrote so the 8-bit library
# stops trying to link the (now stale) multilib archives.
unset(EXTRA_LIB CACHE)
unset(EXTRA_LIB)
unset(LINKED_10BIT CACHE)
unset(LINKED_12BIT CACHE)
unset(X265_MULTILIB_CACHE_OWNED CACHE)
endif()

add_subdirectory(encoder)
add_subdirectory(common)

Expand Down Expand Up @@ -1088,6 +1112,36 @@ endif()
if(EXTRA_LIB)
target_link_libraries(x265-static ${EXTRA_LIB})
endif()
if(ENABLE_MULTILIB)
# Statically merge the 10/12-bit archives into the 8-bit API archive so
# that consumers only need to link against a single library. This runs
# for shared builds as well: the static library is built and installed
# either way, and without the merge it would lack the 10/12-bit objects
# (multilib.sh merges them in this case too)
add_custom_command(TARGET x265-static POST_BUILD
COMMAND "${CMAKE_COMMAND}"
"-DOUTPUT=$<TARGET_FILE:x265-static>"
"-DINPUT1=$<TARGET_FILE:x265-static>"
"-DINPUT2=${CMAKE_CURRENT_BINARY_DIR}/multilib/10bit/${_multilib_archive_name}"
"-DINPUT3=${CMAKE_CURRENT_BINARY_DIR}/multilib/12bit/${_multilib_archive_name}"
"-DARCHIVER=${_multilib_archiver}"
-P "${CMAKE_CURRENT_SOURCE_DIR}/cmake/x265-merge.cmake"
VERBATIM)
endif()
if(ENABLE_MULTILIB)
# Make sure the 10/12-bit sub-builds are done before this library is
# archived and merged
add_dependencies(x265-static x265-multilib-10bit x265-multilib-12bit)
# Run the sub-builds after the 8-bit objects have been compiled (the
# bit-depth libraries are also built one after another), like
# multilib.sh does: a parallel parent build would otherwise interleave
# the nested build logs with the main compile output
foreach(_multilib_objects IN ITEMS encoder common dynamicHDR10 x265-asm)
if(TARGET ${_multilib_objects})
add_dependencies(x265-multilib-10bit ${_multilib_objects})
endif()
endforeach()
endif()
if(ENABLE_LIBVMAF)
target_link_libraries(x265-static ${VMAF})
endif()
Expand Down Expand Up @@ -1155,7 +1209,6 @@ endif()
if(NOT (MSVC_IDE OR XCODE))
add_custom_target(clean-generated COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/clean-generated.cmake)
endif()
option(ENABLE_SHARED "Build shared library" ON)
if(ENABLE_SHARED)
if(ENABLE_HDR10_PLUS)
add_library(x265-shared SHARED "${PROJECT_BINARY_DIR}/x265.def" ${ASM_OBJS}
Expand All @@ -1176,6 +1229,11 @@ if(ENABLE_SHARED)
endif()
if(EXTRA_LIB)
target_link_libraries(x265-shared ${EXTRA_LIB})
endif()
if(ENABLE_MULTILIB)
# Make sure the 10/12-bit sub-builds are done before linking this
# shared library
add_dependencies(x265-shared x265-multilib-10bit x265-multilib-12bit)
endif()
target_link_libraries(x265-shared ${PLATFORM_LIBS})
if(SVTHEVC_FOUND)
Expand Down
270 changes: 270 additions & 0 deletions source/cmake/multilib.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,270 @@
# multilib.cmake - Build the 10-bit and 12-bit static libraries as nested
# sub-builds and link them into the 8-bit API library so that a single library
# can switch bit depths at runtime, following the upstream
# build/linux/multilib.sh recipe.
#
# Included from the top-level CMakeLists.txt when ENABLE_MULTILIB is ON. The
# sub-builds are defined as regular build targets so that they are only built
# when the library is built; the resulting archives are then merged into the
# 8-bit API library by a POST_BUILD step (see CMakeLists.txt).
#
# The nested builds inherit the generator, the toolchain and this project's
# build options, plus the common Android/OHOS target-selection variables. Any
# other toolchain-specific setting that CMake cannot infer (e.g. a packaging
# toolchain's triplet) can be passed through MULTILIB_CMAKE_ARGS.

set(_multilib_root "${CMAKE_CURRENT_BINARY_DIR}/multilib")
set(_multilib_source "${CMAKE_CURRENT_SOURCE_DIR}")

# The nested builds need `file(GLOB_RECURSE ... CONFIGURE_DEPENDS)` and the
# -j option of `cmake --build`, both introduced in CMake 3.12; plain x265
# builds keep supporting older CMake versions
if(CMAKE_VERSION VERSION_LESS 3.12)
message(FATAL_ERROR "ENABLE_MULTILIB requires CMake 3.12 or later. "
"Use build/linux/multilib.sh or the build/vc*-x86_64/multilib.bat "
"scripts to build the multi-bit-depth library with older CMake.")
endif()

# Append "-D<var>=<value>" to the argument list, escaping the list separator.
# Semicolons cannot be stored in list elements, so a value containing a list
# (e.g. CMAKE_OSX_ARCHITECTURES=arm64;x86_64 or ANDROID_ABI with several ABIs)
# is escaped to "\;" here and arrives at the nested configure as a single
# argument with the semicolons restored.
function(x265_multilib_append_d list_var var)
if(DEFINED ${var} AND NOT "${${var}}" STREQUAL "")
string(REPLACE ";" "\\;" _value "${${var}}")
list(APPEND ${list_var} "-D${var}=${_value}")
set(${list_var} "${${list_var}}" PARENT_SCOPE)
endif()
endfunction()

# Inherit the generator, toolchain and platform settings from this build
set(_multilib_common_args
"-DCMAKE_GENERATOR=${CMAKE_GENERATOR}"
"-DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}"
"-DENABLE_SHARED=OFF"
"-DENABLE_CLI=OFF"
"-DEXPORT_C_API=OFF"
)
if(DEFINED CMAKE_GENERATOR_PLATFORM)
list(APPEND _multilib_common_args "-DCMAKE_GENERATOR_PLATFORM=${CMAKE_GENERATOR_PLATFORM}")
endif()
if(DEFINED CMAKE_GENERATOR_TOOLSET AND NOT CMAKE_GENERATOR_TOOLSET STREQUAL "")
list(APPEND _multilib_common_args "-T${CMAKE_GENERATOR_TOOLSET}")
endif()
if(DEFINED CMAKE_MAKE_PROGRAM)
list(APPEND _multilib_common_args "-DCMAKE_MAKE_PROGRAM=${CMAKE_MAKE_PROGRAM}")
endif()
# Inherit the compilers (and any launcher such as ccache/sccache) so the nested
# builds use the same toolchain as the parent instead of whatever CMake happens
# to detect in the build environment
if(DEFINED CMAKE_C_COMPILER)
list(APPEND _multilib_common_args "-DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}")
endif()
if(DEFINED CMAKE_CXX_COMPILER)
list(APPEND _multilib_common_args "-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}")
endif()
if(DEFINED CMAKE_C_COMPILER_LAUNCHER AND NOT CMAKE_C_COMPILER_LAUNCHER STREQUAL "")
list(APPEND _multilib_common_args "-DCMAKE_C_COMPILER_LAUNCHER=${CMAKE_C_COMPILER_LAUNCHER}")
endif()
if(DEFINED CMAKE_CXX_COMPILER_LAUNCHER AND NOT CMAKE_CXX_COMPILER_LAUNCHER STREQUAL "")
list(APPEND _multilib_common_args "-DCMAKE_CXX_COMPILER_LAUNCHER=${CMAKE_CXX_COMPILER_LAUNCHER}")
endif()
if(DEFINED CMAKE_BUILD_TYPE)
list(APPEND _multilib_common_args "-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}")
endif()
if(DEFINED CMAKE_SYSTEM_VERSION)
list(APPEND _multilib_common_args "-DCMAKE_SYSTEM_VERSION=${CMAKE_SYSTEM_VERSION}")
endif()
if(CMAKE_CROSSCOMPILING AND DEFINED CMAKE_SYSTEM_NAME)
# Required for cross-compilation; not propagated for native builds, where
# an explicit CMAKE_SYSTEM_NAME would wrongly put the nested builds into
# cross-compiling mode
list(APPEND _multilib_common_args "-DCMAKE_SYSTEM_NAME=${CMAKE_SYSTEM_NAME}")
endif()
# Inherit this project's build options (STATIC_LINK_CRT matters: mixing /MT
# and /MD objects fails with LNK2038 on MSVC)
foreach(_option IN ITEMS ENABLE_ASSEMBLY ENABLE_PIC ENABLE_LIBNUMA
STATIC_LINK_CRT ENABLE_HDR10_PLUS ENABLE_SVT_HEVC
ENABLE_LIBVMAF ENABLE_ALPHA ENABLE_MULTIVIEW
ENABLE_SCC_EXT)
if(DEFINED ${_option})
list(APPEND _multilib_common_args "-D${_option}=${${_option}}")
endif()
endforeach()
# Inherit user-provided compiler flags so the sub-builds are compiled with
# the same settings as the parent
x265_multilib_append_d(_multilib_common_args CMAKE_C_FLAGS)
x265_multilib_append_d(_multilib_common_args CMAKE_CXX_FLAGS)
# The MSVC runtime selection must match the parent's (LNK2038 otherwise)
if(MSVC)
x265_multilib_append_d(_multilib_common_args CMAKE_MSVC_RUNTIME_LIBRARY)
endif()
# Universal macOS builds (e.g. -DCMAKE_OSX_ARCHITECTURES=arm64;x86_64) must
# produce universal sub-libraries as well
if(APPLE)
x265_multilib_append_d(_multilib_common_args CMAKE_OSX_ARCHITECTURES)
endif()
if(DEFINED CMAKE_DISABLE_FIND_PACKAGE_VLD)
list(APPEND _multilib_common_args "-DCMAKE_DISABLE_FIND_PACKAGE_VLD=${CMAKE_DISABLE_FIND_PACKAGE_VLD}")
endif()
# These are often provided on the configure command line rather than by the
# toolchain file, so forward them explicitly when present
if(DEFINED VERSION)
list(APPEND _multilib_common_args "-DVERSION=${VERSION}")
endif()
if(DEFINED NASM_EXECUTABLE)
list(APPEND _multilib_common_args "-DNASM_EXECUTABLE=${NASM_EXECUTABLE}")
endif()
# Toolchain target selection (e.g. the Android NDK ABI) is passed on the
# command line and lives in plain cache variables, not in the toolchain file;
# forward the common ones so the nested builds target the same ABI. The
# values may themselves be lists (several ANDROID_ABI entries,
# ANDROID_CPP_FEATURES=rtti;exceptions), hence the escaped append.
foreach(_var IN ITEMS ANDROID_ABI ANDROID_ARM_NEON ANDROID_ARM_MODE
ANDROID_PLATFORM ANDROID_STL ANDROID_NDK
ANDROID_TOOLCHAIN ANDROID_CPP_FEATURES
OHOS_ARCH CMAKE_PLATFORM_NO_VERSIONED_SONAME)
x265_multilib_append_d(_multilib_common_args ${_var})
endforeach()
# Packager hook: extra CMake arguments for the nested builds (e.g. the
# toolchain's target selection such as ANDROID_ABI)
if(DEFINED MULTILIB_CMAKE_ARGS AND NOT MULTILIB_CMAKE_ARGS STREQUAL "")
separate_arguments(_multilib_extra_args NATIVE_COMMAND "${MULTILIB_CMAKE_ARGS}")
list(APPEND _multilib_common_args ${_multilib_extra_args})
endif()
# Forward the parallelism of the parent build so the nested builds do not fall
# back to -j1. Prefer CMAKE_BUILD_PARALLEL_LEVEL and otherwise fall back to the
# machine's processor count; the two bit-depth sub-builds are chained
# sequentially, so each may safely use all cores. The level is passed as a
# separate argument: `cmake --build` rejects the combined "-jN" form before
# CMake 3.14.
set(_multilib_parallel_args "")
if(DEFINED ENV{CMAKE_BUILD_PARALLEL_LEVEL} AND NOT "$ENV{CMAKE_BUILD_PARALLEL_LEVEL}" STREQUAL "")
set(_multilib_parallel_args -j "$ENV{CMAKE_BUILD_PARALLEL_LEVEL}")
else()
include(ProcessorCount)
ProcessorCount(_multilib_ncpu)
if(_multilib_ncpu GREATER 0)
set(_multilib_parallel_args -j "${_multilib_ncpu}")
endif()
endif()

# Track the x265 sources so that the nested builds re-run when they change;
# without this the produced archive would be considered up to date forever.
# The build tree is excluded from the tracking: it can be located inside
# source/ (CLion and VS Code do this when they open source/ as the project
# root), and its generated files would make the sub-builds depend on their
# own outputs (circular dependency).
file(GLOB_RECURSE _multilib_depends CONFIGURE_DEPENDS "${_multilib_source}/*")
set(_multilib_build_prefix "${CMAKE_CURRENT_BINARY_DIR}/")
set(_multilib_sources "")
foreach(_dep IN LISTS _multilib_depends)
string(FIND "${_dep}" "${_multilib_build_prefix}" _multilib_dep_pos)
if(NOT _multilib_dep_pos EQUAL 0)
list(APPEND _multilib_sources "${_dep}")
endif()
endforeach()
set(_multilib_depends "${_multilib_sources}")

# The nested builds produce the static archive of the x265-static target
# (libx265.a on non-MSVC, x265-static.lib on MSVC)
if(MSVC)
set(_multilib_archive_name "x265-static.lib")
else()
set(_multilib_archive_name "libx265.a")
endif()

# Archiver used by the static merge. x265-merge.cmake runs in `cmake -P`
# script mode, where WIN32/APPLE/MINGW describe the *host* rather than the
# target, so it cannot be left to auto-detect the tool whenever host and
# target can differ; pass the target toolchain's archiver instead (llvm-ar
# for the Android NDK, GNU ar for MinGW, ...). Apple targets (macOS and
# iOS/tvOS cross-builds) keep the script default: libtool merges Mach-O
# archives for every Apple platform, while Apple's ar does not support the
# MRI scripts used for ELF/COFF archives. Native MSVC and Linux builds also
# keep the default (lib.exe / ar).
if(MULTILIB_ARCHIVER)
set(_multilib_archiver "${MULTILIB_ARCHIVER}")
elseif(MINGW OR (CMAKE_CROSSCOMPILING AND NOT APPLE))
set(_multilib_archiver "${CMAKE_AR}")
else()
set(_multilib_archiver "")
endif()

# Define the bit-depth sub-builds as regular targets that declare their
# produced archive as an output, so that the build system knows how to build
# the archives the main library links against; the main library targets depend
# on them (see CMakeLists.txt).
#
# CMAKE_ARCHIVE_OUTPUT_DIRECTORY is forced so that the produced archive always
# lands directly in the sub-build directory. Multi-config generators such as
# Visual Studio additionally need the per-configuration variants, otherwise
# they append a per-configuration subdirectory to the archive path.
function(x265_define_multilib_variant name dir archive)
set(_archive_dir_args "-DCMAKE_ARCHIVE_OUTPUT_DIRECTORY=${dir}")
if(CMAKE_CONFIGURATION_TYPES)
# Multi-config generators (Visual Studio, Xcode) append a per-config
# subdirectory to the generic path unless the per-config path is set
foreach(_config IN ITEMS DEBUG RELEASE MINSIZEREL RELWITHDEBINFO)
list(APPEND _archive_dir_args "-DCMAKE_ARCHIVE_OUTPUT_DIRECTORY_${_config}=${dir}")
endforeach()
endif()
add_custom_command(
OUTPUT "${archive}"
# The nested configure is driven through the documented `cmake -E
# make_directory`/`-E chdir` tools with the source given as a
# positional argument instead of `cmake -S/-B`, which needs CMake 3.13;
# `cmake -E chdir` does not create the build directory, hence the
# explicit make_directory
COMMAND "${CMAKE_COMMAND}" -E make_directory "${dir}"
COMMAND "${CMAKE_COMMAND}" -E chdir "${dir}" "${CMAKE_COMMAND}"
${_multilib_common_args} ${ARGN}
${_archive_dir_args}
"${_multilib_source}"
# Run the nested build with an empty MAKEFLAGS so the nested make does
# not try to join the parent's jobserver ("resetting jobserver mode"
# warnings); the explicit -j above gives it its own parallelism
COMMAND "${CMAKE_COMMAND}" -E env MAKEFLAGS= "${CMAKE_COMMAND}"
--build "${dir}" --config "$<CONFIG>"
${_multilib_parallel_args}
DEPENDS ${_multilib_depends}
COMMENT "Building the ${name} x265 sub-library"
VERBATIM)
add_custom_target(${name} DEPENDS "${archive}")
endfunction()

x265_define_multilib_variant(x265-multilib-10bit "${_multilib_root}/10bit"
"${_multilib_root}/10bit/${_multilib_archive_name}" "-DHIGH_BIT_DEPTH=ON")
x265_define_multilib_variant(x265-multilib-12bit "${_multilib_root}/12bit"
"${_multilib_root}/12bit/${_multilib_archive_name}" "-DHIGH_BIT_DEPTH=ON" "-DMAIN12=ON")
# Build the bit-depth sub-libraries sequentially (like build/linux/multilib.sh)
# so that a parallel parent build does not run two nested builds at once
add_dependencies(x265-multilib-12bit x265-multilib-10bit)

# Link the 10/12-bit archives into this build; the LINKED_* options enable the
# bit-depth dispatch in the exported C API (see encoder/api.cpp). A user
# provided EXTRA_LIB is respected (with a warning) instead of being silently
# discarded; the cache entries are marked as owned so they can be cleared when
# ENABLE_MULTILIB is turned back OFF in the same build tree.
set(_multilib_extra_lib
"${_multilib_root}/10bit/${_multilib_archive_name};${_multilib_root}/12bit/${_multilib_archive_name}")
if(EXTRA_LIB AND NOT "${EXTRA_LIB}" STREQUAL "${_multilib_extra_lib}")
message(WARNING "ENABLE_MULTILIB is enabled but EXTRA_LIB is already set; the multilib archives will not be linked automatically")
if(X265_MULTILIB_CACHE_OWNED)
# A previous ENABLE_MULTILIB configuration force-set these entries;
# the user's own EXTRA_LIB takes over now, so restore the LINKED_*
# defaults (they referred to the multilib archives, which are no
# longer linked) and release the ownership so that turning
# ENABLE_MULTILIB back OFF will not delete the user's value
unset(LINKED_10BIT CACHE)
unset(LINKED_12BIT CACHE)
unset(X265_MULTILIB_CACHE_OWNED CACHE)
endif()
else()
set(EXTRA_LIB "${_multilib_extra_lib}"
CACHE STRING "Extra libraries to link against" FORCE)
set(LINKED_10BIT ON CACHE BOOL "10bit libx265 is being linked with this library" FORCE)
set(LINKED_12BIT ON CACHE BOOL "12bit libx265 is being linked with this library" FORCE)
set(X265_MULTILIB_CACHE_OWNED ON CACHE INTERNAL "x265 multilib force-set EXTRA_LIB/LINKED_10BIT/LINKED_12BIT")
endif()
Loading
Loading