Merge branch 'f4exb:master' into psk31

This commit is contained in:
srcejon
2026-10-01 14:09:31 +01:00
committed by GitHub
6 changed files with 139 additions and 27 deletions
+46 -16
View File
@@ -110,21 +110,21 @@ if (LINUX)
macro(makeLink src dest target)
if (NOT EXISTS ${dest})
add_custom_command(TARGET ${target} PRE_BUILD
COMMAND ${CMAKE_COMMAND} -E create_symlink ${src} ${dest} DEPENDS ${dest} COMMENT "mklink ${src} -> ${dest}")
COMMAND ${CMAKE_COMMAND} -E create_symlink ${src} ${dest} COMMENT "mklink ${src} -> ${dest}")
endif ()
endmacro()
# macro that copy folder
macro(makeCopy src dest target)
if (NOT EXISTS ${dest})
add_custom_command(TARGET ${target} PRE_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory ${src} ${dest} DEPENDS ${dest} COMMENT "copy ${src} -> ${dest}")
COMMAND ${CMAKE_COMMAND} -E copy_directory ${src} ${dest} COMMENT "copy ${src} -> ${dest}")
endif ()
endmacro()
# macro that copy file
macro(makeCopyFile src dest target)
if (NOT EXISTS ${dest})
add_custom_command(TARGET ${target} PRE_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${src} ${dest} DEPENDS ${dest} COMMENT "copy ${src} -> ${dest}")
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${src} ${dest} COMMENT "copy ${src} -> ${dest}")
endif ()
endmacro()
else ()
@@ -132,7 +132,7 @@ else ()
macro(makeLink src dest target)
if (NOT EXISTS ${dest})
add_custom_command(TARGET ${target} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E create_symlink ${src} ${dest} DEPENDS ${dest} COMMENT "mklink ${src} -> ${dest}")
COMMAND ${CMAKE_COMMAND} -E create_symlink ${src} ${dest} COMMENT "mklink ${src} -> ${dest}")
endif ()
endmacro()
@@ -140,7 +140,7 @@ else ()
macro(makeCopyDir src dest target)
if (NOT EXISTS ${dest})
add_custom_command(TARGET ${target} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory ${src} ${dest} DEPENDS ${dest} COMMENT "copy ${src} -> ${dest}")
COMMAND ${CMAKE_COMMAND} -E copy_directory ${src} ${dest} COMMENT "copy ${src} -> ${dest}")
endif ()
endmacro()
@@ -148,7 +148,7 @@ else ()
macro(makeCopy src dest target)
if (NOT EXISTS ${dest})
add_custom_command(TARGET ${target} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${src} ${dest} DEPENDS ${dest} COMMENT "copy ${src} -> ${dest}")
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${src} ${dest} COMMENT "copy ${src} -> ${dest}")
endif ()
endmacro()
endif (LINUX)
@@ -370,6 +370,7 @@ endif (NOT FFTW3F_FOUND AND NOT USE_PRECOMPILED_LIBS)
include(FetchContent)
FetchContent_Declare(boost
URL "https://sourceforge.net/projects/boost/files/boost/${BOOST_TAG}/boost_${BOOST_TAG2}.tar.gz/download"
DOWNLOAD_EXTRACT_TIMESTAMP TRUE
)
find_package(Boost QUIET)
@@ -895,14 +896,24 @@ if(ENABLE_FEATURE_STARTRACKER)
if (WIN32)
set(CSPICE_LIBRARIES "${SDRANGEL_BINARY_LIB_DIR}/cspice.lib" CACHE INTERNAL "")
elseif (LINUX)
# We need a shared library on Linux, as the static library has non PIC code, and we need to link it into a shared library
# ExternalProject cspice currently outputs libcspice.so under
# ${EXTERNAL_BUILD_LIBRARIES}/cspice/src/cspice-build/lib/libcspice.so
# so point CSPICE_LIBRARIES there.
set(CSPICE_LIBRARIES
"${EXTERNAL_BUILD_LIBRARIES}/lib${LIB_SUFFIX}/libcspice.so"
CACHE INTERNAL ""
)
# We need a shared library on Linux, as the static library has non PIC code,
# and we need to link it into a shared library
if (CMAKE_BUILD_TYPE STREQUAL "None")
# ExternalProject cspice currently outputs libcspice.so under
# ${EXTERNAL_BUILD_LIBRARIES}/cspice/src/cspice-build/lib/libcspice.so
# so point CSPICE_LIBRARIES there.
set(CSPICE_LIBRARIES
"${EXTERNAL_BUILD_LIBRARIES}/cspice/src/cspice-build/lib/libcspice.so"
CACHE INTERNAL ""
)
else()
# Release, Debug, and RelWithDebInfo have configuration-specific output
# directories supplied by SDRangel.
set(CSPICE_LIBRARIES
"${EXTERNAL_BUILD_LIBRARIES}/lib${LIB_SUFFIX}/libcspice.so"
CACHE INTERNAL ""
)
endif()
# Disable -Werror=format-security for cspice only: distro hardening flags (e.g. Ubuntu Noble)
# inject -Werror=format-security which fails cspice sources that lack -Wformat.
set(CSPICE_ARGS "-DCSPICE_BUILD_STATIC_LIBRARY=OFF"
@@ -1226,8 +1237,27 @@ if (LINUX)
if (ENABLE_BLADERF AND (NOT LIBBLADERF_FOUND OR LIBBLADERF_EXTERNAL))
set_global_cache(LIBBLADERF_FOUND ON)
set(LIBBLADERF_EXTERNAL ON CACHE INTERNAL "")
set(LIBBLADERF_INCLUDE_DIRS "${EXTERNAL_BUILD_LIBRARIES}/bladerf/src/bladerf/host/libraries/libbladeRF/include" CACHE INTERNAL "")
set(LIBBLADERF_LIBRARIES "${EXTERNAL_BUILD_LIBRARIES}/lib${LIB_SUFFIX}/libbladeRF.so.2" CACHE INTERNAL "")
set(LIBBLADERF_INCLUDE_DIRS
"${EXTERNAL_BUILD_LIBRARIES}/bladerf/src/bladerf/host/libraries/libbladeRF/include"
CACHE INTERNAL ""
)
if (CMAKE_BUILD_TYPE STREQUAL "None")
# ExternalProject bladerf currently outputs libbladeRF.so.2 under
# ${EXTERNAL_BUILD_LIBRARIES}/bladerf/src/bladerf-build/output/libbladeRF.so.2
# so point LIBBLADERF_LIBRARIES there.
set(LIBBLADERF_LIBRARIES
"${EXTERNAL_BUILD_LIBRARIES}/bladerf/src/bladerf-build/output/libbladeRF.so.2"
CACHE INTERNAL ""
)
else()
# Release, Debug, and RelWithDebInfo have configuration-specific output
# directories supplied by SDRangel.
set(LIBBLADERF_LIBRARIES
"${EXTERNAL_BUILD_LIBRARIES}/lib${LIB_SUFFIX}/libbladeRF.so.2"
CACHE INTERNAL ""
)
endif()
ExternalProject_Add(bladerf
GIT_REPOSITORY https://github.com/Nuand/bladeRF.git
GIT_TAG ${BLADERF_TAG}
+5 -1
View File
@@ -86,8 +86,12 @@ bool FileSinkGUI::handleMessage(const Message& message)
if (m_fixedPosition)
{
const qint32 inputFrequencyOffset = m_settings.m_inputFrequencyOffset;
setFrequencyFromPos();
applySettings(QStringList(), true);
if (m_settings.m_inputFrequencyOffset != inputFrequencyOffset) {
applySettings(QStringList("inputFrequencyOffset"));
}
}
else
{
@@ -93,6 +93,11 @@ if(BUILD_MCP_BUNDLE)
set_target_properties(sdrangel-mcp-tools-dump PROPERTIES BUILD_RPATH "${MACOS_EXTERNAL_LIBS_FIXUP}")
endif()
set(MCPTOOLSDUMP_TIMEOUT 60 CACHE STRING "Timeout in seconds for generating the MCP tool list")
if(NOT MCPTOOLSDUMP_TIMEOUT MATCHES "^[1-9][0-9]*$")
message(FATAL_ERROR "MCPTOOLSDUMP_TIMEOUT must be a positive integer")
endif()
set(MCPBRIDGE_BUNDLE_DIR "${CMAKE_CURRENT_BINARY_DIR}/bundle")
# Alongside the built binaries, which is bin/ on Windows and the build directory itself
# elsewhere. That is also where the running application looks for it in a build tree
@@ -109,14 +114,23 @@ if(BUILD_MCP_BUNDLE)
COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_SOURCE_DIR}/cmake/cpack/sdrangel_icon.png" "${MCPBRIDGE_BUNDLE_DIR}/icon.png"
# The tool list, straight from the server's own registry, beside the bridge that serves
# it when SDRangel is not up
COMMAND $<TARGET_FILE:sdrangel-mcp-tools-dump> "${MCPBRIDGE_BUNDLE_DIR}/server/tools.json"
COMMAND ${CMAKE_COMMAND}
"-DTOOLSDUMP=$<TARGET_FILE:sdrangel-mcp-tools-dump>"
"-DTOOLS_JSON=${MCPBRIDGE_BUNDLE_DIR}/server/tools.json"
"-DTOOLSDUMP_TIMEOUT=${MCPTOOLSDUMP_TIMEOUT}"
-P "${CMAKE_CURRENT_SOURCE_DIR}/run-tools-dump.cmake"
COMMAND ${CMAKE_COMMAND} -E rm -f "${MCPBRIDGE_BUNDLE_FILE}"
# Relative paths and a working directory, so that manifest.json lands at the root of
# the archive, which is where the installer looks for it
COMMAND ${CMAKE_COMMAND} -E echo "mcpbundle: archiving ${MCPBRIDGE_BUNDLE_FILE}"
COMMAND ${CMAKE_COMMAND} -E tar cf "${MCPBRIDGE_BUNDLE_FILE}" --format=zip -- manifest.json icon.png server
COMMAND ${CMAKE_COMMAND} -E echo "mcpbundle: archive complete"
WORKING_DIRECTORY "${MCPBRIDGE_BUNDLE_DIR}"
DEPENDS sdrangel-mcp-bridge sdrangel-mcp-tools-dump "${MCPBRIDGE_BUNDLE_DIR}/manifest.json"
COMMENT "Packaging ${MCPBRIDGE_BUNDLE_FILE}"
# Stream diagnostics to GitHub Actions even while the helper is still running.
# Otherwise Ninja buffers them until the entire bundle command finishes.
USES_TERMINAL
VERBATIM
)
+1 -1
View File
@@ -36,7 +36,7 @@ CMake builds one on its own; the `mcpb` CLI only adds validation and signing, an
npm to build SDRangel would be a poor trade for that. The bundle is not signed, so Claude
warns that the publisher is unverified when it is installed.
Both options default to off, so a normal build is untouched. With `BUILD_MCP_BUNDLE=ON` the
With `BUILD_MCP_BUNDLE=ON` the
bundle is part of `all`, so building the Windows installer picks it up with nothing else to
remember:
@@ -0,0 +1,33 @@
# Run at build time, with the parent custom target's terminal attached. Keep stdout/stderr
# inherited so the last checkpoint is visible even if the process never exits.
foreach(required TOOLSDUMP TOOLS_JSON TOOLSDUMP_TIMEOUT)
if(NOT DEFINED ${required} OR "${${required}}" STREQUAL "")
message(FATAL_ERROR "Missing ${required} for MCP tool list generation")
endif()
endforeach()
if(NOT TOOLSDUMP_TIMEOUT MATCHES "^[1-9][0-9]*$")
message(FATAL_ERROR "TOOLSDUMP_TIMEOUT must be a positive integer")
endif()
message(STATUS "mcpbundle: starting ${TOOLSDUMP}")
message(STATUS "mcpbundle: output ${TOOLS_JSON}; timeout ${TOOLSDUMP_TIMEOUT}s")
get_filename_component(toolsdump_working_directory "${TOOLS_JSON}" DIRECTORY)
execute_process(
COMMAND "${TOOLSDUMP}" "${TOOLS_JSON}"
WORKING_DIRECTORY "${toolsdump_working_directory}"
TIMEOUT "${TOOLSDUMP_TIMEOUT}"
RESULT_VARIABLE toolsdump_result
)
if(NOT "${toolsdump_result}" STREQUAL "0")
message(FATAL_ERROR
"MCP tool list generation failed: ${toolsdump_result}. "
"See the last toolsdump checkpoint above; no 'entered main' checkpoint points to "
"executable/DLL startup, and 'initializing MainCore' points to runtime service initialization.")
endif()
if(NOT EXISTS "${TOOLS_JSON}")
message(FATAL_ERROR "MCP tool list generator exited successfully without creating ${TOOLS_JSON}")
endif()
message(STATUS "mcpbundle: tool list generation complete")
+39 -8
View File
@@ -21,8 +21,9 @@
// the tool list once and does not ask again, so the bridge has to be able to answer with the
// tools this very build of the server provides, before it has ever spoken to it.
//
// The registry is metadata and handler functions: building it touches neither the Web API nor
// any device, so a null adapter is all it needs.
// A null adapter prevents Web API calls, but MCPTools members still initialize MainCore,
// including its pipe workers and platform location/permission services. Log that separately
// from registry construction, since it can block on a headless CI runner.
#include <cstdio>
#include <cstdlib>
@@ -33,13 +34,23 @@
#include <QCoreApplication>
#include <QFile>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include "maincore.h"
#include "mcptools.h"
int main(int argc, char *argv[])
{
// Ninja and CI capture stderr through pipes; publish each checkpoint immediately.
std::setvbuf(stderr, nullptr, _IONBF, 0);
#ifdef _WIN32
std::fprintf(stderr, "toolsdump: entered main (pid %lu)\n", GetCurrentProcessId());
#else
std::fprintf(stderr, "toolsdump: entered main\n");
#endif
std::fprintf(stderr, "toolsdump: creating QCoreApplication\n");
QCoreApplication application(argc, argv);
if (argc < 2)
@@ -49,35 +60,55 @@ int main(int argc, char *argv[])
}
std::fprintf(stderr, "toolsdump: application up\n");
// MainCore starts position updates as it is constructed, and the Windows position plugin
// asks the system for location access and spins until it has an answer. On a machine where
// nobody has ever answered, such as a CI runner, that is for ever. Nothing here needs a
// plugin, so leave Qt nowhere to find one and MainCore carries on without a position source
QCoreApplication::setLibraryPaths(QStringList());
std::fprintf(stderr, "toolsdump: initializing MainCore (pipe workers, permissions, positioning)\n");
MainCore::instance();
std::fprintf(stderr, "toolsdump: MainCore ready; constructing registry\n");
MCPTools tools(nullptr);
std::fprintf(stderr, "toolsdump: registry built\n");
QJsonObject result;
std::fprintf(stderr, "toolsdump: listing tools\n");
result["tools"] = tools.listTools();
std::fprintf(stderr, "toolsdump: listed\n");
QFile file(QString::fromLocal8Bit(argv[1]));
std::fprintf(stderr, "toolsdump: opening %s\n", argv[1]);
if (!file.open(QIODevice::WriteOnly))
{
std::fprintf(stderr, "cannot write %s: %s\n", argv[1], qPrintable(file.errorString()));
return 1;
}
std::fprintf(stderr, "toolsdump: writing JSON\n");
file.write(QJsonDocument(result).toJson(QJsonDocument::Compact));
std::fprintf(stderr, "toolsdump: closing output\n");
file.close();
std::fprintf(stderr, "toolsdump: written\n");
std::fprintf(stderr, "%s: %d tools\n", argv[1], result["tools"].toArray().size());
std::fprintf(stderr, "%s: %lld tools\n", argv[1],
static_cast<long long>(result["tools"].toArray().size()));
// The registry brings background threads up with it and they do not all stop when main
// returns, which leaves the process, and so the build, hanging. Even _Exit does not get
// out, since the runtime's own teardown waits on them, so end the process from the kernel
// side. The file is written and flushed by this point
// Preserve the existing teardown workaround: MainCore has background workers, and
// this one-shot helper never runs the Qt event loop. This does not protect against
// a hang during initialization; the build target applies a separate process timeout.
std::fprintf(stderr, "toolsdump: flushing streams before process exit\n");
std::fflush(nullptr);
#ifdef _WIN32
TerminateProcess(GetCurrentProcess(), 0);
std::fprintf(stderr, "toolsdump: calling TerminateProcess\n");
if (!TerminateProcess(GetCurrentProcess(), 0)) {
std::fprintf(stderr, "toolsdump: TerminateProcess failed: %lu\n", GetLastError());
}
#endif
std::fprintf(stderr, "toolsdump: calling _Exit\n");
std::_Exit(0);
}