C/C++笔记 #035# Makefile

时间:2023-03-09 20:45:05
C/C++笔记 #035# Makefile

相关资料:

实践记录:

1、查看 g++ / gcc 的版本:

root@xkfx:~# gcc --version
gcc (Debian 4.9.-) 4.9.
Copyright (C) Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. root@xkfx:~# gcc -v # 详细版本

2、You can get the help manual via the --help option.

root@xkfx:~# gcc --help
Usage: gcc [options] file...
Options:
-pass-exit-codes Exit with highest error code from a phase
--help Display this information
--target-help Display target specific command line options
--help={common|optimizers|params|target|warnings|[^]{joined|separate|undocumented}}[,...]
Display specific types of command line options
(Use '-v --help' to display command line options of sub-processes)
--version Display compiler version information
-dumpspecs Display all of the built in spec strings
-dumpversion Display the version of the compiler
-dumpmachine Display the compiler's target processor
-print-search-dirs Display the directories in the compiler's search path
-print-libgcc-file-name Display the name of the compiler's companion library
-print-file-name=<lib> Display the full path to library <lib>
-print-prog-name=<prog> Display the full path to compiler component <prog>
-print-multiarch Display the target's normalized GNU triplet, used as
a component in the library path
-print-multi-directory Display the root directory for versions of libgcc
-print-multi-lib Display the mapping between command line options and
multiple library search directories
-print-multi-os-directory Display the relative path to OS libraries
-print-sysroot Display the target libraries directory
-print-sysroot-headers-suffix Display the sysroot suffix used to find headers
-Wa,<options> Pass comma-separated <options> on to the assembler
-Wp,<options> Pass comma-separated <options> on to the preprocessor
-Wl,<options> Pass comma-separated <options> on to the linker
-Xassembler <arg> Pass <arg> on to the assembler
-Xpreprocessor <arg> Pass <arg> on to the preprocessor
-Xlinker <arg> Pass <arg> on to the linker
-save-temps Do not delete intermediate files
-save-temps=<arg> Do not delete intermediate files
-no-canonical-prefixes Do not canonicalize paths when building relative
prefixes to other gcc components
-pipe Use pipes rather than intermediate files
-time Time the execution of each subprocess
-specs=<file> Override built-in specs with the contents of <file>
-std=<standard> Assume that the input sources are for <standard>
--sysroot=<directory> Use <directory> as the root directory for headers
and libraries
-B <directory> Add <directory> to the compiler's search paths
-v Display the programs invoked by the compiler
-### Like -v but options quoted and commands not executed
-E Preprocess only; do not compile, assemble or link
-S Compile only; do not assemble or link
-c Compile and assemble, but do not link
-o <file> Place the output into <file>
-pie Create a position independent executable
-shared Create a shared library
-x <language> Specify the language of the following input files
Permissible languages include: c c++ assembler none
'none' means revert to the default behavior of
guessing the language based on the file's extension Options starting with -g, -f, -m, -O, -W, or --param are automatically
passed on to the various sub-processes invoked by gcc. In order to pass
other options on to these processes the -W<letter> options must be used. For bug reporting instructions, please see:
<file:///usr/share/doc/gcc-4.9/README.Bugs>.

3、You can read the GCC manual pages (or man pages) via the man utility:

root@xkfx:~# man gcc
No manual entry for gcc
See 'man 7 undocumented' for help when manual pages are not available.

4、More GCC Compiler Options

root@xkfx:~/labs# g++ hello.cpp -Wall -g

-Wall: prints "all" warning messages.

-g: generates additional symbolic debuggging information for use with gdb debugger.

5、Compile and Link Separately

The above command compile the source file into object file and link with other object files (system library) into executable in one step. You may separate compile and link in two steps as follows:

root@xkfx:~/labs# ls
hello.cpp root@xkfx:~/labs# g++ hello.cpp -c -Wall -g // Compile-only with -c option
root@xkfx:~/labs# ls 
hello.cpp hello.o
root@xkfx:~/labs# g++ hello.o -o hello.exe -g // Link object file(s) into an executable
root@xkfx:~/labs# ls
hello.cpp hello.exe hello.o root@xkfx:~/labs# ./hello.exe
hello,

6、对于多个源文件编译成一个可执行文件,比较好的实践是,分开编译再链接,这样修改一个源文件就不必再编译所有别的源文件:

root@xkfx:~/labs# g++ hello.cpp hello2.cpp -o hello -g
/tmp/ccGNmFaS.o: In function `main':
/root/labs/hello2.cpp:: multiple definition of `main'
/tmp/ccsm0AxM.o:/root/labs/hello.cpp:: first defined here
collect2: error: ld returned exit status
root@xkfx:~/labs# g++ hello.cpp hello2.cpp -o hello
/tmp/cc1ASkoC.o: In function `main':
hello2.cpp:(.text+0x0): multiple definition of `main'
/tmp/cc4ImNYi.o:hello.cpp:(.text+0x0): first defined here
collect2: error: ld returned exit status

上面是错误的示范,顺便可以看到有 -g 和没 -g 的区别,正确姿势:

root@xkfx:~/labs# g++ hello.cpp -c
root@xkfx:~/labs# g++ hello2.cpp -c
root@xkfx:~/labs# g++ hello.o hello2.o -o hello
hello2.o: In function `main':
hello2.cpp:(.text+0x0): multiple definition of `main'
hello.o:hello.cpp:(.text+0x0): first defined here
collect2: error: ld returned exit status

这样一旦修改了 hello.cpp 就只需要再次编译 hello.cpp 然后重新链接就可以了。

7、To compile and link C/C++ program into a shared library (".dll" in Windows, ".so" in Unixes), use -shared option. Read "Java Native Interface" for example.

于是顺手查了下 JNI 是怎么回事:

jNi就是java调用本地方法的技术,最简单的来说,java运行一个程序需要要和不同的系统平台打交道,在windows里就是和windows平台底层打交道,mac就是要和mac打交道,jvm就是通过大量的jni技术使得java能够在不同平台上运行。而使用了这技术的一个标志就是native,如果一个类里的一个方法被native修饰,那就说明这个方法是jni来实现的,他是通过本地系统api里的方法来实现的。当然这个本地方法可能是c或者C++,当然也可能是别的语言。jni是java跨平台的基础,jvm通过在不同系统上调用不同的本地方法使得jvm可以在不同平台间移植。
当前你自己也可以用jni来写一些程序,这种基本上是你以前使用了其他语言完成了一些功能,但是你有要用java来重复这些功能的时候,就可以使用jni来完成了。不过带来的问题就是,如果你的那个本地方法是依托于本地操作系统的话,那就意味着你的java程序也只能在这一个系统上运行了。所以jni就使得java很容易限定在了一个系统平台上,而jdk的作用在于他提供一个规范,这个规范就是包含了很多native方法,这些方法都是要本地操作系统来实现的,而实现了这些本地方法的操作系统就可以移植java平台了。 ------ 来自 baidu daay1986

8、GCC Compilation Process 图片来自相关资料之二:

C/C++笔记 #035# Makefile

assembly code  汇编代码

9、You can see the detailed compilation process by enabling -v (verbose) option.  详细;啰嗦

root@xkfx:~/labs# g++ hello.cpp -v
Using built-in specs.
COLLECT_GCC=g++
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/4.9/lto-wrapper
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Debian 4.9.2-10' --with-bugurl=file:///usr/share/doc/gcc-4.9/README.Bugs --enable-languages=c,c++,java,go,d,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-4.9 --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --with-gxx-include-dir=/usr/include/c++/4.9 --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --enable-gnu-unique-object --disable-vtable-verify --enable-plugin --with-system-zlib --disable-browser-plugin --enable-java-awt=gtk --enable-gtk-cairo --with-java-home=/usr/lib/jvm/java-1.5.0-gcj-4.9-amd64/jre --enable-java-home --with-jvm-root-dir=/usr/lib/jvm/java-1.5.0-gcj-4.9-amd64 --with-jvm-jar-dir=/usr/lib/jvm-exports/java-1.5.0-gcj-4.9-amd64 --with-arch-directory=amd64 --with-ecj-jar=/usr/share/java/eclipse-ecj.jar --enable-objc-gc --enable-multiarch --with-arch-32=i586 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
Thread model: posix
gcc version 4.9. (Debian 4.9.-)
COLLECT_GCC_OPTIONS='-v' '-shared-libgcc' '-mtune=generic' '-march=x86-64'
/usr/lib/gcc/x86_64-linux-gnu/4.9/cc1plus -quiet -v -imultiarch x86_64-linux-gnu -D_GNU_SOURCE hello.cpp -quiet -dumpbase hello.cpp -mtune=generic -march=x86- -auxbase hello -version -o /tmp/ccqPyWpO.s
GNU C++ (Debian 4.9.-) version 4.9. (x86_64-linux-gnu)
compiled by GNU C version 4.9., GMP version 6.0., MPFR version 3.1.-p3, MPC version 1.0.
GGC heuristics: --param ggc-min-expand= --param ggc-min-heapsize=
ignoring duplicate directory "/usr/include/x86_64-linux-gnu/c++/4.9"
ignoring nonexistent directory "/usr/local/include/x86_64-linux-gnu"
ignoring nonexistent directory "/usr/lib/gcc/x86_64-linux-gnu/4.9/../../../../x86_64-linux-gnu/include"
#include "..." search starts here:
#include <...> search starts here:
/usr/include/c++/4.9
/usr/include/x86_64-linux-gnu/c++/4.9
/usr/include/c++/4.9/backward
/usr/lib/gcc/x86_64-linux-gnu/4.9/include
/usr/local/include
/usr/lib/gcc/x86_64-linux-gnu/4.9/include-fixed
/usr/include/x86_64-linux-gnu
/usr/include
End of search list.
GNU C++ (Debian 4.9.-) version 4.9. (x86_64-linux-gnu)
compiled by GNU C version 4.9., GMP version 6.0., MPFR version 3.1.-p3, MPC version 1.0.
GGC heuristics: --param ggc-min-expand= --param ggc-min-heapsize=
Compiler executable checksum: ff85870e740eb08c48a56c2c170390f8
COLLECT_GCC_OPTIONS='-v' '-shared-libgcc' '-mtune=generic' '-march=x86-64'
as -v -- -o /tmp/cc9l3HyT.o /tmp/ccqPyWpO.s
GNU assembler version 2.25 (x86_64-linux-gnu) using BFD version (GNU Binutils for Debian) 2.25
COMPILER_PATH=/usr/lib/gcc/x86_64-linux-gnu/4.9/:/usr/lib/gcc/x86_64-linux-gnu/4.9/:/usr/lib/gcc/x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/4.9/:/usr/lib/gcc/x86_64-linux-gnu/
LIBRARY_PATH=/usr/lib/gcc/x86_64-linux-gnu/4.9/:/usr/lib/gcc/x86_64-linux-gnu/4.9/../../../x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/4.9/../../../../lib/:/lib/x86_64-linux-gnu/:/lib/../lib/:/usr/lib/x86_64-linux-gnu/:/usr/lib/../lib/:/usr/lib/gcc/x86_64-linux-gnu/4.9/../../../:/lib/:/usr/lib/
COLLECT_GCC_OPTIONS='-v' '-shared-libgcc' '-mtune=generic' '-march=x86-64'
/usr/lib/gcc/x86_64-linux-gnu/4.9/collect2 -plugin /usr/lib/gcc/x86_64-linux-gnu/4.9/liblto_plugin.so -plugin-opt=/usr/lib/gcc/x86_64-linux-gnu/4.9/lto-wrapper -plugin-opt=-fresolution=/tmp/ccVqDgIY.res -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lc -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lgcc --sysroot=/ --build-id --eh-frame-hdr -m elf_x86_64 --hash-style=gnu -dynamic-linker /lib64/ld-linux-x86-.so. /usr/lib/gcc/x86_64-linux-gnu/4.9/../../../x86_64-linux-gnu/crt1.o /usr/lib/gcc/x86_64-linux-gnu/4.9/../../../x86_64-linux-gnu/crti.o /usr/lib/gcc/x86_64-linux-gnu/4.9/crtbegin.o -L/usr/lib/gcc/x86_64-linux-gnu/4.9 -L/usr/lib/gcc/x86_64-linux-gnu/4.9/../../../x86_64-linux-gnu -L/usr/lib/gcc/x86_64-linux-gnu/4.9/../../../../lib -L/lib/x86_64-linux-gnu -L/lib/../lib -L/usr/lib/x86_64-linux-gnu -L/usr/lib/../lib -L/usr/lib/gcc/x86_64-linux-gnu/4.9/../../.. /tmp/cc9l3HyT.o -lstdc++ -lm -lgcc_s -lgcc -lc -lgcc_s -lgcc /usr/lib/gcc/x86_64-linux-gnu/4.9/crtend.o /usr/lib/gcc/x86_64-linux-gnu/4.9/../../../x86_64-linux-gnu/crtn.o

10、You can use the -Dname option to define a macro, or -Dname=value to define a macro with a value. The value should be enclosed in double quotes if it contains spaces. 例如说:

#include <iostream>
using namespace std; int main()
{
cout << HELLO << endl;
return ;
}
root@xkfx:~/labs# g++ hello.cpp -DHELLO= -g
root@xkfx:~/labs# ./a.out

11、Static Libraries (.lib, .a) vs. Shared Library (.dll, .so)

前者是直接把外部代码拷贝进你的代码,然后形成可执行文件。

后者则是在你的可执行程序跑起来之前,操作系统加载外部代码给你的程序调用。

因为后者节省空间,内存(好几个程序可以共用一个 share library),GCC 总是会在可能的情况下优先采用动态链接。

12、Searching for Header Files and Libraries (-I, -L and -l)

When compiling the program, the compiler needs the header files to compile the source codes; the linker needs the libraries to resolve external references from other object files or libraries. The compiler and linker will not find the headers/libraries unless you set the appropriate options, which is not obvious for first-time user.

For each of the headers used in your source (via #include directives), the compiler searches the so-called include-paths for these headers. The include-paths are specified via -Idir option (or environment variable CPATH). Since the header's filename is known (e.g., iostream.h, stdio.h), the compiler only needs the directories.

The linker searches the so-called library-paths for libraries needed to link the program into an executable. The library-path is specified via -Ldir option (uppercase 'L' followed by the directory path) (or environment variable LIBRARY_PATH). In addition, you also have to specify the library name. In Unixes, the library libxxx.a is specified via -lxxx option (lowercase letter 'l', without the prefix "lib" and ".a" extension). In Windows, provide the full name such as -lxxx.lib. The linker needs to know both the directories as well as the library names. Hence, two options need to be specified.

13、尝试通过“cpp -v”列出“GNU C预处理器”使用的系统中的默认 include路径:

root@xkfx:~/labs# cpp -v
// ..........................
#include "..." search starts here:
#include <...> search starts here:
/usr/lib/gcc/x86_64-linux-gnu/4.9/include
/usr/local/include
/usr/lib/gcc/x86_64-linux-gnu/4.9/include-fixed
/usr/include/x86_64-linux-gnu
/usr/include
End of search list.

14、Try running the compilation in verbose mode (-v) to study the library-paths (-L) and libraries (-l) used in your system:

C/C++笔记 #035# Makefile

15、通过 file 查看文件类型:

root@xkfx:~/labs# file hello.o
hello.o: ELF -bit LSB relocatable, x86-, version (SYSV), not stripped
root@xkfx:~/labs# file a.out
a.out: ELF -bit LSB executable, x86-, version (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-.so., for GNU/Linux 2.6., BuildID[sha1]=242ba76e8db35435fd054bf0d930f6ed2ca9375c, not stripped
root@xkfx:~/labs# file hello.cpp
hello.cpp: C source, ASCII text

通过 nm 列出特性文件中的符号信息(像是函数啥的), linux中的nm命令简介 这篇文章很生动,。(附带动态链接库、静态链接库怎么操作)

通过 ldd 列出动态链接库,同一位作者  linux中的ldd命令简介

在实际linux开发与调试中, 要经常查看动态库依赖关系, ldd用得还是比较多的, 特别是出现故障的时候。OK, ldd命令就简单介绍到这里了, 虽然简单, 但很实用, 故不可不知。

16、

root@xkfx:~/labs# vim hello.c
root@xkfx:~/labs# vim makefile
root@xkfx:~/labs# ls
hello.c makefile
root@xkfx:~/labs# make
gcc -c hello.c
gcc -o hello.exe hello.o
root@xkfx:~/labs# ls
hello.c hello.exe hello.o makefile

makefile 内容:

all: hello.exe

hello.exe: hello.o
gcc -o hello.exe hello.o hello.o: hello.c
gcc -c hello.c clean:
rm hello.o hello.exe

make 指令在没有目标的时候默认就是  make all .

A rule consists of 3 parts: a target, a list of pre-requisites and a command (目标,前提条件,命令), as follows:

target: pre-req- pre-req- ...
command

command 前面必须是 Tab 而不能是 Space

makefile 的内容定义了 make 指令在该目录下的行为,

例如输入 make clean , clean 是目标,没有先决条件,那么就执行命令 rm hello.o hello.exe

输入 make all ,先决条件是 hello.exe 如果没发现,那么就搜索指令去制造它。

在制造 hello.exe 的时候,会检查 hello.o 是不是“新鲜”,(大概是比较创建文档的时间?),如果“新鲜”才会制造,否则的话:

root@xkfx:~/labs# make
make: Nothing to be done for 'all'.

现在想正常跑 make,一个方法是 make  clean 一下:

root@xkfx:~/labs# make clean
rm hello.o hello.exe
root@xkfx:~/labs# ls
hello.c makefile
root@xkfx:~/labs# make
gcc -c hello.c
gcc -o hello.exe hello.o

另一个方法就是让 hello.c 变“新鲜”(不用修改,打开后重新 wq保存一下就可以了。):

root@xkfx:~/labs# ls
hello.c hello.exe hello.o makefile
root@xkfx:~/labs# vim hello.c
root@xkfx:~/labs# make
gcc -c hello.c
gcc -o hello.exe hello.o