From ec51a2184689f57a9dc375ef4baeb541ff56b1c0 Mon Sep 17 00:00:00 2001 From: Nav Date: Tue, 12 Dec 2023 23:19:21 +0000 Subject: [PATCH] - Began refactoring TDF build scripts - Separated TDF validation and mapping generation - Moving away from the JSON mapping file, to a generated header file containing the TDF mapping. - Other bits of tidying --- CMakeLists.txt | 54 +++++++-- build/scripts/Avr8TargetDescriptionFiles.php | 111 ------------------ .../GenerateTargetDescriptionFileMapping.php | 96 +++++++++++++++ .../TargetDescriptionFiles/Factory.php | 28 +---- .../TargetDescriptionFile.php | 6 + .../ValidateAvr8TargetDescriptionFiles.php | 35 ------ .../ValidateTargetDescriptionFiles.php | 58 +++++++++ .../TargetDescription/GeneratedMapping.hpp.in | 39 ++++++ .../TargetDescriptionFile.cpp | 12 ++ .../TargetDescriptionFile.hpp | 23 +++- 10 files changed, 273 insertions(+), 189 deletions(-) delete mode 100644 build/scripts/Avr8TargetDescriptionFiles.php create mode 100644 build/scripts/GenerateTargetDescriptionFileMapping.php delete mode 100644 build/scripts/ValidateAvr8TargetDescriptionFiles.php create mode 100644 build/scripts/ValidateTargetDescriptionFiles.php create mode 100644 src/Targets/TargetDescription/GeneratedMapping.hpp.in diff --git a/CMakeLists.txt b/CMakeLists.txt index 67672707..95be3ed8 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,10 +2,13 @@ cmake_minimum_required(VERSION 3.22) project(Bloom LANGUAGES CXX VERSION 1.0.0) set(CMAKE_PROJECT_HOMEPAGE_URL "https://bloom.oscillate.io") +set(CMAKE_CXX_STANDARD 20) + +set(AUTOGEN_BUILD_DIR ${CMAKE_BINARY_DIR}/${CMAKE_PROJECT_NAME}_autogen/) +file(MAKE_DIRECTORY ${AUTOGEN_BUILD_DIR}) set(CMAKE_VERBOSE_MAKEFILE off) -set(CMAKE_CXX_STANDARD 20) set(ENABLE_SANITIZERS off) set(CMAKE_AUTOMOC ON) @@ -78,12 +81,6 @@ target_link_libraries(Bloom Qt6::Core) target_link_libraries(Bloom Qt6::Xml) target_link_libraries(Bloom Qt6::Network) -target_sources( - Bloom - PRIVATE - ${CMAKE_BINARY_DIR}/resources/TargetDescriptionFiles/AVR/Mapping.json -) - qt_add_resources( Bloom "ApplicationResources" @@ -166,15 +163,46 @@ if (${ENABLE_SANITIZERS}) ) endif() -# Copy AVR8 TDFs to build directory and construct JSON mapping of AVR8 target signatures to TDF paths. +# Validate TDF files +add_custom_command( + OUTPUT ${CMAKE_BINARY_DIR}/tdf_validation_timestamp.txt + COMMAND echo 'Validating target description files' + COMMAND php + ARGS + ${CMAKE_CURRENT_SOURCE_DIR}/build/scripts/ValidateTargetDescriptionFiles.php + ${CMAKE_CURRENT_SOURCE_DIR}/src/Targets/TargetDescriptionFiles/ + COMMAND date > ${CMAKE_BINARY_DIR}/tdf_validation_timestamp.txt +) + +# Copy TDF files to build directory and generate TDF mapping +set(GENERATED_TDF_MAPPING_PATH "${CMAKE_BINARY_DIR}/${CMAKE_PROJECT_NAME}_autogen/GeneratedMapping.hpp") + +target_sources( + Bloom + PRIVATE + ${GENERATED_TDF_MAPPING_PATH} +) + +target_compile_definitions( + Bloom + PUBLIC GENERATED_TDF_MAPPING_PATH="${GENERATED_TDF_MAPPING_PATH}" +) + add_custom_command( OUTPUT - ${CMAKE_BINARY_DIR}/resources/TargetDescriptionFiles/AVR/Mapping.json + ${GENERATED_TDF_MAPPING_PATH} + ${CMAKE_BINARY_DIR}/resources/TargetDescriptionFiles/ DEPENDS - ${CMAKE_CURRENT_SOURCE_DIR}/build/scripts/Avr8TargetDescriptionFiles.php - COMMAND echo 'Processing AVR target description files.' - COMMAND - php ${CMAKE_CURRENT_SOURCE_DIR}/build/scripts/Avr8TargetDescriptionFiles.php ${CMAKE_BINARY_DIR} + ${CMAKE_BINARY_DIR}/tdf_validation_timestamp.txt + ${CMAKE_CURRENT_SOURCE_DIR}/build/scripts/GenerateTargetDescriptionFileMapping.php + COMMAND echo 'Processing target description files' + COMMAND php + ARGS + ${CMAKE_CURRENT_SOURCE_DIR}/build/scripts/GenerateTargetDescriptionFileMapping.php + ${CMAKE_CURRENT_SOURCE_DIR}/src/Targets/TargetDescriptionFiles/ + ${CMAKE_CURRENT_SOURCE_DIR}/src/Targets/TargetDescription/GeneratedMapping.hpp.in + ${GENERATED_TDF_MAPPING_PATH} + ${CMAKE_BINARY_DIR}/resources/TargetDescriptionFiles/ ) include(./cmake/Installing.cmake) diff --git a/build/scripts/Avr8TargetDescriptionFiles.php b/build/scripts/Avr8TargetDescriptionFiles.php deleted file mode 100644 index 9cc68e3d..00000000 --- a/build/scripts/Avr8TargetDescriptionFiles.php +++ /dev/null @@ -1,111 +0,0 @@ -targetName . "\n"; - - $strippedTargetName = str_replace([' '] , '_', $avrTdf->targetName); - $id = strtolower($strippedTargetName); - - if (in_array($id, $tdfMapping)) { - print "\033[31m" . "\n"; - print "FATAL ERROR: duplicate AVR8 target ID detected: " . $id . "\n\n" - . "TDF Path: " . realpath($avrTdf->filePath); - print "\033[0m" . "\n"; - exit(1); - } - - if (!empty(($validationFailures = $avrTdf->validate()))) { - print "\033[31m" . "\n"; - print "FATAL ERROR: AVR8 TDF failed validation - failure reasons:" . "\n" - . implode("\n", $validationFailures) . "\n\n" . "TDF Path: " - . realpath($avrTdf->filePath); - print "\033[0m" . "\n"; - exit(1); - } - - $destinationFilePath = AVR_TDF_DEST_FILE_PATH; - $relativeDestinationFilePath = AVR_TDF_DEST_RELATIVE_FILE_PATH; - - if (!empty($avrTdf->targetArchitecture)) { - // Group by architecture - $destinationFilePath .= "/" . strtoupper($avrTdf->targetArchitecture); - $relativeDestinationFilePath .= "/" . strtoupper($avrTdf->targetArchitecture); - } - - $avrFamily = $avrTdf->getFamily(); - if (!empty($avrFamily)) { - // Group by family - $destinationFilePath .= "/" . str_replace([' '] , '_', strtoupper($avrFamily)); - $relativeDestinationFilePath .= "/" . str_replace([' '] , '_', strtoupper($avrFamily)); - } - - if (!file_exists($destinationFilePath)) { - mkdir($destinationFilePath, 0700, true); - } - - $destinationFilePath .= "/" . strtoupper($strippedTargetName) . ".xml"; - $relativeDestinationFilePath .= "/" . strtoupper($strippedTargetName) . ".xml"; - - // Copy TDF to build location - if (copy($avrTdf->filePath, $destinationFilePath) === false) { - print "FATAL ERROR: Failed to copy TDF file to " . $destinationFilePath . "\n"; - print "Aborting\n"; - exit(1); - } - - $tdfMapping[$id] = [ - 'name' => $strippedTargetName, - 'signature' => $avrTdf->getSignature()->toHex(), - 'tdfPath' => $relativeDestinationFilePath, - ]; -} - -if (file_put_contents(AVR_TDF_MAPPING_FILE_PATH, json_encode($tdfMapping, JSON_PRETTY_PRINT)) === false) { - print "FATAL ERROR: Failed to create JSON mapping of target IDs to target description file paths\n"; - exit(1); -} - -print "\n"; -print "Created JSON mapping of target IDs to target description file paths: " . AVR_TDF_MAPPING_FILE_PATH . "\n\n"; -print "Processed " . count($avrTdfs) . " files.\n"; -print "Done\n"; diff --git a/build/scripts/GenerateTargetDescriptionFileMapping.php b/build/scripts/GenerateTargetDescriptionFileMapping.php new file mode 100644 index 00000000..e888c0cb --- /dev/null +++ b/build/scripts/GenerateTargetDescriptionFileMapping.php @@ -0,0 +1,96 @@ + 'TargetFamily::AVR8', +]; + +const MAP_ENTRY_TEMPLATE = '{"@CONFIG_VALUE@", {"@TARGET_NAME@", "@CONFIG_VALUE@", @TARGET_FAMILY@, "@TDF_PATH@"}}'; + +$entries = []; + +foreach ($xmlFiles as $xmlFile) { + $xmlFilePath = $xmlFile->getPathname(); + + print 'Processing ' . $xmlFilePath . PHP_EOL; + $targetDescriptionFile = TargetDescriptionFiles\Factory::loadTdfFromFile($xmlFilePath); + + $relativeTdfPath = $targetDescriptionFile->targetArchitecture . '/' + . strtoupper($targetDescriptionFile->targetName) . '.xml'; + + $entries[] = str_replace( + ['@CONFIG_VALUE@', '@TARGET_NAME@', '@TARGET_FAMILY@', '@TDF_PATH@'], + [ + $targetDescriptionFile->configurationValue, + $targetDescriptionFile->targetName, + $targetFamiliesByArch[$targetDescriptionFile->targetArchitecture], + $relativeTdfPath, + ], + MAP_ENTRY_TEMPLATE + ); + + $tdfDestinationPath = TDF_OUTPUT_PATH . '/' . $relativeTdfPath; + + $tdfDestinationDirPath = dirname($tdfDestinationPath); + if (!file_exists($tdfDestinationDirPath)) { + mkdir($tdfDestinationDirPath, 0700, true); + } + + if (!copy($targetDescriptionFile->filePath, $tdfDestinationPath)) { + print 'FATAL ERROR: Failed to copy TDF file to ' . $tdfDestinationPath . PHP_EOL; + print 'Aborting' . PHP_EOL; + exit(1); + } +} + +file_put_contents( + MAPPING_OUTPUT_PATH, + str_replace( + '//@MAPPING_PLACEHOLDER@', + implode(',' . PHP_EOL . str_repeat(' ', 12), $entries), + file_get_contents(MAPPING_TEMPLATE_PATH) + ) +); + +print PHP_EOL; +print 'Processed ' . count($xmlFiles) . ' TDFs.' . PHP_EOL; +print 'Generated TDF mapping at ' . MAPPING_OUTPUT_PATH . PHP_EOL; +print 'Done' . PHP_EOL; diff --git a/build/scripts/TargetDescriptionFiles/Factory.php b/build/scripts/TargetDescriptionFiles/Factory.php index dd9f58d4..d45281c6 100644 --- a/build/scripts/TargetDescriptionFiles/Factory.php +++ b/build/scripts/TargetDescriptionFiles/Factory.php @@ -8,9 +8,6 @@ require_once __DIR__ . "/AVR8/Avr8TargetDescriptionFile.php"; class Factory { - private const TDF_PATH = __DIR__ . '/../../../src/Targets/TargetDescriptionFiles'; - private const AVR8_TDF_PATH = self::TDF_PATH . '/AVR8'; - /** * Loads a target description file with the appropriate class. * @@ -22,36 +19,19 @@ class Factory $tdf = new TargetDescriptionFile($filePath); if ($tdf->targetArchitecture == TargetDescriptionFile::ARCHITECTURE_AVR8) { - $tdf = new Avr8TargetDescriptionFile($filePath); + return new Avr8TargetDescriptionFile($filePath); } return $tdf; } /** - * Loads all AVR8 target description files. - * - * @return Avr8TargetDescriptionFile[] - */ - public static function loadAvr8Tdfs(): array - { - /** @var Avr8TargetDescriptionFile[] $output */ - $output = []; - - foreach (self::loadXmlFiles(self::AVR8_TDF_PATH) as $xmlFile) { - $output[] = new Avr8TargetDescriptionFile($xmlFile->getPathname()); - } - - return $output; - } - - /** - * Recursively loads all XML files from a given directory. + * Recursively finds all XML files within a given directory. * * @param string $dirPath * @return \SplFileInfo[] */ - private static function loadXmlFiles(string $dirPath): array + public static function findXmlFiles(string $dirPath): array { $output = []; @@ -61,7 +41,7 @@ class Factory $output[] = clone $entry; } else if ($entry->isDir() && !$entry->isDot()) { - $output = array_merge($output, self::loadXmlFiles($entry->getPathname())); + $output = array_merge($output, self::findXmlFiles($entry->getPathname())); } } diff --git a/build/scripts/TargetDescriptionFiles/TargetDescriptionFile.php b/build/scripts/TargetDescriptionFiles/TargetDescriptionFile.php index 14cb523c..1a1c759e 100644 --- a/build/scripts/TargetDescriptionFiles/TargetDescriptionFile.php +++ b/build/scripts/TargetDescriptionFiles/TargetDescriptionFile.php @@ -20,6 +20,7 @@ class TargetDescriptionFile public ?SimpleXMLElement $xml = null; public ?string $targetName = null; + public ?string $configurationValue = null; public ?string $targetArchitecture = null; /** @var string[] */ @@ -71,6 +72,7 @@ class TargetDescriptionFile if (!empty($this->deviceAttributesByName['name'])) { $this->targetName = $device['name']; + $this->configurationValue = strtolower($device['name']); } if (!empty($this->deviceAttributesByName['architecture'])) { @@ -480,6 +482,10 @@ class TargetDescriptionFile $failures[] = 'Target name not found'; } + if (str_contains($this->targetName, ' ')) { + $failures[] = 'Target name cannot contain whitespaces'; + } + if (empty($this->targetArchitecture)) { $failures[] = 'Target architecture not found'; } diff --git a/build/scripts/ValidateAvr8TargetDescriptionFiles.php b/build/scripts/ValidateAvr8TargetDescriptionFiles.php deleted file mode 100644 index 03869551..00000000 --- a/build/scripts/ValidateAvr8TargetDescriptionFiles.php +++ /dev/null @@ -1,35 +0,0 @@ -validate(); - - if (!empty($validationFailures)) { - $failedValidationCount++; - - print "\033[31m"; - print "Validation for " . $targetDescriptionFile->filePath . " failed.\n"; - print count($validationFailures) . " error(s) found:\n"; - print implode("\n", $validationFailures); - print "\n\n"; - print "\033[0m"; - - } else { - print "\033[32m"; - print "Validation for " . $targetDescriptionFile->filePath . " passed.\n"; - print "\033[0m"; - } -} - -print "\n\n"; -print "Validated " . count($avr8Tdfs) . " TDFs. "; -print (($failedValidationCount > 0) ? "\033[31m" : "\033[32m"); -print $failedValidationCount . " failure(s)." . "\033[0m" . "\n"; -print "Done\n"; diff --git a/build/scripts/ValidateTargetDescriptionFiles.php b/build/scripts/ValidateTargetDescriptionFiles.php new file mode 100644 index 00000000..4ef5145f --- /dev/null +++ b/build/scripts/ValidateTargetDescriptionFiles.php @@ -0,0 +1,58 @@ +getPathname(); + + print 'Processing ' . $xmlFilePath . PHP_EOL; + $targetDescriptionFile = TargetDescriptionFiles\Factory::loadTdfFromFile($xmlFilePath); + + $validationFailures = $targetDescriptionFile->validate(); + if (in_array($targetDescriptionFile->configurationValue, $processedTargetConfigValues)) { + $validationFailures[] = 'Duplicate target configuration value ("' + . $targetDescriptionFile->configurationValue . '")'; + } + + if (!empty($validationFailures)) { + $failedValidationCount++; + + print "\033[31m"; + print 'Validation for ' . $xmlFilePath . ' failed' . PHP_EOL; + print count($validationFailures) . ' error(s) found:' . PHP_EOL; + print implode(PHP_EOL, $validationFailures); + print PHP_EOL . PHP_EOL; + + } else { + print "\033[32m"; + print 'Validation for ' . $xmlFilePath . ' passed' . PHP_EOL; + } + + print "\033[0m"; + + $processedTargetConfigValues[] = $targetDescriptionFile->configurationValue; +} + +print 'Validated ' . count($xmlFiles) . ' TDFs' . PHP_EOL; +print (($failedValidationCount > 0) ? "\033[31m" : "\033[32m"); +print $failedValidationCount . ' failure(s)' . "\033[0m" . PHP_EOL; +print 'Done' . PHP_EOL; + +if ($failedValidationCount > 0) { + exit(1); +} diff --git a/src/Targets/TargetDescription/GeneratedMapping.hpp.in b/src/Targets/TargetDescription/GeneratedMapping.hpp.in new file mode 100644 index 00000000..196837a1 --- /dev/null +++ b/src/Targets/TargetDescription/GeneratedMapping.hpp.in @@ -0,0 +1,39 @@ +#pragma once + +#include +#include + +#include "src/Targets/TargetFamily.hpp" + +namespace Targets::TargetDescription +{ + struct GeneratedMapping + { + struct BriefTargetDescriptor + { + std::string targetName; + std::string configValue; + TargetFamily targetFamily; + std::string relativeTdfPath; + + constexpr BriefTargetDescriptor( + const std::string& targetName, + const std::string& configValue, + TargetFamily targetFamily, + const std::string& relativeTdfPath + ) + : targetName(targetName) + , configValue(configValue) + , targetFamily(targetFamily) + , relativeTdfPath(relativeTdfPath) + {} + }; + + /* + * The @MAPPING_PLACEHOLDER@ comment below will be replaced with the TDF mapping + */ + static const inline std::map map = { + //@MAPPING_PLACEHOLDER@ + }; + }; +} diff --git a/src/Targets/TargetDescription/TargetDescriptionFile.cpp b/src/Targets/TargetDescription/TargetDescriptionFile.cpp index 1c038eb9..de6b7b49 100644 --- a/src/Targets/TargetDescription/TargetDescriptionFile.cpp +++ b/src/Targets/TargetDescription/TargetDescriptionFile.cpp @@ -11,6 +11,18 @@ namespace Targets::TargetDescription { using namespace Exceptions; + const std::map& TargetDescriptionFile::mapping() { + return GeneratedMapping::map; + } + + TargetDescriptionFile::TargetDescriptionFile(const QString& xmlFilePath) { + this->init(xmlFilePath); + } + + TargetDescriptionFile::TargetDescriptionFile(const QDomDocument& xml) { + this->init(xml); + } + const std::string& TargetDescriptionFile::getTargetName() const { return this->targetName; } diff --git a/src/Targets/TargetDescription/TargetDescriptionFile.hpp b/src/Targets/TargetDescription/TargetDescriptionFile.hpp index 79158a7e..e5c4b3d8 100644 --- a/src/Targets/TargetDescription/TargetDescriptionFile.hpp +++ b/src/Targets/TargetDescription/TargetDescriptionFile.hpp @@ -12,6 +12,8 @@ #include "Pinout.hpp" #include "Interface.hpp" +#include GENERATED_TDF_MAPPING_PATH + namespace Targets::TargetDescription { /** @@ -36,24 +38,33 @@ namespace Targets::TargetDescription class TargetDescriptionFile { public: + /** + * Returns a mapping of target configuration values to instances of the GeneratedMapping::BriefTargetDescriptor + * struct. + * + * The mapping is generated pre-build. + * + * The GeneratedMapping::BriefTargetDescriptor struct holds some brief info about a particular target, such as + * target name, family and TDF path. See the GeneratedMapping.hpp.in template for more. + * + * @return + */ + static const std::map& mapping(); + /** * Will construct a TargetDescriptionFile instance from the XML of a target description file, the path to which * is given via xmlFilePath. * * @param xmlFilePath */ - explicit TargetDescriptionFile(const QString& xmlFilePath) { - this->init(xmlFilePath); - } + explicit TargetDescriptionFile(const QString& xmlFilePath); /** * Will construct a TargetDescriptionFile instance from pre-loaded XML. * * @param xml */ - explicit TargetDescriptionFile(const QDomDocument& xml) { - this->init(xml); - } + explicit TargetDescriptionFile(const QDomDocument& xml); /** * Returns the target name extracted from the TDF.