如何閱讀 Swift 標準庫中的原始碼

2021-09-25 19:08:39 字數 4701 閱讀 6685

在進行完 gyp 預處理後,閱讀 swift 標準庫原始碼的最簡單的一種方式是執行一次完整的 swift 編譯。(另一種是寫一小段 shell 指令碼。可以看下面的更新)

如果你想要開始閱讀 swift 原始碼,那它的標準庫應該是首先開始閱讀的地方。標準庫中的**是和每乙個使用 swift 的開發者都息息相關的,如果你也曾經對某個 api 的表現和效能有過懷疑,那麼直接閱讀對應的原始碼會是解決問題最快的方式。

標準庫也是 swift 專案中最容易接觸的地方。其中一點理由是,它由 swift 寫的,而不是 c++。因為你每天都用它,所以對它的 api 也非常熟悉。這就意味著,在原始碼中找到你想要的那段**不是特別困難。如果你只是沒有目標隨便看看,那麼在原始碼中你可能會發現一或者這兩塊金子。

gyb 代表 generate your boilerplate(生成你的樣板檔案)。 它是由 swift

團隊開發的預處理的乙個玩意。如果需要編譯十個非常相似的 int,那就得把相同的**複製貼上十次。如果你開啟某個 gyb

檔案,你會發現其中大部分都是 swift 的**,但是也有一些是 python **。這個預處理器在 swift 的**倉庫中的

utils/gyb,雖然大部分的**在 utils/gyb.py。

— brent royal-gordon

我們不希望看到 太多的 gyb,而更想要 swift **,因為它更具有表達性,但是現在,我們不得不面對它們的混和。

如果你只想要閱讀原始碼(而不是向 swift 貢獻**),gyb 則弊大於利。那麼怎麼來預處理這些檔案呢?你可以直接執行 gyb 指令碼,但是它依賴於乙個被 build 指令碼建立的特殊環境。最好的方式是執行一次完整的 swift build。也許對於閱讀原始碼來說,build 一次可能會有點過了,但是我發現 build 以後,原始碼閱讀起來會更容易一些。

更新: toni suter 指出 gyb 的指令碼只依賴於乙個你可以更改的變數(64-bit 和 32-bit 差別),如果你只想要處理 gyb, 這個小指令碼比完整編譯一次 swift 要好很多。

#!/bin/bash

for f in `ls *.gyb`

do echo "processing $f"

name=$

../../../utils/gyb -d cmake_sizeof_void_p=8 -o $name $f --line-directive ""

done

它會把所有的 .gyb 檔案處理完畢後放到相同的位置並去掉 .gyb 字尾。去除 --line-directive 「」 在處理完畢的檔案中新增 source location 的注釋(就像 swift build 中處理的一樣)。

# install build tools

brew install cmake ninja

# create base directory

mkdir swift-source

cd swift-source

# clone swift

# clone dependencies (llvm, clang, etc.)

./swift/utils/update-checkout --clone

最後一句命令會把 build swift 需要的其它部分的 repo 給 clone 下來,比如 llvm,clang,lldb 等等。就像對於 linux 的 foundation 和 libdispatch 模組一樣。在這一步過後,你的 swift-source 資料夾看起來是這樣:

du -h -d 1

250m ./clang

4,7m ./cmark

47m ./compiler-rt

15m ./llbuild

197m ./lldb

523m ./llvm

221m ./swift

26m ./swift-corelibs-foundation

7,8m ./swift-corelibs-libdispatch

1,1m ./swift-corelibs-xctest

316k ./swift-integration-tests

960k ./swift-xcode-playground-support

7,0m ./swiftpm

1,3g .

具體檢視 swift/readme.md中提供的命令

在這裡可能會出現llvm資料夾為空的情況 ,

報錯 update-checkout failed, fix errors and try again

需要安裝專案依賴.

以下命令是在swift/readme.md中提供的命令。

sudo apt-get install git cmake ninja-build clang python uuid-dev libicu-dev icu-devtools libbsd-dev libedit-dev libxml2-dev libsqlite3-dev swig libpython-dev libncurses5-dev pkg-config libblocksruntime-dev libcurl4-openssl-dev autoconf libtool systemtap-sdt-dev
編譯swift原始碼不帶偵錯程式lldb

utils/build-script預設使用的是帶debug資訊的編譯方式,此種方法非常慢,並且所需的磁碟空間非常大。我花了好幾個小時,20多g的磁碟全占滿了,最後說磁碟不夠用而報錯終止了。

//1.先檢視幫助文件

./swift/utils/build-script -h

//2.所有在編譯時務必加上-r選項,編譯成rlease版本

./utils/build-script -t -r

編譯swift原始碼帶偵錯程式lldb

編譯完成後,輸入如下命令,檢查lldb是否編譯成功。

//編譯swift原始碼帶偵錯程式lldb

utils/build-script -l -b -p --xctest --foundation -r

//編譯完成後,輸入如下命令,檢查lldb是否編譯成功

../build/ninja-releaseassert/lldb-linux-x86_64/bin/lldb –version

如果自己編譯工具鏈,參考:

現在就可以開始執行 build 指令碼了,它會先開始 build llvm,然後構建 swift:

./swift/utils/build-script -x -r
引數是很重要的:

當 build 構建完成後,你可以在./build/xcode-releaseassert/swift-macosx-x86_64/的子資料夾中 swift-source 找到結果。其中會有乙個 swift.xcodeproj xcode 專案,已經處理好的標準庫**在./stdlib/public/code/8/中。注意,這個資料夾中只有從 .gyb 檔案處理過來的檔案,之前以 .swift 結尾的檔案還在原來的位置。

不幸的是, 在 xcode 中使用 open quickly (⇧⌘o) 打不開特定的 api. 我通常使用 find in project (⇧⌘f) 來進行導航。如果你使用只出現在函式定義時的字串來搜尋,那就很容易搜尋到了。比如要搜尋 print 函式的定義,搜尋 「func print(「 而不是 「print」。

你也可以執行 swift repl 或者 swiftc 編譯器了。它們都在 ./release/bin/ 中。如果還想測試一些之前在 release **現,但是在 master 中已經被修復的 bug,就會很方便。

如果你想要驗證乙個在生產環境中已經使用的 swift 特定版本 api 的表現,你就需要檢視那個版本的 swift **,而不是當前 master 分支。但是簡單地切換分支並不能解決問題,因為如果一些依賴的版本對不上的話,編譯是會失敗的。

update-checkout指令碼能夠讓你指定乙個特定的 tag 或者 branch。它會幫你切換所有依賴的版本:

# either

./swift/utils/update-checkout --tag swift-3.0-release

# or

./swift/utils/update-checkout --scheme swift-3.0-branch

swift-3.0-release tag 和 swift-3.0-branch branch 的區別是,tag 相當於乙個 mailstone,代表 swift 的某個特定的 release 分支。然而分支是會伴隨著 bug 修復和功能改進不斷更新的。現在來看,在官方 release xcode 8.1 的 swift 3.0.1 時,swift-3.0-branch 分支已經包含了一些 swift 3.0.2 中的修復。

不幸的是,我發現 update-checkout --scheme 的命令非常脆弱(–tag 在我看來能好一些)。這個指令碼會對**進行 rebase 操作,並且切換到指定的分支,這會在子專案中帶來合併衝突,然而我並沒有對**作出任何更改。我不明白為什麼這個指令碼會這樣。

C標準庫的閱讀(1)

c標準庫的閱讀 1 assert.h 1,基本內容 assert.h 裡面提供了乙個巨集 assert exp 這個巨集指向另乙個巨集 ndebug 如果你在include標頭檔案之前,定義了這個巨集 那麼 assert exp 的定義就是 void 0 意思是什麼也不做,相當與關閉斷言的功能,如果...

C 中的標準庫與非標準庫的區別

c 中我們要用到標準輸出,就需要呼叫cout,那麼,cout這條語句怎麼使用呢?例如 include 標頭檔案 int main std cout hello world include int main cout hello world 從這兩個函式來看都沒多大的區別,最後都是輸出 hello w...

標準庫中的名字(std cout)

includeint main std cout enter two numbers v1 v2 std cout the sum of 這段 使用了std cout和std endl 而不是直接用cout和endl,字首std 指出名字cout和endl是定義在std的命名空間 namespace...