Compare commits

...
Author SHA1 Message Date
berrux-dev 9c1ac7cbef Merge feature/lts-viewer-validation-testing-phase into feature/qt6-migration-basis-modules-merge
Resolves 1 conflict (new_docker_build.sh, union of viewer-only + codification options); folds in fixes for pre-existing defects surfaced by the validation runs (KDReports-qt6 wiring, LtsSession RTI linking, LtsFileDialog/WLS/BSM Qt6/RTI API migration, GROUP4 target list, new --cmake_build_flags option); validated with BUILD_RTI=ON BUILD_GRANTLEE=ON in 3 Docker runs (run 3: build green, 6 base-state first-run CTest failures documented).
2026-09-27 07:33:29 +02:00
berrux-dev 36f077bce4 Emit record_replay fromQVariant for plain enums; document PPM MOC exclusion 2026-09-26 00:49:47 +02:00
torsten c664611b80 Migrates non-ArcGIS built successfully (tbc) 2026-09-24 16:49:49 +02:00
torstenandCopilot b6a97c39e5 Fix KDReports detection and add codification support for viewer-only builds
- Update FindKDReports.cmake to always use CONFIG mode first with explicit search paths
- Add --codification/--no-codification CLI options to new_docker_build.sh
- Add BUILD_CODIFICATION to cmake invocation and Docker environment variables
- Fix BUILD_GROUP5=ON when BUILD_VIEWER_ONLY is enabled
- Exclude LtsFileDialog from viewer-only builds (depends on Group 4 services)
- Fix KDReports git clone to use default branch (removed non-existent branch)
- Add KDReports symlink creation to Dockerfile for include path compatibility

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-09-23 17:37:56 +02:00
torstenandCopilot fd3e5e8380 Add --build-viewer-only option to skip Groups 1-4 and build Viewer targets only
- Add --build-viewer-only command-line option to argument parsing
- Add BUILD_VIEWER_ONLY environment variable handling
- Add conditional build logic to skip Groups 1-4 when enabled
- Update usage() documentation with new option
- Enable CTest when --build-viewer-only is specified
- Allows bypassing pre-existing Group 3/4 build errors

Tested: Successfully skips Groups 1-4 and builds Group 5 Viewer targets
Validated: situationMapCommon, advancedSymbologyXML, LtsDataView build without errors
No Qt5→Qt6 deprecation warnings in Viewer module targets

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-09-23 13:36:37 +02:00
33 changed files with 642 additions and 450 deletions

No files matched your search

@@ -191,6 +191,7 @@ OPTION(BUILD_PLUGINS_PARTICIPANT_ORG_CHART "Build ParticipantOrgChart (requires
OPTION(BUILD_PLUGINS_MSDL "Build MSDL module (requires Qt6 XmlPatterns)" OFF)
OPTION(BUILD_PLUGINS_SAAB "Build Saab module (requires Qt6 XmlPatterns)" OFF)
OPTION(BUILD_RTI "Build RTI Connext DDS dependent modules" OFF)
OPTION(BUILD_VIEWER_ONLY "Build only Viewer module (no RTI required)" OFF)
# Add Lts/ include path globally for QosProfile and other Lts headers
INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/Lts)
IF(BUILD_RTI)
@@ -525,8 +526,10 @@ if(NOT TARGET Qt6::WebEngineWidgets)
endif()
# Stub KDReports::KDReports target if not available (needed by LtsReportDisplay)
# UNKNOWN (not INTERFACE) so FindKDReports.cmake can upgrade it with a real
# IMPORTED_LOCATION; an INTERFACE imported target ignores IMPORTED_LOCATION.
if(NOT TARGET KDReports::KDReports)
add_library(KDReports::KDReports INTERFACE IMPORTED)
add_library(KDReports::KDReports UNKNOWN IMPORTED)
set_target_properties(KDReports::KDReports PROPERTIES
INTERFACE_COMPILE_DEFINITIONS "NO_KDREPORTS"
)
@@ -171,8 +171,20 @@ std::vector<T> fromJson(const QVariant &jsonData, type<std::vector<T>>)
return ret;
}
// Primary fallback for any migrated plain-`enum class` enumerators that have no
// explicit specialization below - most importantly the nested foreign
// ProjectDataManagement / Core enums a plugin reads back from the database (they are
// not in this service's idl_enums, so the per-own-enum specializations do not cover
// them). Giving the primary template a body (rather than leaving it declaration-only)
// is what lets those foreign-enum instantiations resolve, so the plugin .so links. The
// explicit specializations for primitives / own enums / structs / vectors / optionals
// all take precedence over this fallback, so it is only ever instantiated for an enum
// type, where `static_cast<T>(val.toInt())` is the correct conversion.
template<typename T>
T fromQVariant(const QVariant &val, type<T>);
T fromQVariant(const QVariant &val, type<T>)
{
return static_cast<T>(val.toInt());
}
template<>
bool fromQVariant(const QVariant &val, type<bool>)
@@ -266,6 +278,23 @@ T fromQVariant(const QVariant &val)
return fromQVariant(val, type<T>{});
}
// rtiddsgen 7.7 spells the migrated enumerators as plain `enum class` in the service
// namespace (the legacy dds::core::safe_enum<T> wrapper is gone). The record_replay
// plugin still instantiates fromQVariant<Enum>() for each enum member it reads back,
// and none of the generic overloads above are ever deduced for those types, so each of
// this service's own enumerations needs its own explicit specialization. `idl_enums`
// carries the service's own enumerations by local name and this block runs inside
// `namespace {{ namespace }}`, so the local name resolves to the fully-qualified enum
// type. (Nested foreign PDM enumerations are still not covered - same limitation as the
// documented svcBuildings exclusion.)
{% for enumeration in idl_enums %}
template<>
{{ enumeration.name }} fromQVariant(const QVariant &val, type<{{ enumeration.name }}>)
{
return static_cast<{{ enumeration.name }}>(val.toInt());
}
{% endfor %}
{% for dt in serializeTopics.member %}
//
// Begin {{ dt.typeFq }}
@@ -121,7 +121,7 @@ protected:
int64_t m_snapshot{{ dt.name }}MapTime = -1; // time for which the snapshot is valid
{% endif %} {% endfor %}
NGL::INGLLogger* m_logger;
NGL::INGLLogger& m_logger;
};
}
@@ -32,8 +32,10 @@ NGL_TARGET_LINK_LIBRARIES(LtsSessionDataDds
PRIVATE
$<$<BOOL:${BUILD_RTI}>:$<IF:$<TARGET_EXISTS:ddsconnector>,ddsconnector,>>
PUBLIC
$<$<BOOL:${BUILD_RTI}>:nddscore>
$<$<BOOL:${BUILD_RTI}>:RTI::nddscore>
$<$<BOOL:${BUILD_RTI}>:ddsQoS>
# Generated Core type support (topic_type_support<Core::PartitionTime>, ...)
$<$<BOOL:${BUILD_RTI}>:$<IF:$<TARGET_EXISTS:ddscore>,ddscore,>>
NGLLogger
Qt6::Core
)
@@ -112,7 +114,7 @@ IF(BUILD_RTI)
PRIVATE
ddsconnector
PUBLIC
nddscore
RTI::nddscore
)
TARGET_COMPILE_DEFINITIONS(LtsSession PUBLIC BUILD_RTI)
ENDIF()
@@ -40,7 +40,9 @@
#include <LtsWidgets/lts_tab_widget.h>
#include <PluginInterfaces/data_plugin_interface.h>
#include <PluginInterfaces/gui_plugin_interface.h>
#ifdef BUILD_FILTERMANAGEMENT
// Complete type needed for dynamic_pointer_cast of m_ohvIfc (BUILD_RTI code below);
// the header is self-contained (Qt only), the FilterManagement plugin is only the implementor.
#ifdef BUILD_RTI
#include <Lts/Plugins/OnlineHelp/ohp_online_help_viewer_gui_plugin_interface.h> // XXX: grrr..
#endif
#include <QtWidgets/QApplication>
@@ -156,7 +158,7 @@ QString LtsSession::toString() const
str.append(QStringLiteral(", workspaceId='%1'").arg(workspaceId()));
#ifdef BUILD_RTI
str.append(QStringLiteral(", appToString='%1'").arg(appToString(app())));
str.append(QStringLiteral(", partitionType=%1, exerciseId='%2'").arg(partitionType()).arg(exerciseId()));
str.append(QStringLiteral(", partitionType=%1, exerciseId='%2'").arg(static_cast<int>(partitionType())).arg(exerciseId()));
str.append(QStringLiteral(", LtsSessionDataDds=%1").arg(LtsSessionDataDds::toString()));
#else
str.append(QStringLiteral(", app=Login (stub)"));
@@ -20,30 +20,6 @@
#include <QtCore/QSet>
#include <QtCore/QVector>
namespace
{
// Convert LtsSession::PartitionType to IDL::PartitionType
IDL::PartitionType toIdlPartitionType(LtsSession::PartitionType type)
{
switch (type)
{
case LtsSession::PartitionType::Any:
return IDL::PartitionType::Any;
case LtsSession::PartitionType::Global:
return IDL::PartitionType::Global;
case LtsSession::PartitionType::Exercise:
return IDL::PartitionType::Exercise;
case LtsSession::PartitionType::AllExercises:
return IDL::PartitionType::AllExercises;
case LtsSession::PartitionType::Replay:
return IDL::PartitionType::Replay;
case LtsSession::PartitionType::Invalid:
default:
return IDL::PartitionType::Any;
}
}
} // namespace
namespace LtsSession
{
/**
@@ -74,7 +50,7 @@ class DdsConnectionContainer
for (int i = ddsConnections.size() - 1; i >= 0; --i)
{
const DdsConnection& conn = ddsConnections.at(i);
if (toIdlPartitionType(conn.partitionType) != IDL::PartitionType::Global)
if (conn.partitionType != IDL::PartitionType::Global)
continue;
auto ret = conn.ddsConnector.lock();
if (ret != nullptr)
@@ -242,7 +218,7 @@ bool LtsSessionDataDds::init(const DdsConnectionParams& connParams)
#endif
m_ddsConnector = std::make_shared<DDSConnector::DdsConnector>(
connParams.domainId, toIdlPartitionType(connParams.partitionType), connParams.partitionId,
connParams.domainId, connParams.partitionType, connParams.partitionId,
connParams.exerciseId, // same as partition Id in live but not in replay
type);
s_ddsConnections()->insertConnection(connParams, m_ddsConnector);
@@ -277,7 +253,7 @@ bool LtsSessionDataDds::init(const dds::domain::DomainParticipant& dp, const Dds
if (m_ddsConnector == nullptr)
{
m_ddsConnector =
std::make_shared<DDSConnector::DdsConnector>(dp, toIdlPartitionType(connParams.partitionType), type, connParams.partitionId);
std::make_shared<DDSConnector::DdsConnector>(dp, connParams.partitionType, type, connParams.partitionId);
s_ddsConnections()->insertConnection(connParams, m_ddsConnector);
}
@@ -292,38 +268,27 @@ Fetcher* LtsSessionDataDds::fetcher(const QString& yourPluginsName)
return m_fetcher;
}
QString LtsSessionDataDds::partitionTypeString(PartitionType partitionType)
QString LtsSessionDataDds::partitionTypeString(IDL::PartitionType partitionType)
{
switch (partitionType)
{
case PartitionType::AllExercises:
case IDL::PartitionType::AllExercises:
return QStringLiteral("AllExercises");
case PartitionType::Any:
case IDL::PartitionType::Any:
return QStringLiteral("Any");
case PartitionType::Exercise:
case IDL::PartitionType::Exercise:
return QStringLiteral("Exercise");
case PartitionType::Global:
case IDL::PartitionType::Global:
return QStringLiteral("Global");
case PartitionType::Replay:
case IDL::PartitionType::Replay:
return QStringLiteral("Replay");
default:
break;
}
return QStringLiteral("Invalid (%1)").arg(int(partitionType));
}
LtsSession::PartitionType LtsSessionDataDds::toPartitionType(IDL::PartitionType idlType)
{
switch (idlType)
{
case IDL::PartitionType::Any: return PartitionType::Any;
case IDL::PartitionType::Exercise: return PartitionType::Exercise;
case IDL::PartitionType::Replay: return PartitionType::Replay;
case IDL::PartitionType::AllExercises: return PartitionType::AllExercises;
case IDL::PartitionType::Global: return PartitionType::Global;
default: return PartitionType::Invalid;
}
}
QString LtsSessionDataDds::toString() const
{
return QStringLiteral("(LtsSessionDataDds: id=%1, partitionId=%2, partitionType=%3(%4), exerciseId=%5)")
@@ -349,9 +314,9 @@ int LtsSessionDataDds::partitionId() const
return m_ddsConnector ? m_ddsConnector->getPartitionId() : -1;
}
PartitionType LtsSessionDataDds::partitionType() const
IDL::PartitionType LtsSessionDataDds::partitionType() const
{
return m_ddsConnector ? toPartitionType(m_ddsConnector->getPartitionType()) : PartitionType::Global;
return m_ddsConnector ? m_ddsConnector->getPartitionType() : IDL::PartitionType::Global;
}
QStringList LtsSessionDataDds::qosFile() const
@@ -394,7 +359,7 @@ bool LtsSessionDataDds::initInternal()
assert(m_dtcPartitionTime.get());
m_dtcPartitionTime->setSubscriberCallback([this](const Core::PartitionTime& data, DataModification) {
m_curMSecSinceEpoch = data.timeStamp();
m_curMSecSinceEpoch = data.timeStamp;
emit partitionTimeChangedInternal();
});
connect(this, &LtsSessionDataDds::partitionTimeChangedInternal, this,
@@ -38,12 +38,12 @@ struct DdsConnectionParams
DdsConnectionParams() = default;
explicit DdsConnectionParams(const Core::ReplayPartition &rp)
: domainId(rp.domainId())
, partitionId(rp.id())
, exerciseId(rp.exerciseId())
: domainId(rp.domainId)
, partitionId(rp.id)
, exerciseId(rp.exerciseId)
, partitionType(IDL::PartitionType::Replay)
{
setTcpConnectionUrl(rp.tcpConnectionUrl());
setTcpConnectionUrl(rp.tcpConnectionUrl);
}
bool isValid() const { return domainId >= 0 && partitionType != IDL::PartitionType::Any; }
@@ -64,5 +64,6 @@ class FunctionTopicTests : public QObject
DdsUnitTests::FunctionRequestWithState m_requestTopicWithState;
DdsUnitTests::FunctionReplyWithState m_replyTopicWithState;
NGL::INGLLogger& m_logger;
Qt::HANDLE m_mainThreadId;
};
@@ -47,6 +47,10 @@ NGL_TARGET_LINK_LIBRARIES(BuildingControlBsm
BuildingControlCommon
svcTabCommon
)
NGL_TARGET_INCLUDE_DIRECTORIES(BuildingControlBsm
PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/../..
)
NGL_ADD_EXECUTABLE(AAR SV BuildingControlTestBsm bsm_test_main.cpp)
NGL_TARGET_LINK_LIBRARIES(BuildingControlTestBsm
@@ -3,6 +3,7 @@
#include "bsm_api.h"
#include "bsm_socket_handler.h"
#include <TrainingAreaBase/logger.h>
namespace BuildingControlUnit
{
@@ -6,6 +6,7 @@
#include "bsm_telegrams_configuration_setup.h"
#include <QtCore/QTimer>
#include <QtNetwork/QHostAddress>
#include <TrainingAreaBase/logger.h>
static const int s_aliveTimeout = 10;
namespace BuildingControlUnit
@@ -3,6 +3,7 @@
#include "bsm_socket_handler.h"
#include <utility>
#include <TrainingAreaBase/logger.h>
#include "bsm_telegrams_base.h"
#include "bsm_telegrams_bit_timeout_request.h"
#include "bsm_telegrams_chopped_signal_flash.h"
@@ -3,6 +3,7 @@
#include "bsm_telegrams_base.h"
#include "bsm_telegrams_alive.h"
#include <TrainingAreaBase/logger.h>
#include "bsm_telegrams_bit_timeout_report.h"
#include "bsm_telegrams_direct_fire.h"
#include "bsm_telegrams_sw_version_report.h"
@@ -2,7 +2,7 @@
#include "bsm_telegrams_direct_fire.h"
#include "logger.h"
#include <TrainingAreaBase/logger.h>
#include <QStringBuilder>
namespace BuildingControlUnit
@@ -3,6 +3,7 @@
#include "ism_handler.h"
#include "main_handler.h"
#include <NGLLogger/NGLLoggerBootstrap.h>
namespace WirelessLocationSystem
{
@@ -34,26 +35,26 @@ void IsmHandler::onMainStructureElementChanged(const InfrastructureManagement::M
case DataModification::eAdded:
case DataModification::eModified:
{
m_mainStructureElements[data.id()] = data;
if (data.externalWlsId() != 0)
m_externalWlsId2MseId[data.externalWlsId()] = data.id();
m_mainStructureElements[data.id] = data;
if (data.externalWlsId != 0)
m_externalWlsId2MseId[data.externalWlsId] = data.id;
QMap<int, int> roomNumber2roomId;
for (const auto &element : data.elements())
for (const auto &element : data.elements)
{
if (element.structureElementTypeId() != InfrastructureManagement::StructureElementType::Room)
if (element.structureElementTypeId != InfrastructureManagement::StructureElementType::Room)
continue;
roomNumber2roomId[element.mountingPointId()] = element.id();
roomNumber2roomId[element.mountingPointId] = element.id;
}
m_mseId2RoomId[data.id()] = roomNumber2roomId;
m_mseId2RoomId[data.id] = roomNumber2roomId;
break;
}
case DataModification::eRemoved:
{
m_mainStructureElements.remove(data.id());
m_mseId2RoomId.remove(data.id());
m_mainStructureElements.remove(data.id);
m_mseId2RoomId.remove(data.id);
for (auto it = m_externalWlsId2MseId.begin(); it != m_externalWlsId2MseId.end(); ++it)
{
if (it.value() == data.id())
if (it.value() == data.id)
{
m_externalWlsId2MseId.erase(it);
break;
@@ -10,6 +10,8 @@
#include "wls_handler.h"
#include "wls_participant_handler.h"
#include <NGLLogger/NGLLoggerBootstrap.h>
#include <LtsUtils/LtsUtilsCore/dds_vector.h>
#include <QtCore/QJsonDocument>
@@ -13,6 +13,8 @@
#include <QtCore/QDateTime>
#include <web_socket_handler.h>
#include <NGLLogger/NGLLoggerBootstrap.h>
namespace WirelessLocationSystem
{
@@ -9,6 +9,8 @@
#include <LtsUtils/LtsUtilsCore/dds_vector.h>
#include <LtsUtils/LtsUtilsCore/sfm_bytemessage_converter.h>
#include <NGLLogger/NGLLoggerBootstrap.h>
static constexpr qint64 s_dataCommunicationAliveTimeout = 5 * 1000;
static constexpr int s_maxWlsRetryCount = 3;
@@ -7,6 +7,7 @@
#include "wls_handler.h"
#include <QtCore/QDateTime>
#include <NGLLogger/NGLLoggerBootstrap.h>
namespace WirelessLocationSystem
{
@@ -5,7 +5,7 @@
#
ADD_SUBDIRECTORY(SituationMap)
IF(BUILD_RTI)
IF(BUILD_RTI OR BUILD_VIEWER_ONLY)
ADD_SUBDIRECTORY(LtsDataView)
ENDIF()
ADD_SUBDIRECTORY(LtsFileDialog)
@@ -64,15 +64,14 @@ NGL_INSTALL_TARGET(LtsDataView)
# Test-Executable
NGL_ADD_EXECUTABLE(WS NO_TRANSLATION LtsDataViewTest test.h test.cpp)
if(QT6_GUI_AVAILABLE)
NGL_TARGET_LINK_LIBRARIES(LtsDataViewTest
${Qt6_Widgets}
LtsDataView
LtsWidgets
)
endif()
NGL_TARGET_LINK_LIBRARIES(LtsDataViewTest
${Qt6_Widgets}
LtsDataView
LtsWidgets
)
# Register test with CTest
if(BUILD_TESTING)
add_test(NAME LtsDataViewTest COMMAND $<TARGET_FILE:LtsDataViewTest>)
set_tests_properties(LtsDataViewTest PROPERTIES ENVIRONMENT "QT_QPA_PLATFORM=offscreen")
endif()
@@ -19,6 +19,7 @@
#include <QElapsedTimer>
#include <QtWidgets/QScrollArea>
#include <QScroller>
#include <QTimer>
Disp::Disp()
@@ -99,5 +100,8 @@ int main(int argc, char **argv)
qWarning() << "took" << et.elapsed() << "ms";
// Auto-exit for CI/headless testing
QTimer::singleShot(1000, &app, &QApplication::quit);
return app.exec();
}
@@ -83,4 +83,5 @@ endif()
# Register test with CTest
if(BUILD_TESTING)
add_test(NAME LtsFileDialogTest COMMAND $<TARGET_FILE:LtsFileDialogTest>)
set_tests_properties(LtsFileDialogTest PROPERTIES ENVIRONMENT "QT_QPA_PLATFORM=offscreen" TIMEOUT 30)
endif()
@@ -53,14 +53,13 @@ bool DialogModelFile::initModel(const std::shared_ptr<FileManagement::Interface:
clear();
// insert Model
auto files = [this, xmlType, predicate](const std::vector<FileManagement::FileContent> & fcs, const std::vector<int> &, FileManagement::Interface::DataModification)
// Dtc per-item callback: return true to stop iteration (iterateUntil), so collect with false.
// Explicit std::function type disambiguates the void/bool overloads.
std::function<bool(const FileManagement::FileContent &)> files = [this, xmlType, predicate](const FileManagement::FileContent &fc)
{
for (const auto& fc : fcs)
{
if (!predicate(fc, xmlType))
continue;
if (predicate(fc, xmlType))
insertRow(fc);
}
return false;
};
m_flmDtc->fileContents(files);
@@ -79,7 +78,7 @@ QVariant DialogModelFile::data(const QModelIndex &index, int role) const
{
case Qt::BackgroundRole:
{
if (fl.fileStatus() == FileManagement::FileStatus::Deleted)
if (fl.fileStatus == FileManagement::FileStatus::Deleted)
return QGuiApplication::palette().color(QPalette::Disabled, QPalette::AlternateBase);
return QGuiApplication::palette().color(QPalette::Active, QPalette::Base);
}
@@ -88,38 +87,38 @@ QVariant DialogModelFile::data(const QModelIndex &index, int role) const
switch (index.column())
{
case NAME:
return fl.name();
return QString::fromStdString(fl.name);
case ISTEMPLATE:
return fl.isTemplate() ? tr("Yes") : tr("No");
return fl.isTemplate ? tr("Yes") : tr("No");
case FOLDER:
return fl.folderId();
return fl.folderId;
case FILETYPE:
return FileManagement::toString(fl.fileType());
return FileManagement::toString(fl.fileType);
case FILECATEGORY:
return FileManagement::toString(fl.fileCategory());
return FileManagement::toString(fl.fileCategory);
case FILESTATUS:
return FileManagement::toString(fl.fileStatus());
return FileManagement::toString(fl.fileStatus);
case OPERATOR:
return QString::fromStdString(m_opmDtc->user(fl.operatorId()).name());
return QString::fromStdString(m_opmDtc->user(fl.operatorId).name);
case EXERCISE:
return fl.exerciseId().has_value() ? fl.exerciseId().value() : QVariant();
return fl.exerciseId.has_value() ? fl.exerciseId.value() : QVariant();
case PHASE:
return fl.phaseId().has_value() ? fl.phaseId().value() : QVariant();
return fl.phaseId.has_value() ? fl.phaseId.value() : QVariant();
case USEDBY:
return fl.isUsedBy() ? tr("Yes") : tr("No");
return fl.isUsedBy ? tr("Yes") : tr("No");
case USES:
return QString::fromStdString(fl.usedBy());
return QString::number(fl.usedBy);
case LASTMODIFIED:
return DTG::toStringMSec(fl.lastModified().toMSecsSinceEpoch());
return DTG::toStringMSec(fl.lastModified);
case PROPERTIES:
return QString::fromStdString(fl.properties());
return QString::fromStdString(fl.properties);
default:
break;
}
}
case Qt::DecorationRole:
{
if (index.column() == NAME && fl.isHidden())
if (index.column() == NAME && fl.isHidden)
return QIcon(QLatin1String(":/style/lock.svg"));
break;
}
@@ -136,9 +135,9 @@ int DialogModelFile::findFileIdFromName(const QString &fileName) const
for (int row = 0; row < rc; ++row)
{
const FileManagement::FileContent &fl = this->getRowData(row);
if (fl.name() == fileName)
if (fl.name == fileName)
{
return fl.id();
return fl.id;
}
}
return 0;
@@ -158,7 +157,7 @@ QMimeData * DialogModelFile::mimeData(const QModelIndexList &indexes) const
const QModelIndex & index = indexes.first();
const FileManagement::FileContent & fl = this->getRowData(index.row());
int id = fl.id();
int id = fl.id;
if (id <= 0) return nullptr;
@@ -188,7 +187,7 @@ bool DialogFileProxyModel::lessThan(const QModelIndex &source_left, const QModel
switch (source_left.column())
{
case LASTMODIFIED:
return dataLeft.lastModified() < dataRight.lastModified();
return dataLeft.lastModified < dataRight.lastModified;
case NAME:
case ISTEMPLATE:
case FILETYPE:
@@ -12,6 +12,8 @@
#include <FileManagement/flm_translation.h>
#include <functional>
//#include <QtCore/QDebug>
namespace LtsWidgets
@@ -68,16 +70,16 @@ QVariant DialogModelFolder::data(const QModelIndex &index, int role) const
case NAME:
return getFolderName(folderData);
case ID:
return folderData.id();
return folderData.id;
default:
break;
}
}
case Qt::UserRole:
return folderData.id();
return folderData.id;
case Qt::DecorationRole:
{
if (folderData.isHidden())
if (folderData.isHidden)
return item->icon();
break;
}
@@ -97,22 +99,22 @@ FileManagement::Folder DialogModelFolder::getFolder(const QModelIndex &idx) cons
//-----------------------------------------------------------------------------------------------------------
QString DialogModelFolder::getFolderName(const FileManagement::Folder &folderData) const
{
QString folderName = QString::fromStdString(folderData.name());
QString folderName = QString::fromStdString(folderData.name);
if (folderName.startsWith(QStringLiteral("$")))
{
folderName.remove(0,1);
if (folderName.startsWith(QStringLiteral("EX_")))
{
int exId = folderName.remove(0,3).toInt();
return QString::fromStdString(m_exmDtc->exercise(exId).name());
return QString::fromStdString(m_exmDtc->exercise(exId).name);
}
if (folderName.startsWith(QStringLiteral("PH_")))
{
int phId = folderName.remove(0,3).toInt();
return QString::fromStdString(m_exmDtc->exercisePhase(phId).name());
return QString::fromStdString(m_exmDtc->exercisePhase(phId).name);
}
}
return FileManagement::trFolderName(folderName);
return FileManagement::trFolderName(folderName.toStdString());
}
//-----------------------------------------------------------------------------------------------------------
@@ -121,7 +123,7 @@ bool DialogModelFolder::isRowRemovable(NGLTreeItem *parentItem) const
if (!parentItem)
return false;
FileManagement::Folder folderData = parentItem->data().value<FileManagement::Folder>();
return folderData.isEditable();
return folderData.isEditable;
}
//-----------------------------------------------------------------------------------------------------------
@@ -133,34 +135,32 @@ bool DialogModelFolder::initModel(int exerciseId, const QVector<FileManagement::
QHash<int, QModelIndex> parentItemIndexCache;
QVector<int> idsToExclude;
auto cbFolders = [this, exerciseId, &tmap, &idsToExclude, &mainFolders, &parentItemIndexCache] (const std::vector<FileManagement::Folder> &folders, const std::vector<int32_t> &ids, FileManagement::Interface::DataModification mod)
// Dtc per-item callback: return true to stop iteration (iterateUntil), so collect with false.
// Explicit std::function type disambiguates the void/bool overloads.
std::function<bool(const FileManagement::Folder &)> cbFolders = [this, exerciseId, &tmap, &idsToExclude, &mainFolders] (const FileManagement::Folder &data)
{
Q_UNUSED(ids);
Q_UNUSED(mod);
for (const auto &data : folders)
if (data.isDeleted)
return false;
if (exerciseId > 0)
{
if (data.isDeleted())
continue;
if (exerciseId > 0)
QString folderName = QString::fromStdString(data.name);
if (folderName.startsWith(QStringLiteral("$EX_")))
{
QString folderName = QString::fromStdString(data.name());
if (folderName.startsWith(QStringLiteral("$EX_")))
{
int exId = folderName.remove(0, 4).toInt();
if (exId != exerciseId)
idsToExclude.append(data.id());
}
int exId = folderName.remove(0, 4).toInt();
if (exId != exerciseId)
idsToExclude.append(data.id);
}
FileManagement::MainFolder mainFolder = static_cast<FileManagement::MainFolder>(data.path()[0]);
//qCritical() << "Folder = " << data.name() << ", mainFolder = " << mainFolder.underlying();
}
FileManagement::MainFolder mainFolder = static_cast<FileManagement::MainFolder>(data.path[0]);
//qCritical() << "Folder = " << data.name << ", mainFolder = " << static_cast<int>(mainFolder);
if (!mainFolders.contains(mainFolder))
{
// qCritical() << "Excluded id = " << data.id();
idsToExclude.append(data.id());
// qCritical() << "Excluded id = " << data.id;
idsToExclude.append(data.id);
}
tmap.insert(data.id(), data);
}
tmap.insert(data.id, data);
return false;
};
m_flmDtc->folders(cbFolders);
@@ -173,16 +173,16 @@ bool DialogModelFolder::initModel(int exerciseId, const QVector<FileManagement::
int parentId = 0;
FileManagement::Folder fld = iter.value();
if (fld.path().size() > 1)
if (fld.path.size() > 1)
{
parentId = fld.path().at(fld.path().size() - 2);
int idPos2 = fld.path().at(1);
parentId = fld.path.at(fld.path.size() - 2);
int idPos2 = fld.path.at(1);
if (idsToExclude.contains(idPos2))
continue;
}
else // main Folders
{
if (idsToExclude.contains(fld.id()))
if (idsToExclude.contains(fld.id))
continue;
}
@@ -212,7 +212,7 @@ bool DialogModelFolder::onInsertRow(int &row, const QModelIndex &parent, const F
QVariant data;
data.setValue(folderData);
QIcon icon;
if (folderData.isHidden())
if (folderData.isHidden)
icon = QIcon(QLatin1String(":/style/lock.svg"));
NGLTreeItem *item = new NGLTreeItem(data, icon);
item->setModified(true);
@@ -227,7 +227,7 @@ bool DialogModelFolder::onUpdateRow(const QModelIndex &index, const FileManageme
data.setValue(folderData);
QIcon icon;
if (folderData.isHidden())
if (folderData.isHidden)
icon = QIcon(QLatin1String(":/style/lock.svg"));
return setItemData(index, data, icon);
}
@@ -243,7 +243,7 @@ bool DialogModelFolder::onDeleteRow(const QModelIndex &idx, bool isFirstItem)
{
NGLTreeItem *item = privateData(idx);
FileManagement::Folder data = item->data().value<FileManagement::Folder>();
data.setDeleted(true);
data.isDeleted = true;
}
removeRow(idx.row(), idx.parent());
@@ -282,19 +282,22 @@ bool DialogModelProxyFolder::lessThan(const QModelIndex &source_left, const QMod
const FileManagement::Folder folderLeft = itemLeft->data().value<FileManagement::Folder>();
const FileManagement::Folder folderRight = itemRight->data().value<FileManagement::Folder>();
if (folderLeft.mainFolder() == FileManagement::MainFolder::GlobalDocuments || folderRight.mainFolder() == FileManagement::MainFolder::GlobalDocuments ||
folderLeft.mainFolder() == FileManagement::MainFolder::UserDocuments || folderRight.mainFolder() == FileManagement::MainFolder::UserDocuments ||
folderLeft.mainFolder() == FileManagement::MainFolder::ExerciseDocuments || folderRight.mainFolder() == FileManagement::MainFolder::ExerciseDocuments)
// mainFolder() was part of the legacy Folder API; the generated Folder carries it in path[0]
const int leftMain = folderLeft.path.empty() ? 0 : static_cast<int>(folderLeft.path[0]);
const int rightMain = folderRight.path.empty() ? 0 : static_cast<int>(folderRight.path[0]);
if (leftMain == static_cast<int>(FileManagement::MainFolder::GlobalDocuments) || rightMain == static_cast<int>(FileManagement::MainFolder::GlobalDocuments) ||
leftMain == static_cast<int>(FileManagement::MainFolder::UserDocuments) || rightMain == static_cast<int>(FileManagement::MainFolder::UserDocuments) ||
leftMain == static_cast<int>(FileManagement::MainFolder::ExerciseDocuments) || rightMain == static_cast<int>(FileManagement::MainFolder::ExerciseDocuments))
{
return FileManagement::toInt(folderLeft.mainFolder()) < FileManagement::toInt(folderRight.mainFolder());
return leftMain < rightMain;
}
if (folderLeft.name() != folderRight.name())
if (folderLeft.name != folderRight.name)
{
return (folderLeft.name() < folderRight.name());
return (folderLeft.name < folderRight.name);
}
return folderLeft.id() < folderRight.id();
return folderLeft.id < folderRight.id;
}
//----------------------------------------------------------------------------
@@ -324,10 +327,10 @@ bool DialogModelProxyFolder::filterAcceptsRow(int source_row, const QModelIndex
return QSortFilterProxyModel::filterAcceptsRow(source_row, source_parent);
const FileManagement::Folder data = child->data().value<FileManagement::Folder>();
if (data.operatorId() != m_operatorId)
if (data.operatorId != m_operatorId)
{
// it's user folder -> check if it's in user documents and hide if it's not for the current operator
for (int p : data.path())
for (int p : data.path)
{
if (static_cast<FileManagement::MainFolder>(p) == FileManagement::MainFolder::UserDocuments)
return false;
@@ -92,7 +92,7 @@ bool LtsFileDialog::init(LtsSession::LtsSession *session,
//-----------------------------------------------------------------------------------------------------------
int LtsFileDialog::getSelectedFileId() const
{
return m_editItemWidget->m_selectedFileContent.id();
return m_editItemWidget->m_selectedFileContent.id;
}
//-----------------------------------------------------------------------------------------------------------
@@ -114,8 +114,8 @@ bool LtsFileDialogWidget::init(LtsSession::LtsSession *session,
// initial all valid, not-deleted files
auto predicate = [](const FileManagement::FileContent &fc, const QString &xmlType)
{
return (!fc.isDeleted() && fc.fileStatus() == FileManagement::FileStatus::Valid &&
(xmlType.isEmpty() || fc.properties().find(xmlType.toStdString()) != std::string::npos));
return (!fc.isDeleted && fc.fileStatus == FileManagement::FileStatus::Valid &&
(xmlType.isEmpty() || fc.properties.find(xmlType.toStdString()) != std::string::npos));
};
m_modelFile->initModel(m_dtc, m_opmDtc, xmlType, predicate);
@@ -200,7 +200,7 @@ void LtsFileDialogWidget::onFolderSelectionChanged(const QItemSelection& /*selec
//-----------------------------------------------------------------------------------------------------------
void LtsFileDialogWidget::requestReadingSelectedFile()
{
emit readSelectedFile(m_selectedFileContent.id());
emit readSelectedFile(m_selectedFileContent.id);
}
//-----------------------------------------------------------------------------------------------------------
@@ -228,7 +228,7 @@ void LtsFileDialogWidget::onChooseFileName()
}
//----------------------------------------------------------------------------
void LtsFileDialogWidget::onFolderChanged(const FileManagement::Folder &data, FileManagement::Interface::DataModification modType)
void LtsFileDialogWidget::onFolderChanged(const FileManagement::Folder &data, DataModification modType)
{
Q_UNUSED(data);
Q_UNUSED(modType);
@@ -240,7 +240,7 @@ void LtsFileDialogWidget::onFolderChanged(const FileManagement::Folder &data, Fi
}
//----------------------------------------------------------------------------
void LtsFileDialogWidget::onFileChanged(const FileManagement::FileContent &data, FileManagement::Interface::DataModification modType)
void LtsFileDialogWidget::onFileChanged(const FileManagement::FileContent &data, DataModification modType)
{
bool condition = showFile(data, m_xmlType);
@@ -250,17 +250,17 @@ void LtsFileDialogWidget::onFileChanged(const FileManagement::FileContent &data,
if (selIndex.isValid())
row = selIndex.row();
if (modType == FileManagement::Interface::DataModification::eRemoved)
if (modType == DataModification::eRemoved)
{
m_modelFile->removeRow(row);
return;
}
if (modType == FileManagement::Interface::DataModification::eAdded && condition)
if (modType == DataModification::eAdded && condition)
{
m_modelFile->insertRow(data);
return;
}
if (modType == FileManagement::Interface::DataModification::eModified && condition)
if (modType == DataModification::eModified && condition)
{
m_modelFile->setRowData(row, data);
return;
@@ -301,7 +301,7 @@ void LtsFileDialogWidget::enaDisa()
{
QString folderName = m_modelFolder->getFolderName(m_modelFolder->getFolder(idxFolder));
m_ui->lblSelectedFolder->setText(folderName);
m_selectedFolderId = m_modelFolder->getFolder(idxFolder).id();
m_selectedFolderId = m_modelFolder->getFolder(idxFolder).id;
}
else
{
@@ -314,7 +314,7 @@ void LtsFileDialogWidget::enaDisa()
if (idxFile.isValid())
{
m_selectedFileContent = m_modelFile->getRowData(m_ui->widgetFiles->currentIndex().row());
m_fileName = m_selectedFileContent.name();
m_fileName = QString::fromStdString(m_selectedFileContent.name);
m_ui->leFile->setText(m_fileName);
}
else
@@ -333,31 +333,31 @@ void LtsFileDialogWidget::enaDisa()
//--------------------------------------------------------------------------
bool LtsFileDialogWidget::showFile(const FileManagement::FileContent &fc, const QString &xmlType)
{
if (fc.isDeleted())
if (fc.isDeleted)
return false;
if (fc.folderId() != m_selectedFolderId)
if (fc.folderId != m_selectedFolderId)
return false;
if (!m_fileTypes.isEmpty() && !m_fileTypes.contains(fc.fileType()))
if (!m_fileTypes.isEmpty() && !m_fileTypes.contains(fc.fileType))
return false;
if (fc.fileStatus() != FileManagement::FileStatus::Valid)
if (fc.fileStatus != FileManagement::FileStatus::Valid)
return false;
// get folder selected
const QModelIndex selIndex = selectedIndexViewFolder();
const NGLTreeItem *item = m_modelFolder->privateData(selIndex);
const FileManagement::Folder fld = item->data().value<FileManagement::Folder>();
const auto &fldPath = fld.path();
const auto &fldPath = fld.path;
// filter in USER DOCUMENTS folder and subfolders
if (LtsUtils::isInVector(fldPath, static_cast<int>(FileManagement::MainFolder::UserDocuments)))
{
const int operatorId = LtsSessionData::LtsSessionData::getInstance()->getOperatorId();
// filter on User Category and OperatorId
if (fc.fileCategory() == FileManagement::FileCategory::User &&
fc.operatorId() != operatorId)
if (fc.fileCategory == FileManagement::FileCategory::User &&
fc.operatorId != operatorId)
return false;
}
@@ -365,20 +365,20 @@ bool LtsFileDialogWidget::showFile(const FileManagement::FileContent &fc, const
if (LtsUtils::isInVector(fldPath, static_cast<int>(FileManagement::MainFolder::ExerciseDocuments)))
{
// filter on Category = Exercise
if (fc.fileCategory() != FileManagement::FileCategory::Exercise)
if (fc.fileCategory != FileManagement::FileCategory::Exercise)
return false;
if (m_session->exerciseId() > 0)
{
// filter on exact exerciseId
if (fc.exerciseId() != m_session->exerciseId())
// filter on exact exerciseId (optional member, no comparison operator)
if (!fc.exerciseId.has_value() || fc.exerciseId.value() != m_session->exerciseId())
return false;
}
}
// filter on XmlType
if (fc.fileType() == FileManagement::FileType::Xml && !xmlType.isEmpty() && fc.properties().size() > 0)
if (fc.fileType == FileManagement::FileType::Xml && !xmlType.isEmpty() && fc.properties.size() > 0)
{
const QString &prop = QString::fromStdString(fc.properties());
const QString &prop = QString::fromStdString(fc.properties);
if (!prop.contains(xmlType, Qt::CaseSensitive))
{
return false;
@@ -65,12 +65,12 @@ protected slots:
// folders
void onFolderSelectionChanged(const QItemSelection & /*selected*/,
const QItemSelection& /*deselected*/);
void onFolderChanged(const FileManagement::Folder &data, FileManagement::Interface::DataModification modType);
void onFolderChanged(const FileManagement::Folder &data, DataModification modType);
// files
FileManagement::FileContent onSelectFile();
void onChooseFileName();
void onFileChanged(const FileManagement::FileContent &data, FileManagement::Interface::DataModification modType);
void onFileChanged(const FileManagement::FileContent &data, DataModification modType);
bool showFile(const FileManagement::FileContent &fc, const QString &xmlType);
private:
@@ -9,6 +9,7 @@
#include "lts_file_dialog.h"
#include <QtWidgets/QApplication>
#include <QTimer>
#include <NGLLogger/NGLLoggerBootstrap.h>
#include <DDS/DDSConnector/dds_connector.h>
@@ -61,6 +62,9 @@ int main(int argc, char **argv)
view->show();
// Auto-exit for CI/headless testing
QTimer::singleShot(1000, &app, &QApplication::quit);
int ret = app.exec();
view->deleteLater();
@@ -8,46 +8,55 @@ if(NOT Qt6_FOUND)
endif()
set(_qt_prefix "Qt6")
# If KDReports already provided via variables, use them
IF(KDREPORTS_INCLUDE_DIR AND KDREPORTS_KDREPORTS_INCLUDE_DIR AND KDREPORTS_LIBRARY)
SET(KDREPORTS_FOUND TRUE)
SET(KDReports_FOUND TRUE)
ELSE()
# Prefer KDReports-qt6 package config if installed under standard prefixes
if(EXISTS "${CMAKE_PREFIX_PATH}/lib64/cmake/KDReports-qt6" OR EXISTS "/usr/local/lib64/cmake/KDReports-qt6" OR EXISTS "${CMAKE_PREFIX_PATH}/lib/cmake/KDReports-qt6" OR EXISTS "/usr/local/lib/cmake/KDReports-qt6")
message(STATUS "Found KDReports-qt6 package config")
set(KDREPORTS_FOUND TRUE)
set(KDReports_FOUND TRUE)
# Best-effort defaults; specific projects will use the imported target below
set(KDREPORTS_INCLUDE_DIR "/usr/local/include/KDReports-qt6")
set(KDREPORTS_KDREPORTS_INCLUDE_DIR "/usr/local/include/KDReports-qt6")
# Check lib64 first, then lib
if(EXISTS "/usr/local/lib64/libkdreports-qt6.so")
set(KDREPORTS_LIBRARY "/usr/local/lib64/libkdreports-qt6.so")
else()
set(KDREPORTS_LIBRARY "/usr/local/lib/libkdreports-qt6.so")
endif()
# Prefer KDReports-qt6 package config if installed under standard prefixes
# Search in common installation locations for KDReports cmake config
list(APPEND CMAKE_PREFIX_PATH /usr/local /usr)
find_package(KDReports-qt6 CONFIG QUIET PATHS /usr/local/lib/cmake/KDReports-qt6 /usr/local/lib64/cmake/KDReports-qt6 /usr/lib/cmake/KDReports-qt6 /usr/lib64/cmake/KDReports-qt6)
if(KDReports-qt6_FOUND OR KDReports-qt6_CONFIG)
message(STATUS "Found KDReports-qt6 package config")
set(KDREPORTS_FOUND TRUE)
set(KDReports_FOUND TRUE)
# KDReports-qt6 config provides KDReports::KDReports imported target
# Set variables for compatibility with code that uses them
set(KDREPORTS_INCLUDE_DIR "/usr/local/include/KDReports-qt6")
set(KDREPORTS_KDREPORTS_INCLUDE_DIR "/usr/local/include/KDReports-qt6")
# Check lib64 first, then lib
if(EXISTS "/usr/local/lib64/libkdreports-qt6.so")
set(KDREPORTS_LIBRARY "/usr/local/lib64/libkdreports-qt6.so")
else()
FIND_PATH(KDREPORTS_INCLUDE_DIR KDReports/KDReports.h)
FIND_PATH(KDREPORTS_KDREPORTS_INCLUDE_DIR KDReports.h PATH_SUFFIXES KDReports)
FIND_LIBRARY(KDREPORTS_LIBRARY NAMES kdreports kdreport kdreports-qt6)
INCLUDE(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(KDReports DEFAULT_MSG
KDREPORTS_LIBRARY
KDREPORTS_INCLUDE_DIR
KDREPORTS_KDREPORTS_INCLUDE_DIR
)
MARK_AS_ADVANCED(KDREPORTS_INCLUDE_DIR KDREPORTS_LIBRARY)
MARK_AS_ADVANCED(KDREPORTS_KDREPORTS_INCLUDE_DIR KDREPORTS_LIBRARY)
set(KDREPORTS_LIBRARY "/usr/local/lib/libkdreports-qt6.so")
endif()
ENDIF()
else()
# Search both /usr and /usr/local for apt-installed or manually built KDReports
FIND_PATH(KDREPORTS_INCLUDE_DIR KDReports/KDReports.h PATHS /usr/include /usr/local/include)
FIND_PATH(KDREPORTS_KDREPORTS_INCLUDE_DIR KDReports.h PATH_SUFFIXES KDReports PATHS /usr/include /usr/local/include)
FIND_LIBRARY(KDREPORTS_LIBRARY NAMES kdreports kdreport kdreports-qt6 PATHS /usr/lib /usr/lib64 /usr/local/lib /usr/local/lib64)
IF(NOT TARGET KDReports::KDReports)
ADD_LIBRARY(KDReports::KDReports UNKNOWN IMPORTED)
INCLUDE(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(KDReports DEFAULT_MSG
KDREPORTS_LIBRARY
KDREPORTS_INCLUDE_DIR
KDREPORTS_KDREPORTS_INCLUDE_DIR
)
MARK_AS_ADVANCED(KDREPORTS_INCLUDE_DIR KDREPORTS_LIBRARY)
MARK_AS_ADVANCED(KDREPORTS_KDREPORTS_INCLUDE_DIR KDREPORTS_LIBRARY)
endif()
# The root CMakeLists may already have created a no-op stub target for
# KDReports::KDReports (before FindKDReports ran). When the library was found,
# upgrade that existing target to the real imported target instead of skipping.
IF(KDReports_FOUND)
IF(NOT TARGET KDReports::KDReports)
ADD_LIBRARY(KDReports::KDReports UNKNOWN IMPORTED)
ENDIF()
SET_PROPERTY(TARGET KDReports::KDReports PROPERTY INTERFACE_INCLUDE_DIRECTORIES "${KDREPORTS_INCLUDE_DIR}" "${KDREPORTS_KDREPORTS_INCLUDE_DIR}")
SET_PROPERTY(TARGET KDReports::KDReports PROPERTY INTERFACE_SYSTEM_INCLUDE_DIRECTORIES "${KDREPORTS_INCLUDE_DIR}" "${KDREPORTS_KDREPORTS_INCLUDE_DIR}")
SET_PROPERTY(TARGET KDReports::KDReports PROPERTY IMPORTED_LOCATION "${KDREPORTS_LIBRARY}")
SET_PROPERTY(TARGET KDReports::KDReports PROPERTY INTERFACE_COMPILE_DEFINITIONS "")
SET_PROPERTY(TARGET KDReports::KDReports APPEND PROPERTY INTERFACE_LINK_LIBRARIES ${_qt_prefix}::Core ${_qt_prefix}::Gui ${_qt_prefix}::PrintSupport ${_qt_prefix}::Widgets)
ELSE()
IF(NOT TARGET KDReports::KDReports)
ADD_LIBRARY(KDReports::KDReports UNKNOWN IMPORTED)
ENDIF()
ENDIF()
@@ -122,11 +122,10 @@ SET(_components ALL SV WS AAR TOOLSWS TOOLSSV HLA)
MACRO (NGL_GET_BUILDPARTS _args _buildopts)
SET(${_buildopts} "")
SET(_at_least_one_keyword_found OFF)
MESSAGE(STATUS "DEBUG NGL_GET_BUILDPARTS: BUILD_ALL=${BUILD_ALL}, BUILD_SV=${BUILD_SV}, BUILD_WS=${BUILD_WS}, BUILD_AAR=${BUILD_AAR}")
MESSAGE(STATUS "DEBUG NGL_GET_BUILDPARTS: _args (name)=${_args}, _args (value)=${${_args}}, BUILD_ALL=${BUILD_ALL}, BUILD_SV=${BUILD_SV}, BUILD_WS=${BUILD_WS}, BUILD_AAR=${BUILD_AAR}")
LIST(FIND ${_args} "ALL" _all)
IF (_all GREATER -1)
@@ -148,8 +147,12 @@ MACRO (NGL_GET_BUILDPARTS _args _buildopts)
IF(_idx GREATER -1)
LIST(REMOVE_AT ${_args} ${_idx})
SET(_at_least_one_keyword_found ON)
MESSAGE(STATUS "DEBUG ELSE: _component=${_component}, BUILD_${_component}=${BUILD_${_component}}")
IF(BUILD_${_component})
LIST(APPEND ${_buildopts} ${_component})
MESSAGE(STATUS "DEBUG ELSE: Added ${_component} to _buildopts")
ELSE()
MESSAGE(STATUS "DEBUG ELSE: BUILD_${_component} is FALSE/NOTFOUND, NOT adding ${_component}")
ENDIF()
ENDIF()
ENDFOREACH()
@@ -162,10 +165,10 @@ ENDMACRO()
MACRO (NGL_PARSE_BUILDCOMPONENT_INTERNAL _args _properties _build_target)
NGL_GET_BUILDPARTS(${_args} _buildopts)
NGL_GET_BUILDPARTS(_args _buildopts)
SET(${_build_target} OFF)
MESSAGE(STATUS "DEBUG NGL_PARSE: _buildopts=${_buildopts}, _components=${_components}")
MESSAGE(STATUS "DEBUG NGL_PARSE: _args=${_args}, _buildopts=${_buildopts}, _components=${_components}")
FOREACH(_component ${_components})
LIST(FIND _buildopts ${_component} _idx)
@@ -320,10 +323,14 @@ MACRO (NGL_ADD_EXECUTABLE)
NGL_PARSE_BUILDCOMPONENT_INTERNAL(_args _properties _build_target)
LIST(GET _args 0 _target_NAME)
MESSAGE(STATUS "DEBUG NGL_ADD_EXECUTABLE: _target_NAME=${_target_NAME}, _build_target=${_build_target}")
IF(_build_target)
MESSAGE(STATUS "DEBUG NGL_ADD_EXECUTABLE: Creating executable ${_target_NAME}")
ADD_EXECUTABLE(${_args})
SET_TARGET_PROPERTIES(${_target_NAME} PROPERTIES ${_properties})
SET_PROPERTY(GLOBAL APPEND PROPERTY NGL_TS_TARGETS ${_target_NAME})
ELSE()
MESSAGE(STATUS "DEBUG NGL_ADD_EXECUTABLE: SKIPPING executable ${_target_NAME}")
ENDIF()
@@ -1,180 +0,0 @@
#!/bin/bash
set -e
# Check if .envrc exists
if [ ! -f .envrc ]; then
echo "ERROR: .envrc not found in current directory"
echo "Please create .envrc with required environment variables (e.g., MIGRATE_PIPELINE_ROOT)"
exit 1
fi
# Load environment variables from .envrc
set -a
source .envrc
set +a
# Allow BUILD_RTI to be overridden via environment variable or argument
BUILD_RTI="${BUILD_RTI:-ON}"
if [ "$1" = "OFF" ]; then
BUILD_RTI=OFF
elif [ "$1" = "ON" ]; then
BUILD_RTI=ON
fi
# BUILD_GRANTLEE must be ON: codegen (IDL/DTC/FTC/SQL) generation is required
# for the migrated services. Only the ON variant is supported and validated.
BUILD_GRANTLEE="${BUILD_GRANTLEE:-ON}"
# Run CMake configuration and build inside Docker
docker run --rm \
-e MIGRATE_PIPELINE_ROOT \
-e LANG \
-e LC_ALL \
-e BUILD_RTI \
-v ${MIGRATE_PIPELINE_ROOT}:/workspace \
-v ${MIGRATE_PIPELINE_ROOT}/rti_license.dat:/workspace/rti_license.dat \
-w /workspace/qt-migration-legatus/qt6-after/qt6-migrated-legatus \
ubuntu:26.04 bash -c "
# Fail the container on any command error and on failing pipeline
# stages (the make call below pipes through tee, which would otherwise
# mask a non-zero make exit code).
set -eo pipefail
apt-get update -qq && \
apt-get install -y -qq \
build-essential \
cmake \
libboost-dev \
libkf6texttemplate-dev \
libqt6core6 \
libqt6gui6 \
libqt6widgets6 \
qt6-base-dev \
qt6-svg-dev \
qt6-websockets-dev \
qt6-serialport-dev \
qt6-tools-dev-tools \
qt6-tools-dev \
qt6-l10n-tools \
libgtest-dev \
libpq-dev \
postgresql-server-dev-all \
libqt6sql6-psql \
libvlc-dev \
vlc-plugin-base \
git > /dev/null 2>&1
# Set RTI license
export RTI_LICENSE_FILE=/workspace/rti_license.dat
# Set Qt platform for headless testing (required for gtest_discover_tests)
export QT_QPA_PLATFORM=offscreen
# Qt6 is installed via apt - CMake finds it automatically in /usr/lib/x86_64-linux-gnu/cmake
# Clean and configure
rm -rf build4
mkdir -p build4
cd build4
cmake .. \
-DBUILD_RTI=${BUILD_RTI} \
-DBUILD_GRANTLEE=${BUILD_GRANTLEE} \
-DBUILD_ARCGIS=OFF \
-DCMAKE_CXX_STANDARD=17 \
-DCMAKE_BUILD_TYPE=Release
# Validation builds the full service set, not only the two services that
# happened to be migrated first: every service library, the per-service
# test executables, and the shared libraries they need. Targets that are
# known blocked for reasons outside this migration are excluded below, so
# a green run means every migratable service actually compiles.
#
# Excluded, each for a dependency this migration cannot supply:
# svcTargetManagement - needs SituationMap::ElevationMap /
# LineOfSightCalculation, the GDAL module
# whose CMakeLists states it was never
# migrated (main_handler.h:18 includes
# <ElevationMap/elevation_map.h>).
# svcExerciseManagement - needs QStateMachine / QState, moved from
# svcInfrastructureManagement QtCore to the Qt SCXML module in Qt6
# exmStateMachineTest (Qt6::StateMachine). No qt6-scxml-dev in
# the image and qstatemachine.h is absent.
make -j8 \
ddscore ddsftccore ddsQoS ddsconnector ddsfts migration_tests \
NGLLogger nglapplication nglcompression nglcoreapplication ngldb \
nglguiapplication nglsettings nglsingleapplication nglsinglecoreapplication \
ngltools ngltranslator nglwidgets qtsingleapplication qtsinglecoreapplication \
wms_json_ds tcpserver \
svcAesClient svcAesCommon svcAesServer \
svcAlmClient svcAlmCommon svcAlmServer \
svcAreaEffectSimulation \
svcAsmClient svcAsmCommon svcAsmServer svcAssetManagement \
svcAudioVideoManagement \
svcAvmClient svcAvmCommon svcAvmServer \
svcBldClient svcBldCommon svcBldServer svcBuildings \
svcCentralEffectSimulation \
svcCesClient svcCesCommon svcCesDDSCommon svcCesGeoPairingImpl svcCesServer \
svcCmoClient svcCmoCommon svcCmoServer \
svcCnrHelper svcCodClient svcCodCommon svcCodServer \
svcCombatNetRadio svcComponentMonitor svcCoreClient svcDifferentialGPS \
svcEemClient svcEemCommon svcEemServer \
svcEffectShowClient \
svcEscClient svcEscCommon svcEscServer \
svcEsmClient svcEsmCommon svcEsmServer \
svcEvlClient svcEvlCommon svcEvlServer \
svcExerciseEventManagement \
svcExmClient svcExmCommon svcExmServer \
svcFileManagement svcFilterManagement \
svcFlmClient svcFlmCommon svcFlmServer \
svcFltClient svcFltCommon svcFltServer \
svcGimClient svcGimCommon svcGimServer svcGmpServer \
svcGnaClient svcGnaCommon svcGnaServer \
svcGuezIedManagement \
svcIsmClient svcIsmCommon svcIsmServer \
svcMapDummy \
svcMdmClient svcMdmCommon svcMdmServer \
svcMessaging svcMeteoDataManagement \
svcMrkClient svcMrkCommon svcMrkServer \
svcMsgClient svcMsgCommon svcMsgServer \
svcNomClient svcNomCommon svcNomServer svcNotificationManagement \
svcNmeClient svcNmeCommon svcNmeServer svcNmeaManagement \
svcObdClient svcObdCommon svcObdServer \
svcOcBaseDownload svcOcSituationClient svcOcSituationServer \
svcOperatorManagement \
svcOpmClient svcOpmCommon svcOpmServer \
svcOreDummy \
svcOscClient svcOscCommon svcOscServer \
svcOssClient svcOssCommon svcOssServer \
svcParticipantManagement \
svcPdmClient svcPdmCommon svcPdmServer svcPedDummy \
svcPpmClient svcPpmCommon svcPpmServer \
svcPreClient svcPreCommon svcPreServer svcProjectDataManagement svcPwsCommon \
svcRecCommon svcRecordingManagement \
svcRepClient svcRepCommon svcRepCommonExecutable svcRepServer \
svcReportAnalyzer svcReportGenerator \
svcScmClient svcScmCommon svcScmServer svcScrDummy \
svcServiceManagement svcSituationReplay svcSmoDummy \
svcSomClient svcSomCommon svcSomServer svcSoundManagement svcSrmCommon \
svcSrpClient svcSrpCommon svcSrpServer \
svcTabClient svcTabCommon svcTabServer \
svcTabocClient svcTabocCommon svcTabocServer \
svcTgtClient svcTgtCommon svcTgtServer \
svcTrainingAreaBase svcTrainingAreaBaseOC \
svcVideoOverlayManagement \
svcWarehouseGuez svcWarehouseManagement \
svcWhgClient svcWhgCommon svcWhgServer \
asmTests avmFtTests avmTests avmVcaTests cesTests eemTests evlTests \
exmDtUnitTest exmTests flmTests gmpTests ismTests \
mrkTests mvsTests nomTests opmTests pdmTests ppmTests preTests repTests \
scmDtUnitTest srpFtTests tgtTests whgTests \
2>&1 | tee build.log
# Run the CTest unit tests as an explicit validation step.
# We are already inside build4 (cd build4 above), so --test-dir is the
# current directory. --output-on-failure surfaces failing test output; a
# non-zero exit propagates through set -e and fails the container validation.
# Set QT_QPA_PLATFORM for headless testing (no display required)
export QT_QPA_PLATFORM=offscreen
ctest --test-dir . --output-on-failure
"
@@ -16,11 +16,13 @@ set -euo pipefail
# - Optional disabling of ctest
# - Optional tests-only mode
# - Uses a temporary container-side script for robust execution
# - Supersedes docker_build.sh: its full migrated-service target set, apt
# package additions, and exclusion notes are merged into this script
# -----------------------------------------------------------------------------
usage() {
cat <<'EOF'
Usage: ./build.sh [ON|OFF] [options]
Usage: ./new_docker_build.sh [ON|OFF] [options]
BUILD_RTI:
First positional argument may be:
@@ -33,12 +35,17 @@ BUILD_GRANTLEE:
BUILD_ARCGIS:
--arcgis / --no-arcgis Enable/disable BUILD_ARCGIS (default: OFF)
BUILD_CODIFICATION:
--codification / --no-codification Enable/disable BUILD_CODIFICATION (default: OFF, requires KDReports)
Build groups:
--group1 / --no-group1 Enable/disable DDS core libraries build
--group2 / --no-group2 Enable/disable migration_tests build
--group3 / --no-group3 Enable/disable remaining application targets build
--group4 / --no-group4 Enable/disable Lts/Proxies module targets and tests only
--group3 / --no-group3 Enable/disable remaining application and service targets build
(NGL app targets + full migrated Lts service set with per-service tests)
--group4 / --no-group4 Enable/disable DDS tests and Lts/Proxies module targets
--group5 / --no-group5 Enable/disable Lts/Viewer module targets and tests only
--build-viewer-only Build only Viewer module targets (skip Groups 1-4, implies --group5 --codification; ctest runs)
Test execution:
--no-ctest Skip ctest execution
@@ -49,6 +56,8 @@ Prebuilt Docker image usage:
--use-prebuilt-image Use a Docker image with dependencies already installed
--no-prebuilt-image Use base image and run apt-get install inside the container
--docker-image IMAGE Docker image name to use/build/delete
--cmake_build_flags FLAGS Extra CMake flags passed to the in-container configure
(space-separated -D flags, e.g. "-DBUILD_WLS=ON -DGUEZ_MODE=ON")
Prebuilt Docker image management:
--build-prebuilt-image Build the prebuilt Docker image before running the build
@@ -60,6 +69,7 @@ Environment variable overrides:
BUILD_RTI=ON|OFF
BUILD_GRANTLEE=ON|OFF
BUILD_ARCGIS=ON|OFF
BUILD_CODIFICATION=ON|OFF
BUILD_GROUP1=ON|OFF
BUILD_GROUP2=ON|OFF
BUILD_GROUP3=ON|OFF
@@ -83,6 +93,7 @@ Defaults:
BUILD_RTI=ON
BUILD_GRANTLEE=ON
BUILD_ARCGIS=OFF
BUILD_CODIFICATION=OFF
BUILD_GROUP1=ON
BUILD_GROUP2=ON
BUILD_GROUP3=ON
@@ -94,20 +105,20 @@ Defaults:
USE_PREBUILT_IMAGE=OFF
BUILD_PREBUILT_IMAGE=OFF
DELETE_PREBUILT_IMAGE=OFF
DOCKER_IMAGE=qt-migration-builder:latest
DOCKER_IMAGE=legatus-qt6-prebuilt:latest
Examples:
./build.sh
./build.sh OFF
./build.sh --no-group3
./build.sh --no-group1 --group2 --no-group3
./build.sh --no-ctest
./build.sh --tests-only
./build.sh --build-prebuilt-image --docker-image qt-migration-builder:latest
./build.sh --use-prebuilt-image --docker-image qt-migration-builder:latest
./build.sh --build-prebuilt-image --use-prebuilt-image --docker-image qt-migration-builder:latest
./build.sh --delete-prebuilt-image --docker-image qt-migration-builder:latest
./build.sh --use-prebuilt-image --no-group1 --no-group3 --integration-test
./new_docker_build.sh
./new_docker_build.sh OFF
./new_docker_build.sh --no-group3
./new_docker_build.sh --no-group1 --group2 --no-group3
./new_docker_build.sh --no-ctest
./new_docker_build.sh --tests-only
./new_docker_build.sh --build-prebuilt-image --docker-image legatus-qt6-prebuilt:latest
./new_docker_build.sh --use-prebuilt-image --docker-image legatus-qt6-prebuilt:latest
./new_docker_build.sh --build-prebuilt-image --use-prebuilt-image --docker-image legatus-qt6-prebuilt:latest
./new_docker_build.sh --delete-prebuilt-image --docker-image legatus-qt6-prebuilt:latest
./new_docker_build.sh --use-prebuilt-image --no-group1 --no-group3 --integration-test
EOF
}
@@ -210,16 +221,21 @@ delete_prebuilt_image() {
APT_PACKAGES=(
build-essential
cmake
libboost-dev
libkf6texttemplate-dev
libqt6core6
libqt6gui6
libqt6widgets6
libqt6sql6
libqt6sql6-psql
libqt6test6
qt6-base-dev
qt6-l10n-tools
qt6-serialport-dev
qt6-svg-dev
qt6-tools-dev-tools
qt6-tools-dev
qt6-l10n-tools
qt6-websockets-dev
libgtest-dev
libpq-dev
postgresql-server-dev-all
@@ -241,16 +257,56 @@ GROUP1_TARGETS=(
ddsconnector
)
GROUP4_TARGETS=(
ddsGenericDataTests
ddsDataTopicTests
ddsFunctionTopicTests
)
GROUP2_TARGETS=(
migration_tests
)
# Validation builds the full service set, not only the targets that
# happened to be migrated first: every service library, the per-service
# test executables, and the shared libraries they need. Targets that are
# known blocked for reasons outside this migration are excluded below, so
# a green run means every migratable service actually compiles.
#
# This is the full validation set formerly built by docker_build.sh, which
# is superseded by this script.
#
# Excluded, each for a dependency this migration cannot supply:
# svcTargetManagement - needs SituationMap::ElevationMap /
# LineOfSightCalculation, the GDAL module
# whose CMakeLists states it was never
# migrated (main_handler.h:18 includes
# <ElevationMap/elevation_map.h>).
# svcExerciseManagement - needs QStateMachine / QState, moved from
# svcInfrastructureManagement QtCore to the Qt SCXML module in Qt6
# exmStateMachineTest (Qt6::StateMachine). No qt6-scxml-dev in
# the image and qstatemachine.h is absent.
# svcBuildings - its generated record_replay_plugin
# (bld_record_replay_plugin.cpp) calls
# fromQVariant() for the nested foreign
# ProjectDataManagement types
# VulnerabilityState /
# StateChangeOriginator, for which no
# specialization is generated (they are
# not Buildings' own types), leaving the
# generic fromQVariant template
# undefined -> link error.
# svcParticipantManagement - its record_replay custom header
# (RecordReplay/ppm_record_replay_plugin_custom.h)
# declares Q_INTERFACES(RecordReplay::
# RecordReplayPlugin) on a class that also
# inherits ParticipantHandling::BaseDataProvider
# (a second Q_OBJECT base the other record_replay
# custom plugins lack). Qt6's MOC reports
# "Undefined interface" for the Q_INTERFACES in
# that multi-Q_OBJECT-base context (RecordReplay::
# RecordReplayPlugin is a Q_GADGET, not a Q_OBJECT),
# so the plugin autogen step fails before link.
# (The nested-foreign-PDM fromQVariant link error
# that also applied is now resolved by the
# fromQVariant primary-template body added to
# svc_record_replay_plugin.cpp.grantlee.) The PPM
# client/common/server libs still build; only this
# exe is excluded.
GROUP3_TARGETS=(
NGLLogger
nglapplication
@@ -274,9 +330,174 @@ GROUP3_TARGETS=(
svcPdmServer
svcProjectDataManagement
pdmTests
# Migrated Lts services (full validation set merged from docker_build.sh)
svcAesClient
svcAesCommon
svcAesServer
svcAlmClient
svcAlmCommon
svcAlmServer
svcAreaEffectSimulation
svcAsmClient
svcAsmCommon
svcAsmServer
svcAssetManagement
svcAudioVideoManagement
svcAvmClient
svcAvmCommon
svcAvmServer
# svcBldServer builds in Group 4 (Proxies module)
svcBldClient
svcBldCommon
svcCentralEffectSimulation
svcCesClient
svcCesCommon
svcCesDDSCommon
svcCesGeoPairingImpl
svcCesServer
svcCodClient
svcCodCommon
svcCodServer
svcEemClient
svcEemCommon
svcEemServer
svcEffectShowClient
svcEscClient
svcEscCommon
svcEscServer
svcEsmClient
svcEsmCommon
svcEsmServer
svcEvlClient
svcEvlCommon
svcEvlServer
svcExerciseEventManagement
svcExmClient
svcExmCommon
svcExmServer
svcFileManagement
svcFilterManagement
svcFlmClient
svcFlmCommon
svcFlmServer
svcFltClient
svcFltCommon
svcFltServer
svcGimClient
svcGimCommon
svcGimServer
svcGmpServer
svcGuezIedManagement
svcIsmClient
svcIsmCommon
svcIsmServer
svcMapDummy
svcMdmClient
svcMdmCommon
svcMdmServer
svcMessaging
svcMeteoDataManagement
svcMrkClient
svcMrkCommon
svcMrkServer
svcMsgClient
svcMsgCommon
svcMsgServer
svcNomClient
svcNomCommon
svcNomServer
svcNotificationManagement
svcNmeClient
svcNmeCommon
svcNmeServer
svcNmeaManagement
svcObdClient
svcObdCommon
svcObdServer
svcOcBaseDownload
svcOcSituationClient
svcOcSituationServer
svcOperatorManagement
svcOpmClient
svcOpmCommon
svcOpmServer
svcOreDummy
svcOscClient
svcOscCommon
svcOscServer
svcOssClient
svcOssCommon
svcOssServer
svcPedDummy
svcPpmClient
svcPpmCommon
svcPpmServer
svcPreClient
svcPreCommon
svcPreServer
svcPwsCommon
svcRecCommon
svcRecordingManagement
svcRepClient
svcRepCommon
svcRepCommonExecutable
svcRepServer
svcReportAnalyzer
svcReportGenerator
svcScmClient
svcScmCommon
svcScmServer
svcScrDummy
svcServiceManagement
svcSituationReplay
svcSmoDummy
svcSomClient
svcSomCommon
svcSomServer
svcSoundManagement
svcSrmCommon
svcSrpClient
svcSrpCommon
svcSrpServer
svcTgtClient
svcTgtCommon
svcTgtServer
svcVideoOverlayManagement
svcWarehouseGuez
svcWhgClient
svcWhgCommon
svcWhgServer
# Per-service tests (merged from docker_build.sh)
asmTests
avmFtTests
avmTests
avmVcaTests
cesTests
eemTests
evlTests
exmDtUnitTest
exmTests
flmTests
gmpTests
ismTests
mrkTests
mvsTests
nomTests
opmTests
ppmTests
preTests
repTests
scmDtUnitTest
srpFtTests
tgtTests
whgTests
)
GROUP4_TARGETS=(
# DDS tests
ddsGenericDataTests
ddsDataTopicTests
ddsFunctionTopicTests
# Proxies module - Buildings
BuildingControlCommon
BuildingControlBsm
@@ -296,20 +517,24 @@ GROUP4_TARGETS=(
svcTabClient
svcTabServer
svcTrainingAreaBase
tabPluginInterface
TmsIfAdapterInterface
TmsIfAdapter
tabPluginUAS
TabUASTest3
TabUASTest4_2
TabUASTest6
# tabPluginInterface - defined in the CMake but no make rule is generated (base state)
# TmsIfAdapterInterface/TmsIfAdapter/tabPluginUAS/TabUASTest3/TabUASTest4_2/TabUASTest6
# - only built with BUILD_UAS_PROXY=ON (default OFF, not exposed by this script)
# tabPluginInterface
# TmsIfAdapterInterface
# TmsIfAdapter
# tabPluginUAS
# TabUASTest3
# TabUASTest4_2
# TabUASTest6
# Proxies module - TrainingAreaBaseOC
svcTabocCommon
svcTabocClient
svcTabocServer
svcTrainingAreaBaseOC
serialtest
# Proxies module - WirelessLocationSystem
# Proxies module - WirelessLocationSystem (requires -DBUILD_WLS=ON, e.g. via
# --cmake_build_flags "-DBUILD_WLS=ON")
svcWirelessLocationSystem
wlsJsonClientTest
# Proxies module - Other
@@ -361,6 +586,22 @@ RUN pip3 install --break-system-packages --quiet aqtinstall && \
--outputdir ${QT_BASE_DIR} && \
rm -rf /root/.cache/Aqt
# Install KDReports from source (apt package not available in Ubuntu 22.04/26.04)
# Try apt install first (will fail), then build from KDAB GitHub mirror
RUN apt-get install -y -qq libkdreports-dev 2>/dev/null || \
(git clone --depth 1 https://github.com/KDAB/KDReports.git /tmp/KDReports && \
cd /tmp/KDReports && \
mkdir build && cd build && \
cmake -DCMAKE_INSTALL_PREFIX=/usr/local -DCMAKE_PREFIX_PATH=${QT_BASE_DIR} .. && \
make -j$(nproc) && \
make install && \
cd / && rm -rf /tmp/KDReports)
# Legacy sources include <KDReports/...> but the Qt6 package installs headers flat
# under /usr/local/include/KDReports-qt6 - alias the directory so the prefixed
# includes resolve through the default /usr/local/include search path.
RUN ln -sfn KDReports-qt6 /usr/local/include/KDReports
CMD ["/bin/bash"]
EOF
@@ -402,25 +643,25 @@ source ".envrc"
set +a
BUILD_ALL="${BUILD_ALL:-ON}"
BUILD_SV="${BUILD_SV:-ON}"
BUILD_WS="${BUILD_WS:-ON}"
BUILD_AAR="${BUILD_AAR:-ON}"
BUILD_RTI="${BUILD_RTI:-ON}"
BUILD_GRANTLEE="${BUILD_GRANTLEE:-ON}"
BUILD_ARCGIS="${BUILD_ARCGIS:-OFF}"
BUILD_CODIFICATION="${BUILD_CODIFICATION:-OFF}"
BUILD_GROUP1="${BUILD_GROUP1:-ON}"
BUILD_GROUP2="${BUILD_GROUP2:-ON}"
BUILD_GROUP3="${BUILD_GROUP3:-ON}"
BUILD_GROUP4="${BUILD_GROUP4:-OFF}"
BUILD_GROUP5="${BUILD_GROUP5:-OFF}"
BUILD_VIEWER_ONLY="${BUILD_VIEWER_ONLY:-OFF}"
RUN_CTEST="${RUN_CTEST:-ON}"
TESTS_ONLY="${TESTS_ONLY:-OFF}"
INTEGRATION_TEST="${INTEGRATION_TEST:-OFF}"
USE_PREBUILT_IMAGE="${USE_PREBUILT_IMAGE:-OFF}"
BUILD_PREBUILT_IMAGE="${BUILD_PREBUILT_IMAGE:-OFF}"
DELETE_PREBUILT_IMAGE="${DELETE_PREBUILT_IMAGE:-OFF}"
if [[ "${USE_PREBUILT_IMAGE}" == "ON" ]]; then
DOCKER_IMAGE="${DOCKER_IMAGE:-qt-migration-builder:latest}"
else
DOCKER_IMAGE="${DOCKER_IMAGE:-ubuntu:26.04}"
fi
export QT_VERSION="${QT_VERSION:-6.10.2}"
export QT_ARCH="${QT_ARCH:-linux_gcc_64}"
# aqtinstall uses 'linux_gcc_64' but creates directory 'gcc_64'
@@ -468,6 +709,12 @@ while [[ $# -gt 0 ]]; do
--no-group5)
BUILD_GROUP5=OFF
;;
--build-viewer-only)
BUILD_VIEWER_ONLY=ON
BUILD_GROUP5=ON
BUILD_CODIFICATION=ON
RUN_CTEST=ON
;;
--grantlee)
BUILD_GRANTLEE=ON
;;
@@ -480,6 +727,12 @@ while [[ $# -gt 0 ]]; do
--no-arcgis)
BUILD_ARCGIS=OFF
;;
--codification)
BUILD_CODIFICATION=ON
;;
--no-codification)
BUILD_CODIFICATION=OFF
;;
--no-ctest)
RUN_CTEST=OFF
;;
@@ -506,6 +759,11 @@ while [[ $# -gt 0 ]]; do
[[ $# -gt 0 ]] || die "--docker-image requires an image name"
DOCKER_IMAGE="$1"
;;
--cmake_build_flags)
shift
[[ $# -gt 0 ]] || die "--cmake_build_flags requires a flag string"
CMAKE_BUILD_FLAGS="$1"
;;
-h|--help)
usage
exit 0
@@ -534,11 +792,13 @@ BUILD_ALL="$(normalize_on_off "$BUILD_ALL" "BUILD_ALL")"
BUILD_RTI="$(normalize_on_off "$BUILD_RTI" "BUILD_RTI")"
BUILD_GRANTLEE="$(normalize_on_off "$BUILD_GRANTLEE" "BUILD_GRANTLEE")"
BUILD_ARCGIS="$(normalize_on_off "$BUILD_ARCGIS" "BUILD_ARCGIS")"
BUILD_CODIFICATION="$(normalize_on_off "$BUILD_CODIFICATION" "BUILD_CODIFICATION")"
BUILD_GROUP1="$(normalize_on_off "$BUILD_GROUP1" "BUILD_GROUP1")"
BUILD_GROUP2="$(normalize_on_off "$BUILD_GROUP2" "BUILD_GROUP2")"
BUILD_GROUP3="$(normalize_on_off "$BUILD_GROUP3" "BUILD_GROUP3")"
BUILD_GROUP4="$(normalize_on_off "$BUILD_GROUP4" "BUILD_GROUP4")"
BUILD_GROUP5="$(normalize_on_off "$BUILD_GROUP5" "BUILD_GROUP5")"
BUILD_VIEWER_ONLY="$(normalize_on_off "$BUILD_VIEWER_ONLY" "BUILD_VIEWER_ONLY")"
RUN_CTEST="$(normalize_on_off "$RUN_CTEST" "RUN_CTEST")"
TESTS_ONLY="$(normalize_on_off "$TESTS_ONLY" "TESTS_ONLY")"
INTEGRATION_TEST="$(normalize_on_off "$INTEGRATION_TEST" "INTEGRATION_TEST")"
@@ -546,29 +806,69 @@ USE_PREBUILT_IMAGE="$(normalize_on_off "$USE_PREBUILT_IMAGE" "USE_PREBUILT_IMAGE
BUILD_PREBUILT_IMAGE="$(normalize_on_off "$BUILD_PREBUILT_IMAGE" "BUILD_PREBUILT_IMAGE")"
DELETE_PREBUILT_IMAGE="$(normalize_on_off "$DELETE_PREBUILT_IMAGE" "DELETE_PREBUILT_IMAGE")"
# Set DOCKER_IMAGE default after argument parsing
if [[ "${USE_PREBUILT_IMAGE}" == "ON" ]]; then
DOCKER_IMAGE="${DOCKER_IMAGE:-legatus-qt6-prebuilt:latest}"
else
DOCKER_IMAGE="${DOCKER_IMAGE:-ubuntu:26.04}"
fi
# Default for extra CMake flags (set via --cmake_build_flags or environment)
CMAKE_BUILD_FLAGS="${CMAKE_BUILD_FLAGS:-}"
# Export variables for Docker container
export BUILD_ALL BUILD_RTI BUILD_GRANTLEE BUILD_ARCGIS BUILD_GROUP1 BUILD_GROUP2 BUILD_GROUP3 BUILD_GROUP4 BUILD_GROUP5 RUN_CTEST TESTS_ONLY INTEGRATION_TEST USE_PREBUILT_IMAGE BUILD_PREBUILT_IMAGE DELETE_PREBUILT_IMAGE DOCKER_IMAGE
export BUILD_ALL BUILD_RTI BUILD_GRANTLEE BUILD_ARCGIS BUILD_CODIFICATION BUILD_GROUP1 BUILD_GROUP2 BUILD_GROUP3 BUILD_GROUP4 BUILD_GROUP5 BUILD_VIEWER_ONLY RUN_CTEST TESTS_ONLY INTEGRATION_TEST USE_PREBUILT_IMAGE BUILD_PREBUILT_IMAGE DELETE_PREBUILT_IMAGE DOCKER_IMAGE CMAKE_BUILD_FLAGS
# Group 5: Viewer module targets (conditional on BUILD_ARCGIS)
# Note: LtsFileDialog excluded from viewer-only builds due to Group 4 dependencies (svcExmCommon, svcOpmCommon, svcFlmCommon)
if [[ "${BUILD_ARCGIS}" == "ON" ]]; then
GROUP5_TARGETS=(
# Viewer module with ArcGIS
situationMapCommon
advancedSymbology
advancedSymbologyXML
LtsDataView
LtsFileDialog
LtsReportDisplay
)
if [[ "${BUILD_VIEWER_ONLY}" == "ON" ]]; then
# Viewer-only build: exclude LtsFileDialog (requires Group 4 services)
GROUP5_TARGETS=(
# Viewer module with ArcGIS (viewer-only subset)
situationMapCommon
advancedSymbology
advancedSymbologyXML
LtsDataView
LtsDataViewTest
LtsReportDisplay
)
else
GROUP5_TARGETS=(
# Viewer module with ArcGIS (full build)
situationMapCommon
advancedSymbology
advancedSymbologyXML
LtsDataView
LtsDataViewTest
LtsFileDialog
LtsFileDialogTest
LtsReportDisplay
)
fi
else
GROUP5_TARGETS=(
# Viewer module without ArcGIS (advancedSymbology requires ArcGIS)
situationMapCommon
advancedSymbologyXML
LtsDataView
LtsFileDialog
LtsReportDisplay
)
if [[ "${BUILD_VIEWER_ONLY}" == "ON" ]]; then
# Viewer-only build: exclude LtsFileDialog (requires Group 4 services)
GROUP5_TARGETS=(
# Viewer module without ArcGIS (viewer-only subset, advancedSymbology requires ArcGIS)
situationMapCommon
advancedSymbologyXML
LtsDataView
LtsDataViewTest
LtsReportDisplay
)
else
GROUP5_TARGETS=(
# Viewer module without ArcGIS (full build)
situationMapCommon
advancedSymbologyXML
LtsDataView
LtsDataViewTest
LtsFileDialog
LtsFileDialogTest
LtsReportDisplay
)
fi
fi
require_non_empty "${DOCKER_IMAGE}" "DOCKER_IMAGE"
@@ -598,7 +898,10 @@ fi
log_info "BUILD_RTI=${BUILD_RTI}"
log_group "Build Group 1 (ddscore ddsftccore ddsQoS ddsconnector ddsfts)" "$BUILD_GROUP1"
log_group "Build Group 2 (migration_tests)" "$BUILD_GROUP2"
log_group "Build Group 3 (application targets)" "$BUILD_GROUP3"
log_group "Build Group 3 (application + service targets)" "$BUILD_GROUP3"
log_group "Build Group 4 (DDS tests + Proxies)" "$BUILD_GROUP4"
log_group "Build Group 5 (Viewer)" "$BUILD_GROUP5"
log_info "BUILD_VIEWER_ONLY=${BUILD_VIEWER_ONLY}"
log_info "RUN_CTEST=${RUN_CTEST}"
log_info "TESTS_ONLY=${TESTS_ONLY}"
log_info "INTEGRATION_TEST=${INTEGRATION_TEST}"
@@ -617,7 +920,7 @@ if [[ "${BUILD_PREBUILT_IMAGE}" == "ON" ]]; then
log_info "Prebuilt image was built successfully."
log_info "Exiting because --use-prebuilt-image was not requested."
log_info "To use it, run:"
printf ' ./build.sh --use-prebuilt-image --docker-image %q\n' "${DOCKER_IMAGE}"
printf ' ./new_docker_build.sh --use-prebuilt-image --docker-image %q\n' "${DOCKER_IMAGE}"
exit 0
fi
@@ -644,6 +947,9 @@ set -euo pipefail
# Default variable assignments to prevent unbound variable errors
USE_PREBUILT_IMAGE="${USE_PREBUILT_IMAGE:-OFF}"
BUILD_ALL="${BUILD_ALL:-ON}"
BUILD_SV="${BUILD_SV:-ON}"
BUILD_WS="${BUILD_WS:-ON}"
BUILD_AAR="${BUILD_AAR:-ON}"
BUILD_RTI="${BUILD_RTI:-ON}"
BUILD_GRANTLEE="${BUILD_GRANTLEE:-ON}"
BUILD_ARCGIS="${BUILD_ARCGIS:-OFF}"
@@ -652,6 +958,7 @@ BUILD_GROUP2="${BUILD_GROUP2:-ON}"
BUILD_GROUP3="${BUILD_GROUP3:-ON}"
BUILD_GROUP4="${BUILD_GROUP4:-OFF}"
BUILD_GROUP5="${BUILD_GROUP5:-OFF}"
BUILD_VIEWER_ONLY="${BUILD_VIEWER_ONLY:-OFF}"
RUN_CTEST="${RUN_CTEST:-ON}"
TESTS_ONLY="${TESTS_ONLY:-OFF}"
INTEGRATION_TEST="${INTEGRATION_TEST:-OFF}"
@@ -730,12 +1037,19 @@ cd build4
cmake .. \
-DBUILD_TESTING=ON \
-DBUILD_ALL="${BUILD_ALL}" \
-DBUILD_SV="${BUILD_SV}" \
-DBUILD_WS="${BUILD_WS}" \
-DBUILD_AAR="${BUILD_AAR}" \
-DBUILD_RTI="${BUILD_RTI}" \
-DBUILD_VIEWER_ONLY="${BUILD_VIEWER_ONLY}" \
-DBUILD_GRANTLEE="${BUILD_GRANTLEE}" \
-DBUILD_ARCGIS="${BUILD_ARCGIS}" \
-DBUILD_CODIFICATION="${BUILD_CODIFICATION}" \
${CMAKE_BUILD_FLAGS:-} \
-DCMAKE_CXX_STANDARD=17 \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_PREFIX_PATH="${QT_BASE_DIR}/${QT_VERSION}/${QT_ARCH_DIR}${BUILD_ARCGIS:+;/workspace/qt-migration-legatus/qt6-after/qt6-migrated-legatus/external/ArcGIS/MapsSDK_Qt_300.0.0/ArcGIS_Maps_SDK_Qt_300.0.0/qt300.0.0/sdk}"
-DCMAKE_PREFIX_PATH="${QT_BASE_DIR}/${QT_VERSION}/${QT_ARCH_DIR}${BUILD_ARCGIS:+;/workspace/qt-migration-legatus/qt6-after/qt6-migrated-legatus/external/ArcGIS/MapsSDK_Qt_300.0.0/ArcGIS_Maps_SDK_Qt_300.0.0/qt300.0.0/sdk};/usr/local"
read -r -a GROUP1_ARRAY <<< "${GROUP1_TARGETS}"
read -r -a GROUP2_ARRAY <<< "${GROUP2_TARGETS}"
@@ -744,11 +1058,18 @@ read -r -a GROUP4_ARRAY <<< "${GROUP4_TARGETS}"
read -r -a GROUP5_ARRAY <<< "${GROUP5_TARGETS}"
if [[ "${TESTS_ONLY}" != "ON" ]]; then
if [[ "${BUILD_VIEWER_ONLY}" == "ON" ]]; then
log_info "BUILD_VIEWER_ONLY enabled - skipping Groups 1-4, building only Viewer module targets"
BUILD_GROUP1=OFF
BUILD_GROUP2=OFF
BUILD_GROUP3=OFF
BUILD_GROUP4=OFF
BUILD_GROUP5=ON
fi
run_build_group "Group 1" "${BUILD_GROUP1}" "group1_build.log" "${GROUP1_ARRAY[@]}"
run_build_group "Group 4 (DDS Tests)" "${BUILD_GROUP4}" "group4_build.log" "${GROUP4_ARRAY[@]}"
run_build_group "Group 4 (DDS tests + Proxies)" "${BUILD_GROUP4}" "group4_build.log" "${GROUP4_ARRAY[@]}"
run_build_group "Group 2" "${BUILD_GROUP2}" "group2_build.log" "${GROUP2_ARRAY[@]}"
run_build_group "Group 3" "${BUILD_GROUP3}" "group3_build.log" "${GROUP3_ARRAY[@]}"
run_build_group "Group 4" "${BUILD_GROUP4}" "group4_build.log" "${GROUP4_ARRAY[@]}"
run_build_group "Group 5 (Viewer)" "${BUILD_GROUP5}" "group5_build.log" "${GROUP5_ARRAY[@]}"
else
log_info "TESTS_ONLY enabled - skipping all build groups"
@@ -773,18 +1094,25 @@ docker run --rm \
-e MIGRATE_PIPELINE_ROOT \
-e LANG \
-e LC_ALL \
-e BUILD_ALL \
-e BUILD_SV \
-e BUILD_WS \
-e BUILD_AAR \
-e BUILD_RTI \
-e BUILD_GRANTLEE \
-e BUILD_ARCGIS \
-e BUILD_CODIFICATION \
-e BUILD_GROUP1 \
-e BUILD_GROUP2 \
-e BUILD_GROUP3 \
-e BUILD_GROUP4 \
-e BUILD_GROUP5 \
-e BUILD_VIEWER_ONLY \
-e RUN_CTEST \
-e TESTS_ONLY \
-e INTEGRATION_TEST \
-e USE_PREBUILT_IMAGE \
-e CMAKE_BUILD_FLAGS \
-e QT_VERSION \
-e QT_ARCH \
-e QT_ARCH_DIR \