1
0
Fork 0
mirror of https://github.com/rbenv/ruby-build.git synced 2025-06-17 09:18:06 +02:00

Automatically link to Homebrew OpenSSL

If a system OpenSSL version was not found or is at version that is incompatible with a Ruby being installed, ruby-build would typically download and compile a new OpenSSL version scoped to that Ruby installation.

Now the `needs_openssl` condition will also check for Homebrew-installed OpenSSL and automatically link to the first one found that satisfies the version requirement. This primarily helps speed up Ruby installation on macOS where the system OpenSSL is never compatible and where Homebrew is a de-facto standard package manager.
This commit is contained in:
Mislav Marohnić 2023-10-13 21:10:45 +02:00
parent 8f294c43ad
commit 31e53468b6
No known key found for this signature in database
3 changed files with 170 additions and 32 deletions

View file

@ -1023,6 +1023,8 @@ use_freebsd_libffi() {
fi
}
# macOS prevents linking to its system OpenSSL/LibreSSL installation, so
# it's basically useless for Ruby purposes.
has_broken_mac_openssl() {
is_mac || return 1
local openssl_version
@ -1030,47 +1032,103 @@ has_broken_mac_openssl() {
[[ $openssl_version = "OpenSSL 0.9.8"?* || $openssl_version = "LibreSSL"* ]]
}
# Detect the OpenSSL version that a compiler can reasonably link to.
system_openssl_version() {
local version_text
version_text=$(printf '#include <openssl/opensslv.h>\nOPENSSL_VERSION_TEXT\n' | cc -xc -E - 2>/dev/null || true)
if [[ $version_text == *"OpenSSL "* ]]; then
local version=${version_text#*OpenSSL }
# shellcheck disable=SC2001
sed 's/[^0-9]//g' <<<"${version%% *}" | sed 's/^0*//'
else
echo "No system openssl version was found, ensure openssl headers are installed (https://github.com/rbenv/ruby-build/wiki#suggested-build-environment)" >&2
echo 000
cc -xc -E - <<EOF 2>/dev/null | sed -n 's/OpenSSL \([0-9][0-9.]*\).*/\1/p'
#include <openssl/opensslv.h>
OPENSSL_VERSION_TEXT
EOF
}
# List all Homebrew-installed OpenSSL versions and their filesystem prefixes.
homebrew_openssl_versions() {
local formula version prefix
# https://github.com/orgs/Homebrew/discussions/4845
brew list 2>/dev/null | grep '^openssl@' | while read -r formula; do
prefix="$(brew --prefix "$formula" 2>/dev/null || true)"
[ -n "$prefix" ] || continue
version="$("$prefix"/bin/openssl version 2>/dev/null | sed -n 's/OpenSSL \([0-9][0-9.]*\).*/\1/p')"
[ -z "$version" ] || printf '%s %s %s\n' "$formula" "$version" "$prefix"
done
}
# Normalizes "X.Y.Z" into a comparable numeric value. Does not support prereleases.
# See also osx_version, require_java
normalize_semver() {
local ver
IFS=. read -d "" -r -a ver <<<"$1" || true
IFS="$OLDIFS"
# 3.1.23 -> 300_123
echo $(( ver[0]*100000 + ver[1]*100 + ver[2] ))
}
# Checks if system OpenSSL does NOT satisfy the version requirement
# between lower and upper bounds. This is used by build definitions to
# conditionally install per-ruby OpenSSL.
#
# If a compatible Homebrew-installed OpenSSL version is found during
# checking, Ruby will be linked to it and the check will return false.
needs_openssl() {
[[ "$RUBY_CONFIGURE_OPTS ${RUBY_CONFIGURE_OPTS_ARRAY[*]}" != *--with-openssl-dir=* ]] || return 1
local system_version
if ! has_broken_mac_openssl; then
system_version="$(system_openssl_version)"
fi
# With no arguments, any system OpenSSL satisfies the check.
if [ $# -lt 2 ]; then
[ -z "$system_version" ] || return 1
return 0
fi
local lower_bound upper_bound
lower_bound="$(normalize_semver "${2%-*}")"
upper_bound="${2#*-}"
upper_bound="$(normalize_semver "${upper_bound//.x/.99}")"
system_version="$(normalize_semver "$system_version")"
# Return early if system openssl satisfies the requirement.
(( system_version < lower_bound || system_version >= upper_bound )) || return 1
# Look for the latest Homebrew-installed OpenSSL that satisfies the requirement
local brew_installs
brew_installs="$(homebrew_openssl_versions)"
[ -n "$brew_installs" ] || return 0
# Link to the highest-matching Homebrew OpenSSL
local versions homebrew_version formula version prefix
# shellcheck disable=SC2207
versions=( $(awk '{print $2}' <<<"$brew_installs" | sort_versions) )
local index="${#versions[@]}"
while [ $((index--)) -ge 0 ]; do
homebrew_version="$(normalize_semver "${versions[index]}")"
(( homebrew_version >= lower_bound && homebrew_version < upper_bound )) || continue
while read -r formula version prefix; do
[ "$version" = "${versions[index]}" ] || continue
echo "ruby-build: using $formula from homebrew"
package_option ruby configure --with-openssl-dir="$prefix"
return 1
done <<<"$brew_installs"
done
}
# openssl gem 1.1.1
# Kept for backward compatibility with 3rd-party Ruby definitions.
needs_openssl_096_102() {
[[ "$RUBY_CONFIGURE_OPTS ${RUBY_CONFIGURE_OPTS_ARRAY[*]}" == *--with-openssl-dir=* ]] && return 1
has_broken_mac_openssl && return 0
local version
version="$(system_openssl_version)"
(( version < 96 || version >= 110 ))
# openssl gem 1.1.1
needs_openssl "$1" "0.9.6-1.0.x"
}
# openssl gem 2.2.1
# Kept for backward compatibility with 3rd-party Ruby definitions.
needs_openssl_101_111() {
[[ "$RUBY_CONFIGURE_OPTS ${RUBY_CONFIGURE_OPTS_ARRAY[*]}" == *--with-openssl-dir=* ]] && return 1
has_broken_mac_openssl && return 0
local version
version="$(system_openssl_version)"
(( version < 101 || version >= 300 ))
# openssl gem 2.2.1
needs_openssl "$1" "1.0.1-1.x.x"
}
# openssl gem 3.0.0
# Kept for backward compatibility with 3rd-party Ruby definitions.
needs_openssl_102_300() {
[[ "$RUBY_CONFIGURE_OPTS ${RUBY_CONFIGURE_OPTS_ARRAY[*]}" == *--with-openssl-dir=* ]] && return 1
has_broken_mac_openssl && return 0
local version
version="$(system_openssl_version)"
(( version < 102 || version >= 400 ))
# openssl gem 3.0.0
needs_openssl "$1" "1.0.2-3.x.x"
}
# Kept for backward compatibility with 3rd-party Ruby definitions.

View file

@ -316,6 +316,33 @@ make install
OUT
}
@test "use system OpenSSL" {
cached_tarball "ruby-2.0.0" configure
stub_repeated uname '-s : echo Linux'
stub_repeated brew false
# shellcheck disable=SC2016
stub cc '-xc -E - : [[ "$(cat)" == *OPENSSL_VERSION_TEXT* ]] && printf "# <unrelated> 4.0.2\nOpenSSL 1.0.3a 1 Aug 202\n0 errors.\n"'
stub_make_install
mkdir -p "$INSTALL_ROOT"/openssl/ssl # OPENSSLDIR
run_inline_definition <<DEF
install_package "openssl-1.1.1w" "https://www.openssl.org/source/openssl-1.1.1w.tar.gz" openssl --if needs_openssl_102_300
install_package "ruby-2.0.0" "http://ruby-lang.org/ruby/2.0/ruby-2.0.0.tar.gz"
DEF
assert_success
unstub uname
unstub brew
unstub make
assert_build_log <<OUT
ruby-2.0.0: [--prefix=$INSTALL_ROOT]
make -j 2
make install
OUT
}
@test "install bundled OpenSSL" {
cached_tarball "openssl-1.1.1w" config
cached_tarball "ruby-2.0.0" configure
@ -339,6 +366,8 @@ DEF
unstub uname
unstub brew
unstub cc
# unstub openssl
unstub make
assert_build_log <<OUT
@ -375,6 +404,56 @@ make install
OUT
}
@test "link to Homebrew OpenSSL" {
cached_tarball "ruby-2.0.0" configure
local homebrew_prefix="${TMP}/homebrew"
executable "${homebrew_prefix}/opt/openssl@3/bin/openssl" <<EXE
#!/$BASH
[ "\$1" = "version" ] || exit 1
echo 'OpenSSL 3.2.1 20 Dec 2019'
EXE
executable "${homebrew_prefix}/opt/openssl@3.1/bin/openssl" <<EXE
#!/$BASH
[ "\$1" = "version" ] || exit 1
echo 'OpenSSL 3.1.22 20 Dec 2019'
EXE
executable "${homebrew_prefix}/opt/openssl@3.0/bin/openssl" <<EXE
#!/$BASH
[ "\$1" = "version" ] || exit 1
echo 'OpenSSL 3.0.2 20 Dec 2019'
EXE
executable "${homebrew_prefix}/opt/openssl@1.1/bin/openssl" <<EXE
#!/$BASH
[ "\$1" = "version" ] || exit 1
echo 'OpenSSL 1.1.1v 20 Dec 2019'
EXE
stub_repeated uname '-s : echo Linux'
stub cc '-xc -E - : echo "OpenSSL 1.0.1a 1 Aug 2023"'
stub_repeated brew \
'list : printf "git\nopenssl@3\nopenssl-utils\nopenssl@1.1\nopenssl@3.0\nwget\nopenssl@3.1"' \
"--prefix : echo '$homebrew_prefix'/opt/\$2 "
stub_make_install
run_inline_definition <<DEF
install_package "openssl-1.1.1w" "https://www.openssl.org/source/openssl-1.1.1w.tar.gz" openssl --if needs_openssl:1.1.0-3.0.x
install_package "ruby-2.0.0" "http://ruby-lang.org/ruby/2.0/ruby-2.0.0.tar.gz"
DEF
assert_success
unstub uname
unstub cc
unstub brew
unstub make
assert_build_log <<OUT
ruby-2.0.0: [--prefix=$INSTALL_ROOT,--with-openssl-dir=$TMP/homebrew/opt/openssl@3.0]
make -j 2
make install
OUT
}
@test "forward extra command-line arguments as configure flags" {
cached_tarball "ruby-2.0.0" configure

View file

@ -44,8 +44,9 @@ inspect_args() {
}
# Loop over each line in the plan.
IFS=$'\n' read -d '' -r -a lines < "${!_STUB_PLAN}" || true
index=0
while IFS= read -r line; do
for line in "${lines[@]}"; do
index=$(($index + 1))
if [[ -z "${!_STUB_END}" && -n "${!_STUB_NOINDEX}" || $index -eq "${!_STUB_INDEX}" ]]; then
@ -93,7 +94,7 @@ while IFS= read -r line; do
eval "${_STUB_RESULT}"=1
fi
fi
done < "${!_STUB_PLAN}"
done
if [ -n "${!_STUB_END}" ]; then