本文档主要总结了集中编译器的使用方法:主要包括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

注:第一种方法编译时需要所有文件重新编译

     第二种方法可以重新编译修改的文件,未修改的文件不要重新编译。

GCC可以用来编译C/C++、FORTRAN、JAVA、OBJC、ADA等语言的程序

来自: http://man.linuxde.net/gcc

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)在预编译上添加依赖