[{"id":25187,"web_url":"https://patchwork.libcamera.org/comment/25187/","msgid":"<20220930154322.kjklx55ctr7fbxkl@uno.localdomain>","date":"2022-09-30T15:43:22","subject":"Re: [libcamera-devel] [PATCH 2/4] utils: semver: Add version helper","submitter":{"id":3,"url":"https://patchwork.libcamera.org/api/people/3/","name":"Jacopo Mondi","email":"jacopo@jmondi.org"},"content":"Hi Kieran\n\nI wonder if importing from a raw file is the best approach here, we'll\nhave to track updates manually...\n\nAt the same time a meson subproject might be an overkill, so I'm kind\nof debated...\n\nOn Thu, Sep 29, 2022 at 03:36:24PM +0100, Kieran Bingham via libcamera-devel wrote:\n> Provide the semver utility from [0] to make use of it with our\n> versioning and release scripts.\n>\n> [0] https://github.com/fsaintjacques/semver-tool\n>\n> Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n> ---\n>  utils/semver | 419 +++++++++++++++++++++++++++++++++++++++++++++++++++\n>  1 file changed, 419 insertions(+)\n>  create mode 100755 utils/semver\n>\n> diff --git a/utils/semver b/utils/semver\n> new file mode 100755\n> index 000000000000..5b25f40ba48c\n> --- /dev/null\n> +++ b/utils/semver\n> @@ -0,0 +1,419 @@\n> +#!/usr/bin/env bash\n> +\n> +set -o errexit -o nounset -o pipefail\n> +\n> +NAT='0|[1-9][0-9]*'\n> +ALPHANUM='[0-9]*[A-Za-z-][0-9A-Za-z-]*'\n> +IDENT=\"$NAT|$ALPHANUM\"\n> +FIELD='[0-9A-Za-z-]+'\n> +\n> +SEMVER_REGEX=\"\\\n> +^[vV]?\\\n> +($NAT)\\\\.($NAT)\\\\.($NAT)\\\n> +(\\\\-(${IDENT})(\\\\.(${IDENT}))*)?\\\n> +(\\\\+${FIELD}(\\\\.${FIELD})*)?$\"\n> +\n> +PROG=semver\n> +PROG_VERSION=\"3.3.0\"\n> +\n> +USAGE=\"\\\n> +Usage:\n> +  $PROG bump (major|minor|patch|release|prerel [<prerel>]|build <build>) <version>\n> +  $PROG compare <version> <other_version>\n> +  $PROG diff <version> <other_version>\n> +  $PROG get (major|minor|patch|release|prerel|build) <version>\n> +  $PROG validate <version>\n> +  $PROG --help\n> +  $PROG --version\n> +\n> +Arguments:\n> +  <version>  A version must match the following regular expression:\n> +             \\\"${SEMVER_REGEX}\\\"\n> +             In English:\n> +             -- The version must match X.Y.Z[-PRERELEASE][+BUILD]\n> +                where X, Y and Z are non-negative integers.\n> +             -- PRERELEASE is a dot separated sequence of non-negative integers and/or\n> +                identifiers composed of alphanumeric characters and hyphens (with\n> +                at least one non-digit). Numeric identifiers must not have leading\n> +                zeros. A hyphen (\\\"-\\\") introduces this optional part.\n> +             -- BUILD is a dot separated sequence of identifiers composed of alphanumeric\n> +                characters and hyphens. A plus (\\\"+\\\") introduces this optional part.\n> +\n> +  <other_version>  See <version> definition.\n> +\n> +  <prerel>  A string as defined by PRERELEASE above. Or, it can be a PRERELEASE\n> +            prototype string (or empty) followed by a dot.\n> +\n> +  <build>   A string as defined by BUILD above.\n> +\n> +Options:\n> +  -v, --version          Print the version of this tool.\n> +  -h, --help             Print this help message.\n> +\n> +Commands:\n> +  bump      Bump by one of major, minor, patch; zeroing or removing\n> +            subsequent parts. \\\"bump prerel\\\" sets the PRERELEASE part and\n> +            removes any BUILD part. A trailing dot in the <prerel> argument\n> +            introduces an incrementing numeric field which is added or\n> +            bumped. If no <prerel> argument is provided, an incrementing numeric\n> +            field is introduced/bumped. \\\"bump build\\\" sets the BUILD part.\n> +            \\\"bump release\\\" removes any PRERELEASE or BUILD parts.\n> +            The bumped version is written to stdout.\n> +\n> +  compare   Compare <version> with <other_version>, output to stdout the\n> +            following values: -1 if <other_version> is newer, 0 if equal, 1 if\n> +            older. The BUILD part is not used in comparisons.\n> +\n> +  diff      Compare <version> with <other_version>, output to stdout the\n> +            difference between two versions by the release type (MAJOR, MINOR,\n> +            PATCH, PRERELEASE, BUILD).\n> +\n> +  get       Extract given part of <version>, where part is one of major, minor,\n> +            patch, prerel, build, or release.\n> +\n> +  validate  Validate if <version> follows the SEMVER pattern (see <version>\n> +            definition). Print 'valid' to stdout if the version is valid, otherwise\n> +            print 'invalid'.\n> +\n> +See also:\n> +  https://semver.org -- Semantic Versioning 2.0.0\"\n> +\n> +function error {\n> +  echo -e \"$1\" >&2\n> +  exit 1\n> +}\n> +\n> +function usage_help {\n> +  error \"$USAGE\"\n> +}\n> +\n> +function usage_version {\n> +  echo -e \"${PROG}: $PROG_VERSION\"\n> +  exit 0\n> +}\n> +\n> +function validate_version {\n> +  local version=$1\n> +  if [[ \"$version\" =~ $SEMVER_REGEX ]]; then\n> +    # if a second argument is passed, store the result in var named by $2\n> +    if [ \"$#\" -eq \"2\" ]; then\n> +      local major=${BASH_REMATCH[1]}\n> +      local minor=${BASH_REMATCH[2]}\n> +      local patch=${BASH_REMATCH[3]}\n> +      local prere=${BASH_REMATCH[4]}\n> +      local build=${BASH_REMATCH[8]}\n> +      eval \"$2=(\\\"$major\\\" \\\"$minor\\\" \\\"$patch\\\" \\\"$prere\\\" \\\"$build\\\")\"\n> +    else\n> +      echo \"$version\"\n> +    fi\n> +  else\n> +    error \"version $version does not match the semver scheme 'X.Y.Z(-PRERELEASE)(+BUILD)'. See help for more information.\"\n> +  fi\n> +}\n> +\n> +function is_nat {\n> +    [[ \"$1\" =~ ^($NAT)$ ]]\n> +}\n> +\n> +function is_null {\n> +    [ -z \"$1\" ]\n> +}\n> +\n> +function order_nat {\n> +    [ \"$1\" -lt \"$2\" ] && { echo -1 ; return ; }\n> +    [ \"$1\" -gt \"$2\" ] && { echo 1 ; return ; }\n> +    echo 0\n> +}\n> +\n> +function order_string {\n> +    [[ $1 < $2 ]] && { echo -1 ; return ; }\n> +    [[ $1 > $2 ]] && { echo 1 ; return ; }\n> +    echo 0\n> +}\n> +\n> +# given two (named) arrays containing NAT and/or ALPHANUM fields, compare them\n> +# one by one according to semver 2.0.0 spec. Return -1, 0, 1 if left array ($1)\n> +# is less-than, equal, or greater-than the right array ($2).  The longer array\n> +# is considered greater-than the shorter if the shorter is a prefix of the longer.\n> +#\n> +function compare_fields {\n> +    local l=\"$1[@]\"\n> +    local r=\"$2[@]\"\n> +    local leftfield=( \"${!l}\" )\n> +    local rightfield=( \"${!r}\" )\n> +    local left\n> +    local right\n> +\n> +    local i=$(( -1 ))\n> +    local order=$(( 0 ))\n> +\n> +    while true\n> +    do\n> +        [ $order -ne 0 ] && { echo $order ; return ; }\n> +\n> +        : $(( i++ ))\n> +        left=\"${leftfield[$i]}\"\n> +        right=\"${rightfield[$i]}\"\n> +\n> +        is_null \"$left\" && is_null \"$right\" && { echo 0  ; return ; }\n> +        is_null \"$left\"                     && { echo -1 ; return ; }\n> +                           is_null \"$right\" && { echo 1  ; return ; }\n> +\n> +        is_nat \"$left\" &&  is_nat \"$right\" && { order=$(order_nat \"$left\" \"$right\") ; continue ; }\n> +        is_nat \"$left\"                     && { echo -1 ; return ; }\n> +                           is_nat \"$right\" && { echo 1  ; return ; }\n> +                                              { order=$(order_string \"$left\" \"$right\") ; continue ; }\n> +    done\n> +}\n> +\n> +# shellcheck disable=SC2206     # checked by \"validate\"; ok to expand prerel id's into array\n> +function compare_version {\n> +  local order\n> +  validate_version \"$1\" V\n> +  validate_version \"$2\" V_\n> +\n> +  # compare major, minor, patch\n> +\n> +  local left=( \"${V[0]}\" \"${V[1]}\" \"${V[2]}\" )\n> +  local right=( \"${V_[0]}\" \"${V_[1]}\" \"${V_[2]}\" )\n> +\n> +  order=$(compare_fields left right)\n> +  [ \"$order\" -ne 0 ] && { echo \"$order\" ; return ; }\n> +\n> +  # compare pre-release ids when M.m.p are equal\n> +\n> +  local prerel=\"${V[3]:1}\"\n> +  local prerel_=\"${V_[3]:1}\"\n> +  local left=( ${prerel//./ } )\n> +  local right=( ${prerel_//./ } )\n> +\n> +  # if left and right have no pre-release part, then left equals right\n> +  # if only one of left/right has pre-release part, that one is less than simple M.m.p\n> +\n> +  [ -z \"$prerel\" ] && [ -z \"$prerel_\" ] && { echo 0  ; return ; }\n> +  [ -z \"$prerel\" ]                      && { echo 1  ; return ; }\n> +                      [ -z \"$prerel_\" ] && { echo -1 ; return ; }\n> +\n> +  # otherwise, compare the pre-release id's\n> +\n> +  compare_fields left right\n> +}\n> +\n> +# render_prerel -- return a prerel field with a trailing numeric string\n> +#                  usage: render_prerel numeric [prefix-string]\n> +#\n> +function render_prerel {\n> +    if [ -z \"$2\" ]\n> +    then\n> +        echo \"${1}\"\n> +    else\n> +        echo \"${2}${1}\"\n> +    fi\n> +}\n> +\n> +# extract_prerel -- extract prefix and trailing numeric portions of a pre-release part\n> +#                   usage: extract_prerel prerel prerel_parts\n> +#                   The prefix and trailing numeric parts are returned in \"prerel_parts\".\n> +#\n> +PREFIX_ALPHANUM='[.0-9A-Za-z-]*[.A-Za-z-]'\n> +DIGITS='[0-9][0-9]*'\n> +EXTRACT_REGEX=\"^(${PREFIX_ALPHANUM})*(${DIGITS})$\"\n> +\n> +function extract_prerel {\n> +    local prefix; local numeric;\n> +\n> +    if [[ \"$1\" =~ $EXTRACT_REGEX ]]\n> +    then                                        # found prefix and trailing numeric parts\n> +        prefix=\"${BASH_REMATCH[1]}\"\n> +        numeric=\"${BASH_REMATCH[2]}\"\n> +    else                                        # no numeric part\n> +        prefix=\"${1}\"\n> +        numeric=\n> +    fi\n> +\n> +    eval \"$2=(\\\"$prefix\\\" \\\"$numeric\\\")\"\n> +}\n> +\n> +# bump_prerel -- return the new pre-release part based on previous pre-release part\n> +#                and prototype for bump\n> +#                usage: bump_prerel proto previous\n> +#\n> +function bump_prerel {\n> +    local proto; local prev_prefix; local prev_numeric;\n> +\n> +    # case one: no trailing dot in prototype => simply replace previous with proto\n> +    if [[ ! ( \"$1\" =~ \\.$ ) ]]\n> +    then\n> +        echo \"$1\"\n> +        return\n> +    fi\n> +\n> +    proto=\"${1%.}\"                              # discard trailing dot marker from prototype\n> +\n> +    extract_prerel \"${2#-}\" prerel_parts        # extract parts of previous pre-release\n> +#   shellcheck disable=SC2154\n> +    prev_prefix=\"${prerel_parts[0]}\"\n> +    prev_numeric=\"${prerel_parts[1]}\"\n> +\n> +    # case two: bump or append numeric to previous pre-release part\n> +    if [ \"$proto\" == \"+\" ]                      # dummy \"+\" indicates no prototype argument provided\n> +    then\n> +        if [ -n \"$prev_numeric\" ]\n> +        then\n> +            : $(( ++prev_numeric ))             # previous pre-release is already numbered, bump it\n> +            render_prerel \"$prev_numeric\" \"$prev_prefix\"\n> +        else\n> +            render_prerel 1 \"$prev_prefix\"      # append starting number\n> +        fi\n> +        return\n> +    fi\n> +\n> +    # case three: set, bump, or append using prototype prefix\n> +    if [  \"$prev_prefix\" != \"$proto\" ]\n> +    then\n> +        render_prerel 1 \"$proto\"                # proto not same pre-release; set and start at '1'\n> +    elif [ -n \"$prev_numeric\" ]\n> +    then\n> +        : $(( ++prev_numeric ))                 # pre-release is numbered; bump it\n> +        render_prerel \"$prev_numeric\" \"$prev_prefix\"\n> +    else\n> +        render_prerel 1 \"$prev_prefix\"          # start pre-release at number '1'\n> +    fi\n> +}\n> +\n> +function command_bump {\n> +  local new; local version; local sub_version; local command;\n> +\n> +  case $# in\n> +    2) case $1 in\n> +        major|minor|patch|prerel|release) command=$1; sub_version=\"+.\"; version=$2;;\n> +        *) usage_help;;\n> +       esac ;;\n> +    3) case $1 in\n> +        prerel|build) command=$1; sub_version=$2 version=$3 ;;\n> +        *) usage_help;;\n> +       esac ;;\n> +    *) usage_help;;\n> +  esac\n> +\n> +  validate_version \"$version\" parts\n> +  # shellcheck disable=SC2154\n> +  local major=\"${parts[0]}\"\n> +  local minor=\"${parts[1]}\"\n> +  local patch=\"${parts[2]}\"\n> +  local prere=\"${parts[3]}\"\n> +  local build=\"${parts[4]}\"\n> +\n> +  case \"$command\" in\n> +    major) new=\"$((major + 1)).0.0\";;\n> +    minor) new=\"${major}.$((minor + 1)).0\";;\n> +    patch) new=\"${major}.${minor}.$((patch + 1))\";;\n> +    release) new=\"${major}.${minor}.${patch}\";;\n> +    prerel) new=$(validate_version \"${major}.${minor}.${patch}-$(bump_prerel \"$sub_version\" \"$prere\")\");;\n> +    build) new=$(validate_version \"${major}.${minor}.${patch}${prere}+${sub_version}\");;\n> +    *) usage_help ;;\n> +  esac\n> +\n> +  echo \"$new\"\n> +  exit 0\n> +}\n> +\n> +function command_compare {\n> +  local v; local v_;\n> +\n> +  case $# in\n> +    2) v=$(validate_version \"$1\"); v_=$(validate_version \"$2\") ;;\n> +    *) usage_help ;;\n> +  esac\n> +\n> +  set +u                        # need unset array element to evaluate to null\n> +  compare_version \"$v\" \"$v_\"\n> +  exit 0\n> +}\n> +\n> +function command_diff {\n> +  validate_version \"$1\" v1_parts\n> +  # shellcheck disable=SC2154\n> +  local v1_major=\"${v1_parts[0]}\"\n> +  local v1_minor=\"${v1_parts[1]}\"\n> +  local v1_patch=\"${v1_parts[2]}\"\n> +  local v1_prere=\"${v1_parts[3]}\"\n> +  local v1_build=\"${v1_parts[4]}\"\n> +\n> +  validate_version \"$2\" v2_parts\n> +  # shellcheck disable=SC2154\n> +  local v2_major=\"${v2_parts[0]}\"\n> +  local v2_minor=\"${v2_parts[1]}\"\n> +  local v2_patch=\"${v2_parts[2]}\"\n> +  local v2_prere=\"${v2_parts[3]}\"\n> +  local v2_build=\"${v2_parts[4]}\"\n> +\n> +  if [ \"${v1_major}\" != \"${v2_major}\" ]; then\n> +    echo \"major\"\n> +  elif [ \"${v1_minor}\" != \"${v2_minor}\" ]; then\n> +    echo \"minor\"\n> +  elif [ \"${v1_patch}\" != \"${v2_patch}\" ]; then\n> +    echo \"patch\"\n> +  elif [ \"${v1_prere}\" != \"${v2_prere}\" ]; then\n> +    echo \"prerelease\"\n> +  elif [ \"${v1_build}\" != \"${v2_build}\" ]; then\n> +    echo \"build\"\n> +  fi\n> +}\n> +\n> +# shellcheck disable=SC2034\n> +function command_get {\n> +    local part version\n> +\n> +    if [[ \"$#\" -ne \"2\" ]] || [[ -z \"$1\" ]] || [[ -z \"$2\" ]]; then\n> +        usage_help\n> +        exit 0\n> +    fi\n> +\n> +    part=\"$1\"\n> +    version=\"$2\"\n> +\n> +    validate_version \"$version\" parts\n> +    local major=\"${parts[0]}\"\n> +    local minor=\"${parts[1]}\"\n> +    local patch=\"${parts[2]}\"\n> +    local prerel=\"${parts[3]:1}\"\n> +    local build=\"${parts[4]:1}\"\n> +    local release=\"${major}.${minor}.${patch}\"\n> +\n> +    case \"$part\" in\n> +        major|minor|patch|release|prerel|build) echo \"${!part}\" ;;\n> +        *) usage_help ;;\n> +    esac\n> +\n> +    exit 0\n> +}\n> +\n> +function command_validate {\n> +  if [[ \"$#\" -ne \"1\" ]]; then\n> +        usage_help\n> +  fi\n> +\n> +  if [[ \"$1\" =~ $SEMVER_REGEX ]]; then\n> +    echo \"valid\"\n> +  else\n> +    echo \"invalid\"\n> +  fi\n> +\n> +  exit 0\n> +}\n> +\n> +case $# in\n> +  0) echo \"Unknown command: $*\"; usage_help;;\n> +esac\n> +\n> +case $1 in\n> +  --help|-h) echo -e \"$USAGE\"; exit 0;;\n> +  --version|-v) usage_version ;;\n> +  bump) shift; command_bump \"$@\";;\n> +  get) shift; command_get \"$@\";;\n> +  compare) shift; command_compare \"$@\";;\n> +  diff) shift; command_diff \"$@\";;\n> +  validate) shift; command_validate \"$@\";;\n> +  *) echo \"Unknown arguments: $*\"; usage_help;;\n> +esac\n> --\n> 2.34.1\n>","headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id D8F13BD16B\n\tfor <parsemail@patchwork.libcamera.org>;\n\tFri, 30 Sep 2022 15:43:26 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 0F0E1623AC;\n\tFri, 30 Sep 2022 17:43:26 +0200 (CEST)","from relay8-d.mail.gandi.net (relay8-d.mail.gandi.net\n\t[217.70.183.201])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 5C33961F77\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 30 Sep 2022 17:43:24 +0200 (CEST)","(Authenticated sender: jacopo@jmondi.org)\n\tby mail.gandi.net (Postfix) with ESMTPSA id D178B1BF20C;\n\tFri, 30 Sep 2022 15:43:23 +0000 (UTC)"],"DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org;\n\ts=mail; t=1664552606;\n\tbh=0H4HABaUovJlWAIs9IrUhXPiKLrH7EnW3LlyJ1PP0Yg=;\n\th=Date:To:References:In-Reply-To:Subject:List-Id:List-Unsubscribe:\n\tList-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:Cc:\n\tFrom;\n\tb=HpfF0LMCZSYNWlEFA8EeSU/0HhndCMtU9wMFUZ/hMVf3R6ltDYTzVuF5pTdqNnxuP\n\tr8j93hwOuhLJSnAaPq3+QFQF7lV6SfsePU0hwYlUFaJXk8J6HiwBXZ9JMGG8f17og/\n\t1i5ZtkYNh7T7sEO3G+U6Bo2D328BJe/2nvfRYmgu9X+JFKDdsy0IvcFzyhohLIq8SD\n\th50mfFooQ/dza2qshp7NtJGSKbXA4H2FK/4VYqkvX7xCRvivETCkX+DnMMYmjtpHGu\n\tIcY5ifugC4Loo4ZR9Z6KUqdQb33pBeyAJ2lptJlch6e3vAZI3/6jXLEowKtQ7cW600\n\tH8NzaRyc4hhSA==","Date":"Fri, 30 Sep 2022 17:43:22 +0200","To":"Kieran Bingham <kieran.bingham@ideasonboard.com>","Message-ID":"<20220930154322.kjklx55ctr7fbxkl@uno.localdomain>","References":"<20220929143626.3100668-1-kieran.bingham@ideasonboard.com>\n\t<20220929143626.3100668-3-kieran.bingham@ideasonboard.com>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","In-Reply-To":"<20220929143626.3100668-3-kieran.bingham@ideasonboard.com>","Subject":"Re: [libcamera-devel] [PATCH 2/4] utils: semver: Add version helper","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","From":"Jacopo Mondi via libcamera-devel <libcamera-devel@lists.libcamera.org>","Reply-To":"Jacopo Mondi <jacopo@jmondi.org>","Cc":"libcamera devel <libcamera-devel@lists.libcamera.org>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":25202,"web_url":"https://patchwork.libcamera.org/comment/25202/","msgid":"<YzdUoPe3muQ8zAWt@pendragon.ideasonboard.com>","date":"2022-09-30T20:42:08","subject":"Re: [libcamera-devel] [PATCH 2/4] utils: semver: Add version helper","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"On Fri, Sep 30, 2022 at 05:43:22PM +0200, Jacopo Mondi via libcamera-devel wrote:\n> Hi Kieran\n> \n> I wonder if importing from a raw file is the best approach here, we'll\n> have to track updates manually...\n> \n> At the same time a meson subproject might be an overkill, so I'm kind\n> of debated...\n\nI think it would be overkill indeed. This is a small script, I don't\nexpect it to be updated often (if ever), so I think it's fine as-is.\n\n> On Thu, Sep 29, 2022 at 03:36:24PM +0100, Kieran Bingham via libcamera-devel wrote:\n> > Provide the semver utility from [0] to make use of it with our\n> > versioning and release scripts.\n> >\n> > [0] https://github.com/fsaintjacques/semver-tool\n\nFollowing the comments on patch 3/4, the commit ID (or tag) should be\nspecified here.\n\nReviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n\n> > Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n> > ---\n> >  utils/semver | 419 +++++++++++++++++++++++++++++++++++++++++++++++++++\n> >  1 file changed, 419 insertions(+)\n> >  create mode 100755 utils/semver\n> >\n> > diff --git a/utils/semver b/utils/semver\n> > new file mode 100755\n> > index 000000000000..5b25f40ba48c\n> > --- /dev/null\n> > +++ b/utils/semver\n> > @@ -0,0 +1,419 @@\n> > +#!/usr/bin/env bash\n> > +\n> > +set -o errexit -o nounset -o pipefail\n> > +\n> > +NAT='0|[1-9][0-9]*'\n> > +ALPHANUM='[0-9]*[A-Za-z-][0-9A-Za-z-]*'\n> > +IDENT=\"$NAT|$ALPHANUM\"\n> > +FIELD='[0-9A-Za-z-]+'\n> > +\n> > +SEMVER_REGEX=\"\\\n> > +^[vV]?\\\n> > +($NAT)\\\\.($NAT)\\\\.($NAT)\\\n> > +(\\\\-(${IDENT})(\\\\.(${IDENT}))*)?\\\n> > +(\\\\+${FIELD}(\\\\.${FIELD})*)?$\"\n> > +\n> > +PROG=semver\n> > +PROG_VERSION=\"3.3.0\"\n> > +\n> > +USAGE=\"\\\n> > +Usage:\n> > +  $PROG bump (major|minor|patch|release|prerel [<prerel>]|build <build>) <version>\n> > +  $PROG compare <version> <other_version>\n> > +  $PROG diff <version> <other_version>\n> > +  $PROG get (major|minor|patch|release|prerel|build) <version>\n> > +  $PROG validate <version>\n> > +  $PROG --help\n> > +  $PROG --version\n> > +\n> > +Arguments:\n> > +  <version>  A version must match the following regular expression:\n> > +             \\\"${SEMVER_REGEX}\\\"\n> > +             In English:\n> > +             -- The version must match X.Y.Z[-PRERELEASE][+BUILD]\n> > +                where X, Y and Z are non-negative integers.\n> > +             -- PRERELEASE is a dot separated sequence of non-negative integers and/or\n> > +                identifiers composed of alphanumeric characters and hyphens (with\n> > +                at least one non-digit). Numeric identifiers must not have leading\n> > +                zeros. A hyphen (\\\"-\\\") introduces this optional part.\n> > +             -- BUILD is a dot separated sequence of identifiers composed of alphanumeric\n> > +                characters and hyphens. A plus (\\\"+\\\") introduces this optional part.\n> > +\n> > +  <other_version>  See <version> definition.\n> > +\n> > +  <prerel>  A string as defined by PRERELEASE above. Or, it can be a PRERELEASE\n> > +            prototype string (or empty) followed by a dot.\n> > +\n> > +  <build>   A string as defined by BUILD above.\n> > +\n> > +Options:\n> > +  -v, --version          Print the version of this tool.\n> > +  -h, --help             Print this help message.\n> > +\n> > +Commands:\n> > +  bump      Bump by one of major, minor, patch; zeroing or removing\n> > +            subsequent parts. \\\"bump prerel\\\" sets the PRERELEASE part and\n> > +            removes any BUILD part. A trailing dot in the <prerel> argument\n> > +            introduces an incrementing numeric field which is added or\n> > +            bumped. If no <prerel> argument is provided, an incrementing numeric\n> > +            field is introduced/bumped. \\\"bump build\\\" sets the BUILD part.\n> > +            \\\"bump release\\\" removes any PRERELEASE or BUILD parts.\n> > +            The bumped version is written to stdout.\n> > +\n> > +  compare   Compare <version> with <other_version>, output to stdout the\n> > +            following values: -1 if <other_version> is newer, 0 if equal, 1 if\n> > +            older. The BUILD part is not used in comparisons.\n> > +\n> > +  diff      Compare <version> with <other_version>, output to stdout the\n> > +            difference between two versions by the release type (MAJOR, MINOR,\n> > +            PATCH, PRERELEASE, BUILD).\n> > +\n> > +  get       Extract given part of <version>, where part is one of major, minor,\n> > +            patch, prerel, build, or release.\n> > +\n> > +  validate  Validate if <version> follows the SEMVER pattern (see <version>\n> > +            definition). Print 'valid' to stdout if the version is valid, otherwise\n> > +            print 'invalid'.\n> > +\n> > +See also:\n> > +  https://semver.org -- Semantic Versioning 2.0.0\"\n> > +\n> > +function error {\n> > +  echo -e \"$1\" >&2\n> > +  exit 1\n> > +}\n> > +\n> > +function usage_help {\n> > +  error \"$USAGE\"\n> > +}\n> > +\n> > +function usage_version {\n> > +  echo -e \"${PROG}: $PROG_VERSION\"\n> > +  exit 0\n> > +}\n> > +\n> > +function validate_version {\n> > +  local version=$1\n> > +  if [[ \"$version\" =~ $SEMVER_REGEX ]]; then\n> > +    # if a second argument is passed, store the result in var named by $2\n> > +    if [ \"$#\" -eq \"2\" ]; then\n> > +      local major=${BASH_REMATCH[1]}\n> > +      local minor=${BASH_REMATCH[2]}\n> > +      local patch=${BASH_REMATCH[3]}\n> > +      local prere=${BASH_REMATCH[4]}\n> > +      local build=${BASH_REMATCH[8]}\n> > +      eval \"$2=(\\\"$major\\\" \\\"$minor\\\" \\\"$patch\\\" \\\"$prere\\\" \\\"$build\\\")\"\n> > +    else\n> > +      echo \"$version\"\n> > +    fi\n> > +  else\n> > +    error \"version $version does not match the semver scheme 'X.Y.Z(-PRERELEASE)(+BUILD)'. See help for more information.\"\n> > +  fi\n> > +}\n> > +\n> > +function is_nat {\n> > +    [[ \"$1\" =~ ^($NAT)$ ]]\n> > +}\n> > +\n> > +function is_null {\n> > +    [ -z \"$1\" ]\n> > +}\n> > +\n> > +function order_nat {\n> > +    [ \"$1\" -lt \"$2\" ] && { echo -1 ; return ; }\n> > +    [ \"$1\" -gt \"$2\" ] && { echo 1 ; return ; }\n> > +    echo 0\n> > +}\n> > +\n> > +function order_string {\n> > +    [[ $1 < $2 ]] && { echo -1 ; return ; }\n> > +    [[ $1 > $2 ]] && { echo 1 ; return ; }\n> > +    echo 0\n> > +}\n> > +\n> > +# given two (named) arrays containing NAT and/or ALPHANUM fields, compare them\n> > +# one by one according to semver 2.0.0 spec. Return -1, 0, 1 if left array ($1)\n> > +# is less-than, equal, or greater-than the right array ($2).  The longer array\n> > +# is considered greater-than the shorter if the shorter is a prefix of the longer.\n> > +#\n> > +function compare_fields {\n> > +    local l=\"$1[@]\"\n> > +    local r=\"$2[@]\"\n> > +    local leftfield=( \"${!l}\" )\n> > +    local rightfield=( \"${!r}\" )\n> > +    local left\n> > +    local right\n> > +\n> > +    local i=$(( -1 ))\n> > +    local order=$(( 0 ))\n> > +\n> > +    while true\n> > +    do\n> > +        [ $order -ne 0 ] && { echo $order ; return ; }\n> > +\n> > +        : $(( i++ ))\n> > +        left=\"${leftfield[$i]}\"\n> > +        right=\"${rightfield[$i]}\"\n> > +\n> > +        is_null \"$left\" && is_null \"$right\" && { echo 0  ; return ; }\n> > +        is_null \"$left\"                     && { echo -1 ; return ; }\n> > +                           is_null \"$right\" && { echo 1  ; return ; }\n> > +\n> > +        is_nat \"$left\" &&  is_nat \"$right\" && { order=$(order_nat \"$left\" \"$right\") ; continue ; }\n> > +        is_nat \"$left\"                     && { echo -1 ; return ; }\n> > +                           is_nat \"$right\" && { echo 1  ; return ; }\n> > +                                              { order=$(order_string \"$left\" \"$right\") ; continue ; }\n> > +    done\n> > +}\n> > +\n> > +# shellcheck disable=SC2206     # checked by \"validate\"; ok to expand prerel id's into array\n> > +function compare_version {\n> > +  local order\n> > +  validate_version \"$1\" V\n> > +  validate_version \"$2\" V_\n> > +\n> > +  # compare major, minor, patch\n> > +\n> > +  local left=( \"${V[0]}\" \"${V[1]}\" \"${V[2]}\" )\n> > +  local right=( \"${V_[0]}\" \"${V_[1]}\" \"${V_[2]}\" )\n> > +\n> > +  order=$(compare_fields left right)\n> > +  [ \"$order\" -ne 0 ] && { echo \"$order\" ; return ; }\n> > +\n> > +  # compare pre-release ids when M.m.p are equal\n> > +\n> > +  local prerel=\"${V[3]:1}\"\n> > +  local prerel_=\"${V_[3]:1}\"\n> > +  local left=( ${prerel//./ } )\n> > +  local right=( ${prerel_//./ } )\n> > +\n> > +  # if left and right have no pre-release part, then left equals right\n> > +  # if only one of left/right has pre-release part, that one is less than simple M.m.p\n> > +\n> > +  [ -z \"$prerel\" ] && [ -z \"$prerel_\" ] && { echo 0  ; return ; }\n> > +  [ -z \"$prerel\" ]                      && { echo 1  ; return ; }\n> > +                      [ -z \"$prerel_\" ] && { echo -1 ; return ; }\n> > +\n> > +  # otherwise, compare the pre-release id's\n> > +\n> > +  compare_fields left right\n> > +}\n> > +\n> > +# render_prerel -- return a prerel field with a trailing numeric string\n> > +#                  usage: render_prerel numeric [prefix-string]\n> > +#\n> > +function render_prerel {\n> > +    if [ -z \"$2\" ]\n> > +    then\n> > +        echo \"${1}\"\n> > +    else\n> > +        echo \"${2}${1}\"\n> > +    fi\n> > +}\n> > +\n> > +# extract_prerel -- extract prefix and trailing numeric portions of a pre-release part\n> > +#                   usage: extract_prerel prerel prerel_parts\n> > +#                   The prefix and trailing numeric parts are returned in \"prerel_parts\".\n> > +#\n> > +PREFIX_ALPHANUM='[.0-9A-Za-z-]*[.A-Za-z-]'\n> > +DIGITS='[0-9][0-9]*'\n> > +EXTRACT_REGEX=\"^(${PREFIX_ALPHANUM})*(${DIGITS})$\"\n> > +\n> > +function extract_prerel {\n> > +    local prefix; local numeric;\n> > +\n> > +    if [[ \"$1\" =~ $EXTRACT_REGEX ]]\n> > +    then                                        # found prefix and trailing numeric parts\n> > +        prefix=\"${BASH_REMATCH[1]}\"\n> > +        numeric=\"${BASH_REMATCH[2]}\"\n> > +    else                                        # no numeric part\n> > +        prefix=\"${1}\"\n> > +        numeric=\n> > +    fi\n> > +\n> > +    eval \"$2=(\\\"$prefix\\\" \\\"$numeric\\\")\"\n> > +}\n> > +\n> > +# bump_prerel -- return the new pre-release part based on previous pre-release part\n> > +#                and prototype for bump\n> > +#                usage: bump_prerel proto previous\n> > +#\n> > +function bump_prerel {\n> > +    local proto; local prev_prefix; local prev_numeric;\n> > +\n> > +    # case one: no trailing dot in prototype => simply replace previous with proto\n> > +    if [[ ! ( \"$1\" =~ \\.$ ) ]]\n> > +    then\n> > +        echo \"$1\"\n> > +        return\n> > +    fi\n> > +\n> > +    proto=\"${1%.}\"                              # discard trailing dot marker from prototype\n> > +\n> > +    extract_prerel \"${2#-}\" prerel_parts        # extract parts of previous pre-release\n> > +#   shellcheck disable=SC2154\n> > +    prev_prefix=\"${prerel_parts[0]}\"\n> > +    prev_numeric=\"${prerel_parts[1]}\"\n> > +\n> > +    # case two: bump or append numeric to previous pre-release part\n> > +    if [ \"$proto\" == \"+\" ]                      # dummy \"+\" indicates no prototype argument provided\n> > +    then\n> > +        if [ -n \"$prev_numeric\" ]\n> > +        then\n> > +            : $(( ++prev_numeric ))             # previous pre-release is already numbered, bump it\n> > +            render_prerel \"$prev_numeric\" \"$prev_prefix\"\n> > +        else\n> > +            render_prerel 1 \"$prev_prefix\"      # append starting number\n> > +        fi\n> > +        return\n> > +    fi\n> > +\n> > +    # case three: set, bump, or append using prototype prefix\n> > +    if [  \"$prev_prefix\" != \"$proto\" ]\n> > +    then\n> > +        render_prerel 1 \"$proto\"                # proto not same pre-release; set and start at '1'\n> > +    elif [ -n \"$prev_numeric\" ]\n> > +    then\n> > +        : $(( ++prev_numeric ))                 # pre-release is numbered; bump it\n> > +        render_prerel \"$prev_numeric\" \"$prev_prefix\"\n> > +    else\n> > +        render_prerel 1 \"$prev_prefix\"          # start pre-release at number '1'\n> > +    fi\n> > +}\n> > +\n> > +function command_bump {\n> > +  local new; local version; local sub_version; local command;\n> > +\n> > +  case $# in\n> > +    2) case $1 in\n> > +        major|minor|patch|prerel|release) command=$1; sub_version=\"+.\"; version=$2;;\n> > +        *) usage_help;;\n> > +       esac ;;\n> > +    3) case $1 in\n> > +        prerel|build) command=$1; sub_version=$2 version=$3 ;;\n> > +        *) usage_help;;\n> > +       esac ;;\n> > +    *) usage_help;;\n> > +  esac\n> > +\n> > +  validate_version \"$version\" parts\n> > +  # shellcheck disable=SC2154\n> > +  local major=\"${parts[0]}\"\n> > +  local minor=\"${parts[1]}\"\n> > +  local patch=\"${parts[2]}\"\n> > +  local prere=\"${parts[3]}\"\n> > +  local build=\"${parts[4]}\"\n> > +\n> > +  case \"$command\" in\n> > +    major) new=\"$((major + 1)).0.0\";;\n> > +    minor) new=\"${major}.$((minor + 1)).0\";;\n> > +    patch) new=\"${major}.${minor}.$((patch + 1))\";;\n> > +    release) new=\"${major}.${minor}.${patch}\";;\n> > +    prerel) new=$(validate_version \"${major}.${minor}.${patch}-$(bump_prerel \"$sub_version\" \"$prere\")\");;\n> > +    build) new=$(validate_version \"${major}.${minor}.${patch}${prere}+${sub_version}\");;\n> > +    *) usage_help ;;\n> > +  esac\n> > +\n> > +  echo \"$new\"\n> > +  exit 0\n> > +}\n> > +\n> > +function command_compare {\n> > +  local v; local v_;\n> > +\n> > +  case $# in\n> > +    2) v=$(validate_version \"$1\"); v_=$(validate_version \"$2\") ;;\n> > +    *) usage_help ;;\n> > +  esac\n> > +\n> > +  set +u                        # need unset array element to evaluate to null\n> > +  compare_version \"$v\" \"$v_\"\n> > +  exit 0\n> > +}\n> > +\n> > +function command_diff {\n> > +  validate_version \"$1\" v1_parts\n> > +  # shellcheck disable=SC2154\n> > +  local v1_major=\"${v1_parts[0]}\"\n> > +  local v1_minor=\"${v1_parts[1]}\"\n> > +  local v1_patch=\"${v1_parts[2]}\"\n> > +  local v1_prere=\"${v1_parts[3]}\"\n> > +  local v1_build=\"${v1_parts[4]}\"\n> > +\n> > +  validate_version \"$2\" v2_parts\n> > +  # shellcheck disable=SC2154\n> > +  local v2_major=\"${v2_parts[0]}\"\n> > +  local v2_minor=\"${v2_parts[1]}\"\n> > +  local v2_patch=\"${v2_parts[2]}\"\n> > +  local v2_prere=\"${v2_parts[3]}\"\n> > +  local v2_build=\"${v2_parts[4]}\"\n> > +\n> > +  if [ \"${v1_major}\" != \"${v2_major}\" ]; then\n> > +    echo \"major\"\n> > +  elif [ \"${v1_minor}\" != \"${v2_minor}\" ]; then\n> > +    echo \"minor\"\n> > +  elif [ \"${v1_patch}\" != \"${v2_patch}\" ]; then\n> > +    echo \"patch\"\n> > +  elif [ \"${v1_prere}\" != \"${v2_prere}\" ]; then\n> > +    echo \"prerelease\"\n> > +  elif [ \"${v1_build}\" != \"${v2_build}\" ]; then\n> > +    echo \"build\"\n> > +  fi\n> > +}\n> > +\n> > +# shellcheck disable=SC2034\n> > +function command_get {\n> > +    local part version\n> > +\n> > +    if [[ \"$#\" -ne \"2\" ]] || [[ -z \"$1\" ]] || [[ -z \"$2\" ]]; then\n> > +        usage_help\n> > +        exit 0\n> > +    fi\n> > +\n> > +    part=\"$1\"\n> > +    version=\"$2\"\n> > +\n> > +    validate_version \"$version\" parts\n> > +    local major=\"${parts[0]}\"\n> > +    local minor=\"${parts[1]}\"\n> > +    local patch=\"${parts[2]}\"\n> > +    local prerel=\"${parts[3]:1}\"\n> > +    local build=\"${parts[4]:1}\"\n> > +    local release=\"${major}.${minor}.${patch}\"\n> > +\n> > +    case \"$part\" in\n> > +        major|minor|patch|release|prerel|build) echo \"${!part}\" ;;\n> > +        *) usage_help ;;\n> > +    esac\n> > +\n> > +    exit 0\n> > +}\n> > +\n> > +function command_validate {\n> > +  if [[ \"$#\" -ne \"1\" ]]; then\n> > +        usage_help\n> > +  fi\n> > +\n> > +  if [[ \"$1\" =~ $SEMVER_REGEX ]]; then\n> > +    echo \"valid\"\n> > +  else\n> > +    echo \"invalid\"\n> > +  fi\n> > +\n> > +  exit 0\n> > +}\n> > +\n> > +case $# in\n> > +  0) echo \"Unknown command: $*\"; usage_help;;\n> > +esac\n> > +\n> > +case $1 in\n> > +  --help|-h) echo -e \"$USAGE\"; exit 0;;\n> > +  --version|-v) usage_version ;;\n> > +  bump) shift; command_bump \"$@\";;\n> > +  get) shift; command_get \"$@\";;\n> > +  compare) shift; command_compare \"$@\";;\n> > +  diff) shift; command_diff \"$@\";;\n> > +  validate) shift; command_validate \"$@\";;\n> > +  *) echo \"Unknown arguments: $*\"; usage_help;;\n> > +esac","headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id 3B18AC0DA4\n\tfor <parsemail@patchwork.libcamera.org>;\n\tFri, 30 Sep 2022 20:42:13 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id DBD5062847;\n\tFri, 30 Sep 2022 22:42:12 +0200 (CEST)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id D323961FB1\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 30 Sep 2022 22:42:10 +0200 (CEST)","from pendragon.ideasonboard.com (62-78-145-57.bb.dnainternet.fi\n\t[62.78.145.57])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 559AE47C;\n\tFri, 30 Sep 2022 22:42:10 +0200 (CEST)"],"DKIM-Signature":["v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org;\n\ts=mail; t=1664570532;\n\tbh=wr416Kqlk0LBX+REDbfFNdaSix+Q8fETVgEBRxc0E18=;\n\th=Date:To:References:In-Reply-To:Subject:List-Id:List-Unsubscribe:\n\tList-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:Cc:\n\tFrom;\n\tb=XpBbvbXlRT1Dw7yMOnWPz3f3mq21rh8w6tvzCtNrgXDUuAhVv/Iup1GrXQnP6ulbW\n\tUEXAXymwT4uS3kmOPFiPNcJiQ9hdbvTSeEknRRfrUj13uDeMT7utZEGaaEAfyqmQNN\n\t4TiTRBFhF1ntWatincOn7Pw/egO1F0OUko/spEurQFUbi5PxS53AzEnJ0eF+gRN14r\n\tke+meTYxfDmhnKtW/jcH6DRRzDxfC5UhYdLNZ27XGFv6BDoXVstLbT7mIqlmAUdi3h\n\tvmUNZA0XgIBXpw9cKi4EqmufsHgUAA+BHDgDapCoaw86yZ/aNjjpEjt7TLp1oOp2vH\n\tBRLXFfjdTIzEQ==","v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1664570530;\n\tbh=wr416Kqlk0LBX+REDbfFNdaSix+Q8fETVgEBRxc0E18=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=QSbSropDDF1llQm75XVIbLbCJ2rLXL4wZWB2vuJm8uXQOmRTcyRSrOyJDFCqvi+/r\n\tXH2Ux9L2FOpxpJzeGIFhfuRYs8g3de5IcnLrTdYahKbdAGO3pW6JO2cozvBOTP/+o6\n\tjT93M8L6ROEnFkNsOGnHv2MnqJuL8Y8XTu0nA/jE="],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key; \n\tunprotected) header.d=ideasonboard.com\n\theader.i=@ideasonboard.com\n\theader.b=\"QSbSropD\"; dkim-atps=neutral","Date":"Fri, 30 Sep 2022 23:42:08 +0300","To":"Jacopo Mondi <jacopo@jmondi.org>","Message-ID":"<YzdUoPe3muQ8zAWt@pendragon.ideasonboard.com>","References":"<20220929143626.3100668-1-kieran.bingham@ideasonboard.com>\n\t<20220929143626.3100668-3-kieran.bingham@ideasonboard.com>\n\t<20220930154322.kjklx55ctr7fbxkl@uno.localdomain>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","In-Reply-To":"<20220930154322.kjklx55ctr7fbxkl@uno.localdomain>","Subject":"Re: [libcamera-devel] [PATCH 2/4] utils: semver: Add version helper","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","From":"Laurent Pinchart via libcamera-devel\n\t<libcamera-devel@lists.libcamera.org>","Reply-To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","Cc":"libcamera devel <libcamera-devel@lists.libcamera.org>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}}]