Tensorflow的Bazel编程(二)

转自:http://blog.csdn.net/langb2014/article/details/54312697

安装官网:https://bazel.build/versions/master/docs/tutorial/Java.html

Build Java

创建一个java项目,然后

[python]  view plain  copy
  1. cd /home/mi/git/TF_pro/Bazel/project  
在终端运行:

[python]  view plain  copy
  1. $ mkdir -p src/main/java/com/example  
  2. $ cat > src/main/java/com/example/ProjectRunner.java <<EOF  
  3. package com.example;  
  4.   
  5. public class ProjectRunner {  
  6.     public static void main(String args[]) {  
  7.         Greeting.sayHi();  
  8.     }  
  9. }  
  10. EOF  
  11. $ cat > src/main/java/com/example/Greeting.java <<EOF  
  12. package com.example;  
  13.   
  14. public class Greeting {  
  15.     public static void sayHi() {  
  16.         System.out.println("Hi!");  
  17.     }  
  18. }  
  19. EOF  
在项目根目录下建一个BUILD文件:

[python]  view plain  copy
  1. java_binary(  
  2.     name = "my-runner",  
  3.     srcs = glob(["**/*.java"]),  
  4.     main_class = "com.example.ProjectRunner",  
  5. )  
然后build
[python]  view plain  copy
  1. bazel build //:my-runner  
进入生成的文件夹
[python]  view plain  copy
  1. bazel-bin  
运行可执行文件
[python]  view plain  copy
  1. my-runner  
输出:Hi!
成功构建了第二个Bazel项目了!
随着项目的增加,我们讲java项目拆成两个部分独立构建,同时设置它们之间的依赖关系。
重新构建一下BUILD文件:

[python]  view plain  copy
  1. java_binary(  
  2.     name = "my-other-runner",  
  3.     srcs = ["src/main/java/com/example/ProjectRunner.java"],  
  4.     main_class = "com.example.ProjectRunner",  
  5.     deps = [":greeter"],  
  6. )  
  7.   
  8. java_library(  
  9.     name = "greeter",  
  10.     srcs = ["src/main/java/com/example/Greeting.java"],  
  11. )  
虽然源文件是一样的,但是现在Bazel将采用不同的方式来构建:首先是构建 greeter 库,然后是构建my-other-runner。可以在构建成功后立刻运行//:my-other-runner
[python]  view plain  copy
  1. $ bazel run //:my-other-runner  
  2. INFO: Found 1 target...  
  3. Target //:my-other-runner up-to-date:  
  4.   bazel-bin/my-other-runner.jar  
  5.   bazel-bin/my-other-runner  
  6. INFO: Elapsed time: 2.454s, Critical Path: 1.58s  
  7.   
  8. INFO: Running command line: bazel-bin/my-other-runner  
  9. Hi!  
更多的包

对于更大的项目,我们通常需要将它们拆分到多个目录中。你可以用类似//path/to/directory:target-name的名字引用在其他BUILD文件定义的目标。假设src/main/java/com/example/有一个cmdline/子目录,包含下面的文件:


 
 
[python] view plain copy
  1. $ mkdir -p src/main/java/com/example/cmdline  
  2. $ cat > src/main/java/com/example/cmdline/Runner.java <<EOF  
  3. package com.example.cmdline;  
  4.   
  5. import com.example.Greeting;  
  6.   
  7. public class Runner {  
  8.     public static void main(String args[]) {  
  9.         Greeting.sayHi();  
  10.     }  
  11. }  
  12. EOF  
Runner.java依赖com.example.Greeting,因此我们需要在src/main/java/com/example/cmdline/BUILD构建文件中添加相应的依赖规则:

 
 
[python] view plain copy
  1. # src/main/java/com/example/cmdline/BUILD  
  2. java_binary(  
  3.     name = "runner",  
  4.     srcs = ["Runner.java"],  
  5.     main_class = "com.example.cmdline.Runner",  
  6.     deps = ["//:greeter"]  
  7. )  
然而,默认情况下构建目标都是 私有 的。也就是说,我们只能在同一个BUILD文件中被引用。这可以避免将很多实现的细节暴漏给公共的接口,但是也意味着我们需要手工允许runner所依赖的//:greeter目标。就是类似下面这个在构建runner目标时遇到的错误:

 
 
[python] view plain copy
  1. $ bazel build //src/main/java/com/example/cmdline:runner  
  2. ERROR: /home/user/gitroot/my-project/src/main/java/com/example/cmdline/BUILD:2:1:  
  3.   Target '//:greeter' is not visible from target '//src/main/java/com/example/cmdline:runner'.  
  4.   Check the visibility declaration of the former target if you think the dependency is legitimate.  
  5. ERROR: Analysis of target '//src/main/java/com/example/cmdline:runner' failed; build aborted.  
  6. INFO: Elapsed time: 0.091s  
可用通过在BUILD文件增加visibility = level属性来改变目标的可间范围。下面是通过在~/gitroot/my-project/BUILD文件增加可见规则,来改变greeter目标的可见范围:

 
 
[python] view plain copy
  1. java_library(  
  2.     name = "greeter",  
  3.     srcs = ["src/main/java/com/example/Greeting.java"],  
  4.     visibility = ["//src/main/java/com/example/cmdline:__pkg__"],  
  5. )  
这个规则表示//:greeter目标对于//src/main/java/com/example/cmdline包是可见的。现在我们可以重新构建runner目标程序:
[python] view plain copy
  1. $ bazel run //src/main/java/com/example/cmdline:runner  
  2. INFO: Found 1 target...  
  3. Target //src/main/java/com/example/cmdline:runner up-to-date:  
  4.   bazel-bin/src/main/java/com/example/cmdline/runner.jar  
  5.   bazel-bin/src/main/java/com/example/cmdline/runner  
  6. INFO: Elapsed time: 1.576s, Critical Path: 0.81s  
  7.   
  8. INFO: Running command line: bazel-bin/src/main/java/com/example/cmdline/runner  
  9. Hi!  

如果你查看 bazel-bin/src/main/java/com/example/cmdline/runner.jar 的内容,可以看到里面只包含了Runner.class,并没有保护所依赖的Greeting.class:

[python]  view plain  copy
  1. $ jar tf bazel-bin/src/main/java/com/example/cmdline/runner.jar  
  2. META-INF/  
  3. META-INF/MANIFEST.MF  
  4. com/  
  5. com/example/  
  6. com/example/cmdline/  
  7. com/example/cmdline/Runner.class  
这只能在本机正常工作(因为Bazel的runner脚本已经将greeter jar添加到了classpath),但是如果将runner.jar单独复制到另一台机器上讲不能正常运行。如果想要构建可用于部署发布的自包含所有依赖的目标,可以构建runner_deploy.jar目标(类似<target-name>_deploy.jar以_deploy为后缀的名字对应可部署目标)。


 
 
[python] view plain copy
  1. $ bazel build //src/main/java/com/example/cmdline:runner_deploy.jar  
  2. INFO: Found 1 target...  
  3. Target //src/main/java/com/example/cmdline:runner_deploy.jar up-to-date:  
  4.   bazel-bin/src/main/java/com/example/cmdline/runner_deploy.jar  
  5. INFO: Elapsed time: 1.700s, Critical Path: 0.23s  
runner_deploy.jar中将包含全部的依赖。


Build C++

首先创建头文件和源文件:

切记一定要先建一个WORKSPACE,这个可以为空,不然找不到工程下面的一些目录,build会报错。

在project1下

[python]  view plain  copy
  1. $ mkdir ./main  
  2. $ cat > main/hello-world.cc <<'EOF'  
  3. #include "lib/hello-greet.h"  
  4. #include "main/hello-time.h"  
  5. #include <iostream>  
  6. #include <string>  
  7.   
  8. int main(int argc, char** argv) {  
  9.   std::string who = "world";  
  10.   if (argc > 1) {  
  11.     who = argv[1];  
  12.   }  
  13.   std::cout << get_greet(who) <<std::endl;  
  14.   print_localtime();  
  15.   return 0;  
  16. }  
  17. EOF  
  18. $ cat > main/hello-time.h <<'EOF'  
  19. #ifndef MAIN_HELLO_TIME_H_  
  20. #define MAIN_HELLO_TIME_H_  
  21.   
  22. void print_localtime();  
  23.   
  24. #endif  
  25. EOF  
  26. $ cat > main/hello-time.cc <<'EOF'  
  27. #include "main/hello-time.h"  
  28. #include <ctime>  
  29. #include <iostream>  
  30.   
  31. void print_localtime() {  
  32.   std::time_t result = std::time(nullptr);  
  33.   std::cout << std::asctime(std::localtime(&result));  
  34. }  
  35. EOF  
  36. $ mkdir ./lib  
  37. $ cat > lib/hello-greet.h <<'EOF'  
  38. #ifndef LIB_HELLO_GREET_H_  
  39. #define LIB_HELLO_GREET_H_  
  40.   
  41. #include <string>  
  42.   
  43. std::string get_greet(const std::string &thing);  
  44.   
  45. #endif  
  46. EOF  
  47. $ cat > lib/hello-greet.cc <<'EOF'  
  48. #include "lib/hello-greet.h"  
  49. #include <string>  
  50.   
  51. std::string get_greet(const std::string& who) {  
  52.   return "Hello " + who;  
  53. }  
  54. EOF  
在lib下添加BUILD文件:
[python]  view plain  copy
  1. cc_library(  
  2.     name = "hello-greet",  
  3.     srcs = ["hello-greet.cc"],  
  4.     hdrs = ["hello-greet.h"],  
  5.     visibility = ["//main:__pkg__"],  
  6. )  
在main下 添加BUILD文件:

[python]  view plain  copy
  1. cc_library(  
  2.     name = "hello-time",  
  3.     srcs = ["hello-time.cc"],  
  4.     hdrs = ["hello-time.h"],  
  5. )  
  6.   
  7. cc_binary(  
  8.     name = "hello-world",  
  9.     srcs = ["hello-world.cc"],  
  10.     deps = [  
  11.         ":hello-time",  
  12.         "//lib:hello-greet",  
  13.     ],  
  14. )  

依赖传递

如果包含了一个头文件,那么需要将头文件对应的库添加到依赖中。不过,只需有添加直接依赖的库。假设三明治对应的sandwich.h文件包含了面包对应的bread.h文件,同时bread.h又包含了面粉对应的flour.h文件。但是,三明治sandwich.h文件并没有直接包含面粉flour.h文件。

BUILD文件是这样的:

[python]  view plain  copy
  1. cc_library(  
  2.     name = "sandwich",  
  3.     srcs = ["sandwich.cc"],  
  4.     hdrs = ["sandwich.h"],  
  5.     deps = [":bread"],  
  6. )  
  7.   
  8. cc_library(  
  9.     name = "bread",  
  10.     srcs = ["bread.cc"],  
  11.     hdrs = ["bread.h"],  
  12.     deps = [":flour"],  
  13. )  
  14.   
  15. cc_library(  
  16.     name = "flour",  
  17.     srcs = ["flour.cc"],  
  18.     hdrs = ["flour.h"],  
  19. )  
上面表示了 sandwich三明治库依赖bread面包库,bread又依赖flour对应的面粉库。
添加头文件路径

很多时候你可能并不希望基于工作区根路径的相对路径来包含每个头文件。因为很多已经存在的第三方库的头文件包含方式并不是基于工作区的根路径。假设有以下目录结构:

[python]  view plain  copy
  1. [workspace]/  
  2.     WORKSPACE  
  3.     third_party/  
  4.         some_lib/  
  5.             include/  
  6.                 some_lib.h  
  7.             BUILD  
  8.             some_lib.cc  

Bazel希望用third_party/some_lib/include/some_lib.h方式包含some_lib.h,但是some_lib.cc可能跟希望用"include/some_lib.h"方式包含。为了使得包含路径有效,需要在third_party/some_lib/BUILD文件中将some_lib/目录添加到头文件包含路径的搜索列表中:

[python]  view plain  copy
  1. cc_library(  
  2.     name = "some_lib",  
  3.     srcs = ["some_lib.cc"],  
  4.     hdrs = ["some_lib.h"],  
  5.     copts = ["-Ithird_party/some_lib"],  
  6. )  

这对于依赖的外部第三方库特别有效,因为可以避免在头文件路径中出现无关的external/[repository-name]/前缀。

添加外部库:

假设使用了Google Test。可以在WORKSPACE文件中使用new_开头的仓库相关的函数,下载依赖的GTest代码到当前仓库中:

[python]  view plain  copy
  1. new_http_archive(  
  2.     name = "gtest",  
  3.     url = "https://googletest.googlecode.com/files/gtest-1.7.0.zip",  
  4.     sha256 = "247ca18dd83f53deb1328be17e4b1be31514cedfc1e3424f672bf11fd7e0d60d",  
  5.     build_file = "gtest.BUILD",  
  6. )  
创建gtest.BUILD文件,对应Google Test的构建配置文件。配置文件中有几个需要特别注意的地方:

  • gtest-1.7.0/src/gtest-all.cc文件已经采用#include语法包含了gtest-1.7.0/src/目录中其它*.cc文件,因此需要将它排除在外(也可以只包含它一个文件,但是需要正确配置包含路径)。
  • 它的头文件在gtest-1.7.0/include/目录,需要将它添加到头文件包含路径列表中
  • GTest依赖pthread多线程库,通过linkopt选项指定。

最终的规则大概是这样:

[python]  view plain  copy
  1. cc_library(  
  2.     name = "main",  
  3.     srcs = glob(  
  4.         ["gtest-1.7.0/src/*.cc"],  
  5.         exclude = ["gtest-1.7.0/src/gtest-all.cc"]  
  6.     ),  
  7.     hdrs = glob([  
  8.         "gtest-1.7.0/include/**/*.h",  
  9.         "gtest-1.7.0/src/*.h"  
  10.     ]),  
  11.     copts = [  
  12.         "-Iexternal/gtest/gtest-1.7.0/include"  
  13.     ],  
  14.     linkopts = ["-pthread"],  
  15.     visibility = ["//visibility:public"],  
  16. )  

这是有点混乱:所有以gtest-1.7.0为前缀的其实都是生成的临时文件。我们可以通过new_http_archive函数中的strip_prefix属性来忽略它:

[python]  view plain  copy
  1. new_http_archive(  
  2.     name = "gtest",  
  3.     url = "https://googletest.googlecode.com/files/gtest-1.7.0.zip",  
  4.     sha256 = "247ca18dd83f53deb1328be17e4b1be31514cedfc1e3424f672bf11fd7e0d60d",  
  5.     build_file = "gtest.BUILD",  
  6.     strip_prefix = "gtest-1.7.0",  
  7. )  

现在gtest.BUILD简洁多了:

[python]  view plain  copy
  1. cc_library(  
  2.     name = "main",  
  3.     srcs = glob(  
  4.         ["src/*.cc"],  
  5.         exclude = ["src/gtest-all.cc"]  
  6.     ),  
  7.     hdrs = glob([  
  8.         "include/**/*.h",  
  9.         "src/*.h"  
  10.     ]),  
  11.     copts = ["-Iexternal/gtest/include"],  
  12.     linkopts = ["-pthread"],  
  13.     visibility = ["//visibility:public"],  
  14. )  

现在cc_相关的规则可以通过//external:gtest/main引用GTest了。

测试./test/hello-test.cc

[python]  view plain  copy
  1. #include "gtest/gtest.h"  
  2. #include "lib/hello-greet.h"  
  3.   
  4. TEST(FactorialTest, Negative) {  
  5.   EXPECT_EQ(get_greet("Bazel"), "Hello Bazel");  
  6. }  
./test/BUILD

[python]  view plain  copy
  1. cc_test(  
  2.     name = "hello-test",  
  3.     srcs = ["hello-test.cc"],  
  4.     copts = ["-Iexternal/gtest/include"],  
  5.     deps = [  
  6.         "@gtest//:main",  
  7.         "//lib:hello-greet",  
  8.     ],  
  9. )  
lib/BUILD:

[python]  view plain  copy
  1. cc_library(  
  2.     name = "hello-greet",  
  3.     srcs = ["hello-greet.cc"],  
  4.     hdrs = ["hello-greet.h"],  
  5.     visibility = ["//test:__pkg__"],  
  6. )  


依赖预编译的库

如果要依赖一个已经编译好的库(可能只有头文件和对应的*.so库文件),可以使用cc_library规则包装一个库对象:

[python]  view plain  copy
  1. cc_library(  
  2.     name = "mylib",  
  3.     srcs = ["mylib.so"],  
  4.     hdrs = ["mylib.h"],  
  5. )  
其它的目标就可以依赖这个包装的库对象了。

参考:

https://my.oschina.NET/chai2010/blog/674110

https://my.oschina.net/chai2010/blog/701807

https://bazel.build/versions/master/docs/tutorial/cpp.html

https://bazel.build/versions/master/docs/tutorial/java.html

https://bazel.build/versions/master/docs/bazel-user-manual.html


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值