Swift與Linux
自2015年Swift宣布開源并支持Linux后,一晃已超過5年。在最初幾年間盡管Swift發(fā)展迅速,但并未被Linux社區(qū)廣泛接受。造成這種局面的原因較多,其中既有語言穩(wěn)定性方面的問題,同時又有對Linux的支持不完善、缺乏具有吸引力的基礎(chǔ)庫和第三方庫、熱點項目不足等多方面原因。
最近兩年,上述問題均得到顯著改善。
- 從Swift5開始Swift團隊宣布ABI穩(wěn)定了。自此,Swift為應(yīng)用程序提供了二進制兼容性,有了ABI穩(wěn)定性作為基礎(chǔ),Swift對其他平臺的支持速度和力度將大大提高
- 2020年Swift團隊推出了5.3版本,除了“重要的質(zhì)量提升和性能增強”之外,Swift5.3的一個關(guān)鍵特性是正式支持Linux和Window平臺。事實上,這也是Swift的發(fā)布流程首次為三個不同的平臺分別設(shè)立了發(fā)布負責(zé)人。作為承諾將Swift引入Linux的第一份成果,Swift團隊宣布新的Swift版本可用于一眾Linux發(fā)行版上。
- 出現(xiàn)了大量優(yōu)秀的官方和第三方的跨平臺庫。僅Apple公司,最近幾年來已經(jīng)為Swift社區(qū)貢獻了大量的Swift代碼,并保持著非常高的提交頻率。
- Swift在服務(wù)器端及深度學(xué)習(xí)領(lǐng)域取得了不錯的應(yīng)用成果。
Swift已經(jīng)準備在Linux有更多的表現(xiàn)。
寫本文的原因
前些日子寫了篇用Publish創(chuàng)建博客(一)——入門(一個用Swift編寫的優(yōu)秀的靜態(tài)網(wǎng)站生成器)的介紹,期間有網(wǎng)友問我是否可以在Linux上使用,我回答不成問題。但轉(zhuǎn)過頭來思考,雖然Publish
完美地支持Linux,但開發(fā)者能否像在mac上一樣方便的進行開發(fā)調(diào)試呢?
之前使用Vapor
的時候,曾通過Docker
在Ubuntu上安裝過Swift,不過代碼是在mac上調(diào)試的。我也十分好奇,在2021年Swift到底在Linux下的開發(fā)環(huán)境如何?
本文的目標是在Linux上搭建一個可供生產(chǎn)標準的Swift開發(fā)調(diào)試環(huán)境。使用者將獲得一個支持代碼高亮、自動補全、定義跳轉(zhuǎn)、斷點調(diào)試、代碼美化、代碼靜態(tài)掃描、跨操作系統(tǒng)平臺配置的綜合開發(fā)體驗。
準備
由于每個人使用的Linux發(fā)行版本不同,因此在安裝過程中,如遇到缺少必要依賴的情況,請自行按系統(tǒng)提示安裝所需的依賴庫即可。
本文在描述每一步該如何做的同時,還會做出必要的解釋。即使你使用的是其他的Linux發(fā)行版,或者不同的編輯器,甚至在Swift或其他工具發(fā)生了重大的升級后,仍可按照下面安裝思路進行環(huán)境搭建。
本文搭建的起點是建立在已經(jīng)安裝了Visual Studio Code 1.53.0的Ubuntu 20.04LTS(最小化安裝)系統(tǒng)上的。選擇安裝的Swift Toolchain為5.3.3。
對于Ubuntu 20.04,需安裝python2.7
及npm
以完成下面其他操作。
$ sudo apt install libpython2.7 libpython2.7-dev libz3-4 npm
Swift Toolchain
工具鏈選擇
盡管你可以直接下載Swift Toolchain的源碼自己編譯,但目前最推薦的方式還是使用官方提供的已編譯好的下載包進行安裝。swift.org上提供了Ubuntu 16.04、Ubuntu 18.04、Ubuntu 20.04、CentOS 7、CentOS 8、Amazon Linux 2的下載包。其他的發(fā)行版本也多有自己的官方支持,比如Fodor、Red Hat Enterprise Linux8、Raspbian OSi等
Swift在5.3版本后開始正式支持Linux平臺,所以本文選擇在Ubuntu 20.04上安裝Swift 5.3.3 Release。
安裝
在swift.org上查找對應(yīng)發(fā)行版的Swift Toolchain下載地址
$cd ~
$wget https://swift.org/builds/swift-5.3.3-release/ubuntu2004/swift-5.3.3-RELEASE/swift-5.3.3-RELEASE-ubuntu20.04.tar.gz
解壓文件
$tar -xzf swift-5.3.3-RELEASE-ubuntu20.04.tar.gz
swift工具鏈將被解壓在 ~/swift-5.3.3-RELEASE-ubuntu20.04
目錄中,將該目錄移動到你習(xí)慣的路徑,比如:
$sudo mv swift-5.3.3-RELEASE-ubuntu20.04 /usr/share/swift
請記住移動后的路徑/usr/share/swift
,該路徑將在下面的配置中被多次使用到。
將swift bin的路徑添加到環(huán)境中
$echo "export PATH=/usr/share/swift/usr/bin:$PATH" >> ~/.bash
$source .bash
至此,Swift已經(jīng)在當(dāng)前系統(tǒng)上安裝好了
$swift --version
Swift version 5.3.3 (swift-5.3.3-RELEASE)
Target: x86_64-unknown-linux-gnu
運行第一段代碼
創(chuàng)建hello.swift
,內(nèi)容為
#!/usr/bin/env swift
print("My first swift code")
$cd ~
$swift hello.swift
My first swift code
或者可以將swift代碼當(dāng)做script來執(zhí)行
$chmod +755 hello.swift
$./hellow.swift
My first swift code
創(chuàng)建第一個Swift項目
Swift Package Manager (SPM) 是蘋果推出的用于創(chuàng)建使用swift的庫和可執(zhí)行程序的工具。目前它已經(jīng)是Swift Toolchain的一部分了。
創(chuàng)建可執(zhí)行程序項目
$cd ~
$mdkir MyProject
$cd MyProject
$swift package init --type executable
Creating executable package: MyProject
Creating Package.swift
Creating README.md
Creating .gitignore
Creating Sources/
Creating Sources/MyProject/main.swift
Creating Tests/
Creating Tests/LinuxMain.swift
Creating Tests/MyProjectTests/
Creating Tests/MyProjectTests/MyProjectTests.swift
Creating Tests/MyProjectTests/XCTestManifests.swift
編譯并運行該項目
~/MyProject$swift run
[4/4] Linking MyProject
Hello, world!
該項目在下面的配置中還將使用到。也可以直接使用vscode打開項目文件
~/MyProject$code .
vscode對于Swift已經(jīng)內(nèi)置支持了代碼高亮。
SourceKit-LSP
什么是LSP
LSP
的全程是Language Sever Protocol
,是微軟提出的一項標準化協(xié)議,旨在統(tǒng)一開發(fā)工具與Lanuguage Server
之間的通信。LSP
為支持的語言提供了一套通用的功能集,包括:語法高亮、自動補全、定義跳轉(zhuǎn)、查找引用等等。蘋果公司從2018年開始為Swift社區(qū)提供了LSP的代碼及支持。目前LSP
已經(jīng)被集成到了Swift的工具鏈中。
安裝
盡管Swift ToolChain中已經(jīng)集成了LSP
,但是我們還是需要為vscode
安裝對應(yīng)的插件并進行配置,才能在vscode中使用Swift的LSP功能。
由于Swift LSP插件沒有被放置在vscode
的插件市場中,我們還是需要從蘋果的LSP Github站點上下載
$git clone https://github.com/apple/sourcekit-lsp.git
下載的文件包含全部LSP的代碼和插件代碼,我們只需安裝插件代碼。
$cd sourcekit-lsp/Editors/vscode/
$npm run createDevPackage
編譯成功的插件被放置在 ~/sourcekit-lsp/Editors/vscode/out
目錄中。
配置vscode
通過命令行將插件安裝到vscode上
$cd ~/sourcekit-lsp/Editors/vscode/out
$code --install-extension sourcekit-lsp-vscode-dev.vsix
或者在vscode中選擇該插件進行安裝
配置Settings
由于lsp
已經(jīng)集成到了swift toolchain中,因此在我們安裝toolchain時,它已經(jīng)被安裝到了/usr/share/swift/usr/bin
的目錄中,并且該目錄也已經(jīng)設(shè)置在環(huán)境的PATH中,因此通常無需指定絕對路徑,vscode便可以使用swift的lsp功能。如果你自己單獨下載了新版本的lsp,可以在settings.json
中設(shè)置對應(yīng)的路徑。
"sourcekit-lsp.serverPath": "/usr/share/swift/usr/bin/sourcekit-lsp"
安裝完成后,vscode便可支持代碼自動補全、定義跳轉(zhuǎn)等功能。
LLDB
什么是LLDB
LLDB
調(diào)試器是LLVM
項目的調(diào)試器組件。它構(gòu)建為一組可重用的組件,這些組件廣泛使用LLVM
中的現(xiàn)有庫,例如Clang
表達式解析器和LLVM
反匯編程序。通過LLDB
,讓vscode
擁有了對Swift代碼進行調(diào)試的能力。
安裝
由于Swift Toolchain當(dāng)前已經(jīng)集成了LLDB
,因此我們無需對其進行安裝,只需要安裝vscode的lldb插件即可。
在vscode的插件市場中,安裝CodeLLDB
在settings.json
中指定lldb
的位置
"lldb.library": "/usr/share/swift/usr/lib/liblldb.so"
也可以在settings UI
中設(shè)定
調(diào)試配置文件
在vscode中用lldb對項目進行調(diào)試,需要在項目的.vscode
目錄中針對每個項目分別創(chuàng)建調(diào)試配置文件launch.json
和tasks.json
。
launch.json
是vscode用于調(diào)試的配置文件,比如指定調(diào)試語言環(huán)境,指定調(diào)試類型等等。其作用和XCode
中的target類似。在swift項目中,我們通常會設(shè)置兩個configuration
,一個用于調(diào)試程序,一個用于進行Unit testing
。
$cd MyProject
$code .
在第一次點擊左側(cè)的run
按鈕時,vscode會提示創(chuàng)建launch.json
文件,我們也可以自己手動在.vscode
目錄中創(chuàng)建該文件。
launch.json
{
"version": "0.2.0",
"configurations": [
{
"type": "lldb",
"request": "launch",
"name": "Debug",
"program": "${workspaceFolder}/.build/debug/MyProject",
"args": [],
"cwd": "${workspaceFolder}",
"preLaunchTask": "swift-build"
},
{
"type": "lldb",
"request": "launch",
"name": "unit testing on Linux",
"program": "./.build/debug/MyProjectPackageTests.xctest",
"preLaunchTask": "swift-build-tests"
}
]
}
-
type
用于此啟動配置的調(diào)試器的類型,swift調(diào)試需設(shè)置成lldb
-
request
此啟動配置的請求類型,swift調(diào)試需設(shè)置成launch,
-
name
在調(diào)試啟動配置下拉列表中的顯示名稱
-
program
可執(zhí)行文件的位置。使用
swift build
編譯后(不加realase參數(shù))的執(zhí)行文件被放置在項目目錄${workspaceFolder}/.build/debug/
下,文件名通常為項目名稱(本例為MyProject);swift build -c release
編譯后的執(zhí)行文件放置在${workspaceFolder}/.build/release/
下,文件名為項目名稱(本例為MyProject);unit testing
的可執(zhí)行文件放置在${workspaceFolder}/.build/debug/
,文件名通常為項目名稱PackageTests.xctest
(本例為MyProjectPackageTests.xctest)。請根據(jù)每個項目的名稱、配置設(shè)定該項。 -
args
傳遞給程序的參數(shù)。比如你的項目支持啟動參數(shù)設(shè)定
MyProject name hello
,則args
為["name","hello"]
-
cwd
當(dāng)前工作目錄,用于查找依賴關(guān)系和其他文件
-
preLaunchTask
要在調(diào)試會話開始之前啟動的任務(wù),每個任務(wù)都需要在
tasks.json
中有對應(yīng)的設(shè)定。比如本例中,swift-build
對應(yīng)著tasks.json中的label:swift-build
的task。對于swift項目,在調(diào)試前最常作的工作便是編譯。
tasks.json
{
"version": "2.0.0",
"tasks": [
{
"label": "swift-build",
"type": "shell",
"command":"swift build"
},
{
"label": "swift-build-tests",
"type": "process",
"command": "swift",
"args": [
"build",
"--build-tests"
]
}
]
}
-
lable
同
launch.json
中的preLaunchTask
對應(yīng) -
type
shell
或process
,為了更好的演示,本例中兩種形式都采用了。 -
command
如果
type
為process
,commnad
只能為需要執(zhí)行命令的可執(zhí)行文件名稱(不可帶參數(shù)),在本例中為swift
,如果type
為shell
則可以在command
中直接寫上需要調(diào)用的參數(shù),比如swift build
-
args
對于
type
為process
的情況,需要調(diào)用的參數(shù)在此填寫。在本例中,swift-build-tests
也可以寫成
"label": "swift-build-tests",
"type": "shell",
"command": "swift build --build-tests"
launch.json
和tasks.json
還有很多其他的選項,更多的用法請參閱vscode手冊以及SPM手冊。
現(xiàn)在我們就可以開始對Swift項目進行調(diào)試了
第一次調(diào)試
$cd MyProject
$code .
隨便給main.swift添加點內(nèi)容,比如:
import Foundation
let a = 100
print("Hello, world!\(Int.random(in: 0...100))")
print("a:\(a)")
SwiftFormat
為什么要對代碼Foramt
許多項目都有固定的代碼風(fēng)格,統(tǒng)一的代碼規(guī)范不僅有助于項目的迭代和維護,同時也讓代碼更加美觀和易讀。但并不是每個程序員都能夠掌握并熟練使用項目風(fēng)格約定。通過使用自動化工具完成上述工作是讓人十分愜意的事情。
Swift社區(qū)中有多個Format項目,目前最活躍的有nicklockwood
的swiftformat和Apple
的swift-format。本例中,我們選擇安裝nicklockwood
的swiftformat。兩者的安裝方法類似,相對來說swiftformat
支持的規(guī)則更多,另外同Swift 的版本也不像swift-foramt
那樣需要嚴格綁定。
安裝命令行工具
$cd ~
$git clone https://github.com/nicklockwood/SwiftFormat.git
$cd SwiftFormat
$swift build -c release
$sudo cp ~/SwiftFormat/.build/release/swiftformat /usr/local/bin
$swiftformat --version
0.47.11
安裝vscode插件
swiftformat
、swift-format
以及swiftlint
的vscode插件都是由Valentin Kabel開發(fā)的,他同時還管理、開發(fā)了其他及個vscode下的swift插件,為在vscode上更好的使用swift作出了不小的貢獻。
在插件商店選擇swiftformat對應(yīng)的插件(注意不要選錯)。
在settings.json
中添加
"swiftformat.path": "/usr/local/bin/swiftformat",
"swiftformat.configSearchPaths": [
"./swiftformat",
"~/.swiftformat"
]
swiftformat
將從Swiftformat.configSearchPaths
設(shè)定的路徑中嘗試查找用戶自己創(chuàng)建的配置文件(.swiftformat
),上面的配置為,如果當(dāng)前目錄沒有,則從用戶根目錄上查找。如果都沒有則使用默認配置和規(guī)則。
swiftformat
目前包含50多個規(guī)則,它的文檔做的很好,可以在Rules.md中找到最新的規(guī)則列表及演示。需要注意的是,vscode目前無法正確的響應(yīng)swiftformat
自定義配置中的--indent
,需要在vscode中對indent
做單獨的設(shè)定(我目前采用的是通過EditorConfig for VS Code
做統(tǒng)一設(shè)置)。另外,如果通過swift.options:["--config","~/rules/.swiftformat"]
指定的規(guī)則文件的優(yōu)先級高于swiftformat.path
中的規(guī)則文件。
SwiftLint
讓代碼更規(guī)范
在計算機科學(xué)中,lint是一種工具程序的名稱,它用來標記源代碼中,某些可疑的、不具結(jié)構(gòu)性的段落。它是一種靜態(tài)程序分析工具,最早適用于C語言,在UNIX平臺上開發(fā)出來。后來它成為通用術(shù)語,可用于描述在任何一種計算機程序語言中,用來標記源代碼中有疑義段落的工具。swift社區(qū)中,被使用的最廣泛的就是realm
開發(fā)的SwiftLint。
其實,上面的swiftformat、swift-format都具有l(wèi)int的功能,并且和swiftlint在很多地方的規(guī)則都類似(都基于Github's Swift Style Guide),但各自的特點還是略有不同。
swiftformat更多的表現(xiàn)在對代碼的自動修改上,而swiftlint由于直接hook了Clang和Sourcekit,因此提供了swiftformat所不具備的,代碼錄入階段的實時驗證和提示功能(通常并不使用它的autocorrect
)。
安裝命令行工具
$git clone https://github.com/realm/SwiftLint.git
$cd SwiftLint
$swift build -c release
$sudo cp ~/SwiftLint/.build/release/swiftlint /usr/local/bin
$swiftlint --version
0.42.0
安裝vscode插件
在vscode插件市場中安裝swiftlint插件
在settings.json
中添加
"swiftlint.path": "/usr/local/bin/swiftlint",
"swiftlint.toolchainPath": "/usr/share/swift/usr/bin",
"swiftlint.configSearchPaths": [
"./.swiftlint.yml",
"~/.swiftlint.yml"
]
configSearchPath
的設(shè)置同swiftformat
類似,如果不需要自定義配置,則無需填寫。
跨平臺配置
我們已經(jīng)在Ubuntu 20.04上構(gòu)建了一個較完整的Swift開發(fā)環(huán)境。
settings
如果你也像我一樣使用了vscode的setting同步功能,那么在其他的平臺(比如mac),上述的settings.json將無法正常使用。
為了讓我們構(gòu)建的開發(fā)環(huán)境適應(yīng)多平臺,需要啟用配置的多平臺支持,并且針對不同平臺分別設(shè)定。
安裝platform-settins
插件
修改settings.json
當(dāng)前為:
{
"sourcekit-lsp.serverPath": "/usr/share/swift/usr/bin/sourcekit-lsp",
"lldb.library": "/usr/share/swift/usr/lib/liblldb.so",
"swiftformat.path": "/usr/local/bin/swiftformat",
"swiftformat.configSearchPaths": [
"./swiftformat",
"~/.swiftformat"
],
"swiftlint.path": "/usr/local/bin/swiftlint",
"swiftlint.toolchainPath": "/usr/share/swift/usr/bin",
"swiftlint.configSearchPaths": [
"./.swiftlint.yml",
"~/.swiftlint.yml"
]
}
修改為:
{
"platformSettings.autoLoad": true,
"platformSettings.platforms": {
"linux":{
"sourcekit-lsp.serverPath": "/usr/share/swift/usr/bin/sourcekit-lsp",
"lldb.library": "/usr/share/swift/usr/lib/liblldb.so",
"swiftformat.path": "/usr/local/bin/swiftformat",
"swiftlint.path": "/usr/local/bin/swiftlint",
"swiftlint.toolchainPath": "/usr/share/swift/usr/bin",
},
"Mac":{
"sourcekit-lsp.serverPath": "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/sourcekit-lsp",
"lldb.library": "/Applications/Xcode.app/Contents/SharedFrameworks/LLDB.framework/Versions/A/LLDB",
"swiftformat.path": "/usr/local/bin/swiftformat", //homebrew目前也恰巧安裝在此
"swiftlint.path": "/usr/local/bin/swiftlint", //指向工具的實際路徑
"swiftlint.toolchainPath": "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin",
}
},
"swiftformat.configSearchPaths": [
"./swiftformat",
"~/.swiftformat"
],
"swiftlint.configSearchPaths": [
"./.swiftlint.yml",
"~/.swiftlint.yml"
]
}
launch.json
在mac平臺下,unit testing
的調(diào)用方式也和linux下不同,因此需要在launch.json
中添加一個configuration
,由于使用同一個preLauchchTask
,因此tasks.json
不用改動。
{
"type": "lldb",
"request": "launch",
"name": "Debug tests on macOS",
"program": "/Applications/Xcode.app/Contents/Developer/usr/bin/xctest", //For example /Applications/Xcode.app/Contents/Developer/usr/bin/xctest
"args": [
"${workspaceFolder}/.build/debug/MyProjectPackageTests.xctest"
],
"preLaunchTask": "swift-build-tests"
},
在不同的平臺上,選擇對應(yīng)的target即可。
結(jié)語
希望本文能夠幫助更多的朋友在Linux上使用Swift進行開發(fā)。
我的個人博客肘子的Swift記事本中會有更多關(guān)于Swift、SwiftUI、CoreData的內(nèi)容。