1.极简入门TensorFlow C++源码
2.ONOS 从零入门教学 (应用程式新增,码下安装及测试)
3.MacOS 下交叉编译的码下折腾笔记
4.TF-TRT使用环境搭建
5.[推理部署]👉Mac源码编译TensorFlow C++指北
6.从源码build Tensorflow2.6.5的记录
极简入门TensorFlow C++源码
前一段时间,我专注在框架开发上,码下并偶尔协助业务同学优化使用TensorFlow的码下代码。在观看dmlc/relay、码下nnvm的码下停电查询小程序源码代码时,我发现了它们的码下有趣之处。我也对TensorFlow的码下Graph IR、PaddlePaddle的码下Graph IR产生了兴趣,上周五在阅读代码时,码下无意间听到了一个数据竞赛群讨论框架的码下底层实现。几位算法大佬提到了看底层源码可能较为繁琐,码下因为这类代码通常相对容易理解。码下在与群内伙伴的码下交流后,我萌生了撰写一篇关于如何阅读TensorFlow或其他框架底层源码的码下文章。
选择合适版本的bazel,对于阅读TensorFlow源码至关重要。应使用版本为0..0的bazel来拉取TF2.0代码,因为太高的版本或太低的版本可能影响阅读体验。在安装了合适的bazel版本后,使用clion上的bazel插件进行导入,然后配置编译,导入项目,等待clion编译整个项目。完成编译后,就能愉快地阅读代码,甚至于protobuf生成的文件也能轻松跳转。
使用c++编译模型是TensorFlow的另一面。尝试使用c++编写模型代码,可以深入理解TensorFlow的底层机制。主要函数包括CreateGraphDef、ConcurrentSteps、ConcurrentSessions等。通过这些函数,可以构建计算图,微网站 源码 免费下载定义节点、常量变量、操作符等。这为理解TensorFlow的逻辑提供了直观的视角。
深入分析代码后,可以了解到TensorFlow的GraphDef机制、Square类的实现、注册到特定op的过程、functor的使用以及最终的实现逻辑。这有助于理解TensorFlow的核心原理,并在阅读源码时进行更深入的思考。
除了阅读源码,还可以通过编写测试用例来增强理解。TensorFlow提供了丰富的测试用例,如在client_session_test.cc中运行测试程序,可以验证代码的正确性。这不仅有助于理解代码,还能提高对TensorFlow框架的掌握程度。
阅读源码只是理解TensorFlow原理的开始,深入行业论文和请教行业专家是进一步深入学习的关键。网络上关于机器学习系统的资料丰富多样,但缺少系统性的课程。希望官方能够分享更多框架的干货,并期待在学习过程中总结和分享更多资源。阅读源码虽然复杂,但其背后蕴含的原理和逻辑十分有趣。
ONOS 从零入门教学 (应用程式新增,安装及测试)
本文将介绍如何从零开始学习并使用ONOS,包括安装、配置和测试。首先,你需要获取ONOS的源代码并将其添加到.bashrc或.bash_profile中,使用预设的alias进行操作。然后使用构建工具(原为buck,数字源码在线阅读现已更改为bazel)构建ONOS,并进行单元测试。
接下来,你可以在本地计算机上启动ONOS服务器,使用另一个终端开启ONOS控制台。成功后,会看到ONOS预设使用端口作为控制台。此时,恭喜你,已经成功在本地机上启动了ONOS。
ONOS还提供了图形用户界面(UI)界面,预设使用端口。你可以通过ile.cc`。
5. **交叉编译执行**:利用 LLVM 工具链结合 arm-linux-gnueabihf-binutils 进行交叉编译。
6. **构建输出**:运行特定编译脚本(通常封装为 `.sh` 文件)生成目标平台可执行文件(如 `hello`),随后将该文件传输至树莓派等目标平台进行执行。
推荐阅读资源:
4. **[野火]i.MX Linux开发实战指南**:该文档提供了一个全面且详细的交叉编译指南,虽然不直接支持 MacOS,但通过开启 Docker 环境,可以轻松实现 MacOS 下的交叉编译。
Crosstool-ng:尽管这是 MacOS 下公认的交叉编译解决方案,但其操作复杂,且存在系统崩溃风险。对于坚持使用此方案的开发者,可参考他人提供的 Docker 镜像,例如 **Dockfile**,但同样建议考虑使用更易管理的 Linux 操作系统(如 Ubuntu)作为 Docker 容器的基础环境。
TF-TRT使用环境搭建
TF-TRT,即TensorFlow与TensorRT的集成,是NVIDIA为加速深度学习推理应用而设计的工具。它简化了TensorFlow用户在GPU上利用TensorRT进行模型推理的流程。本文主要介绍如何在服务器上搭建TF-TRT的使用环境和编写相关代码。
首先,NVIDIA推荐的博易大师dmi源码TF-TRT环境配置基于TensorRT 5.0RC,需要确保NVIDIA驱动程序版本.0以上,CUDA .0以及TensorRT。安装过程建议在Anaconda的虚拟环境中进行,从Tensorflow GitHub上下载1.版本源码,并通过bazel build工具生成pip安装包。在编译时,由于GCC 5.0可能与新版本兼容性问题,需添加特定编译选项。
对于服务器上直接安装,你需按照官方教程安装CUDA、CUDNN、NVIDIA Driver和TensorRT。在Tensorflow的configure文件中,根据你的硬件配置进行相应的调整。然后,通过pip安装生成的.whl文件,安装时需要注意选择nvcc编译器,cudnn 7.3以上版本,以及兼容性的GCC编译选项。
另一种方式是利用Docker容器,Tensorflow .容器需要nvidia driver +版本,并需要获取Nvidia GPU cloud的API密钥。安装完成后,你可以通过Docker拉取tensorflow:.-py3镜像,验证TensorRT与Tensorflow的集成是否成功。
无论是直接安装还是容器化,都需注意选择合适的驱动和软件版本,以确保TF-TRT的稳定运行。安装过程中,还可以根据实际需求在container中安装其他软件,以满足个性化需求。
[推理部署]👉Mac源码编译TensorFlow C++指北
在Mac环境下编译TensorFlow C++源码,需要完成以下步骤,以避免可能的wikidata.org 源码部署编译问题,确保顺利构建。
首先,确认系统环境满足要求。需有Xcode和Command Line Tools,JDK 1.8.0版本以支持编译过程中所需的Java环境,以及Bazel工具,TensorFlow依赖此工具进行编译。特别注意Bazel版本需与TensorFlow对应,如TensorFlow 1.对应Bazel 0..1。
接下里,安装依赖,包括JDK和Bazel。JDK安装时需检查电脑中是否已安装,并确保正确安装。使用HomeBrew安装Bazel,通过命令行接受协议,并使用`--user`指令确保安装在个人目录的`bin`文件夹下,同时设置`.bazelrc`路径为`$HOME/.bazelrc`。
安装自动化工具`automake`和使用Python3.7.5在虚拟环境中构建TensorFlow C++源码。推荐使用清华镜像源加速`pip`的安装过程。通过`git clone`方式下载TensorFlow源码,确保checkout至r1.分支。调整域名映射以提升`git clone`速度。
进行编译选项配置,通常在TensorFlow文件夹内运行命令,根据提示选择默认选项。
开始编译TensorFlow,此过程可能需要较长时间,完成后,应在`bazel-bin/tensorflow`目录下找到编译好的`libtensorflow_cc.so`和`libtensorflow_framework.1.dylib`文件。
若遇到`Undefined symbols for architecture x_: “_CFRelease”`错误,这通常与创建软连接有关,无需特别处理。若需要手动安装额外依赖库,如Eigen3,可参考相关指南。
编译完成后,可对C++接口进行测试,验证编译过程的正确性。通常情况下,Mac下的TensorFlow 1. C++源码编译完成。
最后,编译TFLite,生成的动态链接库将保存在指定目录下。在`CMakelists.txt`文件中增加对应配置项,以完成TFLite的构建。
总结而言,Mac下TensorFlow 1. C++源码编译及TFLite的构建,需要遵循上述步骤,并确保环境与工具版本的兼容性,以顺利进行编译过程。Linux系统下的编译方式相似,但具体细节可能有所不同。
从源码build Tensorflow2.6.5的记录
.从源码编译Tensorflow2.6.5踩坑记录,笔者经过一天的努力,失败四次后终于成功。Tensorflow2.6.5是截至.时,能够从源码编译的最新版本。
0 - 前期准备
为了对Tensorflow进行大规模修改并完成科研工作,笔者有从源码编译Tensorflow的需求。平时更常用的做法是在conda环境中pip install tensorflow,有时为了环境隔离方便打包,会用docker先套住,再上conda + pip安装。
1 - 资料汇总
教程参考:
另注:bazel的编译可以使用换源清华镜像(不是必要)。整体配置流程的根本依据还是官方的教程,但它的教程有些点和坑没有涉及到,所以多方材料了解。
2 - 整体流程
2.1 确定配置目标
官网上给到了配置目标,和对应的版本匹配关系(这张表里缺少了对numpy的版本要求)。笔者最后(在docker中)配置成功的版本为tensorflow2.6.5 numpy1..5 Python3.7. GCC7.5.0 CUDA.3 Bazel3.7.2。
2.2 开始配置
为了打包方便和编译环境隔离,在docker中进行了以下配置:
2. 安装TensorFlow pip软件包依赖项,其编译过程依赖于这些包。
3. Git Tensorflow源代码包。
4. 安装编译工具Bazel。
官网的介绍:(1)您需要安装Bazel,才能构建TensorFlow。您可以使用Bazelisk轻松安装Bazel,并且Bazelisk可以自动为TensorFlow下载合适的Bazel版本。为便于使用,请在PATH中将Bazelisk添加为bazel可执行文件。(2)如果没有Bazelisk,您可以手动安装Bazel。请务必安装受支持的Bazel版本,可以是tensorflow/configure.py中指定的介于_TF_MIN_BAZEL_VERSION和_TF_MAX_BAZEL_VERSION之间的任意版本。
但笔者尝试最快的安装方式是,到Github - bazelbuild/build/releases上下载对应的版本,然后使用sh脚本手动安装。比如依据刚才的配置目标,笔者需要的是Bazel3.7.2,所以下载的文件为bazel-3.7.2-installer-linux-x_.sh。
5. 配置编译build选项
官网介绍:通过运行TensorFlow源代码树根目录下的./configure配置系统build。此脚本会提示您指定TensorFlow依赖项的位置,并要求指定其他构建配置选项(例如,编译器标记)。
这一步就是选择y/N基本没啥问题,其他参考里都有贴实例。笔者需要GPU的支持,故在CUDA那一栏选择了y,其他部分如Rocm部分就是N(直接按enter也可以)。
6.开始编译
编译完成应输出
7.检查TF是否能用
3 - 踩坑记录
3.1 cuda.0在编译时不支持sm_
笔者最初选择的docker是cuda.0的,在bazel build --config=cuda //tensorflow/tools/pip_package:build_pip_package过程中出现了错误。所以之后选择了上面提到的cuda.3的docker。
3.2 问题2: numpy、TF、python版本匹配
在配置过程中,发现numpy、TF、python版本需要匹配,否则会出现错误。
4 - 启示
从源码编译Tensorflow2.6.5的过程,虽然经历了多次失败,但最终还是成功。这个过程也让我对Tensorflow的编译流程有了更深入的了解,同时也提醒我在后续的工作中要注意版本匹配问题。
å¦ä½ä½¿ç¨bazel build
å®è£
å®è£ è¿ç¨è¯·åè: /example
$ cat > src/main/java/com/example/ProjectRunner.java <<EOF
package com.example;
public class ProjectRunner {
public static void main(String args[]) {
Greeting.sayHi();
}
}
EOF
$ cat > src/main/java/com/example/Greeting.java <<EOF
package com.example;
public class Greeting {
public static void sayHi() {
System.out.println("Hi!");
}
}
EOF
Bazeléè¿å·¥ä½åºä¸ææå为 BUILD çæ件æ¥è§£æéè¦æ建ç项ç®ä¿¡æ¯ï¼å æ¤ï¼æ们éè¦å å¨ ~/gitroot/my-project ç®å½å建ä¸ä¸ª BUILD æ建æ件ãä¸é¢æ¯BUILDæ建æ件çå 容ï¼
# ~/gitroot/my-project/BUILD
java_binary(
name = "my-runner",
srcs = glob(["**/*.java"]),
main_class = "com.example.ProjectRunner",
)
BUILDæ件éç¨ç±»ä¼¼Pythonçè¯æ³ãè½ç¶ä¸è½å å«ä»»æçPythonè¯æ³ï¼ä½æ¯BUILDæ件ä¸çæ¯ä¸ªæ建è§åçèµ·æ¥é½è±¡æ¯ä¸ä¸ªPythonå½æ°è°ç¨ï¼èä¸ä½ ä¹å¯ä»¥ç¨ "#" å¼å¤´æ¥æ·»å åè¡æ³¨éã
java_binary æ¯ä¸ä¸ªæ建è§åãå ¶ä¸ name 对åºä¸ä¸ªæ建ç®æ çæ è¯ç¬¦ï¼å¯ç¨ç¨å®æ¥åBazelæå®æ建åªä¸ªé¡¹ç®ãsrcs 对åºä¸ä¸ªæºæ件å表ï¼Bazeléè¦å°è¿äºæºæ件ç¼è¯ä¸ºäºè¿å¶æ件ãå ¶ä¸ glob(["**/*.java"]) 表示éå½å å«æ¯ä¸ªåç®å½ä¸ä»¥æ¯ä¸ª .java 为åç¼åçæ件ãcom.example.ProjectRunner æå®å å«mainæ¹æ³çç±»ã
ç°å¨å¯ä»¥ç¨ä¸é¢çå½ä»¤æ建è¿ä¸ªJavaç¨åºäºï¼
$ cd ~/gitroot/my-project
$ bazel build //:my-runner
INFO: Found 1 target...
Target //:my-runner up-to-date:
bazel-bin/my-runner.jar
bazel-bin/my-runner
INFO: Elapsed time: 1.s, Critical Path: 0.s
$ bazel-bin/my-runner
Hi!
æåï¼ä½ å·²ç»æåæ建äºç¬¬ä¸ä¸ªBazel项ç®äºï¼
æ·»å ä¾èµå ³ç³»
对äºå°é¡¹ç®å建ä¸ä¸ªè§åæ¯å¯ä»¥çï¼ä½æ¯éç项ç®çå大ï¼åéè¦åå«æ建项ç®çä¸åçé¨ä»¶ï¼æç»åç»è£ æ产åãè¿ç§æ建æ¹å¼å¯ä»¥é¿å å 为å±é¨ç»å°çä¿®æ¹å¿å¯¼è´éç°æ建æ´ä¸ªåºç¨ï¼åæ¶ä¸åçæ建æ¥éª¤å¯ä»¥å¾å¥½å°å¹¶åæ§è¡ä»¥æé«æ建æçã
æ们ç°å¨å°ä¸ä¸ªé¡¹ç®æå为两个é¨åç¬ç«æ建ï¼åæ¶è®¾ç½®å®ä»¬ä¹é´çä¾èµå ³ç³»ãåºäºä¸é¢çä¾åï¼æ们éåäºBUILDæ建æ件ï¼
java_binary(
name = "my-other-runner",
srcs = ["src/main/java/com/example/ProjectRunner.java"],
main_class = "com.example.ProjectRunner",
deps = [":greeter"],
)
java_library(
name = "greeter",
srcs = ["src/main/java/com/example/Greeting.java"],
)
è½ç¶æºæ件æ¯ä¸æ ·çï¼ä½æ¯ç°å¨Bazelå°éç¨ä¸åçæ¹å¼æ¥æ建ï¼é¦å æ¯æ建 greeteråºï¼ç¶åæ¯æ建 my-other-runnerãå¯ä»¥å¨æ建æååç«å»è¿è¡ //:my-other-runnerï¼
$ bazel run //:my-other-runner
INFO: Found 1 target...
Target //:my-other-runner up-to-date:
bazel-bin/my-other-runner.jar
bazel-bin/my-other-runner
INFO: Elapsed time: 2.s, Critical Path: 1.s
INFO: Running command line: bazel-bin/my-other-runner
Hi!
ç°å¨å¦æä½ æ¹å¨ProjectRunner.java代ç 并éæ°æ建my-other-runnerç®æ ï¼Greeting.javaæ件å 为没æååèä¸ä¼éç°ç¼è¯ã
使ç¨å¤ä¸ªå ï¼Packagesï¼
对äºæ´å¤§ç项ç®ï¼æ们é常éè¦å°å®ä»¬æåå°å¤ä¸ªç®å½ä¸ãä½ å¯ä»¥ç¨ç±»ä¼¼//path/to/directory:target-nameçååå¼ç¨å¨å ¶ä»BUILDæ件å®ä¹çç®æ ãå设src/main/java/com/example/æä¸ä¸ªcmdline/åç®å½ï¼å å«ä¸é¢çæ件ï¼
$ mkdir -p src/main/java/com/example/cmdline
$ cat > src/main/java/com/example/cmdline/Runner.java <<EOF
package com.example.cmdline;
import com.example.Greeting;
public class Runner {
public static void main(String args[]) {
Greeting.sayHi();
}
}
EOF
Runner.javaä¾èµcom.example.Greetingï¼å æ¤æ们éè¦å¨src/main/java/com/example/cmdline/BUILDæ建æ件ä¸æ·»å ç¸åºçä¾èµè§åï¼
# ~/gitroot/my-project/src/main/java/com/example/cmdline/BUILD
java_binary(
name = "runner",
srcs = ["Runner.java"],
main_class = "com.example.cmdline.Runner",
deps = ["//:greeter"]
)
ç¶èï¼é»è®¤æ åµä¸æ建ç®æ é½æ¯ ç§æ çãä¹å°±æ¯è¯´ï¼æ们åªè½å¨åä¸ä¸ªBUILDæ件ä¸è¢«å¼ç¨ãè¿å¯ä»¥é¿å å°å¾å¤å®ç°çç»èæ´æ¼ç»å ¬å ±çæ¥å£ï¼ä½æ¯ä¹æå³çæ们éè¦æå·¥å 许runneræä¾èµç//:greeterç®æ ãå°±æ¯ç±»ä¼¼ä¸é¢è¿ä¸ªå¨æ建runnerç®æ æ¶éå°çé误ï¼
$ bazel build //src/main/java/com/example/cmdline:runner
ERROR: /home/user/gitroot/my-project/src/main/java/com/example/cmdline/BUILD:2:1:
Target '//:greeter' is not visible from target '//src/main/java/com/example/cmdline:runner'.
Check the visibility declaration of the former target if you think the dependency is legitimate.
ERROR: Analysis of target '//src/main/java/com/example/cmdline:runner' failed; build aborted.
INFO: Elapsed time: 0.s
å¯ç¨éè¿å¨BUILDæ件å¢å visibility = levelå±æ§æ¥æ¹åç®æ çå¯é´èå´ãä¸é¢æ¯éè¿å¨~/gitroot/my-project/BUILDæ件å¢å å¯è§è§åï¼æ¥æ¹ågreeterç®æ çå¯è§èå´ï¼
java_library(
name = "greeter",
srcs = ["src/main/java/com/example/Greeting.java"],
visibility = ["//src/main/java/com/example/cmdline:__pkg__"],
)
è¿ä¸ªè§å表示//:greeterç®æ 对äº//src/main/java/com/example/cmdlineå æ¯å¯è§çãç°å¨æ们å¯ä»¥éæ°æ建runnerç®æ ç¨åºï¼
$ bazel run //src/main/java/com/example/cmdline:runner
INFO: Found 1 target...
Target //src/main/java/com/example/cmdline:runner up-to-date:
bazel-bin/src/main/java/com/example/cmdline/runner.jar
bazel-bin/src/main/java/com/example/cmdline/runner
INFO: Elapsed time: 1.s, Critical Path: 0.s
INFO: Running command line: bazel-bin/src/main/java/com/example/cmdline/runner
Hi!
åèææ¡£ ä¸æå¯è§æ§é 置说æã
é¨ç½²
å¦æä½ æ¥ç bazel-bin/src/main/java/com/example/cmdline/runner.jar çå 容ï¼å¯ä»¥çå°éé¢åªå å«äºRunner.classï¼å¹¶æ²¡æä¿æ¤æä¾èµçGreeting.classï¼
$ jar tf bazel-bin/src/main/java/com/example/cmdline/runner.jar
META-INF/
META-INF/MANIFEST.MF
com/
com/example/
com/example/cmdline/
com/example/cmdline/Runner.class
è¿åªè½å¨æ¬æºæ£å¸¸å·¥ä½ï¼å 为Bazelçrunnerèæ¬å·²ç»å°greeter jaræ·»å å°äºclasspathï¼ï¼ä½æ¯å¦æå°runner.jaråç¬å¤å¶å°å¦ä¸å°æºå¨ä¸è®²ä¸è½æ£å¸¸è¿è¡ãå¦ææ³è¦æ建å¯ç¨äºé¨ç½²åå¸çèªå å«ææä¾èµçç®æ ï¼å¯ä»¥æ建runner_deploy.jarç®æ ï¼ç±»ä¼¼<target-name>_deploy.jar以_deploy为åç¼çåå对åºå¯é¨ç½²ç®æ ï¼ã
$ bazel build //src/main/java/com/example/cmdline:runner_deploy.jar
INFO: Found 1 target...
Target //src/main/java/com/example/cmdline:runner_deploy.jar up-to-date:
bazel-bin/src/main/java/com/example/cmdline/runner_deploy.jar
INFO: Elapsed time: 1.s, Critical Path: 0.s
runner_deploy.jarä¸å°å å«å ¨é¨çä¾èµã
ä¸ä¸æ¥
ç°å¨ï¼æ¨å¯ä»¥å建èªå·±çç®æ 并ç»è£ æç»äº§åäºãæ¥ä¸æ¥ï¼å¯æ¥ç ç¸å ³æç¨ åå«å¦ä¹ å¦ä½ç¨Bazelæ建ä¸ä¸ªæå¡å¨ãAndroidåiOSåºç¨ãä¹å¯ä»¥åè ç¨æ·æåè·å¾æ´å¤çä¿¡æ¯ãå¦ææé®é¢çè¯ï¼å¯ä»¥å° bazel-discuss 论åæé®ã
实战!用Bazel来管理iOS程序
在探索Bazel作为iOS项目管理工具的过程中,我总结了以下几点原因促使我选择Bazel而非Xcode:
首先,Bazel提供更好的代码审查和依赖管理。Xcode的编译设置分散在多个文件中(xcscheme、pbxproj和xcconfig),使得代码审查变得困难。相反,Bazel的配置统一且可追踪,有利于代码审查流程。
其次,Bazel具有高效的缓存机制和快速增量编译能力。Xcode的编译缓存管理效率低下,即使是细微的设置改动也可能导致全量编译。相比之下,Bazel仅对发生变化的文件或依赖进行编译,切换分支也不会显著影响编译速度。
此外,Bazel支持远程缓存,这在项目规模增大时能显著提高编译效率。它还提供了更多模块化实践的友好环境,支持Swift等语言的大型项目开发。
选择迁移Bazel的时机因工程团队规模和需求而异。在感受到Xcode编译系统的不足(如长时间编译时间、单体大项目或持续集成工具频繁报错)时,迁移可能需要几个月的时间。而对于小型团队,迁移Bazel可能不是当前的首要任务。
配置阶段涉及安装工具(如Bazelisk和Tulsi)和调整文件结构。在源码库中创建WORKSPACE文件以整合规则,使用generate_xcodeproj.sh脚本生成Xcode项目。同时,通过PodToBUILD项目自动将CocoaPods依赖转换为Bazel兼容的格式。
处理CocoaPods依赖时,利用PodToBUILD将现有项目的依赖转换为Bazel格式,然后通过特定命令将依赖复制到源码库中。对于某些依赖,如Google的Protobuf,可以将官方的BUILD文件替换原有的CocoaPods配置。其他依赖通常通过Bazel的编译规则进行导入。
在处理C++支持时,需要手动创建自定义的toolchain并将其集成到本地项目中,以确保使用C++。此外,还需要关注Bridging header的配置、Provisioning文件、entitlement设置、以及手机测试和打包的相关步骤。
在Bazel下进行单元测试时,注意区分Hosted Tests与ios_unit_test的使用场景。对于大多数测试应转换为单元测试,而对于依赖特定环境的测试(如SnapshotTesting),则保持Hosted Tests的使用。
将Bazel集成至持续集成系统中,如Bitrise,可以简化测试和打包流程。通过共享缓存,可以进一步提升持续集成的编译速度。同时,使用Bazel的select_a_variant函数选择性地编译不同版本的程序,如针对Apple的提交版本。
随着项目的深入使用Bazel,可以探索更多的代码生成技术,例如自动将配置文件(如JSON文件)生成固定Swift文件,直接嵌入程序中,减少加载步骤。
迁移Bazel虽然需要一定的前期投入,但相对于之前的切换经验(如从Xcode Workspace到Buck),过程变得更加顺畅。对于大型依赖复杂的项目,迁移工作量也相对较小,通常只需几天时间。
尽管Bazel已提供了良好的基础支持,仍有改进空间,尤其是在工具集成与代码生成的自动化方面。此外,社区的反馈和讨论对于进一步优化迁移过程和利用Bazel的潜力至关重要。