From 8fc8cc776a01ad56db1c4d22a46abc0afb38392c Mon Sep 17 00:00:00 2001 From: Fred Hornsey Date: Mon, 30 Oct 2023 17:44:18 -0500 Subject: [PATCH] Put ACE/TAO Version Info in an INI File This is a common place the configure script, CMake, and Sphinx can get the ACE/TAO versions and related info from. This can be updated manually using `tools/scripts/gitrelease.pl --update-ace-tao` or by a workflow that will open a PR when the file needs to be updated. --- .github/workflows/update-ace-tao.yml | 63 +++++++++++++ cmake/get_ace_tao.cmake | 24 +++-- configure | 96 ++++++++++++------- docs/devguide/building/dependencies.rst | 13 ++- docs/internal/docs.rst | 16 ++++ docs/sphinx_extensions/links.py | 16 ++++ docs/sphinx_extensions/version_info.py | 39 ++------ tools/scripts/gitrelease.pl | 113 +++++++++++++++++++++- tools/scripts/modules/ini.pm | 120 ++++++++++++++++++++++++ 9 files changed, 416 insertions(+), 84 deletions(-) create mode 100644 .github/workflows/update-ace-tao.yml create mode 100755 tools/scripts/modules/ini.pm diff --git a/.github/workflows/update-ace-tao.yml b/.github/workflows/update-ace-tao.yml new file mode 100644 index 00000000000..db88a777c96 --- /dev/null +++ b/.github/workflows/update-ace-tao.yml @@ -0,0 +1,63 @@ +name: "Update ACE/TAO Versions" + +on: + workflow_dispatch: + schedule: + - cron: '0 1 * * SUN' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +env: + VCPKG_GIT_COMMIT: 8eb57355a4ffb410a2e94c07b4dca2dffbee8e50 + +jobs: + update: + runs-on: ubuntu-22.04 + steps: + - name: Checkout OpenDDS + uses: actions/checkout@v4 + - name: Install Perl Dependencies + uses: shogo82148/actions-setup-perl@v1 + with: + install-modules: | + Pithub + Net::SFTP::Foreign + Time::Piece + LWP::UserAgent + LWP::Protocol::https + - name: Run gitrelease.pl --update-ace-tao + run: + cd OpenDDS + perl tools/scripts/gitrelease.pl --update-ace-tao + # Help make the title and message for commit and PR + perl tools/scripts/modules/ini.pm acetao.ini --join ', ' '.*/version' > ../acevers + echo "ACEVERS=$(cat ../acevers)" >> $GITHUB_ENV + perl tools/scripts/modules/ini.pm acetao.ini --join ', ' '.*/url' > ../acevers_urls + echo "ACEVERS_URLS=$(cat ../acevers_urls)" >> $GITHUB_ENV + - name: Create Pull Request + uses: peter-evans/create-pull-request@v5 + id: cpr + with: + path: OpenDDS + commit-message: | + Update ACE/TAO to ${{env.ACEVERS}} + + The releases are ${{env.ACEVERS_URLS}} + committer: GitHub + author: ${{ github.actor }} <${{ github.actor }}@users.noreply.github.com> + signoff: false + branch: workflows/update-ace-tao + delete-branch: true + title: Update ACE/TAO to ${{env.ACEVERS}} + body: Releases: ${{env.ACEVERS_URLS}} + labels: | + dependencies + team-reviewers: | + maintainers + - name: Check outputs + if: ${{ steps.cpr.outputs.pull-request-number }} + run: | + echo "Pull Request Number - ${{ steps.cpr.outputs.pull-request-number }}" + echo "Pull Request URL - ${{ steps.cpr.outputs.pull-request-url }} diff --git a/cmake/get_ace_tao.cmake b/cmake/get_ace_tao.cmake index 4bdea474de3..81172afd172 100644 --- a/cmake/get_ace_tao.cmake +++ b/cmake/get_ace_tao.cmake @@ -4,17 +4,11 @@ if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.24) cmake_policy(SET CMP0135 NEW) endif() -set(url_base "https://github.com/DOCGroup/ACE_TAO/releases/download/") -set(ace_ver "7.1.2") -set(zip_md5 "983581a5813331fc7597d1e5ec811e1c") -set(tbz_md5 "949c55575b4a5753e8457eb5b5562fac") - set(_OPENDDS_CONFIGURE_ACE_TAO_ARGS) set(ACE_IS_BEING_BUILT MPC CACHE INTERNAL "") set(TAO_IS_BEING_BUILT MPC CACHE INTERNAL "") if(WIN32) set(ext "zip") - set(md5 "${zip_md5}") if(CMAKE_GENERATOR STREQUAL "Visual Studio 17 2022") set(_OPENDDS_MPC_TYPE vs2022) elseif(CMAKE_GENERATOR STREQUAL "Visual Studio 16 2019") @@ -29,7 +23,6 @@ if(WIN32) set(_OPENDDS_ACE_CONFIG_FILE "config-win32.h") else() set(ext "tar.bz2") - set(md5 "${tbz_md5}") set(_OPENDDS_MPC_TYPE gnuace) if(CMAKE_SYSTEM_NAME STREQUAL Linux) set(_OPENDDS_ACE_CONFIG_FILE "config-linux.h") @@ -78,7 +71,6 @@ if(NOT DEFINED _OPENDDS_MPC_TYPE OR NOT DEFINED _OPENDDS_ACE_CONFIG_FILE) "(${CMAKE_SYSTEM_NAME}/${CMAKE_GENERATOR})") endif() -string(REPLACE "." "_" ace_ver_tag "${ace_ver}") if(DEFINED OPENDDS_ACE_TAO_SRC) if(NOT DEFINED OPENDDS_MPC) message(FATAL_ERROR "OPENDDS_ACE_TAO_SRC requires OPENDDS_MPC to be set") @@ -104,7 +96,21 @@ else() set(OPENDDS_ACE "${OPENDDS_ACE_TAO_SRC}" CACHE INTERNAL "") set(OPENDDS_MPC "${OPENDDS_ACE}/MPC" CACHE INTERNAL "") set(OPENDDS_TAO "${OPENDDS_ACE}/TAO" CACHE INTERNAL "") - set(url "${url_base}ACE+TAO-${ace_ver_tag}/ACE+TAO-src-${ace_ver}.${ext}") + + file(STRINGS "${OPENDDS_SOURCE_DIR}/acetao.ini" ace_tao_ini) + unset(section) + foreach(line IN LISTS ace_tao_ini) + if(line MATCHES "^\\[(.*)\\]$") + set(section "${CMAKE_MATCH_1}") + elseif(section AND line MATCHES "^([^=#]+)=(.*)$") + set(name "${CMAKE_MATCH_1}") + set(value "${CMAKE_MATCH_2}") + set("${section}-${name}" "${value}") + endif() + endforeach() + set(url "${ace7tao3-${ext}-url}") + set(md5 "${ace7tao3-${ext}-md5}") + message(STATUS "Getting ACE/TAO from ${url}") FetchContent_Declare(ace_tao_dl PREFIX "${OPENDDS_BUILD_DIR}/ace_tao_tmp" diff --git a/configure b/configure index 35e495a9481..6bb37fc7e45 100755 --- a/configure +++ b/configure @@ -22,15 +22,13 @@ use FileHandle; use Cwd; use POSIX qw(strftime); use B qw/perlstring/; +use Digest::MD5; use FindBin; use lib "$FindBin::RealBin/tools/scripts/modules"; use command_utils; use ChangeDir; - -## Version of DOC Group ACE/TAO to download, uses the ACE version number -my $doc_tao2_version = '6.5.20'; -my $doc_tao3_version = '7.1.2'; +use ini qw/read_ini_file/; my $backup_timestamp = strftime "%Y-%m-%d-%H-%M-%S", localtime time; @@ -534,6 +532,24 @@ sub git_ensure_submodule { return !git_clone($path, git_submodule_prop($path, 'url'), commit => $commit); } +sub md5sum { + my $path = shift(); + my $expected_hash = shift(); + + my $md5 = Digest::MD5->new; + open(my $fh, $path) or die("Couldn't open \"$path\": $!"); + $md5->addfile($fh); + my $hash = $md5->hexdigest(); + my $failed = $expected_hash ne $hash; + if ($failed) { + print( + "MD5 hash mismatch for $path\n", + " expected: $expected_hash\n", + " got: $hash\n"); + } + return $failed; +} + my $curpathRef = $specific{'refpre'} . 'PATH' . $specific{'refpost'}; my %hostEnv = ('build' => 'host', 'PATH' => $curpathRef); my %targetEnv = ('build' => 'target', 'PATH' => $curpathRef); @@ -870,39 +886,47 @@ if (!$ace_src || !$tao_src) { $tao_src = normalizePath('ACE_TAO/TAO'); } else { - my($urlbase, $file, $download_message); - - if ($opts{'doc-group3'}) { - my $github_url_tao_version = $doc_tao3_version; - $github_url_tao_version =~ s/\./_/g; - $urlbase = 'https://github.com/DOCGroup/ACE_TAO/releases/download/ACE%2BTAO-' . $github_url_tao_version . '/'; - $file = 'ACE+TAO-src-' . $doc_tao3_version . '.' - . ($is_windows ? 'zip' : 'tar.gz'); - $download_message = "Downloading $file"; - } - else { - my $github_url_tao_version = $doc_tao2_version; - $github_url_tao_version =~ s/\./_/g; - $urlbase = 'https://github.com/DOCGroup/ACE_TAO/releases/download/ACE%2BTAO-' . $github_url_tao_version . '/'; - $file = 'ACE+TAO-src-' . $doc_tao2_version . '.' - . ($is_windows ? 'zip' : 'tar.gz'); - $download_message = "Downloading $file"; - } + # Get ACE/TAO version info + my ($section_names, $sections) = read_ini_file("$FindBin::RealBin/acetao.ini"); + my $ace_tao_version = $opts{'doc-group3'} ? $sections->{ace7tao3} : $sections->{ace6tao2}; + if ($opts{verbose}) { + print("ACE/TAO Version Info:"); + new Dumpvalue()->dumpValue($ace_tao_version); + } + my $ext = $is_windows ? 'zip' : 'tar.gz'; + my $file = $ace_tao_version->{"$ext-filename"}; + my $url = $ace_tao_version->{"$ext-url"}; + my $md5_hash = $ace_tao_version->{"$ext-md5"}; + + # Check for an existing file if (-r $file) { - print "Using ACE+TAO source package $file\n" if $opts{'verbose'}; + if (md5sum($file, $md5_hash)) { + if ($opts{'dry-run'}) { + print("Would remove existing $file and attempt to download\n"); + } + else { + print("Removing existing $file and attempting to download\n"); + unlink($file) or die("Couldn't remove $file: $!\nStopped"); + } + } + elsif ($opts{'verbose'}) { + print("Using ACE+TAO source package $file\n"); + } } - else { + + if (!-r $file) { + my $dl_msg = "Downloading $file from using"; $would_download = 1; eval { require LWP::UserAgent; my $ua = LWP::UserAgent->new; $ua->env_proxy; - print $download_message . "\n"; + print("$dl_msg LWP\n"); if ($opts{'dry-run'}) { - print "Dry-run: would LWP::UserAgent get $urlbase$file\n"; + print("Dry-run: would LWP::UserAgent get $url\n"); } else { - my $response = $ua->get($urlbase . $file, ':content_file' => $file); + my $response = $ua->get($url, ':content_file' => $file); if ($response->is_error) { die $response->message . "\nstopped"; } @@ -910,25 +934,25 @@ if (!$ace_src || !$tao_src) { }; if ($@) { if (which('wget')) { - print $download_message . " (using wget)\n"; - if (run_command(['wget', "$urlbase$file"])) { - die "ERROR from wget, stopped"; - } + print("$dl_msg wget\n"); + run_command(['wget', '--output-document', $file, $url], autodie => 1); } elsif (which('curl')) { - print $download_message . " (using curl)\n"; - if (run_command(['curl', '-L', "$urlbase$file", '-o', "$file"])) { - die "ERROR: from curl, stopped"; - } + print("$dl_msg curl\n"); + run_command(['curl', '--location', $url, '--output', $file], autodie => 1); } else { die "ERROR: Can't download ACE+TAO using LWP, wget, or curl.\n" . - "Download ACE+TAO from $urlbase$file, place the file here\n, " . + "Download ACE+TAO from $url, place the file here\n, " . "and re-run the script.\nStopped"; } } } + if (!$opts{'dry-run'} && md5sum($file, $md5_hash)) { + die("MD5 hash check failed after download, try running again?\nStopped"); + } + print "Extracting archive $file\n"; $ENV{'ACTIVEPERL_CONFIG_DISABLE'} = 1 if $^O eq 'MSWin32'; $ENV{'ACTIVEPERL_CONFIG_SILENT'} = 1 if $^O eq 'MSWin32'; diff --git a/docs/devguide/building/dependencies.rst b/docs/devguide/building/dependencies.rst index df371eed4d7..3160a3438d1 100644 --- a/docs/devguide/building/dependencies.rst +++ b/docs/devguide/building/dependencies.rst @@ -49,24 +49,23 @@ ACE/TAO The DOC Group repository for ACE/TAO is hosted on Github at `DOCGroup/ACE_TAO `__. -There are two release series of ACE/TAO that are officially supported by OpenDDS: +There are two versions of ACE/TAO that are officially supported by OpenDDS in this release (|release|): -DOC Group ACE 6.5/TAO 2.5 - The configure script will download this version (|ace6tao2_version|) by default. +DOC Group :acetaorel:`ace6tao2` + The configure script will download this version by default. Pass ``--ace-github-latest`` to the configure script to clone the ``ace6tao2`` branch of ACE/TAO as is. This also clones the ``master`` branch of MPC as is. -DOC Group ACE 7.1/TAO 3.1 - Pass ``--doc-group3`` to the configure script to download this version (|ace7tao3_version|). +DOC Group :acetaorel:`ace7tao3` + :ref:`CMake ` will download this version by default. + Pass ``--doc-group3`` to the configure script to download this version. This version requires a C++14-capable compiler. Pass ``--ace-github-latest`` to the configure script to clone the ``master`` branch of ACE/TAO as is. This also clones the ``master`` branch of MPC as is. -Both can also be manually downloaded from https://github.com/DOCGroup/ACE_TAO/releases - .. _deps-ace: ACE diff --git a/docs/internal/docs.rst b/docs/internal/docs.rst index da9bf0f9a0b..d9ec7ec335d 100644 --- a/docs/internal/docs.rst +++ b/docs/internal/docs.rst @@ -198,6 +198,22 @@ These come in the form of `RST roles ` +.. rst:role:: acetaorel + + ``acetaorel`` accepts the ACE/TAO major version nickname from :ghfile:`acetao.ini` and makes a link to that release this version of OpenDDS uses. + + .. code-block:: rst + + See :acetaorel:`ace6tao2` + + Also see :acetaorel:`this ` + + Turns into: + + See :acetaorel:`ace6tao2` + + Also see :acetaorel:`this ` + .. rst:role:: omgissue .. code-block:: rst diff --git a/docs/sphinx_extensions/links.py b/docs/sphinx_extensions/links.py index 2c91d57baa9..6d5ffc803a3 100644 --- a/docs/sphinx_extensions/links.py +++ b/docs/sphinx_extensions/links.py @@ -171,6 +171,19 @@ def ghrelease_role(name, rawtext, text, lineno, inliner, options={}, content=[]) return ([node], []) +def acetaorel_role(name, rawtext, text, lineno, inliner, options={}, content=[]): + config = get_config(inliner) + info = vars(config.opendds_version_info) + explicit_title, title, target = process_title_target(text) + if not explicit_title: + ace_ver = info[target + '_version'] + parts = ace_ver.split('.') + tao_ver = '.'.join([str(int(parts[0]) - 4)] + parts[1:3]) + title = f'ACE {ace_ver}/TAO {tao_ver}' + return link_node(rawtext, lineno, inliner, + title, explicit_title, info[target + '_url'], options) + + def omgissue_role(name, rawtext, text, lineno, inliner, options={}, content=[]): explicit_title, title, target = process_title_target( text, 'OMG Issue {}'.format(text)) @@ -375,6 +388,9 @@ def setup(app): app.add_role('ghpr', ghpr_role) app.add_role('ghrelease', ghrelease_role) + app.add_config_value('opendds_version_info', None, 'env') + app.add_role('acetaorel', acetaorel_role) + app.add_config_value('omg_specs', {}, 'env', types=[dict]) app.add_role('omgissue', omgissue_role) app.add_role('omgspec', omgspec_role) diff --git a/docs/sphinx_extensions/version_info.py b/docs/sphinx_extensions/version_info.py index 7555a209f9a..b66d334333b 100755 --- a/docs/sphinx_extensions/version_info.py +++ b/docs/sphinx_extensions/version_info.py @@ -2,6 +2,7 @@ import re from pathlib import Path +import configparser opendds_root_path = Path(__file__).parent.parent.parent @@ -9,9 +10,6 @@ with (opendds_root_path / 'dds/Version.h').open() as f: version_file = f.read() -with (opendds_root_path / 'configure').open() as f: - configure_file = f.read() - class VersionInfo: @@ -34,30 +32,6 @@ def get(kind, name): return cast(m[1]) raise KeyError('Could not find ' + macro) - @staticmethod - def configure_get(kind, name): - if kind is bool: - regex = r'([01])' - cast = lambda v: bool(int(v)) - elif kind is int: - regex = r'(\d+)' - cast = int - elif kind is str: - regex = r'["\'](.*)["\']' - cast = lambda v: v - else: - raise TypeError('Unexpected kind: ' + repr(kind)) - m = re.search(r'my \${} = {};'.format(name, regex), configure_file) - if m: - return cast(m[1]) - raise KeyError('Could not find ' + name) - - @staticmethod - def ace_tao_ver(ace_ver): - parts = ace_ver.split('.') - tao_ver = '.'.join([str(int(parts[0]) - 4)] + parts[1:3]) - return f'ACE {ace_ver}/TAO {tao_ver}' - def __init__(self): self.version = self.get(str, 'version') self.is_release = self.get(bool, 'is_release') @@ -73,11 +47,16 @@ def __init__(self): fmt_str += '.{micro}' self.tag = fmt_str.format(**vparts) - self.ace6tao2_version = self.ace_tao_ver(self.configure_get(str, 'doc_tao2_version')) - self.ace7tao3_version = self.ace_tao_ver(self.configure_get(str, 'doc_tao3_version')) + ini = configparser.ConfigParser(interpolation=None) + ini.read(opendds_root_path / 'acetao.ini') + for sec in [ini[sn] for sn in ini.sections()]: + for k in sec.keys(): + name = f'{sec.name}_{k}'.replace('-', '_').replace('.', '_') + setattr(self, name, sec[k]) if __name__ == '__main__': - print(vars(VersionInfo())) + for k, v in vars(VersionInfo()).items(): + print(k, '=', repr(v)) # vim: expandtab:ts=4:sw=4 diff --git a/tools/scripts/gitrelease.pl b/tools/scripts/gitrelease.pl index d448960b426..db5da1ed07b 100755 --- a/tools/scripts/gitrelease.pl +++ b/tools/scripts/gitrelease.pl @@ -155,6 +155,10 @@ sub help { " --update-authors Just update the AUTHORS files like in a release.\n" . " This doesn't run any release steps and doesn't require\n" . " the WORKSPACE or VERSION arguments.\n" . + " --update-ace-tao Update acetao.ini to the latest ACE/TAO releases.\n" . + " This doesn't run any release steps or require the\n" . + " WORKSPACE or VERSION arguments, but does require the\n" . + " GITHUB_TOKEN environment variable\n" . "\n" . "Environment Variables:\n" . " GITHUB_TOKEN GitHub token with repo access to publish release on\n" . @@ -1048,6 +1052,104 @@ sub get_releases { return \@sorted; } +sub update_ace_tao { + my $settings = dclone(shift()); # Clone so we have different pithub + $settings->{pithub} = $settings->{pithub}->new(user => 'DOCGroup', repo => 'ACE_TAO'); + + my @arc_exts = ('zip', 'tar.gz', 'tar.bz2'); + my @ace_tao_versions = ( + { + name => 'ace6tao2', + min => parse_version('6.5.0'), + }, + { + name => 'ace7tao3', + min => parse_version('7.1.0'), + }, + ); + + # Get all the ACE/TAO releases + my $release_list = get_github_releases($settings); + my @releases = (); + while (my $release = $release_list->next) { + next if $release->{prerelease}; + next if ($release->{tag_name} !~ /^ACE\+TAO-(\d+_\d+_\d+)$/); + my $ver = $1; + $ver =~ s/_/./g; + push(@releases, { + version => parse_version($ver), + release => $release, + }); + } + my @sorted = sort { version_cmp($b->{version}, $a->{version}) } @releases; + + for my $ace_tao_version (@ace_tao_versions) { + # Find versions that match ace_tao_versions. This is the first one that's + # less than MAJOR.MINOR+1.MICRO. + my $min = $ace_tao_version->{min}; + my $plus = $min->{minor} + 1; + my $max = parse_version("$min->{major}.$plus.$min->{micro}"); + for my $r (@sorted) { + if (version_lesser($r->{version}, $max)) { + my $version = $r->{version}->{string}; + $ace_tao_version->{version} = $version; + $ace_tao_version->{url} = $r->{release}->{html_url}; + + # Get the filenames, URLs, and checksums for each asset. + my $prefix = quotemeta("ACE+TAO-src-$version."); + for my $asset (@{$r->{release}->{assets}}) { + next if ($asset->{name} !~ /^$prefix(.*)$/); + my $ext = $1; + my $ext_re = quotemeta($ext); + if (grep(/^$ext_re$/, @arc_exts)) { + $ace_tao_version->{"$ext-filename"} = $asset->{name}; + $ace_tao_version->{"$ext-url"} = $asset->{browser_download_url}; + } + elsif ($ext =~ /^(.*)\.md5$/) { + my $about_ext = $1; + my $about_ext_re = quotemeta($about_ext); + if (grep(/^$about_ext_re$/, @arc_exts)) { + my $content; + if (!download($asset->{browser_download_url}, content_ref => \$content)) { + die("Couldn't get file from $asset->{browser_download_url}"); + } + $ace_tao_version->{"$about_ext-md5"} = substr($content, 0, 32); + } + else { + print("Ignored $asset->{name}\n"); + } + } + else { + print("Ignored $asset->{name}\n"); + } + } + + last; + } + } + } + + # Print the INI file + my $ini_path = 'acetao.ini'; + open(my $ini_fh, '>', $ini_path) or die("Could not open $ini_path: $!"); + print $ini_fh ( + "# This file contains the common info for ACE/TAO releases. Insead of editing\n", + "# this directly, run:\n", + "# GITHUB_TOKEN=... ./tools/scripts/gitrelease.pl --update-ace-tao\n"); + for my $ace_tao_version (@ace_tao_versions) { + print $ini_fh ("\n[$ace_tao_version->{name}]\n", + "version=$ace_tao_version->{version}\n", + "url=$ace_tao_version->{url}\n"); + for my $ext (@arc_exts) { + for my $suffix ('filename', 'url', 'md5') { + my $key = "$ext-$suffix"; + print $ini_fh ("$key=$ace_tao_version->{$key}\n"); + } + } + } + close($ini_fh); +} + ############################################################################ # Start of Release Steps @@ -2806,6 +2908,7 @@ sub remedy_release_occurred_flag { my $sftp_base_dir = $default_sftp_base_dir; my $upload_shapes_demo = 0; my $update_authors = 0; +my $update_ace_tao = 0; GetOptions( 'help' => \$print_help, @@ -2830,6 +2933,7 @@ sub remedy_release_occurred_flag { 'skip-github=i' => \$skip_github, 'upload-shapes-demo' => \$upload_shapes_demo, 'update-authors' => \$update_authors, + 'update-ace-tao' => \$update_ace_tao, ) or arg_error("Invalid option"); if ($print_help) { @@ -2855,7 +2959,7 @@ sub remedy_release_occurred_flag { my $base_name = ""; my $release_branch = ""; -my $ignore_args = $update_authors || $print_list_all; +my $ignore_args = $update_authors || $print_list_all || $update_ace_tao; if ($ignore_args) { $parsed_version = $zero_version; $parsed_next_version = $zero_version; @@ -2986,9 +3090,11 @@ sub remedy_release_occurred_flag { force_not_highest_version => $force_not_highest_version, ); -if (!$ignore_args) { +if (!$ignore_args || $update_ace_tao) { $global_settings{pithub} = new_pithub(\%global_settings); +} +if (!$ignore_args) { check_workspace(\%global_settings); if ($mock) { @@ -3442,6 +3548,9 @@ sub run_step { elsif ($update_authors) { remedy_authors(\%global_settings); } +elsif ($update_ace_tao) { + update_ace_tao(\%global_settings); +} elsif (!$alt && ($ignore_args || ($workspace && $parsed_version))) { my @steps_to_do; my $no_steps = scalar(@release_steps); diff --git a/tools/scripts/modules/ini.pm b/tools/scripts/modules/ini.pm new file mode 100755 index 00000000000..e063972f7ac --- /dev/null +++ b/tools/scripts/modules/ini.pm @@ -0,0 +1,120 @@ +#!/usr/bin/env perl + +package ini; + +use strict; +use warnings; + +use Getopt::Long qw/GetOptions/; + +require Exporter; +our @ISA = qw(Exporter); +our @EXPORT_OK = qw( + read_ini_file + get_ini_value +); + +sub die_with_stack_trace { + my $i = 1; + print STDERR ("ERROR: ", @_, " STACK TRACE:\n"); + while (my @call_details = (caller($i++)) ){ + print STDERR ("ERROR: STACK TRACE[", $i - 2, "] " . + "$call_details[1]:$call_details[2] in function $call_details[3]\n"); + } + die(); +} + +sub print_usage { + my $fh = shift() // *STDERR; + + print $fh ("Usage: ini.pm INI_FILE [--join JOINER] [--dump] [KEY...]\n"); +} + +sub print_help { + print_usage(*STDOUT); +} + +sub invalid_args { + print STDERR ("ERROR: Invalid arguments, ", shift(), "\n"); + print_usage(*STDOUT); + exit(1); +} + +sub read_ini_file { + my $path = shift(); + + my @section_names; + my %sections; + my $section; + open(my $fh, $path) or die_with_stack_trace("Couldn't open \"$path\": $!\nStopped"); + while (my $line = <$fh>) { + $line =~ s/\s$//; + if ($line =~ /\[(\w+)\]/) { + push(@section_names, $1); + $section = {}; + $sections{$1} = $section; + } + elsif ($section && $line =~ /([^=#]+)=(.*)/) { + $section->{$1} = $2; + } + } + + return \@section_names, \%sections; +} + +sub get_ini_value { + my $section_names = shift(); + my $sections = shift(); + my $key = shift(); + my $dump = shift(); + + my @results; + for my $section_name (@{$section_names}) { + my $section = $sections->{$section_name}; + for my $key_name (keys(%{$section})) { + my $full_key = "$section_name/$key_name"; + if ($full_key =~ /$key/) { + my $result = $section->{$key_name}; + if ($dump) { + $result = "$full_key => $result"; + } + push(@results, $result); + } + } + } + + return @results; +} + +sub main { + my $help = 0; + my $join = "\n"; + my $dump = 0; + if (!GetOptions( + 'h|help' => \$help, + 'join=s' => \$join, + 'dump' => \$dump, + )) { + print_usage(); + exit(1); + } + if ($help) { + print_help(); + exit(0); + } + + my $ini_file = shift(@ARGV) or invalid_args("must pass INI_FILE"); + + my ($section_names, $sections) = read_ini_file($ini_file); + + my @keys = @ARGV ? @ARGV : ".*/.*"; + my @results; + for my $key (@keys) { + push(@results, get_ini_value($section_names, $sections, $key, $dump || !@ARGV)); + } + print(join($join, @results), "\n"); +} + +main() if not caller(); + +1;