├── VERSION.txt ├── linux ├── internal │ ├── bin32 │ │ ├── g++ │ │ ├── gcc │ │ ├── c++ │ │ └── cc │ ├── bin64 │ │ ├── g++ │ │ ├── gcc │ │ ├── c++ │ │ └── cc │ ├── s3init │ ├── cpucount │ ├── s3configure │ ├── python27 │ ├── rbconfig_patch.rb │ ├── restore_environment.rb │ ├── setuser │ ├── build-ruby │ ├── setup-runtime │ ├── setup-runtime-inside-mock │ ├── my_init │ └── build-ruby-inside-mock ├── .dockerignore ├── Dockerfile ├── package ├── test-gems ├── setup-docker-image ├── image │ └── install.sh ├── shell ├── upload ├── setup-runtime ├── Rakefile ├── README.md └── build-ruby ├── osx ├── internal │ ├── bin │ │ ├── clang │ │ ├── clang++ │ │ ├── cc │ │ └── c++ │ ├── check_sdk │ ├── modify_rbconfig_while_building.rb │ ├── reset_environment.sh │ ├── restore_environment.rb │ ├── rbconfig_patch.rb │ └── sanity_check ├── package ├── test-gems ├── README.md ├── Rakefile ├── build-ruby └── setup-runtime ├── BUNDLER_VERSION.txt ├── RUBY_VERSIONS.txt ├── doc ├── sdk.jpg ├── video.png ├── xcodepackage.jpg ├── download_xcode.jpg ├── linux_build_system_build.png └── linux_build_system_setup_runtime.png ├── .agignore ├── shared ├── ca-bundle.crt ├── gemfiles │ ├── 20150130 │ │ ├── Gemfile │ │ └── Gemfile.lock │ ├── 20150517 │ │ ├── Gemfile │ │ └── Gemfile.lock │ ├── 20150204-extras │ │ ├── Gemfile │ │ └── Gemfile.lock │ ├── README.md │ └── 20150204-default │ │ ├── Gemfile │ │ └── Gemfile.lock ├── dump-load-paths.rb ├── library.sh ├── test-gems └── package ├── windows ├── package ├── Rakefile └── build-ruby ├── .gitignore ├── TravelingRuby.sublime-project ├── .editorconfig ├── LICENSE.md ├── CHANGELOG.md ├── TUTORIAL-4.md ├── REDUCING_PACKAGE_SIZE.md ├── TUTORIAL-1.md ├── TUTORIAL-2.md ├── TUTORIAL-3.md └── README.md /VERSION.txt: -------------------------------------------------------------------------------- 1 | 20150715 2 | -------------------------------------------------------------------------------- /linux/internal/bin32/g++: -------------------------------------------------------------------------------- 1 | c++ -------------------------------------------------------------------------------- /linux/internal/bin32/gcc: -------------------------------------------------------------------------------- 1 | cc -------------------------------------------------------------------------------- /linux/internal/bin64/g++: -------------------------------------------------------------------------------- 1 | c++ -------------------------------------------------------------------------------- /linux/internal/bin64/gcc: -------------------------------------------------------------------------------- 1 | cc -------------------------------------------------------------------------------- /osx/internal/bin/clang: -------------------------------------------------------------------------------- 1 | cc -------------------------------------------------------------------------------- /osx/internal/bin/clang++: -------------------------------------------------------------------------------- 1 | c++ -------------------------------------------------------------------------------- /BUNDLER_VERSION.txt: -------------------------------------------------------------------------------- 1 | 1.9.9 2 | -------------------------------------------------------------------------------- /RUBY_VERSIONS.txt: -------------------------------------------------------------------------------- 1 | 2.1.6 2.2.2 2 | -------------------------------------------------------------------------------- /linux/.dockerignore: -------------------------------------------------------------------------------- 1 | runtime 2 | output 3 | *.tar.gz 4 | -------------------------------------------------------------------------------- /doc/sdk.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/did/traveling-ruby/master/doc/sdk.jpg -------------------------------------------------------------------------------- /doc/video.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/did/traveling-ruby/master/doc/video.png -------------------------------------------------------------------------------- /.agignore: -------------------------------------------------------------------------------- 1 | linux/runtime 2 | linux/output 3 | osx/runtime 4 | osx/output 5 | osx/work 6 | -------------------------------------------------------------------------------- /linux/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM centos:centos6 2 | ADD image /image 3 | RUN /image/install.sh 4 | -------------------------------------------------------------------------------- /linux/internal/s3init: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | cp /s3cfg /root/.s3cfg 4 | exec "$@" 5 | -------------------------------------------------------------------------------- /doc/xcodepackage.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/did/traveling-ruby/master/doc/xcodepackage.jpg -------------------------------------------------------------------------------- /shared/ca-bundle.crt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/did/traveling-ruby/master/shared/ca-bundle.crt -------------------------------------------------------------------------------- /doc/download_xcode.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/did/traveling-ruby/master/doc/download_xcode.jpg -------------------------------------------------------------------------------- /doc/linux_build_system_build.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/did/traveling-ruby/master/doc/linux_build_system_build.png -------------------------------------------------------------------------------- /linux/internal/cpucount: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | set -o pipefail 4 | grep "`echo -en 'processor\t'`" /proc/cpuinfo | wc -l 5 | -------------------------------------------------------------------------------- /linux/internal/s3configure: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | /system/setuser app s3cmd --configure 4 | cat ~app/.s3cfg > /work/s3cfg 5 | -------------------------------------------------------------------------------- /doc/linux_build_system_setup_runtime.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/did/traveling-ruby/master/doc/linux_build_system_setup_runtime.png -------------------------------------------------------------------------------- /osx/package: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | SELFDIR=`dirname "$0"` 5 | SELFDIR=`cd "$SELFDIR" && pwd` 6 | exec "$SELFDIR/../shared/package" "$@" 7 | -------------------------------------------------------------------------------- /linux/package: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | SELFDIR=`dirname "$0"` 5 | SELFDIR=`cd "$SELFDIR" && pwd` 6 | exec "$SELFDIR/../shared/package" "$@" 7 | -------------------------------------------------------------------------------- /osx/test-gems: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | SELFDIR=`dirname "$0"` 5 | SELFDIR=`cd "$SELFDIR" && pwd` 6 | exec "$SELFDIR/../shared/test-gems" "$@" 7 | -------------------------------------------------------------------------------- /windows/package: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | SELFDIR=`dirname "$0"` 5 | SELFDIR=`cd "$SELFDIR" && pwd` 6 | exec "$SELFDIR/../shared/package" "$@" 7 | -------------------------------------------------------------------------------- /linux/test-gems: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | SELFDIR=`dirname "$0"` 5 | SELFDIR=`cd "$SELFDIR" && pwd` 6 | exec "$SELFDIR/../shared/test-gems" "$@" 7 | -------------------------------------------------------------------------------- /shared/gemfiles/20150204-extras/Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gem 'nokogumbo', '1.2.0' 4 | gem 'rugged', '0.22.0b5', :platforms => :ruby 5 | -------------------------------------------------------------------------------- /linux/internal/bin64/c++: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | export CCACHE_COMPRESS=1 3 | export CCACHE_COMPRESS_LEVEL=3 4 | export CCACHE_DIR=/ccache 5 | exec /usr/bin/ccache /usr/bin/g++ "$@" 6 | -------------------------------------------------------------------------------- /linux/internal/bin64/cc: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | export CCACHE_COMPRESS=1 3 | export CCACHE_COMPRESS_LEVEL=3 4 | export CCACHE_DIR=/ccache 5 | exec /usr/bin/ccache /usr/bin/gcc "$@" 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /linux/runtime 2 | /linux/output 3 | /osx/runtime 4 | /osx/output 5 | /osx/work 6 | /windows/cache 7 | /windows/output 8 | *.sublime-workspace 9 | *.tar.gz 10 | -------------------------------------------------------------------------------- /linux/internal/python27: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | export LD_LIBRARY_PATH=/opt/rh/python27/root/usr/lib64:/opt/rh/python27/root/usr/lib 3 | exec /opt/rh/python27/root/usr/bin/python2.7 "$@" 4 | -------------------------------------------------------------------------------- /linux/setup-docker-image: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | SELFDIR=`dirname "$0"` 4 | SELFDIR=`cd "$SELFDIR" && pwd` 5 | set -x 6 | cd "$SELFDIR" 7 | exec docker build --force-rm -t phusion/traveling-ruby-builder . 8 | -------------------------------------------------------------------------------- /shared/dump-load-paths.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | prefix = Regexp.escape(ARGV[0] || "/tmp/ruby") 3 | new_paths = $LOAD_PATH.map do |path| 4 | path.sub(/^#{prefix}/, '$ROOT') 5 | end 6 | puts new_paths.join(":") 7 | -------------------------------------------------------------------------------- /TravelingRuby.sublime-project: -------------------------------------------------------------------------------- 1 | { 2 | "folders": 3 | [ 4 | { 5 | "path": ".", 6 | "folder_exclude_patterns": 7 | [ 8 | "linux/runtime", 9 | "linux/output", 10 | "osx/runtime", 11 | "osx/output" 12 | ] 13 | } 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /linux/internal/bin32/c++: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # -march i586 fixes gcc builtin atomics. The default architecture on 3 | # x86 CentOS 5 i386. 4 | export CCACHE_COMPRESS=1 5 | export CCACHE_COMPRESS_LEVEL=3 6 | export CCACHE_DIR=/ccache 7 | exec /usr/bin/ccache /usr/bin/g++ -march=i586 "$@" 8 | -------------------------------------------------------------------------------- /linux/internal/bin32/cc: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # -march i586 fixes gcc builtin atomics. The default architecture on 3 | # x86 CentOS 5 i386. 4 | export CCACHE_COMPRESS=1 5 | export CCACHE_COMPRESS_LEVEL=3 6 | export CCACHE_DIR=/ccache 7 | exec /usr/bin/ccache /usr/bin/gcc -march=i586 "$@" 8 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://EditorConfig.org 2 | 3 | root = true 4 | 5 | [*] 6 | end_of_line = lf 7 | charset = utf-8 8 | 9 | [Rakefile] 10 | indent_style=space 11 | indent_size=2 12 | trim_trailing_whitespace = true 13 | 14 | [*.rb] 15 | indent_style=space 16 | indent_size=2 17 | trim_trailing_whitespace = true 18 | -------------------------------------------------------------------------------- /linux/internal/rbconfig_patch.rb: -------------------------------------------------------------------------------- 1 | # Traveling Ruby modifications: 2 | # Get rid of our custom compilation flags. 3 | [RbConfig::CONFIG, RbConfig::MAKEFILE_CONFIG].each do |config| 4 | config["CFLAGS"] = config["cflags"].dup 5 | config["CXXFLAGS"] = config["cxxflags"].dup 6 | config["RUBY_EXEC_PREFIX"] = config["exec_prefix"].dup 7 | end 8 | -------------------------------------------------------------------------------- /shared/gemfiles/20150517/Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gem 'fast-stemmer' 4 | gem 'hitimes' 5 | gem 'redcarpet' 6 | gem 'snappy' 7 | gem 'pg' 8 | gem 'mysql2' 9 | gem 'puma', :platforms => :ruby 10 | gem 'kgio', :platforms => :ruby 11 | gem 'raindrops', :platforms => :ruby 12 | gem 'unicorn', :platforms => :ruby 13 | gem 'curses', :platforms => :ruby 14 | -------------------------------------------------------------------------------- /shared/gemfiles/README.md: -------------------------------------------------------------------------------- 1 | The Gemfiles in this directory define all native extensions that Traveling Ruby supports. To add a new native extension, create a new directory and create a Gemfile inside containing the new native extension. Then run `bundle install` inside that directory on your workstation (to create/update the Gemfile.lock), and re-run the Traveling Ruby build system. -------------------------------------------------------------------------------- /osx/internal/check_sdk: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | XCODEPATH=`xcode-select -p` 4 | if [[ ! -e "$XCODEPATH/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.8.sdk" ]]; then 5 | echo " *** ERROR: the OS X 10.8 SDK is required. Please refer to" \ 6 | "https://github.com/phusion/traveling-ruby/blob/master/osx/README.md" \ 7 | "for installation instructions." >&2 8 | exit 1 9 | fi 10 | -------------------------------------------------------------------------------- /shared/gemfiles/20150204-extras/Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | mini_portile (0.6.2) 5 | nokogiri (1.6.6.2) 6 | mini_portile (~> 0.6.0) 7 | nokogiri (1.6.6.2-x86-mingw32) 8 | mini_portile (~> 0.6.0) 9 | nokogumbo (1.2.0) 10 | nokogiri 11 | rugged (0.22.0b5) 12 | 13 | PLATFORMS 14 | ruby 15 | x86-mingw32 16 | 17 | DEPENDENCIES 18 | nokogumbo (= 1.2.0) 19 | rugged (= 0.22.0b5) 20 | -------------------------------------------------------------------------------- /osx/internal/modify_rbconfig_while_building.rb: -------------------------------------------------------------------------------- 1 | require 'rbconfig' 2 | 3 | [RbConfig::CONFIG, RbConfig::MAKEFILE_CONFIG].each do |config| 4 | ldflags = config["LDFLAGS"].dup 5 | ldflags = ldflags.split(/ +/) 6 | 7 | ldflags.reject! { |x| x =~ /^-L/ } 8 | ldflags << "-L." 9 | ldflags << "-L#{ENV['RUNTIME_DIR']}/lib" 10 | ldflags << "-L#{ENV['TMPBUILDROOT']}/lib" 11 | ldflags << "-L/usr/lib" 12 | 13 | config["LDFLAGS"] = ldflags.join(" ") 14 | end 15 | -------------------------------------------------------------------------------- /shared/gemfiles/20150204-default/Gemfile: -------------------------------------------------------------------------------- 1 | #source 'http://production.cf.rubygems.org/' 2 | source 'https://rubygems.org' 3 | 4 | gem 'nokogiri' 5 | gem 'sqlite3' 6 | gem 'mysql2', :platforms => :ruby 7 | gem 'json' 8 | gem 'ffi' 9 | gem 'bcrypt' 10 | gem 'yajl-ruby' 11 | gem 'thin' 12 | gem 'RedCloth' 13 | gem 'escape_utils' 14 | gem 'posix-spawn', :platforms => :ruby 15 | gem 'nokogumbo' 16 | gem 'github-markdown' 17 | gem 'rugged', :platforms => :ruby 18 | gem 'charlock_holmes', :platforms => :ruby 19 | gem 'unf_ext' 20 | -------------------------------------------------------------------------------- /shared/gemfiles/20150130/Gemfile: -------------------------------------------------------------------------------- 1 | #source 'http://production.cf.rubygems.org/' 2 | source 'https://rubygems.org' 3 | 4 | gem 'nokogiri' 5 | gem 'sqlite3' 6 | gem 'mysql2', :platforms => :ruby 7 | gem 'pg' 8 | gem 'json' 9 | gem 'ffi' 10 | gem 'bcrypt' 11 | gem 'yajl-ruby' 12 | gem 'thin' 13 | gem 'RedCloth' 14 | gem 'escape_utils' 15 | gem 'posix-spawn', :platforms => :ruby 16 | gem 'nokogumbo' 17 | gem 'github-markdown' 18 | gem 'rugged', :platforms => :ruby 19 | gem 'charlock_holmes', :platforms => :ruby 20 | gem 'unf_ext' 21 | -------------------------------------------------------------------------------- /osx/internal/reset_environment.sh: -------------------------------------------------------------------------------- 1 | export PATH="$SELFDIR/internal/bin":/usr/bin:/bin:/usr/sbin:/sbin 2 | export MACOSX_DEPLOYMENT_TARGET=10.8 3 | export MACOSX_COMPATIBLE_DEPLOYMENT_TARGETS="10.8 10.9 10.10 10.11" 4 | export CC="$SELFDIR/internal/bin/cc" 5 | export CXX="$SELFDIR/internal/bin/c++" 6 | unset DYLD_LIBRARY_PATH 7 | unset DYLD_INSERT_LIBRARIES 8 | unset CFLAGS 9 | unset CXXFLAGS 10 | unset LDFLAGS 11 | unset RUBYOPT 12 | unset RUBYLIB 13 | unset GEM_HOME 14 | unset GEM_PATH 15 | unset SSL_CERT_DIR 16 | unset SSL_CERT_FILE 17 | -------------------------------------------------------------------------------- /linux/internal/restore_environment.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | # immutable: string 3 | 4 | IN_TRAVELING_RUBY = true 5 | 6 | restorable_envs = ['LD_LIBRARY_PATH', 'RUBYOPT', 'RUBYLIB'].freeze 7 | restorable_envs.each do |name| 8 | ENV[name] = ENV["ORIG_#{name}"] 9 | ENV.delete("ORIG_#{name}") 10 | end 11 | 12 | # We can't restore these environments now because they'll be used later. 13 | # We just store the original values so that the program can decide what to do. 14 | $OVERRIDDEN_ENVIRONMENTS = { 15 | 'SSL_CERT_DIR' => ENV['OLD_SSL_CERT_DIR'], 16 | 'SSL_CERT_FILE' => ENV['OLD_SSL_CERT_FILE'] 17 | } 18 | -------------------------------------------------------------------------------- /osx/internal/restore_environment.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | # immutable: string 3 | 4 | IN_TRAVELING_RUBY = true 5 | 6 | restorable_envs = ['DYLD_LIBRARY_PATH', 'TERMINFO', 'RUBYOPT', 'RUBYLIB'].freeze 7 | restorable_envs.each do |name| 8 | ENV[name] = ENV["ORIG_#{name}"] 9 | ENV.delete("ORIG_#{name}") 10 | end 11 | 12 | # We can't restore these environments now because they'll be used later. 13 | # We just store the original values so that the program can decide what to do. 14 | $OVERRIDDEN_ENVIRONMENTS = { 15 | 'SSL_CERT_DIR' => ENV['OLD_SSL_CERT_DIR'], 16 | 'SSL_CERT_FILE' => ENV['OLD_SSL_CERT_FILE'] 17 | } 18 | -------------------------------------------------------------------------------- /osx/internal/bin/cc: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | CC=(/usr/bin/cc) 4 | ARGS=() 5 | REGEX='(^| )-c( |$)' 6 | 7 | if [[ "$RUNTIME_DIR" != "" && -e "$RUNTIME_DIR/bin/ccache" ]]; then 8 | CC=("$RUNTIME_DIR/bin/ccache" /usr/bin/cc) 9 | fi 10 | if [[ "$DEAD_STRIP" != "" ]] && $DEAD_STRIP; then 11 | ARGS+=(-Wl,-dead_strip) 12 | fi 13 | if [[ "$@" =~ $REGEX ]]; then 14 | if [[ "$RUNTIME_DIR" != "" ]]; then 15 | ARGS+=("-I$RUNTIME_DIR/include") 16 | fi 17 | exec "${CC[@]}" "${ARGS[@]}" "$@" 18 | else 19 | if [[ "$RUNTIME_DIR" != "" ]]; then 20 | ARGS+=("-L$RUNTIME_DIR/lib") 21 | fi 22 | exec "${CC[@]}" -Wl,-headerpad_max_install_names "${ARGS[@]}" "$@" 23 | fi 24 | -------------------------------------------------------------------------------- /osx/internal/bin/c++: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | CXX=(/usr/bin/c++) 4 | ARGS=() 5 | REGEX='(^| )-c( |$)' 6 | 7 | if [[ "$RUNTIME_DIR" != "" && -e "$RUNTIME_DIR/bin/ccache" ]]; then 8 | CXX=("$RUNTIME_DIR/bin/ccache" /usr/bin/c++) 9 | fi 10 | if [[ "$DEAD_STRIP" != "" ]] && $DEAD_STRIP; then 11 | ARGS+=(-Wl,-dead_strip) 12 | fi 13 | if [[ "$@" =~ $REGEX ]]; then 14 | if [[ "$RUNTIME_DIR" != "" ]]; then 15 | ARGS+=("-I$RUNTIME_DIR/include") 16 | fi 17 | exec "${CXX[@]}" "${ARGS[@]}" "$@" 18 | else 19 | if [[ "$RUNTIME_DIR" != "" ]]; then 20 | ARGS+=("-L$RUNTIME_DIR/lib") 21 | fi 22 | exec "${CXX[@]}" -Wl,-headerpad_max_install_names "${ARGS[@]}" "$@" 23 | fi 24 | -------------------------------------------------------------------------------- /osx/internal/rbconfig_patch.rb: -------------------------------------------------------------------------------- 1 | # Traveling Ruby modifications: 2 | # get rid of our custom compilation flags. 3 | [RbConfig::CONFIG, RbConfig::MAKEFILE_CONFIG].each do |config| 4 | config["CC"] = (String.new << "xcrun clang") 5 | config["CXX"] = (String.new << "xcrun clang++") 6 | config["CPP"] = (String.new << "xcrun clang -E") 7 | config["LDSHARED"] = (String.new << "xcrun clang -dynamic -bundle") 8 | config["LDSHAREDXX"] = (String.new << "xcrun clang++ -dynamic -bundle") 9 | config["CFLAGS"] = config["cflags"].dup 10 | config["CXXFLAGS"] = config["cxxflags"].dup 11 | config["RUBY_EXEC_PREFIX"] = config["exec_prefix"].dup 12 | config.delete("CC_VERSION") 13 | end 14 | -------------------------------------------------------------------------------- /linux/internal/setuser: -------------------------------------------------------------------------------- 1 | #!/system/python27 -u 2 | import sys, os, pwd 3 | 4 | if len(sys.argv) < 3: 5 | sys.stderr.write("Usage: /sbin/setuser USERNAME COMMAND [args..]\n") 6 | sys.exit(1) 7 | 8 | def abort(message): 9 | sys.stderr.write("setuser: %s\n" % message) 10 | sys.exit(1) 11 | 12 | username = sys.argv[1] 13 | try: 14 | user = pwd.getpwnam(username) 15 | except KeyError: 16 | abort("user %s not found" % username) 17 | os.initgroups(username, user.pw_gid) 18 | os.setgid(user.pw_gid) 19 | os.setuid(user.pw_uid) 20 | os.environ['USER'] = username 21 | os.environ['HOME'] = user.pw_dir 22 | os.environ['UID'] = str(user.pw_uid) 23 | try: 24 | os.execvp(sys.argv[2], sys.argv[2:]) 25 | except OSError as e: 26 | abort("cannot execute %s: %s" % (sys.argv[2], str(e))) 27 | -------------------------------------------------------------------------------- /shared/gemfiles/20150517/Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | curses (1.0.1) 5 | fast-stemmer (1.0.2) 6 | hitimes (1.2.2) 7 | hitimes (1.2.2-x86-mingw32) 8 | kgio (2.9.3) 9 | mysql2 (0.3.18) 10 | mysql2 (0.3.18-x86-mingw32) 11 | pg (0.18.2) 12 | pg (0.18.2-x86-mingw32) 13 | puma (2.11.2) 14 | rack (>= 1.1, < 2.0) 15 | rack (1.6.1) 16 | raindrops (0.13.0) 17 | redcarpet (3.2.3) 18 | snappy (0.0.11) 19 | unicorn (4.9.0) 20 | kgio (~> 2.6) 21 | rack 22 | raindrops (~> 0.7) 23 | 24 | PLATFORMS 25 | ruby 26 | x86-mingw32 27 | 28 | DEPENDENCIES 29 | curses 30 | fast-stemmer 31 | hitimes 32 | kgio 33 | mysql2 34 | pg 35 | puma 36 | raindrops 37 | redcarpet 38 | snappy 39 | unicorn 40 | -------------------------------------------------------------------------------- /linux/image/install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | function create_user() 5 | { 6 | local name="$1" 7 | local full_name="$2" 8 | local id="$3" 9 | create_group $name $id 10 | if ! grep -q "^$name:" /etc/passwd; then 11 | adduser --uid $id --gid $id --comment "$full_name" $name 12 | fi 13 | usermod -L $name 14 | } 15 | 16 | function create_group() 17 | { 18 | local name="$1" 19 | local id="$2" 20 | if ! grep -q "^$name:" /etc/group >/dev/null; then 21 | groupadd --gid $id $name 22 | fi 23 | } 24 | 25 | set -x 26 | 27 | cd /tmp 28 | rpm -Uvh http://mirror.overthewire.com.au/pub/epel/6/x86_64/epel-release-6-8.noarch.rpm 29 | yum update -y 30 | yum install -y --skip-broken --enablerepo centosplus \ 31 | centos-release-SCL file db4-utils compat-db43 mock wget s3cmd 32 | yum install -y --skip-broken --enablerepo centosplus \ 33 | python27-python 34 | create_user app "App" 1000 35 | usermod -a -G mock app 36 | mkdir -p /etc/container_environment /etc/workaround-docker-2267 37 | ln -s /etc/workaround-docker-2267 /cte 38 | rm -rf /image /tmp/* 39 | yum clean all 40 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014 Phusion 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /osx/internal/sanity_check: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | set -o pipefail 4 | 5 | function cleanup() 6 | { 7 | set +e 8 | local pids=`jobs -p` 9 | if [[ "$pids" != "" ]]; then 10 | kill $pids 2>/dev/null 11 | fi 12 | if [[ `type -t _cleanup` == function ]]; then 13 | _cleanup 14 | fi 15 | } 16 | 17 | function grep_without_fail() 18 | { 19 | grep "$@" || true 20 | } 21 | 22 | trap cleanup EXIT 23 | DIR="$1" 24 | TAB=`perl -e 'print "\t"'` 25 | ERROR=false 26 | STANDARD_LIBS="(@executable_path/|/usr/lib/libobjc|/usr/lib/libSystem|/usr/lib/libutil|/usr/lib/libz" 27 | STANDARD_LIBS="$STANDARD_LIBS|/usr/lib/libiconv|/usr/lib/libstdc\+\+)\." 28 | STANDARD_LIBS="($STANDARD_LIBS|CoreFoundation|CoreServices)" 29 | 30 | for F in $DIR/bin.real/ruby `find $DIR -name '*.bundle'` `find $DIR -name '*.dylib'`; do 31 | EXTRA_LIBS=`otool -L $F | tail -n +2 | sed "s/^${TAB}//" | sed "s/ (.*//" | grep_without_fail -vE "$STANDARD_LIBS"` 32 | EXTRA_LIBS=`echo $EXTRA_LIBS` 33 | if [[ "$EXTRA_LIBS" != "" ]]; then 34 | echo "$F is linked to non-system libraries: $EXTRA_LIBS" 35 | ERROR=true 36 | fi 37 | done 38 | if $ERROR; then 39 | exit 1 40 | else 41 | echo "All OK." 42 | fi 43 | -------------------------------------------------------------------------------- /shared/library.sh: -------------------------------------------------------------------------------- 1 | if perl -v >/dev/null 2>/dev/null; then 2 | RESET=`perl -e 'print("\e[0m")'` 3 | BOLD=`perl -e 'print("\e[1m")'` 4 | YELLOW=`perl -e 'print("\e[33m")'` 5 | BLUE_BG=`perl -e 'print("\e[44m")'` 6 | elif python -V >/dev/null 2>/dev/null; then 7 | RESET=`echo 'import sys; sys.stdout.write("\033[0m")' | python` 8 | BOLD=`echo 'import sys; sys.stdout.write("\033[1m")' | python` 9 | YELLOW=`echo 'import sys; sys.stdout.write("\033[33m")' | python` 10 | BLUE_BG=`echo 'import sys; sys.stdout.write("\033[44m")' | python` 11 | else 12 | RESET= 13 | BOLD= 14 | YELLOW= 15 | BLUE_BG= 16 | fi 17 | 18 | function header() 19 | { 20 | local title="$1" 21 | echo "${BLUE_BG}${YELLOW}${BOLD}${title}${RESET}" 22 | echo "------------------------------------------" 23 | } 24 | 25 | function run() 26 | { 27 | echo "+ $@" 28 | "$@" 29 | } 30 | 31 | function absolute_path() 32 | { 33 | local dir="`dirname \"$1\"`" 34 | local name="`basename \"$1\"`" 35 | dir="`cd \"$dir\" && pwd`" 36 | echo "$dir/$name" 37 | } 38 | 39 | function cleanup() 40 | { 41 | set +e 42 | local pids=`jobs -p` 43 | if [[ "$pids" != "" ]]; then 44 | kill $pids 2>/dev/null 45 | fi 46 | if [[ `type -t _cleanup` == function ]]; then 47 | _cleanup 48 | fi 49 | } 50 | 51 | trap cleanup EXIT 52 | -------------------------------------------------------------------------------- /shared/gemfiles/20150204-default/Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | RedCloth (4.2.9) 5 | RedCloth (4.2.9-x86-mingw32) 6 | bcrypt (3.1.10) 7 | bcrypt (3.1.10-x86-mingw32) 8 | charlock_holmes (0.7.3) 9 | daemons (1.1.9) 10 | escape_utils (1.0.1) 11 | eventmachine (1.0.6) 12 | ffi (1.9.6) 13 | ffi (1.9.6-x86-mingw32) 14 | github-markdown (0.6.8) 15 | json (1.8.2) 16 | mini_portile (0.6.2) 17 | mysql2 (0.3.17) 18 | nokogiri (1.6.6.2) 19 | mini_portile (~> 0.6.0) 20 | nokogiri (1.6.6.2-x86-mingw32) 21 | mini_portile (~> 0.6.0) 22 | nokogumbo (1.3.0) 23 | nokogiri 24 | posix-spawn (0.3.9) 25 | rack (1.6.0) 26 | rugged (0.21.4) 27 | sqlite3 (1.3.10) 28 | sqlite3 (1.3.10-x86-mingw32) 29 | thin (1.6.3) 30 | daemons (~> 1.0, >= 1.0.9) 31 | eventmachine (~> 1.0) 32 | rack (~> 1.0) 33 | unf_ext (0.0.6) 34 | unf_ext (0.0.6-x86-mingw32) 35 | yajl-ruby (1.2.1) 36 | 37 | PLATFORMS 38 | ruby 39 | x86-mingw32 40 | 41 | DEPENDENCIES 42 | RedCloth 43 | bcrypt 44 | charlock_holmes 45 | escape_utils 46 | ffi 47 | github-markdown 48 | json 49 | mysql2 50 | nokogiri 51 | nokogumbo 52 | posix-spawn 53 | rugged 54 | sqlite3 55 | thin 56 | unf_ext 57 | yajl-ruby 58 | -------------------------------------------------------------------------------- /shared/gemfiles/20150130/Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | RedCloth (4.2.9) 5 | RedCloth (4.2.9-x86-mingw32) 6 | bcrypt (3.1.9) 7 | bcrypt (3.1.9-x86-mingw32) 8 | charlock_holmes (0.7.3) 9 | daemons (1.1.9) 10 | escape_utils (1.0.1) 11 | eventmachine (1.0.4) 12 | ffi (1.9.6) 13 | ffi (1.9.6-x86-mingw32) 14 | github-markdown (0.6.8) 15 | json (1.8.1) 16 | mini_portile (0.6.1) 17 | mysql2 (0.3.17) 18 | nokogiri (1.6.5) 19 | mini_portile (~> 0.6.0) 20 | nokogiri (1.6.5-x86-mingw32) 21 | mini_portile (~> 0.6.0) 22 | nokogumbo (1.3.0) 23 | nokogiri 24 | pg (0.17.1) 25 | posix-spawn (0.3.9) 26 | rack (1.6.0) 27 | rugged (0.21.4) 28 | sqlite3 (1.3.9) 29 | sqlite3 (1.3.9-x86-mingw32) 30 | thin (1.6.3) 31 | daemons (~> 1.0, >= 1.0.9) 32 | eventmachine (~> 1.0) 33 | rack (~> 1.0) 34 | unf_ext (0.0.6) 35 | unf_ext (0.0.6-x86-mingw32) 36 | yajl-ruby (1.2.1) 37 | 38 | PLATFORMS 39 | ruby 40 | x86-mingw32 41 | 42 | DEPENDENCIES 43 | RedCloth 44 | bcrypt 45 | charlock_holmes 46 | escape_utils 47 | ffi 48 | github-markdown 49 | json 50 | mysql2 51 | nokogiri 52 | nokogumbo 53 | pg 54 | posix-spawn 55 | rugged 56 | sqlite3 57 | thin 58 | unf_ext 59 | yajl-ruby 60 | -------------------------------------------------------------------------------- /shared/test-gems: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | SELFDIR=`dirname "$0"` 5 | SELFDIR=`cd "$SELFDIR" && pwd` 6 | source "$SELFDIR/library.sh" 7 | 8 | BUILD_OUTPUT_DIR= 9 | 10 | function usage() 11 | { 12 | echo "Usage: ./test-gems [options] " 13 | echo "Test the native extension gems." 14 | echo 15 | echo "Options:" 16 | echo " -h Show this help" 17 | } 18 | 19 | function parse_options() 20 | { 21 | local OPTIND=1 22 | local opt 23 | while getopts "h" opt; do 24 | case "$opt" in 25 | h) 26 | usage 27 | exit 28 | ;; 29 | *) 30 | return 1 31 | ;; 32 | esac 33 | done 34 | 35 | (( OPTIND -= 1 )) || true 36 | shift $OPTIND || true 37 | BUILD_OUTPUT_DIR="$1" 38 | 39 | if [[ "$BUILD_OUTPUT_DIR" = "" ]]; then 40 | usage 41 | exit 1 42 | fi 43 | if [[ ! -e "$BUILD_OUTPUT_DIR" ]]; then 44 | echo "ERROR: $BUILD_OUTPUT_DIR doesn't exist." 45 | exit 1 46 | fi 47 | } 48 | 49 | 50 | parse_options "$@" 51 | 52 | 53 | ########## 54 | 55 | 56 | GEMS=(openssl readline rugged charlock_holmes unf_ext bcrypt RedCloth github/markdown 57 | eventmachine escape_utils json nokogiri mysql2 ffi pg posix-spawn 58 | thin sqlite3 yajl puma/puma_http11 kgio raindrops fast-stemmer 59 | hitimes redcarpet snappy curses) 60 | 61 | header "Testing gems..." 62 | export LD_BIND_NOW=1 63 | export DYLD_BIND_AT_LAUNCH=1 64 | for LIB in ${GEMS[@]}; do 65 | run "$BUILD_OUTPUT_DIR/bin/ruby" -r$LIB -e true 66 | done 67 | echo "All gems OK!" 68 | -------------------------------------------------------------------------------- /linux/internal/build-ruby: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | source /system_shared/library.sh 4 | 5 | header "Preparing" 6 | if [[ "$ARCHITECTURE" = x86 ]]; then 7 | CHROOT_NAME=epel-5-i386 8 | else 9 | CHROOT_NAME=epel-5-x86_64 10 | fi 11 | MOCK_NAME="$CHROOT_NAME-$RUBY_VERSION-$ARCHITECTURE" 12 | 13 | ROOT=/var/lib/mock/$MOCK_NAME/root 14 | run rm -rf $ROOT/system $ROOT/output 15 | run mkdir $ROOT/system $ROOT/output 16 | run cp -R /system/* $ROOT/system/ 17 | run cp -R /system_shared/* $ROOT/system/ 18 | run rm -rf $ROOT/system/gemfiles 19 | if [[ -e /gemfiles ]]; then 20 | run cp -R /gemfiles $ROOT/system/ 21 | fi 22 | echo "Dumping parameters" 23 | echo $ARCHITECTURE > $ROOT/system/NAME 24 | echo $APP_UID > $ROOT/system/APP_UID 25 | echo $APP_GID > $ROOT/system/APP_GID 26 | echo $RUBY_VERSION > $ROOT/system/RUBY_VERSION 27 | echo $BUNDLER_VERSION > $ROOT/system/BUNDLER_VERSION 28 | echo $CONCURRENCY > $ROOT/system/CONCURRENCY 29 | echo $SETUP_SOURCE > $ROOT/system/SETUP_SOURCE 30 | echo $COMPILE > $ROOT/system/COMPILE 31 | echo $SANITY_CHECK_OUTPUT > $ROOT/system/SANITY_CHECK_OUTPUT 32 | echo $DEBUG_SHELL > $ROOT/system/DEBUG_SHELL 33 | echo "Entering mock chroot" 34 | run /system/setuser app /usr/bin/mock \ 35 | -r $CHROOT_NAME \ 36 | --uniqueext "$RUBY_VERSION-$ARCHITECTURE" \ 37 | --quiet \ 38 | --shell \ 39 | /system/build-ruby-inside-mock 40 | echo "Left mock chroot" 41 | echo 42 | 43 | header "Finalizing" 44 | run chown $APP_UID:$APP_GID /output 45 | run cp -dpR $ROOT/output/* /output/ 46 | run rm -rf $ROOT/system $ROOT/output 47 | -------------------------------------------------------------------------------- /linux/shell: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | SELFDIR=`dirname "$0"` 5 | SELFDIR=`cd "$SELFDIR" && pwd` 6 | 7 | RUNTIME_DIR= 8 | 9 | function usage() 10 | { 11 | echo "Usage: ./shell [options] " 12 | echo "Open a shell for the specified runtime directory." 13 | echo 14 | echo "Options:" 15 | echo " -h Show this help" 16 | } 17 | 18 | function parse_options() 19 | { 20 | local OPTIND=1 21 | local opt 22 | while getopts "h" opt; do 23 | case "$opt" in 24 | h) 25 | usage 26 | exit 27 | ;; 28 | *) 29 | return 1 30 | ;; 31 | esac 32 | done 33 | 34 | (( OPTIND += 1 )) || true 35 | shift $OPTIND || true 36 | RUNTIME_DIR="$1" 37 | 38 | if [[ "$RUNTIME_DIR" = "" ]]; then 39 | usage 40 | exit 1 41 | fi 42 | if [[ ! -e "$RUNTIME_DIR" ]]; then 43 | echo "ERROR: $RUNTIME_DIR doesn't exist." 44 | exit 1 45 | fi 46 | } 47 | 48 | 49 | parse_options "$@" 50 | RUNTIME_DIR=`cd "$RUNTIME_DIR" && pwd` 51 | mkdir -p "$RUNTIME_DIR/mock" 52 | 53 | echo "Within the shell, you can the mock environment with:" 54 | if [[ -e "$RUNTIME_DIR/mock/epel-5-i386" ]]; then 55 | echo " /usr/bin/mock -r epel-5-i386 --shell" 56 | else 57 | echo " /usr/bin/mock -r epel-5-x86_64 --shell" 58 | fi 59 | 60 | exec docker run \ 61 | --rm -t -i \ 62 | --cap-add SYS_ADMIN --cap-add SYS_CHROOT \ 63 | -v "$SELFDIR/internal:/system:ro" \ 64 | -v "$RUNTIME_DIR/mock:/var/lib/mock" \ 65 | -v "`pwd`:/host" \ 66 | phusion/traveling-ruby-builder \ 67 | /system/my_init --quiet --skip-runit --skip-startup-files -- \ 68 | /bin/bash -l 69 | -------------------------------------------------------------------------------- /osx/README.md: -------------------------------------------------------------------------------- 1 | # Traveling Ruby OS X build system 2 | 3 | The build system requires the Developer Commandline Tools to be installed, as well as the OS X 10.8 SDK. 4 | 5 | To build binary packages, run: 6 | 7 | cd osx 8 | rake 9 | 10 | You can view all tasks by running `rake -T`. 11 | 12 | ## Installing the OS X 10.8 SDK 13 | 14 | You are required to use an older version of Xcode as you need to have the OS X 10.8 SDK installed locally. You can install Xcode 5.1.1 as follows. 15 | 16 | ### The automated way 17 | 18 | Install [xcode-install](https://github.com/neonichu/xcode-install) using 19 | 20 | ``` 21 | [sudo] gem install xcode-install 22 | ``` 23 | 24 | ``` 25 | xcversion update 26 | xcversion install 5.1.1 27 | ``` 28 | 29 | This will install the old Xcode release in `/Applications/Xcode-5.1.1.app`. 30 | 31 | To copy over the SDK from the old Xcode to the new one, just run 32 | 33 | ``` 34 | rake install_sdk 35 | ``` 36 | 37 | ### The manual way 38 | 39 | #### 1. Download Xcode 5.1.1 40 | 41 | Open the [Developer Portal download page](https://developer.apple.com/downloads/) and login with your Apple account. When you see the list of downloads, just open the [xcode_5.1.1.dmg link](http://adcdownload.apple.com/Developer_Tools/xcode_5.1.1/xcode_5.1.1.dmg) in your browser. 42 | 43 | #### 2. Show Xcode package contents 44 | 45 | ![](https://raw.githubusercontent.com/phusion/traveling-ruby/master/doc/xcodepackage.jpg) 46 | 47 | Open the Xcode .dmg file. Right click on Xcode.app and choose "Show Package Contents". 48 | 49 | #### 3. Locate OS X 10.8 SDK and copy it to the system 50 | 51 | ![](https://raw.githubusercontent.com/phusion/traveling-ruby/master/doc/sdk.jpg) 52 | 53 | Locate the OS X 10.8 SDK inside the Xcode package contents. Copy this directory to /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/. 54 | -------------------------------------------------------------------------------- /windows/Rakefile: -------------------------------------------------------------------------------- 1 | VERSION = File.read("../VERSION.txt").strip 2 | RUBY_VERSIONS = File.read("../RUBY_VERSIONS.txt").strip.split(/\s+/) 3 | 4 | task :default => :package 5 | 6 | desc "Create packages for all Ruby versions (#{RUBY_VERSIONS.join(' ')})" 7 | task :package do 8 | # Do nothing 9 | end 10 | 11 | desc "Build binaries for all Ruby versions (#{RUBY_VERSIONS.join(' ')})" 12 | task :build do 13 | # Do nothing 14 | end 15 | 16 | desc "Upload all packages to the server" 17 | task :upload do 18 | # Do nothing 19 | end 20 | 21 | desc "Clean all packages and binaries" 22 | task :clean do 23 | sh "rm -rf output" 24 | end 25 | 26 | RUBY_VERSIONS.each do |ruby_version| 27 | package = "traveling-ruby-#{VERSION}-#{ruby_version}-win32.tar.gz" 28 | 29 | task :package => "package:#{ruby_version}" 30 | task :build => "build:#{ruby_version}" 31 | task :upload => "upload:#{ruby_version}" 32 | task :clean => "clean:#{ruby_version}" 33 | 34 | desc "Create packages for Ruby #{ruby_version}" 35 | task "package:#{ruby_version}" => [package] 36 | 37 | desc "Build binaries for Ruby #{ruby_version}" 38 | task "build:#{ruby_version}" => "output/#{ruby_version}/bin" 39 | 40 | file(package => "output/#{ruby_version}/bin") do 41 | sh "./package -r #{package} output/#{ruby_version}" 42 | end 43 | 44 | # We use 'file' instead of 'directory' here so that packages are updated 45 | # whenever we update binaries. 46 | file("output/#{ruby_version}/bin") do 47 | sh "mkdir -p cache output/#{ruby_version}" 48 | sh "./build-ruby -a x86 -r #{ruby_version} cache output/#{ruby_version}" 49 | end 50 | 51 | desc "Upload Ruby #{ruby_version} packages to the server" 52 | task "upload:#{ruby_version}" => [package] do 53 | sh "s3cmd -P sync --no-preserve #{package} s3://traveling-ruby/releases/" 54 | end 55 | 56 | desc "Clean Ruby #{ruby_version} packages and binaries, but not the runtime" 57 | task "clean:#{ruby_version}" do 58 | sh "rm -rf #{package} output/#{ruby_version}" 59 | end 60 | end 61 | -------------------------------------------------------------------------------- /linux/internal/setup-runtime: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | source /system_shared/library.sh 4 | 5 | function list_db_files() 6 | { 7 | file "$@" | grep 'Berkeley DB' | grep 'version 9' | awk '{ print $1 }' | sed 's/:$//' 8 | } 9 | 10 | if [[ "$ARCHITECTURE" = x86 ]]; then 11 | CHROOT_NAME=epel-5-i386 12 | else 13 | CHROOT_NAME=epel-5-x86_64 14 | fi 15 | MOCK_NAME="$CHROOT_NAME-$RUBY_VERSION-$ARCHITECTURE" 16 | run chown root:mock /var/lib/mock 17 | run chmod g+ws /var/lib/mock 18 | echo 19 | 20 | header "Setting up chroot" 21 | if $INITIALIZE; then 22 | run /system/setuser app /usr/bin/mock \ 23 | -r $CHROOT_NAME \ 24 | --uniqueext "$RUBY_VERSION-$ARCHITECTURE" \ 25 | --init 26 | fi 27 | run /system/setuser app /usr/bin/mock \ 28 | -r $CHROOT_NAME \ 29 | --uniqueext "$RUBY_VERSION-$ARCHITECTURE" \ 30 | --install yum 31 | echo 32 | 33 | header "Fixing RPM database" 34 | # CentOS 6's RPM generates database files which CentOS 5's RPM 35 | # cannot read, so we fix this. See 36 | # http://forums.fedoraforum.org/showthread.php?t=222988 37 | echo "+ Entering /var/lib/mock/$MOCK_NAME/root/var/lib/rpm" 38 | pushd /var/lib/mock/$MOCK_NAME/root/var/lib/rpm >/dev/null 39 | run rm -fv __db* 40 | for F in $(list_db_files *); do 41 | echo "Fixing $F" 42 | echo "+ db_dump $F > $F.dump" 43 | db_dump $F > $F.dump 44 | run rm $F 45 | echo "+ db43_load $F < $F.dump" 46 | db43_load $F < $F.dump 47 | run rm $F.dump 48 | done 49 | echo "+ Leaving RPM database directory" 50 | popd >/dev/null 51 | echo 52 | 53 | header "Installing additional software inside chroot" 54 | run rm -rf /var/lib/mock/$MOCK_NAME/root/system 55 | run mkdir /var/lib/mock/$MOCK_NAME/root/system 56 | run cp -R /system/* /var/lib/mock/$MOCK_NAME/root/system/ 57 | run cp -R /system_shared/* /var/lib/mock/$MOCK_NAME/root/system/ 58 | echo $ARCHITECTURE > /var/lib/mock/$MOCK_NAME/root/system/ARCHITECTURE 59 | run /system/setuser app /usr/bin/mock \ 60 | -r $CHROOT_NAME \ 61 | --uniqueext "$RUBY_VERSION-$ARCHITECTURE" \ 62 | --shell \ 63 | /system/setup-runtime-inside-mock 64 | run rm -rf /var/lib/mock/$MOCK_NAME/root/system 65 | echo "----- chroot left -----" 66 | echo 67 | 68 | header "Finished!" 69 | -------------------------------------------------------------------------------- /linux/upload: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | SELFDIR=`dirname "$0"` 5 | SELFDIR=`cd "$SELFDIR" && pwd` 6 | source "$SELFDIR/../shared/library.sh" 7 | 8 | TEMPDIR= 9 | 10 | function cleanup() 11 | { 12 | if [[ "$TEMPDIR" != "" ]]; then 13 | rm -rf "$TEMPDIR" 14 | fi 15 | } 16 | 17 | if [[ $# == 0 ]]; then 18 | echo "Usage: ./upload " 19 | echo "Uploads files to Amazon S3." 20 | exit 1 21 | fi 22 | 23 | if [[ ! -e ~/.s3cfg ]]; then 24 | echo "~/.s3cfg doesn't exist; configuring one..." 25 | TEMPDIR=`mktemp -d /tmp/traveling-ruby.XXXXXXXX` 26 | touch "$TEMPDIR/s3cfg" 27 | docker run --rm -t -i \ 28 | -v "$SELFDIR/internal:/system:ro" \ 29 | -v "$TEMPDIR:/work" \ 30 | phusion/traveling-ruby-builder \ 31 | /system/my_init --quiet --skip-runit --skip-startup-files -- \ 32 | /system/s3configure 33 | cp "$TEMPDIR/s3cfg" ~/.s3cfg 34 | rm -rf "$TEMPDIR" 35 | TEMPDIR= 36 | echo 37 | fi 38 | 39 | MOUNTS=() 40 | FILE_BASENAMES=() 41 | DIR_BASENAMES=() 42 | for F in "$@"; do 43 | BASENAME="`basename \"$F\"`" 44 | F="`absolute_path \"$F\"`" 45 | 46 | MOUNTS+=(-v "$F:/$BASENAME:ro") 47 | if [[ -f "$F" ]]; then 48 | FILE_BASENAMES+=("$BASENAME") 49 | else 50 | DIR_BASENAMES+=("$BASENAME") 51 | fi 52 | done 53 | 54 | header "Uploading `echo ${FILE_BASENAMES[@]}` to Amazon S3..." 55 | S3CFG="`echo ~/.s3cfg`" 56 | 57 | docker run --rm -t -i \ 58 | "${MOUNTS[@]}" \ 59 | -v "$S3CFG:/s3cfg:ro" \ 60 | -v "$SELFDIR/internal:/system:ro" \ 61 | phusion/traveling-ruby-builder \ 62 | /system/my_init --quiet --skip-runit --skip-startup-files -- \ 63 | /system/s3init \ 64 | s3cmd -P sync --no-preserve "${FILE_BASENAMES[@]}" s3://traveling-ruby/releases/ 65 | 66 | for DIR_BASENAME in "${DIR_BASENAMES[@]}"; do 67 | echo 68 | header "Uploading $DIR_BASENAME to Amazon S3..." 69 | docker run --rm -t -i \ 70 | "${MOUNTS[@]}" \ 71 | -v "$S3CFG:/s3cfg:ro" \ 72 | -v "$SELFDIR/internal:/system:ro" \ 73 | phusion/traveling-ruby-builder \ 74 | /system/my_init --quiet --skip-runit --skip-startup-files -- \ 75 | /system/s3init \ 76 | s3cmd -P --delete-removed --no-preserve sync "$DIR_BASENAME/" "s3://traveling-ruby/releases/$DIR_BASENAME/" 77 | done 78 | -------------------------------------------------------------------------------- /linux/setup-runtime: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | SELFDIR=`dirname "$0"` 5 | SELFDIR=`cd "$SELFDIR" && pwd` 6 | source "$SELFDIR/../shared/library.sh" 7 | 8 | RUNTIME_DIR= 9 | ARCHITECTURE= 10 | INITIALIZE=true 11 | 12 | function usage() 13 | { 14 | echo "Usage: ./setup-runtime [options] " 15 | echo "Sets up the Traveling Ruby build system's runtime." 16 | echo 17 | echo "Options:" 18 | echo " -a NAME Architecture to setup (x86, x86_64)" 19 | echo " -r VERSION Ruby version to setup" 20 | echo " -i Only initialize chroot environment" 21 | echo " -I Do not initialize chroot environment" 22 | echo " -h Show this help" 23 | } 24 | 25 | function parse_options() 26 | { 27 | local OPTIND=1 28 | local opt 29 | while getopts "a:r:iIh" opt; do 30 | case "$opt" in 31 | a) 32 | ARCHITECTURE=$OPTARG 33 | ;; 34 | r) 35 | RUBY_VERSION=$OPTARG 36 | ;; 37 | i) 38 | INITIALIZE=true 39 | DOWNLOAD_RUBY=false 40 | ;; 41 | I) 42 | INITIALIZE=false 43 | ;; 44 | h) 45 | usage 46 | exit 47 | ;; 48 | *) 49 | return 1 50 | ;; 51 | esac 52 | done 53 | 54 | (( OPTIND -= 1 )) || true 55 | shift $OPTIND || true 56 | RUNTIME_DIR="$1" 57 | 58 | if [[ "$RUNTIME_DIR" = "" ]]; then 59 | usage 60 | exit 1 61 | fi 62 | if [[ ! -e "$RUNTIME_DIR" ]]; then 63 | echo "ERROR: $RUNTIME_DIR doesn't exist." 64 | exit 1 65 | fi 66 | if [[ "$ARCHITECTURE" = "" ]]; then 67 | echo "ERROR: please specify an architecture with -a." 68 | exit 1 69 | fi 70 | if [[ "$RUBY_VERSION" = "" ]]; then 71 | echo "ERROR: please specify a Ruby version with -r." 72 | exit 1 73 | fi 74 | } 75 | 76 | 77 | parse_options "$@" 78 | RUNTIME_DIR=`cd "$RUNTIME_DIR" && pwd` 79 | 80 | header "Preparing" 81 | run mkdir -p "$RUNTIME_DIR/mock" 82 | 83 | exec docker run \ 84 | --rm \ 85 | --privileged \ 86 | -v "$SELFDIR/internal:/system:ro" \ 87 | -v "$SELFDIR/../shared:/system_shared:ro" \ 88 | -v "$RUNTIME_DIR/mock:/var/lib/mock" \ 89 | -e "ARCHITECTURE=$ARCHITECTURE" \ 90 | -e "RUBY_VERSION=$RUBY_VERSION" \ 91 | -e "INITIALIZE=$INITIALIZE" \ 92 | phusion/traveling-ruby-builder \ 93 | /system/my_init --quiet --skip-runit --skip-startup-files -- \ 94 | /system/setup-runtime 95 | -------------------------------------------------------------------------------- /osx/Rakefile: -------------------------------------------------------------------------------- 1 | VERSION = File.read("../VERSION.txt").strip 2 | RUBY_VERSIONS = File.read("../RUBY_VERSIONS.txt").strip.split(/\s+/) 3 | 4 | task :default => :package 5 | 6 | desc "Create packages for all Ruby versions (#{RUBY_VERSIONS.join(' ')})" 7 | task :package do 8 | # Do nothing 9 | end 10 | 11 | desc "Build the runtime" 12 | task :runtime => "runtime/ok" 13 | 14 | desc "Build binaries for all Ruby versions (#{RUBY_VERSIONS.join(' ')})" 15 | task :build do 16 | # Do nothing 17 | end 18 | 19 | desc "Test all Ruby versions (#{RUBY_VERSIONS.join(' ')})" 20 | task :test do 21 | # Do nothing 22 | end 23 | 24 | desc "Upload all packages to the server" 25 | task :upload do 26 | # Do nothing 27 | end 28 | 29 | desc "Clean all packages, but not the runtime" 30 | task :clean do 31 | sh "rm -rf output" 32 | end 33 | 34 | desc "Installs the SDK" 35 | task :install_sdk do 36 | from = "/Applications/Xcode-5.1.1.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.8.sdk" 37 | to = "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs" 38 | 39 | command = "sudo cp -R '#{from}' '#{to}'" 40 | puts command 41 | puts `#{command}` 42 | 43 | puts "Successfully added the 10.8 SDK" 44 | end 45 | 46 | desc "Clean everything, including the runtime" 47 | task "clean-all" => :clean do 48 | sh "rm -rf runtime" 49 | end 50 | 51 | RUBY_VERSIONS.each do |ruby_version| 52 | package = "traveling-ruby-#{VERSION}-#{ruby_version}-osx.tar.gz" 53 | gem_dir = "traveling-ruby-gems-#{VERSION}-#{ruby_version}-osx" 54 | 55 | task :package => "package:#{ruby_version}" 56 | task :build => "build:#{ruby_version}" 57 | task :test => "test:#{ruby_version}" 58 | task :upload => "upload:#{ruby_version}" 59 | task :clean => "clean:#{ruby_version}" 60 | 61 | desc "Create packages for Ruby #{ruby_version}" 62 | task "package:#{ruby_version}" => [package, "#{gem_dir}/ok"] 63 | 64 | desc "Build binaries for Ruby #{ruby_version}" 65 | task "build:#{ruby_version}" => "output/#{ruby_version}/bin" 66 | 67 | 68 | file(package => "output/#{ruby_version}/bin") do 69 | sh "./package -r #{package} output/#{ruby_version}" 70 | end 71 | 72 | file("#{gem_dir}/ok" => "output/#{ruby_version}/bin") do 73 | sh "./package -E #{gem_dir} output/#{ruby_version}" 74 | touch "#{gem_dir}/ok" 75 | end 76 | 77 | # We use 'file' instead of 'directory' here so that packages are updated 78 | # whenever we update binaries. 79 | file("output/#{ruby_version}/bin" => "runtime/ok") do 80 | sh "mkdir -p output/#{ruby_version}" 81 | sh "mkdir -p /tmp/ruby-#{ruby_version}" 82 | sh "./build-ruby -r #{ruby_version} -w /tmp/ruby-#{ruby_version} runtime output/#{ruby_version}" 83 | end 84 | 85 | 86 | desc "Test Ruby #{ruby_version}" 87 | task "test:#{ruby_version}" => "output/#{ruby_version}/bin" do 88 | sh "./test-gems output/#{ruby_version}" 89 | end 90 | 91 | desc "Upload Ruby #{ruby_version} packages to the server" 92 | task "upload:#{ruby_version}" => [package, "#{gem_dir}/ok"] do 93 | sh "s3cmd -P sync --no-preserve #{package} s3://traveling-ruby/releases/" 94 | sh "s3cmd -P --delete-removed --no-preserve -r sync #{gem_dir}/ s3://traveling-ruby/releases/#{gem_dir}/" 95 | sh "s3cmd del s3://traveling-ruby/releases/#{gem_dir}/ok" 96 | end 97 | 98 | desc "Clean Ruby #{ruby_version} packages, but not the runtime" 99 | task "clean:#{ruby_version}" do 100 | sh "rm -rf #{package} #{gem_dir} output/#{ruby_version}" 101 | end 102 | end 103 | 104 | file "runtime/ok" => "setup-runtime" do 105 | sh "./setup-runtime runtime" 106 | sh "touch runtime/ok" 107 | end 108 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## Version 20150715 2 | 3 | Upgrading to this version is strongly recommended because of the OpenSSL upgrade, which fixes some security vulnerabilities! 4 | 5 | * Upgraded to OpenSSL 1.0.2d. 6 | 7 | ## Version 20150517 8 | 9 | * The Ruby versions that we now support are: 2.1.6 and 2.2.2. 10 | * Added additional native extensions: puma, unicorn, kgio, raindrops, fast-stemmer, hitimes, redcarpet, snappy, curses. 11 | * Upgraded bundler gem to version 1.9.9. 12 | * Upgraded mysql2 gem to version 0.3.11. 13 | * Upgraded pg gem to version 0.18.2. 14 | * Upgraded bundler gem to version 1.9.9. 15 | * The sqlite3, pg, rugged and charlock_holmes gems are now smaller because they are now compiled with better compiler optimizations. The total reduction of all these gems together is 4 MB uncompressed. 16 | 17 | ## Version 20150210 18 | 19 | * Added support for Ruby 2.2.0. Version 2.1.5 and 2.2.0 are supported in parallel for the time being. 20 | * Added support for creating Windows packages. But there are currently a number of caveats: 21 | - Traveling Ruby supports creating packages *for* Windows, but it does not yet support creating packages *on* Windows. That is, the Traveling Ruby tutorials and the documentation do not work when you are a Ruby developer on Windows. To create Windows packages, you must use OS X or Linux. 22 | 23 | This is because in our documentation we make heavy use of standard Unix tools. Tools which are not available on Windows. In the future we may replace the use of such tools with Ruby tools so that the documentation works on Windows too. 24 | - Only Ruby 2.1.5 is supported for Windows, not 2.2.0. This is because [the RubyInstaller project](http://rubyinstaller.org/) hasn't released Ruby 2.2.0 binaries yet. 25 | * Fixed a problem with the 'rugged' native extension on Linux. Closes GH-33. 26 | * Fixed a problem with the 'charlock_holmes' native extension on Linux. Closes GH-34. 27 | * Header files are no longer packaged. This saves 256 KB. 28 | * RDoc and various unnecessary Bundler files have been removed. This saves about 1.2 MB. 29 | * Upgraded Bundler to 1.7.12. 30 | 31 | ## Version 20150204 32 | 33 | * Added additonal native extension versions: 34 | - bcrypt 3.1.10 35 | - eventmachine 1.0.6 36 | - json 1.8.2 37 | - mini_portile 0.6.2 38 | - nokogiri 1.6.6.2 39 | - sqlite3 1.3.10 40 | - nokogumbo 1.2.0 41 | - rugged 0.22.0b5 42 | 43 | ## Version 20150130 44 | 45 | Upgrading to this version is strongly recommended because of the OpenSSL upgrade, which fixes some security vulnerabilities! 46 | 47 | * Added the following native extension gems: RedCloth, escape_utils, posix-spawn, nokogumbo, github-markdown, rugged, charlock_holmes, unf_ext. 48 | * Upgraded to OpenSSL 1.0.1l. 49 | * The Linux version and the OS X version now use the same CA root certificates. Closes GH-24. 50 | 51 | ## Version 20141224 52 | 53 | * Fixed the mysql2 native extension not working on Linux. This is done by dynamically linking against libstdc++. Closes GH-21. 54 | * Added the eventmachine and thin native extension gems. 55 | 56 | ## Version 20141219 57 | 58 | * Removed header files. This makes the package 100 KB smaller. 59 | * Removed the sdbm extension because almost nobody uses it. The sqlite3 gem is almost always a better choice anyway. 60 | * Added the yajl-ruby native extension gem. 61 | 62 | ## Version 20141215 63 | 64 | * The Linux packages now include libffi.so.6, which was forgotten in the previous release. Closes GH-16. 65 | 66 | ## Version 20141213 67 | 68 | * Further removed unnecessary files. The Ruby binary packages were about 10 MB before. They are now about 6 MB. 69 | * Supports native extensions. 70 | 71 | ## Version 20141209 72 | 73 | * Fixed inclusion of Bundler. 74 | 75 | ## Version 20141206 76 | 77 | * Initial release. 78 | -------------------------------------------------------------------------------- /linux/Rakefile: -------------------------------------------------------------------------------- 1 | VERSION = File.read("../VERSION.txt").strip 2 | RUBY_VERSIONS = File.read("../RUBY_VERSIONS.txt").strip.split(/\s+/) 3 | ARCHITECTURES = ["x86", "x86_64"] 4 | CONCURRENCY = `./internal/cpucount`.to_i 5 | 6 | task :default => :package 7 | 8 | desc "Create packages for all Ruby versions (#{RUBY_VERSIONS.join(' ')}) and all architectures (#{ARCHITECTURES.join(' ')}" 9 | task :package do 10 | # Do nothing 11 | end 12 | 13 | desc "Build the runtime" 14 | task :runtime do 15 | # Do nothing 16 | end 17 | 18 | desc "Build binaries for all Ruby versions (#{RUBY_VERSIONS.join(' ')}) and all architectures (#{ARCHITECTURES.join(' ')}" 19 | task :build do 20 | # Do nothing 21 | end 22 | 23 | desc "Test all Ruby versions (#{RUBY_VERSIONS.join(' ')})" 24 | task :test do 25 | # Do nothing 26 | end 27 | 28 | desc "Upload all packages to the server" 29 | task :upload do 30 | # Do nothing 31 | end 32 | 33 | desc "Clean all packages, but not the runtime" 34 | task :clean do 35 | sh "rm -rf output" 36 | end 37 | 38 | desc "Clean everything, including the runtime" 39 | task "clean-all" => :clean do 40 | sh "sudo rm -rf runtime" 41 | end 42 | 43 | ARCHITECTURES.each do |arch| 44 | RUBY_VERSIONS.each do |ruby_version| 45 | package = "traveling-ruby-#{VERSION}-#{ruby_version}-linux-#{arch}.tar.gz" 46 | gem_dir = "traveling-ruby-gems-#{VERSION}-#{ruby_version}-linux-#{arch}" 47 | 48 | task :package => "package:#{ruby_version}:#{arch}" 49 | task :runtime => "runtime:#{ruby_version}:#{arch}" 50 | task :build => "build:#{ruby_version}:#{arch}" 51 | task :test => "test:#{ruby_version}:#{arch}" 52 | task :upload => "upload:#{ruby_version}:#{arch}" 53 | task :clean => "clean:#{ruby_version}:#{arch}" 54 | 55 | desc "Create packages for Ruby #{ruby_version} #{arch}" 56 | task "package:#{ruby_version}:#{arch}" => [package, "#{gem_dir}/ok"] 57 | 58 | desc "Build the runtime for Ruby #{ruby_version} #{arch}" 59 | task "runtime:#{ruby_version}:#{arch}" => "runtime/#{ruby_version}-#{arch}/ok" 60 | 61 | desc "Build binaries for Ruby #{ruby_version} #{arch}" 62 | task "build:#{ruby_version}:#{arch}" => "output/#{ruby_version}-#{arch}/bin" 63 | 64 | 65 | file "runtime/#{ruby_version}-#{arch}/ok" => ["setup-runtime", "internal/setup-runtime", "internal/setup-runtime-inside-mock"] do 66 | sh "mkdir -p runtime/#{ruby_version}-#{arch}" 67 | sh "./setup-runtime -a #{arch} -r #{ruby_version} runtime/#{ruby_version}-#{arch}" 68 | sh "touch runtime/#{ruby_version}-#{arch}/ok" 69 | end 70 | 71 | 72 | file(package => "output/#{ruby_version}-#{arch}/bin") do 73 | sh "./package -r #{package} output/#{ruby_version}-#{arch}" 74 | end 75 | 76 | file("#{gem_dir}/ok" => "output/#{ruby_version}-#{arch}/bin") do 77 | sh "./package -E #{gem_dir} output/#{ruby_version}-#{arch}" 78 | touch "#{gem_dir}/ok" 79 | end 80 | 81 | # We use 'file' instead of 'directory' here so that packages are updated 82 | # whenever we update binaries. 83 | file("output/#{ruby_version}-#{arch}/bin" => "runtime/#{ruby_version}-#{arch}/ok") do 84 | sh "mkdir -p output/#{ruby_version}-#{arch}" 85 | sh "./build-ruby -j #{CONCURRENCY} -a #{arch} -r #{ruby_version} runtime/#{ruby_version}-#{arch} output/#{ruby_version}-#{arch}" 86 | end 87 | 88 | 89 | desc "Test Ruby #{ruby_version} #{arch}" 90 | task "test:#{ruby_version}:#{arch}" => "output/#{ruby_version}-#{arch}/bin" do 91 | sh "./test-gems output/#{ruby_version}-#{arch}" 92 | end 93 | 94 | desc "Upload Ruby #{ruby_version} #{arch} packages to the server" 95 | task "upload:#{ruby_version}:#{arch}" => [package, "#{gem_dir}/ok"] do 96 | sh "./upload #{package} #{gem_dir}" 97 | end 98 | 99 | desc "Clean Ruby #{ruby_version} packages, but not the runtime" 100 | task "clean:#{ruby_version}:#{arch}" do 101 | sh "rm -rf #{package} #{gem_dir} output/#{arch}" 102 | end 103 | end 104 | end 105 | -------------------------------------------------------------------------------- /linux/README.md: -------------------------------------------------------------------------------- 1 | # Traveling Ruby Linux build system 2 | 3 | The `linux/` directory contains the build system for building Traveling Ruby binaries for Linux. 4 | 5 | ## Building binaries 6 | 7 | The build system requires Docker and Rake. To build binaries, run: 8 | 9 | cd linux 10 | rake 11 | 12 | This will produce a `traveling-ruby-XXXXX.tar.gz` file which contains the Ruby binaries, and a `traveling-ruby-gems-XXXXX` directory which contains the native extensions. 13 | 14 | ## How it works 15 | 16 | ### Overview 17 | 18 | The build system works by setting up [Mock](http://fedoraproject.org/wiki/Projects/Mock) chroots inside a CentOS 6 Docker container, and using those Mock chroots to perform the actual compilation of the binaries. 19 | 20 | Mock is a Fedora tool for building packages against architectures and different Fedora/Red Hat/CentOS versions. This tool downloads RPMs for the requested OS version, extracts them into a directory (thereby setting up a system root) and runs command inside a chroot jail for that directory. We use Mock to create a CentOS 6 x86 chroot and a CentOS 6 x86-64 chroot inside the Docker container, for the purpose of compiling x86 and x86-64 binaries. 21 | 22 | The Docker image that we use is [phusion/traveling-ruby-builder](https://registry.hub.docker.com/u/phusion/traveling-ruby-builder/). See the Dockerfile in this directory for its definition. This Docker image is generated by running the `setup-docker-image` script. 23 | 24 | But there is a problem with Docker: we cannot create Mock chroots within a Dockerfile. This is because Mock needs `chroot()` privileges, but `docker build` runs containers without that privilege. So we work around this fact by creating Mock chroots outside the `docker build` phase. This is why the Docker image doesn't contain much. It's pretty much a bare CentOS 6 system that only has Mock installed. 25 | 26 | ### The runtime setup phase 27 | 28 | ![](https://raw.githubusercontent.com/phusion/traveling-ruby/master/doc/linux_build_system_setup_runtime.png)
29 | _The `setup-runtime` script creates Mock chroots inside the Docker container._ 30 | 31 | The Rakefile begins by invoking the `setup-runtime` script. This script: 32 | 33 | * Pulls the Docker image. 34 | * Runs Mock inside the container to create the Mock chroots. 35 | * Installs necessary tools (such as compilers) and libraries inside the Mock chroots. 36 | 37 | So this script does not actually produce Traveling Ruby binaries yet. 38 | 39 | Setting up the runtime is only done once, although you may have to re-setup the runtime if you upgrade Traveling Ruby. 40 | 41 | The runtime is saved to the `runtime` directory. 42 | 43 | ### The build phase 44 | 45 | ![](https://raw.githubusercontent.com/phusion/traveling-ruby/master/doc/linux_build_system_build.png)
46 | _The `build-ruby` script uses the Mock chroots inside the Docker container to invoke the compiler, which produces out binaries._ 47 | 48 | Once the runtime is setup, the Rakefile proceeds with invoking the `build-ruby` script. This script runs the compiler inside the Mock chroot environments to produce binaries. The script: 49 | 50 | * Builds Ruby. It extracts the Ruby source tarball and runs `./configure`, `make` and `make install`. 51 | * Builds the native extensions that Traveling Ruby supports. It runs `bundle install` on the Gemfile located in the `shared/` directory in the Traveling Ruby repository. 52 | * Performs various postprocessing tasks, such as stripping debugging symbols from the binaries and running various sanity checks. 53 | 54 | The build outputs are saved to the `output` directory. 55 | 56 | ### The package phase 57 | 58 | Once binaries are compiled, the Rakefile invokes the `package` script. This script packages files inside the `output` directory into various tarballs. 59 | 60 | ## Why CentOS 6? 61 | 62 | CentOS 6 was chosen as the compilation environment because its glibc version is sufficiently old that any produced binaries are compatible with nearly all Linux distributions still in use today. The binaries we generate should be compatible with any Linux distribution that was released circa 2010 and beyond. This includes: 63 | 64 | * Debian 6 65 | * Ubuntu 10.04 66 | -------------------------------------------------------------------------------- /linux/build-ruby: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | SELFDIR=`dirname "$0"` 5 | SELFDIR=`cd "$SELFDIR" && pwd` 6 | source "$SELFDIR/../shared/library.sh" 7 | 8 | CPUCOUNT=`"$SELFDIR/internal/cpucount"` 9 | RUBY_VERSIONS=(`cat "$SELFDIR/../RUBY_VERSIONS.txt"`) 10 | 11 | RUNTIME_DIR= 12 | OUTPUT_DIR= 13 | ARCHITECTURE= 14 | RUBY_VERSION=${RUBY_VERSIONS[0]} 15 | CONCURRENCY=$CPUCOUNT 16 | GEMFILE="$SELFDIR/../shared/gemfiles" 17 | DEBUG_SHELL=none 18 | SETUP_SOURCE=true 19 | COMPILE=true 20 | SANITY_CHECK_OUTPUT=true 21 | GEMFILE_MOUNT=() 22 | 23 | function usage() 24 | { 25 | echo "Usage: ./build-ruby [options] " 26 | echo "Build Traveling Ruby binaries." 27 | echo 28 | echo "Options:" 29 | echo " -a NAME Architecture to setup (x86, x86_64)" 30 | echo " -r VERSION Ruby version to build. Default: $RUBY_VERSION" 31 | echo 32 | echo " -E Do not setup source" 33 | echo " -C Do not compile Ruby" 34 | echo " -G Do not install gems" 35 | echo 36 | echo " -j NUMBER Set build concurrency. Default: $CPUCOUNT" 37 | echo " -g PATH Build gems as specified by the given Gemfile" 38 | echo " -d Open a debugging shell before installing gems" 39 | echo " -D Open a debugging shell after installing gems" 40 | echo " -h Show this help" 41 | } 42 | 43 | function parse_options() 44 | { 45 | local OPTIND=1 46 | local opt 47 | while getopts "a:r:ECGj:g:dDh" opt; do 48 | case "$opt" in 49 | a) 50 | ARCHITECTURE=$OPTARG 51 | ;; 52 | r) 53 | RUBY_VERSION=$OPTARG 54 | ;; 55 | E) 56 | SETUP_SOURCE=false 57 | ;; 58 | C) 59 | COMPILE=false 60 | ;; 61 | G) 62 | GEMFILE= 63 | ;; 64 | j) 65 | CONCURRENCY=$OPTARG 66 | ;; 67 | g) 68 | GEMFILE="$OPTARG" 69 | ;; 70 | d) 71 | DEBUG_SHELL=before 72 | ;; 73 | D) 74 | DEBUG_SHELL=after 75 | ;; 76 | h) 77 | usage 78 | exit 79 | ;; 80 | *) 81 | return 1 82 | ;; 83 | esac 84 | done 85 | 86 | (( OPTIND -= 1 )) || true 87 | shift $OPTIND || true 88 | RUNTIME_DIR="$1" 89 | OUTPUT_DIR="$2" 90 | 91 | if [[ "$RUNTIME_DIR" = "" || "$OUTPUT_DIR" = "" ]]; then 92 | usage 93 | exit 1 94 | fi 95 | if [[ ! -e "$RUNTIME_DIR" ]]; then 96 | echo "ERROR: $RUNTIME_DIR doesn't exist." 97 | exit 1 98 | fi 99 | if [[ ! -e "$OUTPUT_DIR" ]]; then 100 | echo "ERROR: $OUTPUT_DIR doesn't exist." 101 | exit 1 102 | fi 103 | if [[ "$ARCHITECTURE" = "" ]]; then 104 | echo "ERROR: please specify an architecture with -a." 105 | exit 1 106 | fi 107 | } 108 | 109 | 110 | parse_options "$@" 111 | RUNTIME_DIR=`cd "$RUNTIME_DIR" && pwd` 112 | OUTPUT_DIR=`cd "$OUTPUT_DIR" && pwd` 113 | if [[ ! -e "$RUNTIME_DIR/mock" ]]; then 114 | echo "ERROR: runtime directory $RUNTIME_DIR not set up. Please run ./setup-runtime first." 115 | exit 1 116 | fi 117 | if [[ "$GEMFILE" != "" ]]; then 118 | GEMFILE="`absolute_path \"$GEMFILE\"`" 119 | if [[ -d "$GEMFILE" ]]; then 120 | for F in "$GEMFILE"/*/Gemfile; do 121 | DIR="`dirname \"$F\"`" 122 | DIR="`basename \"$DIR\"`" 123 | GEMFILE_MOUNT+=(-v "$F:/gemfiles/$DIR/Gemfile:ro") 124 | if [[ -e "$F.lock" ]]; then 125 | GEMFILE_MOUNT+=(-v "$F.lock:/gemfiles/$DIR/Gemfile.lock:ro") 126 | fi 127 | done 128 | else 129 | GEMFILE_MOUNT=(-v "$GEMFILE:/gemfiles/default/Gemfile:ro") 130 | if [[ -e "$GEMFILE.lock" ]]; then 131 | GEMFILE_MOUNT+=(-v "$GEMFILE.lock:/gemfiles/default/Gemfile.lock:ro") 132 | fi 133 | fi 134 | fi 135 | if [[ "$DEBUG_SHELL" = none ]]; then 136 | TTY_ARGS= 137 | else 138 | TTY_ARGS="-t -i" 139 | fi 140 | 141 | exec docker run \ 142 | $TTY_ARGS \ 143 | --rm \ 144 | --cap-add SYS_ADMIN --cap-add SYS_CHROOT \ 145 | -v "$SELFDIR/internal:/system:ro" \ 146 | -v "$SELFDIR/../shared:/system_shared:ro" \ 147 | -v "$RUNTIME_DIR/mock:/var/lib/mock" \ 148 | -v "$OUTPUT_DIR:/output" \ 149 | "${GEMFILE_MOUNT[@]}" \ 150 | -e "APP_UID=`id -u`" \ 151 | -e "APP_GID=`id -g`" \ 152 | -e "BUNDLER_VERSION=`cat \"$SELFDIR/../BUNDLER_VERSION.txt\"`" \ 153 | -e "ARCHITECTURE=$ARCHITECTURE" \ 154 | -e "RUBY_VERSION=$RUBY_VERSION" \ 155 | -e "CONCURRENCY=$CONCURRENCY" \ 156 | -e "SETUP_SOURCE=$SETUP_SOURCE" \ 157 | -e "COMPILE=$COMPILE" \ 158 | -e "SANITY_CHECK_OUTPUT=$SANITY_CHECK_OUTPUT" \ 159 | -e "DEBUG_SHELL=$DEBUG_SHELL" \ 160 | phusion/traveling-ruby-builder \ 161 | /system/my_init --quiet --skip-runit --skip-startup-files -- \ 162 | /system/build-ruby 163 | -------------------------------------------------------------------------------- /shared/package: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | SELFDIR=`dirname "$0"` 5 | SELFDIR=`cd "$SELFDIR" && pwd` 6 | source "$SELFDIR/library.sh" 7 | BUNDLER_VERSION=`cat "$SELFDIR/../BUNDLER_VERSION.txt"` 8 | 9 | BUILD_OUTPUT_DIR= 10 | RUBY_PACKAGE= 11 | GEM_NATIVE_EXTENSIONS_DIR= 12 | 13 | function load_ruby_info() 14 | { 15 | local BUILD_OUTPUT_DIR="$1" 16 | RUBY_COMPAT_VERSION=`cat "$BUILD_OUTPUT_DIR/info/RUBY_COMPAT_VERSION"` 17 | GEM_PLATFORM=`cat "$BUILD_OUTPUT_DIR/info/GEM_PLATFORM"` 18 | GEM_EXTENSION_API_VERSION=`cat "$BUILD_OUTPUT_DIR/info/GEM_EXTENSION_API_VERSION"` 19 | } 20 | 21 | function find_gems_containing_native_extensions() 22 | { 23 | local BUILD_OUTPUT_DIR="$1" 24 | ( 25 | shopt -s nullglob 26 | GEMS=("$BUILD_OUTPUT_DIR"/lib/ruby/gems/$RUBY_COMPAT_VERSION/extensions/$GEM_PLATFORM/$GEM_EXTENSION_API_VERSION/*) 27 | GEM_NAMES=() 28 | for GEM in "${GEMS[@]}"; do 29 | GEM_NAME="`basename \"$GEM\"`" 30 | GEM_NAMES+=("$GEM_NAME") 31 | done 32 | echo "${GEM_NAMES[@]}" 33 | ) 34 | [[ $? = 0 ]] 35 | } 36 | 37 | function usage() 38 | { 39 | echo "Usage: ./package [options] " 40 | echo "Package built Traveling Ruby binaries." 41 | echo 42 | echo "Options:" 43 | echo " -r PATH Package Ruby into given file" 44 | echo " -E DIR Package gem native extensions into the given directory" 45 | echo " -h Show this help" 46 | } 47 | 48 | function parse_options() 49 | { 50 | local OPTIND=1 51 | local opt 52 | while getopts "r:E:h" opt; do 53 | case "$opt" in 54 | r) 55 | RUBY_PACKAGE="$OPTARG" 56 | ;; 57 | E) 58 | GEM_NATIVE_EXTENSIONS_DIR="$OPTARG" 59 | ;; 60 | h) 61 | usage 62 | exit 63 | ;; 64 | *) 65 | return 1 66 | ;; 67 | esac 68 | done 69 | 70 | (( OPTIND -= 1 )) || true 71 | shift $OPTIND || true 72 | BUILD_OUTPUT_DIR="$1" 73 | 74 | if [[ "$BUILD_OUTPUT_DIR" = "" ]]; then 75 | usage 76 | exit 1 77 | fi 78 | if [[ "$RUBY_PACKAGE" = "" && "$GEM_NATIVE_EXTENSIONS_DIR" = "" ]]; then 79 | echo "ERROR: you must specify either a Ruby package path (-r) or a gem native extensions directory (-E)." 80 | exit 1 81 | fi 82 | if [[ ! -e "$BUILD_OUTPUT_DIR" ]]; then 83 | echo "ERROR: $BUILD_OUTPUT_DIR doesn't exist." 84 | exit 1 85 | fi 86 | } 87 | 88 | 89 | parse_options "$@" 90 | 91 | 92 | ########## 93 | 94 | 95 | export GZIP=--best 96 | load_ruby_info "$BUILD_OUTPUT_DIR" 97 | NATIVE_GEMS=(`find_gems_containing_native_extensions "$BUILD_OUTPUT_DIR"`) 98 | 99 | if [[ "$RUBY_PACKAGE" != "" ]]; then 100 | header "Packaging Ruby..." 101 | run tar -cf "$RUBY_PACKAGE.tmp" -C "$BUILD_OUTPUT_DIR" \ 102 | --exclude "include/*" \ 103 | --exclude "lib/ruby/gems/$RUBY_COMPAT_VERSION/extensions/*" \ 104 | --exclude "lib/ruby/gems/$RUBY_COMPAT_VERSION/gems/*" \ 105 | --exclude "lib/ruby/gems/$RUBY_COMPAT_VERSION/specifications/*" \ 106 | --exclude "lib/ruby/gems/$RUBY_COMPAT_VERSION/deplibs/*" \ 107 | . 108 | run tar -rf "$RUBY_PACKAGE.tmp" -C "$BUILD_OUTPUT_DIR" \ 109 | "./lib/ruby/gems/$RUBY_COMPAT_VERSION/gems/bundler-$BUNDLER_VERSION" \ 110 | "./lib/ruby/gems/$RUBY_COMPAT_VERSION/specifications/bundler-$BUNDLER_VERSION.gemspec" \ 111 | "./lib/ruby/gems/$RUBY_COMPAT_VERSION/specifications/default" 112 | echo "+ gzip --best --no-name -c $RUBY_PACKAGE.tmp > $RUBY_PACKAGE" 113 | gzip --best --no-name -c "$RUBY_PACKAGE.tmp" > "$RUBY_PACKAGE" 114 | run rm "$RUBY_PACKAGE.tmp" 115 | fi 116 | 117 | if [[ "$GEM_NATIVE_EXTENSIONS_DIR" != "" ]]; then 118 | echo 119 | header "Packaging gem native extensions..." 120 | if [[ ${#NATIVE_GEMS[@]} -eq 0 ]]; then 121 | echo "There are no gems with native extensions." 122 | else 123 | run mkdir -p "$GEM_NATIVE_EXTENSIONS_DIR" 124 | for GEM_NAME in "${NATIVE_GEMS[@]}"; do 125 | GEM_NAME_WITHOUT_VERSION=`echo "$GEM_NAME" | sed -E 's/(.*)-.*/\1/'` 126 | run tar -cf "$GEM_NATIVE_EXTENSIONS_DIR/$GEM_NAME.tar" \ 127 | -C "$BUILD_OUTPUT_DIR/lib/ruby/gems" \ 128 | "$RUBY_COMPAT_VERSION/extensions/$GEM_PLATFORM/$GEM_EXTENSION_API_VERSION/$GEM_NAME" 129 | if [[ -e "$BUILD_OUTPUT_DIR/lib/ruby/gems/$RUBY_COMPAT_VERSION/deplibs/$GEM_PLATFORM/$GEM_NAME_WITHOUT_VERSION" ]]; then 130 | run tar -rf "$GEM_NATIVE_EXTENSIONS_DIR/$GEM_NAME.tar" \ 131 | -C "$BUILD_OUTPUT_DIR/lib/ruby/gems" \ 132 | "$RUBY_COMPAT_VERSION/deplibs/$GEM_PLATFORM/$GEM_NAME_WITHOUT_VERSION" 133 | fi 134 | run rm -f "$GEM_NATIVE_EXTENSIONS_DIR/$GEM_NAME.tar.gz" 135 | run gzip --best --no-name "$GEM_NATIVE_EXTENSIONS_DIR/$GEM_NAME.tar" 136 | done 137 | fi 138 | fi 139 | -------------------------------------------------------------------------------- /TUTORIAL-4.md: -------------------------------------------------------------------------------- 1 | # Tutorial 4: creating packages for Windows 2 | 3 | In the previous tutorials we covered [the basics](TUTORIAL-1.md), [gem dependencies](TUTORIAL-2.md) and [native extensions](TUTORIAL-3.md). But we never covered Windows support. That's because the flow for Windows support is a bit different from other platforms, so it deserves its own tutorial. 4 | 5 | But there are several [**important Windows-specific caveats**](README.md#caveats). You should read them before proceeding with this tutorial!! 6 | 7 | You can find the end result of this tutorial at https://github.com/phusion/traveling-ruby-windows-demo. 8 | 9 | ## Creating a batch file 10 | 11 | Suppose that we want to create a Windows package for our hello world app from [tutorial 2](TUTORIAL-2.md). The first thing we need to create is a Windows wrapper script. We already have a Unix wrapper script in `packaging/wrapper.sh`, which works on Linux and OS X, but Windows doesn't support Unix shell scripts. For Windows we'll need to create a wrapper script in the DOS batch format. 12 | 13 | Create `packaging/wrapper.bat`: 14 | 15 | ```Batch 16 | @echo off 17 | 18 | :: Tell Bundler where the Gemfile and gems are. 19 | set "BUNDLE_GEMFILE=%~dp0\lib\vendor\Gemfile" 20 | set BUNDLE_IGNORE_CONFIG= 21 | 22 | :: Run the actual app using the bundled Ruby interpreter, with Bundler activated. 23 | @"%~dp0\lib\ruby\bin\ruby.bat" -rbundler/setup "%~dp0\lib\app\hello.rb" 24 | ``` 25 | 26 | ## Modifying the Rakefile 27 | 28 | The next step is to add a Rake task for creating the Windows package. The Rakefile currently generates tar.gz packages for Linux and OS X, but tar.gz is not a common format on Windows. For Windows, we'll want to create a .zip package instead. 29 | 30 | Add a `package:win32` task to your Rakefile: 31 | 32 | ```Ruby 33 | namespace :package do 34 | ... 35 | 36 | desc "Package your app for Windows x86" 37 | task :win32 => [:bundle_install, "packaging/traveling-ruby-#{TRAVELING_RUBY_VERSION}-win32.tar.gz"] do 38 | create_package("win32", :windows) 39 | end 40 | ``` 41 | 42 | Add a task for downloading the Traveling Ruby Windows binaries: 43 | 44 | ```Ruby 45 | file "packaging/traveling-ruby-#{TRAVELING_RUBY_VERSION}-win32.tar.gz" do 46 | download_runtime("win32") 47 | end 48 | ``` 49 | 50 | We must update the `create_package` method so that it generates the package slightly differently depending on the target platform. Update its signature and add a `os_type = :unix` parameter: 51 | 52 | ```Ruby 53 | def create_package(target, os_type = :unix) 54 | ``` 55 | 56 | This method contains a line which copies `wrapper.sh`, but we'll want to copy `wrapper.bat` when creating Windows packages. 57 | 58 | ```Ruby 59 | # Look for: 60 | sh "cp packaging/wrapper.sh #{package_dir}/hello" 61 | 62 | # Replace it with: 63 | if os_type == :unix 64 | sh "cp packaging/wrapper.sh #{package_dir}/hello" 65 | else 66 | sh "cp packaging/wrapper.bat #{package_dir}/hello.bat" 67 | end 68 | ``` 69 | 70 | There is a line which creates the final tar.gz file. We'll want to modify this so that a .zip file is created when targeting Windows. 71 | 72 | ```Ruby 73 | # Look for: 74 | sh "tar -czf #{package_dir}.tar.gz #{package_dir}" 75 | 76 | # Replace it with: 77 | if os_type == :unix 78 | sh "tar -czf #{package_dir}.tar.gz #{package_dir}" 79 | else 80 | sh "zip -9r #{package_dir}.zip #{package_dir}" 81 | end 82 | ``` 83 | 84 | Finally, add the `package:win32` task to the `package` task's dependencies so that a `rake package` generates a Windows package too: 85 | 86 | ```Ruby 87 | task :package => ['package:linux:x86', 'package:linux:x86_64', 'package:osx', 'package:win32'] 88 | ``` 89 | 90 | ## Creating and testing the package 91 | 92 | Congratulations. The `rake package` command will now generate packages for Windows, Linux and OS X. But let's test the Windows package. Run the following command to generate a Windows package: 93 | 94 | ```Bash 95 | rake package:win32 96 | ``` 97 | 98 | This will generate `hello-1.0.0-win32.zip`. Copy this file to a Windows machine and extract it to `C:\`. Then open a `cmd.exe` command prompt and test it: 99 | 100 | ``` 101 | C:\Users\Test> cd C:\hello-1.0.0-win32 102 | C:\hello-1.0.0-win32> hello 103 | hello Mrs. Mellie Ebert 104 | ``` 105 | 106 | ## Conclusion 107 | 108 | Congratulations, you've learned how to create packages for Windows! You've now reached the end of this tutorial series and you now master the basics of Traveling Ruby. You can find the end result of this tutorial at https://github.com/phusion/traveling-ruby-windows-demo. 109 | 110 | Next up, you may want to read [the guides](README.md#getting-started), which cover intermediate to advanced topics. 111 | -------------------------------------------------------------------------------- /REDUCING_PACKAGE_SIZE.md: -------------------------------------------------------------------------------- 1 | # Reducing the size of your Traveling Ruby packages 2 | 3 | Packages generated by Traveling Ruby can be large, but you can reduce the size of your packages by many megabytes by removing unnecessary files. 4 | 5 | ## Typically removable files 6 | 7 | You can typically safely remove the following files: 8 | 9 | 10 | # Remove tests 11 | rm -rf lib/vendor/ruby/*/gems/*/test 12 | rm -rf lib/vendor/ruby/*/gems/*/tests 13 | rm -rf lib/vendor/ruby/*/gems/*/spec 14 | rm -rf lib/vendor/ruby/*/gems/*/features 15 | rm -rf lib/vendor/ruby/*/gems/*/benchmark 16 | 17 | # Remove documentation 18 | rm -f lib/vendor/ruby/*/gems/*/README* 19 | rm -f lib/vendor/ruby/*/gems/*/CHANGE* 20 | rm -f lib/vendor/ruby/*/gems/*/Change* 21 | rm -f lib/vendor/ruby/*/gems/*/COPYING* 22 | rm -f lib/vendor/ruby/*/gems/*/LICENSE* 23 | rm -f lib/vendor/ruby/*/gems/*/MIT-LICENSE* 24 | rm -f lib/vendor/ruby/*/gems/*/TODO 25 | rm -f lib/vendor/ruby/*/gems/*/*.txt 26 | rm -f lib/vendor/ruby/*/gems/*/*.md 27 | rm -f lib/vendor/ruby/*/gems/*/*.rdoc 28 | rm -rf lib/vendor/ruby/*/gems/*/doc 29 | rm -rf lib/vendor/ruby/*/gems/*/docs 30 | rm -rf lib/vendor/ruby/*/gems/*/example 31 | rm -rf lib/vendor/ruby/*/gems/*/examples 32 | rm -rf lib/vendor/ruby/*/gems/*/sample 33 | rm -rf lib/vendor/ruby/*/gems/*/doc-api 34 | find lib/vendor/ruby -name '*.md' | xargs rm -f 35 | 36 | # Remove misc unnecessary files 37 | rm -rf lib/vendor/ruby/*/gems/*/.gitignore 38 | rm -rf lib/vendor/ruby/*/gems/*/.travis.yml 39 | 40 | # Remove leftover native extension sources and compilation objects 41 | rm -f lib/vendor/ruby/*/gems/*/ext/Makefile 42 | rm -f lib/vendor/ruby/*/gems/*/ext/*/Makefile 43 | rm -f lib/vendor/ruby/*/gems/*/ext/*/tmp 44 | find lib/vendor/ruby -name '*.c' | xargs rm -f 45 | find lib/vendor/ruby -name '*.cpp' | xargs rm -f 46 | find lib/vendor/ruby -name '*.h' | xargs rm -f 47 | find lib/vendor/ruby -name '*.rl' | xargs rm -f 48 | find lib/vendor/ruby -name 'extconf.rb' | xargs rm -f 49 | find lib/vendor/ruby/*/gems -name '*.o' | xargs rm -f 50 | find lib/vendor/ruby/*/gems -name '*.so' | xargs rm -f 51 | find lib/vendor/ruby/*/gems -name '*.bundle' | xargs rm -f 52 | 53 | # Remove Java files. They're only used for JRuby support 54 | find lib/vendor/ruby -name '*.java' | xargs rm -f 55 | find lib/vendor/ruby -name '*.class' | xargs rm -f 56 | 57 | ## Removing gem-specific files 58 | 59 | Depending on which gems you use, there may be more files that you can remove. 60 | 61 | What I typically do is to run `find lib/vendor/ruby/*/gems | less` to inspect which files exist, and try to identify the files that I think can be removed. 62 | 63 | To focus on non-Ruby files, I run `find lib/vendor/ruby/*/gems -type f | grep -v '.rb$' | less` which filters out all .rb files. 64 | 65 | Here are a few examples: 66 | 67 | * Many gems that contain a Rakefile only need those Rakefiles for the purpose of developing those gems. In many cases, you can safely remove those Rakefiles without impacting your application. In a similar fashion, the `task` directory within those gems (which typically contain further Rake tasks) can also be removed. 68 | * The `nokogori` gem contains the `suppressions` directory. This directory contains Valgrind suppression files, so if you never use Valgrind (which is very likely) then you can remove that directory too. 69 | * The `nokogori` gem also contains the `ports` directory. This directory is only used during compilation of the native extension, so it can be removed. 70 | * The `rack` gem contains a `contrib` directory which appears to be only relevant for documentation purposes, so it too can be removed. 71 | * The `rugged` gem contains a `vendor` directory which contains the libgit2 source code, but this directory is only used during compilation of the native extension, so it can be safely removed. 72 | 73 | When in doubt, you should inspect the gem's source code to check how a file is used and whether you can remove it. 74 | 75 | ## Removing seldomly used encodings 76 | 77 | Ruby support many encodings that are seldomly used. Most applications only use ASCII and UTF-8. But Ruby also supports UTF-16, UTF-32, various Chinese, Japanese and Korean encodings, etc. Usually you can get rid of everything besides ASCII and UTF-8: 78 | 79 | rm -f lib/ruby/lib/ruby/*/*/enc/cp949* 80 | rm -f lib/ruby/lib/ruby/*/*/enc/euc_* 81 | rm -f lib/ruby/lib/ruby/*/*/enc/shift_jis* 82 | rm -f lib/ruby/lib/ruby/*/*/enc/koi8_* 83 | rm -f lib/ruby/lib/ruby/*/*/enc/emacs* 84 | rm -f lib/ruby/lib/ruby/*/*/enc/gb* 85 | rm -f lib/ruby/lib/ruby/*/*/enc/big5* 86 | rm -f lib/ruby/lib/ruby/*/*/enc/windows* 87 | rm -f lib/ruby/lib/ruby/*/*/enc/utf_16* 88 | rm -f lib/ruby/lib/ruby/*/*/enc/utf_32* 89 | 90 | Very few applications need support for transcoding strings from one encoding to another, besides ASCII and UTF-8. You can get rid of transcoding support as follows: 91 | 92 | rm -rf lib/ruby/lib/ruby/*/*/enc/trans 93 | 94 | ## Removing RDoc 95 | 96 | Most likely your application does not need RDoc during runtime. You can remove that as follows: 97 | 98 | rm -rf lib/ruby/lib/ruby/*/rdoc* 99 | -------------------------------------------------------------------------------- /windows/build-ruby: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | SELFDIR=`dirname "$0"` 5 | SELFDIR=`cd "$SELFDIR" && pwd` 6 | source "$SELFDIR/../shared/library.sh" 7 | 8 | BUNDLER_VERSION=`cat "$SELFDIR/../BUNDLER_VERSION.txt"` 9 | RUBY_VERSIONS=(`cat "$SELFDIR/../RUBY_VERSIONS.txt"`) 10 | N_RUBY_VERSIONS=${#RUBY_VERSIONS[@]} 11 | LAST_RUBY_VERSION_INDEX=$((N_RUBY_VERSIONS - 1)) 12 | 13 | CACHE_DIR= 14 | OUTPUT_DIR= 15 | ARCHITECTURE=x86 16 | RUBY_VERSION=${RUBY_VERSIONS[$LAST_RUBY_VERSION_INDEX]} 17 | 18 | function usage() 19 | { 20 | echo "Usage: ./setup-ruby [options] " 21 | echo "Build Ruby binaries." 22 | echo 23 | echo "Options:" 24 | echo " -a NAME Architecture to setup (x86, x86_64)" 25 | echo " -r VERSION Ruby version to build. Default: $RUBY_VERSION" 26 | echo " -h Show this help" 27 | } 28 | 29 | function parse_options() 30 | { 31 | local OPTIND=1 32 | local opt 33 | while getopts "a:r:h" opt; do 34 | case "$opt" in 35 | a) 36 | ARCHITECTURE=$OPTARG 37 | ;; 38 | r) 39 | RUBY_VERSION=$OPTARG 40 | ;; 41 | h) 42 | usage 43 | exit 44 | ;; 45 | *) 46 | return 1 47 | ;; 48 | esac 49 | done 50 | 51 | (( OPTIND -= 1 )) || true 52 | shift $OPTIND || true 53 | CACHE_DIR="$1" 54 | OUTPUT_DIR="$2" 55 | 56 | if [[ "$CACHE_DIR" = "" || "$OUTPUT_DIR" = "" ]]; then 57 | usage 58 | exit 1 59 | fi 60 | if [[ ! -e "$CACHE_DIR" ]]; then 61 | echo "ERROR: $CACHE_DIR doesn't exist." 62 | exit 1 63 | fi 64 | if [[ ! -e "$OUTPUT_DIR" ]]; then 65 | echo "ERROR: $OUTPUT_DIR doesn't exist." 66 | exit 1 67 | fi 68 | } 69 | 70 | function create_wrapper() 71 | { 72 | local FILE="$1" 73 | local NAME="$2" 74 | local IS_RUBY_SCRIPT="$3" 75 | 76 | cat > "$FILE" <> "$FILE" <> "$FILE" </dev/null 148 | ) 149 | if [[ $? != 0 ]]; then 150 | exit 1 151 | fi 152 | run mv "$OUTPUT_DIR/ruby-$RUBY_VERSION-$RUBY_FILE_ARCH-mingw32"/* "$OUTPUT_DIR/" 153 | run rm -rf "$OUTPUT_DIR/ruby-$RUBY_VERSION-$RUBY_FILE_ARCH-mingw32" 154 | echo 155 | 156 | 157 | header "Analyzing Ruby..." 158 | if [[ "$OS" =~ Windows ]]; then 159 | export PATH="$OUTPUT_DIR/bin:$PATH" 160 | fi 161 | RUBY_COMPAT_VERSION=`grep '"ruby_version"' "$OUTPUT_DIR"/lib/ruby/*/*/rbconfig.rb | sed -E 's/.*=//; s/.*"(.*)".*/\1/'` 162 | RUBY_ARCH=`grep '"arch"' "$OUTPUT_DIR"/lib/ruby/*/*/rbconfig.rb | sed -E 's/.*=//; s/.*"(.*)".*/\1/'` 163 | GEM_PLATFORM=`ruby -rubygems -e "puts Gem::Platform.new('$RUBY_ARCH').to_s"` 164 | GEM_EXTENSION_API_VERSION=$RUBY_COMPAT_VERSION 165 | run mkdir "$OUTPUT_DIR/info" 166 | echo "+ Dumping information about the Ruby binaries into /tmp/ruby/info" 167 | echo $RUBY_COMPAT_VERSION > "$OUTPUT_DIR/info/RUBY_COMPAT_VERSION" 168 | echo $RUBY_ARCH > "$OUTPUT_DIR/info/RUBY_ARCH" 169 | echo $GEM_PLATFORM > "$OUTPUT_DIR/info/GEM_PLATFORM" 170 | echo $GEM_EXTENSION_API_VERSION > "$OUTPUT_DIR/info/GEM_EXTENSION_API_VERSION" 171 | echo 172 | 173 | 174 | header "Installing Bundler..." 175 | if [[ -e "$CACHE_DIR/bundler-$BUNDLER_VERSION.gem" ]]; then 176 | run gem install "$CACHE_DIR/bundler-$BUNDLER_VERSION.gem" --no-rdoc --no-ri \ 177 | --install-dir "$OUTPUT_DIR/lib/ruby/gems/$RUBY_COMPAT_VERSION" 178 | else 179 | run gem install bundler -v $BUNDLER_VERSION --no-rdoc --no-ri \ 180 | --install-dir "$OUTPUT_DIR/lib/ruby/gems/$RUBY_COMPAT_VERSION" 181 | fi 182 | run cp "$OUTPUT_DIR/lib/ruby/gems/$RUBY_COMPAT_VERSION/cache"/*.gem "$CACHE_DIR/" || true 183 | echo 184 | 185 | 186 | header "Postprocessing..." 187 | echo "+ Entering $OUTPUT_DIR" 188 | pushd "$OUTPUT_DIR" 189 | 190 | run cp "$SELFDIR/../shared/ca-bundle.crt" lib/ 191 | 192 | run rm bin/{erb,rdoc,ri}* 193 | run rm -f bin/testrb* # Only Ruby 2.1 has it 194 | run rm bin/libgdbm* bin/tcl* bin/tk* 195 | run rm -rf include 196 | run rm -rf share 197 | run rm -f lib/*.a 198 | run rm -rf lib/pkgconfig 199 | run rm -rf lib/tcltk 200 | run rm -rf lib/ruby/$RUBY_COMPAT_VERSION/{tcltk,tk,sdbm,gdbm,dbm,dl,coverage} 201 | run rm -rf lib/ruby/$RUBY_COMPAT_VERSION/{tk,sdbm,gdbm,dbm,dl,coverage}.rb 202 | run rm -rf lib/ruby/$RUBY_COMPAT_VERSION/tk* 203 | run rm -rf lib/ruby/$RUBY_COMPAT_VERSION/rdoc/generator/ 204 | run rm -f lib/ruby/gems/$RUBY_COMPAT_VERSION/cache/* 205 | run rm -f /tmp/ruby/lib/ruby/gems/$RUBY_COMPAT_VERSION/extensions/$GEM_PLATFORM/$GEM_EXTENSION_API_VERSION/*/{gem_make.out,mkmf.log} 206 | run rm -rf lib/ruby/gems/$RUBY_COMPAT_VERSION/gems/*/{test,spec,*.md,*.rdoc} 207 | run rm -rf lib/ruby/gems/$RUBY_COMPAT_VERSION/gems/*/ext/*/*.{c,h} 208 | 209 | echo "+ Entering Bundler gem directory" 210 | pushd lib/ruby/gems/$RUBY_COMPAT_VERSION/gems/bundler-$BUNDLER_VERSION >/dev/null 211 | rm -rf .gitignore .rspec .travis.yml man Rakefile lib/bundler/man/*.txt lib/bundler/templates 212 | popd >/dev/null 213 | echo "+ Leaving Bundler gem directory" 214 | 215 | # Create wrapper scripts. 216 | run mv bin bin.real 217 | run mkdir bin 218 | run create_wrapper bin/ruby.bat ruby false 219 | run create_wrapper bin/gem.bat gem true 220 | run create_wrapper bin/irb.bat irb true 221 | run create_wrapper bin/rake.bat rake true 222 | run create_wrapper bin/bundle.bat bundle true 223 | run create_wrapper bin/bundler.bat bundler true 224 | 225 | echo "+ Leaving $OUTPUT_DIR" 226 | popd >/dev/null 227 | echo 228 | 229 | header "All done!" 230 | -------------------------------------------------------------------------------- /TUTORIAL-1.md: -------------------------------------------------------------------------------- 1 | # Tutorial 1: hello world 2 | 3 | This tutorial teaches you, in 5 minutes, how to use Traveling Ruby to create self-contained packages of a hello world app. This app has no gem dependencies; dependency management is covered in [tutorial 2](TUTORIAL-2.md). We will be creating three packages, namely for Linux x86, Linux x86_64 and OS X. 4 | 5 | This tutorial **does not cover Windows**. This tutorial [will not work on Windows](README.md#caveats); nor will this tutorial generate packages for Windows. The creation of packages for Windows is covered in [tutorial 4](TUTORIAL-4.md). 6 | 7 | The process is as follows. First, you create several package directories (one for each target platform) and copy your app into the directory. Then you extract Traveling Ruby binaries into each directory, appropriate for that platform. Then you write a wrapper script so that users can conveniently start your app. Finally, you package everything up in tar.gz files, and automate the process. 8 | 9 | You can find the end result of this tutorial at https://github.com/phusion/traveling-ruby-hello-demo. 10 | 11 | The final hello world package weights 6 MB compressed. 12 | 13 | ## Preparation 14 | 15 | Let's begin by creating a hello world app: 16 | 17 | ```Bash 18 | mkdir hello_app 19 | cd hello_app 20 | echo '#!/usr/bin/env ruby' > hello.rb 21 | echo 'puts "hello world"' >> hello.rb 22 | ruby hello.rb 23 | # => hello world 24 | ``` 25 | 26 | ## Creating package directories 27 | 28 | The next step is to prepare packages for all the target platforms, by creating a directory each platform, and by copying your app into each directory. 29 | 30 | ```Bash 31 | mkdir -p hello-1.0.0-linux-x86/lib/app 32 | cp hello.rb hello-1.0.0-linux-x86/lib/app/ 33 | 34 | mkdir -p hello-1.0.0-linux-x86_64/lib/app 35 | cp hello.rb hello-1.0.0-linux-x86_64/lib/app/ 36 | 37 | mkdir -p hello-1.0.0-osx/lib/app/ 38 | cp hello.rb hello-1.0.0-osx/lib/app/ 39 | ``` 40 | 41 | Next, create a `packaging` directory and download Traveling Ruby binaries for each platform into that directory. Then extract these binaries into each packaging directory. You can find a list of binaries at [the Traveling Ruby Amazon S3 bucket](https://traveling-ruby.s3-us-west-2.amazonaws.com/list.html). For faster download times, use the CloudFront domain "https://d6r77u77i8pq3.cloudfront.net". In this tutorial we're extracting version 20141215-2.1.5. 42 | 43 | ```Bash 44 | mkdir packaging 45 | cd packaging 46 | curl -L -O --fail https://d6r77u77i8pq3.cloudfront.net/releases/traveling-ruby-20141215-2.1.5-linux-x86.tar.gz 47 | curl -L -O --fail https://d6r77u77i8pq3.cloudfront.net/releases/traveling-ruby-20141215-2.1.5-linux-x86_64.tar.gz 48 | curl -L -O --fail https://d6r77u77i8pq3.cloudfront.net/releases/traveling-ruby-20141215-2.1.5-osx.tar.gz 49 | cd .. 50 | 51 | mkdir hello-1.0.0-linux-x86/lib/ruby && tar -xzf packaging/traveling-ruby-20141215-2.1.5-linux-x86.tar.gz -C hello-1.0.0-linux-x86/lib/ruby 52 | mkdir hello-1.0.0-linux-x86_64/lib/ruby && tar -xzf packaging/traveling-ruby-20141215-2.1.5-linux-x86_64.tar.gz -C hello-1.0.0-linux-x86_64/lib/ruby 53 | mkdir hello-1.0.0-osx/lib/ruby && tar -xzf packaging/traveling-ruby-20141215-2.1.5-osx.tar.gz -C hello-1.0.0-osx/lib/ruby 54 | ``` 55 | 56 | Now, each package directory will have Ruby binaries included. It looks like this: 57 | Your directory structure will now look like this: 58 | 59 | hello_app/ 60 | | 61 | +-- hello.rb 62 | | 63 | +-- hello-1.0.0-linux-x86/ 64 | | | 65 | | +-- lib/ 66 | | +-- app/ 67 | | | | 68 | | | +-- hello.rb 69 | | | 70 | | +-- ruby/ 71 | | | 72 | | +-- bin/ 73 | | | | 74 | | | +-- ruby 75 | | | +-- ... 76 | | +-- ... 77 | | 78 | +-- hello-1.0.0-linux-x86_64/ 79 | | | 80 | | ... 81 | | 82 | +-- hello-1.0.0-osx/ 83 | | 84 | ... 85 | 86 | ### Quick sanity testing 87 | 88 | Let's do a basic sanity test by running your app with a bundled Ruby interpreter. Suppose that you are developing on OS X. Run this: 89 | 90 | ```Bash 91 | cd hello-1.0.0-osx 92 | ./lib/ruby/bin/ruby lib/app/hello.rb 93 | # => hello world 94 | cd .. 95 | ``` 96 | 97 | ## Creating a wrapper script 98 | 99 | Now that you've verified that the bundled Ruby interpreter works, you'll want create a *wrapper script*. After all, you don't want your users to run `/path-to-your-app/lib/ruby/bin/ruby /path-to-your-app/lib/app/hello.rb`. You want them to run `/path-to-your-app/hello`. 100 | 101 | Here's what a wrapper script could look like: 102 | 103 | ```Bash 104 | #!/bin/bash 105 | set -e 106 | 107 | # Figure out where this script is located. 108 | SELFDIR="`dirname \"$0\"`" 109 | SELFDIR="`cd \"$SELFDIR\" && pwd`" 110 | 111 | # Run the actual app using the bundled Ruby interpreter. 112 | exec "$SELFDIR/lib/ruby/bin/ruby" "$SELFDIR/lib/app/hello.rb" 113 | ``` 114 | 115 | Save this file as `packaging/wrapper.sh` in your project's root directory. Then you can copy it to each of your package directories and name it `hello`: 116 | 117 | ```Bash 118 | editor packaging/wrapper.sh 119 | ...edit the file as per above... 120 | chmod +x packaging/wrapper.sh 121 | cp packaging/wrapper.sh hello-1.0.0-linux-x86/hello 122 | cp packaging/wrapper.sh hello-1.0.0-linux-x86_64/hello 123 | cp packaging/wrapper.sh hello-1.0.0-osx/hello 124 | ``` 125 | 126 | ## Finalizing packages 127 | 128 | Your package directories are now ready. You can finalize the packages by packaging up all these directories using tar: 129 | 130 | ```Bash 131 | tar -czf hello-1.0.0-linux-x86.tar.gz hello-1.0.0-linux-x86 132 | tar -czf hello-1.0.0-linux-x86_64.tar.gz hello-1.0.0-linux-x86_64 133 | tar -czf hello-1.0.0-osx.tar.gz hello-1.0.0-osx 134 | rm -rf hello-1.0.0-linux-x86 135 | rm -rf hello-1.0.0-linux-x86_64 136 | rm -rf hello-1.0.0-osx 137 | ``` 138 | 139 | Congratulations, you have created packages using Traveling Ruby! 140 | 141 | An x86 Linux user could now use your app like this: 142 | 143 | 1. The user downloads `hello-1.0.0-linux-x86.tar.gz`. 144 | 2. The user extracts this file. 145 | 3. The user runs your app: 146 | 147 | ```Bash 148 | /path-to/hello-1.0.0-linux-x86/hello 149 | # => hello world 150 | ``` 151 | 152 | ## Automating the process using Rake 153 | 154 | Going through all of the above steps on every release is a hassle, so you should automate the packaging process, for example by using Rake. Here's how the Rakefile could look like: 155 | 156 | ```Ruby 157 | PACKAGE_NAME = "hello" 158 | VERSION = "1.0.0" 159 | TRAVELING_RUBY_VERSION = "20150210-2.1.5" 160 | 161 | desc "Package your app" 162 | task :package => ['package:linux:x86', 'package:linux:x86_64', 'package:osx'] 163 | 164 | namespace :package do 165 | namespace :linux do 166 | desc "Package your app for Linux x86" 167 | task :x86 => "packaging/traveling-ruby-#{TRAVELING_RUBY_VERSION}-linux-x86.tar.gz" do 168 | create_package("linux-x86") 169 | end 170 | 171 | desc "Package your app for Linux x86_64" 172 | task :x86_64 => "packaging/traveling-ruby-#{TRAVELING_RUBY_VERSION}-linux-x86_64.tar.gz" do 173 | create_package("linux-x86_64") 174 | end 175 | end 176 | 177 | desc "Package your app for OS X" 178 | task :osx => "packaging/traveling-ruby-#{TRAVELING_RUBY_VERSION}-osx.tar.gz" do 179 | create_package("osx") 180 | end 181 | end 182 | 183 | file "packaging/traveling-ruby-#{TRAVELING_RUBY_VERSION}-linux-x86.tar.gz" do 184 | download_runtime("linux-x86") 185 | end 186 | 187 | file "packaging/traveling-ruby-#{TRAVELING_RUBY_VERSION}-linux-x86_64.tar.gz" do 188 | download_runtime("linux-x86_64") 189 | end 190 | 191 | file "packaging/traveling-ruby-#{TRAVELING_RUBY_VERSION}-osx.tar.gz" do 192 | download_runtime("osx") 193 | end 194 | 195 | def create_package(target) 196 | package_dir = "#{PACKAGE_NAME}-#{VERSION}-#{target}" 197 | sh "rm -rf #{package_dir}" 198 | sh "mkdir -p #{package_dir}/lib/app" 199 | sh "cp hello.rb #{package_dir}/lib/app/" 200 | sh "mkdir #{package_dir}/lib/ruby" 201 | sh "tar -xzf packaging/traveling-ruby-#{TRAVELING_RUBY_VERSION}-#{target}.tar.gz -C #{package_dir}/lib/ruby" 202 | sh "cp packaging/wrapper.sh #{package_dir}/hello" 203 | if !ENV['DIR_ONLY'] 204 | sh "tar -czf #{package_dir}.tar.gz #{package_dir}" 205 | sh "rm -rf #{package_dir}" 206 | end 207 | end 208 | 209 | def download_runtime(target) 210 | sh "cd packaging && curl -L -O --fail " + 211 | "https://d6r77u77i8pq3.cloudfront.net/releases/traveling-ruby-#{TRAVELING_RUBY_VERSION}-#{target}.tar.gz" 212 | end 213 | ``` 214 | 215 | You can then create all 3 packages by running: 216 | 217 | ```Bash 218 | rake package 219 | ``` 220 | 221 | You can also create a package for a specific platform by running one of: 222 | 223 | ```Bash 224 | rake package:linux:x86 225 | rake package:linux:x86_64 226 | rake package:osx 227 | ``` 228 | 229 | You can also just create package directories, without creating the .tar.gz files, by passing DIR_ONLY=1: 230 | 231 | ```Bash 232 | rake package DIR_ONLY=1 233 | rake package:linux:x86 DIR_ONLY=1 234 | rake package:linux:x86_64 DIR_ONLY=1 235 | rake package:osx DIR_ONLY=1 236 | ``` 237 | 238 | ## End users 239 | 240 | You now have three files which you can distribute to end users. 241 | 242 | * hello-1.0.0-linux-x86.tar.gz 243 | * hello-1.0.0-linux-x86_64.tar.gz 244 | * hello-1.0.0-osx.tar.gz 245 | 246 | Suppose the end user is on Linux x86_64. S/he uses your app by downloading `hello-1.0.0-linux-x86_64.tar.gz`, extracting it and running it: 247 | 248 | ```Bash 249 | wget hello-1.0.0-linux-x86_64.tar.gz 250 | ... 251 | tar xzf hello-1.0.0-linux-x86_64.tar.gz 252 | cd hello-1.0.0-linux-x86_64 253 | ./hello 254 | # => hello world 255 | ``` 256 | 257 | ## Conclusion 258 | 259 | You can download the end result of this tutorial at https://github.com/phusion/traveling-ruby-hello-demo. 260 | 261 | Creating self-contained packages with Traveling Ruby is simple and straightforward. But most apps will have gem dependencies. [Read tutorial 2](TUTORIAL-2.md) to learn how to handle gem dependencies. 262 | -------------------------------------------------------------------------------- /TUTORIAL-2.md: -------------------------------------------------------------------------------- 1 | # Tutorial 2: gem dependencies 2 | 3 | In [tutorial 1](TUTORIAL-1.md), we've packaged a hello world app and automated the process using Rake. In this tutorial, we'll continue to build on tutorial 1's sample app, by adding gem dependencies. 4 | 5 | Your app can depend on any gem you wish, subject to the [limitations described in README.md](README.md#limitations). You must include the gems in your packages, and your wrapper script must pass the right parameters to the Ruby interpreter in order for your gems to be found. In this tutorial, we'll teach you how to manage gems using Bundler. 6 | 7 | Gems with native extensions are not covered in this second tutorial. They're covered in [tutorial 3](TUTORIAL-3.md). 8 | 9 | You can find the end result of this tutorial at https://github.com/phusion/traveling-ruby-gems-demo. 10 | 11 | ## Preparation 12 | 13 | Suppose that we want our hello world app from tutorial 1 to greet a random person instead of the world. We'll want to use [the Faker gem](https://github.com/stympy/faker) for that. Let's start by creating a Gemfile... 14 | 15 | ```Ruby 16 | source 'https://rubygems.org' 17 | 18 | gem 'faker' 19 | 20 | group :development do 21 | gem 'rake' 22 | end 23 | ``` 24 | 25 | ...and by modifying hello.rb as follows: 26 | 27 | ```Ruby 28 | #!/usr/bin/env ruby 29 | require 'faker' 30 | puts "hello #{Faker::Name.name}" 31 | ``` 32 | 33 | Then install your gem bundle: 34 | 35 | ```Bash 36 | bundle install 37 | ``` 38 | 39 | Verify that your hello world works: 40 | 41 | ```Bash 42 | bundle exec ruby hello.rb 43 | # => hello Miss Susan Casper 44 | ``` 45 | 46 | Then, using the Rakefile from tutorial 1, create package directories without creating tar.gz files: 47 | 48 | ```Bash 49 | rake package DIR_ONLY=1 50 | ``` 51 | 52 | ## Installing gems for packaging 53 | 54 | In the previous step, we used Bundler to install gems so that you can run your app during development. But you *also* need to run Bundler a second time, to install the gems that you want to include in your package. During the packaging phase, the gems installed by this second Bundler invocation will be copied into the packages. 55 | 56 | But first, be aware that you must run this Bundler instance with the same Ruby version that you intend to package with, because Bundler installs into a directory that contains the Ruby version number. Traveling Ruby currently supports Ruby 2.1.5 and 2.2.0, but this tutorial utilizes Ruby 2.1.5. **So in this tutorial you must run Bundler with Ruby 2.1.** If you run Bundler using any other Ruby version, things will fail in a later step. 57 | 58 | So first verify your Ruby version: 59 | 60 | ```Bash 61 | ruby -v 62 | # => ruby 2.1.x [...] 63 | ``` 64 | 65 | Next, install the gem bundle for packaging. We do this by copying the Gemfile to a temporary directory and running Bundler there, because passing `--path` and `--without` to Bundler will change its configuration file. We don't want to persist such changes in our development Bundler config. 66 | 67 | ```Bash 68 | mkdir packaging/tmp 69 | cp Gemfile Gemfile.lock packaging/tmp/ 70 | cd packaging/tmp 71 | BUNDLE_IGNORE_CONFIG=1 bundle install --path ../vendor --without development 72 | cd ../.. 73 | rm -rf packaging/tmp 74 | ``` 75 | 76 | Note that we passed `--without development` so that Rake isn't installed. In the final packages there is no need to include Rake. 77 | 78 | Bundler also stores various cache files, which we also don't need to package, so we remove them: 79 | 80 | ```Bash 81 | rm -f packaging/vendor/*/*/cache/* 82 | ``` 83 | 84 | ## Copying gems into package directories 85 | 86 | Copy the Bundler gem bundle that you installed in the last step, into the package directories: 87 | 88 | ```Bash 89 | cp -pR packaging/vendor hello-1.0.0-linux-x86/lib/ 90 | cp -pR packaging/vendor hello-1.0.0-linux-x86_64/lib/ 91 | cp -pR packaging/vendor hello-1.0.0-osx/lib/ 92 | ``` 93 | 94 | Copy over your Gemfile and Gemfile.lock into each gem directory inside the packages: 95 | 96 | ```Bash 97 | cp Gemfile Gemfile.lock hello-1.0.0-linux-x86/lib/vendor/ 98 | cp Gemfile Gemfile.lock hello-1.0.0-linux-x86_64/lib/vendor/ 99 | cp Gemfile Gemfile.lock hello-1.0.0-osx/lib/vendor/ 100 | ``` 101 | 102 | ## Bundler config file 103 | 104 | We must create a Bundler config file for each of the gem directories inside the packages. This Bundler config file tells Bundler that gems are to be found in the same directory that the Gemfile resides in, and that gems in the "development" group should not be loaded. 105 | 106 | First, create `packaging/bundler-config` which contains: 107 | 108 | ```Bash 109 | BUNDLE_PATH: . 110 | BUNDLE_WITHOUT: development 111 | BUNDLE_DISABLE_SHARED_GEMS: '1' 112 | ``` 113 | 114 | Then copy the file into `.bundle` directories inside the gem directories inside the packages; 115 | 116 | ```Bash 117 | mkdir hello-1.0.0-linux-x86/lib/vendor/.bundle 118 | mkdir hello-1.0.0-linux-x86_64/lib/vendor/.bundle 119 | mkdir hello-1.0.0-osx/lib/vendor/.bundle 120 | 121 | cp packaging/bundler-config hello-1.0.0-linux-x86/lib/vendor/.bundle/config 122 | cp packaging/bundler-config hello-1.0.0-linux-x86_64/lib/vendor/.bundle/config 123 | cp packaging/bundler-config hello-1.0.0-osx/lib/vendor/.bundle/config 124 | ``` 125 | 126 | ## Wrapper script 127 | 128 | Modify the wrapper script `packaging/wrapper.sh`, which we originally created in [tutorial 1](TUTORIAL-1.md). It should be modified to perform two more things: 129 | 130 | 1. It tells Bundler where your Gemfile is (and thus where the gems are). 131 | 2. It executes your app with Bundler activated. 132 | 133 | Here's how it looks like: 134 | 135 | ```Bash 136 | #!/bin/bash 137 | set -e 138 | 139 | # Figure out where this script is located. 140 | SELFDIR="`dirname \"$0\"`" 141 | SELFDIR="`cd \"$SELFDIR\" && pwd`" 142 | 143 | # Tell Bundler where the Gemfile and gems are. 144 | export BUNDLE_GEMFILE="$SELFDIR/lib/vendor/Gemfile" 145 | unset BUNDLE_IGNORE_CONFIG 146 | 147 | # Run the actual app using the bundled Ruby interpreter, with Bundler activated. 148 | exec "$SELFDIR/lib/ruby/bin/ruby" -rbundler/setup "$SELFDIR/lib/app/hello.rb" 149 | ``` 150 | 151 | Copy over this wrapper script to each of your package directories and finalize the packages: 152 | 153 | ```Bash 154 | cp packaging/wrapper.sh hello-1.0.0-linux-x86/hello 155 | cp packaging/wrapper.sh hello-1.0.0-linux-x86_64/hello 156 | cp packaging/wrapper.sh hello-1.0.0-osx/hello 157 | 158 | tar -czf hello-1.0.0-linux-x86.tar.gz hello-1.0.0-linux-x86 159 | tar -czf hello-1.0.0-linux-x86_64.tar.gz hello-1.0.0-linux-x86_64 160 | tar -czf hello-1.0.0-osx.tar.gz hello-1.0.0-osx 161 | rm -rf hello-1.0.0-linux-x86 162 | rm -rf hello-1.0.0-linux-x86_64 163 | rm -rf hello-1.0.0-osx 164 | ``` 165 | 166 | ## Automating the process using Rake 167 | 168 | We update the Rakefile so that all of the above steps are automated by running `rake package`. The various `package` tasks have been updated to run `package:bundle_install` which installs the gem bundle, and the `create_package` function has been updated to package the Gemfile and Bundler config file. 169 | 170 | ```Ruby 171 | # For Bundler.with_clean_env 172 | require 'bundler/setup' 173 | 174 | PACKAGE_NAME = "hello" 175 | VERSION = "1.0.0" 176 | TRAVELING_RUBY_VERSION = "20150210-2.1.5" 177 | 178 | desc "Package your app" 179 | task :package => ['package:linux:x86', 'package:linux:x86_64', 'package:osx'] 180 | 181 | namespace :package do 182 | namespace :linux do 183 | desc "Package your app for Linux x86" 184 | task :x86 => [:bundle_install, "packaging/traveling-ruby-#{TRAVELING_RUBY_VERSION}-linux-x86.tar.gz"] do 185 | create_package("linux-x86") 186 | end 187 | 188 | desc "Package your app for Linux x86_64" 189 | task :x86_64 => [:bundle_install, "packaging/traveling-ruby-#{TRAVELING_RUBY_VERSION}-linux-x86_64.tar.gz"] do 190 | create_package("linux-x86_64") 191 | end 192 | end 193 | 194 | desc "Package your app for OS X" 195 | task :osx => [:bundle_install, "packaging/traveling-ruby-#{TRAVELING_RUBY_VERSION}-osx.tar.gz"] do 196 | create_package("osx") 197 | end 198 | 199 | desc "Install gems to local directory" 200 | task :bundle_install do 201 | if RUBY_VERSION !~ /^2\.1\./ 202 | abort "You can only 'bundle install' using Ruby 2.1, because that's what Traveling Ruby uses." 203 | end 204 | sh "rm -rf packaging/tmp" 205 | sh "mkdir packaging/tmp" 206 | sh "cp Gemfile Gemfile.lock packaging/tmp/" 207 | Bundler.with_clean_env do 208 | sh "cd packaging/tmp && env BUNDLE_IGNORE_CONFIG=1 bundle install --path ../vendor --without development" 209 | end 210 | sh "rm -rf packaging/tmp" 211 | sh "rm -f packaging/vendor/*/*/cache/*" 212 | end 213 | end 214 | 215 | file "packaging/traveling-ruby-#{TRAVELING_RUBY_VERSION}-linux-x86.tar.gz" do 216 | download_runtime("linux-x86") 217 | end 218 | 219 | file "packaging/traveling-ruby-#{TRAVELING_RUBY_VERSION}-linux-x86_64.tar.gz" do 220 | download_runtime("linux-x86_64") 221 | end 222 | 223 | file "packaging/traveling-ruby-#{TRAVELING_RUBY_VERSION}-osx.tar.gz" do 224 | download_runtime("osx") 225 | end 226 | 227 | def create_package(target) 228 | package_dir = "#{PACKAGE_NAME}-#{VERSION}-#{target}" 229 | sh "rm -rf #{package_dir}" 230 | sh "mkdir #{package_dir}" 231 | sh "mkdir -p #{package_dir}/lib/app" 232 | sh "cp hello.rb #{package_dir}/lib/app/" 233 | sh "mkdir #{package_dir}/lib/ruby" 234 | sh "tar -xzf packaging/traveling-ruby-#{TRAVELING_RUBY_VERSION}-#{target}.tar.gz -C #{package_dir}/lib/ruby" 235 | sh "cp packaging/wrapper.sh #{package_dir}/hello" 236 | sh "cp -pR packaging/vendor #{package_dir}/lib/" 237 | sh "cp Gemfile Gemfile.lock #{package_dir}/lib/vendor/" 238 | sh "mkdir #{package_dir}/lib/vendor/.bundle" 239 | sh "cp packaging/bundler-config #{package_dir}/lib/vendor/.bundle/config" 240 | if !ENV['DIR_ONLY'] 241 | sh "tar -czf #{package_dir}.tar.gz #{package_dir}" 242 | sh "rm -rf #{package_dir}" 243 | end 244 | end 245 | 246 | def download_runtime(target) 247 | sh "cd packaging && curl -L -O --fail " + 248 | "https://d6r77u77i8pq3.cloudfront.net/releases/traveling-ruby-#{TRAVELING_RUBY_VERSION}-#{target}.tar.gz" 249 | end 250 | ``` 251 | 252 | ## Conclusion 253 | 254 | In this tutorial you've learned how to work with gem dependencies. You can download the end result of this tutorial at https://github.com/phusion/traveling-ruby-gems-demo. 255 | 256 | But this tutorial does not cover native extensions. To learn how to deal with native extensions, go to [tutorial 3](TUTORIAL-3.md). 257 | -------------------------------------------------------------------------------- /linux/internal/setup-runtime-inside-mock: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | source /system/library.sh 4 | ARCHITECTURE=`cat /system/ARCHITECTURE` 5 | 6 | function download_and_extract() 7 | { 8 | local BASENAME="$1" 9 | local URL="$2" 10 | local regex='\.bz2$' 11 | 12 | run rm -f "$BASENAME" 13 | run wget -O "$BASENAME" "$URL" 14 | if [[ "$URL" =~ $regex ]]; then 15 | run tar xjf "$BASENAME" 16 | else 17 | run tar xzf "$BASENAME" 18 | fi 19 | run rm "$BASENAME" 20 | } 21 | 22 | 23 | CONCURRENCY=2 24 | export PATH=/usr/local/override/bin:$PATH 25 | if [[ "$ARCHITECTURE" = x86_64 ]]; then 26 | ARCHITECTURE_BITS=64 27 | export PATH=/system/bin64:$PATH 28 | else 29 | ARCHITECTURE_BITS=32 30 | export PATH=/system/bin32:$PATH 31 | fi 32 | 33 | AUTOCONF_VERSION=2.69 34 | AUTOMAKE_VERSION=1.15 35 | LIBTOOL_VERSION=2.4.6 36 | CMAKE_VERSION=3.0.2 37 | OPENSSL_VERSION=1.0.2f 38 | FFI_VERSION=3.2.1 39 | SQLITE3_VERSION=3080702 40 | MYSQL_LIB_VERSION=6.1.5 41 | POSTGRESQL_VERSION=9.3.5 42 | ICU_VERSION=54.1 43 | ICU_DIR_VERSION=54_1 44 | LIBSSH2_VERSION=1.4.3 45 | 46 | 47 | echo "----- chroot entered -----" 48 | # Clear /tmp. 49 | for F in *; do 50 | # During the first 'mock' run, mock somehow mounts /tmp/ccache which we 51 | # can't remove. So we skip this. 52 | if [[ "$F" != ccache ]]; then 53 | run rm -rf "/tmp/$F" 54 | fi 55 | done 56 | run rpm --rebuilddb 57 | run yum install -y @development-tools gcc gcc-c++ wget sudo zlib-devel \ 58 | readline-devel ncurses-devel ccache 59 | run mkdir -p /ccache 60 | echo 61 | 62 | 63 | 64 | header "Installing autoconf..." 65 | if [[ ! -e /usr/local/override/bin/autoconf ]]; then 66 | download_and_extract autoconf-$AUTOCONF_VERSION.tar.gz \ 67 | http://ftp.gnu.org/gnu/autoconf/autoconf-$AUTOCONF_VERSION.tar.gz 68 | echo "Entering autoconf-$AUTOCONF_VERSION" 69 | pushd autoconf-$AUTOCONF_VERSION >/dev/null 70 | 71 | run ./configure --prefix=/usr/local/override \ 72 | --disable-shared --enable-static \ 73 | CFLAGS='-O2 -fPIC -fvisibility=hidden' 74 | run make -j$CONCURRENCY 75 | run make install-strip 76 | 77 | echo "Leaving source directory" 78 | popd >/dev/null 79 | run rm -rf autoconf-$AUTOCONF_VERSION 80 | fi 81 | run mkdir -p /usr/local/override/share/aclocal 82 | run cp /usr/share/aclocal/pkg.m4 /usr/local/override/share/aclocal/ 83 | echo 84 | 85 | header "Installing automake..." 86 | if [[ ! -e /usr/local/override/bin/automake ]]; then 87 | download_and_extract automake-$AUTOMAKE_VERSION.tar.gz \ 88 | http://ftp.gnu.org/gnu/automake/automake-$AUTOMAKE_VERSION.tar.gz 89 | echo "Entering automake-$AUTOMAKE_VERSION" 90 | pushd automake-$AUTOMAKE_VERSION >/dev/null 91 | 92 | run ./configure --prefix=/usr/local/override \ 93 | --disable-shared --enable-static \ 94 | CFLAGS='-O2 -fPIC -fvisibility=hidden' 95 | run make -j$CONCURRENCY 96 | run make install-strip 97 | 98 | echo "Leaving source directory" 99 | popd >/dev/null 100 | run rm -rf automake-$AUTOMAKE_VERSION 101 | fi 102 | echo 103 | 104 | header "Installing libtool..." 105 | if [[ ! -e /usr/local/override/bin/libtoolize ]]; then 106 | download_and_extract libtool-$LIBTOOL_VERSION.tar.gz \ 107 | http://ftp.gnu.org/gnu/libtool/libtool-$LIBTOOL_VERSION.tar.gz 108 | echo "Entering libtool-$LIBTOOL_VERSION" 109 | pushd libtool-$LIBTOOL_VERSION >/dev/null 110 | 111 | run ./configure --prefix=/usr/local/override \ 112 | --disable-shared --enable-static \ 113 | CFLAGS='-O2 -fPIC -fvisibility=hidden' 114 | run make -j$CONCURRENCY 115 | run make install-strip 116 | 117 | echo "Leaving source directory" 118 | popd >/dev/null 119 | run rm -rf libtool-$LIBTOOL_VERSION 120 | fi 121 | echo 122 | 123 | header "Installing CMake..." 124 | if [[ ! -e /usr/local/override/bin/cmake ]]; then 125 | download_and_extract cmake-$CMAKE_VERSION.tar.gz \ 126 | http://www.cmake.org/files/v3.0/cmake-$CMAKE_VERSION.tar.gz 127 | echo "Entering cmake-$CMAKE_VERSION" 128 | pushd cmake-$CMAKE_VERSION >/dev/null 129 | 130 | run ./configure --prefix=/usr/local/override --no-qt-gui --parallel=$CONCURRENCY 131 | run make -j$CONCURRENCY 132 | run make install 133 | 134 | echo "Leaving source directory" 135 | popd >/dev/null 136 | run rm -rf cmake-$CMAKE_VERSION 137 | fi 138 | echo 139 | 140 | header "Installing OpenSSL" 141 | if [[ ! -e /usr/local/override/bin/openssl ]]; then 142 | download_and_extract openssl-$OPENSSL_VERSION.tar.gz \ 143 | https://www.openssl.org/source/openssl-$OPENSSL_VERSION.tar.gz 144 | echo "Entering openssl-$OPENSSL_VERSION" 145 | pushd openssl-$OPENSSL_VERSION >/dev/null 146 | 147 | run ./config --prefix=/usr/local/override --openssldir=/usr/local/override/openssl \ 148 | threads zlib no-shared no-sse2 -fPIC -fvisibility=hidden 149 | run make 150 | run make install_sw 151 | run strip --strip-all /usr/local/override/bin/openssl 152 | run strip --strip-debug /usr/local/override/lib/libcrypto.a 153 | run strip --strip-debug /usr/local/override/lib/libssl.a 154 | run sed -i 's/^Libs:.*/Libs: -L${libdir} -lssl -lcrypto -ldl/' /usr/local/override/lib/pkgconfig/openssl.pc 155 | run sed -i 's/^Libs.private:.*/Libs.private: -L${libdir} -lssl -lcrypto -ldl -lz/' /usr/local/override/lib/pkgconfig/openssl.pc 156 | run sed -i 's/^Libs:.*/Libs: -L${libdir} -lssl -lcrypto -ldl/' /usr/local/override/lib/pkgconfig/libssl.pc 157 | run sed -i 's/^Libs.private:.*/Libs.private: -L${libdir} -lssl -lcrypto -ldl -lz/' /usr/local/override/lib/pkgconfig/libssl.pc 158 | 159 | echo "Leaving source directory" 160 | popd >/dev/null 161 | run rm -rf openssl-$OPENSSL_VERSION 162 | fi 163 | echo 164 | 165 | header "Installing libffi" 166 | LIBFFI_FILE=`echo /usr/local/override/lib*/libffi.so.6` 167 | if [[ ! -e "$LIBFFI_FILE" ]]; then 168 | download_and_extract libffi-$FFI_VERSION.tar.gz \ 169 | ftp://sourceware.org/pub/libffi/libffi-$FFI_VERSION.tar.gz 170 | echo "Entering libffi-$FFI_VERSION" 171 | pushd libffi-$FFI_VERSION >/dev/null 172 | 173 | run ./configure --prefix=/usr/local/override --enable-shared --disable-static \ 174 | --enable-portable-binary 175 | run make -j$CONCURRENCY 176 | run make install-strip 177 | if [[ "$ARCHITECTURE" = x86_64 ]]; then 178 | run strip --strip-debug /usr/local/override/lib64/libffi.so.6 179 | else 180 | run strip --strip-debug /usr/local/override/lib/libffi.so.6 181 | fi 182 | 183 | echo "Leaving source directory" 184 | popd >/dev/null 185 | run rm -rf libffi-$FFI_VERSION 186 | fi 187 | echo 188 | 189 | header "Installing SQLite3" 190 | if [[ ! -e /usr/local/override/lib/libsqlite3.a ]]; then 191 | download_and_extract sqlite-autoconf-$SQLITE3_VERSION.tar.gz \ 192 | http://www.sqlite.org/2014/sqlite-autoconf-$SQLITE3_VERSION.tar.gz 193 | echo "Entering sqlite-autoconf-$SQLITE3_VERSION" 194 | pushd sqlite-autoconf-$SQLITE3_VERSION >/dev/null 195 | 196 | run ./configure --prefix=/usr/local/override --disable-shared \ 197 | --disable-dynamic-extensions CFLAGS='-O2 -fPIC -fvisibility=hidden' 198 | run make -j$CONCURRENCY 199 | run make install-strip 200 | 201 | echo "Leaving source directory" 202 | popd >/dev/null 203 | run rm -rf sqlite-autoconf-$SQLITE3_VERSION 204 | fi 205 | echo 206 | 207 | header "Installing MySQL" 208 | if [[ ! -e /usr/local/override/lib/libmysqlclient.a ]]; then 209 | download_and_extract mysql-connector-c-$MYSQL_LIB_VERSION-src.tar.gz \ 210 | http://dev.mysql.com/get/Downloads/Connector-C/mysql-connector-c-$MYSQL_LIB_VERSION-src.tar.gz 211 | echo "Entering mysql-connector-c-$MYSQL_LIB_VERSION-src" 212 | pushd mysql-connector-c-$MYSQL_LIB_VERSION-src >/dev/null 213 | 214 | run cmake -DCMAKE_INSTALL_PREFIX=/usr/local/override \ 215 | -DCMAKE_C_FLAGS="-fPIC -fvisibility=hidden" \ 216 | -DCMAKE_CXX_FLAGS="-fPIC -fvisibility=hidden" . \ 217 | -DDISABLE_SHARED=1 218 | run make -j$CONCURRENCY libmysql 219 | run make -C libmysql install 220 | run make -C include install 221 | run make -C scripts install 222 | run sed -i "s|^ldflags=''|ldflags='-lstdc++'|" /usr/local/override/bin/mysql_config 223 | 224 | echo "Leaving source directory" 225 | popd >/dev/null 226 | run rm -rf mysql-connector-c-$MYSQL_LIB_VERSION-src 227 | fi 228 | echo 229 | 230 | header "Installing PostgreSQL" 231 | if [[ ! -e /usr/local/override/lib/libpq.a ]]; then 232 | download_and_extract postgresql-$POSTGRESQL_VERSION.tar.bz2 \ 233 | http://ftp.postgresql.org/pub/source/v9.3.5/postgresql-$POSTGRESQL_VERSION.tar.bz2 234 | echo "Entering postgresql-$POSTGRESQL_VERSION" 235 | pushd postgresql-$POSTGRESQL_VERSION >/dev/null 236 | 237 | run ./configure --prefix=/usr/local/override CFLAGS="-O2 -fPIC -fvisibility=hidden" 238 | run make -j$CONCURRENCY -C src/common 239 | run make -j$CONCURRENCY -C src/backend 240 | run make -j$CONCURRENCY -C src/interfaces/libpq 241 | run make -C src/interfaces/libpq install-strip 242 | run make -j$CONCURRENCY -C src/include 243 | run make -C src/include install-strip 244 | run make -j$CONCURRENCY -C src/bin/pg_config 245 | run make -C src/bin/pg_config install-strip 246 | run rm /usr/local/override/lib/libpq.so* 247 | 248 | echo "Leaving source directory" 249 | popd >/dev/null 250 | run rm -rf postgresql-$POSTGRESQL_VERSION 251 | fi 252 | echo 253 | 254 | header "Installing ICU" 255 | if [[ ! -e /usr/local/override/lib/libicudata.a ]]; then 256 | download_and_extract icu4c-$ICU_DIR_VERSION-src.tgz \ 257 | http://download.icu-project.org/files/icu4c/$ICU_VERSION/icu4c-$ICU_DIR_VERSION-src.tgz 258 | echo "Entering $RUNTIME_DIR/icu4c-$ICU_DIR_VERSION-src" 259 | pushd icu/source >/dev/null 260 | 261 | run ./configure --prefix=/usr/local/override --disable-samples --disable-tests \ 262 | --enable-static --disable-shared --with-library-bits=$ARCHITECTURE_BITS \ 263 | CFLAGS="-O2 -fPIC -fvisibility=hidden -DU_CHARSET_IS_UTF8=1 -DU_USING_ICU_NAMESPACE=0" \ 264 | CXXFLAGS="-O2 -fPIC -fvisibility=hidden -DU_CHARSET_IS_UTF8=1 -DU_USING_ICU_NAMESPACE=0" 265 | run make -j$CONCURRENCY VERBOSE=1 266 | run make install -j$CONCURRENCY 267 | run strip --strip-debug /usr/local/override/lib/libicu*.a 268 | 269 | echo "Leaving source directory" 270 | popd >/dev/null 271 | run rm -rf icu 272 | fi 273 | echo 274 | 275 | header "Installing libssh2" 276 | if [[ ! -e /usr/local/override/lib/libssh2.a ]]; then 277 | download_and_extract libssh2-$LIBSSH2_VERSION.tar.gz \ 278 | http://www.libssh2.org/download/libssh2-$LIBSSH2_VERSION.tar.gz 279 | echo "Entering $RUNTIME_DIR/libssh2-$LIBSSH2_VERSION" 280 | pushd libssh2-$LIBSSH2_VERSION >/dev/null 281 | 282 | run ./configure --prefix=/usr/local/override --enable-static --disable-shared \ 283 | --with-openssl --with-libz --disable-examples-build --disable-debug \ 284 | CFLAGS="-O2 -fPIC -fvisibility=hidden" \ 285 | CXXFLAGS="-O2 -fPIC -fvisibility=hidden" 286 | run make -j$CONCURRENCY 287 | run make install-strip -j$CONCURRENCY 288 | 289 | echo "Leaving source directory" 290 | popd >/dev/null 291 | run rm -rf libssh2-$LIBSSH2_VERSION 292 | fi 293 | echo 294 | -------------------------------------------------------------------------------- /TUTORIAL-3.md: -------------------------------------------------------------------------------- 1 | # Tutorial 3: native extensions 2 | 3 | In [tutorial 2](TUTORIAL-2.md) we covered gem dependencies. But those were only gems without native extensions. In this third tutorial we'll cover the usage of native extensions. 4 | 5 | Normally, native extensions need to be compiled. But the goal of Traveling Ruby is to enable packaging for multiple platforms, no matter which OS you are developing on, so we obviously can't ask you to compile native extensions yourself. Aside from the hassle of compiling, compiling native extensions that would work on every system is a challenge in itself. 6 | 7 | So instead, the Traveling Ruby project supplies a number of precompiled native extensions that you can drop into your packages. Only specific versions are supplied, so your Gemfile must match the versions of the native extension gems that we supply. 8 | 9 | **Windows notes**: native extenstions are not yet supported in Windows! See the [caveats](README.md#caveats). 10 | 11 | ## Preparation 12 | 13 | Suppose that we want our hello world app from tutorial 2 to insert a row into an SQLite database file. We'll want to use the sqlite3 gem for that. 14 | 15 | Traveling Ruby provides a specific version of the sqlite3 gem. See [the Traveling Ruby Amazon S3 bucket](https://traveling-ruby.s3-us-west-2.amazonaws.com/list.html). For version 20141215-2.1.5, version 1.3.9 is supplied. So we add the following to our Gemfile: 16 | 17 | gem 'sqlite3', '1.3.9' 18 | 19 | Let's also modify hello.rb to do what we want: 20 | 21 | #!/usr/bin/env ruby 22 | require 'faker' 23 | require 'sqlite3' 24 | 25 | db = SQLite3::Database.new("hello.sqlite3") 26 | db.execute("create table if not exists foo (name varchar(255))") 27 | db.execute("insert into foo values ('hello world')") 28 | db.close 29 | puts "Hello #{Faker::Name.name}, database file modified." 30 | 31 | Then install your gem bundle: 32 | 33 | $ bundle install 34 | 35 | Verify that the modified program works: 36 | 37 | $ bundle exec ruby hello.rb 38 | Hello Freida Walker, database file modified. 39 | $ sqlite3 hello.sqlite3 40 | sqlite> select * from foo; 41 | name 42 | ----------- 43 | hello world 44 | 45 | ## Preparing the gem bundle, without native extensions 46 | 47 | Recall that the idea is that we create a package for every platform, and that we drop platform-specific precompiled native extensions in every package. But there's a little problem that we need to solve first. When you run `rake package`, it runs Bundler to create a local gem bundle for inclusion in packages. However, Bundler compiles native extensions for the platform that you're currently running on, but we don't want that to happen. So in this step, we must clean those things up. 48 | 49 | Using the Rakefile from tutorial 2, create the gem bundle which is to be included in packages: 50 | 51 | $ rake package:bundle_install 52 | 53 | Run these to remove any native extensions and compilation products from that bundle: 54 | 55 | $ rm -rf packaging/vendor/ruby/*/extensions 56 | $ find packaging/vendor/ruby/*/gems -name '*.so' | xargs rm -f 57 | $ find packaging/vendor/ruby/*/gems -name '*.bundle' | xargs rm -f 58 | $ find packaging/vendor/ruby/*/gems -name '*.o' | xargs rm -f 59 | 60 | ## Dropping native extensions 61 | 62 | Now you are ready to drop platform-specific native extensions inside the packages. First, create the package directories: 63 | 64 | $ rake package DIR_ONLY=1 65 | 66 | Next you must download the necessary native extensions, and extract them into `/lib/vendor`. You can find native extensions at [the Traveling Ruby Amazon S3 bucket](https://traveling-ruby.s3-us-west-2.amazonaws.com/list.html). Suppose that you're using Traveling Ruby version 20141215-2.1.5, which supplies sqlite3 version 1.3.9. Download and extract the precompiled binaries as follows. Remember that we're using CloudFront domain "https://d6r77u77i8pq3.cloudfront.net" to speed up downloads. 67 | 68 | $ cd hello-1.0.0-linux-x86/lib/vendor/ruby 69 | $ curl -L -O --fail https://d6r77u77i8pq3.cloudfront.net/releases/traveling-ruby-gems-20141215-2.1.5-linux-x86/sqlite3-1.3.9.tar.gz 70 | $ tar xzf sqlite3-1.3.9.tar.gz 71 | $ rm sqlite3-1.3.9.tar.gz 72 | $ cd ../../../.. 73 | 74 | $ cd hello-1.0.0-linux-x86_64/lib/vendor/ruby 75 | $ curl -L -O --fail https://d6r77u77i8pq3.cloudfront.net/releases/traveling-ruby-gems-20141215-2.1.5-linux-x86_64/sqlite3-1.3.9.tar.gz 76 | $ tar xzf sqlite3-1.3.9.tar.gz 77 | $ rm sqlite3-1.3.9.tar.gz 78 | $ cd ../../../.. 79 | 80 | $ cd hello-1.0.0-osx/lib/vendor/ruby 81 | $ curl -L -O --fail https://d6r77u77i8pq3.cloudfront.net/releases/traveling-ruby-gems-20141215-2.1.5-osx/sqlite3-1.3.9.tar.gz 82 | $ tar xzf sqlite3-1.3.9.tar.gz 83 | $ rm sqlite3-1.3.9.tar.gz 84 | $ cd ../../../.. 85 | 86 | ## Finishing up 87 | 88 | Package the directories and finalize the packages: 89 | 90 | $ tar -czf hello-1.0.0-linux-x86.tar.gz hello-1.0.0-linux-x86 91 | $ tar -czf hello-1.0.0-linux-x86_64.tar.gz hello-1.0.0-linux-x86_64 92 | $ tar -czf hello-1.0.0-osx.tar.gz hello-1.0.0-osx 93 | $ rm -rf hello-1.0.0-linux-x86 94 | $ rm -rf hello-1.0.0-linux-x86_64 95 | $ rm -rf hello-1.0.0-osx 96 | 97 | Now you can test whether it works. Suppose that you're developing on OS X. Extract the OS X package and test it: 98 | 99 | $ tar xzf hello-1.0.0-osx.tar.gz 100 | $ cd hello-1.0.0-osx 101 | $ ./hello 102 | Database file modified. (in red) 103 | $ sqlite3 hello.sqlite3 104 | sqlite> select * from foo; 105 | name 106 | ----------- 107 | hello world 108 | 109 | ## Automating the process using Rake 110 | 111 | We update the Rakefile so that all of the above steps are automated by running `rake package`. The `package:bundle_install` task has been updated to remove any locally compiled native extensions. The various packaging tasks have been updated to extract platform-specific native extension binaries. 112 | 113 | # For Bundler.with_clean_env 114 | require 'bundler/setup' 115 | 116 | PACKAGE_NAME = "hello" 117 | VERSION = "1.0.0" 118 | TRAVELING_RUBY_VERSION = "20150210-2.1.5" 119 | SQLITE3_VERSION = "1.3.9" # Must match Gemfile 120 | 121 | desc "Package your app" 122 | task :package => ['package:linux:x86', 'package:linux:x86_64', 'package:osx'] 123 | 124 | namespace :package do 125 | namespace :linux do 126 | desc "Package your app for Linux x86" 127 | task :x86 => [:bundle_install, 128 | "packaging/traveling-ruby-#{TRAVELING_RUBY_VERSION}-linux-x86.tar.gz", 129 | "packaging/traveling-ruby-#{TRAVELING_RUBY_VERSION}-linux-x86-sqlite3-#{SQLITE3_VERSION}.tar.gz" 130 | ] do 131 | create_package("linux-x86") 132 | end 133 | 134 | desc "Package your app for Linux x86_64" 135 | task :x86_64 => [:bundle_install, 136 | "packaging/traveling-ruby-#{TRAVELING_RUBY_VERSION}-linux-x86_64.tar.gz", 137 | "packaging/traveling-ruby-#{TRAVELING_RUBY_VERSION}-linux-x86_64-sqlite3-#{SQLITE3_VERSION}.tar.gz" 138 | ] do 139 | create_package("linux-x86_64") 140 | end 141 | end 142 | 143 | desc "Package your app for OS X" 144 | task :osx => [:bundle_install, 145 | "packaging/traveling-ruby-#{TRAVELING_RUBY_VERSION}-osx.tar.gz", 146 | "packaging/traveling-ruby-#{TRAVELING_RUBY_VERSION}-osx-sqlite3-#{SQLITE3_VERSION}.tar.gz" 147 | ] do 148 | create_package("osx") 149 | end 150 | 151 | desc "Install gems to local directory" 152 | task :bundle_install do 153 | if RUBY_VERSION !~ /^2\.1\./ 154 | abort "You can only 'bundle install' using Ruby 2.1, because that's what Traveling Ruby uses." 155 | end 156 | sh "rm -rf packaging/tmp" 157 | sh "mkdir packaging/tmp" 158 | sh "cp Gemfile Gemfile.lock packaging/tmp/" 159 | Bundler.with_clean_env do 160 | sh "cd packaging/tmp && env BUNDLE_IGNORE_CONFIG=1 bundle install --path ../vendor --without development" 161 | end 162 | sh "rm -rf packaging/tmp" 163 | sh "rm -f packaging/vendor/*/*/cache/*" 164 | sh "rm -rf packaging/vendor/ruby/*/extensions" 165 | sh "find packaging/vendor/ruby/*/gems -name '*.so' | xargs rm -f" 166 | sh "find packaging/vendor/ruby/*/gems -name '*.bundle' | xargs rm -f" 167 | sh "find packaging/vendor/ruby/*/gems -name '*.o' | xargs rm -f" 168 | end 169 | end 170 | 171 | file "packaging/traveling-ruby-#{TRAVELING_RUBY_VERSION}-linux-x86.tar.gz" do 172 | download_runtime("linux-x86") 173 | end 174 | 175 | file "packaging/traveling-ruby-#{TRAVELING_RUBY_VERSION}-linux-x86_64.tar.gz" do 176 | download_runtime("linux-x86_64") 177 | end 178 | 179 | file "packaging/traveling-ruby-#{TRAVELING_RUBY_VERSION}-osx.tar.gz" do 180 | download_runtime("osx") 181 | end 182 | 183 | file "packaging/traveling-ruby-#{TRAVELING_RUBY_VERSION}-linux-x86-sqlite3-#{SQLITE3_VERSION}.tar.gz" do 184 | download_native_extension("linux-x86", "sqlite3-#{SQLITE3_VERSION}") 185 | end 186 | 187 | file "packaging/traveling-ruby-#{TRAVELING_RUBY_VERSION}-linux-x86_64-sqlite3-#{SQLITE3_VERSION}.tar.gz" do 188 | download_native_extension("linux-x86_64", "sqlite3-#{SQLITE3_VERSION}") 189 | end 190 | 191 | file "packaging/traveling-ruby-#{TRAVELING_RUBY_VERSION}-osx-sqlite3-#{SQLITE3_VERSION}.tar.gz" do 192 | download_native_extension("osx", "sqlite3-#{SQLITE3_VERSION}") 193 | end 194 | 195 | def create_package(target) 196 | package_dir = "#{PACKAGE_NAME}-#{VERSION}-#{target}" 197 | sh "rm -rf #{package_dir}" 198 | sh "mkdir #{package_dir}" 199 | sh "mkdir -p #{package_dir}/lib/app" 200 | sh "cp hello.rb #{package_dir}/lib/app/" 201 | sh "mkdir #{package_dir}/lib/ruby" 202 | sh "tar -xzf packaging/traveling-ruby-#{TRAVELING_RUBY_VERSION}-#{target}.tar.gz -C #{package_dir}/lib/ruby" 203 | sh "cp packaging/wrapper.sh #{package_dir}/hello" 204 | sh "cp -pR packaging/vendor #{package_dir}/lib/" 205 | sh "cp Gemfile Gemfile.lock #{package_dir}/lib/vendor/" 206 | sh "mkdir #{package_dir}/lib/vendor/.bundle" 207 | sh "cp packaging/bundler-config #{package_dir}/lib/vendor/.bundle/config" 208 | sh "tar -xzf packaging/traveling-ruby-#{TRAVELING_RUBY_VERSION}-#{target}-sqlite3-#{SQLITE3_VERSION}.tar.gz " + 209 | "-C #{package_dir}/lib/vendor/ruby" 210 | if !ENV['DIR_ONLY'] 211 | sh "tar -czf #{package_dir}.tar.gz #{package_dir}" 212 | sh "rm -rf #{package_dir}" 213 | end 214 | end 215 | 216 | def download_runtime(target) 217 | sh "cd packaging && curl -L -O --fail " + 218 | "https://d6r77u77i8pq3.cloudfront.net/releases/traveling-ruby-#{TRAVELING_RUBY_VERSION}-#{target}.tar.gz" 219 | end 220 | 221 | def download_native_extension(target, gem_name_and_version) 222 | sh "curl -L --fail -o packaging/traveling-ruby-#{TRAVELING_RUBY_VERSION}-#{target}-#{gem_name_and_version}.tar.gz " + 223 | "https://d6r77u77i8pq3.cloudfront.net/releases/traveling-ruby-gems-#{TRAVELING_RUBY_VERSION}-#{target}/#{gem_name_and_version}.tar.gz" 224 | end 225 | 226 | ## Conclusion 227 | 228 | In this tutorial you've learned how to deal with native extensions. You can download the end result of this tutorial at https://github.com/phusion/traveling-ruby-native-extensions-demo. 229 | 230 | In all the tutorials so far, we've not covered Windows. Proceed with [tutorial 4](TUTORIAL-4.md) to learn about creating Windows packages. 231 | -------------------------------------------------------------------------------- /linux/internal/my_init: -------------------------------------------------------------------------------- 1 | #!/system/python27 -u 2 | import os, os.path, sys, stat, signal, errno, argparse, time, json, re 3 | 4 | KILL_PROCESS_TIMEOUT = 5 5 | KILL_ALL_PROCESSES_TIMEOUT = 5 6 | 7 | LOG_LEVEL_ERROR = 1 8 | LOG_LEVEL_WARN = 1 9 | LOG_LEVEL_INFO = 2 10 | LOG_LEVEL_DEBUG = 3 11 | 12 | log_level = None 13 | 14 | terminated_child_processes = {} 15 | 16 | class AlarmException(Exception): 17 | pass 18 | 19 | def error(message): 20 | if log_level >= LOG_LEVEL_ERROR: 21 | sys.stderr.write("*** %s\n" % message) 22 | 23 | def warn(message): 24 | if log_level >= LOG_LEVEL_WARN: 25 | sys.stderr.write("*** %s\n" % message) 26 | 27 | def info(message): 28 | if log_level >= LOG_LEVEL_INFO: 29 | sys.stderr.write("*** %s\n" % message) 30 | 31 | def debug(message): 32 | if log_level >= LOG_LEVEL_DEBUG: 33 | sys.stderr.write("*** %s\n" % message) 34 | 35 | def ignore_signals_and_raise_keyboard_interrupt(signame): 36 | signal.signal(signal.SIGTERM, signal.SIG_IGN) 37 | signal.signal(signal.SIGINT, signal.SIG_IGN) 38 | raise KeyboardInterrupt(signame) 39 | 40 | def raise_alarm_exception(): 41 | raise AlarmException('Alarm') 42 | 43 | def listdir(path): 44 | try: 45 | result = os.stat(path) 46 | except OSError: 47 | return [] 48 | if stat.S_ISDIR(result.st_mode): 49 | return sorted(os.listdir(path)) 50 | else: 51 | return [] 52 | 53 | def is_exe(path): 54 | try: 55 | return os.path.isfile(path) and os.access(path, os.X_OK) 56 | except OSError: 57 | return False 58 | 59 | def create_hosts_file(): 60 | run_command_killable("/bin/cp", "/etc/hosts", "/etc/workaround-docker-2267/") 61 | 62 | def import_envvars(clear_existing_environment = True, override_existing_environment = True): 63 | new_env = {} 64 | for envfile in listdir("/etc/container_environment"): 65 | name = os.path.basename(envfile) 66 | with open("/etc/container_environment/" + envfile, "r") as f: 67 | # Text files often end with a trailing newline, which we 68 | # don't want to include in the env variable value. See 69 | # https://github.com/phusion/baseimage-docker/pull/49 70 | value = re.sub('\n\Z', '', f.read()) 71 | new_env[name] = value 72 | if clear_existing_environment: 73 | os.environ.clear() 74 | for name, value in new_env.items(): 75 | if override_existing_environment or not name in os.environ: 76 | os.environ[name] = value 77 | 78 | def export_envvars(to_dir = True): 79 | shell_dump = "" 80 | for name, value in os.environ.items(): 81 | if name in ['HOME', 'USER', 'GROUP', 'UID', 'GID', 'SHELL']: 82 | continue 83 | if to_dir: 84 | with open("/etc/container_environment/" + name, "w") as f: 85 | f.write(value) 86 | shell_dump += "export " + shquote(name) + "=" + shquote(value) + "\n" 87 | with open("/etc/container_environment.sh", "w") as f: 88 | f.write(shell_dump) 89 | with open("/etc/container_environment.json", "w") as f: 90 | f.write(json.dumps(dict(os.environ))) 91 | 92 | _find_unsafe = re.compile(r'[^\w@%+=:,./-]').search 93 | 94 | def shquote(s): 95 | """Return a shell-escaped version of the string *s*.""" 96 | if not s: 97 | return "''" 98 | if _find_unsafe(s) is None: 99 | return s 100 | 101 | # use single quotes, and put single quotes into double quotes 102 | # the string $'b is then quoted as '$'"'"'b' 103 | return "'" + s.replace("'", "'\"'\"'") + "'" 104 | 105 | # Waits for the child process with the given PID, while at the same time 106 | # reaping any other child processes that have exited (e.g. adopted child 107 | # processes that have terminated). 108 | def waitpid_reap_other_children(pid): 109 | global terminated_child_processes 110 | 111 | status = terminated_child_processes.get(pid) 112 | if status: 113 | # A previous call to waitpid_reap_other_children(), 114 | # with an argument not equal to the current argument, 115 | # already waited for this process. Return the status 116 | # that was obtained back then. 117 | del terminated_child_processes[pid] 118 | return status 119 | 120 | done = False 121 | status = None 122 | while not done: 123 | try: 124 | this_pid, status = os.waitpid(-1, 0) 125 | if this_pid == pid: 126 | done = True 127 | else: 128 | # Save status for later. 129 | terminated_child_processes[this_pid] = status 130 | except OSError as e: 131 | if e.errno == errno.ECHILD or e.errno == errno.ESRCH: 132 | return None 133 | else: 134 | raise 135 | return status 136 | 137 | def stop_child_process(name, pid, signo = signal.SIGTERM, time_limit = KILL_PROCESS_TIMEOUT): 138 | info("Shutting down %s (PID %d)..." % (name, pid)) 139 | try: 140 | os.kill(pid, signo) 141 | except OSError: 142 | pass 143 | signal.alarm(time_limit) 144 | try: 145 | try: 146 | waitpid_reap_other_children(pid) 147 | except OSError: 148 | pass 149 | except AlarmException: 150 | warn("%s (PID %d) did not shut down in time. Forcing it to exit." % (name, pid)) 151 | try: 152 | os.kill(pid, signal.SIGKILL) 153 | except OSError: 154 | pass 155 | try: 156 | waitpid_reap_other_children(pid) 157 | except OSError: 158 | pass 159 | finally: 160 | signal.alarm(0) 161 | 162 | def run_command_killable(*argv): 163 | filename = argv[0] 164 | status = None 165 | pid = os.spawnvp(os.P_NOWAIT, filename, argv) 166 | try: 167 | status = waitpid_reap_other_children(pid) 168 | except BaseException as s: 169 | warn("An error occurred. Aborting.") 170 | stop_child_process(filename, pid) 171 | raise 172 | if status != 0: 173 | if status is None: 174 | error("%s exited with unknown status\n" % filename) 175 | else: 176 | error("%s failed with status %d\n" % (filename, os.WEXITSTATUS(status))) 177 | sys.exit(1) 178 | 179 | def run_command_killable_and_import_envvars(*argv): 180 | run_command_killable(*argv) 181 | import_envvars() 182 | export_envvars(False) 183 | 184 | def kill_all_processes(time_limit): 185 | info("Killing all processes...") 186 | try: 187 | os.kill(-1, signal.SIGTERM) 188 | except OSError: 189 | pass 190 | signal.alarm(time_limit) 191 | try: 192 | # Wait until no more child processes exist. 193 | done = False 194 | while not done: 195 | try: 196 | os.waitpid(-1, 0) 197 | except OSError as e: 198 | if e.errno == errno.ECHILD: 199 | done = True 200 | else: 201 | raise 202 | except AlarmException: 203 | warn("Not all processes have exited in time. Forcing them to exit.") 204 | try: 205 | os.kill(-1, signal.SIGKILL) 206 | except OSError: 207 | pass 208 | finally: 209 | signal.alarm(0) 210 | 211 | def run_startup_files(): 212 | # Run /etc/my_init.d/* 213 | for name in listdir("/etc/my_init.d"): 214 | filename = "/etc/my_init.d/" + name 215 | if is_exe(filename): 216 | info("Running %s..." % filename) 217 | run_command_killable_and_import_envvars(filename) 218 | 219 | # Run /etc/rc.local. 220 | if is_exe("/etc/rc.local"): 221 | info("Running /etc/rc.local...") 222 | run_command_killable_and_import_envvars("/etc/rc.local") 223 | 224 | def start_runit(): 225 | info("Booting runit daemon...") 226 | pid = os.spawnl(os.P_NOWAIT, "/usr/bin/runsvdir", "/usr/bin/runsvdir", 227 | "-P", "/etc/service") 228 | info("Runit started as PID %d" % pid) 229 | return pid 230 | 231 | def wait_for_runit_or_interrupt(pid): 232 | try: 233 | status = waitpid_reap_other_children(pid) 234 | return (True, status) 235 | except KeyboardInterrupt: 236 | return (False, None) 237 | 238 | def shutdown_runit_services(): 239 | debug("Begin shutting down runit services...") 240 | os.system("/usr/bin/sv down /etc/service/*") 241 | 242 | def wait_for_runit_services(): 243 | debug("Waiting for runit services to exit...") 244 | done = False 245 | while not done: 246 | done = os.system("/usr/bin/sv status /etc/service/* | grep -q '^run:'") != 0 247 | if not done: 248 | time.sleep(0.1) 249 | 250 | def install_insecure_key(): 251 | info("Installing insecure SSH key for user root") 252 | run_command_killable("/usr/sbin/enable_insecure_key") 253 | 254 | def main(args): 255 | create_hosts_file() 256 | import_envvars(False, False) 257 | export_envvars() 258 | 259 | if args.enable_insecure_key: 260 | install_insecure_key() 261 | 262 | if not args.skip_startup_files: 263 | run_startup_files() 264 | 265 | runit_exited = False 266 | exit_code = None 267 | 268 | if not args.skip_runit: 269 | runit_pid = start_runit() 270 | try: 271 | exit_status = None 272 | if len(args.main_command) == 0: 273 | runit_exited, exit_code = wait_for_runit_or_interrupt(runit_pid) 274 | if runit_exited: 275 | if exit_code is None: 276 | info("Runit exited with unknown status") 277 | exit_status = 1 278 | else: 279 | exit_status = os.WEXITSTATUS(exit_code) 280 | info("Runit exited with status %d" % exit_status) 281 | else: 282 | info("Running %s..." % " ".join(args.main_command)) 283 | pid = os.spawnvp(os.P_NOWAIT, args.main_command[0], args.main_command) 284 | try: 285 | exit_code = waitpid_reap_other_children(pid) 286 | if exit_code is None: 287 | info("%s exited with unknown status." % args.main_command[0]) 288 | exit_status = 1 289 | else: 290 | exit_status = os.WEXITSTATUS(exit_code) 291 | info("%s exited with status %d." % (args.main_command[0], exit_status)) 292 | except KeyboardInterrupt: 293 | stop_child_process(args.main_command[0], pid) 294 | raise 295 | except BaseException as s: 296 | warn("An error occurred. Aborting.") 297 | stop_child_process(args.main_command[0], pid) 298 | raise 299 | sys.exit(exit_status) 300 | finally: 301 | if not args.skip_runit: 302 | shutdown_runit_services() 303 | if not runit_exited: 304 | stop_child_process("runit daemon", runit_pid) 305 | wait_for_runit_services() 306 | 307 | # Parse options. 308 | parser = argparse.ArgumentParser(description = 'Initialize the system.') 309 | parser.add_argument('main_command', metavar = 'MAIN_COMMAND', type = str, nargs = '*', 310 | help = 'The main command to run. (default: runit)') 311 | parser.add_argument('--enable-insecure-key', dest = 'enable_insecure_key', 312 | action = 'store_const', const = True, default = False, 313 | help = 'Install the insecure SSH key') 314 | parser.add_argument('--skip-startup-files', dest = 'skip_startup_files', 315 | action = 'store_const', const = True, default = False, 316 | help = 'Skip running /etc/my_init.d/* and /etc/rc.local') 317 | parser.add_argument('--skip-runit', dest = 'skip_runit', 318 | action = 'store_const', const = True, default = False, 319 | help = 'Do not run runit services') 320 | parser.add_argument('--no-kill-all-on-exit', dest = 'kill_all_on_exit', 321 | action = 'store_const', const = False, default = True, 322 | help = 'Don\'t kill all processes on the system upon exiting') 323 | parser.add_argument('--quiet', dest = 'log_level', 324 | action = 'store_const', const = LOG_LEVEL_WARN, default = LOG_LEVEL_INFO, 325 | help = 'Only print warnings and errors') 326 | args = parser.parse_args() 327 | log_level = args.log_level 328 | 329 | if args.skip_runit and len(args.main_command) == 0: 330 | error("When --skip-runit is given, you must also pass a main command.") 331 | sys.exit(1) 332 | 333 | # Run main function. 334 | signal.signal(signal.SIGTERM, lambda signum, frame: ignore_signals_and_raise_keyboard_interrupt('SIGTERM')) 335 | signal.signal(signal.SIGINT, lambda signum, frame: ignore_signals_and_raise_keyboard_interrupt('SIGINT')) 336 | signal.signal(signal.SIGALRM, lambda signum, frame: raise_alarm_exception()) 337 | try: 338 | main(args) 339 | except KeyboardInterrupt: 340 | warn("Init system aborted.") 341 | exit(2) 342 | finally: 343 | if args.kill_all_on_exit: 344 | kill_all_processes(KILL_ALL_PROCESSES_TIMEOUT) 345 | -------------------------------------------------------------------------------- /linux/internal/build-ruby-inside-mock: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | source /system/library.sh 5 | 6 | NAME=`cat /system/NAME` 7 | APP_UID=`cat /system/APP_UID` 8 | APP_GID=`cat /system/APP_GID` 9 | RUBY_VERSION=`cat /system/RUBY_VERSION` 10 | BUNDLER_VERSION=`cat /system/BUNDLER_VERSION` 11 | CONCURRENCY=`cat /system/CONCURRENCY` 12 | SETUP_SOURCE=`cat /system/SETUP_SOURCE` 13 | COMPILE=`cat /system/COMPILE` 14 | SANITY_CHECK_OUTPUT=`cat /system/SANITY_CHECK_OUTPUT` 15 | DEBUG_SHELL=`cat /system/DEBUG_SHELL` 16 | 17 | # Ensure that our static libraries are used first. 18 | export CFLAGS=-I/usr/local/override/include 19 | export CXXFLAGS=-I/usr/local/override/include 20 | export LDFLAGS=-L/usr/local/override/lib 21 | export PATH=/usr/local/override/bin:$PATH 22 | export C_INCLUDE_PATH=/usr/local/override/include 23 | export CPLUS_INCLUDE_PATH=/usr/local/override/include 24 | export LIBRARY_PATH=/usr/local/override/lib 25 | export PKG_CONFIG_PATH=/usr/local/override/lib/pkgconfig:/usr/lib/pkgconfig 26 | export PATH=/usr/local/override/bin:$PATH 27 | 28 | if [[ "$NAME" = x86_64 ]]; then 29 | export PATH=/system/bin64:$PATH 30 | else 31 | export PATH=/system/bin32:$PATH 32 | fi 33 | 34 | function grep_without_fail() 35 | { 36 | grep "$@" || true 37 | } 38 | 39 | function create_environment_file() { 40 | local FILE="$1" 41 | local LOAD_PATHS 42 | 43 | LOAD_PATHS=`/tmp/ruby/bin.real/ruby /system/dump-load-paths.rb` 44 | 45 | cat > "$FILE" <<'EOF' 46 | #!/bin/bash 47 | ROOT=`dirname "$0"` 48 | ROOT=`cd "$ROOT/.." && pwd` 49 | 50 | echo ORIG_LD_LIBRARY_PATH=\"$LD_LIBRARY_PATH\" 51 | echo ORIG_SSL_CERT_DIR=\"$SSL_CERT_DIR\" 52 | echo ORIG_SSL_CERT_FILE=\"$SSL_CERT_FILE\" 53 | echo ORIG_RUBYOPT=\"$RUBYOPT\" 54 | echo ORIG_RUBYLIB=\"$RUBYLIB\" 55 | 56 | echo LD_LIBRARY_PATH=\"$LD_LIBRARY_PATH:$ROOT/lib\" 57 | echo SSL_CERT_FILE=\"$ROOT/lib/ca-bundle.crt\" 58 | echo RUBYOPT=\"-r$ROOT/lib/restore_environment\" 59 | for DIR in "$ROOT"/lib/ruby/gems/*/deplibs/*/*; do 60 | echo LD_LIBRARY_PATH=\"\$LD_LIBRARY_PATH:$DIR\" 61 | done 62 | EOF 63 | echo "echo GEM_HOME=\"\$ROOT/lib/ruby/gems/$RUBY_COMPAT_VERSION\"" >> "$FILE" 64 | echo "echo GEM_PATH=\"\$ROOT/lib/ruby/gems/$RUBY_COMPAT_VERSION\"" >> "$FILE" 65 | 66 | cat >> "$FILE" < "$FILE" <<'EOF' 94 | #!/bin/bash 95 | set -e 96 | ROOT=`dirname "$0"` 97 | ROOT=`cd "$ROOT/.." && pwd` 98 | eval "`\"$ROOT/bin/ruby_environment\"`" 99 | EOF 100 | if $IS_RUBY_SCRIPT; then 101 | cat >> "$FILE" <> "$FILE" < /tmp/ruby/info/RUBY_COMPAT_VERSION 197 | echo $RUBY_ARCH > /tmp/ruby/info/RUBY_ARCH 198 | echo $GEM_PLATFORM > /tmp/ruby/info/GEM_PLATFORM 199 | echo $GEM_EXTENSION_API_VERSION > /tmp/ruby/info/GEM_EXTENSION_API_VERSION 200 | 201 | # Install gem-specific library dependencies. 202 | run mkdir -p /tmp/ruby/lib/ruby/gems/$RUBY_COMPAT_VERSION/deplibs/$GEM_PLATFORM 203 | pushd /tmp/ruby/lib/ruby/gems/$RUBY_COMPAT_VERSION/deplibs/$GEM_PLATFORM 204 | run mkdir curses && run cp $USRLIBDIR/libncursesw.so.5 curses/ 205 | popd 206 | 207 | echo "Patching rbconfig.rb" 208 | echo >> /tmp/ruby/lib/ruby/$RUBY_COMPAT_VERSION/$RUBY_ARCH/rbconfig.rb 209 | cat "/system/rbconfig_patch.rb" >> /tmp/ruby/lib/ruby/$RUBY_COMPAT_VERSION/$RUBY_ARCH/rbconfig.rb 210 | 211 | # Remove some standard dummy gems. We must do this before 212 | # installing further gems in order to prevent accidentally 213 | # removing explicitly gems. 214 | run rm -rf /tmp/ruby/lib/ruby/gems/$RUBY_COMPAT_VERSION/gems/{test-unit,rdoc}-* 215 | 216 | function install_gems() 217 | { 218 | for GEMFILE in /system/gemfiles/*/Gemfile; do 219 | run cp "$GEMFILE" /tmp/ruby/ 220 | if [[ -e "$GEMFILE.lock" ]]; then 221 | run cp "$GEMFILE.lock" /tmp/ruby/ 222 | fi 223 | echo "+ Entering /tmp/ruby" 224 | pushd /tmp/ruby >/dev/null 225 | # We set -j to 1 because of this bug: 226 | # https://github.com/bundler/bundler/issues/3660 227 | # When it is solved, we can set -j to $CONCURRENCY. 228 | run /tmp/ruby/bin/bundle install --system --retry 3 --jobs 1 229 | run rm Gemfile* 230 | echo "+ Leaving /tmp/ruby" 231 | popd >/dev/null 232 | done 233 | } 234 | 235 | function open_debug_shell() 236 | { 237 | export EDITOR=nano 238 | export TERM=xterm-256color 239 | export PATH=/tmp/ruby/bin:$PATH 240 | unset PROMPT_COMMAND 241 | if [[ ! -e /usr/bin/nano ]]; then 242 | echo 243 | echo "----------- Preparing debugging shell -----------" 244 | run yum install -y nano 245 | fi 246 | echo 247 | echo "-------------------------------------------" 248 | echo "A debugging shell will be opened for you." 249 | pushd /tmp/ruby/lib/ruby/gems/* >/dev/null 250 | bash || true 251 | popd >/dev/null 252 | } 253 | 254 | export DEFAULT_LDFLAGS=`/tmp/ruby/bin/ruby -rrbconfig -e 'puts RbConfig::CONFIG["LDFLAGS"]'` 255 | export BUNDLE_BUILD__CHARLOCK_HOLMES=--with-ldflags="'$DEFAULT_LDFLAGS -Wl,--whole-archive -licui18n -licuuc -licudata -Wl,--no-whole-archive -lstdc++'" 256 | export BUNDLE_BUILD__RUGGED=--with-ldflags="'$DEFAULT_LDFLAGS -Wl,--whole-archive -lssl -lcrypto -Wl,--no-whole-archive'" 257 | export BUNDLE_BUILD__PUMA=--with-ldflags="'$DEFAULT_LDFLAGS -lz'" 258 | export BUNDLE_BUILD__EVENTMACHINE=--with-ldflags="'$DEFAULT_LDFLAGS -lz'" 259 | 260 | if [[ "$DEBUG_SHELL" = before ]]; then 261 | open_debug_shell 262 | fi 263 | if [[ -e /system/gemfiles ]]; then 264 | run /tmp/ruby/bin/gem install bundler -v $BUNDLER_VERSION --no-rdoc --no-ri 265 | if [[ "$DEBUG_SHELL" = after ]]; then 266 | install_gems || true 267 | else 268 | install_gems 269 | fi 270 | fi 271 | if [[ "$DEBUG_SHELL" = after ]]; then 272 | open_debug_shell 273 | fi 274 | 275 | # Strip binaries and remove unnecessary files. 276 | run strip --strip-all /tmp/ruby/bin/ruby 277 | ( 278 | set -o pipefail 279 | echo "+ Stripping .so files" 280 | find /tmp/ruby -name '*.so' | xargs strip --strip-debug 281 | ) 282 | if [[ $? != 0 ]]; then 283 | exit 1 284 | fi 285 | run rm /tmp/ruby/bin/{erb,rdoc,ri} 286 | run rm -f /tmp/ruby/bin/testrb # Only Ruby 2.1 has it 287 | run rm -rf /tmp/ruby/include 288 | run rm -rf /tmp/ruby/share 289 | run rm -rf /tmp/ruby/lib/{libruby-static.a,pkgconfig} 290 | run rm -rf /tmp/ruby/lib/ruby/$RUBY_COMPAT_VERSION/rdoc/generator/ 291 | run rm -f /tmp/ruby/lib/ruby/gems/$RUBY_COMPAT_VERSION/cache/* 292 | run rm -f /tmp/ruby/lib/ruby/gems/$RUBY_COMPAT_VERSION/extensions/$GEM_PLATFORM/$GEM_EXTENSION_API_VERSION/*/{gem_make.out} 293 | run rm -rf /tmp/ruby/lib/ruby/gems/$RUBY_COMPAT_VERSION/gems/*/{test,spec,*.md,*.rdoc} 294 | run rm -rf /tmp/ruby/lib/ruby/gems/$RUBY_COMPAT_VERSION/gems/*/ext/*/*.{c,h} 295 | 296 | if [[ -e /system/gemfiles ]]; then 297 | echo "+ Entering Bundler gem directory" 298 | pushd /tmp/ruby/lib/ruby/gems/$RUBY_COMPAT_VERSION/gems/bundler-$BUNDLER_VERSION >/dev/null 299 | rm -rf .gitignore .rspec .travis.yml man Rakefile lib/bundler/man/*.txt lib/bundler/templates 300 | popd >/dev/null 301 | echo "+ Leaving Bundler gem directory" 302 | fi 303 | 304 | # Create wrapper scripts 305 | mv /tmp/ruby/bin /tmp/ruby/bin.real 306 | mkdir /tmp/ruby/bin 307 | create_environment_file /tmp/ruby/bin/ruby_environment 308 | create_wrapper /tmp/ruby/bin/ruby ruby false 309 | create_wrapper /tmp/ruby/bin/gem gem true 310 | create_wrapper /tmp/ruby/bin/irb irb true 311 | create_wrapper /tmp/ruby/bin/rake rake true 312 | if [[ -e /system/Gemfile ]]; then 313 | create_wrapper /tmp/ruby/bin/bundle bundle true 314 | create_wrapper /tmp/ruby/bin/bundler bundler true 315 | fi 316 | echo 317 | 318 | if $SANITY_CHECK_OUTPUT; then 319 | header "Sanity checking build output" 320 | ( 321 | SYSTEM_LIBRARIES="(linux-gate|linux-vdso|libpthread|librt|libdl|libcrypt|libm|libc" 322 | SYSTEM_LIBRARIES="$SYSTEM_LIBRARIES|ld-linux.*|libutil|libnsl|libgcc_s|libstdc\+\+" 323 | SYSTEM_LIBRARIES="$SYSTEM_LIBRARIES|libz|libtermcap|libreadline|libncursesw|libffi)\.so" 324 | ERROR=false 325 | set -o pipefail 326 | for F in /tmp/ruby/bin.real/ruby `find /tmp/ruby -name '*.so'`; do 327 | EXTRA_LIBS=`ldd $F | grep_without_fail -vE "$SYSTEM_LIBRARIES" | sed 's/.*=> //g' | sed 's/ (.*//g'` 328 | EXTRA_LIBS=`echo $EXTRA_LIBS` 329 | if [[ "$EXTRA_LIBS" != "" ]]; then 330 | echo "$F is linked to non-system libraries: $EXTRA_LIBS" 331 | ERROR=true 332 | fi 333 | done 334 | if [[ $? != 0 ]]; then 335 | exit 1 336 | fi 337 | if $ERROR; then 338 | exit 1 339 | else 340 | echo "All OK" 341 | fi 342 | ) 343 | if [[ $? != 0 ]]; then 344 | exit 1 345 | fi 346 | echo 347 | fi 348 | 349 | header "Committing build output" 350 | run chown -R $APP_UID:$APP_GID /tmp/ruby 351 | run mv /tmp/ruby/* /output/ 352 | -------------------------------------------------------------------------------- /osx/build-ruby: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | SELFDIR=`dirname "$0"` 5 | SELFDIR=`cd "$SELFDIR" && pwd` 6 | source "$SELFDIR/internal/reset_environment.sh" 7 | source "$SELFDIR/../shared/library.sh" 8 | BUNDLER_VERSION=`cat "$SELFDIR/../BUNDLER_VERSION.txt"` 9 | RUBY_VERSIONS=(`cat "$SELFDIR/../RUBY_VERSIONS.txt"`) 10 | 11 | GEMFILES=() 12 | 13 | RUNTIME_DIR= 14 | OUTPUT_DIR= 15 | RUBY_VERSION=${RUBY_VERSIONS[0]} 16 | WORKDIR= 17 | OWNS_WORKDIR=true 18 | CONCURRENCY=4 19 | GEMFILE="$SELFDIR/../shared/gemfiles" 20 | SETUP_SOURCE=true 21 | COMPILE=true 22 | 23 | function _cleanup() 24 | { 25 | if $OWNS_WORKDIR && [[ "$WORKDIR" != "" ]]; then 26 | echo "Removing working directory $WORKDIR" 27 | rm -rf "$WORKDIR" 28 | fi 29 | } 30 | 31 | function create_environment_file() { 32 | local FILE="$1" 33 | local LOAD_PATHS 34 | 35 | LOAD_PATHS=`"$TMPBUILDROOT/bin.real/ruby" "$SELFDIR/../shared/dump-load-paths.rb" "$TMPBUILDROOT"` 36 | 37 | cat > "$FILE" <<'EOF' 38 | #!/bin/bash 39 | ROOT=`dirname "$0"` 40 | ROOT=`cd "$ROOT/.." && pwd` 41 | 42 | echo ORIG_DYLD_LIBRARY_PATH=\"$LD_LIBRARY_PATH\" 43 | echo ORIG_TERMINFO=\"$TERMINFO\" 44 | echo ORIG_SSL_CERT_DIR=\"$SSL_CERT_DIR\" 45 | echo ORIG_SSL_CERT_FILE=\"$SSL_CERT_FILE\" 46 | echo ORIG_RUBYOPT=\"$RUBYOPT\" 47 | echo ORIG_RUBYLIB=\"$RUBYLIB\" 48 | 49 | echo TERMINFO=/usr/share/terminfo 50 | echo SSL_CERT_FILE=\"$ROOT/lib/ca-bundle.crt\" 51 | echo RUBYOPT=\"-r$ROOT/lib/restore_environment\" 52 | EOF 53 | echo "echo GEM_HOME=\"\$ROOT/lib/ruby/gems/$RUBY_COMPAT_VERSION\"" >> "$FILE" 54 | echo "echo GEM_PATH=\"\$ROOT/lib/ruby/gems/$RUBY_COMPAT_VERSION\"" >> "$FILE" 55 | 56 | cat >> "$FILE" < "$FILE" <<'EOF' 86 | #!/bin/bash 87 | set -e 88 | ROOT=`dirname "$0"` 89 | ROOT=`cd "$ROOT/.." && pwd` 90 | eval "`\"$ROOT/bin/ruby_environment\"`" 91 | EOF 92 | if $IS_RUBY_SCRIPT; then 93 | cat >> "$FILE" <> "$FILE" < " 118 | echo "Build Traveling Ruby binaries." 119 | echo 120 | echo "Options:" 121 | echo " -E Do not setup source" 122 | echo " -C Do not compile Ruby" 123 | echo " -G Do not install gems" 124 | echo 125 | echo " -r VERSION Ruby version to build. Default: $RUBY_VERSION" 126 | echo " -w DIR Use the given working directory instead of creating a temporary one" 127 | echo " -j NUM Compilation concurrency. Default: 4" 128 | echo " -g PATH Build gems as specified by the given Gemfile" 129 | echo " -h Show this help" 130 | } 131 | 132 | function parse_options() 133 | { 134 | local OPTIND=1 135 | local opt 136 | while getopts "ECGr:w:j:g:h" opt; do 137 | case "$opt" in 138 | E) 139 | SETUP_SOURCE=false 140 | ;; 141 | C) 142 | COMPILE=false 143 | ;; 144 | G) 145 | GEMFILE= 146 | ;; 147 | r) 148 | RUBY_VERSION=$OPTARG 149 | ;; 150 | w) 151 | WORKDIR="$OPTARG" 152 | OWNS_WORKDIR=false 153 | ;; 154 | j) 155 | CONCURRENCY=$OPTARG 156 | ;; 157 | g) 158 | GEMFILE="$OPTARG" 159 | ;; 160 | h) 161 | usage 162 | exit 163 | ;; 164 | *) 165 | return 1 166 | ;; 167 | esac 168 | done 169 | 170 | (( OPTIND -= 1 )) || true 171 | shift $OPTIND || true 172 | RUNTIME_DIR="$1" 173 | OUTPUT_DIR="$2" 174 | 175 | if [[ "$RUNTIME_DIR" = "" || "$OUTPUT_DIR" = "" ]]; then 176 | usage 177 | exit 1 178 | fi 179 | if [[ ! -e "$RUNTIME_DIR" ]]; then 180 | echo "ERROR: $RUNTIME_DIR doesn't exist." 181 | exit 1 182 | fi 183 | if [[ ! -e "$OUTPUT_DIR" ]]; then 184 | echo "ERROR: $OUTPUT_DIR doesn't exist." 185 | exit 1 186 | fi 187 | } 188 | 189 | parse_options "$@" 190 | RUNTIME_DIR="`cd \"$RUNTIME_DIR\" && pwd`" 191 | OUTPUT_DIR="`cd \"$OUTPUT_DIR\" && pwd`" 192 | if [[ "$WORKDIR" = "" ]]; then 193 | WORKDIR=`mktemp -d /tmp/traveling-ruby.XXXXXXXX` 194 | elif [[ ! -e "$WORKDIR" ]]; then 195 | echo "ERROR: working directory $WORKDIR doesn't exist." 196 | exit 1 197 | else 198 | WORKDIR="`cd \"$WORKDIR\" && pwd`" 199 | fi 200 | TMPBUILDROOT="$WORKDIR/inst" 201 | if [[ "$GEMFILE" != "" ]]; then 202 | GEMFILE="`absolute_path \"$GEMFILE\"`" 203 | if [[ -d "$GEMFILE" ]]; then 204 | GEMFILES=("$GEMFILE"/*/Gemfile) 205 | else 206 | GEMFILES=("$GEMFILE") 207 | fi 208 | fi 209 | if [[ -e ~/.bundle/config ]]; then 210 | echo "ERROR: ~/.bundle/config detected. Global Bundler configuration" \ 211 | "could conflict with this build script, so please remove" \ 212 | "~/.bundle/config first." 213 | exit 1 214 | fi 215 | "$SELFDIR/internal/check_sdk" 216 | 217 | 218 | ####################################### 219 | 220 | 221 | if [[ ! -e "$RUNTIME_DIR/ruby-$RUBY_VERSION.tar.gz" ]]; then 222 | header "Downloading Ruby source code..." 223 | RUBY_MAJOR=`echo $RUBY_VERSION | cut -d . -f 1` 224 | RUBY_MINOR=`echo $RUBY_VERSION | cut -d . -f 2` 225 | RUBY_MAJOR_MINOR="$RUBY_MAJOR.$RUBY_MINOR" 226 | run rm -f "$RUNTIME_DIR/ruby-$RUBY_VERSION.tar.gz.tmp" 227 | run curl --fail -L -o "$RUNTIME_DIR/ruby-$RUBY_VERSION.tar.gz.tmp" \ 228 | http://cache.ruby-lang.org/pub/ruby/$RUBY_MAJOR_MINOR/ruby-$RUBY_VERSION.tar.gz 229 | run mv "$RUNTIME_DIR/ruby-$RUBY_VERSION.tar.gz.tmp" "$RUNTIME_DIR/ruby-$RUBY_VERSION.tar.gz" 230 | echo 231 | fi 232 | 233 | 234 | header "Preparing Ruby source code..." 235 | 236 | export PATH="$RUNTIME_DIR/bin:$PATH" 237 | export LIBRARY_PATH="$RUNTIME_DIR/lib" 238 | export DYLD_FALLBACK_LIBRARY_PATH="$RUNTIME_DIR/lib" 239 | export PKG_CONFIG_PATH="$RUNTIME_DIR/lib/pkgconfig:/usr/lib/pkgconfig" 240 | export CCACHE_DIR="$RUNTIME_DIR/ccache" 241 | export CCACHE_COMPRESS=1 242 | export CCACHE_COMPRESS_LEVEL=3 243 | export RUNTIME_DIR 244 | export TMPBUILDROOT 245 | export DEAD_STRIP=false 246 | export RUBYOPT=-r"$SELFDIR/internal/modify_rbconfig_while_building" 247 | 248 | echo "Entering working directory $WORKDIR" 249 | pushd "$WORKDIR" >/dev/null 250 | 251 | if $SETUP_SOURCE; then 252 | run rm -rf "ruby-$RUBY_VERSION" 253 | run tar xzf "$RUNTIME_DIR/ruby-$RUBY_VERSION.tar.gz" 254 | fi 255 | echo "Entering ruby-$RUBY_VERSION" 256 | pushd "ruby-$RUBY_VERSION" >/dev/null 257 | echo 258 | 259 | 260 | if $SETUP_SOURCE; then 261 | header "Configuring..." 262 | ./configure \ 263 | --prefix "$TMPBUILDROOT" \ 264 | --disable-install-doc \ 265 | --disable-install-rdoc \ 266 | --disable-install-capi \ 267 | --with-out-ext=tk,sdbm,gdbm,dbm,dl,coverage \ 268 | --with-openssl-dir="$RUNTIME_DIR" 269 | echo 270 | fi 271 | 272 | 273 | if $COMPILE; then 274 | header "Compiling..." 275 | run make -j$CONCURRENCY Q= V=1 exts.mk 276 | run make -j$CONCURRENCY Q= V=1 277 | echo 278 | fi 279 | 280 | 281 | header "Installing to temporary build output directory..." 282 | ( 283 | shopt -s dotglob 284 | run mkdir -p "$TMPBUILDROOT" 285 | run rm -rf "$TMPBUILDROOT"/* 286 | ) 287 | [[ $? == 0 ]] 288 | run make install-nodoc 289 | echo 290 | 291 | 292 | header "Postprocessing build output..." 293 | 294 | echo "Entering $TMPBUILDROOT" 295 | pushd "$TMPBUILDROOT" >/dev/null 296 | 297 | # Copy over various useful files. 298 | run cp -pR "$RUNTIME_DIR"/lib/*.dylib* lib/ 299 | run cp "$SELFDIR/internal/restore_environment.rb" lib/ 300 | run cp "$SELFDIR/../shared/ca-bundle.crt" lib/ 301 | export SSL_CERT_FILE="$TMPBUILDROOT/lib/ca-bundle.crt" 302 | 303 | # Dump various information about the Ruby binaries. 304 | RUBY_COMPAT_VERSION=`"$TMPBUILDROOT/bin/ruby" -rrbconfig -e 'puts RbConfig::CONFIG["ruby_version"]'` 305 | RUBY_ARCH=`"$TMPBUILDROOT/bin/ruby" -rrbconfig -e 'puts RbConfig::CONFIG["arch"]'` 306 | GEM_PLATFORM=`"$TMPBUILDROOT/bin/ruby" -e 'puts Gem::Platform.local.to_s'` 307 | GEM_EXTENSION_API_VERSION=`"$TMPBUILDROOT/bin/ruby" -e 'puts Gem.extension_api_version'` 308 | run mkdir info 309 | echo "Dumping information about the Ruby binaries into $TMPBUILDROOT/info" 310 | echo $RUBY_COMPAT_VERSION > info/RUBY_COMPAT_VERSION 311 | echo $RUBYARCH > info/RUBY_ARCH 312 | echo $GEM_PLATFORM > info/GEM_PLATFORM 313 | echo $GEM_EXTENSION_API_VERSION > info/GEM_EXTENSION_API_VERSION 314 | 315 | echo "Patching rbconfig.rb" 316 | echo >> "$TMPBUILDROOT/lib/ruby/$RUBY_COMPAT_VERSION/$RUBY_ARCH/rbconfig.rb" 317 | cat "$SELFDIR/internal/rbconfig_patch.rb" >> "$TMPBUILDROOT/lib/ruby/$RUBY_COMPAT_VERSION/$RUBY_ARCH/rbconfig.rb" 318 | 319 | # Remove some standard dummy gems. We must do this before 320 | # installing further gems in order to prevent accidentally 321 | # removing explicitly gems. 322 | run rm -rf lib/ruby/gems/$RUBY_COMPAT_VERSION/gems/{test-unit,rdoc}-* 323 | 324 | if [[ "$GEMFILE" != "" ]]; then 325 | # Restore cached gems. 326 | if [[ -e "$RUNTIME_DIR/vendor" ]]; then 327 | run cp -pR "$RUNTIME_DIR/vendor" vendor 328 | fi 329 | 330 | # Install Bundler, either from cache or directly. 331 | if [[ -e "$RUNTIME_DIR/vendor/cache/bundler-$BUNDLER_VERSION.gem" ]]; then 332 | run "$TMPBUILDROOT/bin/gem" install "$RUNTIME_DIR/vendor/cache/bundler-$BUNDLER_VERSION.gem" --no-rdoc --no-ri 333 | else 334 | run "$TMPBUILDROOT/bin/gem" install bundler -v $BUNDLER_VERSION --no-rdoc --no-ri 335 | run mkdir -p "$RUNTIME_DIR/vendor/cache" 336 | run cp "$TMPBUILDROOT"/lib/ruby/gems/$RUBY_COMPAT_VERSION/cache/bundler-$BUNDLER_VERSION.gem \ 337 | "$RUNTIME_DIR/vendor/cache/" 338 | fi 339 | 340 | export BUNDLE_BUILD__NOKOGIRI=" " 341 | export BUNDLE_BUILD__MYSQL2="--with-mysql_config" 342 | export BUNDLE_BUILD__CHARLOCK_HOLMES="--with-icu-dir=$RUNTIME_DIR" 343 | 344 | # Run bundle install. 345 | for GEMFILE in "${GEMFILES[@]}"; do 346 | run cp "$GEMFILE" ./ 347 | if [[ -e "$GEMFILE.lock" ]]; then 348 | run cp "$GEMFILE.lock" ./ 349 | fi 350 | # We set -j to 1 because of this bug: 351 | # https://github.com/bundler/bundler/issues/3660 352 | # When it is solved, we can set -j to $CONCURRENCY. 353 | run "$TMPBUILDROOT/bin/bundle" install --system --retry 3 --jobs 1 354 | run "$TMPBUILDROOT/bin/bundle" package 355 | 356 | # Cache gems. 357 | run mkdir -p "$RUNTIME_DIR/vendor/cache" 358 | run mv vendor/cache/* "$RUNTIME_DIR"/vendor/cache/ 359 | 360 | run rm -rf Gemfile* .bundle 361 | done 362 | fi 363 | 364 | # Strip binaries and remove unnecessary files. 365 | run strip -S bin/ruby 366 | echo "Stripping libraries..." 367 | ( 368 | set -o pipefail 369 | find . -name '*.bundle' | xargs strip -S 370 | find . -name '*.dylib' | xargs strip -S 371 | ) 372 | [[ $? == 0 ]] 373 | run rm bin/{erb,rdoc,ri} 374 | run rm -f bin/testrb # Only Ruby 2.1 has it 375 | run rm -rf include 376 | run rm -rf share 377 | run rm -rf lib/{libruby-static.a,pkgconfig} 378 | run rm -rf lib/ruby/$RUBY_COMPAT_VERSION/rdoc/generator/ 379 | run rm -f lib/ruby/gems/$RUBY_COMPAT_VERSION/cache/* 380 | run rm -f lib/ruby/gems/$RUBY_COMPAT_VERSION/extensions/$GEM_PLATFORM/$GEM_EXTENSION_API_VERSION/*/{gem_make.out,mkmf.log} 381 | run rm -rf lib/ruby/gems/$RUBY_COMPAT_VERSION/gems/*/{test,spec,*.md,*.rdoc} 382 | run rm -rf lib/ruby/gems/$RUBY_COMPAT_VERSION/gems/*/ext/*/*.{c,h} 383 | 384 | if [[ "$GEMFILE" != "" ]]; then 385 | echo "Entering Bundler gem directory" 386 | pushd lib/ruby/gems/$RUBY_COMPAT_VERSION/gems/bundler-$BUNDLER_VERSION >/dev/null 387 | rm -rf .gitignore .rspec .travis.yml man Rakefile lib/bundler/man/*.txt lib/bundler/templates 388 | popd >/dev/null 389 | echo "Leaving Bundler gem directory" 390 | fi 391 | 392 | # Create wrapper scripts. 393 | run mv bin bin.real 394 | run mkdir bin 395 | run create_environment_file bin/ruby_environment 396 | 397 | run create_wrapper bin/ruby ruby false 398 | run create_wrapper bin/gem gem true 399 | run create_wrapper bin/irb irb true 400 | run create_wrapper bin/rake rake true 401 | if $INSTALL_GEMS; then 402 | run create_wrapper bin/bundle bundle true 403 | run create_wrapper bin/bundler bundler true 404 | fi 405 | 406 | echo "Leaving $TMPBUILDROOT" 407 | popd >/dev/null 408 | echo 409 | 410 | 411 | header "Sanity checking build output..." 412 | bash "$SELFDIR/internal/sanity_check" "$TMPBUILDROOT" 413 | echo 414 | 415 | 416 | header "Committing build output..." 417 | shopt -s dotglob 418 | run rm -rf "$OUTPUT_DIR"/* 419 | run mv "$TMPBUILDROOT"/* "$OUTPUT_DIR"/ 420 | echo 421 | 422 | header "All done!" 423 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Traveling Ruby: self-contained, portable Ruby binaries 2 | 3 | ![](https://openclipart.org/image/300px/svg_to_png/181225/Travel_backpacks.png) 4 | 5 | Traveling Ruby is a project which supplies self-contained, "portable" Ruby binaries: Ruby binaries that can run on any Linux distribution and any OS X machine. It also has Windows support [(with some caveats)](#caveats). This allows Ruby app developers to bundle these binaries with their Ruby app, so that they can distribute a single package to end users, without needing end users to first install Ruby or gems. 6 | 7 | [![](https://raw.githubusercontent.com/phusion/traveling-ruby/master/doc/video.png)](https://vimeo.com/phusionnl/review/113827942/ceca7e70da) 8 | 9 | _Introduction in 2 minutes_ 10 | 11 | ## Motivation 12 | 13 | Ruby is one of our favorite programming languages. Most people use it for web development, but Ruby is so much more. We at Phusion have been using Ruby for years for writing sysadmin automation scripts, developer command line tools and more. [Heroku's Toolbelt](https://toolbelt.heroku.com/) and [Chef](https://www.chef.io/) have also demonstrated that Ruby is an excellent language for these sorts of things. 14 | 15 | However, distributing such Ruby apps to inexperienced end users or non-Ruby-programmer end users is problematic. If users have to install Ruby first, or if they have to use RubyGems, [they can easily run into problems](#end_user_problems). Even if they already have Ruby installed, they [can still run into problems](#end_user_problems), e.g. by having the wrong Ruby version installed. The point is, it's a very real problem that [could harm your reputation](#end_user_problems). 16 | 17 | One solution is to build OS-specific installation packages, e.g. DEBs, RPMs, .pkgs, etc. However, this has two disadvantages: 18 | 19 | 1. It requires a lot of work. You not only have to build separate packages for each OS, but also each OS *version*. And in the context of Linux, you have to treat each distribution as another OS, further increasing the number of combinations. Suppose that you want to support ~2 versions of CentOS/RHEL, ~2 versions of Debian, ~3 versions of Ubuntu, ~2 recent OS X releases, on both x86 and x86_64. You'll have to create `(2 + 2 + 3) * 2 + 2 = 16` packages. 20 | 2. Because you typically cannot build an OS-specific installation package using anything but that OS, you need heavyweight tooling, e.g. a fleet of VMs. For example, you can only build Ubuntu 14.04 DEBs on Ubuntu 14.04; you cannot build them from your OS X developer laptop. 21 | 22 | This is exactly the approach that Chef has chosen. They built [Omnibus](https://github.com/opscode/omnibus), an automation system which spawns an army of VMs for building platform-specific packages. It works, but it's heavyweight and a big hassle. You need a big build machine for that if you want to have reasonable build time. And be prepared to make 20 cups of coffee. 23 | 24 | But there is another — much simpler — solution. 25 | 26 | ### Way of the Traveling Ruby 27 | 28 | The solution that Traveling Ruby advocates, is to distribute your app as a single self-contained tar.gz/zip package that already includes a precompiled Ruby interpreter for a specific platform (that the Traveling Ruby project provides), as well as all gems that your app depends on. This eliminates the need for heavyweight tooling: 29 | 30 | * A tar.gz/zip file can be created on any platform using small and simple tools. 31 | * You can create packages for any OS, regardless of which OS you are using. 32 | 33 | This makes the release process much simpler. Instead of having to create more than 10 packages using a fleet of VMs, you just create 3 packages quickly and easily from your developer laptop. These 3 packages cover all the major platforms that your end users are on: 34 | 35 | * Linux x86. 36 | * Linux x86_64. 37 | * OS X. 38 | * Windows. But see [the Windows-specific caveats](#caveats). 39 | 40 | However, distributing a precompiled Ruby interpreter that works for all end users, is more easily said than done. [Read this section](#why_precompiled_binary_difficult) to learn why it's difficult. 41 | 42 | Traveling Ruby aims to solve the problem of supplying precompiled **Ruby 2.1** binaries that work for all end users. 43 | 44 | ## Getting started 45 | 46 | Begin with the tutorials: 47 | 48 | * [Tutorial 1: hello world](TUTORIAL-1.md) - Learn in 5 minutes how to create self-contained packages of a hello world app without gem dependencies. 49 | * [Tutorial 2: gem dependencies](TUTORIAL-2.md) - Managing and packaging gem dependencies using Bundler. 50 | * [Tutorial 3: native extensions](TUTORIAL-3.md) - Managing and packaging native extension gems. 51 | * [Tutorial 4: creating packages for Windows](TUTORIAL-4.md) - Creating packages for Windows users. 52 | 53 | Once you've finished the tutorials, read the guides for intermediate to advanced topics: 54 | 55 | * [Reducing the size of your Traveling Ruby packages](REDUCING_PACKAGE_SIZE.md) 56 | 57 | There are also some real-world examples of how people used Traveling Ruby to package their Ruby tools: 58 | 59 | * **BOSH (release engineering tool)**
60 | [Blog post](https://blog.starkandwayne.com/2014/12/24/traveling-bosh-cli-no-more-installation-pain/) | [Github repo](https://github.com/cloudfoundry-community/traveling-bosh) 61 | * **Elasticrawl (AWS Elastic MapReduce job runner)**
62 | [Blog post](https://rossfairbanks.com/2015/01/13/packaging-elasticrawl-using-traveling-ruby.html) | [Github repo](https://github.com/rossf7/traveling-elasticrawl) 63 | * **VirtKick (cloud web panel)**
64 | [Github repo](https://github.com/virtkick/virtkick-webapp) 65 | * **Octodown (Github markdown preview tool)**
66 | [Github repo](https://github.com/ianks/octodown) | [Traveling Ruby issue](https://github.com/ianks/octodown/issues/29) | [Traveling Ruby pull request](https://github.com/ianks/octodown/pull/38) 67 | * **WebAirplay (local webapp to send videos to airplay devices)**
68 | [Github repo](https://github.com/antulik/web-airplay) 69 | 70 | 71 | 72 | ## Caveats 73 | 74 | Native extensions: 75 | 76 | * Traveling Ruby only supports native extensions when creating Linux and OS X packages. Native extensions are currently not supported when creating Windows packages. 77 | * Traveling Ruby only supports a number of popular native extension gems, and only in some specific versions. You cannot use just any native extension gem. 78 | * Native extensions are covered in [tutorial 3](TUTORIAL-3.md). 79 | 80 | Windows support: 81 | 82 | * Traveling Ruby supports creating packages *for* Windows, but it does not yet support creating packages *on* Windows. That is, the Traveling Ruby tutorials and the documentation do not work when you are a Ruby developer on Windows. To create Windows packages, you must use OS X or Linux. 83 | 84 | This is because in our documentation we make heavy use of standard Unix tools. Tools which are not available on Windows. In the future we may replace the use of such tools with Ruby tools so that the documentation works on Windows too. 85 | * Traveling Ruby currently supports Ruby 2.1.5 and Ruby 2.2.0 for Linux and OS X. But for Windows, only Ruby 2.1.5 is supported. This is because [the RubyInstaller project](http://rubyinstaller.org/) hasn't released Ruby 2.2.0 binaries yet. 86 | * Native extensions are not yet supported. 87 | 88 | ## Building binaries 89 | 90 | The Traveling Ruby project supplies binaries that application developers can use. These binaries are built using the build systems in this repository. As an application developer, you do not have to use the build system. You only have to use the build systems when contributing to Traveling Ruby, when trying to reproduce our binaries, or when you want to customize the binaries. 91 | 92 | For the Linux build system, see [linux/README.md](linux/README.md). 93 | 94 | For the OS X build system, see [osx/README.md](osx/README.md). 95 | 96 | ## Future work 97 | 98 | * Provide a Rails example. 99 | * Native extensions support for Windows. We're currently blocked by the fact that [the RubyInstaller project](http://rubyinstaller.org/) hasn't been updated for Ruby 2.2.0 yet. 100 | * Document the Windows build system. 101 | * Support for creating a single executable instead of a directory. 102 | 103 | ## FAQ 104 | 105 | 106 | 107 | ### Why it is difficult to supply a precompiled Ruby interpreter that works for all end users? 108 | 109 | Chances are that you think that you can compile a Ruby binary on a certain OS, and that users using that same OS can use your Ruby binary. Not quite. Not even when they run the same OS *version* as you do. 110 | 111 | Basically, there are two problems that can prevent a binary from working on another system: 112 | 113 | 1. Libraries that your binary depends on, may not be available on the user's OS. 114 | * When compiling Ruby, you might accidentally introduce a dependency on a non-standard library! As a developer you probably have all sorts non-standard libraries installed on your system. While compiling Ruby, the Ruby build system autodetects certain libraries and links to them. 115 | * Even different versions of the same OS ship with different libraries! You cannot count on a certain library from an older OS version, to be still available on a newer version of the same OS. 116 | 2. On Linux, there are issues with glibc symbols. This is a little more complicated, so read on. 117 | 118 | Assuming that your binary doesn't use *any* libraries besides the C standard library, binaries compiled on a newer Linux system usually do not work on an older Linux system, even if you do not use newer APIs. This is because of glibc symbols. Each function in glibc - or symbol as C/C++ programmers call it - actually has multiple versions. This allows the glibc developers to change the behavior of a function without breaking backwards compatibility with apps that happen to rely on bugs or implementation-specific behavior. During the linking phase, the linker "helpfully" links against the most recent version of the symbol. The thing is, glibc introduces new symbol versions very often, resulting in binaries that will most likely depend on a recent glibc. 119 | 120 | There is no way to tell the compiler and linker to use older symbol versions unless you want to manually specify the version for each and every symbol, which is an undoable task. 121 | 122 | The only sane way to get around the glibc symbol problem, and to prevent accidental linking to unwanted libraries, is to create a tightly controlled build environment. On Linux, this build environment with come with an old glibc version. This tightly controlled build environment is sometimes called a "holy build box". 123 | 124 | The Traveling Ruby project provides such a holy build box. 125 | 126 | #### Why not just statically link the Ruby binary? 127 | 128 | First of all: easier said than done. The compiler prefers to link to dynamic libraries. You have to hand-edit lots of Makefiles to make everything properly link statically. You can't just add `-static` as compiler flag and expect everything to work. 129 | 130 | Second: Ruby is incompatible with static linking. On Linux systems, executables which are statically linked to the C library cannot dynamically load shared libraries. Yet Ruby extensions are shared libraries, and a Ruby interpreter that cannot load Ruby extensions is heavily crippled. 131 | 132 | So in Traveling Ruby we've taken a different approach. Our Ruby binaries are dynamically linked against the C library, but only uses old symbols to avoid glibc symbol problems. We also ship carefully-compiled versions of dependent shared libraries, like OpenSSL, ncurses, libedit, etc. 133 | 134 | 135 | 136 | ### Why is it problematic for end users if I don't bundle a Ruby interpreter? 137 | 138 | First of all, users just want to run your app as quickly as possible. Requiring them to install Ruby first is not only a distraction, but it can also cause problems. Here are a few examples of such problems: 139 | 140 | * There are various ways to install Ruby, e.g. by compiling from source, by using `apt-get` and `yum`, by using RVM/rbenv/chruby, etc. The choices are obvious to us, but users could get confused by the sheer number of choices. Worse: not all choices are good. APT and YUM often provide old versions of Ruby, which may not be the one that you want. Compiling from source and using rbenv/chruby requires the user to have a compiler toolchain and appropriate libraries pre-installed. How should they know what to pre-install before they can install Ruby? The Internet is filled with a ton of old and outdated tutorials, further increasing their confusion. 141 | * Users could install Ruby incorrectly, e.g. to a location that isn't in PATH. They could then struggle with "command not found" errors. PATH is obvious to us, but there are a lot of users out there can barely use the command line. We shouldn't punish them for lack of knowledge, they are end users after all. 142 | 143 | One way to solve this is for you to "hold the user's hand", by going through the trouble of supplying 4 or 5 different platform-specific installation instructions for installing Ruby. These instructions must be continuously kept up-to-date. That's a lot of work and QA on your part, and I'm sure you just want to concentrate on building your app. 144 | 145 | And let's for the sake of argument suppose that the user somehow has Ruby correctly installed. They still need to install your app. The most obvious way to do that is through RubyGems. But that will open a whole new can of worms: 146 | 147 | * On some OSes, RubyGems is configured in such a way that the RubyGems-installed commands are not in PATH. For a classic example, try running this on Debian 6: 148 | 149 | $ sudo apt-get install rubygems 150 | $ sudo gem install rails 151 | $ rails new foo 152 | bash: rails: command not found 153 | 154 | Not a good first impression for end users. 155 | * Depending on how Ruby is installed, you may or may not have to run `gem install` with `sudo`. It depends on whether `GEM_HOME` is writable by the current user or not. You can't tell them "always run with sudo", because if their `GEM_HOME` is in their home directory, running `gem install` with sudo will mess up all sorts of permissions. 156 | * Did I just mention `sudo`? No, because `sudo` by default resets a lot of environment variables. Environment variables which may be important for Ruby to work. 157 | - If the user installed Ruby with RVM, then the user has to run `rvmsudo` instead of sudo. RVM is implemented by setting `PATH`, `RUBYLIB`, `GEM_HOME` and other environment variables. rvmsudo is a wrapper around sudo which preserves these environment variables. 158 | - If the user installed Ruby with rbenv or chruby... pray that they know what they're doing. Rbenv and chruby also require correct `PATH`, `RUBYLIB`, `GEM_HOME` etc to be set to specific values, but they provide no rvmsudo-like tool for preserving them after taking sudo access. So if you want to be user-friendly, you have to write documentation that tells users to sudo to a bash shell first, fix their `PATH`, `RUBYLIB` etc, and *then* run `gem install`. 159 | 160 | The point is, there's a lot of opportunity for end users to get stuck, confused and frustrated. You can deal with all these problems by supplying excellent documentation that handles all of these cases (and probably more, because there are infinite ways to break things). That's exactly what we've done for [Phusion Passenger](https://www.phusionpassenger.com). Our [RubyGems installation instructions](https://www.phusionpassenger.com/documentation/Users%20guide%20Nginx.html#rubygems_generic_install) spell out exactly how to install Ruby for each of the major operating systems, how to find out whether they need to run `gem install` with sudo, how to find out whether they need to run rvmsudo instead of sudo. It has been a lot of work, and even then we still haven't covered all the cases. We're still lacking documentation on what rbenv and chruby users should do. Right now, rbenv/chruby users regularly contact our community discussion forum about installation problems related to sudo access and environment variables. 161 | 162 | Or you can just use Traveling Ruby and be done with it. We can't do it for Phusion Passenger because by its very nature it has to work with an already-installed Ruby, but maybe you can for writing your next command line tool. 163 | 164 | #### The problems sound hypothetical. Is it really that big of a deal for end users? 165 | 166 | Yes. These problems can put off your users from installing your app at all and can give you a bad reputation. Especially Chef has suffered a lot from this. A lot of people have had bad experience in the past with installing Chef through RubyGems. Chef has solved this problem for years by supplying platform-specific packages for years (DEBs, RPMs, etc), but the reputation stuck: there are still people out there who shun Chef because they think they have to install Ruby and use RubyGems. 167 | 168 | #### I target OS X, which already ships Ruby. Should I still bundle a Ruby interpreter? 169 | 170 | Yes. OS X versions up to 10.8 Mountain Lion ship Ruby 1.8. Only starting from 10.9 Mavericks does it ship Ruby 2.0. There are significant compatibility differences between Ruby 1.8 and 2.0. Future OS X versions might ship yet another Ruby version. Only by bundling Ruby can you be sure that OS upgrades won't break your app. 171 | 172 | 173 | 174 | ### Does Traveling Ruby support Windows? 175 | 176 | [Yes](TUTORIAL-4.md), but with some [caveats](#caveats). 177 | 178 | ### How big is a hello world packaged with Traveling Ruby? 179 | 180 | It's about 6 MB compressed. 181 | -------------------------------------------------------------------------------- /osx/setup-runtime: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | SELFDIR=`dirname "$0"` 5 | SELFDIR=`cd "$SELFDIR" && pwd` 6 | source "$SELFDIR/internal/reset_environment.sh" 7 | source "$SELFDIR/../shared/library.sh" 8 | 9 | TEMPDIR= 10 | 11 | RUNTIME_DIR= 12 | CONCURRENCY=4 13 | FORCE_CCACHE=false 14 | SKIP_CCACHE=false 15 | FORCE_CMAKE=false 16 | SKIP_CMAKE=false 17 | FORCE_PKG_CONFIG=false 18 | SKIP_PKG_CONFIG=false 19 | FORCE_AUTOCONF=false 20 | SKIP_AUTOCONF=false 21 | FORCE_AUTOMAKE=false 22 | SKIP_AUTOMAKE=false 23 | FORCE_LIBTOOL=false 24 | SKIP_LIBTOOL=false 25 | FORCE_OPENSSL=false 26 | SKIP_OPENSSL=false 27 | FORCE_NCURSES=false 28 | SKIP_NCURSES=false 29 | FORCE_LIBEDIT=false 30 | SKIP_LIBEDIT=false 31 | FORCE_GMP=false 32 | SKIP_GMP=false 33 | FORCE_LIBFFI=false 34 | SKIP_LIBFFI=false 35 | FORCE_LIBYAML=false 36 | SKIP_LIBYAML=false 37 | FORCE_SQLITE3=false 38 | SKIP_SQLITE3=false 39 | FORCE_LIBLZMA=false 40 | SKIP_LIBLZMA=false 41 | FORCE_MYSQL=false 42 | SKIP_MYSQL=false 43 | FORCE_POSTGRESQL=false 44 | SKIP_POSTGRESQL=false 45 | FORCE_ICU=false 46 | SKIP_ICU=false 47 | FORCE_LIBSSH2=false 48 | SKIP_LIBSSH2=false 49 | 50 | function _cleanup() 51 | { 52 | if [[ "$TEMPDIR" != "" ]]; then 53 | rm -rf "$TEMPDIR" 54 | fi 55 | } 56 | 57 | function download_and_extract() 58 | { 59 | local BASENAME="$1" 60 | local URL="$2" 61 | local regex='\.bz2$' 62 | 63 | run rm -f "$BASENAME" 64 | run curl --fail -L -o "$BASENAME" "$URL" 65 | if [[ "$URL" =~ $regex ]]; then 66 | run tar xjf "$BASENAME" 67 | else 68 | run tar xzf "$BASENAME" 69 | fi 70 | run rm "$BASENAME" 71 | } 72 | 73 | function usage() 74 | { 75 | echo "Usage: ./setup-runtime [options] " 76 | echo "Sets up the Traveling Ruby build system's runtime." 77 | echo 78 | echo "Options:" 79 | echo " -o Force installing OpenSSL" 80 | echo " -O Skip installing OpenSSL" 81 | echo " -n Force installing ncurses" 82 | echo " -N Skip installing ncurses" 83 | echo " -e Force installing libedit" 84 | echo " -E Skip installing libedit" 85 | echo " -e Force installing GMP" 86 | echo " -E Skip installing GMP" 87 | echo " -f Force installing libffi" 88 | echo " -F Skip installing libffi" 89 | echo " -y Force installing libyaml" 90 | echo " -Y Skip installing libyaml" 91 | echo " -s Force installing sqlite3" 92 | echo " -S Skip installing sqlite3" 93 | echo " -z Force installing liblzma" 94 | echo " -Z Skip installing liblzma" 95 | echo " -m Force installing MySQL" 96 | echo " -M Skip installing MySQL" 97 | echo " -p Force installing PostgreSQL" 98 | echo " -P Skip installing PostgreSQL" 99 | echo " -i Force installing ICU" 100 | echo " -I Skip installing ICU" 101 | echo " -b Force installing libssh2" 102 | echo " -B Skip installing libssh2" 103 | echo 104 | echo " -j NUM Compilation concurrency. Default: 4" 105 | echo " -h Show this help" 106 | } 107 | 108 | function parse_options() 109 | { 110 | local OPTIND=1 111 | local opt 112 | while getopts "oOnNeEgGfFyYsSzZmMpPiIbBj:h" opt; do 113 | case "$opt" in 114 | o) 115 | FORCE_OPENSSL=true 116 | ;; 117 | O) 118 | SKIP_OPENSSL=true 119 | ;; 120 | n) 121 | FORCE_NCURSES=true 122 | ;; 123 | N) 124 | SKIP_NCURSES=true 125 | ;; 126 | e) 127 | FORCE_LIBEDIT=true 128 | ;; 129 | E) 130 | SKIP_LIBEDIT=true 131 | ;; 132 | g) 133 | FORCE_GMP=true 134 | ;; 135 | G) 136 | SKIP_GMP=true 137 | ;; 138 | f) 139 | FORCE_LIBFFI=true 140 | ;; 141 | F) 142 | SKIP_LIBFFI=true 143 | ;; 144 | y) 145 | FORCE_LIBYAML=true 146 | ;; 147 | Y) 148 | SKIP_LIBYAML=true 149 | ;; 150 | s) 151 | FORCE_SQLITE3=true 152 | ;; 153 | S) 154 | SKIP_SQLITE3=true 155 | ;; 156 | z) 157 | FORCE_LIBLZMA=true 158 | ;; 159 | Z) 160 | SKIP_LIBLZMA=true 161 | ;; 162 | m) 163 | FORCE_MYSQL=true 164 | ;; 165 | M) 166 | SKIP_MYSQL=true 167 | ;; 168 | p) 169 | FORCE_POSTGRESQL=true 170 | ;; 171 | P) 172 | SKIP_POSTGRESQL=true 173 | ;; 174 | i) 175 | FORCE_ICU=true 176 | ;; 177 | I) 178 | SKIP_ICU=true 179 | ;; 180 | b) 181 | FORCE_LIBSSH2=true 182 | ;; 183 | B) 184 | SKIP_LIBSSH2=true 185 | ;; 186 | j) 187 | CONCURRENCY=$OPTARG 188 | ;; 189 | h) 190 | usage 191 | exit 192 | ;; 193 | *) 194 | return 1 195 | ;; 196 | esac 197 | done 198 | 199 | (( OPTIND -= 1 )) || true 200 | shift $OPTIND || true 201 | RUNTIME_DIR="$1" 202 | 203 | if [[ "$RUNTIME_DIR" = "" ]]; then 204 | usage 205 | exit 1 206 | fi 207 | } 208 | 209 | parse_options "$@" 210 | mkdir -p "$RUNTIME_DIR" 211 | RUNTIME_DIR="`cd \"$RUNTIME_DIR\" && pwd`" 212 | "$SELFDIR/internal/check_sdk" 213 | 214 | 215 | ####################################### 216 | 217 | 218 | TOTAL_TOOLS=6 219 | TOTAL_LIBS=12 220 | CCACHE_VERSION=3.2.1 221 | CMAKE_VERSION=3.0.2 222 | PKG_CONFIG_VERSION=0.28 223 | AUTOCONF_VERSION=2.69 224 | AUTOMAKE_VERSION=1.15 225 | LIBTOOL_VERSION=2.4.6 226 | OPENSSL_VERSION=1.0.2f 227 | NCURSES_VERSION=5.9 228 | LIBEDIT_VERSION=20141030-3.1 229 | LIBEDIT_DIR_VERSION=20141029-3.1 230 | GMP_VERSION=6.0.0a 231 | GMP_DIR_VERSION=6.0.0 232 | LIBFFI_VERSION=3.2.1 233 | LIBYAML_VERSION=0.1.5 234 | SQLITE3_VERSION=3080702 235 | XZ_VERSION=5.0.8 236 | MYSQL_LIB_VERSION=6.1.5 237 | POSTGRESQL_VERSION=9.3.5 238 | ICU_VERSION=54.1 239 | ICU_DIR_VERSION=54_1 240 | LIBSSH2_VERSION=1.4.3 241 | export PATH="$RUNTIME_DIR/bin:$PATH" 242 | export LIBRARY_PATH="$RUNTIME_DIR/lib" 243 | export DYLD_FALLBACK_LIBRARY_PATH="$RUNTIME_DIR/lib" 244 | export PKG_CONFIG_PATH="$RUNTIME_DIR/lib/pkgconfig:/usr/lib/pkgconfig" 245 | export RUNTIME_DIR 246 | export DEAD_STRIP=true 247 | 248 | 249 | header "Initializing..." 250 | run mkdir -p "$RUNTIME_DIR" 251 | run mkdir -p "$RUNTIME_DIR/ccache" 252 | export CCACHE_DIR="$RUNTIME_DIR/ccache" 253 | export CCACHE_COMPRESS=1 254 | export CCACHE_COMPRESS_LEVEL=3 255 | echo "Entering $RUNTIME_DIR" 256 | cd "$RUNTIME_DIR" 257 | echo 258 | 259 | header "Installing tool 1/$TOTAL_TOOLS: ccache..." 260 | if $SKIP_CCACHE; then 261 | echo "Skipped." 262 | elif [[ ! -e "$RUNTIME_DIR/bin/ccache" ]] || $FORCE_CCACHE; then 263 | download_and_extract ccache-$CCACHE_VERSION.tar.gz \ 264 | http://samba.org/ftp/ccache/ccache-$CCACHE_VERSION.tar.gz 265 | echo "Entering $RUNTIME_DIR/cmake-$CMAKE_VERSION" 266 | pushd ccache-$CCACHE_VERSION >/dev/null 267 | 268 | run ./configure --prefix="$RUNTIME_DIR" 269 | run make -j$CONCURRENCY 270 | run make install 271 | 272 | echo "Leaving source directory" 273 | popd >/dev/null 274 | run rm -rf ccache-$CCACHE_VERSION 275 | else 276 | echo "Already installed." 277 | fi 278 | echo 279 | 280 | header "Installing tool 2/$TOTAL_TOOLS: CMake..." 281 | if $SKIP_CMAKE; then 282 | echo "Skipped." 283 | elif [[ ! -e "$RUNTIME_DIR/bin/cmake" ]] || $FORCE_CMAKE; then 284 | download_and_extract cmake-$CMAKE_VERSION.tar.gz \ 285 | http://www.cmake.org/files/v3.0/cmake-$CMAKE_VERSION.tar.gz 286 | echo "Entering $RUNTIME_DIR/cmake-$CMAKE_VERSION" 287 | pushd cmake-$CMAKE_VERSION >/dev/null 288 | 289 | run ./configure --prefix="$RUNTIME_DIR" --no-qt-gui --parallel=$CONCURRENCY 290 | run make -j$CONCURRENCY 291 | run make install 292 | 293 | echo "Leaving source directory" 294 | popd >/dev/null 295 | run rm -rf cmake-$CMAKE_VERSION 296 | else 297 | echo "Already installed." 298 | fi 299 | echo 300 | 301 | header "Installing tool 3/$TOTAL_TOOLS: pkg-config..." 302 | if $SKIP_PKG_CONFIG; then 303 | echo "Skipped." 304 | elif [[ ! -e "$RUNTIME_DIR/bin/pkg-config" ]] || $FORCE_PKG_CONFIG; then 305 | download_and_extract pkg-config-$PKG_CONFIG_VERSION.tar.gz \ 306 | http://pkgconfig.freedesktop.org/releases/pkg-config-$PKG_CONFIG_VERSION.tar.gz 307 | echo "Entering $RUNTIME_DIR/pkg-config-$PKG_CONFIG_VERSION" 308 | pushd pkg-config-$PKG_CONFIG_VERSION >/dev/null 309 | 310 | run ./configure --prefix="$RUNTIME_DIR" --with-internal-glib 311 | run make -j$CONCURRENCY 312 | run make install 313 | echo "Entering $RUNTIME_DIR" 314 | popd >/dev/null 315 | run rm -rf pkg-config-$PKG_CONFIG_VERSION 316 | else 317 | echo "Already installed." 318 | fi 319 | echo 320 | 321 | header "Installing tool 4/$TOTAL_TOOLS: autoconf..." 322 | if $SKIP_AUTOCONF; then 323 | echo "Skipped." 324 | elif [[ ! -e "$RUNTIME_DIR/bin/autoconf" ]] || $FORCE_AUTOCONF; then 325 | download_and_extract autoconf-$AUTOCONF_VERSION.tar.gz \ 326 | http://ftp.gnu.org/gnu/autoconf/autoconf-$AUTOCONF_VERSION.tar.gz 327 | echo "Entering $RUNTIME_DIR/autoconf-$AUTOCONF_VERSION" 328 | pushd autoconf-$AUTOCONF_VERSION >/dev/null 329 | 330 | run ./configure --prefix="$RUNTIME_DIR" 331 | run make -j$CONCURRENCY 332 | run make install-strip 333 | echo "Entering $RUNTIME_DIR" 334 | popd >/dev/null 335 | run rm -rf autoconf-$AUTOCONF_VERSION 336 | else 337 | echo "Already installed." 338 | fi 339 | echo 340 | 341 | header "Installing tool 5/$TOTAL_TOOLS: automake..." 342 | if $SKIP_AUTOMAKE; then 343 | echo "Skipped." 344 | elif [[ ! -e "$RUNTIME_DIR/bin/automake" ]] || $FORCE_AUTOMAKE; then 345 | download_and_extract automake-$AUTOMAKE_VERSION.tar.gz \ 346 | http://ftp.gnu.org/gnu/automake/automake-$AUTOMAKE_VERSION.tar.gz 347 | echo "Entering $RUNTIME_DIR/automake-$AUTOMAKE_VERSION" 348 | pushd automake-$AUTOMAKE_VERSION >/dev/null 349 | 350 | run ./configure --prefix="$RUNTIME_DIR" 351 | run make -j$CONCURRENCY 352 | run make install-strip 353 | echo "Entering $RUNTIME_DIR" 354 | popd >/dev/null 355 | run rm -rf automake-$AUTOMAKE_VERSION 356 | else 357 | echo "Already installed." 358 | fi 359 | echo 360 | 361 | header "Installing tool 6/$TOTAL_TOOLS: libtool..." 362 | if $SKIP_LIBTOOL; then 363 | echo "Skipped." 364 | elif [[ ! -e "$RUNTIME_DIR/bin/libtoolize" ]] || $FORCE_LIBTOOL; then 365 | download_and_extract libtool-$LIBTOOL_VERSION.tar.gz \ 366 | http://ftp.gnu.org/gnu/libtool/libtool-$LIBTOOL_VERSION.tar.gz 367 | echo "Entering $RUNTIME_DIR/libtool-$LIBTOOL_VERSION" 368 | pushd libtool-$LIBTOOL_VERSION >/dev/null 369 | 370 | run ./configure --prefix="$RUNTIME_DIR" \ 371 | --disable-shared --enable-static \ 372 | CFLAGS='-O2 -fPIC -fvisibility=hidden' 373 | run make -j$CONCURRENCY 374 | run make install-strip 375 | echo "Entering $RUNTIME_DIR" 376 | popd >/dev/null 377 | run rm -rf libtool-$LIBTOOL_VERSION 378 | else 379 | echo "Already installed." 380 | fi 381 | echo 382 | 383 | header "Compiling runtime libraries 1/$TOTAL_LIBS: OpenSSL..." 384 | if $SKIP_OPENSSL; then 385 | echo "Skipped." 386 | elif [[ ! -e "$RUNTIME_DIR/lib/openssl-ok" ]] || $FORCE_OPENSSL; then 387 | run rm -f openssl-$OPENSSL_VERSION.tar.gz 388 | run curl --fail -L -O https://www.openssl.org/source/openssl-$OPENSSL_VERSION.tar.gz 389 | run tar xzf openssl-$OPENSSL_VERSION.tar.gz 390 | run rm openssl-$OPENSSL_VERSION.tar.gz 391 | echo "Entering $RUNTIME_DIR/openssl-$OPENSSL_VERSION" 392 | pushd openssl-$OPENSSL_VERSION >/dev/null 393 | 394 | run ./Configure darwin64-x86_64-cc --prefix="$RUNTIME_DIR" --openssldir="$RUNTIME_DIR/openssl" \ 395 | threads zlib shared no-sse2 396 | # For some reason the -j1 is explicitly required. Otherwise, running `make -j2` 397 | # on the Makefile which invoked setup-runtime could somehow pass the -j2 to sub-makes. 398 | run make -j1 399 | run make install_sw 400 | 401 | echo "Leaving source directory" 402 | popd >/dev/null 403 | run rm -rf openssl-$OPENSSL_VERSION 404 | 405 | run chmod u+rw lib/*.dylib 406 | run rm lib/libcrypto.a 407 | run rm lib/libssl.a 408 | run strip bin/openssl 409 | run strip -S lib/libcrypto.dylib 410 | run strip -S lib/libssl.dylib 411 | run install_name_tool -id "@executable_path/../lib/libssl.1.0.0.dylib" \ 412 | "$RUNTIME_DIR/lib/libssl.1.0.0.dylib" 413 | run install_name_tool -change \ 414 | "$RUNTIME_DIR/lib/libcrypto.1.0.0.dylib" "@executable_path/../lib/libcrypto.1.0.0.dylib" \ 415 | "$RUNTIME_DIR/lib/libssl.1.0.0.dylib" 416 | run install_name_tool -id "@executable_path/../lib/libcrypto.1.0.0.dylib" \ 417 | "$RUNTIME_DIR/lib/libcrypto.1.0.0.dylib" 418 | 419 | run sed -i '' 's/^Libs\.private:.*/Libs.private: -L\${libdir} -lssl -lcrypto -lz/' "$RUNTIME_DIR/lib/pkgconfig/openssl.pc" 420 | run sed -i '' 's/^Libs\.private:.*/Libs.private: -L\${libdir} -lssl -lcrypto -lz/' "$RUNTIME_DIR/lib/pkgconfig/libssl.pc" 421 | touch "$RUNTIME_DIR/lib/openssl-ok" 422 | else 423 | echo "Already installed." 424 | fi 425 | echo 426 | 427 | header "Compiling runtime libraries 2/$TOTAL_LIBS: ncurses..." 428 | if $SKIP_NCURSES; then 429 | echo "Skipped." 430 | elif [[ ! -e "$RUNTIME_DIR/lib/libncurses.5.dylib" ]] || $FORCE_NCURSES; then 431 | run rm -f ncurses-$NCURSES_VERSION.tar.bz2 432 | run curl --fail -L -O http://ftp.gnu.org/pub/gnu/ncurses/ncurses-$NCURSES_VERSION.tar.gz 433 | run tar xzf ncurses-$NCURSES_VERSION.tar.gz 434 | run rm ncurses-$NCURSES_VERSION.tar.gz 435 | echo "Entering $RUNTIME_DIR/ncurses-$NCURSES_VERSION" 436 | pushd ncurses-$NCURSES_VERSION >/dev/null 437 | 438 | run ./configure --prefix="$RUNTIME_DIR" --with-shared --without-normal --without-cxx --without-cxx-binding \ 439 | --without-ada --without-manpages --without-progs --without-tests --enable-pc-files \ 440 | --without-develop 441 | run make -j$CONCURRENCY 442 | run make install 443 | 444 | echo "Leaving source directory" 445 | popd >/dev/null 446 | run rm -rf ncurses-$NCURSES_VERSION 447 | 448 | run rm -f "$RUNTIME_DIR/lib"/{libpanel,libmenu,libform}* 449 | run rm -f "$RUNTIME_DIR/lib"/*.a 450 | run strip -S "$RUNTIME_DIR/lib/libncurses.5.dylib" 451 | pushd "$RUNTIME_DIR/lib" >/dev/null 452 | run ln -sf libncurses.5.dylib libtermcap.dylib 453 | popd >/dev/null 454 | run install_name_tool -id "@executable_path/../lib/libncurses.5.dylib" \ 455 | "$RUNTIME_DIR/lib/libncurses.5.dylib" 456 | else 457 | echo "Already installed." 458 | fi 459 | echo 460 | 461 | header "Compiling runtime libraries 3/$TOTAL_LIBS: libedit..." 462 | if $SKIP_LIBEDIT; then 463 | echo "Skipped." 464 | elif [[ ! -e "$RUNTIME_DIR/lib/libedit.0.dylib" ]] || $FORCE_LIBEDIT; then 465 | run rm -f libedit-$LIBEDIT_VERSION.tar.gz 466 | run curl --fail -L -O http://thrysoee.dk/editline/libedit-$LIBEDIT_VERSION.tar.gz 467 | run tar xzf libedit-$LIBEDIT_VERSION.tar.gz 468 | run rm libedit-$LIBEDIT_VERSION.tar.gz 469 | echo "Entering $RUNTIME_DIR/libedit-$LIBEDIT_VERSION" 470 | pushd libedit-$LIBEDIT_DIR_VERSION >/dev/null 471 | 472 | run ./configure --prefix="$RUNTIME_DIR" --disable-static --enable-widec 473 | run make -j$CONCURRENCY 474 | run make install-strip 475 | 476 | echo "Leaving source directory" 477 | popd >/dev/null 478 | run rm -rf libedit-$LIBEDIT_DIR_VERSION 479 | 480 | pushd "$RUNTIME_DIR/lib" >/dev/null 481 | run ln -sf libedit.0.dylib libreadline.dylib 482 | popd >/dev/null 483 | run install_name_tool -id "@executable_path/../lib/libedit.0.dylib" \ 484 | "$RUNTIME_DIR/lib/libedit.0.dylib" 485 | else 486 | echo "Already installed." 487 | fi 488 | echo 489 | 490 | header "Compiling runtime libraries 4/$TOTAL_LIBS: gmp..." 491 | if $SKIP_GMP; then 492 | echo "Skipped." 493 | elif [[ ! -e "$RUNTIME_DIR/lib/libgmp.10.dylib" ]] || $FORCE_GMP; then 494 | run rm -f gmp-$GMP_VERSION.tar.bz2 495 | run curl --fail -L -O https://gmplib.org/download/gmp/gmp-$GMP_VERSION.tar.bz2 496 | run tar xjf gmp-$GMP_VERSION.tar.bz2 497 | run rm gmp-$GMP_VERSION.tar.bz2 498 | echo "Entering $RUNTIME_DIR/gmp-$GMP_VERSION" 499 | pushd gmp-$GMP_DIR_VERSION >/dev/null 500 | 501 | run ./configure --prefix="$RUNTIME_DIR" --disable-static --without-readline 502 | run make -j$CONCURRENCY 503 | run make install-strip 504 | 505 | echo "Leaving source directory" 506 | popd >/dev/null 507 | run rm -rf gmp-$GMP_DIR_VERSION 508 | 509 | run install_name_tool -id "@executable_path/../lib/libgmp.10.dylib" \ 510 | "$RUNTIME_DIR/lib/libgmp.10.dylib" 511 | else 512 | echo "Already installed." 513 | fi 514 | echo 515 | 516 | header "Compiling runtime libraries 5/$TOTAL_LIBS: libffi..." 517 | if $SKIP_LIBFFI; then 518 | echo "Skipped." 519 | elif [[ ! -e "$RUNTIME_DIR/lib/libffi.6.dylib" ]] || $FORCE_LIBFFI; then 520 | run rm -f libffi-$LIBFFI_VERSION.tar.bz2 521 | run curl --fail -L -O ftp://sourceware.org/pub/libffi/libffi-$LIBFFI_VERSION.tar.gz 522 | run tar xzf libffi-$LIBFFI_VERSION.tar.gz 523 | run rm libffi-$LIBFFI_VERSION.tar.gz 524 | echo "Entering $RUNTIME_DIR/libffi-$LIBFFI_VERSION" 525 | pushd libffi-$LIBFFI_VERSION >/dev/null 526 | 527 | run env CFLAGS="-O3 -fomit-frame-pointer -fstrict-aliasing -ffast-math -Wall -fexceptions -fPIC" \ 528 | ./configure --prefix="$RUNTIME_DIR" --disable-static --enable-portable-binary 529 | run make -j$CONCURRENCY 530 | run make install-strip 531 | 532 | echo "Leaving source directory" 533 | popd >/dev/null 534 | run rm -rf libffi-$LIBFFI_VERSION 535 | 536 | run install_name_tool -id "@executable_path/../lib/libffi.6.dylib" \ 537 | "$RUNTIME_DIR/lib/libffi.6.dylib" 538 | else 539 | echo "Already installed." 540 | fi 541 | echo 542 | 543 | header "Compiling runtime libraries 6/$TOTAL_LIBS: libyaml..." 544 | if $SKIP_LIBYAML; then 545 | echo "Skipped." 546 | elif [[ ! -e "$RUNTIME_DIR/lib/libyaml-0.2.dylib" ]] || $FORCE_LIBYAML; then 547 | download_and_extract yaml-$LIBYAML_VERSION.tar.gz \ 548 | http://pyyaml.org/download/libyaml/yaml-$LIBYAML_VERSION.tar.gz 549 | echo "Entering $RUNTIME_DIR/libyaml-$LIBYAML_VERSION" 550 | pushd yaml-$LIBYAML_VERSION >/dev/null 551 | 552 | run ./configure --prefix="$RUNTIME_DIR" --disable-static 553 | run make -j$CONCURRENCY 554 | run make install-strip 555 | 556 | echo "Leaving source directory" 557 | popd >/dev/null 558 | run rm -rf yaml-$LIBYAML_VERSION 559 | 560 | run install_name_tool -id "@executable_path/../lib/libyaml-0.2.dylib" \ 561 | "$RUNTIME_DIR/lib/libyaml-0.2.dylib" 562 | else 563 | echo "Already installed." 564 | fi 565 | echo 566 | 567 | header "Compiling runtime libraries 7/$TOTAL_LIBS: sqlite3..." 568 | if $SKIP_SQLITE3; then 569 | echo "Skipped." 570 | elif [[ ! -e "$RUNTIME_DIR/lib/libsqlite3.a" ]] || $FORCE_SQLITE3; then 571 | download_and_extract sqlite-autoconf-$SQLITE3_VERSION.tar.gz \ 572 | http://www.sqlite.org/2014/sqlite-autoconf-$SQLITE3_VERSION.tar.gz 573 | echo "Entering $RUNTIME_DIR/sqlite-autoconf-$SQLITE3_VERSION" 574 | pushd sqlite-autoconf-$SQLITE3_VERSION >/dev/null 575 | 576 | run ./configure --prefix="$RUNTIME_DIR" --disable-shared \ 577 | --disable-dynamic-extensions CFLAGS='-O2 -fPIC -fvisibility=hidden' 578 | run make -j$CONCURRENCY 579 | run make install-strip 580 | echo "Entering $RUNTIME_DIR" 581 | popd >/dev/null 582 | run rm -rf sqlite-autoconf-$SQLITE3_VERSION 583 | else 584 | echo "Already installed." 585 | fi 586 | echo 587 | 588 | header "Compiling runtime libraries 8/$TOTAL_LIBS: liblzma..." 589 | if $SKIP_LIBLZMA; then 590 | echo "Skipped." 591 | elif [[ ! -e "$RUNTIME_DIR/lib/liblzma.5.dylib" ]] || $FORCE_LIBLZMA; then 592 | download_and_extract xz-$XZ_VERSION.tar.bz2 \ 593 | http://tukaani.org/xz/xz-$XZ_VERSION.tar.bz2 594 | echo "Entering $RUNTIME_DIR/xz-$XZ_VERSION" 595 | pushd xz-$XZ_VERSION >/dev/null 596 | 597 | run ./configure --prefix="$RUNTIME_DIR" --disable-static --disable-xz \ 598 | --disable-xzdec --disable-lzmadec --disable-lzmainfo --disable-lzma-links \ 599 | --disable-scripts --disable-doc 600 | run make -j$CONCURRENCY 601 | run make install-strip 602 | 603 | echo "Leaving source directory" 604 | popd >/dev/null 605 | run rm -rf xz-$XZ_VERSION 606 | 607 | run install_name_tool -id "@executable_path/../lib/liblzma.5.dylib" \ 608 | "$RUNTIME_DIR/lib/liblzma.5.dylib" 609 | else 610 | echo "Already installed." 611 | fi 612 | echo 613 | 614 | header "Compiling runtime libraries 9/$TOTAL_LIBS: MySQL..." 615 | if $SKIP_MYSQL; then 616 | echo "Skipped." 617 | elif [[ ! -e "$RUNTIME_DIR/lib/libmysqlclient.a" ]] || $FORCE_MYSQL; then 618 | download_and_extract mysql-connector-c-$MYSQL_LIB_VERSION-src.tar.gz \ 619 | http://dev.mysql.com/get/Downloads/Connector-C/mysql-connector-c-$MYSQL_LIB_VERSION-src.tar.gz 620 | echo "Entering $RUNTIME_DIR/mysql-connector-c-$MYSQL_LIB_VERSION-src" 621 | pushd mysql-connector-c-$MYSQL_LIB_VERSION-src >/dev/null 622 | 623 | # We do not use internal/bin/cc and c++ because MySQL includes 624 | # yassl, which has an OpenSSL compatibility layer. We want the 625 | # yassl headers to be used, not the OpenSSL headers in the runtime. 626 | run cmake -DCMAKE_INSTALL_PREFIX="$RUNTIME_DIR" \ 627 | -DCMAKE_C_COMPILER=/usr/bin/cc \ 628 | -DCMAKE_CXX_COMPILER=/usr/bin/c++ \ 629 | -DCMAKE_C_FLAGS="-fPIC -fvisibility=hidden" \ 630 | -DCMAKE_CXX_FLAGS="-fPIC -fvisibility=hidden" . \ 631 | -DDISABLE_SHARED=1 \ 632 | -DCMAKE_VERBOSE_MAKEFILE=1 633 | run make -j$CONCURRENCY libmysql 634 | run make -C libmysql install 635 | run make -C include install 636 | run make -C scripts install 637 | 638 | echo "Leaving source directory" 639 | popd >/dev/null 640 | run rm -rf mysql-connector-c-$MYSQL_LIB_VERSION-src 641 | else 642 | echo "Already installed." 643 | fi 644 | echo 645 | 646 | header "Compiling runtime libraries 10/$TOTAL_LIBS: PostgreSQL..." 647 | if $SKIP_POSTGRESQL; then 648 | echo "Skipped." 649 | elif [[ ! -e "$RUNTIME_DIR/lib/libpq.a" ]] || $FORCE_POSTGRESQL; then 650 | download_and_extract postgresql-$POSTGRESQL_VERSION.tar.bz2 \ 651 | http://ftp.postgresql.org/pub/source/v9.3.5/postgresql-$POSTGRESQL_VERSION.tar.bz2 652 | echo "Entering $RUNTIME_DIR/postgresql-$POSTGRESQL_VERSION" 653 | pushd postgresql-$POSTGRESQL_VERSION >/dev/null 654 | 655 | run ./configure --prefix="$RUNTIME_DIR" CFLAGS="-O2 -fPIC -fvisibility=hidden" 656 | run make -j$CONCURRENCY -C src/common 657 | run make -j$CONCURRENCY -C src/backend 658 | run make -j$CONCURRENCY -C src/interfaces/libpq 659 | run make -C src/interfaces/libpq install-strip 660 | run make -j$CONCURRENCY -C src/include 661 | run make -C src/include install-strip 662 | run make -j$CONCURRENCY -C src/bin/pg_config 663 | run make -C src/bin/pg_config install-strip 664 | 665 | echo "Leaving source directory" 666 | popd >/dev/null 667 | run rm -rf postgresql-$POSTGRESQL_VERSION 668 | 669 | run rm "$RUNTIME_DIR"/lib/libpq.*dylib 670 | else 671 | echo "Already installed." 672 | fi 673 | echo 674 | 675 | header "Compiling runtime libraries 11/$TOTAL_LIBS: ICU..." 676 | if $SKIP_ICU; then 677 | echo "Skipped." 678 | elif [[ ! -e "$RUNTIME_DIR/lib/libicudata.a" ]] || $FORCE_ICU; then 679 | download_and_extract icu4c-$ICU_DIR_VERSION-src.tgz \ 680 | http://download.icu-project.org/files/icu4c/$ICU_VERSION/icu4c-$ICU_DIR_VERSION-src.tgz 681 | echo "Entering $RUNTIME_DIR/icu4c-$ICU_DIR_VERSION-src" 682 | pushd icu/source >/dev/null 683 | 684 | run ./configure --prefix="$RUNTIME_DIR" --disable-samples --disable-tests \ 685 | --enable-static --disable-shared --with-library-bits=64 \ 686 | CFLAGS="-O2 -fPIC -fvisibility=hidden -DU_CHARSET_IS_UTF8=1 -DU_USING_ICU_NAMESPACE=0" \ 687 | CXXFLAGS="-O2 -fPIC -fvisibility=hidden -DU_CHARSET_IS_UTF8=1 -DU_USING_ICU_NAMESPACE=0" 688 | run make -j$CONCURRENCY VERBOSE=1 689 | run make install -j$CONCURRENCY 690 | 691 | echo "Leaving source directory" 692 | popd >/dev/null 693 | run rm -rf icu 694 | else 695 | echo "Already installed." 696 | fi 697 | echo 698 | 699 | header "Compiling runtime libraries 12/$TOTAL_LIBS: libssh2..." 700 | if $SKIP_LIBSSH2; then 701 | echo "Skipped." 702 | elif [[ ! -e "$RUNTIME_DIR/lib/libssh2.a" ]] || $FORCE_LIBSSH2; then 703 | download_and_extract libssh2-$LIBSSH2_VERSION.tar.gz \ 704 | http://www.libssh2.org/download/libssh2-$LIBSSH2_VERSION.tar.gz 705 | echo "Entering $RUNTIME_DIR/libssh2-$LIBSSH2_VERSION" 706 | pushd libssh2-$LIBSSH2_VERSION >/dev/null 707 | 708 | run ./configure --prefix="$RUNTIME_DIR" --enable-static --disable-shared \ 709 | --with-openssl --with-libz --disable-examples-build --disable-debug \ 710 | CFLAGS="-O2 -fPIC -fvisibility=hidden" \ 711 | CXXFLAGS="-O2 -fPIC -fvisibility=hidden" 712 | run make -j$CONCURRENCY 713 | run make install-strip -j$CONCURRENCY 714 | 715 | echo "Leaving source directory" 716 | popd >/dev/null 717 | run rm -rf libssh2-$LIBSSH2_VERSION 718 | else 719 | echo "Already installed." 720 | fi 721 | echo 722 | 723 | header "All done!" 724 | --------------------------------------------------------------------------------