Integrate macOS code signing into CMake

Moves code signing from the release-tool to CMake and unifies the Windows-equivalent code.
This commit is contained in:
Janek Bevendorff
2025-11-15 02:02:59 +01:00
committed by Jonathan White
parent c09ba0113b
commit 4e59c1c579
8 changed files with 301 additions and 232 deletions

View File

@@ -36,7 +36,7 @@ find_library(
NAMES ${BOTAN_NAMES}
PATH_SUFFIXES release/lib lib
DOC "The Botan (release) library")
if(MSVC)
if(WIN32 AND NOT MINGW)
find_library(
BOTAN_LIBRARY_DEBUG
NAMES ${BOTAN_NAMES_DEBUG}
@@ -55,7 +55,7 @@ endif()
if(BOTAN_FOUND)
set(BOTAN_INCLUDE_DIRS ${BOTAN_INCLUDE_DIR})
if(MSVC)
if(WIN32 AND NOT MINGW)
set(BOTAN_LIBRARIES optimized ${BOTAN_LIBRARY} debug ${BOTAN_LIBRARY_DEBUG})
else()
set(BOTAN_LIBRARIES ${BOTAN_LIBRARY})

View File

@@ -15,7 +15,7 @@
find_path(QRENCODE_INCLUDE_DIR NAMES qrencode.h)
if(WIN32 AND MSVC)
if(WIN32 AND NOT MINGW)
find_library(QRENCODE_LIBRARY_RELEASE qrencode)
find_library(QRENCODE_LIBRARY_DEBUG qrencoded)
set(QRENCODE_LIBRARY optimized ${QRENCODE_LIBRARY_RELEASE} debug ${QRENCODE_LIBRARY_DEBUG})

View File

@@ -0,0 +1,102 @@
# Copyright (C) 2025 KeePassXC Team <team@keepassxc.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 or (at your option)
# version 3 of the License.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# CPACK_PACKAGE_FILES is set only during POST_BUILD
if(NOT CPACK_PACKAGE_FILES) # PRE_BUILD: Sign binaries
set(PROGNAME "@PROGNAME@")
set(CODESIGN_IDENTITY "@WITH_XC_CODESIGN_IDENTITY@")
set(ENTITLEMENTS @MACOSX_BUNDLE_APPLE_ENTITLEMENTS@)
set(APP_DIR "${CPACK_TEMPORARY_INSTALL_DIRECTORY}/ALL_IN_ONE/${PROGNAME}.app")
if(NOT CODESIGN_IDENTITY)
message(FATAL_ERROR "No codesign identity specified.")
endif()
message(STATUS "Codesign identity used: ${CODESIGN_IDENTITY}")
message(STATUS "Signing ${PROGNAME}.app, this may take while...")
# Sign all binaries
execute_process(
COMMAND xcrun codesign --sign=${CODESIGN_IDENTITY} --force --options=runtime --deep ${APP_DIR}
RESULT_VARIABLE SIGN_RESULT
OUTPUT_VARIABLE SIGN_OUTPUT
ERROR_VARIABLE SIGN_ERROR
OUTPUT_STRIP_TRAILING_WHITESPACE
ERROR_STRIP_TRAILING_WHITESPACE
ECHO_OUTPUT_VARIABLE
)
if (NOT SIGN_RESULT EQUAL 0)
message(FATAL_ERROR "Signing binaries failed: ${SIGN_ERROR}")
endif()
# (Re-)Sign main executable with --entitlements
execute_process(
COMMAND xcrun codesign --sign=${CODESIGN_IDENTITY} --force --options=runtime --deep --entitlements=${ENTITLEMENTS} ${APP_DIR}
RESULT_VARIABLE SIGN_RESULT
OUTPUT_VARIABLE SIGN_OUTPUT
ERROR_VARIABLE SIGN_ERROR
OUTPUT_STRIP_TRAILING_WHITESPACE
ERROR_STRIP_TRAILING_WHITESPACE
ECHO_OUTPUT_VARIABLE
)
if (NOT SIGN_RESULT EQUAL 0)
message(FATAL_ERROR "Signing main binary failed: ${SIGN_ERROR}")
endif()
message(STATUS "${PROGNAME}.app signed successfully.")
else() # POST_BUILD: Notarize DMG
set(KEYCHAIN_PROFILE "@WITH_XC_NOTARY_KEYCHAIN_PROFILE@")
file(GLOB_RECURSE DMG_FILE "${CPACK_PACKAGE_DIRECTORY}/${CPACK_PACKAGE_FILE_NAME}.dmg")
if(NOT KEYCHAIN_PROFILE)
message(FATAL_ERROR "No notarization credentials keychain profile specified.")
endif()
# Submit for notarization
message(STATUS "Submitting DMG bundle for notarization, this may take while...")
execute_process(
COMMAND xcrun notarytool submit --keychain-profile=${KEYCHAIN_PROFILE} --wait ${DMG_FILE}
RESULT_VARIABLE NOTARIZE_RESULT
OUTPUT_VARIABLE NOTARIZE_OUTPUT
ERROR_VARIABLE NOTARIZE_ERROR
OUTPUT_STRIP_TRAILING_WHITESPACE
ERROR_STRIP_TRAILING_WHITESPACE
ECHO_OUTPUT_VARIABLE
)
if (NOT NOTARIZE_RESULT EQUAL 0)
message(FATAL_ERROR "Notarization failed: ${NOTARIZE_ERROR}")
endif()
message(STATUS "DMG bundle notarized successfully.")
# Staple tickets
message(STATUS "Stapling notarization ticket...")
execute_process(
COMMAND xcrun stapler staple ${DMG_FILE} && xcrun stapler validate ${DMG_FILE}
RESULT_VARIABLE STAPLE_RESULT
OUTPUT_VARIABLE STAPLE_OUTPUT
ERROR_VARIABLE STAPLE_ERROR
OUTPUT_STRIP_TRAILING_WHITESPACE
ERROR_STRIP_TRAILING_WHITESPACE
ECHO_OUTPUT_VARIABLE
)
if (NOT STAPLE_RESULT EQUAL 0)
message(FATAL_ERROR "Stapling failed: ${STAPLE_ERROR}")
endif()
message(STATUS "DMG bundle notarization ticket stapled successfully.")
endif()

View File

@@ -0,0 +1,79 @@
# Copyright (C) 2025 KeePassXC Team <team@keepassxc.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 or (at your option)
# version 3 of the License.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
set(INSTALL_DIR ${CPACK_TEMPORARY_INSTALL_DIRECTORY})
set(CODESIGN_IDENTITY @WITH_XC_CODESIGN_IDENTITY@)
set(TIMESTAMP_URL @WITH_XC_CODESIGN_TIMESTAMP_URL@)
if(CPACK_PACKAGE_FILES)
# This variable is set only during POST_BUILD, reset SIGN_FILES first
set(SIGN_FILES "")
foreach(PACKAGE_FILE ${CPACK_PACKAGE_FILES})
# Check each package file to see if it can be signed
if(PACKAGE_FILE MATCHES "\\.msix?$" OR PACKAGE_FILE MATCHES "\\.exe$")
message(STATUS "Adding ${PACKAGE_FILE} for signature")
list(APPEND SIGN_FILES "${PACKAGE_FILE}")
endif()
endforeach()
else()
# Setup portable zip file if building one
if(INSTALL_DIR MATCHES "/ZIP/")
file(TOUCH "${INSTALL_DIR}/.portable")
message(STATUS "Injected portable marker into ZIP file.")
endif()
# Find all dll and exe files in the install directory
file(GLOB_RECURSE SIGN_FILES
RELATIVE "${INSTALL_DIR}"
"${INSTALL_DIR}/*.dll"
"${INSTALL_DIR}/*.exe"
)
endif()
# Sign relevant binaries if requested
if(CODESIGN_IDENTITY AND SIGN_FILES)
# Find signtool in PATH or error out
find_program(SIGNTOOL signtool.exe QUIET)
if(NOT SIGNTOOL)
message(FATAL_ERROR "signtool.exe not found in PATH, correct or unset WITH_XC_CODESIGN_IDENTITY")
endif()
# Check that a certificate thumbprint was provided or error out
if(CODESIGN_IDENTITY STREQUAL "auto")
message(STATUS "Signing using best available certificate.")
set(CERT_OPTS /a)
else ()
message(STATUS "Signing using certificate with fingerprint ${CODESIGN_IDENTITY}.")
set(CERT_OPTS /sha1 ${CODESIGN_IDENTITY})
endif()
message(STATUS "Signing binary files, this may take a while...")
# Use cmd /c to enable pop-up for pin entry if needed
execute_process(
COMMAND cmd /c ${SIGNTOOL} sign /fd SHA256 ${CERT_OPTS} /tr ${TIMESTAMP_URL} /td SHA256 /d ${CPACK_PACKAGE_FILE_NAME} ${SIGN_FILES}
WORKING_DIRECTORY "${INSTALL_DIR}"
RESULT_VARIABLE SIGN_RESULT
OUTPUT_VARIABLE SIGN_OUTPUT
ERROR_VARIABLE SIGN_ERROR
OUTPUT_STRIP_TRAILING_WHITESPACE
ERROR_STRIP_TRAILING_WHITESPACE
ECHO_OUTPUT_VARIABLE
)
if(NOT SIGN_RESULT EQUAL 0)
message(FATAL_ERROR "Signing binary files failed: ${SIGN_ERROR}")
endif()
message(STATUS "Binary files signed successfully.")
endif()

View File

@@ -1,71 +0,0 @@
# Copyright (C) 2025 KeePassXC Team <team@keepassxc.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 or (at your option)
# version 3 of the License.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
set(_installdir ${CPACK_TEMPORARY_INSTALL_DIRECTORY})
set(_sign @WITH_XC_SIGNINSTALL@)
set(_cert_thumbprint @WITH_XC_SIGNINSTALL_CERT@)
set(_timestamp_url @WITH_XC_SIGNINSTALL_TIMESTAMP_URL@)
# Setup portable zip file if building one
if(_installdir MATCHES "/ZIP/")
file(TOUCH "${_installdir}/.portable")
message(STATUS "Injected portable zip file.")
endif()
# Find all dll and exe files in the install directory
file(GLOB_RECURSE _sign_files
RELATIVE "${_installdir}"
"${_installdir}/*.dll"
"${_installdir}/*.exe"
)
# Sign relevant binaries if requested
if(_sign AND _sign_files)
# Find signtool in PATH or error out
find_program(_signtool signtool.exe QUIET)
if(NOT _signtool)
message(FATAL_ERROR "signtool.exe not found in PATH, correct or unset WITH_XC_SIGNINSTALL")
endif()
# Set a default timestamp URL if none was provided
if (NOT _timestamp_url)
set(_timestamp_url "http://timestamp.sectigo.com")
endif()
# Check that a certificate thumbprint was provided or error out
if (NOT _cert_thumbprint)
message(STATUS "Signing using best available certificate.")
set(_certopt /a)
else()
message(STATUS "Signing using certificate with thumbprint ${_cert_thumbprint}.")
set(_certopt /sha1 ${_cert_thumbprint})
endif()
message(STATUS "Signing binary files with signtool, this may take a while...")
# Use cmd /c to enable pop-up for pin entry if needed
execute_process(
COMMAND cmd /c ${_signtool} sign /fd SHA256 ${_certopt} /tr ${_timestamp_url} /td SHA256 ${_sign_files}
WORKING_DIRECTORY "${_installdir}"
RESULT_VARIABLE sign_result
OUTPUT_VARIABLE sign_output
ERROR_VARIABLE sign_error
OUTPUT_STRIP_TRAILING_WHITESPACE
ERROR_STRIP_TRAILING_WHITESPACE
ECHO_OUTPUT_VARIABLE
)
if (NOT sign_result EQUAL 0)
message(FATAL_ERROR "signtool failed: ${sign_error}")
endif()
endif()