From bd25511f5a4629bad3fbce8c0aafa41d6d73554a Mon Sep 17 00:00:00 2001 From: Alejandro R Mosteo Date: Fri, 14 Jun 2024 09:34:09 +0200 Subject: [PATCH] Tag builds done through `alr build` with unique build string (#1530) * Replace version build with current commit hash * Detect dirtiness in . to flag it too in version * Fix for line terminators on Windows * Self-review * Ada version * Windows dispatcher script * Patch from the dev/build.sh script too --- alire.toml | 23 ++++ dev/build.sh | 12 +- scripts/ci-github.sh | 17 ++- scripts/version-patcher.ps1 | 25 ++++ scripts/version-patcher.py | 55 ++++++++ scripts/version-patcher.sh | 31 +++++ src/alire/alire-version.ads | 4 + support/version_patcher/.gitignore | 4 + support/version_patcher/alire.toml | 12 ++ .../config/version_patcher_config.ads | 20 +++ .../config/version_patcher_config.gpr | 50 ++++++++ .../config/version_patcher_config.h | 20 +++ .../version_patcher/src/version_patcher.adb | 117 ++++++++++++++++++ support/version_patcher/version_patcher.gpr | 22 ++++ 14 files changed, 404 insertions(+), 8 deletions(-) create mode 100644 scripts/version-patcher.ps1 create mode 100644 scripts/version-patcher.py create mode 100755 scripts/version-patcher.sh create mode 100644 support/version_patcher/.gitignore create mode 100644 support/version_patcher/alire.toml create mode 100644 support/version_patcher/config/version_patcher_config.ads create mode 100644 support/version_patcher/config/version_patcher_config.gpr create mode 100644 support/version_patcher/config/version_patcher_config.h create mode 100644 support/version_patcher/src/version_patcher.adb create mode 100644 support/version_patcher/version_patcher.gpr diff --git a/alire.toml b/alire.toml index 81deb0d6a..7cee2636d 100644 --- a/alire.toml +++ b/alire.toml @@ -94,3 +94,26 @@ commit = "f607a63b714f09bbf6126de9851cbc21cf8666c9" [pins.toml_slicer] url = "https://github.com/mosteo/toml_slicer" branch = "alire" + +# To disable version updating, export ALR_VERSION_DONT_PATCH with any value + +# Before building, we add the commit to the version, for unique identification: +[[actions]] +[actions.'case(os)'.windows] +type = "pre-build" +command = ["pwsh", "scripts/version-patcher.ps1"] + +[actions.'case(os)'.'...'] +type = "pre-build" +command = ["scripts/version-patcher.sh"] + +# Afterwards we leave an extra note, so people manually building don't use a +# misleading commit. +[[actions]] +[actions.'case(os)'.windows] +type = "post-build" +command = ["pwsh", "scripts/version-patcher.ps1", "_or_later"] + +[actions.'case(os)'.'...'] +type = "post-build" +command = ["scripts/version-patcher.sh", "_or_later"] \ No newline at end of file diff --git a/dev/build.sh b/dev/build.sh index 79e79e15d..934986401 100755 --- a/dev/build.sh +++ b/dev/build.sh @@ -1,12 +1,16 @@ #!/usr/bin/env bash # Import reusable bits -pushd $( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) > /dev/null +pushd "$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" > /dev/null || exit 1 . functions.sh -popd > /dev/null +popd > /dev/null || exit 1 ALIRE_BUILD_JOBS="${ALIRE_BUILD_JOBS:-0}" -export ALIRE_OS=$(get_OS) +ALIRE_OS=$(get_OS); export ALIRE_OS + +scripts/version-patcher.sh echo "Building with ALIRE_OS=$ALIRE_OS..." -gprbuild "-j$ALIRE_BUILD_JOBS" -r -p -P `dirname $0`/../alr_env.gpr "$@" +gprbuild "-j$ALIRE_BUILD_JOBS" -r -p -P "$(dirname $0)/../alr_env.gpr" "$@" + +scripts/version-patcher.sh _or_later \ No newline at end of file diff --git a/scripts/ci-github.sh b/scripts/ci-github.sh index 400905ab4..845cd96d0 100755 --- a/scripts/ci-github.sh +++ b/scripts/ci-github.sh @@ -9,10 +9,19 @@ set -o nounset export PATH+=:${PWD}/bin # Import reusable bits -pushd $( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) +pushd "$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" . ../dev/functions.sh popd +# Mark location safe to assuage git if necessary (happens in some distros) +if git status 2>&1 | grep -q "dubious ownership"; then + echo "Marking $PWD as safe for git" + git config --global --add safe.directory "$PWD" +fi + +# Patch version +scripts/version-patcher.sh + # Build alr export ALIRE_OS=$(get_OS) gprbuild -j0 -p -P alr_env @@ -37,7 +46,7 @@ echo ............................ # Set up index if not default: if [ "${INDEX:-}" != "" ]; then - echo Setting default index to: $INDEX + echo Setting default index to: "$INDEX" alr index --name default --add "$INDEX" fi @@ -65,8 +74,8 @@ fi echo PYTHON installing testsuite dependencies... -echo Python version: $($run_python --version) -echo Pip version: $($run_pip --version) +echo "Python version: $($run_python --version)" +echo "Pip version: $($run_pip --version)" $run_pip install --upgrade -r requirements.txt echo Python search paths: diff --git a/scripts/version-patcher.ps1 b/scripts/version-patcher.ps1 new file mode 100644 index 000000000..1d1408bae --- /dev/null +++ b/scripts/version-patcher.ps1 @@ -0,0 +1,25 @@ +# This script dispatches to the Ada patcher, after building it. + +# Set strict mode for PowerShell to exit on error +$ErrorActionPreference = "Stop" + +$bin = "support/version_patcher/bin/version_patcher.exe" + +# If the binary is already in place, do nothing +if (Test-Path $bin) { + Write-Output "Patcher already built." +} elseif (Get-Command gprbuild -ErrorAction SilentlyContinue) { + Write-Output "Building patcher with gprbuild..." + gprbuild -P support/version_patcher/version_patcher.gpr +} elseif (Get-Command alr -ErrorAction SilentlyContinue) { + Write-Output "Building patcher with alr..." + alr -C (Split-Path $bin) build +} else { + Write-Output "WARNING: No Ada tool available to build patcher, skipping." + exit 0 +} + +& $bin @args + +Write-Output "Resulting version file:" +Get-Content src/alire/alire-version.ads | Select-String "Current_Str" diff --git a/scripts/version-patcher.py b/scripts/version-patcher.py new file mode 100644 index 000000000..7088bb313 --- /dev/null +++ b/scripts/version-patcher.py @@ -0,0 +1,55 @@ +import os +import re +import subprocess +import sys + +def replace_version(filename, build_info): + pattern = r'(Current : constant String := "[^+]+\+)([^"]*)(";)' + + # Depending on the context in which this is run, there may be mix-ups with + # line terminators between the environment detected, github runner, etc... + # So just keep them as they are and that should always work. + + with open(filename, 'rb') as file: + content = file.read().decode() + + # The pattern captures the part between '+' and '";', replacing it with our + # new build information + + new_content = re.sub(pattern, r'\g<1>' + build_info + r'\3', content) + + # A few sanity checks and write if needed + + if new_content == content: + if build_info in content: + print(f"Note: version in {filename} already up to date") + else: + print(f"WARNING: failed to update version in {filename}") + else: + # Ensure the content line terminators are not changed + with open(filename, 'wb') as file: + file.write(new_content.encode()) + + +# If a flag exists, skip any updating, just print a message and exit +if "ALR_VERSION_DONT_PATCH" in os.environ: + print("Note: skipping version update") + sys.exit(0) + +# If there is an argument to the script, retrieve it here and use it as the new +# dirty flag after the commit +if len(sys.argv) > 1: + dirty = sys.argv[1] +else: + # Detect whether the current directory contains changes + if subprocess.call(['git', 'diff-index', '--quiet', 'HEAD', '--']) != 0: + dirty = "_dirty" + else: + dirty = "" + +# Find the short git commit of the repository in the current directory +commit = subprocess.check_output(['git', 'rev-parse', '--short', 'HEAD']).decode('utf-8').strip() + +# Replace the build version part with the short commit hash plus any extra info +print(f"Updating version in src/alire/alire-version.ads to commit {commit}{dirty}...") +replace_version('src/alire/alire-version.ads', commit+dirty) diff --git a/scripts/version-patcher.sh b/scripts/version-patcher.sh new file mode 100755 index 000000000..1b0c07895 --- /dev/null +++ b/scripts/version-patcher.sh @@ -0,0 +1,31 @@ +#!/usr/bin/env bash +# This script dispatches to the Ada patcher, after building it. + +set -o errexit + +# Exit already if the ALR_VERSION_DONT_PATCH flag is defined +if [ "${ALR_VERSION_DONT_PATCH:-unset}" != "unset" ]; then + echo "Skipping version patching..." + exit 0 +fi + +bin=support/version_patcher/bin/version_patcher + +# If the binary is already in place, do nothing +if [ -f $bin ]; then + echo "Patcher already built." +elif (which gprbuild &>/dev/null); then + echo "Building patcher with gprbuild..." + gprbuild -P support/version_patcher/version_patcher.gpr +elif (which alr &>/dev/null); then + echo "Building patcher with alr..." + alr -C "$(dirname $bin)" build +else + echo "WARNING: No Ada tool available to build patcher, skipping." + exit 0 +fi + +$bin "$@" + +echo "Resulting version file:" +cat src/alire/alire-version.ads | grep Current_Str \ No newline at end of file diff --git a/src/alire/alire-version.ads b/src/alire/alire-version.ads index 6a0e4f70d..441a826dc 100644 --- a/src/alire/alire-version.ads +++ b/src/alire/alire-version.ads @@ -12,6 +12,10 @@ private -- Remember to update Alire.Index branch if needed too + -- NOTE: in the following version string, the build part (after '+') will + -- be replaced by `alr build` with the current commit, and appended with + -- "_or_later" after build. + Current_Str : constant String := "2.1-dev"; -- 2.0.0: alr settings refactor and minor fixes -- 2.0.0-rc1: release candidate for 2.0 diff --git a/support/version_patcher/.gitignore b/support/version_patcher/.gitignore new file mode 100644 index 000000000..5866d7bfa --- /dev/null +++ b/support/version_patcher/.gitignore @@ -0,0 +1,4 @@ +/obj/ +/bin/ +/alire/ +/config/ diff --git a/support/version_patcher/alire.toml b/support/version_patcher/alire.toml new file mode 100644 index 000000000..22000e222 --- /dev/null +++ b/support/version_patcher/alire.toml @@ -0,0 +1,12 @@ +name = "version_patcher" +description = "Patches current commit into alire-version.ads" +version = "0.1.0-dev" + +authors = ["Alejandro R. Mosteo"] +maintainers = ["Alejandro R. Mosteo "] +maintainers-logins = ["mosteo"] +licenses = "MIT OR Apache-2.0 WITH LLVM-exception" +website = "" +tags = [] + +executables = ["version_patcher"] diff --git a/support/version_patcher/config/version_patcher_config.ads b/support/version_patcher/config/version_patcher_config.ads new file mode 100644 index 000000000..440dfe5a6 --- /dev/null +++ b/support/version_patcher/config/version_patcher_config.ads @@ -0,0 +1,20 @@ +-- Configuration for version_patcher generated by Alire +pragma Restrictions (No_Elaboration_Code); +pragma Style_Checks (Off); + +package Version_Patcher_Config is + pragma Pure; + + Crate_Version : constant String := "0.1.0-dev"; + Crate_Name : constant String := "version_patcher"; + + Alire_Host_OS : constant String := "linux"; + + Alire_Host_Arch : constant String := "x86_64"; + + Alire_Host_Distro : constant String := "ubuntu"; + + type Build_Profile_Kind is (release, validation, development); + Build_Profile : constant Build_Profile_Kind := development; + +end Version_Patcher_Config; diff --git a/support/version_patcher/config/version_patcher_config.gpr b/support/version_patcher/config/version_patcher_config.gpr new file mode 100644 index 000000000..df412e7cb --- /dev/null +++ b/support/version_patcher/config/version_patcher_config.gpr @@ -0,0 +1,50 @@ +-- Configuration for version_patcher generated by Alire +abstract project Version_Patcher_Config is + Crate_Version := "0.1.0-dev"; + Crate_Name := "version_patcher"; + + Alire_Host_OS := "linux"; + + Alire_Host_Arch := "x86_64"; + + Alire_Host_Distro := "ubuntu"; + Ada_Compiler_Switches := External_As_List ("ADAFLAGS", " "); + Ada_Compiler_Switches := Ada_Compiler_Switches & + ( + "-Og" -- Optimize for debug + ,"-ffunction-sections" -- Separate ELF section for each function + ,"-fdata-sections" -- Separate ELF section for each variable + ,"-g" -- Generate debug info + ,"-gnatwa" -- Enable all warnings + ,"-gnatw.X" -- Disable warnings for No_Exception_Propagation + ,"-gnatVa" -- All validity checks + ,"-gnaty3" -- Specify indentation level of 3 + ,"-gnatya" -- Check attribute casing + ,"-gnatyA" -- Use of array index numbers in array attributes + ,"-gnatyB" -- Check Boolean operators + ,"-gnatyb" -- Blanks not allowed at statement end + ,"-gnatyc" -- Check comments + ,"-gnaty-d" -- Disable check no DOS line terminators present + ,"-gnatye" -- Check end/exit labels + ,"-gnatyf" -- No form feeds or vertical tabs + ,"-gnatyh" -- No horizontal tabs + ,"-gnatyi" -- Check if-then layout + ,"-gnatyI" -- check mode IN keywords + ,"-gnatyk" -- Check keyword casing + ,"-gnatyl" -- Check layout + ,"-gnatym" -- Check maximum line length + ,"-gnatyn" -- Check casing of entities in Standard + ,"-gnatyO" -- Check that overriding subprograms are explicitly marked as such + ,"-gnatyp" -- Check pragma casing + ,"-gnatyr" -- Check identifier references casing + ,"-gnatyS" -- Check no statements after THEN/ELSE + ,"-gnatyt" -- Check token spacing + ,"-gnatyu" -- Check unnecessary blank lines + ,"-gnatyx" -- Check extra parentheses + ,"-gnatW8" -- UTF-8 encoding for wide characters + ); + + type Build_Profile_Kind is ("release", "validation", "development"); + Build_Profile : Build_Profile_Kind := "development"; + +end Version_Patcher_Config; diff --git a/support/version_patcher/config/version_patcher_config.h b/support/version_patcher/config/version_patcher_config.h new file mode 100644 index 000000000..638f7552b --- /dev/null +++ b/support/version_patcher/config/version_patcher_config.h @@ -0,0 +1,20 @@ +/* Configuration for version_patcher generated by Alire */ +#ifndef VERSION_PATCHER_CONFIG_H +#define VERSION_PATCHER_CONFIG_H + +#define CRATE_VERSION "0.1.0-dev" +#define CRATE_NAME "version_patcher" + +#define ALIRE_HOST_OS "linux" + +#define ALIRE_HOST_ARCH "x86_64" + +#define ALIRE_HOST_DISTRO "ubuntu" + +#define BUILD_PROFILE_RELEASE 1 +#define BUILD_PROFILE_VALIDATION 2 +#define BUILD_PROFILE_DEVELOPMENT 3 + +#define BUILD_PROFILE 3 + +#endif diff --git a/support/version_patcher/src/version_patcher.adb b/support/version_patcher/src/version_patcher.adb new file mode 100644 index 000000000..b91768447 --- /dev/null +++ b/support/version_patcher/src/version_patcher.adb @@ -0,0 +1,117 @@ +with Ada.Command_Line; use Ada.Command_Line; +with Ada.Directories; +with Ada.Environment_Variables; use Ada.Environment_Variables; +with Ada.Strings.Unbounded; use Ada.Strings.Unbounded; +with Ada.Text_IO; + +with GNAT.Expect; +with GNAT.OS_Lib; + +--------------------- +-- Version_Patcher -- +--------------------- + +procedure Version_Patcher is + + --------------------- + -- Replace_Version -- + --------------------- + + procedure Replace_Version (Filename : String; Build_Info : String) is + F : Ada.Text_IO.File_Type; + O : Ada.Text_IO.File_Type; + use Ada.Text_IO; + + Target : constant String := "Current_Str : constant String :="; + begin + Open (F, In_File, Filename); + Create (O, Out_File, Filename & ".new"); + while not End_Of_File (F) loop + declare + Line : constant String := Get_Line (F); + begin + if (for some I in Line'Range => + I + Target'Length - 1 <= Line'Last and then + Line (I .. I + Target'Length - 1) = Target) + then + declare + Quotes_Seen : Boolean := False; + begin + for Char of Line loop + if Char = '"' and then not Quotes_Seen then + Quotes_Seen := True; + Put (O, Char); + elsif (Char = '"' and then Quotes_Seen) + or else Char = '+' + then + Put_Line (O, "+" & Build_Info & '"' & ";"); + exit; + else + Put (O, Char); + end if; + end loop; + end; + else + Put_Line (O, Line); + end if; + end; + end loop; + + Close (F); + Close (O); + + Ada.Directories.Delete_File (Filename); + Ada.Directories.Rename (Filename & ".new", Filename); + + end Replace_Version; + + ----------------- + -- Git_Command -- + ----------------- + + type Result is record + Output : Unbounded_String; + Code : Integer; + end record; + + function Git_Command (Args : String) return Result is + use GNAT.OS_Lib; + Arg_List : constant Argument_List_Access := + Argument_String_To_List (Args); + Code : aliased Integer; + Output : constant String + := GNAT.Expect.Get_Command_Output + ("git", Arg_List.all, "", Code'Access, True); + begin + return (To_Unbounded_String (Output), Code); + end Git_Command; + +begin + if Exists ("ALR_VERSION_DONT_PATCH") then + Ada.Text_IO.Put_Line ("Note: skipping version update"); + return; + end if; + + declare + Dirty : constant String + := (if Argument_Count > 0 then + Argument (1) + elsif Git_Command ("diff-index --quiet HEAD --").Code /= 0 then + "_dirty" + else + ""); + Commit_Result : constant Result := + Git_Command ("rev-parse --short HEAD"); + Commit : constant String := To_String (Commit_Result.Output); + begin + if Commit_Result.Code /= 0 then + raise Constraint_Error with + "Git error while trying to get commit:" + & Commit_Result.Code'Image; + end if; + Ada.Text_IO.Put_Line + ("Updating version in src/alire/alire-version.ads to commit " + & Commit & Dirty & "..."); + Replace_Version ("src/alire/alire-version.ads", Commit & Dirty); + end; +end Version_Patcher; diff --git a/support/version_patcher/version_patcher.gpr b/support/version_patcher/version_patcher.gpr new file mode 100644 index 000000000..3a496900e --- /dev/null +++ b/support/version_patcher/version_patcher.gpr @@ -0,0 +1,22 @@ +with "config/version_patcher_config.gpr"; +project Version_Patcher is + + for Source_Dirs use ("src/", "config/"); + for Object_Dir use "obj/" & Version_Patcher_Config.Build_Profile; + for Create_Missing_Dirs use "True"; + for Exec_Dir use "bin"; + for Main use ("version_patcher.adb"); + + package Compiler is + for Default_Switches ("Ada") use Version_Patcher_Config.Ada_Compiler_Switches; + end Compiler; + + package Binder is + for Switches ("Ada") use ("-Es"); -- Symbolic traceback + end Binder; + + package Install is + for Artifacts (".") use ("share"); + end Install; + +end Version_Patcher;