diff --git a/.github/download-ROOT-source.rb b/.github/download-ROOT-source.rb deleted file mode 100755 index 955da806..00000000 --- a/.github/download-ROOT-source.rb +++ /dev/null @@ -1,50 +0,0 @@ -#!/usr/bin/env ruby -# download the ROOT source code - -if ARGV.empty? - $stderr.puts "USAGE: #{$0} [tag]" - exit 2 -end -TAG = ARGV.first -REPO = 'root-project/ROOT' - -info = Hash.new -case TAG -when 'latest' - info = { - :api_url => "https://api.github.com/repos/#{REPO}/releases/latest", - :jq_filter => '.tarball_url', - } -else - info = { - :api_url => "https://api.github.com/repos/#{REPO}/tags", - :jq_filter => ".[] | select (.name==\"#{TAG}\") | .tarball_url", - } -end - -api_args = [ - '--silent', - '-L', - '-H "Accept: application/vnd.github+json"', - '-H "X-GitHub-Api-Version: 2022-11-28"', - info[:api_url], -] -cmd = "curl #{api_args.join ' '} | jq -r '#{info[:jq_filter]}'" -puts """API call: -#{'='*82} -#{cmd} -#{'='*82}""" - -error = Proc.new do |msg| - $stderr.puts "ERROR: #{msg}" - exit 1 -end - -payload = `#{cmd}` -error.call 'GitHub API call failure' unless $?.success? -error.call "GitHub API call returned empty string, perhaps tag '#{TAG}' does not exist?" if payload.empty? -url = payload.chomp -puts "TARBALL URL = #{url}" - -puts "Downloading ROOT version '#{TAG}'..." -exec "wget -nv --no-check-certificate --output-document root.tar.gz #{url}" diff --git a/.github/download-ROOT-source.sh b/.github/download-ROOT-source.sh new file mode 100755 index 00000000..c83ca582 --- /dev/null +++ b/.github/download-ROOT-source.sh @@ -0,0 +1,27 @@ +#!/usr/bin/env bash +# download the ROOT source code + +set -euo pipefail + +if [ $# -ne 1 ]; then + echo "USAGE: $0 [TAG]" >&2 + exit 1 +fi + +tag=$1 +curl_cmd='curl --silent -L -H "Accept: application/vnd.github+json" -H "X-GitHub-Api-Version: 2022-11-28"' + +echo "Download ROOT tag '$tag' from:" +case $tag in + latest) + url=$($curl_cmd https://api.github.com/repos/root-project/ROOT/releases/latest | jq -r '.tarball_url') + ;; + *) + url=$($curl_cmd https://api.github.com/repos/root-project/ROOT/tags | jq -r '.[] | select (.name=="'$tag'") | .tarball_url') + ;; +esac + +echo " $url" +[ -z "$url" ] && echo "ERROR: GitHub API call returned empty string, perhaps tag '$tag' does not exist?" && exit 1 +echo "downloading ..." +wget -nv --no-check-certificate --output-document root.tar.gz $url diff --git a/.github/install-dependency-packages.sh b/.github/install-dependency-packages.sh index 318493e9..7e18e4ca 100755 --- a/.github/install-dependency-packages.sh +++ b/.github/install-dependency-packages.sh @@ -7,7 +7,6 @@ set -e ############################## GENERAL_PACKAGE_LIST_LINUX=( python - ruby gcc gcc-fortran clang diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ca8c2e30..d44a3f1d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -113,7 +113,7 @@ jobs: - name: download ROOT source code if: ${{ steps.cache.outputs.cache-hit != 'true' }} run: | - iguana_src/.github/download-ROOT-source.rb ${{ steps.key.outputs.root_version }} + iguana_src/.github/download-ROOT-source.sh ${{ steps.key.outputs.root_version }} tar xf $(ls -t *.gz | head -n1) ls -t mv -v $(ls -td root-* | head -n1) root_src @@ -199,6 +199,51 @@ jobs: key: ${{ steps.key.outputs.key }}---${{ matrix.root_dep }} path: hipo.tar.zst + build_ruby_minver: + name: Build Minver Ruby + runs-on: ${{ inputs.runner }} + container: + image: ${{ inputs.container }} + outputs: + key: ${{ steps.key.outputs.key }} + steps: + - name: checkout iguana + if: ${{ inputs.verset == 'minver' }} + uses: actions/checkout@v4 + with: + path: iguana_src + - name: key + if: ${{ inputs.verset == 'minver' }} + id: key + run: | + ver=$(iguana_src/meson/minimum-version.sh ruby) + echo ver=$ver >> $GITHUB_OUTPUT + echo key=ruby---${{ inputs.id }}---${ver}---$(date +%Y-week%U) >> $GITHUB_OUTPUT + - uses: actions/cache/restore@v4 + if: ${{ inputs.verset == 'minver' }} + id: cache + with: + key: ${{ steps.key.outputs.key }} + path: ruby.tar.zst + lookup-only: true + - name: build ruby + if: ${{ steps.cache.outputs.cache-hit != 'true' && inputs.verset == 'minver' }} + run: | + iguana_src/.github/install-dependency-packages.sh ${{ inputs.runner }} ${{ inputs.verset }} + export RBENV_ROOT=$(pwd)/.rbenv + git clone https://github.com/rbenv/rbenv.git $RBENV_ROOT + eval "$($RBENV_ROOT/bin/rbenv init - bash)" + git clone https://github.com/rbenv/ruby-build.git $(rbenv root)/plugins/ruby-build + rbenv install ${{ steps.key.outputs.ver }} + rbenv global ${{ steps.key.outputs.ver }} + tar cavf ruby.tar.zst .rbenv + - uses: actions/cache/save@v4 + if: ${{ steps.cache.outputs.cache-hit != 'true' && inputs.verset == 'minver' }} + id: cache_save + with: + key: ${{ steps.key.outputs.key }} + path: ruby.tar.zst + # build and test Iguana ######################################################### @@ -208,6 +253,7 @@ jobs: - download_test_data - build_hipo - build_root + - build_ruby_minver runs-on: ${{ inputs.runner }} container: image: ${{ inputs.container }} @@ -235,6 +281,26 @@ jobs: ###### system - name: install dependency packages run: iguana_src/.github/install-dependency-packages.sh ${{ inputs.runner }} ${{ inputs.verset }} + ###### ruby + - name: install ruby from linux package manager + if: ${{ inputs.id != 'macOS' && inputs.verset != 'minver' }} + run: pacman -S --noconfirm ruby + - name: install ruby from cached minver build + if: ${{ inputs.id != 'macOS' && inputs.verset == 'minver' }} + uses: actions/cache/restore@v4 + with: + key: ${{ needs.build_ruby_minver.outputs.key }} + path: ruby.tar.zst + - name: source ruby from cached minver build + if: ${{ inputs.id != 'macOS' && inputs.verset == 'minver' }} + run: | + tar xf ruby.tar.zst + export RBENV_ROOT=$(pwd)/.rbenv + echo RBENV_ROOT=$RBENV_ROOT >> $GITHUB_ENV + eval "$($RBENV_ROOT/bin/rbenv init - bash)" + echo "ruby --version:" + ruby --version + echo PATH=$PATH >> $GITHUB_ENV ###### python bindings - name: install python bindings dependencies if: ${{ matrix.id == 'python' }} @@ -289,6 +355,7 @@ jobs: echo '| --- | --- |' >> $GITHUB_STEP_SUMMARY echo "| \`hipo\` | ${{ env.hipo_ref }} |" >> $GITHUB_STEP_SUMMARY echo "| \`root\` | $(root --version 2>&1 | head -n1) |" >> $GITHUB_STEP_SUMMARY + echo "| \`ruby\` | $(ruby --version) |" >> $GITHUB_STEP_SUMMARY cat pkg_summary.md >> $GITHUB_STEP_SUMMARY ### build - name: meson setup diff --git a/doc/setup.md b/doc/setup.md index 55b5092c..4b186775 100644 --- a/doc/setup.md +++ b/doc/setup.md @@ -52,6 +52,12 @@ cmake --build build-hipo cmake --install build-hipo ``` +### 🟩 Optional: `ruby`: programming language + +- Likely available in your package manager, likely as `ruby` +- This is only needed if you intend to use Iguana with languages other than C++ + - Bindings to other programming languages are generated by [`chameleon`](/src/chameleon), a local Ruby program + ### 🟩 Optional: `ROOT`: Data analysis framework - ROOT is an **optional** dependency: some algorithms and test code depends on ROOT, but if you do not diff --git a/meson.build b/meson.build index 780cef9d..a37d5031 100644 --- a/meson.build +++ b/meson.build @@ -21,29 +21,40 @@ project_description = 'Implementation Guardian of Analysis Algorithms' # initialize binding languanges add_languages('fortran', native: false, required: get_option('bind_fortran')) +use_chameleon = get_option('bind_fortran') # meson modules pkg = import('pkgconfig') fs = import('fs') # resolve dependencies +prog_minver = find_program('meson' / 'minimum-version.sh') # NOTE: those that are typically installed by package managers should use `meson/minimum-version.sh` fmt_dep = dependency( 'fmt', method: 'pkg-config', - version: run_command('meson' / 'minimum-version.sh', 'fmt', check: true).stdout().strip(), + version: '>=' + run_command(prog_minver, 'fmt', check: true).stdout().strip(), ) yamlcpp_dep = dependency( 'yaml-cpp', method: 'pkg-config', - version: run_command('meson' / 'minimum-version.sh', 'yaml-cpp', check: true).stdout().strip(), + version: '>=' + run_command(prog_minver, 'yaml-cpp', check: true).stdout().strip(), +) +hipo_dep = dependency( + 'hipo4', + method: 'pkg-config', + version: '>=4.1.0' ) -hipo_dep = dependency('hipo4', method: 'pkg-config', version: '>=4.1.0') ROOT_dep = dependency( 'ROOT', required: get_option('z_require_root'), method: 'cmake', - version: run_command('meson' / 'minimum-version.sh', 'ROOT', check: true).stdout().strip(), + version: '>=' + run_command(prog_minver, 'ROOT', check: true).stdout().strip(), +) +prog_ruby = find_program( + 'ruby', + version: '>=' + run_command(prog_minver, 'ruby', check: true).stdout().strip(), + required: use_chameleon, ) # list of dependencies @@ -169,7 +180,6 @@ add_project_arguments( ) # start chameleon -use_chameleon = get_option('bind_fortran') if use_chameleon subdir('src/chameleon') endif diff --git a/meson/minimum-version.sh b/meson/minimum-version.sh index 823aef85..1d955ffc 100755 --- a/meson/minimum-version.sh +++ b/meson/minimum-version.sh @@ -1,12 +1,12 @@ #!/usr/bin/env bash if [ $# -eq 0 ]; then - echo """USAGE: $0 [package] [command(default=meson)] + echo """USAGE: $0 [package] [command(default=ver)] package: the package name; since this varies between package manager repositories, we prefer to use the name from that used by the CI - command: meson return a string for meson function \`dependency()\` + command: ver return the minimum version number ALA return a URL for the Arch Linux Archive (ALA), for CI https://archive.archlinux.org/ @@ -16,7 +16,7 @@ if [ $# -eq 0 ]; then exit 2 fi dep=$1 -[ $# -ge 2 ] && cmd=$2 || cmd=meson +[ $# -ge 2 ] && cmd=$2 || cmd=ver not_used() { [ "$cmd" = "$1" ] && echo "ERROR: command '$cmd' is not used for '$dep'" >&2 && exit 1 @@ -26,20 +26,25 @@ not_used() { case $dep in fmt) - result_meson='>=9.1.0' + result_ver='9.1.0' result_ala='https://archive.archlinux.org/packages/f/fmt/fmt-9.1.0-4-x86_64.pkg.tar.zst' not_used 'tag' ;; yaml-cpp) - result_meson='>=0.7.0' + result_ver='0.7.0' result_ala='https://archive.archlinux.org/packages/y/yaml-cpp/yaml-cpp-0.7.0-2-x86_64.pkg.tar.zst' not_used 'tag' ;; root|ROOT) - result_meson='>=6.28.12' + result_ver='6.28.12' result_tag='v6-28-12' not_used 'ALA' ;; + ruby) + result_ver='2.7.2' + not_used 'ALA' + not_used 'tag' + ;; *) echo "ERROR: dependency '$dep' is unknown" >&2 exit 1 @@ -49,9 +54,9 @@ esac ############################################# case $cmd in - meson) echo $result_meson ;; - ALA) echo $result_ala ;; - tag) echo $result_tag ;; + ver) echo $result_ver ;; + ALA) echo $result_ala ;; + tag) echo $result_tag ;; *) echo "ERROR: command '$cmd' is unknown" >&2 exit 1 diff --git a/src/chameleon/meson.build b/src/chameleon/meson.build index 44163b94..ebb37fde 100644 --- a/src/chameleon/meson.build +++ b/src/chameleon/meson.build @@ -4,5 +4,4 @@ chameleon_sources = files( 'src' / 'bind_c.rb', ) -ruby = find_program('ruby', version: '>=3.0.0', required: use_chameleon) -chameleon_gen = find_program(chameleon_sources[0], required: use_chameleon) +prog_chameleon = find_program(chameleon_sources[0], required: use_chameleon) diff --git a/src/iguana/algorithms/meson.build b/src/iguana/algorithms/meson.build index 37186cf4..08fc9ac6 100644 --- a/src/iguana/algorithms/meson.build +++ b/src/iguana/algorithms/meson.build @@ -132,7 +132,7 @@ foreach algo : algo_dict input: [ algo_dir / 'Action.yaml', chameleon_sources ], output: [ target_name + '_bind.cc' ], command: [ - chameleon_gen, + prog_chameleon, '--input', '@INPUT0@', '--output', '@OUTDIR@', '--prefix', target_name,