建立 Fuchsia 的靜態分析工具

Shac (可擴充的遺傳分析與檢查) 是統合且符合人體工學的工具 用於編寫和執行靜態分析檢查的架構這項工具的來源 請參閱 shac-documentationShac 檢查是以 Starlark

設定

Shac 指令碼實作位於 Fuchsia 的 //scripts/shac 目錄中。

  • SHA-C 檢查已實作為星系函式,且會經過 引數。使用這個 ctx 引數存取 shac 標準程式庫。
  • 如果檢查項目僅適用於特定語言,則應使用其中一種語言 特定檔案 (例如 rust.stargo.starfidl.star)。新增語言 但不含 language.star 檔案,請先建立檔案。如果是一般問題 使用 title.star (標題是檢查函式的名稱)。

簡易範例

以下範例是靜態分析工具,適用於所有建立 非封鎖性、威脅性警告留言,指出「http://」字串有所變更 ,將使用者改為使用「https://」。

def http_links(ctx):
    for path, meta in ctx.scm.affected_files().items():
        for num, line in meta.new_lines():
            matches = ctx.re.allmatches(r"(http://)\w+", line)
            if not matches:
                continue
            for match in matches:
                ctx.emit.finding(
                    message = "Avoid http:// links, prefer https://",
                    # Change to "error" if the check should block presubmit.
                    level = "warning",
                    filepath = path,
                    line = num,
                    col = match.offset + 1,
                    end_col = match.offset + 1 + len(match.groups[1]),
                    replacements = ["https://"],
                )

進一步瞭解 shac 實作 emit.findings

load("./http_links.star", "http_links")  # NEW

...

def register_all_checks():
    ...
    shac.register_check(http_links)  # NEW
    ...

進階範例

如果有現有工具可以檢查 。 Starlark 刻意限制功能,因此撰寫過程相當複雜 和自身的單元測試獨立工具的商業邏輯。

以下範例說明在單獨的 Python 中實作的 JSON 格式器 並以子程序的形式執行

這項檢查會計算格式化檔案,而不是重新撰寫格式錯誤的檔案 並傳遞到標記的 replacements 引數 ctx.emit.finding() 函式。所有的格式檢查都必須執行 ,基於以下原因:

  • 檢查作業執行的子程序不得寫入結帳中的檔案 目錄。這樣可以防止運作不良的工具產生非預期的變更 可確保能同時執行多項檢查,避免發生競速 條件。(請注意,只有 Linux 會強制執行檔案系統沙箱)。
  • Shac 的設計可輕鬆與 提議向使用者 (例如 Gerrit) 進行變更,而不要自動 因此,若要使用這些應用範例進行差異比較,您必須 傳遞至 shac,而非子程序套用。
import json
import sys


def main():
    # Accepts one positional argument referring to the file to format.
    path = sys.args[1]
    with open(path) as f:
        original = f.read()
    # Always use 2-space indents and a trailing blank line.
    formatted = json.dumps(json.loads(original), indent=2) + "\n"
    if formatted == original:
        sys.exit(0)
    else:
        print(json.dumps(doc, indent=2) + "\n")
        sys.exit(1)


if __name__ == "__main__":
    main()
load("./common.star", "FORMATTER_MSG", "cipd_platform_name", "get_fuchsia_dir", "os_exec")

def json_format(ctx):
    # Launch processes in parallel.
    procs = {}
    for f in ctx.scm.affected_files():
        if not f.endswith(".json"):
            continue
        # Call fuchsia-specific `os_exec` function instead of
        # `ctx.os.exec()` to ensure proper executable resolution.
        # `os_exec` starts the subprocess but does not block.
        procs[f] = os_exec(ctx, [
            "%s/prebuilt/third_party/python3/%s/bin/python3" % (
                get_fuchsia_dir(ctx),
                cipd_platform_name(ctx),
            ),
            "scripts/shac/json_format.py",
            f,
        ])

    for f, proc in procs.items():
        # wait() blocks until the process completes.
        res = proc.wait()
        if proc.retcode != 0:
            ctx.emit.finding(
                level = "error",
                filepath = f,
                # FORMATTER_MSG is the standard message for formatters
                # in fuchsia.git.
                message = FORMATTER_MSG,
                # json_format.py prints the formatted file contents to stdout.
                # Passing it to `replacements` is necessary for shac to know
                # how to apply the fix.
                replacements = [res.stdout],
            )

# TODO: call this somewhere
shac.register_check(shac.check(
    json_format,
    # Mark the check as a formatter. Only checks with `formatter = True`
    # get run by `fx format-code`.
    formatter = True,
))
成效最佳化

部分格式設定工具內建支援驗證多數格式設定 檔案通常會在內部平行處理 比起啟動獨立的子程序來檢查每個檔案,速度更快。在本例中 您可以對「check」中的所有檔案執行一次格式設定工具取得一系列 然後只反覆查看格式不正確的檔案 取得格式化結果 (而非針對所有檔案進行疊代)。

範例:針對 rustfmt,請先執行 rustfmt --check --files-with-diff <all rust files> 以取得格式錯誤檔案的清單,然後執行 rustfmt 分別傳回各個檔案,藉此取得格式化結果。

如果格式化工具沒有模擬測試模式,無法將格式化結果 stdout:格式設定工具子程序無法寫入結帳。 不過,有些格式設定工具可無條件寫入檔案。在這種情況下 為每個檔案複製到 tempdir 中,子程序可寫入該 tempdir,格式為 並回報其內容,如建構工具所示。

根據預設,如果子程序產生無法復原的錯誤,os_exec 會發出無法復原的錯誤 傳回非零的傳回代碼如果預期的傳回代碼並非零,您可以使用 ok_retcodes 參數,例如:ok_retcodes = [0, 1]可能適合 如果檔案未格式化,formatter 會產生 1 的傳回代碼。

在本機執行檢查

開發本機檢查期間,建議您執行 直接透過 fx host-tool shac check <file> 執行 SHA-C。我們先建立一個情境 我們可以測試上述的 http_links 檢查:

  1. 找出目前違反檢查的檔案,或是建立新檢查 (如果有的話) 不存在,例如:echo "http://example.com" > temp.txt
  2. fx host-tool shac check --only http_links temp.txt
    • 這個指令應會失敗,並顯示「http://」檔案內容重要留言
    • --only 會使 Shac 僅執行 http_links 檢查,不包括其他 因為我們在這裡只關心 http_links 和 不關心其他檢查的執行結果
  3. fx host-tool shac fix --only http_links temp.txt 應將 http:// 改為 https://
  4. fx host-tool shac check --only http_links temp.txt現在應會略過
  5. fx host-tool shac check --only http_links --all
    • 對樹狀結構中的所有檔案執行 (除了 //shac.textproto),而不只是變更的檔案
    • 如果這項作業因發生錯誤而失敗,請修正 可能出現在相同修訂版本或個別修訂版本中 (建議修正超過 10 個檔案) 檢查。
      • 或者,您也可以將檢查標示為非封鎖並修正錯誤,然後修正錯誤,然後 切換為封鎖
    • 如果檢查發出警示,請注意當中有多少警示。如果有 這個數字相當龐大 (超過 100 秒),可能會造成許多雜訊 大量提供意見,可能會對其他貢獻者造成負面影響。請考慮 預先進行大量修正,藉此縮小檢查範圍,或者重新評估 查看的實用程度
  6. 最後,將支票上傳至 Gerrit、執行預先提交、檢查失敗 失敗目標為 0(預先提交的行為與執行 fx host-tool shac check --all 相同)

建議您記錄檢查程序,確認系統是否已選擇加入 (不預先提交),或另有沒有明確清單 選擇不採用機制所有說明文件都應新增至「//docs/development/source_code/presubmit_checks.md