- 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
This commit is contained in:
Nav
2023-12-12 23:19:21 +00:00
parent 275885e6ec
commit ec51a21846
10 changed files with 273 additions and 189 deletions

View File

@@ -1,111 +0,0 @@
<?php
/*
* Copies AVR8 target description files to AVR_TDF_DEST_FILE_PATH, in preparation for a build, and creates a JSON
* mapping of target signatures to file paths (relative to Bloom's resource directory).
* The JSON mapping is compiled as a Qt resource and used for looking-up target description file paths, by target ID.
*
* This script should be run as part of the build process.
*/
namespace Bloom\BuildScripts;
$buildPath = $argv[1] ?? null;
if (empty($buildPath)) {
print "Missing build path. Aborting\n";
exit(1);
}
require_once __DIR__ . "/TargetDescriptionFiles/Factory.php";
define("AVR_TDF_DEST_FILE_PATH", $buildPath . "/resources/TargetDescriptionFiles/AVR");
define("AVR_TDF_DEST_RELATIVE_FILE_PATH", "TargetDescriptionFiles/AVR");
define("AVR_TDF_MAPPING_FILE_PATH", AVR_TDF_DEST_FILE_PATH . "/Mapping.json");
// Empty destination directory
if (file_exists(AVR_TDF_DEST_FILE_PATH)) {
// There is no PHP function to delete a non-empty directory and I can't be arsed to write one. Bite me
exec("rm -r " . AVR_TDF_DEST_FILE_PATH);
}
if (file_exists(AVR_TDF_MAPPING_FILE_PATH)) {
unlink(AVR_TDF_MAPPING_FILE_PATH);
}
mkdir(AVR_TDF_DEST_FILE_PATH, 0700, true);
print "Loading AVR8 TDFs\n\n";
$tdfMapping = [];
$avrTdfs = TargetDescriptionFiles\Factory::loadAvr8Tdfs();
print "Processing " . count($avrTdfs) . " AVR8 TDFs...\n\n";
foreach ($avrTdfs as $avrTdf) {
print "Processing AVR8 TDF for target " . $avrTdf->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";

View File

@@ -0,0 +1,96 @@
<?php
namespace Bloom\BuildScripts;
use Bloom\BuildScripts\TargetDescriptionFiles\TargetDescriptionFile;
define('TDF_DIR_PATH', $argv[1] ?? null);
define('MAPPING_TEMPLATE_PATH', $argv[2] ?? null);
define('MAPPING_OUTPUT_PATH', $argv[3] ?? null);
define('TDF_OUTPUT_PATH', $argv[4] ?? null);
if (empty(TDF_DIR_PATH)) {
print 'Missing TDF directory path. Aborting' . PHP_EOL;
exit(1);
}
if (empty(MAPPING_TEMPLATE_PATH)) {
print 'Missing TDF mapping template path. Aborting' . PHP_EOL;
exit(1);
}
if (empty(MAPPING_OUTPUT_PATH)) {
print 'Missing TDF mapping output path. Aborting' . PHP_EOL;
exit(1);
}
if (empty(TDF_OUTPUT_PATH)) {
print 'Missing TDF output path. Aborting' . PHP_EOL;
exit(1);
}
if (!file_exists(dirname(MAPPING_OUTPUT_PATH))) {
mkdir(dirname(MAPPING_OUTPUT_PATH), 0700);
}
require_once __DIR__ . '/TargetDescriptionFiles/Factory.php';
require_once __DIR__ . '/TargetDescriptionFiles/AVR8/Avr8TargetDescriptionFile.php';
$xmlFiles = TargetDescriptionFiles\Factory::findXmlFiles(TDF_DIR_PATH);
print count($xmlFiles) . ' target descriptions files found in ' . TDF_DIR_PATH . PHP_EOL . PHP_EOL;
$targetFamiliesByArch = [
TargetDescriptionFile::ARCHITECTURE_AVR8 => '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;

View File

@@ -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()));
}
}

View File

@@ -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';
}

View File

@@ -1,35 +0,0 @@
<?php
namespace Bloom\BuildScripts;
require_once __DIR__ . "/TargetDescriptionFiles/Factory.php";
print "Loading AVR8 target description files.\n";
$avr8Tdfs = TargetDescriptionFiles\Factory::loadAvr8Tdfs();
$failedValidationCount = 0;
foreach ($avr8Tdfs as $targetDescriptionFile) {
$validationFailures = $targetDescriptionFile->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";

View File

@@ -0,0 +1,58 @@
<?php
namespace Bloom\BuildScripts;
define('TDF_DIR_PATH', $argv[1] ?? null);
if (empty(TDF_DIR_PATH)) {
print 'Missing TDF directory path. Aborting\n';
exit(1);
}
require_once __DIR__ . '/TargetDescriptionFiles/Factory.php';
$xmlFiles = TargetDescriptionFiles\Factory::findXmlFiles(TDF_DIR_PATH);
print count($xmlFiles) . ' target descriptions files found in ' . TDF_DIR_PATH . PHP_EOL . PHP_EOL;
$processedTargetConfigValues = [];
$failedValidationCount = 0;
foreach ($xmlFiles as $xmlFile) {
$xmlFilePath = $xmlFile->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);
}