Update sdv_packager (#6)

This commit is contained in:
tompzf
2026-03-27 14:12:49 +01:00
committed by GitHub
parent 234be8917f
commit aefefd52f7
717 changed files with 42252 additions and 11334 deletions

View File

@@ -1,30 +1,32 @@
#*******************************************************************************
# Copyright (c) 2025-2026 ZF Friedrichshafen AG
#
# This program and the accompanying materials are made available under the
# terms of the Apache License Version 2.0 which is available at
# https://www.apache.org/licenses/LICENSE-2.0
#
# SPDX-License-Identifier: Apache-2.0
#
# Contributors:
# Erik Verhoeven - initial API and implementation
#*******************************************************************************
# Define project
project(sdv_packager VERSION 1.0 LANGUAGES CXX)
# build test application
add_executable(sdv_packager
"main.cpp"
"../../sdv_services/core/installation_manifest.h"
"../../sdv_services/core/installation_manifest.cpp"
"../../sdv_services/core/installation_composer.h"
"../../sdv_services/core/installation_composer.cpp"
"../../sdv_services/core/toml_parser/parser_toml.h"
"../../sdv_services/core/toml_parser/parser_toml.cpp"
"../../sdv_services/core/toml_parser/lexer_toml.h"
"../../sdv_services/core/toml_parser/lexer_toml.cpp"
"../../sdv_services/core/toml_parser/lexer_toml_token.cpp"
"../../sdv_services/core/toml_parser/parser_node_toml.h"
"../../sdv_services/core/toml_parser/parser_node_toml.cpp"
"../../sdv_services/core/toml_parser/character_reader_utf_8.h"
"../../sdv_services/core/toml_parser/character_reader_utf_8.cpp"
"../../sdv_services/core/toml_parser/miscellaneous.h"
"../../sdv_services/core/toml_parser/miscellaneous.cpp"
"../../sdv_services/core/toml_parser/exception.h"
"environment.cpp"
"environment.h"
"packager.cpp"
"packager.h"
)
"core_control.cpp" "core_control.h")
# Compiler settings
add_compile_definitions(SDV_NO_CLASS_DEFINITION)
# Linker settings
if (WIN32)
target_link_libraries(sdv_packager ${CMAKE_THREAD_LIBS_INIT} Ws2_32 Winmm Rpcrt4.lib)
else()

View File

@@ -0,0 +1,27 @@
/********************************************************************************
* Copyright (c) 2025-2026 ZF Friedrichshafen AG
*
* This program and the accompanying materials are made available under the
* terms of the Apache License Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0
*
* SPDX-License-Identifier: Apache-2.0
*
* Contributors:
* Erik Verhoeven - initial API and implementation
********************************************************************************/
#include "core_control.h"
#include "../../sdv_services/core/app_settings.cpp"
#include "../../sdv_services/core/app_config.cpp"
#include "../../sdv_services/core/app_config_file.cpp"
#include "../../sdv_services/core/installation_manifest.cpp"
#include "../../sdv_services/core/installation_composer.cpp"
#include "../../sdv_services/core/toml_parser/parser_toml.cpp"
#include "../../sdv_services/core/toml_parser/lexer_toml.cpp"
#include "../../sdv_services/core/toml_parser/lexer_toml_token.cpp"
#include "../../sdv_services/core/toml_parser/parser_node_toml.cpp"
#include "../../sdv_services/core/toml_parser/character_reader_utf_8.cpp"
#include "../../sdv_services/core/toml_parser/miscellaneous.cpp"
#include "../../sdv_services/core/toml_parser/code_snippet.cpp"

View File

@@ -0,0 +1,133 @@
/********************************************************************************
* Copyright (c) 2025-2026 ZF Friedrichshafen AG
*
* This program and the accompanying materials are made available under the
* terms of the Apache License Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0
*
* SPDX-License-Identifier: Apache-2.0
*
* Contributors:
* Erik Verhoeven - initial API and implementation
********************************************************************************/
#ifndef CORE_CONTROL_H
#include <interfaces/core.h>
#include <interfaces/config.h>
#include <interfaces/app.h>
#include <support/component_impl.h>
// Core control service implementation functions targeting sdv_packager.
// Prevent including app_control.h, module_control.h, repository.h, logger.h
#define APP_CONTROL_H
#define MODULE_CONTROL_H
#define REPOSITORY_H
#define LOGGER_H
#define LOGGER_CONTROL_H
/**
* @brief CAppControl redefined
*/
class CAppControl : public sdv::IInterfaceAccess
{
public:
BEGIN_SDV_INTERFACE_MAP()
END_SDV_INTERFACE_MAP()
/**
* @brief Application control is only in configuration state.
* @return Returns the operation state of the application.
*/
virtual sdv::app::EAppOperationState GetOperationState() const
{
return sdv::app::EAppOperationState::configuring;
}
};
/**
* @brief Return the application control.
* @return Reference to the application control.
*/
inline CAppControl& GetAppControl()
{
static CAppControl app_control;
return app_control;
}
/**
* @brief CModuleControl redefined
*/
class CModuleControl : public sdv::IInterfaceAccess
{
public:
BEGIN_SDV_INTERFACE_MAP()
END_SDV_INTERFACE_MAP()
};
/**
* @brief Return the module control.
* @return Reference to the module control.
*/
inline CModuleControl& GetModuleControl()
{
static CModuleControl module_control;
return module_control;
}
class CAppConfigFile;
/**
* @brief CRepository redefined
*/
class CRepository : public sdv::IInterfaceAccess
{
public:
BEGIN_SDV_INTERFACE_MAP()
END_SDV_INTERFACE_MAP()
/**
* @brief Mock function, starting the components from the configuration.
* @return Returns the result, which is always failed.
*/
sdv::core::EConfigProcessResult StartFromConfig(const CAppConfigFile&, bool) { return sdv::core::EConfigProcessResult::failed; }
/**
* @brief Mock function, resetting the configuration baseline.
*/
void ResetConfigBaseline() {}
};
/**
* @brief Return the repository.
* @return Reference to the repository.
*/
inline CRepository& GetRepository()
{
static CRepository repository;
return repository;
}
/**
* @brief CLoggerControl redefined
*/
class CLoggerControl : public sdv::IInterfaceAccess
{
public:
BEGIN_SDV_INTERFACE_MAP()
END_SDV_INTERFACE_MAP()
};
/**
* @brief Return the logger control.
* @return Reference to the logger control.
*/
inline CLoggerControl& GetLoggerControl()
{
static CLoggerControl logger_control;
return logger_control;
}
#endif // !defined CORE_CONTROL_H

View File

@@ -1,10 +1,25 @@
/********************************************************************************
* Copyright (c) 2025-2026 ZF Friedrichshafen AG
*
* This program and the accompanying materials are made available under the
* terms of the Apache License Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0
*
* SPDX-License-Identifier: Apache-2.0
*
* Contributors:
* Erik Verhoeven - initial API and implementation
********************************************************************************/
#include "environment.h"
#include <string_view>
#include "../../global/cmdlnparser/cmdlnparser.cpp"
CSdvPackagerEnvironment::CSdvPackagerEnvironment() : m_cmdln(static_cast<uint32_t>(CCommandLine::EParseFlags::no_assignment_character)) {}
CSdvPackagerEnvironment::CSdvPackagerEnvironment() :
m_cmdln(static_cast<uint32_t>(CCommandLine::EParseFlags::no_assignment_character))
{}
CSdvPackagerEnvironment::CSdvPackagerEnvironment(const std::vector<std::string>& rvecArgs) :
m_cmdln(static_cast<uint32_t>(CCommandLine::EParseFlags::no_assignment_character))
@@ -25,7 +40,7 @@ bool CSdvPackagerEnvironment::Help() const
void CSdvPackagerEnvironment::ShowHelp() const
{
size_t nArgumentGroup = 0;
size_t nArgumentGroup = 0;
std::stringstream sstreamText;
// Show default information:
@@ -34,6 +49,63 @@ void CSdvPackagerEnvironment::ShowHelp() const
<< std::endl
<< std::endl;
constexpr std::string_view svCollectModules = "Provide one or more files to add to the installation on the command line. "
"Wildcards (**, *, ?) are supported, allowing multiple files and directories to be added automatically. Regular "
"expressions can also be used when prefixing the paths with \"regex:\". If a specific target location (relative to the "
"installation directory) is required, this can be added by \"=<directory>\".\n\n"
"Examples:\n"
" *.sdv - This will pack all SDV files from the input directory.\n"
" **/*.* - This will pack all files from all directories.\n"
" abc.sdv=mysub - This will pack \"abc.sdv\" into target \"mysub\".\n"
" regex:[^/].sdv - This will pack all SDV files from the input directory.\n\n"
"REMARKS: To create updatable components, use the '--set_version' option to set a version.\n";
constexpr std::string_view svCollectConfigFiles = "Provide one or more configuration files to merge into installed "
"configuration files. Wildcards (**, *, ?) are supported, allowing multiple files and directories to be added "
"automatically. Regular expressions can also be used when prefixing the paths with \"regex:\".\n\n"
"Examples:\n"
" *.toml - This will use all TOML files from the input directory.\n"
" **/*.* - This will use all files from all directories.\n"
" abc.toml - This will use \"abc.toml\".\n"
" regex:[^/].toml - This will use all TOML files from the input directory.\n";
constexpr std::string_view svConfiguration = "The configuration uses an optional string following the option to define what to "
"configure. The string is separated by the '+' separator followed by a list of components (separated by a comma ',' or a "
"'+') to include in the configuration. If a component should be instantiated using a specific name, this can be achieved "
"by adding the assignment character '='. For example to update a local configuration ('--local' option) use:\n\n"
" --config_fileabc.toml+my_component1,my_component2,my_component2=test21\n\n"
"and for a server configuration:\n\n"
" --user_config+my_component1+my_component2+my_component2=test21\n\n"
"If the configuration file is not existing, it will be created automatically.\n\n"
"To provide component specific parameters, there are two options available. The first option would be to assign parameters "
"directly to a component instance by providing the parameter at the command line. Hereby the instance name of the "
"component is the assigned name of the component (if any was assigned) or the default name if known or the component class "
"name if one instance is available. The value assignment to the parameter is optional. If the value is omitted, a default "
"value is assigned. Here are two examples:\n\n"
" --parametersmy_component1:param1=10,param2=20,groupA.param3=string value\n"
" --parameterstest21:param5=,groupB.param6=60\n\n"
"The second option would be to specify a parameter file containing the parameters for the object instances. The parameter "
"file is a TOML file containing all the parameters for several components. Each component is present as a table containing "
"the instance name of the component (the assigned name, if there is any, or the default name or the class name). "
"Underneath the table header the parameters follow in a \"parameter_name = value\" format. Parameter names must follow the "
"TOML rules for keys. If they contain special characters, they need to be quoted. Furthermore, the names must follow the "
"grouping format of the component, whereas any group is either specified as additional TOML table, or is preceded in the "
"key name (the parameter name) and separated by a dot. An example for the command line to assign a configuration to the "
"one or more component instances is as follow:\n"
" --param_fileMyComponentParams.toml\n"
"And an example file could be like this:\n\n"
" # Parameters for my_component1\n"
" [my_component1]\n"
" param1 = 10\n"
" param2 = 20\n"
" [my_component1.groupA] # Example with additional table for the GroupA parameters\n"
" param3 = \"string value\"\n\n"
" # Parameters for test21 (instance of my_component2)\n"
" [test21]\n"
" param5=50\n"
" groupB.param6=60 # Grouping using inline table for GroupB parameters\n\n"
"REMARKS: Utilities cannot be added to the configuration.\n\n"
"REMARKS: For server based user configuration, the configuration cannot contain devices, basic services and system "
"objects.\n";
// Show information based on the command.
switch (m_eOperatingMode)
{
@@ -42,124 +114,68 @@ void CSdvPackagerEnvironment::ShowHelp() const
sstreamText << "PACK command usage:" << std::endl
<< " sdv_package PACK <name> <files> [options]" << std::endl
<< std::endl;
sstreamText
<< "Provide one or more files to add to the installation onto the command line. Wildcards (**, *, ?) are "
"supported, allowing multiple files and directories to be added automatically. Instead regular expressions can also "
"be used by prefixing the paths by \"regex:\". If a specific target location (relative to the installation "
"directory) is required, this can be added by \"=<directory>\"."
<< std::endl
<< std::endl
<< "Examples:" << std::endl
<< " *.sdv - This will pack all SDV files from the input directory." << std::endl
<< " **/*.* - This will pack all files from all directories." << std::endl
<< " abc.sdv=mysub - This will pack \"abc.sdv\" into target \"mysub\"." << std::endl
<< " regex:[^/].sdv - This will pack all SDV files from the input directory." << std::endl
<< std::endl
<< "REMARKS: To create updatable components, use the '--set_version' option to set a version." << std::endl;
sstreamText << svCollectModules;
break;
case EOperatingMode::install:
nArgumentGroup = 2;
sstreamText << "INSTALL command usage:" << std::endl
<< " sdv_package INSTALL <package> [options]" << std::endl
<< std::endl;
sstreamText
<< "Install an installation package and update the configuration if applicable." << std::endl
<< std::endl
<< "The configuration uses an optional string following the option to define what to configure. The string is "
"separated "
"by the '+' separator followed by a list of components (separated by a comma ',' or a '+') to include in the "
"configuration. For example to update a local configuration ('--local' option) use:"
<< std::endl
<< " --config_fileabc.toml+my_component1,my_component2" << std::endl
<< "and a server configuration:" << std::endl
<< " --user_config+my_component1+my_component2" << std::endl
<< "If the configuration file is not existing, it will be created automatically." << std::endl
<< std::endl
<< "REMARKS: The server based installation can only take place when the system is offline. Use \"sdv_control\" "
"to install on a running server instance."
<< std::endl;
sstreamText << "INSTALL command usage:" << std::endl << " sdv_package INSTALL <package> [options]" << std::endl <<
std::endl;
sstreamText << "Install an installation package and update the configuration if applicable." << std::endl << std::endl <<
svConfiguration << std::endl << "REMARKS: The server based installation use sdv_packager can only take place when the "
"system is offline. Use \"sdv_control\" to install on a running server instance (limited installation possible) or "
"to shutdown and restart the server."<< std::endl;
break;
case EOperatingMode::direct_install:
nArgumentGroup = 3;
sstreamText << "DIRECT_INSTALL command usage:" << std::endl
<< " sdv_package DIRECT_INSTALL <name> <files> [options]" << std::endl
<< std::endl;
sstreamText
<< "Provide one or more files to add to the installation onto the command line. Wildcards (**, *, ?) are "
"supported, allowing multiple files and directories to be added automatically. Instead regular expressions can also "
"used by prefixing the paths by \"regex:\". If a specific target location (relative to the installation "
"directory) is required, this can be added by \"=<directory>\"."
<< std::endl
<< std::endl
<< "Examples:" << std::endl
<< " *.sdv - This will install all SDV files from the input directory." << std::endl
<< " **/*.* - This will install all files from the input directory and sub-directories." << std::endl
<< " abc.sdv=mysub - This will install \"abc.sdv\" into target \"mysub\"." << std::endl
<< " regex:[^/].sdv - This will install all SDV files from the input directory." << std::endl
<< std::endl
<< "The configuration uses an optional string following the option to define what to configure. The string is "
"separated "
"by the '+' separator followed by a list of components (separated by a comma ',' or a '+') to include in the "
"configuration. For example to update a local configuration ('--local' option) use:"
<< std::endl
<< " --config_fileabc.toml+my_component1,my_component2" << std::endl
<< "and a server configuration:" << std::endl
<< " --user_config+my_component1+my_component2" << std::endl
<< "If the configuration file is not existing, it will be created automatically." << std::endl
<< std::endl
<< "REMARKS: The server based installation can only take place when the system is offline. Use \"sdv_control\" to "
"install "
"on a running server instance."
<< std::endl
<< std::endl
<< "REMARKS: To create updatable components, use the '--set_version' option to set a version." << std::endl;
sstreamText << "DIRECT_INSTALL command usage:" << std::endl << " sdv_package DIRECT_INSTALL <name> <files> [options]" <<
std::endl << std::endl;
sstreamText << svCollectModules << std::endl << svConfiguration << std::endl << "REMARKS: The server based installation "
"can only take place when the system is offline. Use \"sdv_control\" to install on a running server instance (limited "
"installation possible) or to shutdown and restart the server." << std::endl;
break;
case EOperatingMode::configure:
nArgumentGroup = 4;
sstreamText << "CONFIGURE command usage:" << std::endl << " sdv_package CONFIGURE <config_files> [options]" <<
std::endl << std::endl;
sstreamText << svCollectConfigFiles << std::endl << "REMARKS: The server based configuration can only take place when the "
"system is offline. Use \"sdv_control\" to shutdown and restart the server." << std::endl;
break;
case EOperatingMode::uninstall:
nArgumentGroup = 4;
sstreamText << "UNINSTALL command usage:" << std::endl
<< " sdv_package UNINSTALL <name> <files> [options]" << std::endl
<< std::endl;
sstreamText
<< "Uninstall a previously installed package with the supplied name. Update the configuration files where "
"applicable."
<< std::endl
<< std::endl
<< "REMARKS: The server based unistallation can only take place when the system is offline. Use \"sdv_control\" to "
"install on a running server instance."
<< std::endl;
nArgumentGroup = 5;
sstreamText << "UNINSTALL command usage:" << std::endl << " sdv_package UNINSTALL <name> <files> [options]" <<
std::endl << std::endl;
sstreamText << "Uninstall a previously installed package with the supplied name. Update the configuration files where "
"applicable." << std::endl << std::endl << "REMARKS: The server based unistallation can only take place when the "
"system is offline. Use \"sdv_control\" to install on a running server instance (limited un-installation possible) or "
"to shutdown and restart the server." << std::endl;
break;
case EOperatingMode::verify:
nArgumentGroup = 5;
sstreamText << "VERIFY command usage:" << std::endl
<< " sdv_package VERIFY <package> [options]" << std::endl
<< std::endl;
nArgumentGroup = 6;
sstreamText << "VERIFY command usage:" << std::endl << " sdv_package VERIFY <package> [options]" << std::endl <<
std::endl;
sstreamText << "Verify the package for corruption and/or tampering." << std::endl;
break;
case EOperatingMode::show:
nArgumentGroup = 6;
sstreamText << "SHOW command usage:" << std::endl
<< " sdv_package SHOW [ALL|INFO|MODULES|COMPONENTS] <package> [options]" << std::endl
<< std::endl;
sstreamText << "Show package information. The following information can be selected (multiple selections are possible):"
<< std::endl
<< " ALL Show all information about the package content." << std::endl
<< " INFO Show package information." << std::endl
<< " MODULES Show the modules contained in the package." << std::endl
<< " COMPONENTS Show the components contained in the package." << std::endl;
nArgumentGroup = 7;
sstreamText << "SHOW command usage:" << std::endl << " sdv_package SHOW [ALL|INFO|MODULES|COMPONENTS] <package> "
"[options]" << std::endl << std::endl;
sstreamText << "Show package information. The following information can be selected (multiple selections are "
"possible):" << std::endl << " ALL Show all information about the package content." << std::endl <<
" INFO Show package information." << std::endl <<
" MODULES Show the modules contained in the package." << std::endl <<
" COMPONENTS Show the components contained in the package." << std::endl;
break;
default:
sstreamText << "Usage: sdv_package <command> [options]" << std::endl << std::endl;
sstreamText << "The following commands are available:" << std::endl
<< " PACK Create an installation package." << std::endl
<< " INSTALL Install an installation package." << std::endl
<< " DIRECT_INSTALL Install the components directly without creating an installation package."
<< std::endl
<< " UNINSTALL Uninstall a previously installed installation package." << std::endl
<< " VERIFY Verify the integrity of the installation package." << std::endl
<< " SHOW Show information about the installation package." << std::endl
<< std::endl
<< "For information about the options of each command, provide the '--help' option following the command."
<< std::endl;
sstreamText << "The following commands are available:" << std::endl <<
" PACK Create an installation package." << std::endl <<
" INSTALL Install an installation package." << std::endl <<
" DIRECT_INSTALL Install the components directly." << std::endl <<
" CONFIGURE Configure the target system." << std::endl <<
" UNINSTALL Uninstall a previously installed installation package." << std::endl <<
" VERIFY Verify the integrity of the installation package." << std::endl <<
" SHOW Show information about the installation package." << std::endl << std::endl <<
"For information about the options of each command, provide the '--help' option following the command." << std::endl;
break;
}
@@ -174,7 +190,7 @@ void CSdvPackagerEnvironment::ReportInfo() const
// Dependable on the verbose and the silent settings, select the output stream for information.
std::stringstream sstreamDummy;
std::ostream& rstreamVerbose = !m_bHelp && m_bVerbose ? std::cout : sstreamDummy;
std::ostream& rstreamNormal = m_bHelp || m_bSilent ? sstreamDummy : std::cout;
std::ostream& rstreamNormal = m_bHelp || m_bSilent ? sstreamDummy : std::cout;
// Assign additional paths and report information...
switch (m_eOperatingMode)
@@ -228,44 +244,44 @@ void CSdvPackagerEnvironment::ReportInfo() const
else
{
bool bInitial = true;
for (const auto& rssComponent : m_vecUserConfigComponents)
for (const auto& rprComponent : m_vecUserConfigComponents)
{
if (bInitial)
rstreamVerbose << "User config components: ";
else
rstreamVerbose << " ";
bInitial = false;
rstreamVerbose << rssComponent << std::endl;
rstreamVerbose << rprComponent.first << std::endl;
}
bInitial = true;
for (const auto& rssComponent : m_vecPlatformConfigComponents)
for (const auto& rprComponent : m_vecPlatformConfigComponents)
{
if (bInitial)
rstreamVerbose << "Platform config components: ";
else
rstreamVerbose << " ";
bInitial = false;
rstreamVerbose << rssComponent << std::endl;
rstreamVerbose << rprComponent.first << std::endl;
}
bInitial = true;
for (const auto& rssComponent : m_vecVehIfcConfigComponents)
for (const auto& rprComponent : m_vecVehIfcConfigComponents)
{
if (bInitial)
rstreamVerbose << "Vehicle interface config components: ";
else
rstreamVerbose << " ";
bInitial = false;
rstreamVerbose << rssComponent << std::endl;
rstreamVerbose << rprComponent.first << std::endl;
}
bInitial = true;
for (const auto& rssComponent : m_vecVehAbstrConfigComponents)
for (const auto& rprComponent : m_vecVehAbstrConfigComponents)
{
if (bInitial)
rstreamVerbose << "Vehicle abstraction config components: ";
else
rstreamVerbose << " ";
bInitial = false;
rstreamVerbose << rssComponent << std::endl;
rstreamVerbose << rprComponent.first << std::endl;
}
}
break;
@@ -311,44 +327,44 @@ void CSdvPackagerEnvironment::ReportInfo() const
else
{
bool bInitial = true;
for (const auto& rssComponent : m_vecUserConfigComponents)
for (const auto& rprComponent : m_vecUserConfigComponents)
{
if (bInitial)
rstreamVerbose << "User config components: ";
else
rstreamVerbose << " ";
bInitial = false;
rstreamVerbose << rssComponent << std::endl;
rstreamVerbose << rprComponent.first << std::endl;
}
bInitial = true;
for (const auto& rssComponent : m_vecPlatformConfigComponents)
for (const auto& rprComponent : m_vecPlatformConfigComponents)
{
if (bInitial)
rstreamVerbose << "Platform config components: ";
else
rstreamVerbose << " ";
bInitial = false;
rstreamVerbose << rssComponent << std::endl;
rstreamVerbose << rprComponent.first << std::endl;
}
bInitial = true;
for (const auto& rssComponent : m_vecVehIfcConfigComponents)
for (const auto& rprComponent : m_vecVehIfcConfigComponents)
{
if (bInitial)
rstreamVerbose << "Vehicle interface config components: ";
else
rstreamVerbose << " ";
bInitial = false;
rstreamVerbose << rssComponent << std::endl;
rstreamVerbose << rprComponent.first << std::endl;
}
bInitial = true;
for (const auto& rssComponent : m_vecVehAbstrConfigComponents)
for (const auto& rprComponent : m_vecVehAbstrConfigComponents)
{
if (bInitial)
rstreamVerbose << "Vehicle abstraction config components: ";
else
rstreamVerbose << " ";
bInitial = false;
rstreamVerbose << rssComponent << std::endl;
rstreamVerbose << rprComponent.first << std::endl;
}
}
break;
@@ -404,7 +420,6 @@ void CSdvPackagerEnvironment::ReportInfo() const
}
}
bool CSdvPackagerEnvironment::Silent() const
{
return m_bSilent;
@@ -440,6 +455,11 @@ const std::vector<CSdvPackagerEnvironment::SModule>& CSdvPackagerEnvironment::Mo
return m_vecModules;
}
const std::vector<std::string>& CSdvPackagerEnvironment::ConfigFileList() const
{
return m_vecConfigFiles;
}
const std::filesystem::path& CSdvPackagerEnvironment::PackagePath() const
{
return m_pathPackage;
@@ -490,7 +510,7 @@ bool CSdvPackagerEnvironment::Overwrite() const
return m_bOverwrite;
}
const std::filesystem::path& CSdvPackagerEnvironment::LocalConfigFile(std::vector<std::string>& rvecComponents) const
const std::filesystem::path& CSdvPackagerEnvironment::LocalConfigFile(CComponentVector& rvecComponents) const
{
rvecComponents = m_vecConfigLocalComponents;
return m_pathConfigLocal;
@@ -501,35 +521,38 @@ const std::vector<std::filesystem::path>& CSdvPackagerEnvironment::LocalConfigLo
return m_vecLocalConfigDirs;
}
bool CSdvPackagerEnvironment::ActivateLocalConfig() const
{
return m_bActivateLocalConfig;
}
bool CSdvPackagerEnvironment::InsertIntoUserConfig(std::vector<std::string>& rvecComponents) const
bool CSdvPackagerEnvironment::InsertIntoUserConfig(CComponentVector& rvecComponents) const
{
rvecComponents = m_vecUserConfigComponents;
return m_bInsertIntoUserConfig;
}
bool CSdvPackagerEnvironment::InsertIntoPlatformConfig(std::vector<std::string>& rvecComponents) const
bool CSdvPackagerEnvironment::InsertIntoPlatformConfig(CComponentVector& rvecComponents) const
{
rvecComponents = m_vecPlatformConfigComponents;
return m_bInsertIntoPlatformConfig;
}
bool CSdvPackagerEnvironment::InsertIntoVehicleInterfaceConfig(std::vector<std::string>& rvecComponents) const
bool CSdvPackagerEnvironment::InsertIntoVehicleInterfaceConfig(CComponentVector& rvecComponents) const
{
rvecComponents = m_vecVehIfcConfigComponents;
return m_bInsertIntoVehIfcConfig;
}
bool CSdvPackagerEnvironment::InsertIntoVehicleAbstractionConfig(std::vector<std::string>& rvecComponents) const
bool CSdvPackagerEnvironment::InsertIntoVehicleAbstractionConfig(CComponentVector& rvecComponents) const
{
rvecComponents = m_vecVehAbstrConfigComponents;
return m_bInsertIntoVehAbstrConfig;
}
const TParameterVector& CSdvPackagerEnvironment::ObjectParameters(const std::string& rssObjectInstance) const
{
static TParameterVector vecEmpty;
auto itParameters = m_mapParameters.find(rssObjectInstance);
if (itParameters == m_mapParameters.end()) return vecEmpty;
return itParameters->second;
}
const std::string& CSdvPackagerEnvironment::InstallName() const
{
return m_ssInstallName;
@@ -585,18 +608,26 @@ const std::string& CSdvPackagerEnvironment::ArgError() const
return m_ssArgError;
}
void CSdvPackagerEnvironment::SplitConfigString(const std::string& rssInput,
std::filesystem::path& rpath,
std::vector<std::string>& rvecComponents)
void CSdvPackagerEnvironment::SplitConfigString(const std::string& rssInput, std::filesystem::path& rpath,
std::vector<std::pair<std::string, std::string>>& rvecComponents)
{
size_t nPos = rssInput.find('+');
rpath = std::filesystem::u8path(rssInput.substr(0, nPos));
rpath = std::filesystem::u8path(rssInput.substr(0, nPos));
while (nPos != std::string::npos)
{
size_t nNextPos = rssInput.find_first_of("+,", nPos + 1);
std::string ss = rssInput.substr(nPos + 1, nNextPos);
std::string ss = rssInput.substr(nPos + 1, nNextPos - nPos - 1);
if (!ss.empty())
rvecComponents.push_back(ss);
{
// Check for an assignment.
size_t nAssignment = ss.find_first_of('=');
std::string ssClassName = ss.substr(0, nAssignment);
std::string ssName;
if (nAssignment != std::string::npos)
ssName = ss.substr(nAssignment + 1);
if (!ssClassName.empty())
rvecComponents.push_back(std::make_pair(std::move(ssClassName), std::move(ssName)));
}
nPos = nNextPos;
}
}
@@ -622,36 +653,41 @@ bool CSdvPackagerEnvironment::ProcessCommandLine(const std::vector<std::string>&
else if (iequals(rvecCommands[0], "PACK"))
{
m_eOperatingMode = EOperatingMode::pack;
nOptionGroup = 1;
nOptionGroup = 1;
}
else if (iequals(rvecCommands[0], "INSTALL"))
{
m_eOperatingMode = EOperatingMode::install;
nOptionGroup = 2;
nOptionGroup = 2;
}
else if (iequals(rvecCommands[0], "DIRECT_INSTALL"))
{
m_eOperatingMode = EOperatingMode::direct_install;
nOptionGroup = 3;
nOptionGroup = 3;
}
else if (iequals(rvecCommands[0], "CONFIGURE"))
{
m_eOperatingMode = EOperatingMode::configure;
nOptionGroup = 4;
}
else if (iequals(rvecCommands[0], "UNINSTALL"))
{
m_eOperatingMode = EOperatingMode::uninstall;
nOptionGroup = 4;
nOptionGroup = 5;
}
else if (iequals(rvecCommands[0], "VERIFY"))
{
m_eOperatingMode = EOperatingMode::verify;
nOptionGroup = 5;
nOptionGroup = 6;
}
else if (iequals(rvecCommands[0], "SHOW"))
{
m_eOperatingMode = EOperatingMode::show;
nOptionGroup = 6;
nOptionGroup = 7;
}
else
{
m_nError = CMDLN_ARG_ERR;
m_nError = CMDLN_ARG_ERR;
m_ssArgError = "Invalid command \"" + rvecCommands[0] + "\" supplied at the command line.";
return false;
}
@@ -664,7 +700,7 @@ bool CSdvPackagerEnvironment::ProcessCommandLine(const std::vector<std::string>&
// Check for silent and verbose flags.
if (m_bSilent && m_bVerbose)
{
m_nError = CMDLN_SILENT_VERBOSE;
m_nError = CMDLN_SILENT_VERBOSE;
m_ssArgError = CMDLN_SILENT_VERBOSE_MSG;
return false;
}
@@ -675,7 +711,7 @@ bool CSdvPackagerEnvironment::ProcessCommandLine(const std::vector<std::string>&
// Expecting the installation name
if (nCommandPos >= rvecCommands.size())
{
m_nError = CMDLN_INSTALL_NAME_MISSING;
m_nError = CMDLN_INSTALL_NAME_MISSING;
m_ssArgError = CMDLN_INSTALL_NAME_MISSING_MSG;
return false;
}
@@ -690,7 +726,7 @@ bool CSdvPackagerEnvironment::ProcessCommandLine(const std::vector<std::string>&
// Expecting at least one source
if (nCommandPos >= rvecCommands.size())
{
m_nError = CMDLN_SOURCE_FILE_MISSING;
m_nError = CMDLN_SOURCE_FILE_MISSING;
m_ssArgError = CMDLN_SOURCE_FILE_MISSING_MSG;
return false;
}
@@ -707,12 +743,27 @@ bool CSdvPackagerEnvironment::ProcessCommandLine(const std::vector<std::string>&
return true;
};
// Get the configuration files to merge into an existing configuration.
auto fnGetConfigFiles = [rvecCommands, &nCommandPos, this]() -> bool
{
// Expecting at least one source
if (nCommandPos >= rvecCommands.size())
{
m_nError = CMDLN_SOURCE_FILE_MISSING;
m_ssArgError = CMDLN_SOURCE_FILE_MISSING_MSG;
return false;
}
for (;nCommandPos < rvecCommands.size(); nCommandPos++)
m_vecConfigFiles.push_back(rvecCommands[nCommandPos]);
return true;
};
// Cneck for no more commands
auto fnCheckForNoMoreCommands = [rvecCommands, &nCommandPos, this]() -> bool
{
if (nCommandPos != rvecCommands.size())
{
m_nError = CMDLN_TOO_MANY_SOURCE_FILES;
m_nError = CMDLN_TOO_MANY_SOURCE_FILES;
m_ssArgError = CMDLN_TOO_MANY_SOURCE_FILES_MSG;
return false;
}
@@ -726,7 +777,7 @@ bool CSdvPackagerEnvironment::ProcessCommandLine(const std::vector<std::string>&
// Expecting exactly one installation package
if (nCommandPos >= rvecCommands.size())
{
m_nError = CMDLN_SOURCE_FILE_MISSING;
m_nError = CMDLN_SOURCE_FILE_MISSING;
m_ssArgError = CMDLN_SOURCE_FILE_MISSING_MSG;
return false;
}
@@ -737,7 +788,7 @@ bool CSdvPackagerEnvironment::ProcessCommandLine(const std::vector<std::string>&
if (m_pathPackage.extension() != ".sdv_package" || !std::filesystem::exists(m_pathPackage)
|| !std::filesystem::is_regular_file(m_pathPackage))
{
m_nError = CMDLN_SOURCE_FILE_ERROR;
m_nError = CMDLN_SOURCE_FILE_ERROR;
m_ssArgError = CMDLN_SOURCE_FILE_ERROR_MSG;
return false;
}
@@ -780,7 +831,7 @@ bool CSdvPackagerEnvironment::ProcessCommandLine(const std::vector<std::string>&
std::filesystem::create_directories(m_pathOutputLocation);
if (!std::filesystem::is_directory(m_pathOutputLocation))
{
m_nError = CMDLN_OUTPUT_LOCATION_ERROR;
m_nError = CMDLN_OUTPUT_LOCATION_ERROR;
m_ssArgError = std::string(CMDLN_OUTPUT_LOCATION_ERROR_MSG)
+ "\n Output location: " + m_pathOutputLocation.generic_u8string();
return false;
@@ -803,7 +854,7 @@ bool CSdvPackagerEnvironment::ProcessCommandLine(const std::vector<std::string>&
try
{
// If not locally, get the target from the environment variable
if (!m_bLocal)
if (!m_bLocal && m_pathTargetLocation.empty())
{
#ifdef _WIN32
const wchar_t* szInstallDir = _wgetenv(L"SDV_COMPONENT_INSTALL");
@@ -816,7 +867,7 @@ bool CSdvPackagerEnvironment::ProcessCommandLine(const std::vector<std::string>&
#endif
}
// Is the directory existing? If not, creat ethe directory
// Is the directory existing? If not, create the directory
if (!m_pathTargetLocation.empty() && !std::filesystem::is_directory(m_pathTargetLocation))
std::filesystem::create_directories(m_pathTargetLocation);
@@ -828,6 +879,9 @@ bool CSdvPackagerEnvironment::ProcessCommandLine(const std::vector<std::string>&
m_pathTargetLocation.generic_u8string();
return false;
}
// Make the directory absolute...
m_pathTargetLocation = std::filesystem::absolute(m_pathTargetLocation);
}
catch (const std::filesystem::filesystem_error&)
{
@@ -846,56 +900,86 @@ bool CSdvPackagerEnvironment::ProcessCommandLine(const std::vector<std::string>&
m_ssProductName = m_ssInstallName;
};
// Check configuration targets
auto fnConfigTargets = [this]() -> bool
{
if (m_bLocal && (m_bInsertIntoPlatformConfig || m_bInsertIntoUserConfig || m_bInsertIntoVehAbstrConfig ||
m_bInsertIntoVehIfcConfig))
{
m_nError = CMDLN_INCOMPATIBLE_ARGUMENTS;
m_ssArgError = CMDLN_INCOMPATIBLE_ARGUMENTS_MSG;
return false;
}
if (m_bLocal && m_pathConfigLocal.empty())
{
m_nError = CMDLN_MISSING_TARGET;
m_ssArgError = CMDLN_MISSING_TARGET_MSG;
return false;
}
if (!m_bLocal && !m_pathConfigLocal.empty())
{
m_nError = CMDLN_INCOMPATIBLE_ARGUMENTS;
m_ssArgError = CMDLN_INCOMPATIBLE_ARGUMENTS_MSG;
return false;
}
size_t nCntServerConfigs = 0;
if (m_bInsertIntoPlatformConfig) ++nCntServerConfigs;
if (m_bInsertIntoUserConfig) ++nCntServerConfigs;
if (m_bInsertIntoVehAbstrConfig) ++nCntServerConfigs;
if (m_bInsertIntoVehIfcConfig) ++nCntServerConfigs;
if (!m_bLocal && !nCntServerConfigs)
{
m_nError = CMDLN_MISSING_TARGET;
m_ssArgError = CMDLN_MISSING_TARGET_MSG;
return false;
}
if (!m_bLocal && nCntServerConfigs > 1)
{
m_nError = CMDLN_TOO_MANY_CONFIG_TARGETS;
m_ssArgError = CMDLN_TOO_MANY_CONFIG_TARGETS_MSG;
return false;
}
return true;
};
// Following the initial command, several additional information must be supplied.
switch (m_eOperatingMode)
{
case EOperatingMode::pack:
if (!fnGetInstallName())
return false;
if (!fnGetModules())
return false;
if (!fnCheckSourceLocation())
return false;
if (!fnCheckOutputLocation())
return false;
if (!fnGetInstallName()) return false;
if (!fnGetModules()) return false;
if (!fnCheckSourceLocation()) return false;
if (!fnCheckOutputLocation()) return false;
fnGetProduct();
if (m_pathOutputLocation.empty())
m_pathPackage = std::filesystem::u8path(m_ssInstallName + ".sdv_package");
else
m_pathPackage = m_pathOutputLocation / std::filesystem::u8path(m_ssInstallName + ".sdv_package");
break;
case EOperatingMode::install:
if (!fnGetPackage()) return false;
if (!fnCheckForNoMoreCommands()) return false;
if (!fnCheckTargetLocation()) return false;
break;
case EOperatingMode::direct_install:
if (!fnGetInstallName())
return false;
if (!fnGetModules())
return false;
if (!fnCheckSourceLocation())
return false;
if (!fnCheckTargetLocation())
return false;
if (!fnGetInstallName()) return false;
if (!fnGetModules()) return false;
if (!fnCheckSourceLocation()) return false;
if (!fnCheckTargetLocation()) return false;
fnGetProduct();
break;
case EOperatingMode::install:
if (!fnGetPackage())
return false;
if (!fnCheckForNoMoreCommands())
return false;
if (!fnCheckTargetLocation())
return false;
case EOperatingMode::configure:
if (!fnGetConfigFiles()) return false;
if (!fnConfigTargets()) return false;
break;
case EOperatingMode::uninstall:
if (!fnGetInstallName())
return false;
if (!fnCheckForNoMoreCommands())
return false;
if (!fnCheckTargetLocation())
return false;
if (!fnGetInstallName()) return false;
if (!fnCheckForNoMoreCommands()) return false;
if (!fnCheckTargetLocation()) return false;
break;
case EOperatingMode::verify:
if (!fnGetPackage())
return false;
if (!fnCheckForNoMoreCommands())
return false;
if (!fnGetPackage()) return false;
if (!fnCheckForNoMoreCommands()) return false;
break;
case EOperatingMode::show:
// Expecting one or more show commands followed by one installation package
@@ -903,7 +987,7 @@ bool CSdvPackagerEnvironment::ProcessCommandLine(const std::vector<std::string>&
{
if (!m_vecModules.empty())
{
m_nError = CMDLN_TOO_MANY_SOURCE_FILES;
m_nError = CMDLN_TOO_MANY_SOURCE_FILES;
m_ssArgError = CMDLN_TOO_MANY_SOURCE_FILES_MSG;
return false;
}
@@ -932,7 +1016,7 @@ bool CSdvPackagerEnvironment::ProcessCommandLine(const std::vector<std::string>&
// Check whether preceded by at least one command
if (!(m_uiShowFlags & 0x00ff))
{
m_nError = CMDLN_MISSING_SHOW_COMMAND;
m_nError = CMDLN_MISSING_SHOW_COMMAND;
m_ssArgError = CMDLN_MISSING_SHOW_COMMAND_MSG;
return false;
}
@@ -942,7 +1026,7 @@ bool CSdvPackagerEnvironment::ProcessCommandLine(const std::vector<std::string>&
}
if (m_pathPackage.empty())
{
m_nError = CMDLN_SOURCE_FILE_MISSING;
m_nError = CMDLN_SOURCE_FILE_MISSING;
m_ssArgError = CMDLN_SOURCE_FILE_MISSING_MSG;
return false;
}
@@ -955,7 +1039,7 @@ bool CSdvPackagerEnvironment::ProcessCommandLine(const std::vector<std::string>&
auto vecIncompatibleOptions = m_cmdln.IncompatibleArguments(nOptionGroup, false);
if (!vecIncompatibleOptions.empty() && m_eOperatingMode != EOperatingMode::none)
{
m_nError = CMDLN_INCOMPATIBLE_ARGUMENTS;
m_nError = CMDLN_INCOMPATIBLE_ARGUMENTS;
m_ssArgError = std::string("The option '" + vecIncompatibleOptions[0] + "' cannot be used with the " +
fnMakeUpper(rvecCommands[0]) + " command.");
return false;

View File

@@ -1,3 +1,16 @@
/********************************************************************************
* Copyright (c) 2025-2026 ZF Friedrichshafen AG
*
* This program and the accompanying materials are made available under the
* terms of the Apache License Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0
*
* SPDX-License-Identifier: Apache-2.0
*
* Contributors:
* Erik Verhoeven - initial API and implementation
********************************************************************************/
#ifndef ENVIRONMENT_H
#define ENVIRONMENT_H
@@ -5,9 +18,14 @@
#include <vector>
#include <filesystem>
#include <cstdint>
#include <fstream>
#include <functional>
#include "../../global/cmdlnparser/cmdlnparser.h"
#include "../error_msg.h"
#include <interfaces/config.h>
#include "../../sdv_services/core/app_config_file.h"
#include "../../sdv_services/core/toml_parser/parser_toml.h"
#include <support/toml.h>
/**
* @brief Compare whether two characters are identical when both are converted to lower case.
@@ -39,6 +57,10 @@ inline bool iequals(const std::string& rssLeft, const std::string& rssRight)
class CSdvPackagerEnvironment
{
public:
/**
* @brief Component vector containing the component class names and optionally the component instantiation names.
*/
using CComponentVector = std::vector<std::pair<std::string, std::string>>;
/**
* @brief Environment exception triggered on error during command line processing.
@@ -74,8 +96,9 @@ public:
{
none, ///< No mode selected
pack, ///< Packing operation (default)
direct_install, ///< Direct installation.
install, ///< Installation
direct_install, ///< Direct installation.
configure, ///< Configure
uninstall, ///< Remove an installed package.
verify, ///< Verify integrity
show, ///< Show content
@@ -197,6 +220,13 @@ public:
*/
const std::vector<SModule>& ModuleList() const;
/**
* @brief List of files to configure the system.
* @return Returns a reference to the variable containing the list of files. Could contain wildcards and regular expression
* strings.
*/
const std::vector<std::string>& ConfigFileList() const;
/**
* @brief Get the package path. Available for installation, uninstallation, verification and content showing commands.
* @return Returns a reference to the variable containing the package path.
@@ -264,7 +294,7 @@ public:
* are to be added (if a configuration file was supplied).
* @return Returns a reference to the variable containing the path to the configuration file or empty when no file was supplied.
*/
const std::filesystem::path& LocalConfigFile(std::vector<std::string>& rvecComponents) const;
const std::filesystem::path& LocalConfigFile(CComponentVector& rvecComponents) const;
/**
* @brief During package installation, the configuration file locations during local operation.
@@ -272,12 +302,6 @@ public:
*/
const std::vector<std::filesystem::path>& LocalConfigLocations() const;
/**
* @brief Activate the local configuration in the settings file of the system.
* @return Returns whether a configuration is requested.
*/
bool ActivateLocalConfig() const;
/**
* @brief Add the components to the user configuration.
* @remarks Only complex services will be inserted.
@@ -285,7 +309,7 @@ public:
* are to be added (if a the function returns true).
* @return Returns true if a configuration update is requested; false otherwise.
*/
bool InsertIntoUserConfig(std::vector<std::string>& rvecComponents) const;
bool InsertIntoUserConfig(CComponentVector& rvecComponents) const;
/**
* @brief Add the components to the platform configuration.
@@ -293,7 +317,7 @@ public:
* are to be added (if a the function returns true).
* @return Returns true if a configuration update is requested; false otherwise.
*/
bool InsertIntoPlatformConfig(std::vector<std::string>& rvecComponents) const;
bool InsertIntoPlatformConfig(CComponentVector& rvecComponents) const;
/**
* @brief Add the components to the vehicle interface configuration.
@@ -301,7 +325,7 @@ public:
* are to be added (if a the function returns true).
* @return Returns true if a configuration update is requested; false otherwise.
*/
bool InsertIntoVehicleInterfaceConfig(std::vector<std::string>& rvecComponents) const;
bool InsertIntoVehicleInterfaceConfig(CComponentVector& rvecComponents) const;
/**
* @brief Add the components to the vehicle abstraction configuration.
@@ -309,7 +333,15 @@ public:
* are to be added (if a the function returns true).
* @return Returns true if a configuration update is requested; false otherwise.
*/
bool InsertIntoVehicleAbstractionConfig(std::vector<std::string>& rvecComponents) const;
bool InsertIntoVehicleAbstractionConfig(CComponentVector& rvecComponents) const;
/**
* @brief Get the object parameter map for the provided object instance.
* @param[in] rssObjectInstance Reference to the object instance name to get the parameter map for.
* @return Returns a reference to the parameter vector. It is guaranteed that the parameters are unique. Can be empty when
* there are no parameters for the object instance.
*/
const TParameterVector& ObjectParameters(const std::string& rssObjectInstance) const;
/**
* @brief The installation name for the package creation.
@@ -385,9 +417,10 @@ private:
* missing, the string starts with '+'. The components might also be optional. If missing, the '+' is also not needed.
* @param[in] rssInput Reference to the configuration string.
* @param[out] rpath Reference to the path to be returned.
* @param[out] rvecComponents Reference to the vector containing the component names.
* @param[out] rvecComponents Reference to the vector containing the component class names and optionally component
* instantiation names (or empty when the name should be extracted from the component class).
*/
void SplitConfigString(const std::string& rssInput, std::filesystem::path& rpath, std::vector<std::string>& rvecComponents);
void SplitConfigString(const std::string& rssInput, std::filesystem::path& rpath, CComponentVector& rvecComponents);
/**
* @brief Process the parsed environment settings.
@@ -407,14 +440,17 @@ private:
bool m_bUpdate = false; ///< Update an existing installation.
bool m_bOverwrite = false; ///< Overwrite an existing installation.
EOperatingMode m_eOperatingMode = EOperatingMode::none; ///< Operating mode of the packager utility.
std::vector<SModule> m_vecModules; ///< List of modules (module search string).
std::vector<SModule> m_vecModules; ///< List of modules (module search strings).
std::vector<std::string> m_vecConfigFiles; ///< List of configuration files (file search strings).
std::filesystem::path m_pathSourceLocation; ///< Path to the input location.
std::filesystem::path m_pathOutputLocation; ///< Path to the output location.
std::filesystem::path m_pathTargetLocation; ///< Path to the target location (at installation).
std::filesystem::path m_pathRootLocation; ///< Target root location (includes the instance for server location).
std::filesystem::path m_pathInstallLocation; ///< Target install location (root location with installation name).
std::filesystem::path m_pathPackage; ///< Path to the package during installation, uninstallation, integrity checking and
///< content showing.
std::filesystem::path m_pathRootLocation; ///< Target root location (includes the instance for server
///< location).
std::filesystem::path m_pathInstallLocation; ///< Target install location (root location with installation
///< name).
std::filesystem::path m_pathPackage; ///< Path to the package during installation, uninstallation,
///< integrity checking andcontent showing.
uint32_t m_uiInstanceID = 1000u; ///< Instance number (optional).
std::string m_ssInstallName; ///< Installation name.
std::string m_ssProductName; ///< Product name (default is package name).
@@ -424,17 +460,21 @@ private:
std::string m_ssCopyrights; ///< Copyright
std::string m_ssPackageVersion; ///< Package version string
std::filesystem::path m_pathConfigLocal; ///< Configuration file path (local only).
std::vector<std::string> m_vecConfigLocalComponents; ///< List of components to add to the configuration file.
bool m_bActivateLocalConfig = false; ///< Activate the local config in the settings file.
CComponentVector m_vecConfigLocalComponents; ///< List of components to add to the configuration file.
std::vector<std::filesystem::path> m_vecLocalConfigDirs; ///< List of directories to scan for the component to uninstall.
std::vector<std::string> m_vecUserConfigComponents; ///< User configuration (server only).
std::vector<std::string> m_vecPlatformConfigComponents; ///< Platform configuration (server only).
std::vector<std::string> m_vecVehIfcConfigComponents; ///< Vehicle interface configuration (server only).
std::vector<std::string> m_vecVehAbstrConfigComponents; ///< Vehicle abstraction configuration (server only).
bool m_bInsertIntoUserConfig = false; ///< When set, insert the components into the user config (server only).
bool m_bInsertIntoPlatformConfig = false;///< When set, insert the components into the platform config (server only).
bool m_bInsertIntoVehIfcConfig = false; ///< When set, insert the components into the vehicle interface config (server only).
bool m_bInsertIntoVehAbstrConfig = false;///< When set, insert the components into the vehicle abstraction config (server only).
CComponentVector m_vecUserConfigComponents; ///< User configuration (server only).
CComponentVector m_vecPlatformConfigComponents; ///< Platform configuration (server only).
CComponentVector m_vecVehIfcConfigComponents; ///< Vehicle interface configuration (server only).
CComponentVector m_vecVehAbstrConfigComponents; ///< Vehicle abstraction configuration (server only).
std::map<std::string, TParameterVector> m_mapParameters; ///< Map with parameters associated to their instances.
bool m_bInsertIntoUserConfig = false; ///< When set, insert the components into the user config
///< (server only).
bool m_bInsertIntoPlatformConfig = false;///< When set, insert the components into the platform config
///< (server only).
bool m_bInsertIntoVehIfcConfig = false; ///< When set, insert the components into the vehicle interface
///< config (server only).
bool m_bInsertIntoVehAbstrConfig = false;///< When set, insert the components into the vehicle
///< abstraction config (server only).
uint32_t m_uiShowFlags = 0; ///< Show package information bitmask.
int m_nError = NO_ERROR; ///< Error code after processing the command line.
std::string m_ssArgError; ///< Error text after processing the command line.
@@ -451,18 +491,20 @@ CSdvPackagerEnvironment::CSdvPackagerEnvironment(size_t nArgs, const TCharType**
{
std::vector<std::string> vecCommands;
// COMMANDS (selective argument groups 1-6):
// PACK Pack modules and files into an installation package
// COMMANDS (selective argument groups 1-7):
// PACK Pack modules and files into an installation package.
// Usage: sdv_packager PACK <package name> <files> <options>
// INSTALL Install a package into the system
// INSTALL Install a package into the system.
// Usage: sdv_packager INSTALL <package path> <options>
// DIRECT_INSTALL Directly install modules and files into the system (without the creation of a package)
// DIRECT_INSTALL Directly install modules and files into the system (without the creation of a package).
// Usage: sdv_packager DIRECT_INSTALL <package name> <files> <options>
// UNINSTALL Remove an installation from the system
// CONFIGURE Install a configuration into the system.
// Usage: sdv_packager CONFIGURE <config files> <options>
// UNINSTALL Remove an installation from the system.
// Usage: sdv_packager UNINSTALL <package name> <options>
// VERIFY Verify the consistency and the integrity of an installation package.
// Usage: sdv_packager VERIFY <package path> <options>
// SHOW Show package information
// SHOW Show package information.
// Usage: sdv_packager SHOW <ALL|INFO|MODULES|COMPONENTS> <package path> <options>
m_cmdln.DefineDefaultArgument(vecCommands, "COMMAND <...> <options>");
@@ -471,7 +513,7 @@ CSdvPackagerEnvironment::CSdvPackagerEnvironment(size_t nArgs, const TCharType**
// -s, --silent Silent mode
// -v, --verbose Verbose mode
// --version Show version
auto& rArgHelpDef = m_cmdln.DefineOption("?", m_bHelp, "Show help", true, 0, 1, 2, 3, 4, 5, 6);
auto& rArgHelpDef = m_cmdln.DefineOption("?", m_bHelp, "Show help", true, 0, 1, 2, 3, 4, 5, 6, 7);
rArgHelpDef.AddSubOptionName("help");
auto& rArgSilentDef = m_cmdln.DefineOption("s", m_bSilent, "Do not show any information on STDOUT. Not compatible with "
"'--verbose'.");
@@ -481,13 +523,13 @@ CSdvPackagerEnvironment::CSdvPackagerEnvironment(size_t nArgs, const TCharType**
rArgVerboseDef.AddSubOptionName("verbose");
m_cmdln.DefineSubOption("version", m_bVersion, "Show version information.");
// ARGUMENT SELECTION GROUP #2 & #3 & #4: General options during installation/uninstallation
// ARGUMENT SELECTION GROUP #2 & #3, #4 & #5: General options during installation/uninstallation
// -L, --local Local installation
// --instance<id> Instance ID to use for installation
auto& rLocalArgDef = m_cmdln.DefineOption("L", m_bLocal, "Target the local system.", true, 2, 3, 4);
auto& rLocalArgDef = m_cmdln.DefineOption("L", m_bLocal, "Target the local system.", true, 2, 3, 4, 5);
rLocalArgDef.AddSubOptionName("local");
auto& rInstance = m_cmdln.DefineSubOption("instance", m_uiInstanceID, "The instance ID of the SDV server instance when not "
"targeting the local system (default ID is 1000).", true, 2, 3, 4);
"targeting the local system (default ID is 1000).", true, 2, 3, 4, 5);
// ARGUMENT SELECTION GROUP #1 - Packing:
// -O<path> Optional destination location
@@ -519,14 +561,16 @@ CSdvPackagerEnvironment::CSdvPackagerEnvironment(size_t nArgs, const TCharType**
m_cmdln.DefineSubOption("set_copyright", m_ssCopyrights, "Set copyright information.", true, 1, 3);
m_cmdln.DefineSubOption("set_version", m_ssPackageVersion, "Set package version (needed to allow updates).", true, 1, 3);
// ARGUMENT SELECTION GROUP #2 & #3 & #4- Installation from package and direct installation:
// ARGUMENT SELECTION GROUP #2 & #3 & #5- Installation from package and direct installation:
// -T<path> Optional target location
// -P, --update Use update if a previous package with older version has been found
// -W, --overwrite Use overwrite if a previous package has been found
// --verify_signed Verify whether the package is signed and if not return an error (not implemented)
m_cmdln.DefineGroup("Installation / uninstallation");
auto& rTargetLoc = m_cmdln.DefineOption("T", m_pathTargetLocation, "The target location for package installation. Required "
"when targeting a local system ('--local' option).", true, 2, 3, 4);
auto& rTargetLoc = m_cmdln.DefineOption("T", m_pathTargetLocation, "The target location for package installation. For "
"server based installations, this is the root location containing the instance ID sub-directories with the "
"installations. For local installations, this is the real target directory for installing the content.",
true, 2, 3, 5);
auto& rArgDefUpdate = m_cmdln.DefineOption("P", m_bUpdate, "Update the installation if an older version is existing.",
true, 2, 3);
rArgDefUpdate.AddSubOptionName("update");
@@ -535,23 +579,20 @@ CSdvPackagerEnvironment::CSdvPackagerEnvironment(size_t nArgs, const TCharType**
rArgDefOverwrite.AddSubOptionName("overwrite");
// Configuration
// ARGUMENT SELECTION GROUP #2 & #3 & #4 - Update from package and directly and uninstallation:
// ARGUMENT SELECTION GROUP #2 & #3, #4 & #5 - Update from package and directly and uninstallation:
// --config_dir<paths...> Local instance only; directory to configuration files to update.
m_cmdln.DefineGroup("Configuration update");
auto& rConfigDir = m_cmdln.DefineSubOption("config_dir", m_vecLocalConfigDirs, "One or more configuration directories "
"to scan for components being updated. Use with local systems only ('--local' option).", true, 2, 3, 4);
"to scan for components being updated. Use with local systems only ('--local' option).", true, 2, 3, 4, 5);
// --config_file<path>[=<x,>[all,-component,-...]|[+component,+....]] Local instance only; configuration file.
// --activate_config Local instance only; select config into settings.
// --user_config[=<x,>[all,-component,-...]|[+component,+....]] Server instance only; user configuration.
// --platform_config[=<x,>[all,-component,-...]|[+component,+....]] Server instance only; platform config.
// --vehifc_config[=<x,>[all,-component,-...]|[+component,+....]] Server instance only; vehicle interface config.
// --vehabstr_config[=<x,>[all,-component,-...]|[+component,+....]] Server instance only; vehicle abstraction config.
// --config_file<path>[+component[=name]+....]] Local instance only; configuration file.
// --user_config[+component[=name]+....]] Server instance only; user configuration.
// --platform_config[+component[=name]+....]] Server instance only; platform config.
// --interface_config[+component[=name]+....]] Server instance only; vehicle interface config.
// --abstract_config[+component[=name]+....]] Server instance only; vehicle abstraction config.
std::string ssConfigFileLocal;
auto& rConfigFile = m_cmdln.DefineSubOption("config_file", ssConfigFileLocal, "Update a user configuration file. For "
"usage, see explanatory text above. Use with local systems only ('--local' option).", true, 2, 3);
auto& rActivateConfig = m_cmdln.DefineSubOption("activate_config", m_bActivateLocalConfig, "Update the settings file to "
"automatically activate the provided configuration file. Use with local system only ('--local' option).", true, 2, 3);
std::string ssUserConfigServer, ssPlatformConfigServer, ssInterfaceConfigServer, ssAbstrConfigServer;
auto& rPlatformConfig = m_cmdln.DefineSubOption("platform_config", ssPlatformConfigServer, "Update the platform "
"configuration defining which components of this installation are used to interact with the hardware platform. For "
@@ -565,18 +606,41 @@ CSdvPackagerEnvironment::CSdvPackagerEnvironment(size_t nArgs, const TCharType**
auto& rUserConfig = m_cmdln.DefineSubOption("user_config", ssUserConfigServer, "Update the user configuration defining "
"which components of this installation need to be instantiated automatically. User configuration can only contain "
"complex services. For usage, see explanatory text above. Use on server only.", true, 2, 3);
m_cmdln.DefineSubOption("config_file", m_pathConfigLocal, "Update a user configuration file. usage, see explanatory text "
"above. Use with local systems only ('--local' option).", true, 4);
m_cmdln.DefineSubOption("platform_config", m_bInsertIntoPlatformConfig, "Update the platform "
"configuration defining which components of this installation are used to interact with the hardware platform. Use "
"on server only.", true, 4);
m_cmdln.DefineSubOption("interface_config", m_bInsertIntoVehIfcConfig, "Update the vehicle "
"interface configuration defining which components of this installation represent the vehicle interface. Use on server "
"only.", true, 4);
m_cmdln.DefineSubOption("abstract_config", m_bInsertIntoVehAbstrConfig, "Update the vehicle abstraction "
"configuration defining which components of this installation represent an abstracted vehicle to the application "
"components. Use on server only.", true, 4);
m_cmdln.DefineSubOption("user_config", m_bInsertIntoUserConfig, "Update the user configuration defining "
"which components of this installation need to be instantiated automatically. User configuration can only contain "
"complex services. Use on server only.", true, 4);
// ARGUMENT SELECTION GROUP #5 - Package verififcation
// --parameters<component_name>:<param>=<value>[,<param>=<value>...] Parameters for an added component.
// --param_file<TOML_file> Parameters from a file for an added component.
std::vector<std::string> vecParametersRaw;
m_cmdln.DefineSubOption("parameters", vecParametersRaw, "Parameters for a component instance. For usage, see explanatory "
"text above.", true, 2, 3);
std::vector<std::filesystem::path> vecParamFiles;
m_cmdln.DefineSubOption("param_file", vecParamFiles, "Parameters for one or more component instances located in a "
"parameter TOML file. For usage, see explanatory text above.", true, 2, 3);
// ARGUMENT SELECTION GROUP #6 - Package verififcation
m_cmdln.DefineGroup("Verification options");
// --verify_signed Verify whether the package is signed and if not return an error (not implemented)
// ARGUMENT SELECTION GROUP #6 - Show package information
// ARGUMENT SELECTION GROUP #7 - Show package information
// --show_simple
// --xml (not implemented)
// --json (not implemented)
m_cmdln.DefineGroup("Show options");
bool bShowSimple = false;
m_cmdln.DefineSubOption("show_simple", bShowSimple, "Show package information in simple format.", true, 6);
m_cmdln.DefineSubOption("show_simple", bShowSimple, "Show package information in simple format.", true, 7);
m_cmdln.Parse(static_cast<size_t>(nArgs), rgszArgs);
@@ -589,7 +653,7 @@ CSdvPackagerEnvironment::CSdvPackagerEnvironment(size_t nArgs, const TCharType**
else // TODO: if not XML and JSON, add console
m_uiShowFlags |= static_cast<uint32_t>(EShowMask::console);
// Split the configuration strings
// Split the configuration strings, unless in CONFIGURE mode
if (!ssConfigFileLocal.empty())
SplitConfigString(ssConfigFileLocal, m_pathConfigLocal, m_vecConfigLocalComponents);
if (rUserConfig.OptionAvailableOnCommandLine())
@@ -655,8 +719,8 @@ CSdvPackagerEnvironment::CSdvPackagerEnvironment(size_t nArgs, const TCharType**
rInterfaceConfig.OptionAvailableOnCommandLine() || rAbstrConfig.OptionAvailableOnCommandLine())
{
m_nError = CMDLN_INCOMPATIBLE_ARGUMENTS;
m_ssArgError = "The configuration options '--user_config', '--platform_config', '--vehifc_config' and "
"'--vehabstr_config' cannot be used with with local installations ('--local' option).";
m_ssArgError = "The configuration options '--user_config', '--platform_config', '--interface_config' and "
"'--abstract_config' cannot be used with with local installations ('--local' option).";
return;
}
if (!rTargetLoc.OptionAvailableOnCommandLine())
@@ -666,13 +730,6 @@ CSdvPackagerEnvironment::CSdvPackagerEnvironment(size_t nArgs, const TCharType**
"option).";
return;
}
if (rActivateConfig.OptionAvailableOnCommandLine() && !rConfigFile.OptionAvailableOnCommandLine())
{
m_nError = CMDLN_INCOMPATIBLE_ARGUMENTS;
m_ssArgError = "Activating a configuration file using the option '--activate_config' can only be used with when a "
"configuration file is provided using the option '--config_file'.";
return;
}
} else
{
if (rConfigFile.OptionAvailableOnCommandLine())
@@ -682,13 +739,6 @@ CSdvPackagerEnvironment::CSdvPackagerEnvironment(size_t nArgs, const TCharType**
"('--local' option).";
return;
}
if (rActivateConfig.OptionAvailableOnCommandLine())
{
m_nError = CMDLN_INCOMPATIBLE_ARGUMENTS;
m_ssArgError = "The configuration file option '--activate_config' can only be used with with local "
"installations ('--local' option).";
return;
}
if (rConfigDir.OptionAvailableOnCommandLine())
{
m_nError = CMDLN_INCOMPATIBLE_ARGUMENTS;
@@ -696,10 +746,159 @@ CSdvPackagerEnvironment::CSdvPackagerEnvironment(size_t nArgs, const TCharType**
"('--local' option).";
return;
}
if (rTargetLoc.OptionAvailableOnCommandLine())
}
// Add a parameter to the current instance. If the parameter is already present, the parameter will not be overwritten.
auto itInstance = m_mapParameters.end();
auto fnAddParameter = [&](const std::string& rssObjectInstance, const std::string& rssKey,
const sdv::any_t& rany) -> bool
{
if (itInstance == m_mapParameters.end() || itInstance->first != rssObjectInstance)
{
// Find or create the instance
itInstance = m_mapParameters.find(rssObjectInstance);
if (itInstance == m_mapParameters.end())
{
auto prInsert = m_mapParameters.emplace(rssObjectInstance, std::vector<std::pair<std::string, sdv::any_t>>());
if (!prInsert.second || prInsert.first == m_mapParameters.end())
return false;
itInstance = prInsert.first;
}
}
if (itInstance == m_mapParameters.end()) return false;
// Check for the existence
if (std::find_if(itInstance->second.begin(), itInstance->second.end(), [&](const auto& prParameter)
{ return prParameter.first == rssKey; }) != itInstance->second.end()) return true; // Not an error...
// Add the parameter
itInstance->second.push_back(std::make_pair(rssKey, rany));
return true;
};
// Parameters are only valid when a configuration is supplied.
if (!vecParametersRaw.empty() || !vecParamFiles.empty())
{
// Parameters can only be added when a configuration is created.
if (!m_bInsertIntoUserConfig && !m_bInsertIntoPlatformConfig && !m_bInsertIntoVehIfcConfig
&& !m_bInsertIntoVehAbstrConfig && ssConfigFileLocal.empty())
{
m_nError = CMDLN_INCOMPATIBLE_ARGUMENTS;
m_ssArgError = "The target location option '-T' can only be used with with local installations ('--local' option).";
m_ssArgError = CMDLN_INCOMPATIBLE_ARGUMENTS_MSG;
return;
}
}
// Process command line parameters
// The list of raw parameters are started with a component instance, followed by parameters.
std::string ssInstance;
for (const std::string& rssParameterRaw : vecParametersRaw)
{
size_t nInstanceSep = rssParameterRaw.find_first_of(':');
if (nInstanceSep == std::string::npos)
nInstanceSep = 0;
else
{
ssInstance = rssParameterRaw.substr(0, nInstanceSep);
++nInstanceSep;
}
size_t nAssignment = rssParameterRaw.find_first_of('=', nInstanceSep);
if (nAssignment == nInstanceSep || nAssignment == std::string::npos)
{
m_nError = CMDLN_INVALID_PARAM_STRING;
m_ssArgError = CMDLN_INVALID_PARAM_STRING_MSG;
return;
}
std::string ssParamName = rssParameterRaw.substr(nInstanceSep, nAssignment - nInstanceSep);
std::string ssParamValue = rssParameterRaw.substr(nAssignment + 1);
if (ssParamName.empty()) // An empty name is not allowed. An empty value is allowed.
{
m_nError = CMDLN_INVALID_PARAM_STRING;
m_ssArgError = CMDLN_INVALID_PARAM_STRING_MSG;
return;
}
// Add the parameter to the instance in the map.
sdv::any_t anyValue;
if (!ssParamValue.empty()) anyValue = ssParamValue;
if (!fnAddParameter(ssInstance, ssParamName, anyValue))
{
m_nError = CMDLN_INVALID_PARAM_STRING;
m_ssArgError = CMDLN_INVALID_PARAM_STRING_MSG;
return;
}
}
// Process parameter files
for (std::filesystem::path& rpathParamFile : vecParamFiles)
{
try
{
// Load the file
std::ifstream fstream(rpathParamFile);
if (!fstream.is_open())
{
m_nError = CMDLN_INVALID_PARAM_FILE;
m_ssArgError = CMDLN_INVALID_PARAM_FILE_MSG;
return;
}
std::string ssContent((std::istreambuf_iterator<char>(fstream)), std::istreambuf_iterator<char>());
fstream.close();
// Split the parameter path from the object instance.
auto fnSplit = [](const std::string& rssNodePath) -> std::pair<std::string, std::string>
{
size_t nSeparator = rssNodePath.find_first_of('.');
if (nSeparator == std::string::npos)
return std::make_pair(rssNodePath, std::string());
return std::make_pair(rssNodePath.substr(0, nSeparator), rssNodePath.substr(nSeparator + 1));
};
// Parse the file
toml_parser::CParser parser(ssContent);
sdv::toml::CNodeCollection root(&parser.Root());
// Lambda function to iterative add parameters
std::function<void(const sdv::toml::CNodeCollection&)> fnProcessObjectInstance =
[&](const sdv::toml::CNodeCollection& rTable)
{
for (size_t nNodeIndex = 0; nNodeIndex < rTable.GetCount(); nNodeIndex++)
{
sdv::toml::CNode node = rTable.Get(nNodeIndex);
switch (node.GetType())
{
case sdv::toml::ENodeType::node_boolean:
case sdv::toml::ENodeType::node_integer:
case sdv::toml::ENodeType::node_floating_point:
case sdv::toml::ENodeType::node_string:
{
auto prParameter = fnSplit(node.GetQualifiedPath());
if (!prParameter.first.empty() && !prParameter.second.empty())
fnAddParameter(prParameter.first, prParameter.second, node.GetValue());
}
break;
case sdv::toml::ENodeType::node_table:
{
sdv::toml::CNodeCollection group(node);
if (!group.IsValid())
break;
fnProcessObjectInstance(group);
}
break;
default:
// Ignore...
break;
}
}
};
fnProcessObjectInstance(root);
}
catch (const sdv::toml::XTOMLParseException& /*rxExcept*/)
{
m_nError = CMDLN_INVALID_PARAM_FILE;
m_ssArgError = CMDLN_INVALID_PARAM_FILE_MSG;
return;
}
}

View File

@@ -1,3 +1,16 @@
/********************************************************************************
* Copyright (c) 2025-2026 ZF Friedrichshafen AG
*
* This program and the accompanying materials are made available under the
* terms of the Apache License Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0
*
* SPDX-License-Identifier: Apache-2.0
*
* Contributors:
* Erik Verhoeven - initial API and implementation
********************************************************************************/
#include "../../global/process_watchdog.h"
#include <support/mem_access.h>
#include <support/sdv_core.h>
@@ -5,7 +18,6 @@
#include <support/toml.h>
#include "../../global/exec_dir_helper.h"
#include "../../global/filesystem_helper.h"
#include "../../sdv_services/core/installation_manifest.h"
#include <filesystem>
#include <fstream>
#include <sstream>
@@ -13,6 +25,8 @@
#include <vector>
#include <set>
#include "packager.h"
#include "../../sdv_services/core/toml_parser/miscellaneous.h"
#include "../../sdv_services/core/app_settings.h"
#if defined(_WIN32) && defined(_UNICODE)
extern "C" int wmain(int iArgc, const wchar_t* rgszArgv[])
@@ -70,445 +84,46 @@ extern "C" int main(int iArgc, const char* rgszArgv[])
// Anything to do?
if (environment.OperatingMode() == CSdvPackagerEnvironment::EOperatingMode::none) return NO_ERROR;
// Create a settings startup configuration
std::stringstream sstreamStartupConfig;
sstreamStartupConfig << "[Application]" << std::endl;
if (environment.Local())
{
sstreamStartupConfig << "Mode = \"Standalone\"" << std::endl;
}
else
{
sstreamStartupConfig << "Mode = \"Maintenance\"" << std::endl;
sstreamStartupConfig << "Instance = " << environment.InstanceID() << std::endl;
if (!environment.TargetLocation().empty())
sstreamStartupConfig << "InstallDir = \"" << environment.TargetLocation().generic_u8string() << "\"" << std::endl;
}
sstreamStartupConfig << "[Console]" << std::endl;
if (environment.Silent())
sstreamStartupConfig << "Report = \"Silent\"" << std::endl;
else if (environment.Verbose())
sstreamStartupConfig << "Report = \"Verbose\"" << std::endl;
else
sstreamStartupConfig << "Report = \"Normal\"" << std::endl;
// Process the application startup configuration
if (!GetAppSettings().ProcessAppStartupConfig(sstreamStartupConfig.str()))
{
std::cerr << "ERROR: Failed to process the startup configuration; cannot continue!" << std::endl;
return -1;
}
// If running as server, load the settings file
if (!environment.Local() && !GetAppSettings().LoadSettingsFile())
{
std::cerr << "ERROR: Failed to load the application settings file; cannot continue!" << std::endl;
return -1;
}
// Enable the packager.
CPackager packager(environment);
packager.Execute();
if (!packager.ArgError().empty() && !environment.Silent())
std::cerr << "ERROR: " << packager.ArgError() << std::endl;
return packager.Error();
#if 0
if (environment.CreateManifestOnly() && environment.Verbose())
std::cout << "No file copy, creating manifest only..." << std::endl;
if (!environment.Silent())
std::cout << "Installation name: " << environment.InstallName() << std::endl;
if (!environment.CreateManifestOnly())
{
if (environment.Verbose())
std::cout << "Instance ID: " << environment.InstanceID() << std::endl;
}
std::filesystem::path pathTargetRoot = environment.TargetLocation();
pathTargetRoot /= std::to_string(environment.InstanceID());
if (environment.Verbose())
std::cout << "Target root location: " << pathTargetRoot.generic_u8string() << std::endl;
if (environment.Verbose())
{
if (environment.OperatingMode() == CSdvPackagerEnvironment::EOperatingMode::pack)
std::cout << "Installing a package..." << std::endl;
else
{
if (environment.OperatingMode() == CSdvPackagerEnvironment::EOperatingMode::direct_install)
std::cout << "Direct installation of modules..." << std::endl;
else if (environment.CreateManifestOnly())
std::cout << "Creating an installation manifest..." << std::endl;
else
std::cout << "Creating an installation package..." << std::endl;
}
}
std::filesystem::path pathInstallLocation = environment.TargetLocation();
if (!environment.CreateManifestOnly())
{
pathInstallLocation /= std::to_string(environment.InstanceID());
pathInstallLocation /= environment.InstallName();
if (environment.Verbose())
std::cout << "Install location: " << pathInstallLocation.generic_u8string() << std::endl;
}
if (!environment.ConfigPath().empty())
{
if (environment.ConfigPath().is_relative()) environment.ConfigPath() = pathTargetRoot / environment.ConfigPath();
if (environment.Verbose())
std::cout << "Config location: " << environment.ConfigPath().parent_path().generic_u8string() << std::endl;
}
if (environment.CreateManifestOnly())
environment.InputLocation() = environment.TargetLocation();
if (environment.Verbose())
std::cout << "Source location: "
<< (environment.InputLocation().empty() ? "not provided" : environment.InputLocation().generic_u8string()) << std::endl;
// ================= START OF OPERATION =================
// Start the application control
sdv::app::CAppControl appcontrol;
std::string ssAppConfigTOML = R"code(
[Application]
Mode="Essential"
)code";
ssAppConfigTOML += "Instance=" + std::to_string(environment.InstanceID());
// Start the appcontrol
if (!appcontrol.Startup(ssAppConfigTOML))
{
if (!environment.Silent())
std::cerr << "ERROR: " << APP_CONTROL_STARTUP_ERROR_MSG << std::endl;
return APP_CONTROL_STARTUP_ERROR;
}
// Create the target directory
try
{
std::filesystem::create_directories(environment.TargetLocation());
}
catch (const std::filesystem::filesystem_error& /*rexcept*/)
{
if (!environment.Silent())
std::cerr << "ERROR: " << CREATE_TARGET_DIR_ERROR_MSG << std::endl;
return CREATE_TARGET_DIR_ERROR;
}
if (!environment.CreateManifestOnly())
{
// Remove existing installation directory
try
{
if (std::filesystem::exists(pathInstallLocation))
std::filesystem::remove_all(pathInstallLocation);
}
catch (const std::filesystem::filesystem_error& /*rexcept*/)
{
if (!environment.Silent())
std::cerr << "ERROR: " << CANNOT_REMOVE_INSTALL_DIR_MSG << std::endl;
return CANNOT_REMOVE_INSTALL_DIR;
}
// Create the installation directory
try
{
std::filesystem::create_directories(pathInstallLocation);
}
catch (const std::filesystem::filesystem_error& /*rexcept*/)
{
if (!environment.Silent())
std::cerr << "ERROR: " << CREATE_INSTALL_DIR_ERROR_MSG << std::endl;
return CREATE_INSTALL_DIR_ERROR;
}
}
// Create the config directory
if (!environment.ConfigPath().empty())
{
try
{
std::filesystem::create_directories(environment.ConfigPath().parent_path());
}
catch (const std::filesystem::filesystem_error& /*rexcept*/)
{
if (!environment.Silent())
std::cerr << "ERROR: " << CREATE_CONFIG_DIR_ERROR_MSG << std::endl;
return CREATE_CONFIG_DIR_ERROR;
}
}
// Add the installation manifest
CInstallManifest manifest;
manifest.Create(environment.InstallName());
// Process all modules
std::set<std::filesystem::path> setModules;
for (std::filesystem::path& rpathSearchModule : environment.ModuleList())
{
if (environment.Verbose())
std::cout << "Supplied module: " << rpathSearchModule.generic_u8string() << std::endl;
if (rpathSearchModule.is_relative()) rpathSearchModule = environment.InputLocation() / rpathSearchModule;
// Does the path have wildcards?
bool bHasWildcards = rpathSearchModule.generic_u8string().find_first_of("*?") != std::string::npos;
// Get a list of modules based on the module search criteria
std::vector<std::filesystem::path> vecSearchedModules = FindFilesWithWildcards(rpathSearchModule);
if (vecSearchedModules.empty() && !bHasWildcards)
{
if (!environment.Silent())
std::cerr << "ERROR: " << CMDLN_SOURCE_FILE_ERROR_MSG << " (" << rpathSearchModule.generic_u8string() << ")" <<
std::endl;
return CMDLN_SOURCE_FILE_ERROR;
}
// Process each module...
for (std::filesystem::path& rpathModule : vecSearchedModules)
{
// Processed already?
if (setModules.find(rpathModule) != setModules.end()) continue;
setModules.insert(rpathModule);
// Determine the relative path to from source location
std::filesystem::path pathRelSource;
try
{
pathRelSource = environment.InputLocation().empty() ?
rpathModule.filename() : std::filesystem::proximate(rpathModule, environment.InputLocation());
} catch (std::filesystem::filesystem_error& /*rexcept*/)
{}
if (pathRelSource.empty() || pathRelSource.begin()->string() == "..")
{
if (!environment.Silent())
std::cerr << "ERROR: The source directory must be a parent of the module! The module will not be installed." <<
std::endl;
continue;
}
if (!environment.Silent())
std::cout << "Processing module: " << pathRelSource.generic_u8string() << std::endl;
if (!std::filesystem::exists(rpathModule) || !std::filesystem::is_regular_file(rpathModule))
{
if (!environment.Silent())
std::cerr << "ERROR: Module cannot be found or is invalid!" << std::endl;
continue;
}
if (!environment.CreateManifestOnly())
{
// Create target module path
std::filesystem::path pathPargetModule = pathInstallLocation / pathRelSource;
// Create the target directory if not existing
try
{
std::filesystem::create_directories(pathPargetModule.parent_path());
}
catch (std::filesystem::filesystem_error& /*rexcept*/)
{
if (!environment.Silent())
std::cerr << "ERROR: Cannot create the module target directory!" << std::endl;
continue;
}
// Copy the file to there
std::filesystem::copy(rpathModule, pathPargetModule);
if (environment.Verbose())
std::cout << "Target module: " << pathPargetModule.generic_u8string() << std::endl;
}
// Add the installation manifest
if (!manifest.AddModule(rpathModule))
{
if (!environment.Silent())
std::cerr << "ERROR: Cannot store the component manifest!" << std::endl;
continue;
}
}
}
if (!manifest.Save(pathInstallLocation))
{
if (!environment.Silent())
std::cerr << "ERROR: " << SAVE_INSTALL_MANIFEST_ERROR_MSG << std::endl;
return SAVE_INSTALL_MANIFEST_ERROR;
}
if (environment.Verbose())
std::cout << "Saved installation manifest: " << (pathInstallLocation / "install_manifest.toml").generic_u8string() <<
std::endl;
// Write config file
if (!environment.ConfigPath().empty())
{
std::ofstream fstream(environment.ConfigPath());
if (!fstream.is_open())
{
if (!environment.Silent())
std::cerr << "ERROR: " << SAVE_CONFIG_FILE_ERROR_MSG << std::endl;
return SAVE_CONFIG_FILE_ERROR;
}
fstream << "[Configuration]" << std::endl;
fstream << "Version = " << SDVFrameworkInterfaceVersion << std::endl << std::endl;
// Add each component that is a system object, device, basic service, complex service or an app...
// Also add the modules that contain one of the other component types (utility, proxy or stub).
std::map<std::filesystem::path, bool> mapModules;
for (const auto& rsComponent : manifest.ComponentList())
{
//// Exclude the class from the configuration?
//if (std::find(environment.ExcludeConfigClassList().begin(), environment.ExcludeConfigClassList().end(), rsComponent.ssClassName) !=
// environment.ExcludeConfigClassList().end()) continue;
// Get or insert the module in the map
auto itModule = mapModules.find(rsComponent.pathRelModule);
if (itModule == mapModules.end())
{
auto prInsert = mapModules.insert(std::make_pair(rsComponent.pathRelModule, false));
if (!prInsert.second) continue;
itModule = prInsert.first;
}
// Add the component if it is a system object, device, basic service, complex service or an app.
switch (rsComponent.eType)
{
case sdv::EObjectType::SystemObject:
case sdv::EObjectType::Device:
case sdv::EObjectType::BasicService:
case sdv::EObjectType::ComplexService:
fstream << "[[Component]]" << std::endl;
fstream << "Path = \"" << rsComponent.pathRelModule.generic_u8string() << "\"" << std::endl;
fstream << "Class = \"" << rsComponent.ssClassName << "\"" << std::endl;
if (rsComponent.ssDefaultObjectName.empty())
fstream << "Name = \"" << rsComponent.ssClassName << "\"" << std::endl;
else
fstream << "Name = \"" << rsComponent.ssDefaultObjectName << "\"" << std::endl;
fstream << std::endl;
itModule->second = true; // Module was added through component.
break;
default:
break;
}
}
// Add all modules that were not added through a component already
for (const auto& rvtModule : mapModules)
{
if (rvtModule.second) continue; // Module added already
fstream << "[[Module]]" << std::endl;
fstream << "Path = \"" << rvtModule.first.generic_u8string() << "\"" << std::endl << std::endl;
}
fstream.close();
if (environment.Verbose())
std::cout << "Saved configuration file: " << environment.ConfigPath().generic_u8string() << std::endl;
}
// Write settings file
if (environment.Settings())
{
// Read the existing settings
std::filesystem::path pathSettings = pathTargetRoot / "settings.toml";
std::list<std::string> lstSystemConfigs;
std::string ssAppConfig;
if (std::filesystem::exists(pathSettings))
{
std::ifstream fstream(pathSettings);
if (fstream.is_open())
{
if (environment.Verbose())
std::cout << "Reading existing settings file..." << std::endl;
std::string ssContent((std::istreambuf_iterator<char>(fstream)), std::istreambuf_iterator<char>());
sdv::toml::CTOMLParser parser(ssContent);
sdv::toml::CNode nodeVersion = parser.GetDirect("Settings.Version");
if (nodeVersion.GetValue() != SDVFrameworkInterfaceVersion)
{
if (!environment.Silent())
std::cout << "ERROR: " << SETTING_FILE_VERSION_INVALID_MSG << " No update taken place. (version " <<
static_cast<uint32_t>(nodeVersion.GetValue()) << " detected; version " <<
SDVFrameworkInterfaceVersion << " needed)." << std::endl;
return SETTING_FILE_VERSION_INVALID;
}
// Read the system configurations
sdv::toml::CNodeCollection nodeSysConfigs = parser.GetDirect("Settings.SystemConfig");
for (size_t nIndex = 0; nIndex < nodeSysConfigs.GetCount(); nIndex++)
{
sdv::toml::CNode nodeSysConfig = nodeSysConfigs[nIndex];
if (nodeSysConfig)
{
lstSystemConfigs.push_back(nodeSysConfig.GetValue());
if (environment.Verbose())
std::cout << "Detected existing SystemConfig entry: " <<
static_cast<std::string>(nodeSysConfig.GetValue()) << std::endl;
}
}
// Read the app config
sdv::toml::CNode nodeAppConfig = parser.GetDirect("Settings.AppConfig");
if (nodeAppConfig)
{
ssAppConfig = static_cast<std::string>(nodeAppConfig.GetValue());
if (environment.Verbose())
std::cout << "Detected existing AppConfig entry: " << ssAppConfig << std::endl;
}
}
}
// (Over)write existing settings file
std::ofstream fstream(pathSettings);
if (!fstream.is_open())
{
std::cerr << "ERROR: " << SAVE_SETTINGS_FILE_ERROR_MSG << std::endl;
return SAVE_SETTINGS_FILE_ERROR;
}
// TODO... allow partial update of TOML file. Needs parser-updates first to be able to do this. See:
// https://dev.azure.com/SW4ZF/AZP-074_DivDI_SofDCarResearch/_workitems/edit/580309
const char* szSettingsTemplatePart1 = R"code(# Settings file
[Settings]
Version = 100
# The system config array can contain zero or more configurations that are loaded at the time
# the system ist started. It is advisable to split the configurations in:
# platform config - containing all the components needed to interact with the OS,
# middleware, vehicle bus, Ethernet.
# vehicle interface - containing the vehicle bus interpretation components like data link
# based on DBC and devices for their abstraction.
# vehicle abstraction - containing the basic services
# Load the system configurations by providing the "SystemConfig" keyword as an array of strings.
# A relative path is relative to the installation directory (being "exe_location/instance_id").
#
# Example:
# SystemConfig = [ "platform.toml", "vehicle_ifc.toml", "vehicle_abstract.toml" ]
#
)code";
fstream << szSettingsTemplatePart1;
if (!lstSystemConfigs.empty())
fstream << "SystemConfig = [ ";
for (auto it = lstSystemConfigs.begin(); it != lstSystemConfigs.end(); ++it)
{
if (environment.Verbose())
std::cout << "Storing system config " << *it << " to the settings file." << std::endl;
fstream << "\"" << *it << "\"";
if (std::next(it) != lstSystemConfigs.end())
{
fstream << ", ";
}
}
if (!lstSystemConfigs.empty())
fstream << " ]" << std::endl;
const char* szSettingsTemplatePart2 = R"code(
# The application config contains the configuration file that can be updated when services and
# apps are being added to the system (or being removed from the system). Load the application
# config by providing the "AppConfig" keyword as a string value. A relative path is relative to
# the installation directory (being "exe_location/instance_id").
#
# Example
# AppConfig = "app_config.toml"
#
)code";
fstream << szSettingsTemplatePart2;
if (!environment.ConfigPath().empty())
{
if (!ssAppConfig.empty() && ssAppConfig != environment.ConfigPath().filename().generic_u8string())
{
if (!environment.Silent())
std::cout << "WARNING: The application config file in the settings is being updated from " <<
environment.ConfigPath().filename().generic_u8string() << " to " << ssAppConfig << std::endl;
}
ssAppConfig = environment.ConfigPath().filename().generic_u8string();
}
if (!ssAppConfig.empty())
{
if (environment.Verbose())
std::cout << "Storing application config " << ssAppConfig << " to the settings file." << std::endl;
fstream << "AppConfig = \"" << ssAppConfig << "\"" << std::endl;
}
fstream.close();
if (environment.Verbose())
std::cout << "Saved settings file: " << pathSettings.generic_u8string() << std::endl;
}
appcontrol.Shutdown();
if (!environment.Silent())
std::cout << std::endl << "Done..." << std::endl;
return NO_ERROR;
#endif
return NOT_IMPLEMENTED;
}

View File

@@ -1,5 +1,22 @@
/********************************************************************************
* Copyright (c) 2025-2026 ZF Friedrichshafen AG
*
* This program and the accompanying materials are made available under the
* terms of the Apache License Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0
*
* SPDX-License-Identifier: Apache-2.0
*
* Contributors:
* Erik Verhoeven - initial API and implementation
********************************************************************************/
#include "packager.h"
#include <support/component_impl.h>
#include "../../sdv_services/core/app_settings.h"
#include "../../sdv_services/core/app_config.h"
#include "../../sdv_services/core/installation_composer.h"
#include "../global/path_match.h"
CPackager::CPackager(CSdvPackagerEnvironment& renv) : m_env(renv)
{}
@@ -17,6 +34,9 @@ bool CPackager::Execute()
case CSdvPackagerEnvironment::EOperatingMode::direct_install:
if (!Copy()) return false;
break;
case CSdvPackagerEnvironment::EOperatingMode::configure:
if (!Configure()) return false;
break;
case CSdvPackagerEnvironment::EOperatingMode::uninstall:
if (!Remove()) return false;
break;
@@ -58,8 +78,8 @@ bool CPackager::Pack()
if (rsModule.ssSearchString.substr(0, 6) == "regex:")
{
ssSearchString = rsModule.ssSearchString.substr(6);
uiFlags = static_cast<uint32_t>(CInstallComposer::EAddModuleFlags::regex);
bExpectFile = false;
uiFlags = static_cast<uint32_t>(CInstallComposer::EAddModuleFlags::regex);
bExpectFile = false;
}
else
{
@@ -80,7 +100,7 @@ bool CPackager::Pack()
auto vecFiles = composer.AddModule(m_env.SourceLocation(), ssSearchString, rsModule.pathRelTarget, uiFlags);
if (bExpectFile && !vecFiles.size())
{
m_nError = CMDLN_SOURCE_FILE_ERROR;
m_nError = CMDLN_SOURCE_FILE_ERROR;
m_ssArgError = CMDLN_SOURCE_FILE_ERROR_MSG;
return false;
}
@@ -96,7 +116,7 @@ bool CPackager::Pack()
if (!nCount)
{
m_nError = NO_SOURCE_FILES;
m_nError = NO_SOURCE_FILES;
m_ssArgError = NO_SOURCE_FILES_MSG;
return false;
}
@@ -171,6 +191,13 @@ bool CPackager::Unpack()
std::cout << "Copyright: " << *manifest.Property("Copyrights") << std::endl;
std::cout << "Version: " << *manifest.Property("Version") << std::endl;
}
// Report count if requested
if (!m_env.Silent())
std::cout << "Unpacked " << manifest.ModuleList().size() << " files..." << std::endl;
// Update the configuration
if (!ConfigureFromManifest(manifest)) return false;
}
catch (const sdv::XSysExcept& rexception)
{
@@ -186,6 +213,9 @@ bool CPackager::Copy()
CInstallManifest manifest;
try
{
if (m_env.Verbose())
std::cout << "Direct installation..." << std::endl;
CInstallComposer composer;
size_t nCount = 0;
for (const CSdvPackagerEnvironment::SModule& rsModule : m_env.ModuleList())
@@ -197,8 +227,8 @@ bool CPackager::Copy()
if (rsModule.ssSearchString.substr(0, 6) == "regex:")
{
ssSearchString = rsModule.ssSearchString.substr(6);
uiFlags = static_cast<uint32_t>(CInstallComposer::EAddModuleFlags::regex);
bExpectFile = false;
uiFlags = static_cast<uint32_t>(CInstallComposer::EAddModuleFlags::regex);
bExpectFile = false;
}
else
{
@@ -219,7 +249,7 @@ bool CPackager::Copy()
auto vecFiles = composer.AddModule(m_env.SourceLocation(), ssSearchString, rsModule.pathRelTarget, uiFlags);
if (bExpectFile && !vecFiles.size())
{
m_nError = CMDLN_SOURCE_FILE_ERROR;
m_nError = CMDLN_SOURCE_FILE_ERROR;
m_ssArgError = CMDLN_SOURCE_FILE_ERROR_MSG;
return false;
}
@@ -235,7 +265,7 @@ bool CPackager::Copy()
if (!nCount)
{
m_nError = NO_SOURCE_FILES;
m_nError = NO_SOURCE_FILES;
m_ssArgError = NO_SOURCE_FILES_MSG;
return false;
}
@@ -257,7 +287,6 @@ bool CPackager::Copy()
// Report count if requested
if (!m_env.Silent())
std::cout << "Added " << nCount << " files..." << std::endl;
CInstallComposer::EUpdateRules eUpdateRule = CInstallComposer::EUpdateRules::not_allowed;
if (m_env.Overwrite())
eUpdateRule = CInstallComposer::EUpdateRules::overwrite;
@@ -268,6 +297,9 @@ bool CPackager::Copy()
// Report count if requested
if (!m_env.Silent())
std::cout << "Copied " << manifest.ModuleList().size() << " files..." << std::endl;
// Update the configuration
if (!ConfigureFromManifest(manifest)) return false;
}
catch (const sdv::XSysExcept& rexception)
{
@@ -278,6 +310,180 @@ bool CPackager::Copy()
return true;
}
bool CPackager::Configure()
{
if (m_env.Verbose())
std::cout << "Configure installation..." << std::endl;
std::vector<std::filesystem::path> vecConfigFiles;
for (const std::string& rssConfigFileSearchString : m_env.ConfigFileList())
{
// Check for regular expression or wildcard search string
std::string ssSearchString;
EPathMatchAlgorithm eAlgorithm = EPathMatchAlgorithm::wildcards;
if (rssConfigFileSearchString.substr(0, 6) == "regex:")
{
ssSearchString = rssConfigFileSearchString.substr(6);
eAlgorithm = EPathMatchAlgorithm::regex;
}
else
ssSearchString = rssConfigFileSearchString;
std::filesystem::path pathBasePath = m_env.SourceLocation();
if (pathBasePath.empty())
{
// If no base path is supplied, the file path needs to be absolute.
if (!std::filesystem::path(ssSearchString).is_absolute())
{
m_nError = CMDLN_SOURCE_LOCATION_ERROR;
m_ssArgError = CMDLN_SOURCE_LOCATION_ERROR_MSG;
return false;
}
}
else
{
// The base path needs to be absolute.
if (!pathBasePath.is_absolute() || !std::filesystem::exists(pathBasePath) ||
!std::filesystem::is_directory(pathBasePath))
{
m_nError = CMDLN_SOURCE_LOCATION_ERROR;
m_ssArgError = CMDLN_SOURCE_LOCATION_ERROR_MSG;
return false;
}
}
// Check for the config search path
if (ssSearchString.empty())
{
// Base path must be present
if (pathBasePath.empty())
{
m_nError = CMDLN_SOURCE_LOCATION_ERROR;
m_ssArgError = CMDLN_SOURCE_LOCATION_ERROR_MSG;
return false;
}
}
// If the pattern is defined using an absolute path, this path needs to correspond with the base path.
std::filesystem::path pathConfigFile(ssSearchString);
if (pathConfigFile.is_absolute())
{
auto itBasePart = pathBasePath.begin();
auto itConfigPart = pathConfigFile.begin();
while (itBasePart != pathBasePath.end())
{
if (itConfigPart == pathConfigFile.end() || *itBasePart != *itConfigPart)
{
m_nError = CMDLN_SOURCE_LOCATION_ERROR;
m_ssArgError = CMDLN_SOURCE_LOCATION_ERROR_MSG;
return false;
}
// Next part
itBasePart++;
itConfigPart++;
}
}
// Get the list of files
auto vecFiles = CollectWildcardPath(pathBasePath, ssSearchString, eAlgorithm);
// For each file, check whether the file is somewhere within the base path (if provided) and add the file to the file list.
for (const std::filesystem::path& rpathFile : vecFiles)
{
// Check whether the files have already been added
if (std::find_if(vecConfigFiles.begin(), vecConfigFiles.end(), [&](const std::filesystem::path& rpath) -> bool
{ return rpath == rpathFile; }) != vecConfigFiles.end())
continue;
// Add the file
vecConfigFiles.push_back(rpathFile);
}
}
// Check whether there are configuration files to merge
if (vecConfigFiles.empty())
{
m_nError = NO_SOURCE_FILES;
m_ssArgError = NO_SOURCE_FILES_MSG;
return false;
}
// Detemine the target to update
std::filesystem::path pathTargetConfig;
CSdvPackagerEnvironment::CComponentVector vecUnused;
if (m_env.Local())
pathTargetConfig = m_env.LocalConfigFile(vecUnused);
else
{
if (m_env.InsertIntoPlatformConfig(vecUnused))
{
GetAppSettings().EnableConfig(CAppSettings::EConfigType::platform_config);
pathTargetConfig = GetAppSettings().GetConfigPath(CAppSettings::EConfigType::platform_config);
}
if (m_env.InsertIntoVehicleInterfaceConfig(vecUnused))
{
GetAppSettings().EnableConfig(CAppSettings::EConfigType::vehicle_interface_config);
pathTargetConfig = GetAppSettings().GetConfigPath(CAppSettings::EConfigType::vehicle_interface_config);
}
if (m_env.InsertIntoVehicleAbstractionConfig(vecUnused))
{
GetAppSettings().EnableConfig(CAppSettings::EConfigType::vehicle_abstraction_config);
pathTargetConfig = GetAppSettings().GetConfigPath(CAppSettings::EConfigType::vehicle_abstraction_config);
}
if (m_env.InsertIntoUserConfig(vecUnused))
{
GetAppSettings().EnableConfig(CAppSettings::EConfigType::user_config);
pathTargetConfig = GetAppSettings().GetConfigPath(CAppSettings::EConfigType::user_config);
}
}
if (pathTargetConfig.empty())
{
m_nError = CMDLN_MISSING_TARGET;
m_ssArgError = CMDLN_MISSING_TARGET_MSG;
return false;
}
if (!GetAppSettings().SaveSettingsFile())
{
SDV_LOG_ERROR("Failed to save application settings. Cannot configure components!");
return false;
}
// Open the target location
if (m_env.Verbose())
std::cout << "Updating configuration " << pathTargetConfig.generic_u8string() << "..." << std::endl;
CAppConfigFile configTarget(pathTargetConfig);
if (!configTarget.LoadConfigFile())
{
m_nError = CANNOT_READ_CONFIG;
m_ssArgError = CANNOT_READ_CONFIG_MSG;
return false;
}
// Merge all files
for (const auto& rpathFile : vecConfigFiles)
{
// Report the files if requested
if (m_env.Verbose())
std::cout << "Merging " << rpathFile.generic_u8string() << "..." << std::endl;
switch (configTarget.MergeConfigFile(rpathFile))
{
case CAppConfigFile::EMergeResult::not_successful:
m_nError = FAILED_UPDATING_CONFIG;
m_ssArgError = FAILED_UPDATING_CONFIG_MSG;
return false;
case CAppConfigFile::EMergeResult::partly_successfull:
m_nError = PARTLY_FAILED_UPDATING_CONFIG;
m_ssArgError = PARTLY_FAILED_UPDATING_CONFIG_MSG;
return false;
default:
break;
}
}
return configTarget.SaveConfigFile();
}
bool CPackager::Remove()
{
CInstallManifest manifest;
@@ -327,14 +533,381 @@ bool CPackager::CheckIntegrity()
}
catch (const sdv::XSysExcept& rexception)
{
m_nError = PACKAGE_READ_ERROR;
m_nError = PACKAGE_READ_ERROR;
m_ssArgError = rexception.what();
return false;
}
return true;
}
void DrawTable(const std::vector<std::vector<std::string>>& rvecInfoTable, bool bSimple)
bool CPackager::ShowContent()
{
CInstallManifest manifest;
try
{
if (m_env.Verbose())
std::cout << "Show package information..." << std::endl;
CInstallComposer extractor;
manifest = extractor.ExtractInstallManifest(m_env.PackagePath());
bool bConsole = m_env.CheckShowFlag(CSdvPackagerEnvironment::EShowMask::console);
// Currently no support for XML and JSON.
if (!bConsole) return manifest.IsValid();
bool bSimple = m_env.CheckShowFlag(CSdvPackagerEnvironment::EShowMask::console_simple);
// Information
if (m_env.CheckShowFlag(CSdvPackagerEnvironment::EShowMask::info))
{
// Show information about the package
std::cout << std::endl;
if (!bSimple && !m_env.Silent())
std::cout << "Information:" << std::endl;
std::vector<std::vector<std::string>> vecTable;
auto vecProperties = manifest.PropertyList();
if (bSimple)
{
// No header, one column
std::vector<std::string> vecInitial = {std::string("Installation=") + manifest.InstallName()};
vecTable.push_back(vecInitial);
for (const auto& prProperty : vecProperties)
{
std::vector<std::string> vecLine = {prProperty.first + "=" + prProperty.second};
vecTable.push_back(vecLine);
}
} else
{
// Header and multiple columns
std::vector<std::string> vecHeader = {"Name", "Value"};
vecTable.push_back(vecHeader);
std::vector<std::string> vecInitial = {"Installation", manifest.InstallName()};
vecTable.push_back(vecInitial);
for (const auto& prProperty : vecProperties)
{
std::vector<std::string> vecLine = {prProperty.first, prProperty.second};
vecTable.push_back(vecLine);
}
}
DrawTable(vecTable, bSimple);
}
// Modules
if (m_env.CheckShowFlag(CSdvPackagerEnvironment::EShowMask::modules))
{
// Show module information
std::cout << std::endl;
if (!bSimple && !m_env.Silent())
std::cout << "Modules:" << std::endl;
std::vector<std::vector<std::string>> vecTable;
if (!bSimple)
{
// Header only when not simple
std::vector<std::string> vecHeader = {"Filename"};
vecTable.push_back(vecHeader);
}
auto vecModules = manifest.ModuleList();
for (const auto& pathModule : vecModules)
{
std::vector<std::string> vecLine = {pathModule.generic_u8string()};
vecTable.push_back(vecLine);
}
DrawTable(vecTable, bSimple);
}
// Components
if (m_env.CheckShowFlag(CSdvPackagerEnvironment::EShowMask::components))
{
// Show component information
std::cout << std::endl;
if (!bSimple && !m_env.Silent())
std::cout << "Components:" << std::endl;
std::vector<std::vector<std::string>> vecTable;
if (!bSimple)
{
// Header only when not simple
std::vector<std::string> vecHeader = {"Class", "Alias", "Type", "Dependency"};
vecTable.push_back(vecHeader);
}
auto vecClasses = manifest.ClassList();
for (const auto& sComponent : vecClasses)
{
if (bSimple)
{
// Simple - no header and onle column containing all classes and associated aliases.
vecTable.push_back(std::vector<std::string>{sComponent.ssName});
for (const auto& rssAlias : sComponent.seqClassAliases)
vecTable.push_back(std::vector<std::string>{rssAlias});
}
else
{
// Not simple - header and multiple columns allowed
// Components can have a list of aliases and a list of dependencies. Add extra lines for each additional
// alias and dependcy.
size_t nIndex = 0;
std::vector<std::string> vecLine;
bool bAliasEnd = sComponent.seqClassAliases.empty(), bDependencyEnd = sComponent.seqDependencies.empty();
do
{
vecLine.push_back(nIndex ? "" : sComponent.ssName);
vecLine.push_back(bAliasEnd ? "" : sComponent.seqClassAliases[nIndex]);
if (!nIndex)
vecLine.push_back(sdv::ObjectType2String(sComponent.eType));
else
vecLine.push_back("");
vecLine.push_back(bDependencyEnd ? "" : sComponent.seqDependencies[nIndex]);
vecTable.push_back(vecLine);
nIndex++;
bAliasEnd = nIndex >= sComponent.seqClassAliases.size();
bDependencyEnd = nIndex >= sComponent.seqDependencies.size();
} while (!bAliasEnd && !bDependencyEnd);
}
}
DrawTable(vecTable, bSimple);
}
}
catch (const sdv::XSysExcept& rexception)
{
m_nError = PACKAGE_READ_ERROR;
m_ssArgError = rexception.what();
return false;
}
return manifest.IsValid();
}
bool CPackager::ConfigureFromManifest(const CInstallManifest& rmanifest)
{
// Get the class list.
std::vector<sdv::SClassInfo> vecClasses = rmanifest.ClassList();
CSdvPackagerEnvironment::CComponentVector vecManifestComponents, vecAddedToConfig;
for (const sdv::SClassInfo& rsClass : vecClasses)
vecManifestComponents.push_back(
std::make_pair(rsClass.ssName, ""));
// Add modules, classes and components if requested.
if (m_env.Local())
{
CSdvPackagerEnvironment::CComponentVector vecComponentsToAdd;
std::filesystem::path pathConfig = m_env.LocalConfigFile(vecComponentsToAdd);
if (vecComponentsToAdd.empty())
vecComponentsToAdd = vecManifestComponents; // If non are specified, add all components.
if (!pathConfig.empty())
WriteConfig(vecClasses, pathConfig, vecComponentsToAdd, true, vecAddedToConfig);
}
else
{
// Make certain that the settings contain the configurations if there are components to add.
CSdvPackagerEnvironment::CComponentVector vecComponentsToAdd;
if (m_env.InsertIntoPlatformConfig(vecComponentsToAdd))
{
if (vecComponentsToAdd.empty())
vecComponentsToAdd = vecManifestComponents; // If non are specified, add all components.
GetAppSettings().EnableConfig(CAppSettings::EConfigType::platform_config);
auto pathConfig = GetAppSettings().GetConfigPath(CAppSettings::EConfigType::platform_config);
WriteConfig(vecClasses, pathConfig, vecComponentsToAdd, false, vecAddedToConfig);
}
if (m_env.InsertIntoVehicleInterfaceConfig(vecComponentsToAdd))
{
if (vecComponentsToAdd.empty())
vecComponentsToAdd = vecManifestComponents; // If non are specified, add all components.
GetAppSettings().EnableConfig(CAppSettings::EConfigType::vehicle_interface_config);
auto pathConfig = GetAppSettings().GetConfigPath(CAppSettings::EConfigType::vehicle_interface_config);
WriteConfig(vecClasses, pathConfig, vecComponentsToAdd, false, vecAddedToConfig);
}
if (m_env.InsertIntoVehicleAbstractionConfig(vecComponentsToAdd))
{
if (vecComponentsToAdd.empty())
vecComponentsToAdd = vecManifestComponents; // If non are specified, add all components.
GetAppSettings().EnableConfig(CAppSettings::EConfigType::vehicle_abstraction_config);
auto pathConfig = GetAppSettings().GetConfigPath(CAppSettings::EConfigType::vehicle_abstraction_config);
WriteConfig(vecClasses, pathConfig, vecComponentsToAdd, false, vecAddedToConfig);
}
if (m_env.InsertIntoUserConfig(vecComponentsToAdd))
{
if (vecComponentsToAdd.empty())
vecComponentsToAdd = vecManifestComponents; // If non are specified, add all components.
GetAppSettings().EnableConfig(CAppSettings::EConfigType::user_config);
auto pathConfig = GetAppSettings().GetConfigPath(CAppSettings::EConfigType::user_config);
WriteConfig(vecClasses, pathConfig, vecComponentsToAdd, true, vecAddedToConfig);
}
if (!GetAppSettings().SaveSettingsFile())
{
SDV_LOG_ERROR("Failed to save application settings. Cannot configure components!");
return false;
}
}
return true;
}
void CPackager::WriteConfig(const std::vector<sdv::SClassInfo>& rvecAllClasses, const std::filesystem::path& rpathConfig,
const CSdvPackagerEnvironment::CComponentVector& rvecComponents, bool bUserConfig,
CSdvPackagerEnvironment::CComponentVector& rvecAddedToConfig)
{
CAppConfigFile file(rpathConfig);
if (std::filesystem::exists(rpathConfig) && !file.IsLoaded())
{
if (!m_env.Silent())
std::cerr << "ERROR: Failed to read existing configuration file '" << rpathConfig.generic_u8string() << "'" <<
std::endl;
return;
}
// Find the class with the corresponding class name
auto fnFindClass = [&](const std::string& rssClassName) -> std::optional<sdv::SClassInfo>
{
auto itClass = std::find_if(rvecAllClasses.begin(), rvecAllClasses.end(), [&](const sdv::SClassInfo& rsClass)
{ return rsClass.ssName == rssClassName; });
return itClass != rvecAllClasses.end() ? *itClass : std::optional<sdv::SClassInfo>();
};
// Add the components to the configuration.
// For local installation, add any component. For server installation, only add complex services for user congiguration
// and devices, basic services and system services for the other configurations. For server installation, the component
// must be present in the installation and be added only once.
std::vector<std::string> vecNotInInstallation, vecNotAllowed, vecDuplicate, vecFailed, vecSucceeded;
CSdvPackagerEnvironment::CComponentVector vecComponentsCopy = rvecComponents;
if (rvecComponents.empty()) return; // Nothing to do...
for (const auto& rprComponent : vecComponentsCopy)
{
// For server components, get the corresponding class and check whether installation is allowed.
if (!m_env.Local())
{
const auto& optClass = fnFindClass(rprComponent.first);
if (!optClass)
{
vecNotInInstallation.push_back(rprComponent.first);
continue;
}
enum class ECompatibility {compatible, incompatible, silent_incompatible} eIncompatible = ECompatibility::incompatible;
switch (optClass->eType)
{
case sdv::EObjectType::device:
case sdv::EObjectType::platform_abstraction:
case sdv::EObjectType::vehicle_bus:
case sdv::EObjectType::basic_service:
case sdv::EObjectType::sensor:
case sdv::EObjectType::actuator:
case sdv::EObjectType::system_object:
eIncompatible = bUserConfig ? ECompatibility::incompatible : ECompatibility::compatible;
break;
case sdv::EObjectType::complex_service:
case sdv::EObjectType::vehicle_function:
case sdv::EObjectType::utility:
eIncompatible = bUserConfig ? ECompatibility::compatible : ECompatibility::incompatible;
break;
case sdv::EObjectType::proxy:
case sdv::EObjectType::stub:
eIncompatible = ECompatibility::silent_incompatible;
break;
default:
break;
}
if (eIncompatible == ECompatibility::incompatible && !rvecComponents.empty())
{
// Only add to incompatible list if not extracted from manifest list.
vecNotAllowed.push_back(rprComponent.first);
}
if (eIncompatible != ECompatibility::compatible)
continue;
std::string ssInstanceName = rprComponent.second;
if (ssInstanceName.empty())
ssInstanceName = optClass->ssDefaultObjectName;
if (ssInstanceName.empty())
ssInstanceName = rprComponent.first;
auto itInstalledComponent = std::find_if(rvecAddedToConfig.begin(), rvecAddedToConfig.end(),
[&](const auto& rprInstalledComponent) { return rprInstalledComponent.second == ssInstanceName; });
if (itInstalledComponent != rvecAddedToConfig.end())
{
// Only add to duplicate list if not extracted from manifest list.
if (!rvecComponents.empty())
vecDuplicate.push_back(ssInstanceName);
continue;
}
rvecAddedToConfig.push_back(std::make_pair(rprComponent.first, ssInstanceName));
}
// Find the object instrance parameters
TParameterVector vecParameters = m_env.ObjectParameters(rprComponent.first);
// Add the component to the configuration...
if (file.InsertComponent(sdv::toml::npos, std::filesystem::path(), rprComponent.first, rprComponent.second,
vecParameters))
vecSucceeded.push_back(rprComponent.first);
else
vecFailed.push_back(rprComponent.first);
}
// Report warnings.
if (!vecNotInInstallation.empty())
{
if (!m_env.Silent())
std::cerr << "WARNING: in the configuration file '" << rpathConfig.generic_u8string() << "' the following "
"component classes were not part of the installation and cannot be added: ";
bool bInitial = true;
for (const std::string& rssName : vecNotInInstallation)
{
std::cerr << (bInitial ? "" : ", ") << rssName;
bInitial = false;
}
std::cerr << std::endl;
}
if (!vecNotAllowed.empty())
{
if (!m_env.Silent())
std::cerr << "WARNING: in the configuration file '" << rpathConfig.generic_u8string() << "' the following "
"component classes are of invalid type and cannot be added: ";
bool bInitial = true;
for (const std::string& rssName : vecNotAllowed)
{
std::cerr << (bInitial ? "" : ", ") << rssName;
bInitial = false;
}
std::cerr << std::endl;
}
if (!vecDuplicate.empty())
{
if (!m_env.Silent())
std::cerr << "WARNING: in the configuration file '" << rpathConfig.generic_u8string() << "' the following "
"component classes are duplicated and cannot be added: ";
bool bInitial = true;
for (const std::string& rssName : vecDuplicate)
{
std::cerr << (bInitial ? "" : ", ") << rssName;
bInitial = false;
}
std::cerr << std::endl;
}
if (!vecFailed.empty())
{
if (!m_env.Silent())
std::cerr << "WARNING: in the configuration file '" << rpathConfig.generic_u8string() << "' the following "
"component classes cannot be added due ot a technical issue: ";
bool bInitial = true;
for (const std::string& rssName : vecFailed)
{
std::cerr << (bInitial ? "" : ", ") << rssName;
bInitial = false;
}
std::cerr << std::endl;
}
// Save the configuration file.
file.SaveConfigFile();
// Report count if requested
if (!m_env.Silent())
std::cout << "Added " << vecSucceeded.size() << " components to '" << rpathConfig.generic_u8string() << "'" <<
std::endl;
}
void CPackager::DrawTable(const std::vector<std::vector<std::string>>& rvecInfoTable, bool bSimple)
{
if (rvecInfoTable.empty()) return; // No columns
@@ -435,173 +1008,3 @@ void DrawTable(const std::vector<std::vector<std::string>>& rvecInfoTable, bool
std::cout << sstream.str();
}
bool CPackager::ShowContent()
{
CInstallManifest manifest;
try
{
if (m_env.Verbose())
std::cout << "Show package information..." << std::endl;
CInstallComposer extractor;
manifest = extractor.ExtractInstallManifest(m_env.PackagePath());
bool bConsole = m_env.CheckShowFlag(CSdvPackagerEnvironment::EShowMask::console);
bool bSimple = m_env.CheckShowFlag(CSdvPackagerEnvironment::EShowMask::console_simple);
// Currently no support for XML and JSON.
if (bConsole)
{
// Information
if (m_env.CheckShowFlag(CSdvPackagerEnvironment::EShowMask::info))
{
// Show information about the package
std::cout << std::endl;
if (!bSimple && !m_env.Silent())
std::cout << "Information:" << std::endl;
std::vector<std::vector<std::string>> vecTable;
auto vecProperties = manifest.PropertyList();
if (bSimple)
{
// No header, one column
std::vector<std::string> vecInitial = {std::string("Installation=") + manifest.InstallName()};
vecTable.push_back(vecInitial);
for (const auto& prProperty : vecProperties)
{
std::vector<std::string> vecLine = {prProperty.first + "=" + prProperty.second};
vecTable.push_back(vecLine);
}
} else
{
// Header and multiple columns
std::vector<std::string> vecHeader = {"Name", "Value"};
vecTable.push_back(vecHeader);
std::vector<std::string> vecInitial = {"Installation", manifest.InstallName()};
vecTable.push_back(vecInitial);
for (const auto& prProperty : vecProperties)
{
std::vector<std::string> vecLine = {prProperty.first, prProperty.second};
vecTable.push_back(vecLine);
}
}
DrawTable(vecTable, bSimple);
}
// Modules
if (m_env.CheckShowFlag(CSdvPackagerEnvironment::EShowMask::modules))
{
// Show module information
std::cout << std::endl;
if (!bSimple && !m_env.Silent())
std::cout << "Modules:" << std::endl;
std::vector<std::vector<std::string>> vecTable;
if (!bSimple)
{
// Header only when not simple
std::vector<std::string> vecHeader = {"Filename"};
vecTable.push_back(vecHeader);
}
auto vecModules = manifest.ModuleList();
for (const auto& pathModule : vecModules)
{
std::vector<std::string> vecLine = {pathModule.generic_u8string()};
vecTable.push_back(vecLine);
}
DrawTable(vecTable, bSimple);
}
// Components
if (m_env.CheckShowFlag(CSdvPackagerEnvironment::EShowMask::components))
{
// Show component information
std::cout << std::endl;
if (!bSimple && !m_env.Silent())
std::cout << "Components:" << std::endl;
std::vector<std::vector<std::string>> vecTable;
if (!bSimple)
{
// Header only when not simple
std::vector<std::string> vecHeader = {"Class", "Alias", "Type", "Dependency"};
vecTable.push_back(vecHeader);
}
auto vecComponents = manifest.ComponentList();
for (const auto& sComponent : vecComponents)
{
if (bSimple)
{
// Simple - no header and onle column containing all classes and associated aliases.
vecTable.push_back(std::vector<std::string>{sComponent.ssClassName});
for (const auto& rssAlias : sComponent.seqAliases)
vecTable.push_back(std::vector<std::string>{rssAlias});
}
else
{
// Not simple - header and multiple columns allowed
// Components can have a list of aliases and a list of dependencies. Add extra lines for each additional
// alias and dependcy.
size_t nIndex = 0;
std::vector<std::string> vecLine;
bool bAliasEnd = sComponent.seqAliases.empty(), bDependencyEnd = sComponent.seqDependencies.empty();
do
{
vecLine.push_back(nIndex ? "" : sComponent.ssClassName);
vecLine.push_back(bAliasEnd ? "" : sComponent.seqAliases[nIndex]);
if (!nIndex)
{
switch (sComponent.eType)
{
case sdv::EObjectType::SystemObject:
vecLine.push_back("System object");
break;
case sdv::EObjectType::Device:
vecLine.push_back("Device");
break;
case sdv::EObjectType::BasicService:
vecLine.push_back("Basic service");
break;
case sdv::EObjectType::ComplexService:
vecLine.push_back("Complex service");
break;
case sdv::EObjectType::Application:
vecLine.push_back("Application");
break;
case sdv::EObjectType::Proxy:
vecLine.push_back("Proxy object");
break;
case sdv::EObjectType::Stub:
vecLine.push_back("Stub object");
break;
case sdv::EObjectType::Utility:
vecLine.push_back("Utility");
break;
default:
vecLine.push_back("Unknown object");
break;
}
}
else
vecLine.push_back("");
vecLine.push_back(bDependencyEnd ? "" : sComponent.seqDependencies[nIndex]);
vecTable.push_back(vecLine);
nIndex++;
bAliasEnd = nIndex >= sComponent.seqAliases.size();
bDependencyEnd = nIndex >= sComponent.seqDependencies.size();
} while (!bAliasEnd && !bDependencyEnd);
}
}
DrawTable(vecTable, bSimple);
}
}
}
catch (const sdv::XSysExcept& rexception)
{
m_nError = PACKAGE_READ_ERROR;
m_ssArgError = rexception.what();
return false;
}
return manifest.IsValid();
}

View File

@@ -1,7 +1,22 @@
/********************************************************************************
* Copyright (c) 2025-2026 ZF Friedrichshafen AG
*
* This program and the accompanying materials are made available under the
* terms of the Apache License Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0
*
* SPDX-License-Identifier: Apache-2.0
*
* Contributors:
* Erik Verhoeven - initial API and implementation
********************************************************************************/
#ifndef PACKAGER_H
#define PACKAGER_H
#include "core_control.h"
#include "environment.h"
#include "../../sdv_services/core/installation_manifest.h"
/**
* @brief Packager class.
@@ -53,6 +68,13 @@ private:
*/
bool Copy();
/**
* @brief Configure the system with configuration files from the source location. Uses the environment settings to retrieve the
* configuration files and the set the target configuration the files should integrate into.
* @return Returns whether the configuration operation was successful.
*/
bool Configure();
/**
* @brief Remove an installation from the target location. Uses the environment settings to remove the files.
* @return Returns whether the removal operation was successful.
@@ -70,8 +92,37 @@ private:
* @return Returns whether the show operation was successful.
*/
bool ShowContent();
private:
/**
* @brief After copying files, it is possible to configure the system using the manifest.
* @param[in] rmanifest Reference to the manifest holding the objects that were installed.
* @return Returns whether the copy operation was successful.
*/
bool ConfigureFromManifest(const CInstallManifest& rmanifest);
/**
* @brief Create/update a configuration file
* @param[in] rvecAllClasses Reference to the vector containing all component classes installed by the packager.
* @param[in] rpathConfig Reference to the path of the configuration file.
* @param[in] rvecComponents Reference to the components to add to the configuration.
* @param[in] bUserConfig Set when the configuration is an user configuration; otherwise the configuration is a system
* configuration.
* @param[in, out] rvecAddedToConfig Reference to the vector containing and being updated with all the installed components.
* This vector is used to prevent adding the component to more than configuration.
*/
void WriteConfig(const std::vector<sdv::SClassInfo>& rvecAllClasses, const std::filesystem::path& rpathConfig,
const CSdvPackagerEnvironment::CComponentVector& rvecComponents, bool bUserConfig,
CSdvPackagerEnvironment::CComponentVector& rvecAddedToConfig);
/**
* @brief Draw a table using the two dimensional vector with information.
* @param[in] rvecInfoTable Reference to the two dimensional vector containing the table information.
* @param[in] bSimple When set, draws a one column table only. Otherwise all the table information is included.
*/
static void DrawTable(const std::vector<std::vector<std::string>>& rvecInfoTable, bool bSimple);
CSdvPackagerEnvironment m_env; ///< The packager environment
CSdvPackagerEnvironment m_env; ///< The packager environment
int m_nError = NO_ERROR; ///< Error code after processing the command line.
std::string m_ssArgError; ///< Error text after processing the command line.
};