本文档主要总结了集中编译器的使用方法:主要包括bazel,gcc,g++
1.gcc
编译语言:C/C++/FORTRAN/JAVA/OBJC/ADA
- 语法:gcc(选项) (参数)
- 选项:
-o:指定生成的输出文件; -E:仅执行编译预处理; -S:将C代码转换为汇编代码; -wall:显示警告信息; -c:仅执行编译操作,不进行连接操作。
- 参数:
C源文件:指定C语言源代码文件。
- 实例
#假设源程序文件名为test.c #无选项编译链接 gcc test.c #将test.c预处理,汇编,编译并链接形成可执行文件,这里未指定输出文件,默认输出为a.out。 #选项 -o gcc test.c -o test #将test.c预处理,汇编,编译并链接形成可执行文件test。 #-o选项用来指定输出的文件名。 #选项 -E gcc -E test.c -o test.i #将test.c预处理输出test.i文件。 #选项-S gcc -S test.i #将预处理输出文件test.i汇编成test.s文件。 #选项-c gcc -c test.s #将汇编输出文件test.s链接成最终可执行文件test。 #无选项链接 gcc test.o -o test #将编译出文件test.o链接成最终可执行文件test。 #选项 -O gcc -O1 test.c -o test #使用编译优化级别1编译程序。级别为1~3,级别越大优化效果越好,但是编译时间越长。
- 多源文件的编译方法
如果有多个源文件,有两种编译方法:
#方法一:多个文件一起编译 gcc testfun.c test.c -o test #将testfun.c和test.c分别编译后链接成test可执行文件。 #方法二:分别编译各个源文件,之后对编译后输出的目标文件链接。 gcc -c testfun.c #将testfun.c编译成testfun.o gcc -c test.c #将test.c编译成test.o gcc -o testfun.o test.o -o test #将testfun.c和test.c链接成test
注:第一种方法编译时需要所有文件重新编译
第二种方法可以重新编译修改的文件,未修改的文件不要重新编译。
2.g++
- g++是linux下c++的编译器,在执行编译工作的时候,总共需要4步:
(1)预处理,生成*.i的文件;
(2)将预处理后的文件不转换成汇编语言,生成文件*.s;
(3)由汇编变为目标代码(机器代码)生成*.o的文件;
(4)连接目标代码,生成可执行程序。
- g++编译c++经常使用的参数
#-c:只编译,不连接 g++ -c helloworld.cpp #只生成helloworld.o不连接 #-o:指定输出文件名 g++ -c helloworld -o abc.o #默认是生成helloworld.o,用-o abc.o以后,就生成就是abc.o #-I:附加一个包含头文件的路径。 g++ helloworld.cpp -I"/usr/helloworld/include" #-l(小L):附一个库 g++ helloworld.cpp -labc #使用libabc.so # -L:添加一个库的路径 g++ hello.cpp -L"/usr/hello/lib" -labc #-shared:生成动态库文件 g++ -shared hello.cpp -o libhello.so
- 调用动态库的时候有几个问题会经常用到,有时候,明明已经库的头文件所在目录通过include进来了,库所在文件通过“-L”参数引导,并指定了“-l”的库名,但通过ldd命令查看时,就是死活找不到你指定链接的
so文件。其实编译链接上了共享库不代表执行时可以找到。所以“-L“什么的对执行没有用,你需要指明共享库的路径。方法有三个:
(1)修改LD_LIBRARY_PATH,指明共享库的路径。LD_LIBRARY_PATH:这个环境变量指示动态连接器可以装载动态库的路径。在终端下使用如下命令:
export LD_LIBRARY_PATH = your lib dir export
(2)通过/etc/ld.so.conf文件来指定动态库的目录。然后运行ldconfig命令更新搜索共享库的路径。通常这个做法就可以解决库无法链接的问题并且一劳永逸。
(3)把库文件拷贝到/lib下,然后ldconfig就ok了。这个方法有的取巧,且破坏原库文件的纯洁性,不推荐。
- linux下文件的类型是不依赖于其后缀名的,但是一般讲来:
*.o:是目标文件,相当于windows中的*.obj文件
*.so为共享库,是shared object,用于动态连接的,和dll差不多
*.a为静态库,是好多个*.o合在一起,用于静态连接
*.la为libtool自动生成的一些共享库,主要是记录了一些配置信息
3.bazel
参考:http://blog.163.com/wujiaxing009@126/blog/static/71988399201751110532137/
- 特点:
(1)多语言支持
JAVA/Objective-C和C++
(2)高级构建描述语言
项目是使用一种叫BUILD的语言来描述的,它是一种简简洁的文本语言,它把一个项目是为一个集合,这个集合由一些互相关联的库,二进制文件和测试用例组成。相反,像Make这样的工具,需要去描述每一个文件如何调用编译器。
(3)多平台支持
同一套工具和相同的BUILD文件可以用来为不同的体系结构构建软件,甚至是不同的平台。在google,bazel被同时用在数据中心系统中的服务器应用和手机端的移动应用上。
(4)可重复性
在BUILD文件中,每个库,测试用例和二进制文件都需要明确指定它们的依赖关系。当一个源文件被修改时,bazel凭这些依赖关系来判断哪些部分需要重新构建,以及那些任务可以并行进行。这就是意味着所有构建都是增量的,并且相同构建总是产生一样的结果。
(5)可伸缩性
bazel可以处理大型项目,在google,一个服务器软件由十几万行代码是很常见的,在什么都不改的前提下重新构建这样一个项目,大概需要200毫秒。
- bazel构建C++项目
(1)使用工作区
bazel builds应该在一个工作区内运行,这个工作区应该包括源代码和build输出目录的符号链接(如:bazel-bin、bazel-out)。工作区目录的位置是可以随意的,但是工作区的根目录必须包含一个名为WORKSPACE的工作区配置问价,工作区配置文件可以是一个空文件,也可以包含引用外表不构建输出所需的依赖关系,在一个工作区内,可根据需求共享多个项目。
(2)创建Build文件
bazel通过检查BUILD文件可以知道哪些目标文件被创建在项目中,这些BUILD文件采用与Python相似的语法所写,这种语言通常是一系列规则的声明,每个规则制定相应的输入、输出以及实现输入到输出的方法。
(3)通过一个例子学习
1)建立工作区
假设有个项目,对应于~/gitroot/my-project/目录,先创建一个空的~/gitroot/my-project/WORKSPACE工作区配置文件,用于表示这是Bazel项目对应的根目录。创立一个hello world工程。
└── my-project ├── lib │ ├── BUILD │ ├── hello-greet.cc │ └── hello-greet.h ├── main │ ├── BUILD │ ├── hello-time.cc │ ├── hello-time.h │ └── hello-world.cc └── WORKSPACE
2)创建源文件
1 $ # If you're not already there, move to your workspace directory. 2 $ cd ~/gitroot/my-project #进入项目根目录 3 $ mkdir ./main #创建一个main 的文件夹 4 $ cat > main/hello-world.cc <<'EOF' #在main文件夹中创建一个名为hello-world.cc的文件,并以EOF作为Linux创建文件结束的标志 5 #include "lib/hello-greet.h" #include lib中的hello-greet.h 6 #include "main/hello-time.h" #include main文件夹中的hello-time.h,而hello-time.cc则为hello-time.h的实现 7 #include <iostream> #标准库 8 #include <string> 9 10 int main(int argc, char** argv) { 11 std::string who = "world"; 12 if (argc > 1) { 13 who = argv[1]; 14 } 15 std::cout << get_greet(who) <<std::endl; 16 print_localtime(); 17 return 0; 18 } 19 EOF 20 $ cat > main/hello-time.h <<'EOF' #声明hello-time.cc是干什么事情的 21 #ifndef MAIN_HELLO_TIME_H_ 22 #define MAIN_HELLO_TIME_H_ 23 24 void print_localtime(); 25 26 #endif 27 EOF 28 $ cat > main/hello-time.cc <<'EOF'#具体hello-time.cc是怎么实现的 29 #include "main/hello-time.h" 30 #include <ctime> 31 #include <iostream> 32 33 void print_localtime() { 34 std::time_t result = std::time(nullptr); 35 std::cout << std::asctime(std::localtime(&result)); 36 } 37 EOF 38 $ mkdir ./lib 39 $ cat > lib/hello-greet.h <<'EOF' 40 #ifndef LIB_HELLO_GREET_H_ 41 #define LIB_HELLO_GREET_H_ 42 43 #include <string> 44 45 std::string get_greet(const std::string &thing); 46 47 #endif 48 EOF 49 $ cat > lib/hello-greet.cc <<'EOF' 50 #include "lib/hello-greet.h" 51 #include <string> 52 53 std::string get_greet(const std::string& who) { 54 return "Hello " + who; 55 } 56 EOF
3)添加BUILD文件
从上面的源代码可知,main/hello-world.cc要用到lib/hello-greet.h和main/hello-time.h。首先在lib目录下为hello-greet.cc创建BUILD.
1 cc_library( 2 name = "hello-greet", #要BUILD的文件的名字 3 srcs = ["hello-greet.cc"],#脚本 4 hdrs = ["hello-greet.h"],#头文件 5 visibility = ["//main:__pkg__"], 6 )
注意:visibility = ["//main:__pkg__"]表示hello-greet对于main/BUILD是可见的。
接下来在main目录下创建BUILD文件:
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 )
注意:当依赖的包在同一个目录下,只需要用:hello-time,当依赖的包在不同的目录下,需要用全路径://lib:hello-greet
现在可以建立hello world的c++二进制程序了:
1 $ bazel build main:hello-world 2 INFO: Found 1 target... 3 Target //main:hello-world up-to-date: 4 bazel-bin/main/hello-world 5 INFO: Elapsed time: 2.869s, Critical Path: 1.00s 6 $ ./bazel-bin/main/hello-world 7 Hello world 8 Thu Jun 23 18:51:46 2016 9 $ ./bazel-bin/main/hello-world Bazel 10 Hello Bazel 11 Thu Jun 23 18:52:10 2016
ok!成功建立了第一个bazel项目!
4)传递依赖(Transitive includes)
如果一个文件包含一个头文件,那么这个文件的规则也应该依赖与头文件的库,相反的,只有直接依赖需要被指定为依赖。例如,假设sandwich.h包括bread.h,而且bread.h包括flour.h,sandwich.h不包括flour.h,因此这个BUILD文件应该是这样:
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库。
5)添加包含路径(adding include paths)
有时候不能(或者不愿意)让依赖的文件包含咋工作区根目录的路径,现有的库可能已经拥有了包括与工作空间不匹配路径的目录。例如,假设有如下的目录结构:
1 └── my-project 2 ├── third_party 3 │ └── some_lib 4 │ ├── BUILD 5 │ ├── include 6 │ │ └── some_lib.h 7 │ └── some_lib.cc 8 └── WORKSPACE
bazel希望some_lib.h被包含在third_party/some_lib/include/some_lib.h中,但假定some_lib.cc要依赖于include/some_lib.h。为了使包含路径有效, third_party/some_lib/BUILD需要指定some_lib是一个包含目录:
1 cc_library( 2 name = "some_lib", 3 srcs = ["some_lib.cc"], 4 hdrs = ["some_lib.h"], 5 copts = ["-Ithird_party/some_lib"], 6 )
这对外部的依赖尤其有用,因为它们的头文件,否则必须被包含于外部/ [库名称]/前缀。
6)包含外部库
7)编译并运行c++测试程序
8)在预编译上添加依赖