pyenv/plugins/python-build/bin/python-build
2014-03-14 22:51:34 +09:00

1499 lines
37 KiB
Bash
Executable file

#!/usr/bin/env bash
PYTHON_BUILD_VERSION="20140225"
set -E
exec 3<&2 # preserve original stderr at fd 3
lib() {
parse_options() {
OPTIONS=()
ARGUMENTS=()
local arg option index
for arg in "$@"; do
if [ "${arg:0:1}" = "-" ]; then
if [ "${arg:1:1}" = "-" ]; then
OPTIONS[${#OPTIONS[*]}]="${arg:2}"
else
index=1
while option="${arg:$index:1}"; do
[ -n "$option" ] || break
OPTIONS[${#OPTIONS[*]}]="$option"
index=$(($index+1))
done
fi
else
ARGUMENTS[${#ARGUMENTS[*]}]="$arg"
fi
done
}
if [ "$1" == "--$FUNCNAME" ]; then
declare -f "$FUNCNAME"
echo "$FUNCNAME \"\$1\";"
exit
fi
}
lib "$1"
resolve_link() {
$(type -p greadlink readlink | head -1) "$1"
}
abs_dirname() {
local cwd="$(pwd)"
local path="$1"
while [ -n "$path" ]; do
cd "${path%/*}"
local name="${path##*/}"
path="$(resolve_link "$name" || true)"
done
pwd
cd "$cwd"
}
capitalize() {
printf "%s" "$1" | tr a-z A-Z
}
sanitize() {
printf "%s" "$1" | sed "s/[^A-Za-z0-9.-]/_/g; s/__*/_/g"
}
colorize() {
if [ -t 1 ]; then printf "\e[%sm%s\e[m" "$1" "$2"
else echo -n "$2"
fi
}
build_failed() {
{ echo
echo "BUILD FAILED"
echo
if ! rmdir "${BUILD_PATH}" 2>/dev/null; then
echo "Inspect or clean up the working tree at ${BUILD_PATH}"
if file_is_not_empty "$LOG_PATH"; then
echo "Results logged to ${LOG_PATH}"
echo
echo "Last 10 log lines:"
tail -n 10 "$LOG_PATH"
fi
fi
} >&3
exit 1
}
file_is_not_empty() {
local filename="$1"
local line_count="$(wc -l "$filename" 2>/dev/null || true)"
if [ -n "$line_count" ]; then
words=( $line_count )
[ "${words[0]}" -gt 0 ]
else
return 1
fi
}
num_cpu_cores() {
local num=""
if [ "Darwin" = "$(uname -s)" ]; then
num="$(sysctl -n hw.ncpu 2>/dev/null || true)"
elif [ -r /proc/cpuinfo ]; then
num="$(grep ^processor /proc/cpuinfo | wc -l)"
[ "$num" -gt 0 ] || num=""
fi
echo "${num:-2}"
}
install_package() {
install_package_using "tarball" 1 "$@"
}
install_git() {
install_package_using "git" 2 "$@"
}
install_hg() {
install_package_using "hg" 2 "$@"
}
install_svn() {
install_package_using "svn" 2 "$@"
}
install_jar() {
install_package_using "jar" 1 "$@"
}
install_zip() {
install_package_using "zip" 1 "$@"
}
install_script() {
install_package_using "script" 1 "$@"
}
install_package_using() {
local package_type="$1"
local package_type_nargs="$2"
local package_name="$3"
shift 3
local fetch_args=( "$package_name" "${@:1:$package_type_nargs}" )
local make_args=( "$package_name" )
local arg last_arg
for arg in "${@:$(( $package_type_nargs + 1 ))}"; do
if [ "$last_arg" = "--if" ]; then
"$arg" || return 0
elif [ "$arg" != "--if" ]; then
make_args["${#make_args[@]}"]="$arg"
fi
last_arg="$arg"
done
pushd "$BUILD_PATH" >&4
"fetch_${package_type}" "${fetch_args[@]}"
make_package "${make_args[@]}"
popd >&4
{ echo "Installed ${package_name} to ${PREFIX_PATH}"
echo
} >&2
}
make_package() {
local package_name="$1"
shift
pushd "$package_name" >&4
setup_builtin_patches "$package_name"
before_install_package "$package_name"
build_package "$package_name" $*
after_install_package "$package_name"
cleanup_builtin_patches "$package_name"
fix_directory_permissions
popd >&4
}
compute_md5() {
if type md5 &>/dev/null; then
md5 -q
elif type openssl &>/dev/null; then
local output="$(openssl md5)"
echo "${output##* }"
elif type md5sum &>/dev/null; then
local output="$(md5sum -b)"
echo "${output% *}"
else
return 1
fi
}
verify_checksum() {
# If there's no MD5 support, return success
[ -n "$HAS_MD5_SUPPORT" ] || return 0
# If the specified filename doesn't exist, return success
local filename="$1"
[ -e "$filename" ] || return 0
# If there's no expected checksum, return success
local expected_checksum=`echo "$2" | tr [A-Z] [a-z]`
[ -n "$expected_checksum" ] || return 0
# If the computed checksum is empty, return failure
local computed_checksum=`echo "$(compute_md5 < "$filename")" | tr [A-Z] [a-z]`
[ -n "$computed_checksum" ] || return 1
if [ "$expected_checksum" != "$computed_checksum" ]; then
{ echo
echo "checksum mismatch: ${filename} (file is corrupt)"
echo "expected $expected_checksum, got $computed_checksum"
echo
} >&4
return 1
fi
}
http() {
local method="$1"
local url="$2"
local file="$3"
[ -n "$url" ] || return 1
if type curl &>/dev/null; then
"http_${method}_curl" "$url" "$file"
elif type wget &>/dev/null; then
"http_${method}_wget" "$url" "$file"
else
echo "error: please install \`curl\` or \`wget\` and try again" >&2
exit 1
fi
}
http_head_curl() {
curl -qsILf "$1" >&4 2>&1
}
http_get_curl() {
curl -C - -o "${2:--}" -qsSLf "$1"
}
http_head_wget() {
wget -q --spider "$1" >&4 2>&1
}
http_get_wget() {
wget -nv -c -O "${2:--}" "$1"
}
fetch_tarball() {
local package_name="$1"
local package_url="$2"
local mirror_url
local checksum
if [ "$package_url" != "${package_url/\#}" ]; then
checksum="${package_url#*#}"
package_url="${package_url%%#*}"
if [ -n "$PYTHON_BUILD_MIRROR_URL" ]; then
mirror_url="${PYTHON_BUILD_MIRROR_URL}/$checksum"
fi
fi
local tar_args="xzvf"
local package_filename="${package_name}.tar.gz"
if [ "$package_url" != "${package_url%tgz}" ]; then
package_filename="${package_filename%tar.gz}tgz"
fi
if [ "$package_url" != "${package_url%bz2}" ]; then
package_filename="${package_filename%.gz}.bz2"
tar_args="${tar_args/z/j}"
fi
if ! symlink_tarball_from_cache "$package_filename" "$checksum"; then
echo "Downloading ${package_filename}..." >&2
http head "$mirror_url" &&
download_tarball "$mirror_url" "$package_filename" "$checksum" ||
download_tarball "$package_url" "$package_filename" "$checksum"
fi
{ if tar $tar_args "$package_filename"; then
if [ -z "$KEEP_BUILD_PATH" ]; then
rm -f "$package_filename"
else
true
fi
fi
} >&4 2>&1
}
symlink_tarball_from_cache() {
[ -n "$PYTHON_BUILD_CACHE_PATH" ] || return 1
local package_filename="$1"
local cached_package_filename="${PYTHON_BUILD_CACHE_PATH}/$package_filename"
local checksum="$2"
[ -e "$cached_package_filename" ] || return 1
verify_checksum "$cached_package_filename" "$checksum" >&4 2>&1 || return 1
ln -s "$cached_package_filename" "$package_filename" >&4 2>&1 || return 1
}
download_tarball() {
local package_url="$1"
[ -n "$package_url" ] || return 1
local package_filename="$2"
local checksum="$3"
echo "-> $package_url" >&2
if http get "$package_url" "$package_filename" >&4 2>&1; then
verify_checksum "$package_filename" "$checksum" >&4 2>&1 || return 1
else
echo "error: failed to download $package_filename" >&2
return 1
fi
if [ -n "$PYTHON_BUILD_CACHE_PATH" ]; then
local cached_package_filename="${PYTHON_BUILD_CACHE_PATH}/$package_filename"
{ mv "$package_filename" "$cached_package_filename"
ln -s "$cached_package_filename" "$package_filename"
} >&4 2>&1 || return 1
fi
}
fetch_git() {
local package_name="$1"
local git_url="$2"
local git_ref="$3"
echo "Cloning ${git_url}..." >&2
if type git &>/dev/null; then
if [ -n "$PYTHON_BUILD_CACHE_PATH" ]; then
pushd "$PYTHON_BUILD_CACHE_PATH" >&4
local clone_name="$(sanitize "$git_url")"
if [ -e "${clone_name}" ]; then
{ cd "${clone_name}"
git fetch --force "$git_url" "+${git_ref}:${git_ref}"
} >&4 2>&1
else
git clone --bare --branch "$git_ref" "$git_url" "${clone_name}" >&4 2>&1
fi
git_url="$PYTHON_BUILD_CACHE_PATH/${clone_name}"
popd >&4
fi
git clone --depth 1 --branch "$git_ref" "$git_url" "${package_name}" >&4 2>&1
else
echo "error: please install \`git\` and try again" >&2
exit 1
fi
}
fetch_hg() {
local package_name="$1"
local hg_url="$2"
local hg_ref="$3"
echo "Cloning ${hg_url}..." >&2
if type hg &>/dev/null; then
if [ -n "$PYTHON_BUILD_CACHE_PATH" ]; then
pushd "$PYTHON_BUILD_CACHE_PATH" >&4
local clone_name="$(sanitize "$hg_url")"
if [ -e "${clone_name}" ]; then
{ cd "${clone_name}"
hg pull --force "$hg_url"
} >&4 2>&1
else
{ hg clone --branch "$hg_ref" "$hg_url" "${clone_name}"
cd "${clone_name}"
hg update null
} >&4 2>&1
fi
hg_url="$PYTHON_BUILD_CACHE_PATH/${clone_name}"
popd >&4
fi
hg clone --branch "$hg_ref" "$hg_url" "${package_name}" >&4 2>&1
else
echo "error: please install \`hg\` and try again" >&2
exit 1
fi
}
fetch_svn() {
local package_name="$1"
local svn_url="$2"
local svn_rev="$3"
echo "Checking out ${svn_url}..." >&2
if type svn &>/dev/null; then
svn co -r "$svn_rev" "$svn_url" "${package_name}" >&4 2>&1
else
echo "error: please install \`svn\` and try again" >&2
exit 1
fi
}
fetch_jar() {
local package_name="$1"
local package_url="$2"
local mirror_url
local checksum
if [ "$package_url" != "${package_url/\#}" ]; then
checksum="${package_url#*#}"
package_url="${package_url%%#*}"
if [ -n "$PYTHON_BUILD_MIRROR_URL" ]; then
mirror_url="${PYTHON_BUILD_MIRROR_URL}/$checksum"
fi
fi
local package_filename="${package_name}.jar"
if ! symlink_tarball_from_cache "$package_filename" "$checksum"; then
echo "Downloading ${package_filename}..." >&2
http head "$mirror_url" &&
download_tarball "$mirror_url" "$package_filename" "$checksum" ||
download_tarball "$package_url" "$package_filename" "$checksum"
fi
{ if $JAVA -jar ${package_name}.jar -s -d ${package_name}; then
if [ -z "$KEEP_BUILD_PATH" ]; then
rm -f "$package_filename"
else
true
fi
fi
} >&4 2>&1
}
fetch_zip() {
local package_name="$1"
local package_url="$2"
local mirror_url
local checksum
if [ "$package_url" != "${package_url/\#}" ]; then
checksum="${package_url#*#}"
package_url="${package_url%%#*}"
if [ -n "$PYTHON_BUILD_MIRROR_URL" ]; then
mirror_url="${PYTHON_BUILD_MIRROR_URL}/$checksum"
fi
fi
local package_filename="${package_name}.zip"
if ! symlink_tarball_from_cache "$package_filename" "$checksum"; then
echo "Downloading ${package_filename}..." >&2
http head "$mirror_url" &&
download_tarball "$mirror_url" "$package_filename" "$checksum" ||
download_tarball "$package_url" "$package_filename" "$checksum"
fi
{ if unzip "$package_filename"; then
if [ -z "$KEEP_BUILD_PATH" ]; then
rm -f "$package_filename"
else
true
fi
fi
} >&4 2>&1
}
fetch_script() {
local package_name="$1"
local package_url="$2"
local mirror_url
local checksum
if [ "$package_url" != "${package_url/\#}" ]; then
checksum="${package_url#*#}"
package_url="${package_url%%#*}"
if [ -n "$PYTHON_BUILD_MIRROR_URL" ]; then
mirror_url="${PYTHON_BUILD_MIRROR_URL}/$checksum"
fi
fi
local package_filename="${package_name}.sh" # TODO: extract suffix from ${package_url}
if ! symlink_tarball_from_cache "$package_filename" "$checksum"; then
echo "Downloading ${package_filename}..." >&2
http head "$mirror_url" &&
download_tarball "$mirror_url" "$package_filename" "$checksum" ||
download_tarball "$package_url" "$package_filename" "$checksum"
fi
mkdir -p "$(dirname "${package_name}/${package_filename}")"
mv -f "${package_filename}" "${package_name}/${package_filename}"
}
build_package() {
local package_name="$1"
shift
if [ "$#" -eq 0 ]; then
local commands="standard"
else
local commands="$*"
fi
echo "Installing ${package_name}..." >&2
[ -n "$HAS_PATCH" ] && apply_python_patch "$package_name"
for command in $commands; do
"build_package_${command}" "$package_name"
done
}
package_option() {
local package_name="$1"
local command_name="$2"
local variable="$(capitalize "${package_name}_${command_name}")_OPTS_ARRAY"
local array="$variable[@]"
shift 2
local value=( "${!array}" "$@" )
eval "$variable=( \"\${value[@]}\" )"
}
build_package_standard() {
local package_name="$1"
if [ "${MAKEOPTS+defined}" ]; then
MAKE_OPTS="$MAKEOPTS"
elif [ -z "${MAKE_OPTS+defined}" ]; then
MAKE_OPTS="-j $(num_cpu_cores)"
fi
# Support YAML_CONFIGURE_OPTS, PYTHON_CONFIGURE_OPTS, etc.
local package_var_name="$(capitalize "${package_name%%-*}")"
local PACKAGE_CONFIGURE="${package_var_name}_CONFIGURE"
local PACKAGE_PREFIX_PATH="${package_var_name}_PREFIX_PATH"
local PACKAGE_CONFIGURE_OPTS="${package_var_name}_CONFIGURE_OPTS"
local PACKAGE_CONFIGURE_OPTS_ARRAY="${package_var_name}_CONFIGURE_OPTS_ARRAY[@]"
local PACKAGE_MAKE_OPTS="${package_var_name}_MAKE_OPTS"
local PACKAGE_MAKE_OPTS_ARRAY="${package_var_name}_MAKE_OPTS_ARRAY[@]"
local PACKAGE_MAKE_INSTALL_OPTS="${package_var_name}_MAKE_INSTALL_OPTS"
local PACKAGE_MAKE_INSTALL_OPTS_ARRAY="${package_var_name}_MAKE_INSTALL_OPTS_ARRAY[@]"
local PACKAGE_CFLAGS="${package_var_name}_CFLAGS"
[ "$package_var_name" = "PYTHON" ] && use_homebrew_readline || true
( if [ "${CFLAGS+defined}" ] || [ "${!PACKAGE_CFLAGS+defined}" ]; then
export CFLAGS="$CFLAGS ${!PACKAGE_CFLAGS}"
fi
${!PACKAGE_CONFIGURE:-./configure} --prefix="${!PACKAGE_PREFIX_PATH:-$PREFIX_PATH}" \
--libdir="${!PACKAGE_PREFIX_PATH:-$PREFIX_PATH}/lib" \
$CONFIGURE_OPTS ${!PACKAGE_CONFIGURE_OPTS} "${!PACKAGE_CONFIGURE_OPTS_ARRAY}"
) >&4 2>&1
{ "$MAKE" $MAKE_OPTS ${!PACKAGE_MAKE_OPTS} "${!PACKAGE_MAKE_OPTS_ARRAY}"
"$MAKE" install $MAKE_INSTALL_OPTS ${!PACKAGE_MAKE_INSTALL_OPTS} "${!PACKAGE_MAKE_INSTALL_OPTS_ARRAY}"
} >&4 2>&1
}
build_package_autoconf() {
{ autoconf
} >&4 2>&1
}
build_package_ruby() {
local package_name="$1"
{ "$RUBY_BIN" setup.rb
} >&4 2>&1
}
build_package_python() {
local package_name="$1"
{ "$PYTHON_BIN" setup.py install
} >&4 2>&1
}
build_package_ree_installer() {
build_package_auto_tcltk
local options=""
if [[ "Darwin" = "$(uname)" ]]; then
options="--no-tcmalloc"
fi
local option
for option in $RUBY_CONFIGURE_OPTS ${RUBY_CONFIGURE_OPTS_ARRAY[@]}; do
options="$options -c $option"
done
# Work around install_useful_libraries crash with --dont-install-useful-gems
mkdir -p "$PREFIX_PATH/lib/ruby/gems/1.8/gems"
{ ./installer --auto "$PREFIX_PATH" --dont-install-useful-gems $options $CONFIGURE_OPTS
} >&4 2>&1
}
build_package_rbx() {
local package_name="$1"
{ bundle --path=vendor/bundle
RUBYOPT="-rubygems $RUBYOPT" ./configure --prefix="$PREFIX_PATH" $RUBY_CONFIGURE_OPTS
rake install
fix_rbx_gem_binstubs "$PREFIX_PATH"
fix_rbx_irb "$PREFIX_PATH"
} >&4 2>&1
}
build_package_mruby() {
local package_name="$1"
{ rake
mkdir -p "$PREFIX_PATH"
cp -fR build/host/* "$PREFIX_PATH"
cd "$PREFIX_PATH/bin"
ln -fs mruby ruby
ln -fs mirb irb
} >&4 2>&1
}
build_package_maglev() {
build_package_copy
{ cd "${PREFIX_PATH}"
./install.sh
cd "${PREFIX_PATH}/bin"
echo "Creating symlink for ruby*"
ln -fs maglev-ruby ruby
echo "Creating symlink for irb*"
ln -fs maglev-irb irb
} >&4 2>&1
echo
echo "Run 'maglev start' to start up the stone before using 'ruby' or 'irb'"
}
build_package_topaz() {
build_package_copy
{ cd "${PREFIX_PATH}/bin"
echo "Creating symlink for ruby*"
ln -fs topaz ruby
} >&4 2>&1
}
topaz_architecture() {
case "$(uname -s)" in
"Darwin") echo "osx64";;
"Linux") [[ "$(uname -m)" = "x86_64" ]] && echo "linux64" || echo "linux32";;
*)
echo "no nightly builds available" >&2
exit 1;;
esac
}
build_package_jruby() {
build_package_copy
cd "${PREFIX_PATH}/bin"
ln -fs jruby ruby
chmod +x ruby
install_jruby_launcher
remove_windows_files
fix_jruby_shebangs
}
graal_architecture() {
if [ "$(uname -m)" != "x86_64" ]; then
echo "no nightly builds available" >&2
exit 1
fi
case "$(uname -s)" in
"Darwin") echo "macosx-x86_64";;
"Linux") echo "linux-x86_64";;
*)
echo "no nightly builds available" >&2
exit 1;;
esac
}
install_jruby_launcher() {
# If this version of JRuby has been modified for Graal, don't overwrite the
# launcher scripts
if ! grep -q graalvm "${PREFIX_PATH}/bin/jruby"; then
cd "${PREFIX_PATH}/bin"
{ ./ruby gem install jruby-launcher
} >&4 2>&1
fi
}
fix_jruby_shebangs() {
for file in "${PREFIX_PATH}/bin"/*; do
if [ "$(head -c 20 "$file")" = "#!/usr/bin/env jruby" ]; then
sed -i.bak "1 s:.*:#\!${PREFIX_PATH}\/bin\/jruby:" "$file"
rm "$file".bak
fi
done
}
remove_windows_files() {
cd "$PREFIX_PATH"
rm -f bin/*.exe bin/*.dll bin/*.bat bin/jruby.sh
}
build_package_jython() {
build_package_copy
{ if [ -x "${PREFIX_PATH}/bin/jython" ] && [ ! -x "${PREFIX_PATH}/bin/python" ]; then
( cd "${PREFIX_PATH}/bin" && ln -fs jython python )
fi
} >&4 2>&1
}
build_package_jython_builder() {
ant >&4 2>&1
( cd "dist" && build_package_jython )
}
pypy_architecture() {
case "$(uname -s)" in
"Darwin" ) echo "osx64" ;;
"Linux" )
case "$(uname -m)" in
"armel" ) echo "linux-armel" ;;
"armhf" | "armv6l" | "armv7l" ) echo "linux-armhf" ;;
"i386" | "i486" | "i586" | "i686" | "i786" ) echo "linux" ;;
"x86_64" ) echo "linux64" ;;
* ) return 1 ;;
esac
;;
"CYGWIN"* | "MINGW"* ) echo "win32" ;;
* ) return 1 ;;
esac
}
build_package_pypy() {
build_package_copy
{ if [ -x "${PREFIX_PATH}/bin/pypy" ] && [ ! -x "${PREFIX_PATH}/bin/python" ]; then
( cd "${PREFIX_PATH}/bin" && ln -fs pypy python )
fi
} >&4 2>&1
}
build_package_pypy_builder() {
if [ -f "rpython/bin/rpython" ]; then # pypy 2.x
python "rpython/bin/rpython" ${PYPY_OPTS:-"-Ojit"} "pypy/goal/targetpypystandalone.py" >&4 2>&1
elif [ -f "pypy/translator/goal/translate.py" ]; then # pypy 1.x
( cd "pypy/translator/goal" && python "translate.py" ${PYPY_OPTS:-"--opt=jit"} "targetpypystandalone.py" ) 1>&4 2>&1
else
echo "not a pypy source tree" 1>&3
return 1
fi
if [ -x "pypy-c" ] && [ ! -x "bin/pypy" ]; then
mkdir -p "bin"
mv -f "pypy-c" "bin/pypy"
fi
build_package_pypy
}
anaconda_architecture() {
case "$(uname -s)" in
"Darwin" ) echo "MacOSX-x86_64" ;;
"Linux" )
case "$(uname -m)" in
"i386" | "i486" | "i586" | "i686" | "i786" ) echo "Linux-x86" ;;
"x86_64" ) echo "Linux-x86_64" ;;
* ) return 1 ;;
esac
;;
* ) return 1 ;;
esac
}
build_package_anaconda() {
local package_name="$1"
{ bash "${package_name}.sh" -b -p "${PREFIX_PATH}"
} >&4 2>&1
}
build_package_miniconda() {
build_package_anaconda "$@"
"${PREFIX_PATH}/bin/conda" install --yes "pip"
}
build_package_copy() {
mkdir -p "$PREFIX_PATH"
cp -fR . "$PREFIX_PATH"
}
before_install_package() {
local stub=1
}
after_install_package() {
local stub=1
}
setup_builtin_patches() {
local package_name="$1"
local package_patch_path="${PYTHON_BUILD_ROOT}/share/python-build/patches/${DEFINITION_PATH##*/}/${package_name}"
ORIG_HAS_PATCH="$HAS_PATCH"
# Apply built-in patches if patch was not given from stdin
if [ -z "$HAS_PATCH" ] && [ -d "${package_patch_path}" ]; then
local patch
{ for patch in "${package_patch_path}"/*; do
[ -f "${patch}" ] && echo "${patch}"
done
} 2>/dev/null | sort | xargs cat 1>"${package_name}.patch"
exec <&-
exec <"${package_name}.patch"
HAS_PATCH=true
fi
}
cleanup_builtin_patches() {
local package_name="$1"
rm -f "${package_name}.patch"
HAS_PATCH="$ORIG_HAS_PATCH"
}
fix_directory_permissions() {
# Ensure installed directories are not world-writable to avoid Bundler warnings
find "$PREFIX_PATH" -type d \( -perm -020 -o -perm -002 \) -exec chmod go-w {} \;
}
fix_rbx_gem_binstubs() {
local prefix="$1"
local gemdir="${prefix}/gems/bin"
local bindir="${prefix}/bin"
local file binstub
# Symlink Rubinius' `gems/bin/` into `bin/`
if [ -d "$gemdir" ]; then
for file in "$gemdir"/*; do
binstub="${bindir}/${file##*/}"
rm -f "$binstub"
sed -E "s:^#\!.+:#\!${bindir}/ruby:" < "$file" > "$binstub"
chmod +x "$binstub"
done
rm -rf "$gemdir"
ln -s ../bin "$gemdir"
fi
}
fix_rbx_irb() {
local prefix="$1"
"${prefix}/bin/irb" --version &>/dev/null ||
"${prefix}/bin/gem" install rubysl-tracer -v '~> 2.0' --no-rdoc --no-ri &>/dev/null ||
true
}
require_java7() {
local version="$(java -version 2>&1 | head -1)"
if [[ $version != *1.[789]* ]]; then
colorize 1 "ERROR" >&3
echo ": Java 7 required. Please install a 1.7-compatible JRE." >&3
return 1
fi
}
require_gcc() {
local gcc="$(locate_gcc || true)"
if [ -z "$gcc" ]; then
{ echo
colorize 1 "ERROR"
echo ": This package must be compiled with GCC, but python-build couldn't"
echo "find a suitable \`gcc\` executable on your system. Please install GCC"
echo "and try again."
echo
if [ "$(uname -s)" = "Darwin" ]; then
colorize 1 "DETAILS"
echo ": Apple no longer includes the official GCC compiler with Xcode"
echo "as of version 4.2. Instead, the \`gcc\` executable is a symlink to"
echo "\`llvm-gcc\`, a modified version of GCC which outputs LLVM bytecode."
echo
echo "For most programs the \`llvm-gcc\` compiler works fine. However,"
echo "versions of CPython newer than 3.3.0 are incompatible with"
echo "\`llvm-gcc\`. To build newer versions of CPython you must have the official"
echo "GCC compiler installed on your system."
echo
colorize 1 "TO FIX THE PROBLEM"
if type brew &>/dev/null; then
echo ": Install Homebrew's apple-gcc42 package with this"
echo -n "command: "
colorize 4 "brew tap homebrew/dupes ; brew install apple-gcc42"
else
echo ": Install the official GCC compiler using these"
echo -n "packages: "
colorize 4 "https://github.com/kennethreitz/osx-gcc-installer/downloads"
fi
echo
echo
echo "You will need to install the official GCC compiler to build newer"
echo "versions of CPython even if you have installed Apple's Command Line Tools"
echo "for Xcode package. The Command Line Tools for Xcode package only"
echo "includes \`llvm-gcc\`."
fi
} >&3
return 1
fi
export CC="$gcc"
}
locate_gcc() {
local gcc gccs
IFS=: gccs=($(gccs_in_path))
verify_gcc "$CC" ||
verify_gcc "$(command -v gcc || true)" || {
for gcc in "${gccs[@]}"; do
verify_gcc "$gcc" && break || true
done
}
return 1
}
gccs_in_path() {
local gcc path paths
local gccs=()
IFS=: paths=($PATH)
shopt -s nullglob
for path in "${paths[@]}"; do
for gcc in "$path"/gcc-*; do
gccs["${#gccs[@]}"]="$gcc"
done
done
shopt -u nullglob
printf :%s "${gccs[@]}"
}
verify_gcc() {
local gcc="$1"
if [ -z "$gcc" ]; then
return 1
fi
local version="$("$gcc" --version || true)"
if [ -z "$version" ]; then
return 1
fi
if echo "$version" | grep LLVM >/dev/null; then
return 1
fi
echo "$gcc"
}
require_cc() {
local cc=("$@")
while [ -n "${cc}" ]; do
{ if [ "${#cc[@]}" -le 1 ]; then
"require_${cc}" # display last error
else
"require_${cc}" 3>/dev/null
fi
} && return 0
cc=("${cc[@]:1}")
done
return 1
}
require_clang() {
local clang="$(command -v "$CC" || command -v "clang" || true)"
if [ -z "$clang" ]; then
local esc=$'\033'
{ echo
echo "${esc}[1mERROR${esc}[0m: This package must be compiled with clang, but python-build couldn't"
echo "find a suitable \`clang\` executable on your system. Please install clang"
echo "and try again."
echo
} >&3
return 1
fi
export CC="$clang"
}
require_java() {
local java="$(command -v java || true)"
if [ -z "$java" ]; then
local esc=$'\033'
{ echo
echo "${esc}[1mERROR${esc}[0m: This package must be installed with java, but python-build couldn't"
echo "find a suitable \`java\` executable on your system. Please install Java"
echo "and try again."
echo
} >&3
return 1
fi
export JAVA="$java"
}
needs_yaml() {
! use_homebrew_yaml
}
use_homebrew_yaml() {
local libdir="$(brew --prefix libyaml 2>/dev/null || true)"
if [ -d "$libdir" ]; then
package_option python configure CPPFLAGS="-I$libdir/include" LDFLAGS="-L$libdir/lib"
else
return 1
fi
}
configured_with_readline_dir() {
# Mac OS X 10.4 has broken readline.
# https://github.com/yyuu/pyenv/issues/23
local arg flag
for arg in ${CONFIGURE_OPTS} ${PYTHON_CONFIGURE_OPTS} "${PYTHON_CONFIGURE_OPTS_ARRAY[@]}"; do
if [[ "$arg" == "CPPFLAGS="* ]]; then
for flag in ${CPPFLAGS} ${arg##CPPFLAGS=}; do
if [[ "$flag" == "-I"* ]] && [ -e "${flag##-I}/readline/rlconf.h" ]; then
return 0
fi
done
fi
done
return 1
}
has_broken_mac_readline() {
[ "$(uname -s)" = "Darwin" ] &&
! configured_with_readline_dir &&
! use_homebrew_readline
}
use_homebrew_readline() {
if ! configured_with_readline_dir; then
local libdir="$(brew --prefix readline 2>/dev/null || true)"
if [ -d "$libdir" ]; then
package_option python configure CPPFLAGS="-I$libdir/include" LDFLAGS="-L$libdir/lib"
else
return 1
fi
fi
}
has_broken_mac_openssl() {
[ "$(uname -s)" = "Darwin" ] &&
[[ "$(openssl version 2>/dev/null || true)" = "OpenSSL 0.9.8"?* ]] &&
! use_homebrew_openssl
}
use_homebrew_openssl() {
local ssldir="$(brew --prefix openssl 2>/dev/null || true)"
if [ -d "$ssldir" ]; then
package_option python configure CPPFLAGS="-I$ssldir/include" LDFLAGS="-L$ssldir/lib"
else
return 1
fi
}
build_package_mac_openssl() {
# Install to a subdirectory since we don't want shims for bin/openssl.
OPENSSL_PREFIX_PATH="${PREFIX_PATH}/openssl"
# Put openssl.conf, certs, etc in ~/.pyenv/versions/*/openssl/ssl
OPENSSLDIR="${OPENSSLDIR:-$OPENSSL_PREFIX_PATH/ssl}"
# Tell Python to use this openssl for its extension.
package_option python configure CPPFLAGS="-I${OPENSSL_PREFIX_PATH}/include" LDFLAGS="-L${OPENSSL_PREFIX_PATH}/lib"
# Hint OpenSSL that we prefer a 64-bit build.
export KERNEL_BITS="64"
OPENSSL_CONFIGURE="${OPENSSL_CONFIGURE:-./config}"
# Compile a shared lib with zlib dynamically linked, no kerberos.
package_option openssl configure --openssldir="$OPENSSLDIR" zlib-dynamic no-krb5 shared
# Default MAKE_OPTS are -j 2 which can confuse the build. Thankfully, make
# gives precedence to the last -j option, so we can override that.
package_option openssl make -j 1
build_package_standard "$@"
# Extract root certs from the system keychain in .pem format and rehash.
local pem_file="$OPENSSLDIR/cert.pem"
security find-certificate -a -p /Library/Keychains/System.keychain > "$pem_file"
security find-certificate -a -p /System/Library/Keychains/SystemRootCertificates.keychain >> "$pem_file"
}
# Post-install check that the openssl extension was built.
build_package_verify_openssl() {
"$RUBY_BIN" -e 'begin
require "openssl"
rescue LoadError
abort "The Ruby openssl extension was not compiled. Missing the OpenSSL lib?"
end' >&4 2>&1
}
# Ensure that directories listed in LDFLAGS exist
build_package_ldflags_dirs() {
local arg
for arg in $LDFLAGS; do
case "$arg" in
-L* ) mkdir -p "${arg#-L}" ;;
esac
done
}
build_package_auto_tcltk() {
if [ "Darwin" = "$(uname -s)" ] && [ ! -d /usr/include/X11 ]; then
if [ -d /opt/X11/include ]; then
if [[ "$CPPFLAGS" != *-I/opt/X11/include* ]]; then
export CPPFLAGS="-I/opt/X11/include $CPPFLAGS"
fi
else
package_option python configure --without-tk
fi
fi
}
rake() {
if [ -e "./Gemfile" ]; then
bundle exec rake "$@"
else
isolated_gem_dependency "rake --version" rake -v '~> 10.1.0'
command rake "$@"
fi
}
bundle() {
isolated_gem_dependency "bundle --version" bundler -v '~> 1.3.5'
command bundle "$@"
}
isolated_gem_dependency() {
set +E
( command $1 &>/dev/null ) || {
set -E
shift 1
isolated_gem_install "$@"
}
set -E
}
isolated_gem_install() {
export GEM_HOME="${PWD}/.gem"
export PATH="${GEM_HOME}/bin:${PATH}"
gem install "$@"
}
apply_python_patch() {
case "$1" in
Python-* | jython-* | pypy-* )
patch -p0 -i "${2:--}"
;;
esac
}
verify_python() {
local python="$1"
if [[ "$PYTHON_CONFIGURE_OPTS" == *"--enable-framework"* ]]; then
# Only symlinks are installed in ${PREFIX_PATH}/bin
rm -fr "${PREFIX_PATH}/bin"
ln -fs "${PREFIX_PATH}/Python.framework/Versions/Current/bin" "${PREFIX_PATH}/bin"
fi
if [ ! -e "${PREFIX_PATH}/bin/python" ] && [ -e "${PREFIX_PATH}/bin/${python}" ]; then
( cd "${PREFIX_PATH}/bin" && ln -fs "${python}" "python" )
fi
if [ ! -x "${PYTHON_BIN}" ]; then
{ colorize 1 "ERROR"
echo ": invalid Python executable: ${PYTHON_BIN}"
echo
echo "The python-build could not find proper executable of Python after successful build."
echo "Please open an issue for future improvements."
echo "https://github.com/yyuu/pyenv/issues"
return 1
} >&3
fi
}
try_python_module() {
if ! "$PYTHON_BIN" -c "import $1" 1>/dev/null 2>&1; then
{ colorize 1 "WARNING"
echo ": The Python $1 extension was not compiled. Missing the ${2:-$1}?"
return 0
} >&3
fi
}
verify_python_module() {
if ! "$PYTHON_BIN" -c "import $1" 1>/dev/null 2>&1; then
{ colorize 1 "ERROR"
echo ": The Python $1 extension was not compiled. Missing the ${2:-$1}?"
echo
echo "Please consult to the Wiki page to fix the problem."
echo "https://github.com/yyuu/pyenv/wiki/Common-build-problems"
echo
return 1
} >&3
fi
}
# Post-install check for Python 2.4.x
build_package_verify_py24() {
verify_python "${2:-python2.4}"
try_python_module "readline" "GNU readline lib"
verify_python_module "zlib" "zlib"
try_python_module "bz2" "bzip2 lib"
}
# Post-install check for Python 2.5.x
build_package_verify_py25() {
build_package_verify_py24 "$1" "${2:-python2.5}"
try_python_module "sqlite3" "SQLite3 lib"
}
# Post-install check for Python 2.6.x
build_package_verify_py26() {
build_package_verify_py25 "$1" "${2:-python2.6}"
verify_python_module "ssl" "OpenSSL lib"
}
# Post-install check for Python 2.7.x
build_package_verify_py27() {
build_package_verify_py26 "$1" "${2:-python2.7}"
}
# Post-install check for Python 3.0.x
build_package_verify_py30() {
verify_python "${2:-python3.0}"
try_python_module "bz2" "bzip2 lib"
try_python_module "readline" "GNU readline lib"
verify_python_module "ssl" "OpenSSL lib"
try_python_module "sqlite3" "SQLite3 lib"
verify_python_module "zlib" "zlib"
}
# Post-install check for Python 3.1.x
build_package_verify_py31() {
build_package_verify_py30 "$1" "${2:-python3.1}"
}
# Post-install check for Python 3.2.x
build_package_verify_py32() {
build_package_verify_py31 "$1" "${2:-python3.2}"
}
# Post-install check for Python 3.3.x
build_package_verify_py33() {
build_package_verify_py32 "$1" "${2:-python3.3}"
}
# Post-install check for Python 3.4.x
build_package_verify_py34() {
build_package_verify_py33 "$1" "${2:-python3.4}"
}
build_package_ensurepip() {
{ "$PYTHON_BIN" -m ensurepip
} >&4 2>&1
if [ ! -e "${PREFIX_PATH}/bin/pip" ]; then
local pip="$("$PYTHON_BIN" -c 'import sys;v=sys.version_info;sys.stdout.write("pip%d.%d"%(v[0],v[1]))')"
if [ -e "${PREFIX_PATH}/bin/${pip}" ]; then
( cd "${PREFIX_PATH}/bin" && ln -fs "${pip}" "pip" )
fi
fi
}
version() {
echo "python-build ${PYTHON_BUILD_VERSION}"
}
usage() {
{ version
echo "usage: python-build [-k|--keep] [-v|--verbose] [-p|--patch] [-g|--debug] definition prefix"
echo " python-build --definitions"
} >&2
if [ -z "$1" ]; then
exit 1
fi
}
list_definitions() {
{ for definition in "${PYTHON_BUILD_ROOT}/share/python-build/"*; do
[ -f "${definition}" ] && echo "${definition##*/}"
done
} | sort
}
unset VERBOSE
unset KEEP_BUILD_PATH
unset HAS_PATCH
PYTHON_BUILD_ROOT="$(abs_dirname "$0")/.."
unset DEBUG
parse_options "$@"
for option in "${OPTIONS[@]}"; do
case "$option" in
"h" | "help" )
usage without_exiting
{ echo
echo " -k/--keep Do not remove source tree after installation"
echo " -v/--verbose Verbose mode: print compilation status to stdout"
echo " -p/--patch Apply a patch from stdin before building"
echo " -g/--debug Build a debug version"
echo " --definitions List all built-in definitions"
echo
} >&2
exit 0
;;
"definitions" )
list_definitions
exit 0
;;
"k" | "keep" )
KEEP_BUILD_PATH=true
;;
"v" | "verbose" )
VERBOSE=true
;;
"p" | "patch" )
HAS_PATCH=true
;;
"g" | "debug" )
DEBUG=true
;;
"version" )
version
exit 0
;;
esac
done
DEFINITION_PATH="${ARGUMENTS[0]}"
if [ -z "$DEFINITION_PATH" ]; then
usage
elif [ ! -f "$DEFINITION_PATH" ]; then
BUILTIN_DEFINITION_PATH="${PYTHON_BUILD_ROOT}/share/python-build/${DEFINITION_PATH}"
if [ -e "$BUILTIN_DEFINITION_PATH" ]; then
DEFINITION_PATH="$BUILTIN_DEFINITION_PATH"
else
echo "python-build: definition not found: ${DEFINITION_PATH}" >&2
exit 2
fi
fi
PREFIX_PATH="${ARGUMENTS[1]}"
if [ -z "$PREFIX_PATH" ]; then
usage
elif [ "${PREFIX_PATH#/}" = "$PREFIX_PATH" ]; then
PREFIX_PATH="${PWD}/${PREFIX_PATH}"
fi
if [ -z "$TMPDIR" ]; then
TMP="/tmp"
else
TMP="${TMPDIR%/}"
fi
if [ ! -w "$TMP" ] || [ ! -x "$TMP" ]; then
echo "python-build: TMPDIR=$TMP is set to a non-accessible location" >&2
exit 1
fi
# Work around warnings building Ruby 2.0 on Clang 2.x:
# pass -Wno-error=shorten-64-to-32 if the compiler accepts it.
#
# When we set CFLAGS, Ruby won't apply its default flags, though. Since clang
# builds 1.9.x and 2.x only, where -O3 is default, we can safely set that flag.
# Ensure it's the first flag since later flags take precedence.
#if "${CC:-cc}" -x c /dev/null -E -Wno-error=shorten-64-to-32 &>/dev/null; then
# PYTHON_CFLAGS="-O3 -Wno-error=shorten-64-to-32 $PYTHON_CFLAGS"
#fi
if [ -z "$MAKE" ]; then
if [[ "FreeBSD" = "$(uname -s)" ]]; then
export MAKE="gmake"
else
export MAKE="make"
fi
fi
if [ -n "$PYTHON_BUILD_CACHE_PATH" ] && [ -d "$PYTHON_BUILD_CACHE_PATH" ]; then
PYTHON_BUILD_CACHE_PATH="${PYTHON_BUILD_CACHE_PATH%/}"
else
unset PYTHON_BUILD_CACHE_PATH
fi
if [ -z "$PYTHON_BUILD_MIRROR_URL" ]; then
PYTHON_BUILD_MIRROR_URL="http://yyuu.github.io/pythons"
else
PYTHON_BUILD_MIRROR_URL="${PYTHON_BUILD_MIRROR_URL%/}"
fi
if [ -n "$PYTHON_BUILD_SKIP_MIRROR" ]; then
unset PYTHON_BUILD_MIRROR_URL
fi
if echo test | compute_md5 >/dev/null; then
HAS_MD5_SUPPORT=1
else
unset HAS_MD5_SUPPORT
unset PYTHON_BUILD_MIRROR_URL
fi
# Add an option to build a debug version of Python (#11)
if [ -n "$DEBUG" ]; then
package_option python configure --with-pydebug
fi
# python-build: Set `RPATH` if `--enable-shared` was given (#65, #66, 82)
if [[ "$CONFIGURE_OPTS" == *"--enable-shared"* ]] || [[ "$PYTHON_CONFIGURE_OPTS" == *"--enable-shared"* ]]; then
# The ld on Darwin embeds the full paths to each dylib by default
if [[ "$LDFLAGS" != *"-rpath="* ]] && [[ "Darwin" != "$(uname -s)" ]]; then
export LDFLAGS="-Wl,-rpath=${PREFIX_PATH}/lib ${LDFLAGS}"
fi
fi
# Add support for framework installation (`--enable-framework`) of CPython (#55, #99)
if [[ "$PYTHON_CONFIGURE_OPTS" == *"--enable-framework"* ]]; then
create_framework_dirs() {
local version="$(echo "$1" | sed -E 's/^[^0-9]*([0-9]+\.[0-9]+).*$/\1/')"
mkdir -p "${PREFIX_PATH}/Python.framework/Versions/${version}"
( cd "${PREFIX_PATH}/Python.framework/Versions" && ln -fs "${version}" "Current")
local path
for path in include lib share; do
mkdir -p "${PREFIX_PATH}/Python.framework/Versions/Current/${path}"
ln -fs "${PREFIX_PATH}/Python.framework/Versions/Current/${path}" "${PREFIX_PATH}/${path}"
done
}
create_framework_dirs "${DEFINITION_PATH##*/}"
package_option python configure --enable-framework="${PREFIX_PATH}"
fi
# SSL Certificate error with older wget that does not support Server Name Indication (#60)
if ! command -v curl 1>/dev/null 2>&1 && [[ "$(wget --version 2>/dev/null || true)" = "GNU Wget 1.1"[0-3]* ]]; then
echo "python-build: wget (< 1.14) doesn't support Server Name Indication. Please install curl (>= 7.18.1) and try again" >&2
exit 1
fi
# pydistutils.cfg may corrupt install location of Python libraries (#35, #111)
if [ -e "$HOME/.pydistutils.cfg" ]; then
echo "python-build: Please make sure you remove any previous custom paths from your $HOME/.pydistutils.cfg file." >&2
exit 1
fi
SEED="$(date "+%Y%m%d%H%M%S").$$"
LOG_PATH="${TMP}/python-build.${SEED}.log"
PYTHON_BIN="${PREFIX_PATH}/bin/python"
CWD="$(pwd)"
if [ -z "$PYTHON_BUILD_BUILD_PATH" ]; then
BUILD_PATH="${TMP}/python-build.${SEED}"
else
BUILD_PATH="$PYTHON_BUILD_BUILD_PATH"
fi
exec 4<> "$LOG_PATH" # open the log file at fd 4
if [ -n "$VERBOSE" ]; then
tail -f "$LOG_PATH" &
TAIL_PID=$!
trap "kill $TAIL_PID" SIGINT SIGTERM EXIT
fi
export LDFLAGS="-L${PREFIX_PATH}/lib ${LDFLAGS}"
export CPPFLAGS="-I${PREFIX_PATH}/include ${CPPFLAGS}"
unset PYTHONHOME
unset PYTHONPATH
trap build_failed ERR
mkdir -p "$BUILD_PATH"
source "$DEFINITION_PATH"
[ -z "${KEEP_BUILD_PATH}" ] && rm -fr "$BUILD_PATH"
trap - ERR