#!/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 { echo colorize 1 "ERROR" echo ": 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 { echo colorize 1 "ERROR" echo ": 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