中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

如何進行Bazel中的自定義工具鏈分析

發布時間:2021-12-08 15:26:33 來源:億速云 閱讀:199 作者:柒染 欄目:大數據

如何進行Bazel中的自定義工具鏈分析,相信很多沒有經驗的人對此束手無策,為此本文總結了問題出現的原因和解決方法,通過這篇文章希望你能解決這個問題。


 

1 前言

小編講述 Bazel 自定義工具鏈的兩種方式,PlatformNon-Platform 方式。會存在這兩種方式的原因是 Bazel 的歷史問題。

例如,C++ 相關規則使用 --cpu--crosstool_top 來設置一個構建目標 CPUC++ 工具鏈,這樣就可以實現選擇不同的工具鏈構建 C++ 項目。但是這都不能正確地表達出“平臺”特征。使用這種方式不可避免地導致出現了笨拙且不準確的構建 APIs。這其中導致了對 Java 工具鏈基本沒有涉及,Java 工具鏈就發展了他們自己的獨立接口 --java_toolchain。因此非平臺方式(Non-Platform)的自定義工具鏈實現并沒有統一的 APIs 來規范不同語言的跨平臺構建。而 Bazel 的目標是在大型、混合語言、多平臺項目中脫穎而出。這就要求對這些概念有更原則的支持,包括清晰的 APIs,這些 API 綁定而不是分散語言和項目。這就是新平臺(platform)和工具鏈(toolchain) APIs 所實現的內容。

如果沒有去了解 PlatformNon-Platform 方式區別,可能會對上面說的內容有點不理解,這里通俗的來講下這兩者區別。比如我們編譯 C++Java 混合的相關項目,這個項目需要在多個平臺下可以運行,因此涉及到多個平臺下的工具鏈,而 C++Java 的工具鏈是不一樣的,非平臺方式,對于 C++,我們需要通過 --crosstool_top 來指定工具鏈集合,--cpu 來指定具體的某設備工具鏈;對于 Java,則需要通過 --java_toolchain--host_java_toolchain--javabase--host_javabase 來構建 Java 相關內容。這樣一個 C++Java 的混合項目,需要指定這么多的輸入才能夠完整編譯項目。

如果用了平臺方式,那就簡單了。首先理解平臺概念很簡單,平臺就是一組約束值(constraint_value)的集合,即比如一個平臺可以由 OSCPU 兩個約束類型來決定,又或者一個平臺可以由 OSCPUGLibc_Version 來決定。則我們可以將 C++ 相關編譯的平臺約束綁定平臺,將 Java 相關編譯的平臺約束也綁定平臺,這樣就可以將混合語言項目統一到一個平臺,即一旦確定了某個平臺,那么只需要在命令行執行類似如下命令即可編譯混合語言項目:

$ bazel build //:my_mixed_project --platforms==//:myplatform
 

目前平臺方式構建在 Bazel 中并不完善。這些 APIs 不足以讓所有項目都使用平臺。Bazel 還必須淘汰舊的 APIs。這不是很容易就完成的任務,因為項目的所有語言、工具鏈、依賴項和 select() 都必須支持新的 APIs。這需要一個有序的遷移順序來保持項目正常工作。Bazel 的 C++ 相關規則已經支持平臺,而 Android 相關規則不支持。你的 C++ 項目可能不關心 Android,但其他人可能會。因此,在全球范圍內啟用所有 C++ 平臺構建方式是不安全的。已經完整支持平臺構建方式的有:

  • C/C++
  • Rust
  • Go
  • Java

未來 Bazel 的目標是實現 $ bazel build //:all,即一個命令行就可以構建任何項目和目標平臺。

 

2 Non-Platform 方式

通過上一章節的介紹,Non-Platform 方式,則是通過各項目性質采用對應的獨立構建方式,比如 C++ 相關的 --crosstool_top--cpuJava 相關的 --java_toolchain--host_java_toolchain--javabase--host_javabase。這一節我們僅僅實現 C++Non-Platform 方式構建(當然完整的平臺構建方式并未完善,比如 Apple、Android 都還未支持平臺構建方式)。

在 Bazel 的官方文檔中有一個教程已經詳細地介紹了如何去配置一個 C++ 工具鏈,具體見 https://docs.bazel.build/versions/master/tutorial/cc-toolchain-config.html ,主要涉及的內置規則有:cc_commoncc_toolchaincc_toolchain_suite

當然這里可以進一步去做一些工程上的優化:

  • 生成     CcToolchainConfigInfo 的規則,可以優化其輸入配置,使得寫一個工具鏈配置規則即可配置所有主流的     C++ 編譯器    
    attrs = {
        "include_paths" : attr.string_list(doc = "The compiler include directories."),
        "compiler_root" : attr.string(doc = "The compiler root directory."),
        "host_os" : attr.string(default = "linux", doc = "The cross toolchain prefix."),
        "toolchain_identifier": attr.string(),
        "target_os" : attr.string(default = "linux"),
        "target_arch" : attr.string(default = "x86-64"), 
        "cc_compiler" : attr.string(default = "gcc", doc = "The compiler type."),
        "extra_features": attr.string_list(),
    }
  • 優化多工具配置生成,可以僅僅通過一個所有工具鏈配置文件自動生成所有工具鏈集合,從而方便命令行通過     --cpu 可以切換到某個工具鏈    
    def generate_toolchain_suite():
        toolchains = {}
        native.filegroup(name = "empty")

        for (platform, toolchain_info) in TOOLCHAIN_SUPPORT_MATRIX.items():
            host_os = toolchain_info[TOOLCHAIN_HOST_OS]
            target_os = toolchain_info[TOOLCHAIN_TARGET_OS]
            target_arch = toolchain_info[TOOLCHAIN_TARGET_ARCH]
            compiler_root = toolchain_info[TOOLCHAIN_COMPILER_ROOT]
            include_paths = toolchain_info[TOOLCHAIN_INCLUDE_PATHS]
            toolchain_identifier = toolchain_info[TOOLCHAIN_IDENTIFIER]
            cc_compiler = toolchain_info[TOOLCHAIN_CC_COMPILER]

            base_name = "{platform}_{target_os}_{target_arch}_{cc_compiler}_{toolchain_identifier}".format(
                platform = platform,
                target_os = target_os,
                target_arch = target_arch,
                cc_compiler = cc_compiler,
                toolchain_identifier = toolchain_identifier
            )

            configuration_name = "%s_cc_toolchain_config" % base_name
            cc_name = "%s_cc_toolchain" % base_name
            toolchain_name = "%s_cc" % base_name

            my_cc_toolchain_config(
                name = configuration_name,
                include_paths = include_paths,
                compiler_root = compiler_root,
                host_os = host_os,
                toolchain_identifier = toolchain_identifier,
                target_os = target_os,
                target_arch = target_arch,
                cc_compiler = cc_compiler,
                extra_features = [],
            )

            cc_toolchain(
                name = cc_name,
                toolchain_identifier = toolchain_name,
                toolchain_config = ":%s" % configuration_name,
                all_files = ":empty",
                compiler_files = ":empty",
                dwp_files = ":empty",
                linker_files = ":empty",
                objcopy_files = ":empty",
                strip_files = ":empty",
                supports_param_files = 0,
            )

            if platform in toolchains.keys():
                print("%s already exist!" % platform)
                fail("%s already exist!" % platform)
            else:
                toolchains[platform] = cc_name

        print("toolchains = ", toolchains)
        cc_toolchain_suite(
            name = "compiler_suite",
            toolchains = toolchains
        )
       
    TOOLCHAIN_SUPPORT_MATRIX = {
        "hisi": {
            TOOLCHAIN_HOST_OS : "linux",
            TOOLCHAIN_TARGET_OS : "linux",
            TOOLCHAIN_TARGET_ARCH : "armv7",
            TOOLCHAIN_COMPILER_ROOT : "",
            TOOLCHAIN_INCLUDE_PATHS : [],
            TOOLCHAIN_IDENTIFIER : "",
            TOOLCHAIN_CC_COMPILER : "gcc"
        },
        "ubuntu_gcc": {
            TOOLCHAIN_HOST_OS : "linux",
            TOOLCHAIN_TARGET_OS : "linux",
            TOOLCHAIN_TARGET_ARCH : "x86-64",
            TOOLCHAIN_COMPILER_ROOT : "/usr/bin/",
            TOOLCHAIN_INCLUDE_PATHS : [
                "/usr/include",
                "/usr/lib/gcc",
                "/usr/local/include"
            ],
            TOOLCHAIN_IDENTIFIER : "",
            TOOLCHAIN_CC_COMPILER : "gcc"
        },
        "ubuntu_clang": {
            TOOLCHAIN_HOST_OS : "linux",
            TOOLCHAIN_TARGET_OS : "linux",
            TOOLCHAIN_TARGET_ARCH : "x86-64",
            TOOLCHAIN_COMPILER_ROOT : "",
            TOOLCHAIN_INCLUDE_PATHS : [],
            TOOLCHAIN_IDENTIFIER : "",
            TOOLCHAIN_CC_COMPILER : "clang"
        },
        "ubuntu_arm_linux_gnueabihf" : {
            TOOLCHAIN_HOST_OS : "linux",
            TOOLCHAIN_TARGET_OS : "linux",
            TOOLCHAIN_TARGET_ARCH : "aarch74",
            TOOLCHAIN_COMPILER_ROOT : "/usr/bin/",
            TOOLCHAIN_INCLUDE_PATHS : [
                "/usr/arm-linux-gnueabihf/include/",
                "/usr/lib/gcc-cross/arm-linux-gnueabihf/7/include",
            ],
            TOOLCHAIN_IDENTIFIER : "arm-linux-gnueabihf-",
            TOOLCHAIN_CC_COMPILER : "gcc"
        }
    }
    • 配置文件設計內容示例:

最后--crosstool_top=//toolchains/cpp:{cc_toolchain_suite的名稱}--cpu={cc_toolchain的名稱},即可實現交叉編譯。整個實現內容這里就不貼出來了。為了簡化 $ bazel build 命令,可以將默認配置項寫入 .bazelrc 文件中:

build:compiler_config --crosstool_top=//toolchains/cpp:compiler_suite
build:compiler_config --cpu=ubuntu_gcc
build:compiler_config --host_crosstool_top=@bazel_tools//tools/cpp:toolchain
   

3 Platform 方式

 

3.1 平臺

 
3.1.1 概述

Bazel 可以在各種硬件、操作系統和系統配置上構建和測試代碼,使用許多不同版本的構建工具,比如鏈接器和編譯器。為了幫助管理這種復雜性,Bazel 提出了約束(constraints )和平臺(platforms)的概念。約束是構建或生產環境可能不同的維度,比如 CPU 架構、GPU 的存在或缺失,或者系統安裝的編譯器的版本。如第一章所述,平臺是這些約束的指定選擇集合,表示在某些環境中可用的特定資源。

將環境建模為平臺有助于 Bazel 為構建操作自動選擇適當的工具鏈。平臺還可以與 config_setting 規則結合使用來編寫可配置屬性。

Bazel 認為平臺可以扮演三個角色:

  • Host(主機): Bazel 本身運行的平臺
  • Execution(執行): 構建工具執行構建操作以產生中間和最終輸出的平臺,執行平臺設置一般是固定的。
  • Target(目標): 最終輸出駐留在其上并在其上執行的平臺,比如可能在執行平臺上交叉編譯目標平臺輸出,則目標平臺是多變的。
“  

注:這里 Host 平臺只是平臺扮演一個角色的闡述,跟實際編寫 Bazel 規則沒有關系。toolchain 規則里也只有對執行平臺和目標平臺的約束設置。

Bazel 支持以下針對平臺的構建場景:

  • 單平臺構建(默認):主機、執行和目標平臺是相同的。例如,在運行在 Intel x64 CPU 上的 Ubuntu 上構建 Linux 可執行文件。
  • 交叉編譯構建:主機和執行平臺是相同的,但是目標平臺是不同的。例如,在 macOS 上開發一個運行在 MacBook Pro 上的 iOS 應用。
  • 多平臺構建:主機、執行和目標平臺都是不同的。
 
3.1.2 定義約束和平臺

平臺的可能選擇空間是通過使用構建文件中的 constraint_settingconstraint_value 規則定義的。constraint_setting 創建一個新維度,可以說是一個約束值集合,constraint_value 為給定維度(constraint_setting)創建一個新值;它們一起有效地定義了枚舉及其可能的值。簡單來說,constraint_settingconstraint_value 就是一個單鍵多值的 map ,例如,下面的構建文件片段為系統的 glibc 版本引入了具有兩個可能值的約束。

constraint_setting(name = "glibc_version")

constraint_value(
   name = "glibc_2_25",
   constraint_setting = ":glibc_version",
)

constraint_value(
   name = "glibc_2_26",
   constraint_setting = ":glibc_version",
)
 

約束及其值可以在工作區中的不同包之間定義。它們通過標簽進行引用,并服從通常的可見性控制。如果可見性允許,你就可以通過定義自己的值來擴展現有的約束設置。

平臺規則 `platform`[1] 引入了一個具有特定約束值選擇的新平臺。下面創建了一個名為 linux_x86 的平臺,描述了在 glibc 版本為 2.25x86_64 體系結構上運行 Linux 操作系統的任何環境。

platform(
   name = "linux_x86",
   constraint_values = [
       "@platforms//os:linux",
       "@platforms//cpu:x86_64",
       ":glibc_2_25",
   ],
)
 

注意,對于一個平臺來說,同一個約束設置多個值是錯誤的,比如 glibc_2_25glibc_2_26 不能同時設置,因為他們都屬于 glibc_version 約束。

 
3.1.3 通用的約束和平臺

為了保持生態系統的一致性,Bazel 團隊維護了一個存儲庫,其中包含最流行的 CPU 架構和操作系統的約束定義。這些都位于 https://github.com/bazelbuild/platforms。當然你也可以自己自定義。

Bazel 附帶以下特殊的平臺定義 :@local_config_platform//:host。會自動檢測主機平臺的值:表示 Bazel 運行的系統的平臺。

 
3.1.4 指定平臺構建

你可以使用以下命令行標志為構建指定主機和目標平臺:

  • --host_platform:默認為     @bazel_tools//platforms:host_platform
  • --platforms:默認為     @bazel_tools//platforms:target_platform
  • 不指定     --platforms,默認是一個表示本地構建機器的平臺,即由     @local_config_platform//:host 自動生成。
 

3.2 工具鏈

在“前言”一章節中,可以知道平臺可以實現混合語言項目的構建,而如果對每一種語言實現構建,則需要配置工具鏈以及實現工具鏈的平臺約束設定。這樣就可以將平臺與工具鏈聯合在一起了,原理類似依賴注入。

工具鏈是使用 toolchain[2] 規則定義的目標,該規則將工具鏈實現與工具鏈類型相關聯。工具鏈類型是使用 tooclhain_type() 規則定義的目標(其實用一個字符串常量也可以替代)。工具鏈實現是一個目標,它通過列出作為工具鏈一部分的文件(例如,編譯器和標準庫)以及使用該工具鏈所需的代碼來表示實際的工具鏈。工具鏈實現必須返回 ToolchainInfo Provider(Provider 可以認為就是一個函數的返回值),ToolchainInfo 存放著工具鏈相關配置信息,對于存放什么內容沒有要求,即你可以定義任何你想要存放的信息。

任何定義工具鏈的人都需要聲明一個 toolchain_type 目標,這是一個字符串標識,用來標志工具鏈類別,以避免在加載了多個語言規則的工作區中出現潛在的沖突。比如 Bazel 官方提供了一個 CPP 的標識:@bazel_tools//tools/cpp:toolchain_type,而 rules_go 提供了 @io_bazel_rules_go//go:toolchain 用以區分工具鏈類別。

對于 C++cc_toolchain 規則即工具鏈實現,跟 Non-Platform 的工具鏈目標實現一致。當然你也可以使用任何返回 ToolchainInfo 的規則,而不僅僅是 cc_toolchain,比如可以通過 platform_common.ToolchainInfo 創建一個 ToolchainInfo,然后創建自己的工具鏈實現規則。

HELLOSDK = provider(
    fields = {
        "os": "The host OS the SDK was built for.",
        "arch": "The host architecture the SDK was built for.",
        "root_file": "A file in the SDK root directory",
        "libs": ("List of pre-compiled .a files for the standard library " +
                 "built for the execution platform."),
        "headers": ("List of .h files from pkg/include that may be included " +
                    "in assembly sources."),
        "srcs": ("List of source files for importable packages in the " +
                 "standard library. Internal, vendored, and tool packages " +
                 "may not be included."),
        "package_list": ("A file containing a list of importable packages " +
                         "in the standard library."),
        "hello": "The hello binary file",
    },
)

def _hello_toolchain_impl(ctx):
    return [platform_common.ToolchainInfo(
        sdk = ctx.attr.sdk,
        cflags = ctx.attr.cflags,
    )]

hello_toolchain = rule(
    _hello_toolchain_impl,
    attrs = {
        "sdk": attr.label(
            mandatory = True,
            providers = [HELLOSDK],
            cfg = "exec",
            doc = "The SDK this toolchain is based on sdk",
        ),
        "cflags": attr.string_list(),
    },
    doc = "Defines a hello toolchain based on SDK",
    provides = [platform_common.ToolchainInfo],
)
 

工具鏈實現規則可以認為是面向對象中的類,我們可以 New 很多實例出來,這里 New 出來的就是很多不同平臺架構或者不同版本的工具鏈了。完成工具鏈實例創建,就可以通過 native.toolchain 綁定工具鏈類型、目標平臺、運行平臺約束了。

用戶通過在 WORKSPACE 文件中調用 `register_toolchains`[3] 函數或者在命令行中傳遞 --extra_toolchains 標志來注冊他們想要使用的工具鏈。

最后,當 Bazel 開始構建時,它會檢查執行和目標平臺的約束條件。然后選擇與這些約束兼容的一組合適的工具鏈。Bazel 將向請求它們的規則提供這些工具鏈的 ToolchainInfo 對象。

如果想了解 Bazel 如何選擇或拒絕注冊的工具鏈,可以使用 --toolchain_resolution_debug 標志來調試。

 

3.3 Platform + Toolchain 實現平臺方式構建

Bazel 的 C++ 規則使用平臺來選擇工具鏈,需要設置 --incompatible_enable_cc_toolchain_resolution,如果不設置,即使顯示的在命令行加上--platforms也不起作用。

同樣地,Platform + Toolchain 實現平臺方式構建,官方文檔也提供了一個樣例,參見:https://docs.bazel.build/versions/master/toolchains.html 。總的步驟這里總結下:

  1. 創建     ToolchainInfo
  2. 創建     xx_toolchain,比如     C++ 已經有了內置的     cc_toolchain,則無需第一步和這一步了,即不用自己手動去實現該規則,只需要配置     cc_toolchain 即可
  3. 用     native.toolchain 關聯工具鏈實現,并設定     target_compatible_with,與平臺綁定以及工具鏈類型等,這里關聯相關平臺約束也需要創建。
  4. 在 WORKSPACE 文件中注冊所有聲明的工具鏈,可以用     register_toolchains() 或者命令行指定注冊     --extra_toolchains=
  5. 通過     --platforms= 就可以通過平臺方式構建了

這里同樣跟 Non-Platform 方式一樣,對于 C++,我們可以復用工具鏈的配置和 cc_toolchain 配置部分。工程上可以優化:

  • 可以根據工具鏈配置自動生成平臺約束    
    def generate_constraint_set_platform():
        available = get_available_unique_platform_idetifier()
        native.constraint_setting(
            name = "platform",
            visibility = ["//visibility:public"],
        )

        for item in available:
            native.constraint_value(
                name = item,
                constraint_setting = ":platform",
                visibility = ["//visibility:public"],
            )
  • 可以根據工具鏈配置自動聲明工具鏈,同樣地類似     Non-Platform 可以批量聲明    
    native.toolchain(
        name = toolchain_name,
        exec_compatible_with = [
            "@platforms//cpu:%s" % bazel_exec_platform_info["cpu"],
            "@platforms//os:%s" % bazel_exec_platform_info["os"],
        ], 
        target_compatible_with = [
            "//platforms:%s" % platform,
        ], 
        toolchain = "//toolchains/cpp:%s" % cc_name,
        toolchain_type = "@bazel_tools//tools/cpp:toolchain_type",
    )
  • 配置文件可以配置同一平臺下多個工具鏈或不同平臺下的工具鏈

--incompatible_enable_cc_toolchain_resolution 啟動平臺方式設置我們也可以將其放入 .bazelrc 全局構建配置文件中,從而省去命令行鍵入:

build   --incompatible_enable_cc_toolchain_resolution

看完上述內容,你們掌握如何進行Bazel中的自定義工具鏈分析的方法了嗎?如果還想學到更多技能或想了解更多相關內容,歡迎關注億速云行業資訊頻道,感謝各位的閱讀!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

渭南市| 金溪县| 达孜县| 山阴县| 安仁县| 遂溪县| 江安县| 柞水县| 荔浦县| 高青县| 老河口市| 五华县| 北海市| 台前县| 临安市| 乐东| 大关县| 苏尼特左旗| 策勒县| 聂荣县| 沁源县| 怀远县| 腾冲县| 锡林浩特市| 玛沁县| 乌审旗| 揭东县| 瓮安县| 曲靖市| 宕昌县| 越西县| 兴海县| 长寿区| 京山县| 永兴县| 鄄城县| 云霄县| 方山县| 寿阳县| 谢通门县| 西昌市|