-#! /bin/sh
-# Copyright 2020 The OpenSSL Project Authors. All Rights Reserved.
+#! /bin/bash
+# Copyright 2020-2023 The OpenSSL Project Authors. All Rights Reserved.
#
# Licensed under the Apache License 2.0 (the "License"). You may not use
# this file except in compliance with the License. You can obtain a copy
# in the file LICENSE in the source distribution or at
# https://www.openssl.org/source/license.html
-# These functions load, manipulate and store the current version information
-# for OpenSSL 3.0 and on.
-# They are meant to be minimalistic for easy refactoring depending on OpenSSL
-# version.
+# These functions collect and manipulate information relevant for diverse
+# OpenSSL versions, and populate the following variables:
#
-# Version information is stored in the following variables:
+# VERSION_FILE The path of the file where the version information
+# is found and should be stored. If this is empty,
+# no version information was found, and the release
+# should be aborted.
#
-# |MAJOR|, |MINOR|, |PATCH| are the three parts of a version number.
-# |MAJOR| is to be increased for new major releases, |MINOR| for new
-# minor releases, and |PATCH| for update releases.
+# MAJOR, MINOR, FIX, PATCH
+# The three or four parts of a version number, depending
+# on the version scheme.
+# Examples:
+# With OpenSSL 3.0.9, MAJOR=3, MINOR=0 and PATCH=9.
+# With OpenSSL 1.1.1u, MAJOR=1, MINOR=1, FIX=1 and
+# PATCH=t.
+# PRE_RELEASE_TAG, BUILD_METADATA, RELEASE_DATE, SHLIB_VERSION
+# Supplemental state data found in the version file.
#
-# |SERIES| tells what release series the current version belongs to, and
-# is composed from |MAJOR| and |MINOR|.
-# |VERSION| tells what the current version is, and is composed from |MAJOR|,
-# |MINOR| and |PATCH|.
-# |TYPE| tells what state the source is in. It may have an empty value
-# for released source, or 'dev' for "in development".
-# |PRE_LABEL| may be "alpha" or "beta" to signify an ongoing series of
-# alpha or beta releases. |PRE_NUM| is a pre-release counter for the
-# alpha and beta release series, but isn't necessarily strictly tied
-# to the prerelease label.
+# _PRE_RELEASE_TAG, _BUILD_METADATA
+# Computed variants of PRE_RELEASE_TAG and BUILD_METADATA,
+# with added markup suitable for version numbers in text
+# form.
#
-# Scripts loading this file are not allowed to manipulate these
-# variables directly. They must use functions such as fixup_version()
-# below, or next_release_state(), found in release-state-fn.sh.
-
-# These functions depend on |SOURCEDIR|, which must have the intended
-# OpenSSL source directory as value.
+# SERIES The current release series. It is computed from
+# MAJOR, MINOR and (possibly) FIX
+# VERSION The current version number. It is copmuted from
+# MAJOR, MINOR, (possibly) FIX and PATCH
+#
+# TYPE The state the source is in. It may have an empty value
+# for released source, or 'dev' for "in development".
+#
+# PRE_LABEL May be "alpha" or "beta" to signify an ongoing series
+# of alpha or beta releases.
+# PRE_NUM A pre-release counter for the alpha and beta release
+# series, but isn't necessarily strictly tied to the
+# prerelease label.
+#
+# Scripts loading this file are not allowed to manipulate these variables
+# directly. They must use next_release_state(), found in release-state-fn.sh.
get_version () {
- eval $(git cat-file blob HEAD:VERSION.dat)
- VERSION="$MAJOR.$MINOR.$PATCH"
- SERIES="$MAJOR.$MINOR"
- TYPE=$( echo "$PRE_RELEASE_TAG" \
- | sed -E \
- -e 's|^dev$|dev|' \
- -e 's|^alpha([0-9]+)(-(dev))?$|\3|' \
- -e 's|^beta([0-9]+)(-(dev))?$|\3|' )
- PRE_LABEL=$( echo "$PRE_RELEASE_TAG" \
- | sed -E \
- -e 's|^dev$||' \
- -e 's|^alpha([0-9]+)(-(dev))?$|alpha|' \
- -e 's|^beta([0-9]+)(-(dev))?$|beta|' )
- PRE_NUM=$( echo "$PRE_RELEASE_TAG" \
- | sed -E \
- -e 's|^dev$|0|' \
- -e 's|^alpha([0-9]+)(-(dev))?$|\1|' \
- -e 's|^beta([0-9]+)(-(dev))?$|\1|' )
- _BUILD_METADATA=''
- if [ -n "$PRE_RELEASE_TAG" ]; then _PRE_RELEASE_TAG="-${PRE_RELEASE_TAG}"; fi
- if [ -n "$BUILD_METADATA" ]; then _BUILD_METADATA="+${BUILD_METADATA}"; fi
+ ### Reset all variables we defined
+ # VERSION_FILE is the version file used
+ VERSION_FILE=
+
+ # These are the variables possibly extracted from the version file
+ MAJOR=
+ MINOR=
+ FIX=
+ PATCH=
+ PRE_RELEASE_TAG=
+ BUILD_METADATA=
+ RELEASE_DATE=
+ SHLIB_VERSION=
+
+ # These are computed from extracted variables
+ SERIES=
+ VERSION=
+ TYPE=
+ PRE_LABEL=
+ PRE_NUM=
+ _PRE_RELEASE_TAG=
+ _BUILD_METADATA=
+
+ # Detect possible version files.
+ # OpenSSL 3.0 and on use VERSION.dat.
+ # OpenSSL 1.1.y use include/openssl/opensslv.h
+ # OpenSSL 1.0.y (as well as OpenSSL 0.x.y) use crypto/opensslv.h
+ for vf in VERSION.dat include/openssl/opensslv.h crypto/opensslv.h; do
+ if [ -n "$(git ls-files $vf)" ]; then
+ VERSION_FILE=$vf
+ break
+ fi
+ done
+
+ case "$VERSION_FILE" in
+ VERSION.dat )
+ # The base version data is simply there in VERSION.dat,
+ # All we need is to evaluate that file like a shell script.
+ eval $(git cat-file blob HEAD:"$VERSION_FILE")
+
+ if [ -n "$PRE_RELEASE_TAG" ]; then
+ _PRE_RELEASE_TAG="-${PRE_RELEASE_TAG}"
+ fi
+ if [ -n "$BUILD_METADATA" ]; then
+ _BUILD_METADATA="+${BUILD_METADATA}"
+ fi
+
+ SERIES="$MAJOR.$MINOR"
+ VERSION="$MAJOR.$MINOR.$PATCH"
+ TYPE=$( echo "$PRE_RELEASE_TAG" \
+ | sed -E \
+ -e 's|^dev$|dev|' \
+ -e 's|^alpha([0-9]+)(-(dev))?$|\3|' \
+ -e 's|^beta([0-9]+)(-(dev))?$|\3|' )
+ PRE_LABEL=$( echo "$PRE_RELEASE_TAG" \
+ | sed -E \
+ -e 's|^dev$||' \
+ -e 's|^alpha([0-9]+)(-(dev))?$|alpha|' \
+ -e 's|^beta([0-9]+)(-(dev))?$|beta|' )
+ PRE_NUM=$( echo "$PRE_RELEASE_TAG" \
+ | sed -E \
+ -e 's|^dev$|0|' \
+ -e 's|^alpha([0-9]+)(-(dev))?$|\1|' \
+ -e 's|^beta([0-9]+)(-(dev))?$|\1|' )
+ ;;
+ */opensslv.h )
+ # opensslv.h is a bit more difficult to get version data from,
+ # as it involves find the C macro definition for it, and calculate
+ # the version number from hex digits, having the following meaning:
+ #
+ # 0xMNNFFPPSL
+ #
+ # For M = MAJOR, NN = MINOR, FF = FIX, PP = PATCH, S = STATE
+ #
+ # S has one of the values 0 for development, 1 to e for betas
+ # 1 to 14, and f for release. Because the versions using this
+ # scheme are all already released, and this scheme is otherwise
+ # abandonned, we only care about the state numbers 0 and f.
+
+ # Extract the base version numbers by converting the macro
+ # definition of OPENSSL_VERSION_NUMBER into a small shell script
+ # that defines appropriate shell variables. It turns out the
+ # perl is the better processor for this sort of thing.
+ local version_extractor='
+if (m|^[[:space:]]*#[[:space:]]*define[[:space:]]+OPENSSL_VERSION_NUMBER[[:space:]]+0x([[:xdigit:]])([[:xdigit:]]{2})([[:xdigit:]]{2})([[:xdigit:]]{2})([[:xdigit:]])L$|) {
+ my $PP = hex($4);
+ my $letter_PP = "";
+ while ($PP > 25) {
+ $letter_PP .= "z";
+ $PP -= 25;
+ }
+ if ($PP > 0) {
+ $letter_PP .= chr($PP + ord("a") - 1);
+ }
+ my $S = hex($5);
+ my $tag_S = $S == 0 ? "dev" : "";
+
+ print "MAJOR=",hex($1),"\n";
+ print "MINOR=",hex($2),"\n";
+ print "FIX=",hex($3),"\n";
+ print "PATCH=",$letter_PP,"\n";
+ print "PRE_RELEASE_TAG=$tag_S\n";
+} elsif (m|^[[:space:]]*#[[:space:]]*define[[:space:]]+SHLIB_VERSION_NUMBER[[:space:]]+"([^"]*)"$|) {
+ print "SHLIB_VERSION=$1\n";
+}
+'
+ eval $(git cat-file blob HEAD:"$VERSION_FILE" \
+ | perl -n -e "$version_extractor" )
+
+ # Additional data that's default or computed from the version
+ # number data.
+ if [ "$MINOR" -eq 0 ]; then
+ # 1.0.x
+ SHLIB_VERSION="$MAJOR.$MINOR.0"
+ else
+ # 1.1.x
+ SHLIB_VERSION="$MAJOR.$MINOR"
+ fi
+
+ if [ -n "$PRE_RELEASE_TAG" ]; then
+ _PRE_RELEASE_TAG="-${PRE_RELEASE_TAG}"
+ fi
+
+ SERIES="$MAJOR.$MINOR.$FIX"
+ VERSION="$MAJOR.$MINOR.$FIX$PATCH"
+ TYPE=$PRE_RELEASE_TAG
+ PRE_LABEL=
+ PRE_NUM=0
+ ;;
+ * )
+ ;;
+ esac
}
# $1 is one of "alpha", "beta", "final", "", or "minor"
;;
final | '' )
if [ "$TYPE" = 'dev' ]; then
- PATCH=$(expr $PATCH + 1)
+ case "$VERSION_FILE" in
+ VERSION.dat )
+ PATCH=$(expr $PATCH + 1)
+ ;;
+ */opensslv.h )
+ local -A patch_transitions
+ patch_transitions=(
+ [_]=a [_a]=b [_b]=c [_c]=d [_d]=e [_e]=f
+ [_f]=g [_g]=h [_h]=i [_i]=j [_j]=k [_k]=l
+ [_l]=m [_m]=n [_n]=o [_o]=p [_p]=q [_q]=r
+ [_r]=s [_s]=t [_t]=u [_u]=v [_v]=w [_w]=x
+ [_x]=y [_y]=za
+ )
+ PATCH=$( eval set -- "$(echo $PATCH | sed -E -e 's|^(z*)([a-y]?)$|"\1" "\2"|')"
+ echo $1${patch_transitions[_$2]} )
+ ;;
+ esac
fi
PRE_LABEL=
PRE_NUM=0
;;
minor )
if [ "$TYPE" = 'dev' ]; then
- MINOR=$(expr $MINOR + 1)
- PATCH=0
+ case "$VERSION_FILE" in
+ VERSION.dat )
+ MINOR=$(expr $MINOR + 1)
+ PATCH=0
+ ;;
+ */opensslv.h )
+ # Minor release updated the FIX number
+ FIX=$(expr $FIX + 1)
+ PATCH=
+ ;;
+ esac
fi
PRE_LABEL=
PRE_NUM=0
;;
esac
- VERSION="$MAJOR.$MINOR.$PATCH"
- SERIES="$MAJOR.$MINOR"
-}
-
-set_version () {
case "$TYPE+$PRE_LABEL+$PRE_NUM" in
*++* )
PRE_RELEASE_TAG="$TYPE"
PRE_RELEASE_TAG="$PRE_LABEL$PRE_NUM"
;;
esac
- if [ -n "$PRE_RELEASE_TAG" ]; then _PRE_RELEASE_TAG="-${PRE_RELEASE_TAG}"; fi
- cat > "$SOURCEDIR/VERSION.dat" <<EOF
+
+ _PRE_RELEASE_TAG=
+ if [ -n "$PRE_RELEASE_TAG" ]; then
+ _PRE_RELEASE_TAG="-${PRE_RELEASE_TAG}"
+ fi
+
+ case "$VERSION_FILE" in
+ VERSION.dat )
+ SERIES="$MAJOR.$MINOR"
+ VERSION="$SERIES.$PATCH"
+ ;;
+ */opensslv.h )
+ SERIES="$MAJOR.$MINOR.$FIX"
+ VERSION="$SERIES$PATCH"
+ ;;
+ esac
+}
+
+set_version () {
+ case "$VERSION_FILE" in
+ VERSION.dat )
+ cat > "$VERSION_FILE" <<EOF
MAJOR=$MAJOR
MINOR=$MINOR
PATCH=$PATCH
RELEASE_DATE="$RELEASE_DATE"
SHLIB_VERSION=$SHLIB_VERSION
EOF
+ ;;
+ */opensslv.h )
+ local version_updater='
+BEGIN {
+ my $TYPE="'"$TYPE"'";
+ my $MAJOR='"$MAJOR"';
+ my $MINOR='"$MINOR"';
+ my $FIX='"$FIX"';
+ my $PATCH="'"$PATCH"'";
+
+ $PATCH =~ m|^(z)*(.)$|;
+ my $PP = length($1) * 25 + ord($2) - ord("a") + 1;
+
+ our $version_number = sprintf("%x%02x%02x%02x%x",
+ $MAJOR, $MINOR, $FIX, $PP,
+ $TYPE eq "dev" ? 0 : 0xf);
+ our $version_text = sprintf("%d.%d.%d%s", $MAJOR, $MINOR, $FIX, $PATCH);
+ our $version_tag="'"$_PRE_RELEASE_TAG"'";
+ our $release_date="'"$RELEASE_DATE"'";
+ our $shlib_version="'"$SHLIB_VERSION"'";
+
+ $release_date = "xx XXX xxxx" unless ($release_date);
+}
+
+s|^([[:space:]]*#[[:space:]]*define[[:space:]]+OPENSSL_VERSION_NUMBER[[:space:]]+0x)[[:xdigit:]]+L$|$1${version_number}L|;
+s|^([[:space:]]*#[[:space:]]*define[[:space:]]+OPENSSL_VERSION_TEXT[[:space:]]+)"OpenSSL \d+\.\d+\.\dz*[a-x]?(-fips)?(-dev)? [^"]+"$|$1"OpenSSL ${version_text}$2${version_tag} $release_date"|;
+s|^([[:space:]]*#[[:space:]]*define[[:space:]]+SHLIB_VERSION_NUMBER[[:space:]]+)"[^"]*"$|$1"${shlib_version}"|;
+'
+ perl -pi -e "$version_updater" "$VERSION_FILE"
+ ;;
+ esac
+}
+
+std_branch_name () {
+ case "$VERSION_FILE" in
+ VERSION.dat )
+ echo "openssl-${SERIES}"
+ ;;
+ */opensslv.h )
+ echo "OpenSSL_${SERIES//./_}-stable"
+ ;;
+ esac
+}
+
+std_tag_name () {
+ case "$VERSION_FILE" in
+ VERSION.dat )
+ echo "openssl-$VERSION$_PRE_RELEASE_TAG$_BUILD_METADATA"
+ ;;
+ */opensslv.h )
+ echo "OpenSSL_${VERSION//./_}"
+ ;;
+ esac
}
--- /dev/null
+#! /bin/bash
+# Copyright 2023 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the Apache License 2.0 (the "License"). You may not use
+# this file except in compliance with the License. You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+# This script runs a test suite to check the functions in release-state-fn.sh
+# and release-version-fn.sh. It does this by setting up a small temporary
+# repository with just enough fake data (in include/openssl/opensslv.h or
+# VERSION.dat) to see that version data is updated correctly.
+
+DEBUG=:
+export LANG=C
+
+HERE=$(cd $(dirname $0); pwd)
+. $HERE/release-state-fn.sh
+. $HERE/release-version-fn.sh
+
+today="$(date '+%-d %b %Y')"
+
+repo=release-test-$$.git
+git init --quiet /var/tmp/$repo
+cd /var/tmp/$repo
+trap "cd $HERE; rm -rf /var/tmp/$repo" EXIT
+
+echo "===== OpenSSL 3.0 version scheme"
+
+cat > VERSION.dat <<_____
+MAJOR=3
+MINOR=2
+PATCH=0
+PRE_RELEASE_TAG=dev
+BUILD_METADATA=
+RELEASE_DATE=""
+SHLIB_VERSION=3
+_____
+git add VERSION.dat
+git commit -m 'Fake 3.2.0-dev' --quiet
+
+declare -A expected
+
+function check () {
+ local errs=0
+
+ for key in "${!expected[@]}"; do
+ if [ "${!key}" != "${expected[$key]}" ]; then
+ (( errs++ ))
+ fi
+ done
+
+ if [ $errs -gt 0 ]; then
+ echo >&2 "Got the wrong data:"
+ for key in "${!expected[@]}"; do
+ echo >&2 " \$$key=${!key}"
+ done
+ echo >&2 "Expected:"
+ for key in "${!expected[@]}"; do
+ echo >&2 " \$$key=${expected[$key]}"
+ done
+ exit 1
+ fi
+}
+
+echo "Initial read of VERSION.DAT"
+expected=(
+ [TYPE]=dev
+ [SERIES]=3.2
+ [VERSION]=3.2.0
+ [PRE_RELEASE_TAG]=dev
+ [SHLIB_VERSION]=3
+)
+get_version
+check
+
+echo "Test release of 3.2.0-alpha1"
+expected=(
+ [TYPE]=
+ [VERSION]=3.2.0
+ [PRE_RELEASE_TAG]=alpha1
+ [RELEASE_DATE]="$today"
+)
+next_release_state alpha
+check
+
+echo "Test post-release of 3.2.0-alpha1"
+expected=(
+ [TYPE]=dev
+ [VERSION]=3.2.0
+ [PRE_RELEASE_TAG]=alpha2-dev
+ [RELEASE_DATE]=
+)
+next_release_state alpha
+check
+
+echo "Test release of 3.2.0-beta1"
+expected=(
+ [TYPE]=
+ [VERSION]=3.2.0
+ [PRE_RELEASE_TAG]=beta1
+ [RELEASE_DATE]="$today"
+)
+next_release_state beta
+check
+
+echo "Test post-release of 3.2.0-beta1"
+expected=(
+ [TYPE]=dev
+ [VERSION]=3.2.0
+ [PRE_RELEASE_TAG]=beta2-dev
+ [RELEASE_DATE]=
+)
+next_release_state beta
+check
+
+echo "Test release of 3.2.0"
+expected=(
+ [TYPE]=
+ [VERSION]=3.2.0
+ [PRE_RELEASE_TAG]=
+ [RELEASE_DATE]="$today"
+)
+next_release_state final
+check
+
+echo "Test post-release of 3.2.0"
+expected=(
+ [TYPE]=dev
+ [VERSION]=3.2.1
+ [PRE_RELEASE_TAG]=dev
+ [RELEASE_DATE]=
+)
+next_release_state final
+check
+
+echo "Test release of 3.2.1"
+expected=(
+ [TYPE]=
+ [VERSION]=3.2.1
+ [PRE_RELEASE_TAG]=
+ [RELEASE_DATE]="$today"
+)
+next_release_state ''
+check
+
+echo "Test post-release of 3.2.1"
+expected=(
+ [TYPE]=dev
+ [VERSION]=3.2.2
+ [PRE_RELEASE_TAG]=dev
+ [RELEASE_DATE]=
+)
+next_release_state ''
+check
+
+echo "Test switch to next minor release (3.3.0-dev)"
+expected=(
+ [TYPE]=dev
+ [VERSION]=3.3.0
+ [PRE_RELEASE_TAG]=dev
+ [RELEASE_DATE]=
+)
+next_release_state minor
+check
+
+echo "Test writing $VERSION_FILE"
+set_version
+cat > expected-VERSION.dat <<_____
+MAJOR=3
+MINOR=3
+PATCH=0
+PRE_RELEASE_TAG=dev
+BUILD_METADATA=
+RELEASE_DATE=""
+SHLIB_VERSION=3
+_____
+if ! diff_output="$(diff -u expected-VERSION.dat VERSION.dat)"; then
+ echo >&2 "$diff_output"
+ exit 1
+fi
+
+echo "===== OpenSSL 1.0.2 version scheme"
+
+git restore .
+git rm --quiet VERSION.dat
+mkdir crypto
+cat > crypto/opensslv.h <<_____
+# define OPENSSL_VERSION_NUMBER 0x10002210L
+# ifdef OPENSSL_FIPS
+# define OPENSSL_VERSION_TEXT "OpenSSL 1.0.2zh-fips-dev xx XXX xxxx"
+# else
+# define OPENSSL_VERSION_TEXT "OpenSSL 1.0.2zh-dev xx XXX xxxx"
+# endif
+# define OPENSSL_VERSION_PTEXT " part of " OPENSSL_VERSION_TEXT
+_____
+touch openssl.spec
+git add openssl.spec crypto/opensslv.h
+git commit -m 'Fake 1.0.2zh-dev' --quiet
+
+echo "Test initial read of crypto/opensslv.h"
+expected=(
+ [TYPE]=dev
+ [SERIES]=1.0.2
+ [VERSION]=1.0.2zh
+ [PRE_RELEASE_TAG]=dev
+ [SHLIB_VERSION]=1.0.0
+)
+get_version
+check
+
+echo "Test release of 1.0.2zh"
+expected=(
+ [TYPE]=
+ [VERSION]=1.0.2zh
+ [PRE_RELEASE_TAG]=
+ [RELEASE_DATE]="$today"
+)
+next_release_state ''
+check
+
+echo "Test post-release of 1.0.2zh"
+expected=(
+ [TYPE]=dev
+ [VERSION]=1.0.2zi
+ [PRE_RELEASE_TAG]=dev
+ [RELEASE_DATE]=
+)
+next_release_state ''
+check
+
+echo "Test writing $VERSION_FILE"
+set_version
+cat > crypto/expected-opensslv.h <<_____
+# define OPENSSL_VERSION_NUMBER 0x10002220L
+# ifdef OPENSSL_FIPS
+# define OPENSSL_VERSION_TEXT "OpenSSL 1.0.2zi-fips-dev xx XXX xxxx"
+# else
+# define OPENSSL_VERSION_TEXT "OpenSSL 1.0.2zi-dev xx XXX xxxx"
+# endif
+# define OPENSSL_VERSION_PTEXT " part of " OPENSSL_VERSION_TEXT
+_____
+if ! diff_output="$(diff -u crypto/expected-opensslv.h crypto/opensslv.h)"; then
+ echo >&2 "$diff_output"
+ exit 1
+fi
+
+echo "===== PASS ====="