├── README.md ├── bin ├── ndenv-install ├── ndenv-uninstall └── node-build └── install.sh /README.md: -------------------------------------------------------------------------------- 1 | # node-build 2 | node-build is an [ndenv](https://github.com/riywo/ndenv) plugin that provides an `ndenv install` command. 3 | 4 | Almost all of code come from [ruby-build](https://github.com/sstephenson/ruby-build) and [nvm](https://github.com/creationix/nvm). Thanks a lot! 5 | 6 | ## INSTALL 7 | 8 | $ git clone https://github.com/riywo/node-build.git $(ndenv root)/plugins/node-build 9 | 10 | node-build currently supports only download a compiled tarball. 11 | Both [node.js](http://nodejs.org/) and [io.js](https://iojs.org) are supported. 12 | 13 | ## LICENSE 14 | 15 | (The MIT license) 16 | 17 | Copyright (c) 2013 Ryosuke Iwanaga 18 | 19 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 20 | 21 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 22 | 23 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /bin/ndenv-install: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Summary: Install a Node version using the node-build plugin 4 | # 5 | # Usage: ndenv install [-f|--force] [-k|--keep] [-v|--verbose] 6 | # ndenv install [-f|--force] [-k|--keep] [-v|--verbose] 7 | # ndenv install -l|--list 8 | # 9 | # -l/--list List all available versions 10 | # -f/--force Install even if the version appears to be installed already 11 | # -s/--skip-existing Skip if the version appears to be installed already 12 | # -k/--keep Keep source tree in $NDENV_BUILD_ROOT after installation 13 | # (defaults to $NDENV_ROOT/sources) 14 | # -v/--verbose Verbose mode: print compilation status to stdout 15 | # 16 | # For detailed information on installing Node versions with 17 | # node-build, including a list of environment variables for adjusting 18 | # compilation, see: https://github.com/riywo/node-build#usage 19 | # 20 | set -e 21 | [ -n "$NDENV_DEBUG" ] && set -x 22 | 23 | # Provide ndenv completions 24 | if [ "$1" = "--complete" ]; then 25 | exec node-build --definitions 26 | fi 27 | 28 | if [ -z "$NDENV_ROOT" ]; then 29 | NDENV_ROOT="${HOME}/.ndenv" 30 | fi 31 | 32 | # Load shared library functions 33 | eval "$(node-build --lib)" 34 | 35 | usage() { 36 | # We can remove the sed fallback once rbenv 0.4.0 is widely available. 37 | ndenv-help install 2>/dev/null || sed -ne '/^#/!q;s/.//;s/.//;1,4d;p' < "$0" 38 | [ -z "$1" ] || exit "$1" 39 | } 40 | 41 | definitions() { 42 | local query="$1" 43 | node-build --definitions | grep -F "$query" || true 44 | } 45 | 46 | indent() { 47 | sed 's/^/ /' 48 | } 49 | 50 | unset FORCE 51 | unset SKIP_EXISTING 52 | unset KEEP 53 | unset VERBOSE 54 | 55 | parse_options "$@" 56 | for option in "${OPTIONS[@]}"; do 57 | case "$option" in 58 | "h" | "help" ) 59 | usage 0 60 | ;; 61 | "l" | "list" ) 62 | echo "Available versions:" 63 | definitions | indent 64 | exit 65 | ;; 66 | "f" | "force" ) 67 | FORCE=true 68 | ;; 69 | "s" | "skip-existing" ) 70 | SKIP_EXISTING=true 71 | ;; 72 | "k" | "keep" ) 73 | [ -n "${NDENV_BUILD_ROOT}" ] || NDENV_BUILD_ROOT="${NDENV_ROOT}/sources" 74 | ;; 75 | "v" | "verbose" ) 76 | VERBOSE="-v" 77 | ;; 78 | "version" ) 79 | exec node-build --version 80 | ;; 81 | * ) 82 | usage 1 83 | ;; 84 | esac 85 | done 86 | 87 | unset VERSION_NAME 88 | 89 | # The first argument contains the definition to install. If the 90 | # argument is missing, try to install whatever local app-specific 91 | # version is specified by rbenv. Show usage instructions if a local 92 | # version is not specified. 93 | DEFINITION="${ARGUMENTS[0]}" 94 | [ -n "$DEFINITION" ] || DEFINITION="$(ndenv local 2>/dev/null || true)" 95 | [ -n "$DEFINITION" ] || usage 1 96 | 97 | 98 | # Define `before_install` and `after_install` functions that allow 99 | # plugin hooks to register a string of code for execution before or 100 | # after the installation process. 101 | declare -a before_hooks after_hooks 102 | 103 | before_install() { 104 | local hook="$1" 105 | before_hooks["${#before_hooks[@]}"]="$hook" 106 | } 107 | 108 | after_install() { 109 | local hook="$1" 110 | after_hooks["${#after_hooks[@]}"]="$hook" 111 | } 112 | 113 | # Load plugin hooks. 114 | for script in $(ndenv-hooks install); do 115 | source "$script" 116 | done 117 | 118 | 119 | # Set VERSION_NAME from $DEFINITION, if it is not already set. Then 120 | # compute the installation prefix. 121 | [ -n "$VERSION_NAME" ] || VERSION_NAME="${DEFINITION##*/}" 122 | PREFIX="${NDENV_ROOT}/versions/${VERSION_NAME}" 123 | 124 | [ -d "${PREFIX}" ] && PREFIX_EXISTS=1 125 | 126 | # If the installation prefix exists, prompt for confirmation unless 127 | # the --force option was specified. 128 | if [ -d "${PREFIX}/bin" ]; then 129 | if [ -z "$FORCE" ] && [ -z "$SKIP_EXISTING" ]; then 130 | echo "ndenv: $PREFIX already exists" >&2 131 | read -p "continue with installation? (y/N) " 132 | 133 | case "$REPLY" in 134 | y* | Y* ) ;; 135 | * ) exit 1 ;; 136 | esac 137 | elif [ -n "$SKIP_EXISTING" ]; then 138 | # Since we know the node version is already installed, and are opting to 139 | # not force installation of existing versions, we just `exit 0` here to 140 | # leave things happy 141 | exit 0 142 | fi 143 | fi 144 | 145 | # If NDENV_BUILD_ROOT is set, always pass keep options to node-build. 146 | if [ -n "${NDENV_BUILD_ROOT}" ]; then 147 | export NODE_BUILD_BUILD_PATH="${NDENV_BUILD_ROOT}/${VERSION_NAME}" 148 | KEEP="-k" 149 | fi 150 | 151 | # Set NODE_BUILD_CACHE_PATH to $NDENV_ROOT/cache, if the directory 152 | # exists and the variable is not already set. 153 | if [ -z "${NODE_BUILD_CACHE_PATH}" ] && [ -d "${NDENV_ROOT}/cache" ]; then 154 | export NODE_BUILD_CACHE_PATH="${NDENV_ROOT}/cache" 155 | fi 156 | 157 | # Default NDENV_VERSION to the globally-specified Node version. 158 | export NDENV_VERSION="$(ndenv global 2>/dev/null || true)" 159 | 160 | 161 | # Execute `before_install` hooks. 162 | for hook in "${before_hooks[@]}"; do eval "$hook"; done 163 | 164 | # Plan cleanup on unsuccessful installation. 165 | cleanup() { 166 | [ -z "${PREFIX_EXISTS}" ] && rm -rf "$PREFIX" 167 | } 168 | 169 | trap cleanup SIGINT 170 | 171 | # Invoke `node-build` and record the exit status in $STATUS. 172 | STATUS=0 173 | node-build $KEEP $VERBOSE "$DEFINITION" "$PREFIX" || STATUS="$?" 174 | 175 | # Display a more helpful message if the definition wasn't found. 176 | if [ "$STATUS" == "2" ]; then 177 | { candidates="$(definitions "$DEFINITION")" 178 | if [ -n "$candidates" ]; then 179 | echo 180 | echo "The following versions contain \`$DEFINITION' in the name:" 181 | echo "$candidates" | indent 182 | fi 183 | echo 184 | echo "You can list all available versions with \`ndenv install --list'." 185 | echo 186 | echo "If the version you're looking for is not present, first try upgrading" 187 | echo "node-build. If it's still missing, open a request on the node-build" 188 | echo "issue tracker: https://github.com/riywo/node-build/issues" 189 | } >&2 190 | fi 191 | 192 | # Execute `after_install` hooks. 193 | for hook in "${after_hooks[@]}"; do eval "$hook"; done 194 | 195 | # Run `ndenv-rehash` after a successful installation. 196 | if [ "$STATUS" == "0" ]; then 197 | ndenv rehash 198 | else 199 | cleanup 200 | fi 201 | 202 | exit "$STATUS" 203 | -------------------------------------------------------------------------------- /bin/ndenv-uninstall: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Summary: Uninstall a specific Node version 4 | # 5 | # Usage: ndenv uninstall [-f|--force] 6 | # 7 | # -f Attempt to remove the specified version without prompting 8 | # for confirmation. If the version does not exist, do not 9 | # display an error message. 10 | # 11 | # See `ndenv versions` for a complete list of installed versions. 12 | # 13 | set -e 14 | 15 | # Provide rbenv completions 16 | if [ "$1" = "--complete" ]; then 17 | exec ndenv versions --bare 18 | fi 19 | 20 | if [ -z "$NDENV_ROOT" ]; then 21 | NDENV_ROOT="${HOME}/.ndenv" 22 | fi 23 | 24 | unset FORCE 25 | if [ "$1" = "-f" ] || [ "$1" = "--force" ]; then 26 | FORCE=true 27 | shift 28 | fi 29 | 30 | DEFINITION="$1" 31 | case "$DEFINITION" in 32 | "" | -* ) 33 | # We can remove the sed fallback once rbenv 0.4.0 is widely available. 34 | { ndenv-help uninstall 2>/dev/null || 35 | sed -ne '/^#/!q;s/.\{1,2\}//;1,4d;p' < "$0" 36 | } >&2 37 | exit 1 38 | ;; 39 | esac 40 | 41 | VERSION_NAME="${DEFINITION##*/}" 42 | PREFIX="${NDENV_ROOT}/versions/${VERSION_NAME}" 43 | 44 | if [ -z "$FORCE" ]; then 45 | if [ ! -d "$PREFIX" ]; then 46 | echo "ndenv: version \`$VERSION_NAME' not installed" >&2 47 | exit 1 48 | fi 49 | 50 | read -p "ndenv: remove $PREFIX? " 51 | case "$REPLY" in 52 | y* | Y* ) ;; 53 | * ) exit 1 ;; 54 | esac 55 | fi 56 | 57 | if [ -d "$PREFIX" ]; then 58 | rm -rf "$PREFIX" 59 | ndenv rehash 60 | fi 61 | -------------------------------------------------------------------------------- /bin/node-build: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | NODE_BUILD_VERSION="20150215" 4 | 5 | set -E 6 | exec 3<&2 # preserve original stderr at fd 3 7 | 8 | 9 | lib() { 10 | parse_options() { 11 | OPTIONS=() 12 | ARGUMENTS=() 13 | local arg option index 14 | 15 | for arg in "$@"; do 16 | if [ "${arg:0:1}" = "-" ]; then 17 | if [ "${arg:1:1}" = "-" ]; then 18 | OPTIONS[${#OPTIONS[*]}]="${arg:2}" 19 | else 20 | index=1 21 | while option="${arg:$index:1}"; do 22 | [ -n "$option" ] || break 23 | OPTIONS[${#OPTIONS[*]}]="$option" 24 | index=$(($index+1)) 25 | done 26 | fi 27 | else 28 | ARGUMENTS[${#ARGUMENTS[*]}]="$arg" 29 | fi 30 | done 31 | } 32 | 33 | if [ "$1" == "--$FUNCNAME" ]; then 34 | declare -f "$FUNCNAME" 35 | echo "$FUNCNAME \"\$1\";" 36 | exit 37 | fi 38 | } 39 | lib "$1" 40 | 41 | 42 | resolve_link() { 43 | $(type -p greadlink readlink | head -1) "$1" 44 | } 45 | 46 | abs_dirname() { 47 | local cwd="$(pwd)" 48 | local path="$1" 49 | 50 | while [ -n "$path" ]; do 51 | cd "${path%/*}" 52 | local name="${path##*/}" 53 | path="$(resolve_link "$name" || true)" 54 | done 55 | 56 | pwd 57 | cd "$cwd" 58 | } 59 | 60 | capitalize() { 61 | printf "%s" "$1" | tr a-z A-Z 62 | } 63 | 64 | sanitize() { 65 | printf "%s" "$1" | sed "s/[^A-Za-z0-9.-]/_/g; s/__*/_/g" 66 | } 67 | 68 | build_failed() { 69 | { echo 70 | echo "BUILD FAILED" 71 | echo 72 | 73 | if ! rmdir "${BUILD_PATH}" 2>/dev/null; then 74 | echo "Inspect or clean up the working tree at ${BUILD_PATH}" 75 | 76 | if file_is_not_empty "$LOG_PATH"; then 77 | echo "Results logged to ${LOG_PATH}" 78 | echo 79 | echo "Last 10 log lines:" 80 | tail -n 10 "$LOG_PATH" 81 | fi 82 | fi 83 | } >&3 84 | exit 1 85 | } 86 | 87 | file_is_not_empty() { 88 | local filename="$1" 89 | local line_count="$(wc -l "$filename" 2>/dev/null || true)" 90 | 91 | if [ -n "$line_count" ]; then 92 | words=( $line_count ) 93 | [ "${words[0]}" -gt 0 ] 94 | else 95 | return 1 96 | fi 97 | } 98 | 99 | install_package() { 100 | install_package_using "tarball" 1 "$@" 101 | } 102 | 103 | install_git() { 104 | install_package_using "git" 2 "$@" 105 | } 106 | 107 | install_svn() { 108 | install_package_using "svn" 2 "$@" 109 | } 110 | 111 | install_package_using() { 112 | local package_type="$1" 113 | local package_type_nargs="$2" 114 | local package_name="$3" 115 | shift 3 116 | 117 | local fetch_args=( "$package_name" "${@:1:$package_type_nargs}" ) 118 | local make_args=( "$package_name" ) 119 | local arg last_arg 120 | 121 | for arg in "${@:$(( $package_type_nargs + 1 ))}"; do 122 | if [ "$last_arg" = "--if" ]; then 123 | "$arg" || return 0 124 | elif [ "$arg" != "--if" ]; then 125 | make_args["${#make_args[@]}"]="$arg" 126 | fi 127 | last_arg="$arg" 128 | done 129 | 130 | pushd "$BUILD_PATH" >&4 131 | "fetch_${package_type}" "${fetch_args[@]}" 132 | make_package "${make_args[@]}" 133 | popd >&4 134 | 135 | { echo "Installed ${package_name} to ${PREFIX_PATH}" 136 | echo 137 | } >&2 138 | } 139 | 140 | make_package() { 141 | local package_name="$1" 142 | shift 143 | 144 | pushd "$package_name" >&4 145 | before_install_package "$package_name" 146 | build_package "$package_name" $* 147 | after_install_package "$package_name" 148 | fix_directory_permissions 149 | popd >&4 150 | } 151 | 152 | compute_sha1() { 153 | local output 154 | if type shasum &>/dev/null; then 155 | output="$(shasum -a 1 -b)" || return 1 156 | echo "${output% *}" 157 | elif type openssl &>/dev/null; then 158 | local openssl="$(command -v "$(brew --prefix openssl 2>/dev/null || true)"/bin/openssl openssl | head -1)" 159 | output="$("$openssl" dgst -sha1 2>/dev/null)" || return 1 160 | echo "${output##* }" 161 | elif type sha1sum &>/dev/null; then 162 | output="$(sha1sum --quiet)" || return 1 163 | echo "${output% *}" 164 | else 165 | return 1 166 | fi 167 | } 168 | 169 | compute_sha2() { 170 | local output 171 | if type shasum &>/dev/null; then 172 | output="$(shasum -a 256 -b)" || return 1 173 | echo "${output% *}" 174 | elif type openssl &>/dev/null; then 175 | local openssl="$(command -v "$(brew --prefix openssl 2>/dev/null || true)"/bin/openssl openssl | head -1)" 176 | output="$("$openssl" dgst -sha256 2>/dev/null)" || return 1 177 | echo "${output##* }" 178 | elif type sha256sum &>/dev/null; then 179 | output="$(sha256sum --quiet)" || return 1 180 | echo "${output% *}" 181 | else 182 | return 1 183 | fi 184 | } 185 | 186 | verify_checksum() { 187 | # If the specified filename doesn't exist, return success 188 | local filename="$1" 189 | [ -e "$filename" ] || return 0 190 | 191 | # If there's no expected checksum, return success 192 | local expected_checksum=`echo "$2" | tr [A-Z] [a-z]` 193 | [ -n "$expected_checksum" ] || return 0 194 | local expected_checksum_length="${#expected_checksum}" 195 | 196 | # If the checksum length is 40 chars, assume SHA-1 197 | if [ "$expected_checksum_length" -eq 40 ]; then 198 | 199 | # If there's no SHA1 support, return success 200 | [ -n "$HAS_SHA1_SUPPORT" ] || return 0 201 | local checksum_command="compute_sha1" 202 | 203 | # If the checksum length is 64 chars, assume SHA-256 204 | elif [ "$expected_checksum_length" -eq 64 ]; then 205 | 206 | # If there's no SHA2 support, return success 207 | [ -n "$HAS_SHA2_SUPPORT" ] || return 0 208 | local checksum_command="compute_sha2" 209 | 210 | # Otherwise return failure 211 | else 212 | return 1 213 | fi 214 | 215 | # If the computed checksum is empty, return failure 216 | local computed_checksum=`echo "$($checksum_command < "$filename")" | tr [A-Z] [a-z]` 217 | [ -n "$computed_checksum" ] || return 1 218 | 219 | if [ "$expected_checksum" != "$computed_checksum" ]; then 220 | { echo 221 | echo "checksum mismatch: ${filename} (file is corrupt)" 222 | echo "expected $expected_checksum, got $computed_checksum" 223 | echo 224 | } >&4 225 | return 1 226 | fi 227 | } 228 | 229 | http() { 230 | local method="$1" 231 | local url="$2" 232 | local file="$3" 233 | [ -n "$url" ] || return 1 234 | 235 | if type curl &>/dev/null; then 236 | "http_${method}_curl" "$url" "$file" 237 | elif type wget &>/dev/null; then 238 | "http_${method}_wget" "$url" "$file" 239 | else 240 | echo "error: please install \`curl\` or \`wget\` and try again" >&2 241 | exit 1 242 | fi 243 | } 244 | 245 | http_head_curl() { 246 | curl -qsILf "$1" >&4 2>&1 247 | } 248 | 249 | http_get_curl() { 250 | curl -C - -o "${2:--}" -qsSLf "$1" 251 | } 252 | 253 | http_head_wget() { 254 | wget -q --spider "$1" >&4 2>&1 255 | } 256 | 257 | http_get_wget() { 258 | wget -nv -c -O "${2:--}" "$1" 259 | } 260 | 261 | fetch_tarball() { 262 | local package_name="$1" 263 | local package_url="$2" 264 | local checksum 265 | 266 | if [ "$package_url" != "${package_url/\#}" ]; then 267 | checksum="${package_url#*#}" 268 | package_url="${package_url%%#*}" 269 | fi 270 | 271 | local package_filename="${package_name}.tar.gz" 272 | symlink_tarball_from_cache "$package_filename" "$checksum" || { 273 | echo "Downloading ${package_filename}..." >&2 274 | download_tarball "$package_url" "$package_filename" "$checksum" 275 | } 276 | 277 | { if tar xzvf "$package_filename"; then 278 | if [ -z "$KEEP_BUILD_PATH" ]; then 279 | rm -f "$package_filename" 280 | else 281 | true 282 | fi 283 | fi 284 | } >&4 2>&1 285 | } 286 | 287 | symlink_tarball_from_cache() { 288 | [ -n "$NODE_BUILD_CACHE_PATH" ] || return 1 289 | 290 | local package_filename="$1" 291 | local cached_package_filename="${NODE_BUILD_CACHE_PATH}/$package_filename" 292 | local checksum="$2" 293 | 294 | [ -e "$cached_package_filename" ] || return 1 295 | verify_checksum "$cached_package_filename" "$checksum" >&4 2>&1 || return 1 296 | ln -s "$cached_package_filename" "$package_filename" >&4 2>&1 || return 1 297 | } 298 | 299 | download_tarball() { 300 | local package_url="$1" 301 | [ -n "$package_url" ] || return 1 302 | 303 | local package_filename="$2" 304 | local checksum="$3" 305 | 306 | echo "-> $package_url" >&2 307 | 308 | { http get "$package_url" "$package_filename" 309 | verify_checksum "$package_filename" "$checksum" 310 | } >&4 2>&1 || return 1 311 | 312 | if [ -n "$NODE_BUILD_CACHE_PATH" ]; then 313 | local cached_package_filename="${NODE_BUILD_CACHE_PATH}/$package_filename" 314 | { mv "$package_filename" "$cached_package_filename" 315 | ln -s "$cached_package_filename" "$package_filename" 316 | } >&4 2>&1 || return 1 317 | fi 318 | } 319 | 320 | fetch_git() { 321 | local package_name="$1" 322 | local git_url="$2" 323 | local git_ref="$3" 324 | 325 | echo "Cloning ${git_url}..." >&2 326 | 327 | if type git &>/dev/null; then 328 | if [ -n "$NODE_BUILD_CACHE_PATH" ]; then 329 | pushd "$NODE_BUILD_CACHE_PATH" >&4 330 | local clone_name="$(sanitize "$git_url")" 331 | if [ -e "${clone_name}" ]; then 332 | { cd "${clone_name}" 333 | git fetch --force "$git_url" "+${git_ref}:${git_ref}" 334 | } >&4 2>&1 335 | else 336 | git clone --bare --branch "$git_ref" "$git_url" "${clone_name}" >&4 2>&1 337 | fi 338 | git_url="$NODE_BUILD_CACHE_PATH/${clone_name}" 339 | popd >&4 340 | fi 341 | 342 | git clone --depth 1 --branch "$git_ref" "$git_url" "${package_name}" >&4 2>&1 343 | else 344 | echo "error: please install \`git\` and try again" >&2 345 | exit 1 346 | fi 347 | } 348 | 349 | fetch_svn() { 350 | local package_name="$1" 351 | local svn_url="$2" 352 | local svn_rev="$3" 353 | 354 | echo "Checking out ${svn_url}..." >&2 355 | 356 | if type svn &>/dev/null; then 357 | svn co -r "$svn_rev" "$svn_url" "${package_name}" >&4 2>&1 358 | else 359 | echo "error: please install \`svn\` and try again" >&2 360 | exit 1 361 | fi 362 | } 363 | 364 | build_package() { 365 | local package_name="$1" 366 | shift 367 | 368 | if [ "$#" -eq 0 ]; then 369 | local commands="standard" 370 | else 371 | local commands="$*" 372 | fi 373 | 374 | echo "Installing ${package_name}..." >&2 375 | 376 | for command in $commands; do 377 | "build_package_${command}" "$package_name" 378 | done 379 | } 380 | 381 | package_option() { 382 | local package_name="$1" 383 | local command_name="$2" 384 | local variable="$(capitalize "${package_name}_${command_name}")_OPTS_ARRAY" 385 | local array="$variable[@]" 386 | shift 2 387 | local value=( "${!array}" "$@" ) 388 | eval "$variable=( \"\${value[@]}\" )" 389 | } 390 | 391 | build_package_standard() { 392 | local package_name="$1" 393 | 394 | if [ "${MAKEOPTS+defined}" ]; then 395 | MAKE_OPTS="$MAKEOPTS" 396 | elif [ -z "${MAKE_OPTS+defined}" ]; then 397 | MAKE_OPTS="-j 2" 398 | fi 399 | 400 | # Support YAML_CONFIGURE_OPTS, RUBY_CONFIGURE_OPTS, etc. 401 | local package_var_name="$(capitalize "${package_name%%-*}")" 402 | local PACKAGE_CONFIGURE="${package_var_name}_CONFIGURE" 403 | local PACKAGE_PREFIX_PATH="${package_var_name}_PREFIX_PATH" 404 | local PACKAGE_CONFIGURE_OPTS="${package_var_name}_CONFIGURE_OPTS" 405 | local PACKAGE_CONFIGURE_OPTS_ARRAY="${package_var_name}_CONFIGURE_OPTS_ARRAY[@]" 406 | local PACKAGE_MAKE_OPTS="${package_var_name}_MAKE_OPTS" 407 | local PACKAGE_MAKE_OPTS_ARRAY="${package_var_name}_MAKE_OPTS_ARRAY[@]" 408 | local PACKAGE_CFLAGS="${package_var_name}_CFLAGS" 409 | 410 | ( if [ "${CFLAGS+defined}" ] || [ "${!PACKAGE_CFLAGS+defined}" ]; then 411 | export CFLAGS="$CFLAGS ${!PACKAGE_CFLAGS}" 412 | fi 413 | ${!PACKAGE_CONFIGURE:-./configure} --prefix="${!PACKAGE_PREFIX_PATH:-$PREFIX_PATH}" $CONFIGURE_OPTS ${!PACKAGE_CONFIGURE_OPTS} "${!PACKAGE_CONFIGURE_OPTS_ARRAY}" 414 | ) >&4 2>&1 415 | 416 | { "$MAKE" $MAKE_OPTS ${!PACKAGE_MAKE_OPTS} "${!PACKAGE_MAKE_OPTS_ARRAY}" 417 | "$MAKE" install 418 | } >&4 2>&1 419 | } 420 | 421 | build_package_autoconf() { 422 | { autoconf 423 | } >&4 2>&1 424 | } 425 | 426 | build_package_copy() { 427 | mkdir -p "$PREFIX_PATH" 428 | cp -R . "$PREFIX_PATH" 429 | } 430 | 431 | before_install_package() { 432 | local stub=1 433 | } 434 | 435 | after_install_package() { 436 | local stub=1 437 | } 438 | 439 | fix_directory_permissions() { 440 | # Ensure installed directories are not world-writable to avoid Bundler warnings 441 | find "$PREFIX_PATH" -type d \( -perm -020 -o -perm -002 \) -exec chmod go-w {} \; 442 | } 443 | 444 | version() { 445 | echo "node-build ${NODE_BUILD_VERSION}" 446 | } 447 | 448 | usage() { 449 | { version 450 | echo "usage: node-build [-k|--keep] [-v|--verbose] definition prefix" 451 | echo " node-build --definitions" 452 | } >&2 453 | 454 | if [ -z "$1" ]; then 455 | exit 1 456 | fi 457 | } 458 | 459 | list_definitions_node() { 460 | curl -s https://nodejs.org/dist/ \ 461 | | egrep -o 'v[0-9]+\.[0-9]+\.[0-9]+' \ 462 | | sort -t. -u -k 1.2,1n -k 2,2n -k 3,3n 463 | } 464 | 465 | list_definitions_iojs() { 466 | curl -s https://iojs.org/dist/index.tab \ 467 | | awk '{print $1}' \ 468 | | egrep -o 'v[0-9]+\.[0-9]+\.[0-9]+' \ 469 | | sort -t. -u -k 1.2,1n -k 2,2n -k 3,3n \ 470 | | sed -e 's/^/iojs-/' 471 | } 472 | 473 | list_definitions() { 474 | local PATTERN=$1 475 | case "$PATTERN" in 476 | iojs-*) list_definitions_iojs ;; 477 | node-*) list_definitions_node ;; 478 | *) list_definitions_node; list_definitions_iojs ;; 479 | esac 480 | } 481 | 482 | get_version_prefix() { 483 | local PATTERN=$1 484 | local PREFIX= 485 | case "$PATTERN" in 486 | iojs-*) PREFIX="iojs" ;; 487 | *) PREFIX="" ;; 488 | esac 489 | echo "$PREFIX" 490 | } 491 | 492 | get_version() { 493 | local PATTERN=$1 494 | echo "$PATTERN" | sed -e "s/^[^\.]\{1,\}\-//" 495 | } 496 | 497 | normalize_version() { 498 | local PATTERN=$1 499 | local PREFIX=$(get_version_prefix "$PATTERN") 500 | local VERSION=$(get_version "$PATTERN") 501 | if echo "$VERSION" | grep -v '^v' &>/dev/null ; then 502 | VERSION=v$VERSION 503 | fi 504 | 505 | if [ ${#PREFIX} -ne 0 ]; then 506 | echo "${PREFIX}-${VERSION}" 507 | else 508 | echo "${VERSION}" 509 | fi 510 | } 511 | 512 | ls_remote() { 513 | local PATTERN=$1 514 | local VERSIONS 515 | if [ "$PATTERN" ]; then 516 | PATTERN="$(normalize_version "$PATTERN")" 517 | else 518 | PATTERN=".*" 519 | fi 520 | VERSIONS=$(list_definitions "$PATTERN" | egrep -w "^${PATTERN}") 521 | if [ ! "$VERSIONS" ]; then 522 | echo "N/A" 523 | return 524 | fi 525 | echo "$VERSIONS" 526 | return 527 | } 528 | 529 | remote_version() { 530 | local PATTERN=$1 531 | local VERSION=$(ls_remote "$PATTERN" | tail -n1) 532 | echo "$VERSION" 533 | 534 | if [ "$VERSION" = 'N/A' ]; then 535 | return 1 536 | fi 537 | } 538 | 539 | install_node() { 540 | local VERSION="$1" 541 | 542 | local uname="$(uname -a)" 543 | local os= 544 | local arch="$(uname -m)" 545 | local nobinary=0 546 | 547 | case "$uname" in 548 | Linux\ *) os=linux ;; 549 | Darwin\ *) os=darwin; ;; 550 | SunOS\ *) os=sunos ;; 551 | FreeBSD\ *) os=freebsd; nobinary=1 ;; 552 | esac 553 | case "$uname" in 554 | *x86_64*) arch=x64 ;; 555 | *i*86*) arch=x86 ;; 556 | esac 557 | 558 | case "$arch" in 559 | armv6l) arch=arm-pi ;; 560 | esac 561 | 562 | if [ $nobinary -ne 1 ]; then 563 | # shortcut - try the binary if possible. 564 | if [ -n "$os" ]; then 565 | local binavail= 566 | # binaries started with node 0.8.6 567 | case "$VERSION" in 568 | v0.8.[012345]) binavail=0 ;; 569 | v0.[1234567].*) binavail=0 ;; 570 | *) binavail=1 ;; 571 | esac 572 | 573 | sha_algorithm= 574 | # checksum is SHA256 in v0.10 and above and SHA1 in v0.9 and below 575 | case "$VERSION" in 576 | v0.[0-9].*) sha_algorithm= ;; 577 | *) sha_algorithm=256 ;; 578 | esac 579 | 580 | if [ $binavail -eq 1 ]; then 581 | t="$VERSION-$os-$arch" 582 | sum=`curl -s https://nodejs.org/dist/$VERSION/SHASUMS${sha_algorithm}.txt | grep node-${t}.tar.gz | awk '{print $1}'` 583 | url="https://nodejs.org/dist/$VERSION/node-${t}.tar.gz#${sum}" 584 | install_package "node-${t}" "$url" copy 585 | fi 586 | fi 587 | fi 588 | } 589 | 590 | install_iojs() { 591 | local VERSION="$1" 592 | 593 | local uname="$(uname -a)" 594 | local os= 595 | local arch="$(uname -m)" 596 | local nobinary=0 597 | 598 | case "$uname" in 599 | Linux\ *) os=linux ;; 600 | Darwin\ *) os=darwin; ;; 601 | SunOS\ *) os=sunos; nobinary=1 ;; 602 | FreeBSD\ *) os=freebsd; nobinary=1 ;; 603 | esac 604 | case "$uname" in 605 | *x86_64*) arch=x64 ;; 606 | *i*86*) arch=x86 ;; 607 | esac 608 | 609 | case "$arch" in 610 | armv6l) arch=armv6l ;; 611 | armv7l) arch=armv7l ;; 612 | esac 613 | 614 | if [ $nobinary -ne 1 ]; then 615 | sha_algorithm=256 616 | t="$VERSION-$os-$arch" 617 | sum=`curl -s https://iojs.org/dist/$VERSION/SHASUMS${sha_algorithm}.txt | grep iojs-${t}.tar.gz | awk '{print $1}'` 618 | url="https://iojs.org/dist/$VERSION/iojs-${t}.tar.gz#${sum}" 619 | install_package "iojs-${t}" "$url" copy 620 | fi 621 | } 622 | 623 | do_install() { 624 | local PATTERN="$1" 625 | local NORMALIZED_PATTERN="$(normalize_version "$PATTERN")" 626 | local DIST="$(get_version_prefix "$NORMALIZED_PATTERN")" 627 | local VERSION="$(get_version "$NORMALIZED_PATTERN")" 628 | 629 | case "$DIST" in 630 | "iojs" ) install_iojs "$VERSION" ;; 631 | * ) install_node "$VERSION" ;; 632 | esac 633 | } 634 | 635 | 636 | unset VERBOSE 637 | unset KEEP_BUILD_PATH 638 | NODE_BUILD_ROOT="$(abs_dirname "$0")/.." 639 | 640 | parse_options "$@" 641 | 642 | for option in "${OPTIONS[@]}"; do 643 | case "$option" in 644 | "h" | "help" ) 645 | usage without_exiting 646 | { echo 647 | echo " -k/--keep Do not remove source tree after installation" 648 | echo " -v/--verbose Verbose mode: print compilation status to stdout" 649 | echo " --definitions List all built-in definitions" 650 | echo 651 | } >&2 652 | exit 0 653 | ;; 654 | "definitions" ) 655 | list_definitions 656 | exit 0 657 | ;; 658 | "k" | "keep" ) 659 | KEEP_BUILD_PATH=true 660 | ;; 661 | "v" | "verbose" ) 662 | VERBOSE=true 663 | ;; 664 | "version" ) 665 | version 666 | exit 0 667 | ;; 668 | esac 669 | done 670 | 671 | NODE_VERSION=$(remote_version "${ARGUMENTS[0]}") || { 672 | echo "The specified version is not available." 673 | echo "Run ndenv install -l or node-build --definitions" 674 | echo "to show available versions of node.js/io.js." 675 | exit 1 676 | } 677 | 678 | PREFIX_PATH="${ARGUMENTS[1]}" 679 | if [ -z "$PREFIX_PATH" ]; then 680 | usage 681 | fi 682 | 683 | if [ -z "$TMPDIR" ]; then 684 | TMP="/tmp" 685 | else 686 | TMP="${TMPDIR%/}" 687 | fi 688 | 689 | if [ -z "$MAKE" ]; then 690 | export MAKE="make" 691 | fi 692 | 693 | if [ -n "$NODE_BUILD_CACHE_PATH" ] && [ -d "$NODE_BUILD_CACHE_PATH" ]; then 694 | NODE_BUILD_CACHE_PATH="${NODE_BUILD_CACHE_PATH%/}" 695 | else 696 | unset NODE_BUILD_CACHE_PATH 697 | fi 698 | 699 | if echo test | compute_sha1 >/dev/null; then 700 | HAS_SHA1_SUPPORT=1 701 | else 702 | unset HAS_SHA1_SUPPORT 703 | fi 704 | 705 | if echo test | compute_sha2 >/dev/null; then 706 | HAS_SHA2_SUPPORT=1 707 | else 708 | unset HAS_SHA2_SUPPORT 709 | fi 710 | 711 | SEED="$(date "+%Y%m%d%H%M%S").$$" 712 | LOG_PATH="${TMP}/node-build.${SEED}.log" 713 | NODE_BIN="${PREFIX_PATH}/bin/node" 714 | CWD="$(pwd)" 715 | 716 | if [ -z "$NODE_BUILD_BUILD_PATH" ]; then 717 | BUILD_PATH="${TMP}/node-build.${SEED}" 718 | else 719 | BUILD_PATH="$NODE_BUILD_BUILD_PATH" 720 | fi 721 | 722 | exec 4<> "$LOG_PATH" # open the log file at fd 4 723 | if [ -n "$VERBOSE" ]; then 724 | tail -f "$LOG_PATH" & 725 | TAIL_PID=$! 726 | trap "kill $TAIL_PID" SIGINT SIGTERM EXIT 727 | fi 728 | 729 | export LDFLAGS="-L'${PREFIX_PATH}/lib' ${LDFLAGS}" 730 | export CPPFLAGS="-I'${PREFIX_PATH}/include' ${CPPFLAGS}" 731 | 732 | unset NODEOPT 733 | unset NODELIB 734 | 735 | trap build_failed ERR 736 | mkdir -p "$BUILD_PATH" 737 | #source "$DEFINITION_PATH" 738 | do_install "$NODE_VERSION" 739 | [ -z "${KEEP_BUILD_PATH}" ] && rm -fr "$BUILD_PATH" 740 | trap - ERR 741 | -------------------------------------------------------------------------------- /install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | if [ -z "${PREFIX}" ]; then 6 | PREFIX="/usr/local" 7 | fi 8 | 9 | BIN_PATH="${PREFIX}/bin" 10 | SHARE_PATH="${PREFIX}/share/node-build" 11 | 12 | mkdir -p "${BIN_PATH}" 13 | mkdir -p "${SHARE_PATH}" 14 | 15 | for file in bin/*; do 16 | cp "${file}" "${BIN_PATH}" 17 | done 18 | 19 | for file in share/node-build/*; do 20 | cp "${file}" "${SHARE_PATH}" 21 | done 22 | 23 | echo "Installed node-build at ${PREFIX}" 24 | --------------------------------------------------------------------------------