|
|
@ -0,0 +1,527 @@ |
|
|
|
#!/usr/bin/env bash |
|
|
|
|
|
|
|
PYTHON_BUILD_VERSION="20120815" |
|
|
|
|
|
|
|
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" |
|
|
|
} |
|
|
|
|
|
|
|
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 |
|
|
|
} |
|
|
|
|
|
|
|
install_package() { |
|
|
|
install_package_using "tarball" 1 $* |
|
|
|
} |
|
|
|
|
|
|
|
install_git() { |
|
|
|
install_package_using "git" 2 $* |
|
|
|
} |
|
|
|
|
|
|
|
install_jar() { |
|
|
|
install_package_using "jar" 1 $* |
|
|
|
} |
|
|
|
|
|
|
|
install_package_using() { |
|
|
|
local package_type="$1" |
|
|
|
local package_type_nargs="$2" |
|
|
|
local package_name="$3" |
|
|
|
shift 3 |
|
|
|
|
|
|
|
pushd "$BUILD_PATH" >&4 |
|
|
|
"fetch_${package_type}" "$package_name" $* |
|
|
|
shift $(($package_type_nargs)) |
|
|
|
make_package "$package_name" $* |
|
|
|
popd >&4 |
|
|
|
|
|
|
|
echo "Installed ${package_name} to ${PREFIX_PATH}" >&2 |
|
|
|
} |
|
|
|
|
|
|
|
make_package() { |
|
|
|
local package_name="$1" |
|
|
|
shift |
|
|
|
|
|
|
|
pushd "$package_name" >&4 |
|
|
|
before_install_package "$package_name" |
|
|
|
build_package "$package_name" $* |
|
|
|
after_install_package "$package_name" |
|
|
|
fix_directory_permissions |
|
|
|
popd >&4 |
|
|
|
} |
|
|
|
|
|
|
|
fetch_url() { |
|
|
|
if type curl &>/dev/null; then |
|
|
|
curl -L "$@" |
|
|
|
elif type wget &>/dev/null; then |
|
|
|
wget -O- "$@" |
|
|
|
else |
|
|
|
echo "error: please install \`curl\` or \`wget\` and try again" >&2 |
|
|
|
exit 1 |
|
|
|
fi |
|
|
|
} |
|
|
|
|
|
|
|
fetch_tarball() { |
|
|
|
local package_name="$1" |
|
|
|
local package_url="$2" |
|
|
|
|
|
|
|
echo "Downloading ${package_url}..." >&2 |
|
|
|
{ fetch_url "$package_url" > "${package_name}.tar" |
|
|
|
tar xvf "${package_name}.tar" |
|
|
|
} >&4 2>&1 |
|
|
|
} |
|
|
|
|
|
|
|
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 |
|
|
|
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_jar() { |
|
|
|
local package_name="$1" |
|
|
|
local package_url="$2" |
|
|
|
|
|
|
|
echo "Downloading ${package_url}..." >&2 |
|
|
|
{ fetch_url "$package_url" > "${package_name}.jar" |
|
|
|
$JAVA -jar ${package_name}.jar -s -d ${package_name} |
|
|
|
} >&4 2>&1 |
|
|
|
} |
|
|
|
|
|
|
|
build_package() { |
|
|
|
local package_name="$1" |
|
|
|
shift |
|
|
|
|
|
|
|
if [ "$#" -eq 0 ]; then |
|
|
|
local commands="standard" |
|
|
|
else |
|
|
|
local commands="$*" |
|
|
|
fi |
|
|
|
|
|
|
|
echo "Installing ${package_name}..." >&2 |
|
|
|
|
|
|
|
for command in $commands; do |
|
|
|
"build_package_${command}" "${package_name}" |
|
|
|
done |
|
|
|
|
|
|
|
if [ ! -f "$PYTHON_BIN" ]; then |
|
|
|
for python in ${PREFIX_PATH}/bin/python*; do |
|
|
|
if basename "$python" | grep '^python[0-9][0-9]*\.[0-9][0-9]*$' >/dev/null; then |
|
|
|
{ |
|
|
|
cd ${PREFIX_PATH}/bin |
|
|
|
ln -fs "$(basename $python)" python |
|
|
|
} |
|
|
|
break |
|
|
|
fi |
|
|
|
done |
|
|
|
fi |
|
|
|
} |
|
|
|
|
|
|
|
build_package_standard() { |
|
|
|
local package_name="$1" |
|
|
|
|
|
|
|
if [ "${MAKEOPTS+defined}" ]; then |
|
|
|
MAKE_OPTS="$MAKEOPTS" |
|
|
|
elif [ -z "${MAKE_OPTS+defined}" ]; then |
|
|
|
MAKE_OPTS="-j 2" |
|
|
|
fi |
|
|
|
|
|
|
|
{ ./configure --prefix="$PREFIX_PATH" $CONFIGURE_OPTS |
|
|
|
make $MAKE_OPTS |
|
|
|
make install |
|
|
|
} >&4 2>&1 |
|
|
|
} |
|
|
|
|
|
|
|
build_package_autoconf() { |
|
|
|
{ autoconf |
|
|
|
} >&4 2>&1 |
|
|
|
} |
|
|
|
|
|
|
|
build_package_python() { |
|
|
|
local package_name="$1" |
|
|
|
|
|
|
|
{ |
|
|
|
"$PYTHON_BIN" setup.py install |
|
|
|
} >&4 2>&1 |
|
|
|
} |
|
|
|
|
|
|
|
build_package_jython() { |
|
|
|
{ |
|
|
|
build_package_copy |
|
|
|
cd "${PREFIX_PATH}/bin" |
|
|
|
ln -fs jython python |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
build_package_pypy() { |
|
|
|
{ |
|
|
|
mkdir -p "$PREFIX_PATH" |
|
|
|
cp -R . "$PREFIX_PATH" |
|
|
|
cd "${PREFIX_PATH}/bin" |
|
|
|
ln -fs pypy python |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
build_package_copy() { |
|
|
|
mkdir -p "$PREFIX_PATH" |
|
|
|
cp -R . "$PREFIX_PATH" |
|
|
|
} |
|
|
|
|
|
|
|
bild_package_noop() { |
|
|
|
echo "Nothing to do." |
|
|
|
} |
|
|
|
|
|
|
|
before_install_package() { |
|
|
|
local stub=1 |
|
|
|
} |
|
|
|
|
|
|
|
after_install_package() { |
|
|
|
local stub=1 |
|
|
|
} |
|
|
|
|
|
|
|
fix_directory_permissions() { |
|
|
|
# Ensure installed directories are not world-writable to avoid Bundler warnings |
|
|
|
find "$PREFIX_PATH" -type d -exec chmod go-w {} \; |
|
|
|
} |
|
|
|
|
|
|
|
require_gcc() { |
|
|
|
local gcc="$(locate_gcc || true)" |
|
|
|
|
|
|
|
if [ -z "$gcc" ]; then |
|
|
|
local esc=$'\033' |
|
|
|
{ echo |
|
|
|
echo "${esc}[1mERROR${esc}[0m: 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 |
|
|
|
echo "${esc}[1mDETAILS${esc}[0m: 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 Python older than 1.9.3-p125 are incompatible with" |
|
|
|
echo "\`llvm-gcc\`. To build older versions of Python you must have the official" |
|
|
|
echo "GCC compiler installed on your system." |
|
|
|
echo |
|
|
|
echo "${esc}[1mTO FIX THE PROBLEM${esc}[0m: Install the official GCC compiler using these" |
|
|
|
echo "packages: ${esc}[4mhttps://github.com/kennethreitz/osx-gcc-installer/downloads${esc}[0m" |
|
|
|
echo |
|
|
|
echo "You will need to install the official GCC compiler to build older" |
|
|
|
echo "versions of Python 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_java() { |
|
|
|
local java="$(locate_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" |
|
|
|
} |
|
|
|
|
|
|
|
locate_java() { |
|
|
|
local java javas |
|
|
|
IFS=: javas=($(javas_in_path)) |
|
|
|
|
|
|
|
verify_java "$JAVA" || |
|
|
|
verify_java "$(command -v java || true)" || { |
|
|
|
for java in "${javas[@]}"; do |
|
|
|
verify_java "$java" && break || true |
|
|
|
done |
|
|
|
} |
|
|
|
|
|
|
|
return 1 |
|
|
|
} |
|
|
|
|
|
|
|
javas_in_path() { |
|
|
|
local java path paths |
|
|
|
local javas=() |
|
|
|
IFS=: paths=($PATH) |
|
|
|
|
|
|
|
shopt -s nullglob |
|
|
|
for path in "${paths[@]}"; do |
|
|
|
local java="$path"/java |
|
|
|
if [ -x "$java" ]; then |
|
|
|
javas["${#javas[@]}"]="$java" |
|
|
|
fi |
|
|
|
done |
|
|
|
shopt -u nullglob |
|
|
|
|
|
|
|
printf :%s "${javas[@]}" |
|
|
|
} |
|
|
|
|
|
|
|
verify_java() { |
|
|
|
local java="$1" |
|
|
|
if [ -z "$java" ]; then |
|
|
|
return 1 |
|
|
|
fi |
|
|
|
|
|
|
|
if [ ! -x "$java" ]; then |
|
|
|
return 1 |
|
|
|
fi |
|
|
|
|
|
|
|
echo "$java" |
|
|
|
} |
|
|
|
|
|
|
|
version() { |
|
|
|
echo "python-build ${PYTHON_BUILD_VERSION}" |
|
|
|
} |
|
|
|
|
|
|
|
usage() { |
|
|
|
{ version |
|
|
|
echo "usage: python-build [-k|--keep] [-v|--verbose] 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 |
|
|
|
echo "${definition##*/}" |
|
|
|
done |
|
|
|
} | sort |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
unset VERBOSE |
|
|
|
unset KEEP_BUILD_PATH |
|
|
|
PYTHON_BUILD_ROOT="$(abs_dirname "$0")/.." |
|
|
|
|
|
|
|
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 " --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 |
|
|
|
;; |
|
|
|
"version" ) |
|
|
|
version |
|
|
|
exit 0 |
|
|
|
;; |
|
|
|
esac |
|
|
|
done |
|
|
|
|
|
|
|
DEFINITION_PATH="${ARGUMENTS[0]}" |
|
|
|
if [ -z "$DEFINITION_PATH" ]; then |
|
|
|
usage |
|
|
|
elif [ ! -e "$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 1 |
|
|
|
fi |
|
|
|
fi |
|
|
|
|
|
|
|
PREFIX_PATH="${ARGUMENTS[1]}" |
|
|
|
if [ -z "$PREFIX_PATH" ]; then |
|
|
|
usage |
|
|
|
fi |
|
|
|
|
|
|
|
if [ -z "$TMPDIR" ]; then |
|
|
|
TMP="/tmp" |
|
|
|
else |
|
|
|
TMP="${TMPDIR%/}" |
|
|
|
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" & |
|
|
|
trap "kill 0" SIGINT SIGTERM EXIT |
|
|
|
fi |
|
|
|
|
|
|
|
export LDFLAGS="-L'${PREFIX_PATH}/lib' ${LDFLAGS}" |
|
|
|
export CPPFLAGS="-I'${PREFIX_PATH}/include' ${CPPFLAGS}" |
|
|
|
|
|
|
|
unset PYTHONOPT |
|
|
|
unset PYTHONLIB |
|
|
|
|
|
|
|
trap build_failed ERR |
|
|
|
mkdir -p "$BUILD_PATH" |
|
|
|
source "$DEFINITION_PATH" |
|
|
|
[ -z "${KEEP_BUILD_PATH}" ] && rm -fr "$BUILD_PATH" |
|
|
|
trap - ERR |