Revision control

Copy as Markdown

# Copyright (C) 2026 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
# Mangles Objective-C class and category names in a target's binary. See the
# qt_mangle_objc_symbols() CMake command reference for full documentation.
#
# This function is currently in Technology Preview.
# Its signature and behavior might change.
function(qt6_mangle_objc_symbols target)
set(options
GENERATE_RANDOM_NEW_NAMESPACE
QUIET
CODESIGN
USE_QT_NAMESPACE_AS_OLD
)
set(oneValueArgs
OLD_NAMESPACE
NEW_NAMESPACE
CODESIGN_IDENTITY
)
set(multiValueArgs
EXCLUDE_CLASSES
)
cmake_parse_arguments(PARSE_ARGV 1 arg "${options}" "${oneValueArgs}"
"${multiValueArgs}")
if(NOT arg_OLD_NAMESPACE AND NOT arg_USE_QT_NAMESPACE_AS_OLD)
message(FATAL_ERROR
"qt6_mangle_objc_symbols: Either OLD_NAMESPACE or "
"USE_QT_NAMESPACE_AS_OLD argument is required")
endif()
if(arg_OLD_NAMESPACE AND arg_USE_QT_NAMESPACE_AS_OLD)
message(FATAL_ERROR
"qt6_mangle_objc_symbols: OLD_NAMESPACE and "
"USE_QT_NAMESPACE_AS_OLD are mutually exclusive")
endif()
if(NOT TARGET ${target})
message(FATAL_ERROR
"qt6_mangle_objc_symbols: ${target} is not a valid target")
endif()
# Handle USE_QT_NAMESPACE_AS_OLD mode
if(arg_USE_QT_NAMESPACE_AS_OLD)
if(NOT TARGET ${QT_CMAKE_EXPORT_NAMESPACE}::Core)
message(FATAL_ERROR
"qt6_mangle_objc_symbols: USE_QT_NAMESPACE_AS_OLD mode requires "
"Qt::Core target to be available")
endif()
get_target_property(qt_namespace ${QT_CMAKE_EXPORT_NAMESPACE}::Core
QT_NAMESPACE)
if(NOT qt_namespace OR qt_namespace STREQUAL "qt_namespace-NOTFOUND")
message(FATAL_ERROR
"qt6_mangle_objc_symbols: Could not determine Qt namespace from "
"Qt::Core. QT_NAMESPACE property is not set.")
endif()
set(arg_OLD_NAMESPACE "${qt_namespace}")
endif()
# Check that either NEW_NAMESPACE or GENERATE_RANDOM_NEW_NAMESPACE
# is specified
if(NOT arg_NEW_NAMESPACE AND NOT arg_GENERATE_RANDOM_NEW_NAMESPACE)
message(FATAL_ERROR
"qt6_mangle_objc_symbols: Either NEW_NAMESPACE or "
"GENERATE_RANDOM_NEW_NAMESPACE must be specified")
endif()
if(arg_NEW_NAMESPACE AND arg_GENERATE_RANDOM_NEW_NAMESPACE)
message(FATAL_ERROR
"qt6_mangle_objc_symbols: NEW_NAMESPACE and "
"GENERATE_RANDOM_NEW_NAMESPACE are mutually exclusive")
endif()
# Check if we're building on a platform that supports Objective-C mangling
# (macOS/iOS)
if(NOT APPLE)
if(NOT arg_QUIET)
message(WARNING
"qt6_mangle_objc_symbols: Objective-C mangling is only supported "
"on Apple platforms. Skipping.")
endif()
return()
endif()
# Build the command arguments
set(mangler_args "")
if(arg_QUIET)
list(APPEND mangler_args --quiet)
endif()
# Add excluded classes
if(arg_EXCLUDE_CLASSES)
foreach(excluded_class IN LISTS arg_EXCLUDE_CLASSES)
list(APPEND mangler_args --exclude "${excluded_class}")
endforeach()
endif()
# Determine the replacement string
if(arg_GENERATE_RANDOM_NEW_NAMESPACE)
# Generate a random string of the same length as the namespace
string(LENGTH "${arg_OLD_NAMESPACE}" namespace_len)
string(RANDOM LENGTH ${namespace_len}
ALPHABET
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
replacement_namespace)
set(comment_suffix "with random namespace '${replacement_namespace}'")
else()
set(replacement_namespace "${arg_NEW_NAMESPACE}")
# Validate that replacement has the same length as namespace
string(LENGTH "${arg_OLD_NAMESPACE}" namespace_len)
string(LENGTH "${replacement_namespace}" replacement_len)
if(arg_USE_QT_NAMESPACE_AS_OLD)
# In USE_QT_NAMESPACE_AS_OLD mode, pad with underscores if
# shorter, warn if longer
if(replacement_len LESS namespace_len)
string(REPEAT "_" ${namespace_len} padding)
string(SUBSTRING "${padding}" ${replacement_len} -1
needed_padding)
set(replacement_namespace
"${replacement_namespace}${needed_padding}")
string(LENGTH "${replacement_namespace}" replacement_len)
elseif(replacement_len GREATER namespace_len)
message(WARNING
"qt6_mangle_objc_symbols: NEW_NAMESPACE "
"'${replacement_namespace}' is longer than Qt namespace "
"'${arg_OLD_NAMESPACE}' (${replacement_len} > ${namespace_len}). "
"Truncating to match length.")
string(SUBSTRING "${replacement_namespace}" 0 ${namespace_len}
replacement_namespace)
string(LENGTH "${replacement_namespace}" replacement_len)
endif()
else()
# In manual OLD_NAMESPACE mode, require exact length match
if(NOT namespace_len EQUAL replacement_len)
message(FATAL_ERROR
"qt6_mangle_objc_symbols: NEW_NAMESPACE "
"'${replacement_namespace}' must be the same length as "
"OLD_NAMESPACE '${arg_OLD_NAMESPACE}' "
"(${replacement_len} != ${namespace_len})")
endif()
endif()
set(comment_suffix "with namespace '${replacement_namespace}'")
endif()
list(APPEND mangler_args --replace "${arg_OLD_NAMESPACE}"
"${replacement_namespace}")
# Build the full command
set(commands "")
set(target_file "$<TARGET_FILE:${target}>")
list(APPEND commands
COMMAND "$<TARGET_FILE:${QT_CMAKE_EXPORT_NAMESPACE}::objcnamemangler>"
${mangler_args}
"${target_file}"
)
if(arg_CODESIGN)
if(NOT arg_CODESIGN_IDENTITY)
set(arg_CODESIGN_IDENTITY "-")
endif()
list(APPEND commands
COMMAND codesign --force --sign "${arg_CODESIGN_IDENTITY}"
"${target_file}"
)
set(comment_suffix "${comment_suffix}, then re-signing")
endif()
add_custom_command(TARGET ${target} POST_BUILD
${commands}
VERBATIM
COMMENT "Mangling Objective-C symbols in namespace '${arg_OLD_NAMESPACE}' ${comment_suffix}"
)
endfunction()
if(NOT QT_NO_CREATE_VERSIONLESS_FUNCTIONS)
#[[
qt_mangle_objc_symbols(<target>
(OLD_NAMESPACE <namespace> | USE_QT_NAMESPACE_AS_OLD)
(NEW_NAMESPACE <replacement> | GENERATE_RANDOM_NEW_NAMESPACE)
[EXCLUDE_CLASSES <class>...]
[QUIET]
[CODESIGN]
[CODESIGN_IDENTITY <identity>]
)
This is a versionless wrapper for qt6_mangle_objc_symbols().
See qt6_mangle_objc_symbols() for detailed documentation.
#]]
function(qt_mangle_objc_symbols target)
if(QT_DEFAULT_MAJOR_VERSION EQUAL 6)
qt6_mangle_objc_symbols(${target} ${ARGN})
else()
message(FATAL_ERROR
"qt_mangle_objc_symbols() is only available in Qt 6.")
endif()
endfunction()
endif()