diff --git a/.github/scripts/esp-idf-versions.php b/.github/scripts/esp-idf-versions.php index bb14fe35..6e23f7a9 100644 --- a/.github/scripts/esp-idf-versions.php +++ b/.github/scripts/esp-idf-versions.php @@ -1,121 +1,436 @@ $line ) +$fqbns = []; + +foreach($idf_versions_json['VERSIONS'] as $version) { - if( !preg_match("/release/", $line ) ) - continue; // tag or commit - $line = trim($line); - if( empty($line) ) - continue; // EOL or separator - $line_parts = explode("/", trim($line)); // tag name is the last part - if( !empty( $line_parts ) ) - $releases[] = end($line_parts); + if(!isset($version['name']) || !isset($version['old']) || !isset($version['supported_targets']) ) + continue; // skip first entry, EOL, unsupported and preview versions + if($version['old']!=='false') + continue; // only keep current versions + + foreach($version['supported_targets'] as $board) + { + if(in_array($board, $idf_boards)) // only keep supported targets + { + // e.g. esp32@5.2.1 + $fqbns[] = $board.'@'.str_replace(["v","V"], "", $version['name']); + } + } + } -!empty($releases) or php_die("releases not found"); +// merge collected fqbns with hardcoded fqbns +array_push($fqbns, ...$hardcoded_fqbns); -arsort( $releases ); +// print the json and exit +php_die( json_encode( [ "esp-idf-fqbn" => $fqbns ], JSON_PRETTY_PRINT ), 0 ); -// get version numbers from enumerated tags -foreach( $lines as $num => $line ) +// same as die() with with end of line +function php_die($msg, $errcode=1) { - if( !preg_match("/tags/", $line ) ) - continue; - $line = trim($line); - $tag_parts = explode("/", $line ); - $tag_name = end( $tag_parts ); - if( substr( $tag_name, 0, 1 ) == 'v' // esp-idf official tag names are prefixed with "v" - && substr( $tag_name, -3 ) != '^{}' // ignore commit pointers returned by git ls-remote - /*&& !preg_match( '/beta|dev|rc|head|merge/i', $tag_name)*/ ) // ignore beta/dev/rc and other non significant tags + echo $msg.PHP_EOL; + exit($errcode); +} + + +// grabbed from https://github.com/ovidigital/js-object-to-json/blob/master/src/JsConverter.php +// changes by tobozo: +// - added ::trimToBrackets(string $input):string +// - disabled the unquoting of booleans in ::convertToJson() +class JsConverter +{ + /** + * Converts a JavaScript object string to a JSON formatted string. + * + * @param string $jsObjectString The JavaScript object + * @return string + */ + public static function convertToJson(string $jsObjectString): string { - if(! preg_match("/^v?(0|(?:[1-9]\d*))(?:\.(0|(?:[1-9]\d*))(?:\.(0|(?:[1-9]\d*)))?(?:\-([\w][\w\.\-_]*))?)?$/i", $tag_name, $results ) ) + $replacedStringsList = []; + // Remove single line comments + $convertedString = self::removeSingleLineComments($jsObjectString); + // Remove functions from objects + $convertedString = self::removeFunctions($convertedString); + // Replace all delimited string literals with placeholders + $convertedString = self::escapeSingleQuoteBetweenDoubleQuotes($convertedString); + $convertedString = self::replaceSectionsWithPlaceholders($convertedString, $replacedStringsList, "`"); + $convertedString = self::replaceSectionsWithPlaceholders($convertedString, $replacedStringsList, "'"); + self::fixQuoteEscapingForSingleQuoteDelimitedStrings($replacedStringsList); + $convertedString = self::unescapeSingleQuoteBetweenDoubleQuotes($convertedString); + $convertedString = self::replaceSectionsWithPlaceholders($convertedString, $replacedStringsList, '"'); + // Now is safe to remove all white space + $convertedString = preg_replace('/\s+/m', '', $convertedString); + // And remove all trailing commas in objects + $convertedString = str_replace([',}', ',]'], ['}', ']'], $convertedString); + // Add double quotes for keys + $convertedString = preg_replace('/([^{}\[\]#,]+):/', '"$1":', $convertedString); + // Add double quotes for values + $convertedString = preg_replace_callback( + '/:([^{}\[\]#,]+)/', + static function ($matches) + { + if (is_numeric($matches[1])) + { + return ':' . $matches[1]; + } + + return ':"' . $matches[1] . '"'; + }, + $convertedString + ); + // Make sure "true", "false" and "null" values get delimited by double quotes + // Need to run the replacement twice, because not all values get replaced if they are adjacent + $convertedString = preg_replace('/([^"])(true|false|null)([^"])/', '$1"$2"$3', $convertedString); + $convertedString = preg_replace('/([^"])(true|false|null)([^"])/', '$1"$2"$3', $convertedString); + // Replace the placeholders with the initial strings + $deep = false; + do { - php_die("Bad semver with entry $num: $tag_name"); + $convertedString = preg_replace_callback( + '/###(\d+)###/', + static function ($matches) use (&$replacedStringsList, $deep) + { + $replace = $replacedStringsList[$matches[1]]; + unset($replacedStringsList[$matches[1]]); + return ($deep ? "'" . $replace . "'" : '"' . $replace . '"'); + }, + $convertedString + ); + + $deep = true; + } while (!empty($replacedStringsList)); + // Note: unquoting booleans is disabled for the sakes of this script only + // $convertedString = preg_replace('/:(")(true|false|null)(")/', ':$2', $convertedString); + return self::trimToBrackets($convertedString); + } + + /** + * Convert the given JS object to JSON and json_decode it + * + * @param string $input + * @return array|null + */ + public static function convertToArray(string $input): ?array + { + return json_decode(self::convertToJson($input), true); + } + + // remove (var/let/const) root declaration and trailing semicolon + // so the output is really enclosed by curly brackets and no javascript + // artefact remains + public static function trimToBrackets(string $input): string + { + $input = trim($input); + if($input[0] !== '{') + { + $input = substr($input, strpos($input, '{')); } - unset($results[0]); - $semver = "v".implode('.', $results ); - if( $semver != $tag_name ) - continue; // pattern matching failed with $semver - //php_die("uh oh pattern matching failed with $semver/$tag_name"); - $minor = $results[1].'.'.$results[2]; - $patch = !empty($results[3]) ? $results[1].'.'.$results[2].'.'.$results[3] : ""; - if( !in_array( 'v'.$minor, $releases ) ) - continue; // this tag is not listed in releases - if( !empty($results[3]) && !in_array( $patch, $patch_versions ) ) - $patch_versions[] = $patch; + if($input[-1] == ';' ) + $input[-1] = ' '; + //echo $input; + return trim($input); } -} -!empty($patch_versions) or php_die("tags not found"); + /** + * Remove all JS functions by counting the number of braces between open and + * close (excl strings which should be placeholder-ed at this point). + * + * Removes shorthand and longhand functions whether they're single or multi-line: + * key: (var) => 'Test', + * key: var => 'Test', + * key: () => 'Test', + * key: () => { return 'Test'; }, + * key: (var) => { + * return 'Test'; + * }, + * key: () => { + * return 'Test'; + * }, + * key: () => { + * if (complex) { + * return 'Test'; + * } + * + * return 'Test'; + * }, + * key() { + * return 'Test'; + * }, + * + * @param string $input + * @param boolean $debug (optional table view of the logic being broken down) + * @return string + */ + protected static function removeFunctions(string $input, bool $debug = false): string + { + $functionLines = '/^(\s*)(?:([\'"]?\w+[\'"]?):\s*(function\s*\([^)]*\)\s*{|\s*(?:\([^)]*\)|[a-z0-9]+)\s*=>\s*)|[a-z0-9]+\([^)]*\)\s*{)/i'; + $lines = preg_split('/[\n\r]/', $input); -arsort( $patch_versions ); + $delete = false; -$max_boards = (count($idf_boards)*$max_versions); + $opens = 0; + $closes = 0; + $table = []; -// match release versions with tag versions -foreach( $releases as $minor ) -{ - $top_version = ''; - foreach( $patch_versions as $patch ) - { - if( str_starts_with( 'v'.$patch, $minor ) ) + foreach ($lines as $index => $line) { - if( $patch > $top_version ) // SEQ comparator on a string is just cheap semver, what could go wrong ? :) + $row = [ + 'line' => $line, + 'mode' => 'standard', + 'action' => 'Keep', + 'opens' => $opens, + 'closes' => $closes, + ]; + + if (preg_match($functionLines, $line)) + { + $opens = substr_count($line, '{'); + $closes = substr_count($line, '}'); + + $row['mode'] = 'Start (found opens: ' . substr_count($line, '{') . ')'; + $row['opens'] = $opens; + $row['closes'] = $closes; + unset($lines[$index]); + + if ($opens === $closes) + { + $row['action'] = 'Delete (Final)'; + } + else + { + $delete = true; + $row['action'] = 'Delete (Continue)'; + } + + + $table[] = $row; + continue; + } + + if ($delete) + { + $opens += substr_count($line, '{'); + $closes += substr_count($line, '}'); + + $row['opens'] = $opens; + $row['closes'] = $closes; + $row['action'] = 'Delete'; + + unset($lines[$index]); + + if ($opens === $closes) + { + $row['action'] = 'Delete (Final)'; + $delete = false; + } + } + + if ($debug) { - $top_version = $patch; + $table[] = $row; } } - } - if( $top_version == '' ) - continue; - $idf_versions[] = str_replace('v', '', $top_version ); - if( count( $idf_versions ) == $max_versions ) - break; -} + if ($debug) + { + print '
Index | Line | Mode | Action | Opens | Closes |
---|---|---|---|---|---|
' . $index . ' | ' + . ''. $row['line'] . ' | '
+ . ''. $row['mode'] . ' | ' + . ''. $row['action'] . ' | ' + . ''. $row['opens'] . ' | ' + . ''. $row['closes'] . ' | ' + . '