check_cxx_source_compiles() caches MUSRVIEW_HAS_ROOT7_API under a plain variable name with no dependency tracking, so a failure recorded once (e.g. against an older ROOT built into the same build/ directory) kept being reported as missing on every later reconfigure even after ROOT was fine. Unset both cache entries before the check so it always re-runs. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
314 lines
14 KiB
CMake
314 lines
14 KiB
CMake
#--- MusrfitCheckROOT7.cmake --------------------------------------------------
|
|
#
|
|
# Config time checks for everything musrview needs from ROOT.
|
|
#
|
|
# Since musrfit 1.13 musrview is built on the ROOT7 web graphics: the C++ side
|
|
# only holds the data and pushes it as JSON through an ROOT::RWebWindow
|
|
# websocket to a JSROOT page which does the rendering. This requires quite a
|
|
# few ROOT build options to be enabled (root7, webgui, http, ...) which are
|
|
# *not* all on by default in every ROOT installation. Without them musrview
|
|
# either does not compile at all or, worse, compiles and then fails at run time
|
|
# with a blank window. Hence check all of it here, at config time, and tell the
|
|
# user precisely what is missing and how to fix it.
|
|
#
|
|
# Expects find_package(ROOT ...) to have been called already.
|
|
#
|
|
# Sets (cached, for the config summary and for the sources):
|
|
# MUSRVIEW_JSROOT_DIR - the JSROOT directory of the ROOT installation
|
|
# MUSRVIEW_HAS_QT6WEB - ROOT provides the embedded Qt6 web display
|
|
# MUSRVIEW_HAS_IMAGE_EXPORT- ROOT provides libASImage (png/gif/jpg export)
|
|
#
|
|
#------------------------------------------------------------------------------
|
|
|
|
include(CheckCXXSourceCompiles)
|
|
|
|
if (NOT ROOT_FOUND)
|
|
message(FATAL_ERROR "MusrfitCheckROOT7: find_package(ROOT) has to be called first.")
|
|
endif ()
|
|
|
|
#--- collect all problems first, so that the user gets the complete list with
|
|
#--- a single cmake run instead of one error per run --------------------------
|
|
set(_musrview_missing "")
|
|
|
|
#--- ROOT_BINDIR is set by ROOTConfig.cmake, but be defensive about it since it
|
|
#--- is only used for the messages and for locating the JSROOT directory ------
|
|
if (NOT ROOT_BINDIR)
|
|
list(GET ROOT_INCLUDE_DIRS 0 _root_incdir)
|
|
get_filename_component(ROOT_BINDIR "${_root_incdir}/../bin" REALPATH)
|
|
endif ()
|
|
|
|
#--- cmake does not re-wrap indented lines, hence the texts handed to
|
|
#--- musrview_missing() are wrapped by hand below ----------------------------
|
|
macro(musrview_missing msg)
|
|
string(APPEND _musrview_missing " * ${msg}\n")
|
|
endmacro()
|
|
|
|
#------------------------------------------------------------------------------
|
|
# 1) the ROOT build options musrview relies on
|
|
#
|
|
# ROOTConfig.cmake exports every enabled ROOT build option as
|
|
# ROOT_<option>_FOUND, which is exactly what 'root-config --features' lists.
|
|
#------------------------------------------------------------------------------
|
|
if (NOT ROOT_root7_FOUND)
|
|
musrview_missing("ROOT was built without '-Droot7=ON' (no ROOT7 classes at all).")
|
|
endif ()
|
|
|
|
if (NOT ROOT_webgui_FOUND)
|
|
musrview_missing("ROOT was built without '-Dwebgui=ON' (no RWebWindow/web display).")
|
|
endif ()
|
|
|
|
if (NOT ROOT_http_FOUND)
|
|
musrview_missing("ROOT was built without '-Dhttp=ON'. RWebWindow serves its\n\
|
|
page and its websocket through THttpServer, hence musrview cannot work\n\
|
|
without it.")
|
|
endif ()
|
|
|
|
#------------------------------------------------------------------------------
|
|
# 2) the ROOT libraries musrview links against
|
|
#
|
|
# ROOTConfig-targets.cmake only defines the targets which this particular ROOT
|
|
# build actually produced, i.e. a missing target is a reliable statement about
|
|
# the ROOT installation, not about cmake.
|
|
#------------------------------------------------------------------------------
|
|
foreach (_root7_target ROOT::ROOTWebDisplay ROOT::RHTTP)
|
|
if (NOT TARGET ${_root7_target})
|
|
musrview_missing("the ROOT library ${_root7_target} is missing.")
|
|
endif ()
|
|
endforeach ()
|
|
|
|
#------------------------------------------------------------------------------
|
|
# 3) the ROOT7 headers musrview includes
|
|
#------------------------------------------------------------------------------
|
|
foreach (_root7_header ROOT/RWebWindow.hxx ROOT/RWebWindowsManager.hxx)
|
|
string(REGEX REPLACE "[/.]" "_" _root7_header_var "MUSRVIEW_HEADER_${_root7_header}")
|
|
find_file(${_root7_header_var}
|
|
NAMES ${_root7_header}
|
|
HINTS ${ROOT_INCLUDE_DIRS}
|
|
NO_DEFAULT_PATH
|
|
)
|
|
mark_as_advanced(${_root7_header_var})
|
|
if (NOT ${_root7_header_var})
|
|
musrview_missing("the header <${_root7_header}> is not part of this ROOT\n\
|
|
installation.")
|
|
endif ()
|
|
endforeach ()
|
|
|
|
#------------------------------------------------------------------------------
|
|
# 4) the ROOT7 API musrview uses
|
|
#
|
|
# Checking the headers/libraries is not enough: RWebWindow lived in
|
|
# ROOT::Experimental until ROOT 6.32 and moved to the plain ROOT namespace
|
|
# afterwards, which is what PMusrWebCanvas/musrview use. Compile the actual
|
|
# calls, so that an API change is caught here and not in the middle of the
|
|
# build. CMAKE_TRY_COMPILE_TARGET_TYPE=STATIC_LIBRARY keeps this a compile-only
|
|
# check, i.e. no ROOT libraries have to be dragged in for it.
|
|
#------------------------------------------------------------------------------
|
|
set(MUSRVIEW_ROOT7_API_TEST_SRC "
|
|
#include <memory>
|
|
#include <string>
|
|
#include <TBufferJSON.h>
|
|
#include <TList.h>
|
|
#include <ROOT/RWebWindow.hxx>
|
|
#include <ROOT/RWebWindowsManager.hxx>
|
|
|
|
int main() {
|
|
@MUSRVIEW_NS@RWebWindowsManager::SetSingleConnMode(false);
|
|
|
|
std::shared_ptr<@MUSRVIEW_NS@RWebWindow> win = @MUSRVIEW_NS@RWebWindow::Create();
|
|
win->SetDefaultPage(std::string(\"file:musrview.html\"));
|
|
win->SetGeometry(800, 600);
|
|
win->SetConnLimit(1);
|
|
win->SetConnectCallBack([](unsigned) {});
|
|
win->SetDisconnectCallBack([](unsigned) {});
|
|
win->SetDataCallBack([](unsigned, const std::string &) {});
|
|
win->Show(std::string(\"qt6\"));
|
|
win->Send(1, std::string(\"PLOT:\"));
|
|
win->CloseConnections();
|
|
|
|
TList list;
|
|
TString json = TBufferJSON::ToJSON(&list, TBufferJSON::kNoSpaces + TBufferJSON::kSameSuppression);
|
|
|
|
return json.Length() > 0 ? 0 : 1;
|
|
}
|
|
")
|
|
|
|
set(CMAKE_REQUIRED_INCLUDES ${ROOT_INCLUDE_DIRS})
|
|
set(CMAKE_REQUIRED_FLAGS "${ROOT_CXX_FLAGS}")
|
|
set(CMAKE_REQUIRED_QUIET TRUE)
|
|
set(_musrview_try_compile_type_save "${CMAKE_TRY_COMPILE_TARGET_TYPE}")
|
|
set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)
|
|
|
|
#--- check_cxx_source_compiles() caches its result under a plain variable
|
|
#--- name with no dependency tracking: if it once failed (e.g. against an
|
|
#--- older ROOT in this same build directory) it would keep reporting that
|
|
#--- stale failure on every later reconfigure, even after ROOT got fixed,
|
|
#--- since neither the test source nor ROOT_VERSION are part of the cache
|
|
#--- key. Unset both results first so the compile check always re-runs. ------
|
|
unset(MUSRVIEW_HAS_ROOT7_API CACHE)
|
|
unset(MUSRVIEW_HAS_ROOT7_API_EXPERIMENTAL CACHE)
|
|
|
|
set(MUSRVIEW_NS "ROOT::")
|
|
string(CONFIGURE "${MUSRVIEW_ROOT7_API_TEST_SRC}" _musrview_api_src @ONLY)
|
|
check_cxx_source_compiles("${_musrview_api_src}" MUSRVIEW_HAS_ROOT7_API)
|
|
|
|
if (NOT MUSRVIEW_HAS_ROOT7_API)
|
|
#--- was it only the namespace, i.e. a ROOT which is too old? --------------
|
|
set(MUSRVIEW_NS "ROOT::Experimental::")
|
|
string(CONFIGURE "${MUSRVIEW_ROOT7_API_TEST_SRC}" _musrview_api_src_exp @ONLY)
|
|
check_cxx_source_compiles("${_musrview_api_src_exp}" MUSRVIEW_HAS_ROOT7_API_EXPERIMENTAL)
|
|
|
|
if (MUSRVIEW_HAS_ROOT7_API_EXPERIMENTAL)
|
|
musrview_missing("this ROOT (${ROOT_VERSION}) still has RWebWindow in the\n\
|
|
'ROOT::Experimental' namespace. musrview needs the final\n\
|
|
ROOT::RWebWindow, i.e. ROOT 6.32 or newer.")
|
|
else ()
|
|
musrview_missing("the ROOT7 web window API (ROOT::RWebWindow::Create(),\n\
|
|
SetDefaultPage(), SetGeometry(), SetConnLimit(), SetDataCallBack(),\n\
|
|
Show(), Send(), ROOT::RWebWindowsManager::SetSingleConnMode(),\n\
|
|
TBufferJSON::ToJSON()) does not compile against this ROOT\n\
|
|
(${ROOT_VERSION}). The cmake configure log holds the compiler output.")
|
|
endif ()
|
|
endif ()
|
|
|
|
set(CMAKE_TRY_COMPILE_TARGET_TYPE "${_musrview_try_compile_type_save}")
|
|
unset(_musrview_try_compile_type_save)
|
|
unset(CMAKE_REQUIRED_INCLUDES)
|
|
unset(CMAKE_REQUIRED_FLAGS)
|
|
unset(CMAKE_REQUIRED_QUIET)
|
|
unset(MUSRVIEW_NS)
|
|
unset(MUSRVIEW_ROOT7_API_TEST_SRC)
|
|
unset(_musrview_api_src)
|
|
unset(_musrview_api_src_exp)
|
|
|
|
#------------------------------------------------------------------------------
|
|
# 5) JSROOT
|
|
#
|
|
# The rendering is done by the JSROOT which ships with ROOT. src/musrview_web/
|
|
# musrview.html imports the bare module specifiers 'jsroot/core',
|
|
# 'jsroot/draw' and 'jsroot/webwindow' and relies on THttpServer replacing the
|
|
# '<!--jsroot_importmap-->' marker in the served page by the import map which
|
|
# resolves them. Check both: the JSROOT modules on disk and the import map
|
|
# support of the installed libRHTTP.
|
|
#------------------------------------------------------------------------------
|
|
find_path(MUSRVIEW_JSROOT_DIR
|
|
NAMES modules/core.mjs
|
|
HINTS $ENV{JSROOTSYS}
|
|
${ROOT_BINDIR}/../js
|
|
${ROOT_BINDIR}/../share/root/js
|
|
$ENV{ROOTSYS}/js
|
|
NO_DEFAULT_PATH
|
|
DOC "JSROOT directory of the ROOT installation used by musrview"
|
|
)
|
|
mark_as_advanced(MUSRVIEW_JSROOT_DIR)
|
|
|
|
if (NOT MUSRVIEW_JSROOT_DIR)
|
|
musrview_missing("the JSROOT of this ROOT installation could not be found\n\
|
|
(no 'js/modules/core.mjs'). musrview is rendered by JSROOT, hence it is\n\
|
|
needed. If it sits somewhere else, point JSROOTSYS or\n\
|
|
-DMUSRVIEW_JSROOT_DIR=<dir> at it.")
|
|
else ()
|
|
foreach (_jsroot_module core.mjs draw.mjs webwindow.mjs)
|
|
if (NOT EXISTS "${MUSRVIEW_JSROOT_DIR}/modules/${_jsroot_module}")
|
|
musrview_missing("the JSROOT module '${_jsroot_module}' is missing in\n\
|
|
${MUSRVIEW_JSROOT_DIR}/modules, but musrview.html imports it.")
|
|
endif ()
|
|
endforeach ()
|
|
endif ()
|
|
|
|
#--- The import map itself is generated by THttpServer, i.e. by libRHTTP.
|
|
#--- Two independent signals that this ROOT supports it: the pages ROOT itself
|
|
#--- serves carry the very same marker, and libRHTTP holds the marker together
|
|
#--- with the 'jsroot/...' module specifiers it maps (file(STRINGS ...) also
|
|
#--- works on binaries). Either one is good enough, complain only if neither is
|
|
#--- there - which is much more robust than deducing it from the ROOT version.
|
|
set(_musrview_has_importmap FALSE)
|
|
|
|
#--- signal 1: a page shipped by ROOT which is served through THttpServer ----
|
|
if (MUSRVIEW_JSROOT_DIR AND EXISTS "${MUSRVIEW_JSROOT_DIR}/files/online.htm")
|
|
file(STRINGS "${MUSRVIEW_JSROOT_DIR}/files/online.htm" _jsroot_marker
|
|
REGEX "jsroot_importmap")
|
|
if (_jsroot_marker)
|
|
set(_musrview_has_importmap TRUE)
|
|
endif ()
|
|
endif ()
|
|
|
|
#--- signal 2: libRHTTP itself ----------------------------------------------
|
|
if (NOT _musrview_has_importmap AND TARGET ROOT::RHTTP)
|
|
set(_rhttp_lib "")
|
|
foreach (_prop IMPORTED_LOCATION IMPORTED_LOCATION_RELEASE IMPORTED_LOCATION_NOCONFIG)
|
|
if (NOT _rhttp_lib)
|
|
get_target_property(_rhttp_lib ROOT::RHTTP ${_prop})
|
|
if (NOT _rhttp_lib)
|
|
set(_rhttp_lib "")
|
|
endif ()
|
|
endif ()
|
|
endforeach ()
|
|
|
|
if (_rhttp_lib AND EXISTS "${_rhttp_lib}")
|
|
file(STRINGS "${_rhttp_lib}" _rhttp_importmap REGEX "jsroot_importmap")
|
|
file(STRINGS "${_rhttp_lib}" _rhttp_webwindow REGEX "^jsroot/webwindow$")
|
|
if (_rhttp_importmap AND _rhttp_webwindow)
|
|
set(_musrview_has_importmap TRUE)
|
|
endif ()
|
|
else ()
|
|
#--- nothing to look at, do not cry wolf ---------------------------------
|
|
set(_musrview_has_importmap TRUE)
|
|
endif ()
|
|
endif ()
|
|
|
|
if (NOT _musrview_has_importmap)
|
|
musrview_missing("the THttpServer of this ROOT does not provide the JSROOT\n\
|
|
import map ('<!--jsroot_importmap-->' and the 'jsroot/...' module\n\
|
|
specifiers) which src/musrview_web/musrview.html relies on. ROOT 6.32 or\n\
|
|
newer is required.")
|
|
endif ()
|
|
|
|
#------------------------------------------------------------------------------
|
|
# 6) bail out if anything is missing
|
|
#------------------------------------------------------------------------------
|
|
if (_musrview_missing)
|
|
get_filename_component(_root_prefix "${ROOT_BINDIR}/.." REALPATH)
|
|
message(FATAL_ERROR
|
|
"musrview is built on the ROOT7 web graphics, but the ROOT installation\n"
|
|
" ${_root_prefix} (version ${ROOT_VERSION})\n"
|
|
"does not provide everything it needs:\n"
|
|
"${_musrview_missing}"
|
|
"Please rebuild ROOT with at least\n"
|
|
" -Droot7=ON -Dwebgui=ON -Dhttp=ON\n"
|
|
"and, for the embedded musrview window, in addition -Dqt6web=ON.\n"
|
|
"If more than one ROOT is installed, check that ROOTSYS/CMAKE_PREFIX_PATH point to the right one.")
|
|
endif ()
|
|
|
|
#------------------------------------------------------------------------------
|
|
# 7) optional, i.e. musrview works without it but with reduced functionality
|
|
#------------------------------------------------------------------------------
|
|
|
|
#--- embedded window: '--embedded' is musrview's default, without the Qt6 web
|
|
#--- display of ROOT it falls back to the standard browser of the system. -----
|
|
if (TARGET ROOT::ROOTQt6WebDisplay AND ROOT_qt6web_FOUND)
|
|
set(MUSRVIEW_HAS_QT6WEB TRUE CACHE INTERNAL "ROOT provides the embedded Qt6 web display")
|
|
else ()
|
|
set(MUSRVIEW_HAS_QT6WEB FALSE CACHE INTERNAL "ROOT provides the embedded Qt6 web display")
|
|
message(WARNING
|
|
"ROOT is built without the Qt6 web display (-Dqt6web=ON), hence musrview cannot "
|
|
"show its embedded window and will use the standard browser of the system instead. "
|
|
"Everything else works as usual.")
|
|
endif ()
|
|
|
|
#--- graphics export: musrview --png/--gif/--jpg draw into a real TCanvas in
|
|
#--- batch mode, which needs libASImage. eps/pdf/svg/root do not. The
|
|
#--- musrview_check tests compare png output, i.e. they need it as well. ------
|
|
if (TARGET ROOT::ASImage AND ROOT_asimage_FOUND)
|
|
set(MUSRVIEW_HAS_IMAGE_EXPORT TRUE CACHE INTERNAL "ROOT provides libASImage")
|
|
else ()
|
|
set(MUSRVIEW_HAS_IMAGE_EXPORT FALSE CACHE INTERNAL "ROOT provides libASImage")
|
|
message(WARNING
|
|
"ROOT is built without libASImage (-Dasimage=ON), hence 'musrview --png/--gif/--jpg' "
|
|
"will not produce any output (eps/pdf/svg/root are fine) and the musrview_check "
|
|
"tests will fail.")
|
|
endif ()
|
|
|
|
unset(_musrview_missing)
|
|
unset(_musrview_has_importmap)
|
|
unset(_rhttp_lib)
|