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
78 changes: 78 additions & 0 deletions CMake/FixMacOSBundleSymlinks.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
if(NOT DEFINED BUNDLE_PATH OR BUNDLE_PATH STREQUAL "")
message(FATAL_ERROR "BUNDLE_PATH is required")
endif()

set(FRAMEWORKS_PATH "${BUNDLE_PATH}/Contents/Frameworks")

if(NOT IS_DIRECTORY "${BUNDLE_PATH}")
message(FATAL_ERROR "App bundle does not exist: ${BUNDLE_PATH}")
endif()

if(NOT IS_DIRECTORY "${FRAMEWORKS_PATH}")
message(STATUS "No embedded Frameworks directory; nothing to fix")
return()
endif()

execute_process(
COMMAND find "${FRAMEWORKS_PATH}" -type l -print
RESULT_VARIABLE FIND_RESULT
OUTPUT_VARIABLE SYMLINK_OUTPUT
ERROR_VARIABLE FIND_ERROR
OUTPUT_STRIP_TRAILING_WHITESPACE
)

if(NOT FIND_RESULT EQUAL 0)
message(FATAL_ERROR "Could not inspect bundle symlinks: ${FIND_ERROR}")
endif()

if(SYMLINK_OUTPUT STREQUAL "")
message(STATUS "No framework symlinks found")
return()
endif()

string(REPLACE "\n" ";" SYMLINKS "${SYMLINK_OUTPUT}")
set(UNRESOLVED_SYMLINKS)

foreach(SYMLINK IN LISTS SYMLINKS)
if(IS_SYMLINK "${SYMLINK}" AND NOT EXISTS "${SYMLINK}")
if(SYMLINK MATCHES "/lib/python[^/]+/site-packages$")
message(STATUS "Removing external Homebrew site-packages link: ${SYMLINK}")
file(REMOVE "${SYMLINK}")
file(MAKE_DIRECTORY "${SYMLINK}")
else()
list(APPEND UNRESOLVED_SYMLINKS "${SYMLINK}")
endif()
endif()
endforeach()

if(UNRESOLVED_SYMLINKS)
list(JOIN UNRESOLVED_SYMLINKS "\n " UNRESOLVED_TEXT)
message(FATAL_ERROR
"Unexpected broken symlinks remain in the app bundle:\n ${UNRESOLVED_TEXT}\n"
"Refusing to produce a potentially corrupt release."
)
endif()

set(DECODERS_PATH "${BUNDLE_PATH}/Contents/share/libsigrokdecode4DSL/decoders")
if(IS_DIRECTORY "${DECODERS_PATH}")
execute_process(
COMMAND find "${DECODERS_PATH}" -type d -name __pycache__ -print
RESULT_VARIABLE CACHE_FIND_RESULT
OUTPUT_VARIABLE CACHE_OUTPUT
ERROR_VARIABLE CACHE_FIND_ERROR
OUTPUT_STRIP_TRAILING_WHITESPACE
)
if(NOT CACHE_FIND_RESULT EQUAL 0)
message(FATAL_ERROR "Could not inspect Python bytecode caches: ${CACHE_FIND_ERROR}")
endif()
if(NOT CACHE_OUTPUT STREQUAL "")
string(REPLACE "\n" ";" CACHE_DIRECTORIES "${CACHE_OUTPUT}")
foreach(CACHE_DIRECTORY IN LISTS CACHE_DIRECTORIES)
file(REMOVE_RECURSE "${CACHE_DIRECTORY}")
endforeach()
list(LENGTH CACHE_DIRECTORIES CACHE_COUNT)
message(STATUS "Removed ${CACHE_COUNT} Python bytecode cache directories")
endif()
endif()

message(STATUS "macOS bundle symlinks validated")
108 changes: 108 additions & 0 deletions CMake/ValidateMacOSBundle.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
if(NOT DEFINED BUNDLE_PATH OR BUNDLE_PATH STREQUAL "")
message(FATAL_ERROR "BUNDLE_PATH is required")
endif()

if(NOT IS_DIRECTORY "${BUNDLE_PATH}")
message(FATAL_ERROR "App bundle does not exist: ${BUNDLE_PATH}")
endif()

set(CONTENTS_PATH "${BUNDLE_PATH}/Contents")
set(FRAMEWORKS_PATH "${CONTENTS_PATH}/Frameworks")
set(MACOS_PATH "${CONTENTS_PATH}/MacOS")

execute_process(
COMMAND find
"${MACOS_PATH}"
"${FRAMEWORKS_PATH}"
"${CONTENTS_PATH}/PlugIns"
-type f -print
RESULT_VARIABLE FIND_RESULT
OUTPUT_VARIABLE CANDIDATE_OUTPUT
ERROR_VARIABLE FIND_ERROR
OUTPUT_STRIP_TRAILING_WHITESPACE
)

if(NOT FIND_RESULT EQUAL 0)
message(FATAL_ERROR "Could not inspect app bundle binaries: ${FIND_ERROR}")
endif()

if(CANDIDATE_OUTPUT STREQUAL "")
message(FATAL_ERROR "No executable files found in app bundle: ${BUNDLE_PATH}")
endif()

string(REPLACE "\n" ";" CANDIDATES "${CANDIDATE_OUTPUT}")
set(UNRESOLVED_DEPENDENCIES)
set(EXTERNAL_DEPENDENCIES)
set(MACHO_COUNT 0)

foreach(BINARY IN LISTS CANDIDATES)
execute_process(
COMMAND /usr/bin/file -b "${BINARY}"
OUTPUT_VARIABLE FILE_TYPE
OUTPUT_STRIP_TRAILING_WHITESPACE
)
if(NOT FILE_TYPE MATCHES "Mach-O")
continue()
endif()

math(EXPR MACHO_COUNT "${MACHO_COUNT} + 1")
execute_process(
COMMAND /usr/bin/otool -L "${BINARY}"
RESULT_VARIABLE OTOOL_RESULT
OUTPUT_VARIABLE OTOOL_OUTPUT
ERROR_VARIABLE OTOOL_ERROR
)
if(NOT OTOOL_RESULT EQUAL 0)
message(FATAL_ERROR "otool failed for ${BINARY}: ${OTOOL_ERROR}")
endif()

get_filename_component(LOADER_PATH "${BINARY}" DIRECTORY)
string(REPLACE "\n" ";" DEPENDENCY_LINES "${OTOOL_OUTPUT}")
set(SKIP_INSTALL_NAME FALSE)
if(FILE_TYPE MATCHES "dynamically linked shared library")
set(SKIP_INSTALL_NAME TRUE)
endif()
foreach(LINE IN LISTS DEPENDENCY_LINES)
if(NOT LINE MATCHES "^[ \t]+([^ \t]+)[ \t]+\\(")
continue()
endif()
if(SKIP_INSTALL_NAME)
set(SKIP_INSTALL_NAME FALSE)
continue()
endif()
set(DEPENDENCY "${CMAKE_MATCH_1}")
set(RESOLVED_PATH "")

if(DEPENDENCY MATCHES "^@rpath/(.+)$")
set(RESOLVED_PATH "${FRAMEWORKS_PATH}/${CMAKE_MATCH_1}")
elseif(DEPENDENCY MATCHES "^@executable_path/(.+)$")
set(RESOLVED_PATH "${MACOS_PATH}/${CMAKE_MATCH_1}")
elseif(DEPENDENCY MATCHES "^@loader_path/(.+)$")
set(RESOLVED_PATH "${LOADER_PATH}/${CMAKE_MATCH_1}")
elseif(DEPENDENCY MATCHES "^/")
if(NOT DEPENDENCY MATCHES "^/System/" AND NOT DEPENDENCY MATCHES "^/usr/lib/")
list(APPEND EXTERNAL_DEPENDENCIES "${BINARY}: ${DEPENDENCY}")
endif()
endif()

if(NOT RESOLVED_PATH STREQUAL "" AND NOT EXISTS "${RESOLVED_PATH}")
list(APPEND UNRESOLVED_DEPENDENCIES "${BINARY}: ${DEPENDENCY}")
endif()
endforeach()
endforeach()

if(MACHO_COUNT EQUAL 0)
message(FATAL_ERROR "No Mach-O binaries found in app bundle: ${BUNDLE_PATH}")
endif()

if(UNRESOLVED_DEPENDENCIES)
list(JOIN UNRESOLVED_DEPENDENCIES "\n " UNRESOLVED_TEXT)
message(FATAL_ERROR "Unresolved bundled dependencies:\n ${UNRESOLVED_TEXT}")
endif()

if(EXTERNAL_DEPENDENCIES)
list(JOIN EXTERNAL_DEPENDENCIES "\n " EXTERNAL_TEXT)
message(FATAL_ERROR "Non-system absolute dependencies remain:\n ${EXTERNAL_TEXT}")
endif()

message(STATUS "Validated ${MACHO_COUNT} Mach-O binaries in the app bundle")
139 changes: 125 additions & 14 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
## along with this program. If not, see <http://www.gnu.org/licenses/>.
##

cmake_minimum_required(VERSION 2.8.6)
cmake_minimum_required(VERSION 3.16)

project(DSView)

Expand Down Expand Up @@ -631,7 +631,17 @@ else()
list(APPEND DSVIEW_LINK_LIBS ${PKGDEPS_LIBRARIES})
endif()

add_executable(${PROJECT_NAME}
set(DSVIEW_EXECUTABLE_OPTIONS)

if(APPLE)
list(APPEND DSVIEW_EXECUTABLE_OPTIONS MACOSX_BUNDLE)
set_source_files_properties(DSView.icns PROPERTIES
MACOSX_PACKAGE_LOCATION "Resources"
)
list(APPEND DSView_SOURCES DSView.icns)
endif()

add_executable(${PROJECT_NAME} ${DSVIEW_EXECUTABLE_OPTIONS}
${common_SOURCES}
${DSView_SOURCES}
${DSView_HEADERS_MOC}
Expand All @@ -643,6 +653,45 @@ add_executable(${PROJECT_NAME}

target_link_libraries(${PROJECT_NAME} ${DSVIEW_LINK_LIBS})

if(APPLE)
set_target_properties(${PROJECT_NAME} PROPERTIES
MACOSX_BUNDLE_BUNDLE_NAME "DSView"
MACOSX_BUNDLE_GUI_IDENTIFIER "com.dreamsourcelab.DSView"
MACOSX_BUNDLE_ICON_FILE "DSView.icns"
MACOSX_BUNDLE_BUNDLE_VERSION "${DS_VERSION_STRING}"
MACOSX_BUNDLE_SHORT_VERSION_STRING "${DS_VERSION_STRING}"
)

set(DSVIEW_BUNDLE_SHARE_DIR "$<TARGET_BUNDLE_DIR:${PROJECT_NAME}>/Contents/share")
add_custom_target(stage_bundle_resources ALL
COMMAND ${CMAKE_COMMAND} -E make_directory
"${DSVIEW_BUNDLE_SHARE_DIR}/DSView"
"${DSVIEW_BUNDLE_SHARE_DIR}/libsigrokdecode4DSL"
COMMAND ${CMAKE_COMMAND} -E copy_directory
"${CMAKE_CURRENT_SOURCE_DIR}/DSView/res"
"${DSVIEW_BUNDLE_SHARE_DIR}/DSView/res"
COMMAND ${CMAKE_COMMAND} -E copy_directory
"${CMAKE_CURRENT_SOURCE_DIR}/DSView/demo"
"${DSVIEW_BUNDLE_SHARE_DIR}/DSView/demo"
COMMAND ${CMAKE_COMMAND} -E copy_directory
"${CMAKE_CURRENT_SOURCE_DIR}/lang"
"${DSVIEW_BUNDLE_SHARE_DIR}/DSView/lang"
COMMAND ${CMAKE_COMMAND} -E copy_directory
"${CMAKE_CURRENT_SOURCE_DIR}/libsigrokdecode4DSL/decoders"
"${DSVIEW_BUNDLE_SHARE_DIR}/libsigrokdecode4DSL/decoders"
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${CMAKE_CURRENT_SOURCE_DIR}/DSView/icons/logo.svg"
"${CMAKE_CURRENT_SOURCE_DIR}/NEWS25"
"${CMAKE_CURRENT_SOURCE_DIR}/NEWS31"
"${CMAKE_CURRENT_SOURCE_DIR}/ug25.pdf"
"${CMAKE_CURRENT_SOURCE_DIR}/ug31.pdf"
"${DSVIEW_BUNDLE_SHARE_DIR}/DSView"
DEPENDS ${PROJECT_NAME}
COMMENT "Stage DSView resources and protocol decoders in the app bundle"
VERBATIM
)
endif()

if(WIN32)
# Pass -mwindows so that no "DOS box" will open when PulseView is started.
set_target_properties(${PROJECT_NAME} PROPERTIES LINK_FLAGS "-mwindows")
Expand All @@ -661,10 +710,18 @@ set(EXECUTABLE_OUTPUT_PATH "${CMAKE_CURRENT_SOURCE_DIR}/build.dir")
#-------------------------------------------------------------------------------

# Install the executable.
install(TARGETS ${PROJECT_NAME} DESTINATION bin)
install(DIRECTORY DSView/res DESTINATION share/DSView)
install(DIRECTORY DSView/demo DESTINATION share/DSView)
install(FILES DSView/icons/logo.svg DESTINATION share/DSView RENAME logo.svg)
if(APPLE)
install(TARGETS ${PROJECT_NAME} BUNDLE DESTINATION .)
set(DSVIEW_DATA_DESTINATION "${PROJECT_NAME}.app/Contents/share/DSView")
set(DSVIEW_DECODER_DESTINATION "${PROJECT_NAME}.app/Contents/share/libsigrokdecode4DSL")
else()
install(TARGETS ${PROJECT_NAME} DESTINATION bin)
set(DSVIEW_DATA_DESTINATION "share/DSView")
set(DSVIEW_DECODER_DESTINATION "share/libsigrokdecode4DSL")
endif()
install(DIRECTORY DSView/res DESTINATION "${DSVIEW_DATA_DESTINATION}")
install(DIRECTORY DSView/demo DESTINATION "${DSVIEW_DATA_DESTINATION}")
install(FILES DSView/icons/logo.svg DESTINATION "${DSVIEW_DATA_DESTINATION}" RENAME logo.svg)
install(FILES DSView/icons/logo.svg DESTINATION share/icons/hicolor/scalable/apps RENAME dsview.svg)
install(FILES DSView/icons/logo.svg DESTINATION share/pixmaps RENAME dsview.svg)

Expand All @@ -683,13 +740,13 @@ if(CMAKE_SYSTEM_NAME MATCHES "Linux")

endif()

install(FILES NEWS25 DESTINATION share/DSView RENAME NEWS25)
install(FILES NEWS31 DESTINATION share/DSView RENAME NEWS31)
install(FILES ug25.pdf DESTINATION share/DSView RENAME ug25.pdf)
install(FILES ug31.pdf DESTINATION share/DSView RENAME ug31.pdf)
install(FILES NEWS25 DESTINATION "${DSVIEW_DATA_DESTINATION}" RENAME NEWS25)
install(FILES NEWS31 DESTINATION "${DSVIEW_DATA_DESTINATION}" RENAME NEWS31)
install(FILES ug25.pdf DESTINATION "${DSVIEW_DATA_DESTINATION}" RENAME ug25.pdf)
install(FILES ug31.pdf DESTINATION "${DSVIEW_DATA_DESTINATION}" RENAME ug31.pdf)

install(DIRECTORY libsigrokdecode4DSL/decoders DESTINATION share/libsigrokdecode4DSL)
install(DIRECTORY lang DESTINATION share/DSView)
install(DIRECTORY libsigrokdecode4DSL/decoders DESTINATION "${DSVIEW_DECODER_DESTINATION}")
install(DIRECTORY lang DESTINATION "${DSVIEW_DATA_DESTINATION}")

#===============================================================================
#= Packaging (handled by CPack)
Expand All @@ -707,6 +764,62 @@ set(CPACK_SOURCE_GENERATOR "TGZ")

include(CPack)

#===============================================================================
#= macOS: Fix broken symlinks after macdeployqt
#-------------------------------------------------------------------------------

if(APPLE)
set(FIX_MACOS_BUNDLE_SCRIPT "${CMAKE_CURRENT_SOURCE_DIR}/CMake/FixMacOSBundleSymlinks.cmake")
set(VALIDATE_MACOS_BUNDLE_SCRIPT "${CMAKE_CURRENT_SOURCE_DIR}/CMake/ValidateMacOSBundle.cmake")

add_custom_target(fixup_bundle
COMMAND ${CMAKE_COMMAND}
"-DBUNDLE_PATH=$<TARGET_BUNDLE_DIR:${PROJECT_NAME}>"
-P "${FIX_MACOS_BUNDLE_SCRIPT}"
DEPENDS stage_bundle_resources
COMMENT "Validate and fix known Homebrew Python symlinks in the app bundle"
VERBATIM
)

if(Qt6Core_FOUND)
get_filename_component(QT_INSTALL_PREFIX "${Qt6Core_DIR}/../../.." ABSOLUTE)
else()
get_filename_component(QT_INSTALL_PREFIX "${Qt5Core_DIR}/../../.." ABSOLUTE)
endif()

find_program(MACDEPLOYQT_EXECUTABLE
NAMES macdeployqt
HINTS "${QT_INSTALL_PREFIX}/bin"
)

if(MACDEPLOYQT_EXECUTABLE)
add_custom_target(deploy_bundle
COMMAND "${MACDEPLOYQT_EXECUTABLE}"
"$<TARGET_BUNDLE_DIR:${PROJECT_NAME}>"
-always-overwrite
"-libpath=${QT_INSTALL_PREFIX}/lib"
-no-codesign
COMMAND ${CMAKE_COMMAND}
"-DBUNDLE_PATH=$<TARGET_BUNDLE_DIR:${PROJECT_NAME}>"
-P "${FIX_MACOS_BUNDLE_SCRIPT}"
COMMAND ${CMAKE_COMMAND}
"-DBUNDLE_PATH=$<TARGET_BUNDLE_DIR:${PROJECT_NAME}>"
-P "${VALIDATE_MACOS_BUNDLE_SCRIPT}"
COMMAND /usr/bin/codesign
--force --deep --sign -
"$<TARGET_BUNDLE_DIR:${PROJECT_NAME}>"
COMMAND /usr/bin/codesign
--verify --deep --strict --verbose=2
"$<TARGET_BUNDLE_DIR:${PROJECT_NAME}>"
DEPENDS stage_bundle_resources
COMMENT "Deploy dependencies and verify the macOS app bundle"
VERBATIM
)
else()
message(WARNING "macdeployqt was not found; the deploy_bundle target is unavailable")
endif()
endif()

#===============================================================================
#= Tests
#-------------------------------------------------------------------------------
Expand All @@ -716,5 +829,3 @@ if(ENABLE_TESTS)
enable_testing()
add_test(test ${CMAKE_CURRENT_BINARY_DIR}/DSView/test/DSView-test)
endif(ENABLE_TESTS)


Loading