├── LICENSE ├── Makefile ├── README.md ├── bin └── chswift-exec ├── share └── chswift │ ├── auto.sh │ └── chswift.sh └── test ├── helper.sh ├── list_test.sh ├── runner └── switch_test.sh /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 Boris Bügling 2 | Copyright (c) 2012-2013 Hal Brodigan 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining 5 | a copy of this software and associated documentation files (the 6 | 'Software'), to deal in the Software without restriction, including 7 | without limitation the rights to use, copy, modify, merge, publish, 8 | distribute, sublicense, and/or sell copies of the Software, and to 9 | permit persons to whom the Software is furnished to do so, subject to 10 | the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be 13 | included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 19 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 20 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 21 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: install test 2 | 3 | DIRS=etc lib bin sbin share 4 | INSTALL_DIRS=`find $(DIRS) -type d 2>/dev/null` 5 | INSTALL_FILES=`find $(DIRS) -type f 2>/dev/null` 6 | 7 | install: 8 | for dir in $(INSTALL_DIRS); do mkdir -p $(DESTDIR)$(PREFIX)/$$dir; done 9 | for file in $(INSTALL_FILES); do cp $$file $(DESTDIR)$(PREFIX)/$$file; done 10 | 11 | test: 12 | SHELL=`command -v bash` ./test/runner 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # chswift 2 | 3 | [![No Maintenance Intended](http://unmaintained.tech/badge.svg)](http://unmaintained.tech/) 4 | 5 | The missing Swift version manager. 6 | 7 | ## Installation 8 | 9 | ```bash 10 | $ brew tap neonichu/formulae 11 | $ brew install chswift 12 | ``` 13 | 14 | Amend configuration as advised by the installation procedure. 15 | 16 | ## Usage 17 | 18 | Running without any arguments will show installed Swift versions and the currently selected one: 19 | 20 | ```bash 21 | $ chswift 22 | * 1.1 (swift-600.0.57.4) 23 | 1.2 (swiftlang-602.0.47.4 clang-602.0.48) 24 | ``` 25 | 26 | Specify a version number to change the current Swift: 27 | 28 | ```bash 29 | $ chswift 1.2 30 | $ swift --version 31 | Apple Swift version 1.2 (swiftlang-602.0.47.4 clang-602.0.48) 32 | Target: x86_64-apple-darwin14.3.0 33 | ``` 34 | 35 | Fall back to the Swift defined by your `xcode-select` configuration: 36 | 37 | ```bash 38 | $ chswift system 39 | ``` 40 | 41 | You can also configure a local environment in *.swift-version* files, which contain only the the version you want to use. To use Swift 1.2, do the following in your directory: 42 | 43 | ```bash 44 | $ echo 1.2 >.swift-version 45 | ``` 46 | 47 | You can verify it's working using the following. 48 | 49 | ```bash 50 | $ swift --version 51 | Apple Swift version 1.2 (swiftlang-602.0.47.4 clang-602.0.48) 52 | Target: x86_64-apple-darwin14.3.0 53 | ``` 54 | 55 | ### Managing Swift Versions 56 | 57 | In order to have multiple Swift versions listed when you run chswift, you will need to have multiple Xcode versions running. The simplest way is to use [xcode-install](https://github.com/neonichu/xcode-install). If you rely solely on the Mac App Store, updates will change your system's Swift version. 58 | 59 | ## Testing 60 | 61 | ```bash 62 | $ brew install shunit2 63 | $ make test 64 | ``` 65 | 66 | ## Thanks 67 | 68 | This project is based on ideas and code of the amazing [chruby][1]. If 69 | you are also in the market for a Ruby chooser, you have found it. 70 | 71 | 72 | [1]: https://github.com/postmodern/chruby 73 | -------------------------------------------------------------------------------- /bin/chswift-exec: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | chswift_sh="${0%/*}/../share/chswift/chswift.sh" 4 | source "$chswift_sh" 5 | 6 | case "$1" in 7 | -h|--help) 8 | echo "usage: chswift-exec SWIFT -- COMMAND [ARGS...]" 9 | exit 10 | ;; 11 | -V|--version) 12 | echo "chswift version $CHSWIFT_VERSION" 13 | exit 14 | ;; 15 | esac 16 | 17 | if (( $# == 0 )); then 18 | echo "chswift-exec: SWIFT and COMMAND required" >&2 19 | exit 1 20 | fi 21 | 22 | argv=() 23 | 24 | for arg in "$@"; do 25 | shift 26 | 27 | if [[ "$arg" == "--" ]]; then break 28 | else argv+=($arg) 29 | fi 30 | done 31 | 32 | if (( $# == 0 )); then 33 | echo "chswift-exec: COMMAND required" >&2 34 | exit 1 35 | fi 36 | 37 | shell_opts=("-l") 38 | [[ -t 0 ]] && shell_opts+=("-i") 39 | 40 | source_command="command -v chswift >/dev/null || source $chswift_sh" 41 | chswift_command="chswift $(printf "%q " "${argv[@]}")" 42 | sub_command="$(printf "%q " "$@")" 43 | command="$source_command; $chswift_command && $sub_command" 44 | 45 | exec "$SHELL" "${shell_opts[@]}" -c "$command" 46 | -------------------------------------------------------------------------------- /share/chswift/auto.sh: -------------------------------------------------------------------------------- 1 | unset SWIFT_AUTO_VERSION 2 | 3 | function chswift_auto() { 4 | local dir="$PWD/" version 5 | 6 | until [[ -z "$dir" ]]; do 7 | dir="${dir%/*}" 8 | 9 | if { read -r version <"$dir/.swift-version"; } 2>/dev/null || [[ -n "$version" ]]; then 10 | version="${version%%[[:space:]]}" 11 | 12 | if [[ "$version" == "$SWIFT_AUTO_VERSION" ]]; then return 13 | else 14 | SWIFT_AUTO_VERSION="$version" 15 | chswift "$version" 16 | return $? 17 | fi 18 | fi 19 | done 20 | 21 | if [[ -n "$SWIFT_AUTO_VERSION" ]]; then 22 | chswift_reset 23 | unset SWIFT_AUTO_VERSION 24 | fi 25 | } 26 | 27 | if [[ -n "$ZSH_VERSION" ]]; then 28 | if [[ ! "$preexec_functions" == *chswift_auto* ]]; then 29 | preexec_functions+=("chswift_auto") 30 | fi 31 | elif [[ -n "$BASH_VERSION" ]]; then 32 | trap '[[ "$BASH_COMMAND" != "$PROMPT_COMMAND" ]] && chswift_auto' DEBUG 33 | fi 34 | -------------------------------------------------------------------------------- /share/chswift/chswift.sh: -------------------------------------------------------------------------------- 1 | CHSWIFT_VERSION="0.2.0" 2 | 3 | DEV_DIR="Contents/Developer" 4 | TOOL_DIR="Toolchains/XcodeDefault.xctoolchain/usr/bin" 5 | 6 | SWIFTS=() 7 | XCODES=() 8 | OLDIFS=$IFS 9 | IFS=$'\n' 10 | for dir in $(mdfind "kMDItemCFBundleIdentifier == 'com.apple.dt.Xcode'" 2>/dev/null); do 11 | [[ -d "$dir" && -n "$(ls -A "$dir/$DEV_DIR")" ]] && XCODES+=("$dir/$DEV_DIR") 12 | done 13 | for xcode in $XCODES; do 14 | for dir in $(ls -d "$xcode/Toolchains/Swift"_* 2>/dev/null); do 15 | [[ -d "$dir" && ! -L "$dir" ]] && SWIFTS+=("$dir/usr/bin") 16 | done 17 | done 18 | for dir in $(ls -d /Library/Developer/Toolchains/swift-* 2>/dev/null); do 19 | [[ -d "$dir" && ! -L "$dir" ]] && SWIFTS+=("$dir/usr/bin") 20 | done 21 | IFS=$OLDIFS 22 | unset dir 23 | 24 | function chswift_list() 25 | { 26 | local dir star selected has_star 27 | 28 | for dir in "${SWIFTS[@]}"; do 29 | if [[ "$dir" == "$CHSWIFT_TOOLCHAIN" ]]; then star="*"; has_star="y" 30 | else star=" " 31 | fi 32 | 33 | echo " $star $(swift_version "$dir")" 34 | done 35 | 36 | if [ -z "$has_star" ] 37 | then 38 | selected="$("xcode-select" -p)" 39 | fi 40 | 41 | for dir in "${XCODES[@]}"; do 42 | if [[ "$dir" == "${selected%/}" ]]; then star="*" 43 | else star=" " 44 | fi 45 | 46 | echo " $star $(swift_version "$dir")" 47 | done 48 | } 49 | 50 | function chswift_reset() 51 | { 52 | [[ -z "$DEVELOPER_DIR" && -z "$CHSWIFT_TOOLCHAIN" ]] && return 53 | 54 | PATH=":$PATH:"; PATH="${PATH//:$DEVELOPER_DIR\/$TOOL_DIR:/:}" 55 | PATH="${PATH//:$CHSWIFT_TOOLCHAIN:/:}" 56 | PATH="${PATH#:}"; PATH="${PATH%:}" 57 | unset DEVELOPER_DIR 58 | hash -r 59 | } 60 | 61 | function chswift_use() 62 | { 63 | if [[ ! -x "$1/$TOOL_DIR/swift" ]]; then 64 | chswift_use_toolchain "$1" 65 | return 0 66 | fi 67 | 68 | chswift_reset 69 | 70 | export DEVELOPER_DIR="$1" 71 | export PATH="$DEVELOPER_DIR/$TOOL_DIR:$PATH" 72 | 73 | hash -r 74 | } 75 | 76 | function chswift_use_toolchain() 77 | { 78 | if [[ ! -x "$1/swift" ]]; then 79 | echo "chswift: $1/$TOOL_DIR/swift not executable" >&2 80 | return 1 81 | fi 82 | 83 | chswift_reset 84 | 85 | export CHSWIFT_TOOLCHAIN="$1" 86 | export PATH="$CHSWIFT_TOOLCHAIN:$PATH" 87 | 88 | hash -r 89 | } 90 | 91 | function swift_version() 92 | { 93 | if [ -x "$1/usr/bin/xcodebuild" ]; then 94 | DEVELOPER_DIR="$1" xcrun swift --version 95 | else 96 | $1/swift --version 97 | fi|head -n 1|perl -pe 's/.*? ([^ ]+ \(.*?\))/$1/' 98 | } 99 | 100 | function chswift() 101 | { 102 | case "$1" in 103 | -h|--help) 104 | echo "usage: chswift [SWIFT|VERSION|system]" 105 | ;; 106 | -V|--version) 107 | echo "chswift: $CHSWIFT_VERSION" 108 | ;; 109 | "") 110 | chswift_list|sort -n 111 | ;; 112 | system) chswift_reset ;; 113 | *) 114 | local dir match arg 115 | arg="`echo "$1"|sed -e 's/swift-//g' -e 's/-.*//g'`" 116 | for dir in "${XCODES[@]}" "${SWIFTS[@]}"; do 117 | case "$(swift_version "$dir")" in 118 | "$arg") match="$dir" && break ;; 119 | "$arg"*) match="$dir" ;; 120 | esac 121 | done 122 | 123 | if [[ -z "$match" ]]; then 124 | echo "chswift: unknown Swift: $1" >&2 125 | return 1 126 | fi 127 | 128 | shift 129 | chswift_use "$match" "$*" 130 | ;; 131 | esac 132 | } 133 | -------------------------------------------------------------------------------- /test/helper.sh: -------------------------------------------------------------------------------- 1 | [[ -z "$SHUNIT2" ]] && SHUNIT2=/usr/local/bin/shunit2 2 | [[ -n "$ZSH_VERSION" ]] && setopt shwordsplit 3 | 4 | export PREFIX="$PWD/test" 5 | export HOME="$PREFIX/home" 6 | export PATH="$PWD/bin:$PATH" 7 | 8 | export LIST=" 2.2-dev (LLVM 3ebdbb2c7e, Clang f66c5bb67b, Swift 17fe37d715)\n 2.3 (swiftlang-800.10.11 clang-800.0.36)\n 3.0 (swiftlang-800.0.42.1 clang-800.0.36.1)\n 3.0-dev (LLVM c191431197, Clang c6195325c5, Swift add621a959)" 9 | export VERSION_3_0="Apple Swift version 3.0 (swiftlang-800.0.42.1 clang-800.0.36.1)" 10 | export VERSION_2_3="Apple Swift version 2.3 (swiftlang-800.10.11 clang-800.0.36)" 11 | export VERSION_DEV="Apple Swift version 2.2-dev (LLVM 3ebdbb2c7e, Clang f66c5bb67b, Swift 17fe37d715)" 12 | export TOOLCHAINS="/Applications/Xcode-8.app/Contents/Developer/Toolchains/Swift_2.3.xctoolchain/usr/bin" 13 | 14 | . ./share/chswift/chswift.sh 15 | chswift_reset 16 | 17 | setUp() { return; } 18 | tearDown() { return; } 19 | oneTimeTearDown() { return; } 20 | -------------------------------------------------------------------------------- /test/list_test.sh: -------------------------------------------------------------------------------- 1 | . ./test/helper.sh 2 | 3 | function setUp() { 4 | chswift_reset 5 | } 6 | 7 | function test_chswift_find_toolchains() { 8 | assertEquals "wrong toolchains" "$TOOLCHAINS" "$SWIFTS" 9 | } 10 | 11 | function test_chswift_list() { 12 | expected=$(echo -e "$LIST"|tr '*' ' '|sort -n|tr -d '\n') 13 | output=$(chswift|tr '*' ' '|sort -n|tr -d '\n') 14 | assertEquals "wrong list" "$expected" "$output" 15 | } 16 | 17 | function test_chswift_list_robustness() { 18 | export DEVELOPER_DIR="/Applications/Xcode.app/Contents/Developer/" 19 | expected=$(echo -e "$LIST"|tr '*' ' '|sort -n|tr -d '\n') 20 | output=$(chswift|tr '*' ' '|sort -n|tr -d '\n') 21 | assertEquals "wrong list" "$expected" "$output" 22 | } 23 | 24 | function test_chswift_list_marks_selected() { 25 | assertEquals "does not mark selected" "1" "$(chswift|grep -c '*')" 26 | } 27 | 28 | SHUNIT_PARENT=$0 . $SHUNIT2 29 | -------------------------------------------------------------------------------- /test/runner: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | function log() { 4 | if [[ -t 1 ]]; then 5 | printf "\x1b[1m\x1b[32m>>>\x1b[0m \x1b[1m%s\x1b[0m\n" "$1" 6 | else 7 | printf ">>> %s\n" "$1" 8 | fi 9 | } 10 | 11 | error=0 12 | 13 | ## FIXME: Somehow the second test stops the process in interactive 14 | #for test in ${0%/*}/*_test.sh; do 15 | #log "Running $test under $SHELL (interactive) ..." 16 | #"$SHELL" $* -i "$test" || error=1 17 | #echo 18 | #done 19 | 20 | for test in ${0%/*}/*_test.sh; do 21 | log "Running $test under $SHELL (non-interactive) ..." 22 | "$SHELL" $* "$test" || error=1 23 | echo 24 | done 25 | 26 | exit "$error" 27 | -------------------------------------------------------------------------------- /test/switch_test.sh: -------------------------------------------------------------------------------- 1 | . ./test/helper.sh 2 | 3 | function setUp() { 4 | chswift_reset 5 | } 6 | 7 | function test_chswift_reset() { 8 | chswift 2.3 9 | chswift_reset 10 | assertEquals "wrong version" "$VERSION_3_0" "$(swift --version|head -n 1)" 11 | } 12 | 13 | function test_chswift_reset_toolchain() { 14 | CHSWIFT_TOOLCHAIN=/yolo/bin 15 | PATH=$CHSWIFT_TOOLCHAIN:/bin:/usr/bin 16 | assertEquals "invalid setup" "/yolo/bin:/bin:/usr/bin" "$PATH" 17 | chswift_reset 18 | assertEquals "PATH not reset" "/bin:/usr/bin" "$PATH" 19 | } 20 | 21 | function test_chswift_switch() { 22 | chswift 2.3 23 | assertEquals "wrong version" "$VERSION_2_3" "$(swift --version|head -n 1)" 24 | } 25 | 26 | function test_chswift_switch_toolchain() { 27 | chswift 2.2-dev 28 | assertEquals "wrong version" "$VERSION_DEV" "$(swift --version|head -n 1)" 29 | } 30 | 31 | function test_chswift_switch_toolchain_one_star_only() { 32 | chswift 2.2 33 | assertEquals "wrong list" "1" "$(chswift|grep -c '*')" 34 | } 35 | 36 | function test_chswift_swiftenv_compatibility() { 37 | chswift swift-2.2-SNAPSHOT-2015-12-22-a 38 | assertEquals "wrong version" "$VERSION_DEV" "$(swift --version|head -n 1)" 39 | } 40 | 41 | SHUNIT_PARENT=$0 . $SHUNIT2 42 | --------------------------------------------------------------------------------