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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,5 @@
CMakePresets.json
CMakeUserPresets.json

.DS_Store
/build
63 changes: 63 additions & 0 deletions CMake/PackageMacOS.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# macOS packaging: runs macdeployqt after build, fixes bundled-dylib rpaths,
# and configures CPack to produce a drag-and-drop .dmg installer.

# Locate macdeployqt relative to where Qt6 was found.
# Qt6_DIR is typically <prefix>/lib/cmake/Qt6; macdeployqt lives at:
# conda layout → <prefix>/lib/qt6/bin/macdeployqt
# standard layout → <prefix>/bin/macdeployqt
find_program(MACDEPLOYQT_EXECUTABLE macdeployqt
HINTS
"${Qt6_DIR}/../../../lib/qt6/bin"
"${Qt6_DIR}/../../../bin"
NO_DEFAULT_PATH
)

if(NOT MACDEPLOYQT_EXECUTABLE)
message(WARNING "macdeployqt not found — the .app bundle will not be self-contained. "
"Hint: set Qt6_DIR to the cmake directory inside your Qt installation.")
return()
endif()

message(STATUS "Found macdeployqt: ${MACDEPLOYQT_EXECUTABLE}")

# Determine the conda/prefix lib path from CMAKE_PREFIX_PATH (first entry wins).
# This is the rpath that macdeployqt leaves behind in bundled dylibs; we replace
# it with @loader_path so the bundle works on machines without the conda env.
list(GET CMAKE_PREFIX_PATH 0 _prefix)
set(_conda_lib "${_prefix}/lib")

# After the build, deploy Qt into the .app, fix the remaining rpaths, then
# re-sign the whole bundle with an ad-hoc identity. The signing step is
# required because install_name_tool (used by both macdeployqt and the rpath
# fix) invalidates any existing code signatures, and macOS will SIGKILL the
# process at load time if the signature doesn't match the binary pages.
add_custom_command(TARGET RhizoVisionExplorer POST_BUILD
COMMAND ${MACDEPLOYQT_EXECUTABLE}
"$<TARGET_BUNDLE_DIR:RhizoVisionExplorer>"
-verbose=1
-no-strip
-no-codesign
"-libpath=${_conda_lib}"
COMMAND ${CMAKE_COMMAND}
"-DBUNDLE_DIR=$<TARGET_BUNDLE_DIR:RhizoVisionExplorer>"
"-DCONDA_LIB_PATH=${_conda_lib}"
-P "${CMAKE_CURRENT_SOURCE_DIR}/CMake/fix_bundle_rpaths.cmake"
COMMAND codesign
--force
--deep
--sign -
"$<TARGET_BUNDLE_DIR:RhizoVisionExplorer>"
COMMENT "Deploying Qt frameworks, fixing bundle rpaths, and ad-hoc signing"
VERBATIM
)

# CPack: DragNDrop generator produces a .dmg with a symlink to /Applications.
set(CPACK_GENERATOR "DragNDrop")
set(CPACK_PACKAGE_NAME "RhizoVisionExplorer")
set(CPACK_PACKAGE_VERSION "${PROJECT_VERSION}")
set(CPACK_PACKAGE_VENDOR "Oak Ridge National Laboratory")
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "RhizoVision Explorer — root image analysis tool")
set(CPACK_DMG_VOLUME_NAME "RhizoVision Explorer ${PROJECT_VERSION}")
set(CPACK_DMG_FORMAT "UDZO")
set(CPACK_PACKAGE_FILE_NAME "RhizoVisionExplorer-${PROJECT_VERSION}-macOS-arm64")
include(CPack)
55 changes: 52 additions & 3 deletions CMake/cvutilInstallTargets.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ elseif (CMAKE_SYSTEM_NAME STREQUAL "Linux")
# Install additional files.
install(FILES README.md COPYING CONFIGURATIONS Debug Release DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/RhizoVisionExplorer)

install(FILES
install(FILES
licenses/LICENSE_Qt6
licenses/LICENSE_opencv.txt
licenses/LICENSE_FFMPEG.txt
Expand All @@ -176,7 +176,7 @@ elseif (CMAKE_SYSTEM_NAME STREQUAL "Linux")

install(FILES manual/RhizoVisionExplorerManualv2.pdf CONFIGURATIONS Debug Release DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/RhizoVisionExplorer/manual)

install(FILES
install(FILES
imageexamples/crowns/crown1.png
imageexamples/crowns/crown2.png
imageexamples/crowns/crown3.png
Expand All @@ -185,12 +185,61 @@ elseif (CMAKE_SYSTEM_NAME STREQUAL "Linux")
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/RhizoVisionExplorer/imageexamples/crowns
)

install(FILES
install(FILES
imageexamples/scans/scan1.jpg
imageexamples/scans/scan2.jpg
imageexamples/scans/scan3.jpg
imageexamples/scans/wheatscan_settings.csv
CONFIGURATIONS Debug Release
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/RhizoVisionExplorer/imageexamples/scans
)

elseif (CMAKE_SYSTEM_NAME STREQUAL "Darwin")
# Install the .app bundle and the CLI tool
install(TARGETS RhizoVisionExplorer
CONFIGURATIONS Debug Release
BUNDLE DESTINATION .
)
install(TARGETS rv
CONFIGURATIONS Debug Release
RUNTIME DESTINATION .
)

# Resources installed inside the .app bundle
set(_bundle_res "RhizoVisionExplorer.app/Contents/Resources")

install(FILES README.md COPYING
CONFIGURATIONS Debug Release
DESTINATION "${_bundle_res}"
)
install(FILES
licenses/LICENSE_Qt6
licenses/LICENSE_opencv.txt
licenses/LICENSE_FFMPEG.txt
licenses/LICENSE_cvutil
licenses/LICENSE.indicators
licenses/LICENSE.termcolor
CONFIGURATIONS Debug Release
DESTINATION "${_bundle_res}/licenses"
)
install(FILES manual/RhizoVisionExplorerManualv2.pdf
CONFIGURATIONS Debug Release
DESTINATION "${_bundle_res}/manual"
)
install(FILES
imageexamples/crowns/crown1.png
imageexamples/crowns/crown2.png
imageexamples/crowns/crown3.png
imageexamples/crowns/wheatcrown_settings.csv
CONFIGURATIONS Debug Release
DESTINATION "${_bundle_res}/imageexamples/crowns"
)
install(FILES
imageexamples/scans/scan1.jpg
imageexamples/scans/scan2.jpg
imageexamples/scans/scan3.jpg
imageexamples/scans/wheatscan_settings.csv
CONFIGURATIONS Debug Release
DESTINATION "${_bundle_res}/imageexamples/scans"
)
endif()
61 changes: 61 additions & 0 deletions CMake/fix_bundle_rpaths.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# CMake script to fix rpaths in dylibs bundled by macdeployqt.
#
# macdeployqt rewrites the main binary's LC_RPATH to @executable_path/../Frameworks,
# but the bundled dylibs still carry the original build-tree rpath (e.g. the conda
# env's lib directory). This script replaces that absolute path with @loader_path so
# the dylibs resolve their own dependencies from within the bundle.
#
# Required variables (pass via -D on the command line):
# BUNDLE_DIR — path to the .app bundle
# CONDA_LIB_PATH — the absolute rpath to replace (e.g. /path/to/conda/envs/.../lib)

if(NOT BUNDLE_DIR)
message(FATAL_ERROR "fix_bundle_rpaths.cmake: BUNDLE_DIR not set")
endif()
if(NOT CONDA_LIB_PATH)
message(FATAL_ERROR "fix_bundle_rpaths.cmake: CONDA_LIB_PATH not set")
endif()

file(GLOB FRAMEWORK_DYLIBS "${BUNDLE_DIR}/Contents/Frameworks/*.dylib")

foreach(dylib ${FRAMEWORK_DYLIBS})
execute_process(
COMMAND otool -l "${dylib}"
OUTPUT_VARIABLE otool_out
ERROR_QUIET
)
if(otool_out MATCHES "${CONDA_LIB_PATH}")
execute_process(
COMMAND install_name_tool -rpath "${CONDA_LIB_PATH}" "@loader_path" "${dylib}"
RESULT_VARIABLE result
)
if(NOT result EQUAL 0)
message(WARNING "fix_bundle_rpaths: failed to patch ${dylib}")
else()
message(STATUS "Patched rpath in: ${dylib}")
endif()
endif()
endforeach()

# Plugins sit two directories deeper (Contents/PlugIns/<subdir>/), so they need
# to reach back up to Contents/Frameworks via @loader_path/../../Frameworks.
file(GLOB_RECURSE PLUGIN_DYLIBS "${BUNDLE_DIR}/Contents/PlugIns/*.dylib")

foreach(dylib ${PLUGIN_DYLIBS})
execute_process(
COMMAND otool -l "${dylib}"
OUTPUT_VARIABLE otool_out
ERROR_QUIET
)
if(otool_out MATCHES "${CONDA_LIB_PATH}")
execute_process(
COMMAND install_name_tool -rpath "${CONDA_LIB_PATH}" "@loader_path/../../Frameworks" "${dylib}"
RESULT_VARIABLE result
)
if(NOT result EQUAL 0)
message(WARNING "fix_bundle_rpaths: failed to patch plugin ${dylib}")
else()
message(STATUS "Patched rpath in plugin: ${dylib}")
endif()
endif()
endforeach()
31 changes: 28 additions & 3 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,18 @@ endif()
option(ENABLE_AVX512 "Enable AVX-512 support" OFF) # To be enabled on x86_64 server processors
option(ENABLE_AVX2_FMA "Enable AVX2 and FMA support" ON) # Default support for x86_64 desktop processors

# Disable x86-specific SIMD flags on non-x86 architectures (e.g., Apple Silicon arm64)
if(CMAKE_SYSTEM_PROCESSOR MATCHES "arm64|aarch64|ARM64")
if(ENABLE_AVX512)
message(STATUS "Disabling AVX-512: not supported on ${CMAKE_SYSTEM_PROCESSOR}")
set(ENABLE_AVX512 OFF CACHE BOOL "Enable AVX-512 support" FORCE)
endif()
if(ENABLE_AVX2_FMA)
message(STATUS "Disabling AVX2/FMA: not supported on ${CMAKE_SYSTEM_PROCESSOR}")
set(ENABLE_AVX2_FMA OFF CACHE BOOL "Enable AVX2 and FMA support" FORCE)
endif()
endif()

# Detect the OS
if(WIN32)
message(STATUS "Configuring for Windows")
Expand Down Expand Up @@ -141,11 +153,12 @@ if(WIN32)
endif()

# Define the target as an executable.
add_executable(RhizoVisionExplorer WIN32
# WIN32 suppresses the console window on Windows; MACOSX_BUNDLE creates a .app on macOS.
add_executable(RhizoVisionExplorer WIN32 MACOSX_BUNDLE
RhizoVisionExplorer/main.cpp
${SOURCES}
${HEADERS})
add_executable(rv
add_executable(rv
RhizoVisionExplorer/rv.cpp
RhizoVisionExplorer/indicators/indicators.hpp
${SOURCES}
Expand All @@ -154,6 +167,16 @@ add_executable(rv
set_target_properties(RhizoVisionExplorer PROPERTIES DEBUG_POSTFIX ${CMAKE_DEBUG_POSTFIX})
set_target_properties(rv PROPERTIES DEBUG_POSTFIX ${CMAKE_DEBUG_POSTFIX})

if(APPLE)
set_target_properties(RhizoVisionExplorer PROPERTIES
MACOSX_BUNDLE_BUNDLE_NAME "RhizoVisionExplorer"
MACOSX_BUNDLE_BUNDLE_VERSION "${PROJECT_VERSION}"
MACOSX_BUNDLE_SHORT_VERSION_STRING "${PROJECT_VERSION}"
MACOSX_BUNDLE_GUI_IDENTIFIER "gov.ornl.RhizoVisionExplorer"
MACOSX_BUNDLE_INFO_STRING "RhizoVision Explorer ${PROJECT_VERSION}"
)
endif()

# Add the generated resource .cpp file to the target
target_sources(RhizoVisionExplorer PRIVATE ${RESOURCES})

Expand Down Expand Up @@ -207,7 +230,7 @@ include(CMake/cvutilInstallTargets.cmake)
# Include the CPack module
if(CMAKE_SYSTEM_NAME STREQUAL "Windows")
include(CMake/PackageWindows.cmake)
else(CMAKE_SYSTEM_NAME STREQUAL "Linux")
elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux")
# Check if we are using a conda env or not
if (DEFINED ENV{CONDA_PREFIX})
message(STATUS "Using conda environment: $ENV{CONDA_PREFIX} not generating a Debian package.")
Expand All @@ -216,4 +239,6 @@ else(CMAKE_SYSTEM_NAME STREQUAL "Linux")
message(STATUS "Not using conda environment, use CPack to create a Debian package.")
include(CMake/PackageLinux.cmake)
endif()
elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
include(CMake/PackageMacOS.cmake)
endif()