c++ 编译链接,makefile思路整理
2016-09-22 10:52
549 查看
长虹剑自己的一些经验总结,具体请自行根据实际情况决定。
(未完且待细节补充 )
- 2016-10-25 补充了一个makefile模板
- 2016-10-27 补充了cmake及cmake生成nmake,整理了windows下有关编译的目录,有助于理解vs的x86和x64编译过程。
- 2017-6-7 加入了一个gcc选项,关于库的依赖顺序
至于说有关makefile的工具,windows下有nmake,linux下有make,还有一些稍有封装的工具cmake,qmake。无论是哪种只要明白了编译链接的目的(不是原理啊)才能很好地写出来。
要注意把编译和链接两个过程区分开,不要混为一谈
检查是否符合c++语法(c++11等)
处理一些宏定义(需要用
看变量函数有没有定义,其实同第一点,如果你额外引入一些库就用
这些错误是编译阶段的错误,如果你make的时候发现有这些错误,一定不要想什么缺库文件。不过一般编译阶段的错误非常容易解决这里就不多说了。
编译完之后一般对每个.cpp 生成一个.o
编译阶段没有错误,并不代表你程序写的没有问题。如果你对链接过程不熟悉,很可能写出来函数或变量重定义的错误。比如说你把一个函数func实现写在一个.h中,结果在两个.cpp中都引用了这个文件,结果生成两个.o,而这两个.o文件都有func的实现(假设在同一个作用域中),一旦他们同时编入到第三个cpp生成的.o中就会导致冲突。
不仅仅是函数,非静态或者非常量变量也是一样的道理。所以一般.h中定义static的变量,让其他cpp用
【这个问题非常严重,而且你不懂这个原理很难解决】
编译的过程主要检查的是你用一个函数有没有对其进行声明,而链接的过程则是你声明了,有没有给出其实现的方法(即使你没有实现,你给的.so .a 里面也要有实现)如果没有找到就会报
需要注意的事项
指定一个静态库一般使用静态库绝对路径 如
动态库的指定可能有一定的顺序, 一般来说最好先是自己的库,然后是三方库,然后是系统库。有时候链接不上但是位置正确,可能会是顺序问题(我觉得深层原因还是库名冲突),最佳的顺序是
如果你把库路径写在了环境变量
编译完成之后其实自己可以用
如果你编译生成的是静态库或动态库,你还可以用
生成静态库时
一般 ar -cvq -o your.a *.o 就行。【具体查看别的博客吧】
我觉得静态库就是若干个.o文件的组合吧,可用 ar -t 查看一下
生成静态库
需要使用
最好先mkdir build, cd build,然后进行主要工作
安装源码一般就是使用其库文件之类的,自己编译的时候就指定你安装的目录的头文件和库文件。
安装好后,删掉build就行。此外 make uninstall 就是卸载了。
跳出makefile的外表后,你就知道一般编译个小项目没必要写makefile了,如果是自己写的复杂的项目可以使用cmake。
生成nmake的makefile同时使用Release模式
下面进入有关windows下的链接部分。
注意:上面加中括号表示那时x64位程序的目录所以如果使用cmake生成nmake文件时一定要注意选好合适的编译器。
如果要用nmake就需要指定其目录,可以用相应的vs版本的nmake。比如说如果要用vs2015 64位编译就先在命令行中运行
如果写在batch脚本中就在前面加个 start
下面是我的一个模板,自行修改相应的位置(目录最好用绝对路径)
(未完且待细节补充 )
- 2016-10-25 补充了一个makefile模板
- 2016-10-27 补充了cmake及cmake生成nmake,整理了windows下有关编译的目录,有助于理解vs的x86和x64编译过程。
- 2017-6-7 加入了一个gcc选项,关于库的依赖顺序
前言
无论在windows上还是linux上我们都遇到很多编译链接的问题,如果对这些了解不透彻,那么makefile这些都是写不好的,即使用vs这样的软件,如果你没有清晰的思路盲目修改,那永远走不出泥潭。至于说有关makefile的工具,windows下有nmake,linux下有make,还有一些稍有封装的工具cmake,qmake。无论是哪种只要明白了编译链接的目的(不是原理啊)才能很好地写出来。
要注意把编译和链接两个过程区分开,不要混为一谈
编译
编译中和我们相关的主要过程其实就是检查是否符合c++语法(c++11等)
处理一些宏定义(需要用
-D指出)
看变量函数有没有定义,其实同第一点,如果你额外引入一些库就用
-I参数指明它头文件的位置这是编译阶段最有可能出错的地方
这些错误是编译阶段的错误,如果你make的时候发现有这些错误,一定不要想什么缺库文件。不过一般编译阶段的错误非常容易解决这里就不多说了。
编译完之后一般对每个.cpp 生成一个.o
提醒
编译阶段没有错误,并不代表你程序写的没有问题。如果你对链接过程不熟悉,很可能写出来函数或变量重定义的错误。比如说你把一个函数func实现写在一个.h中,结果在两个.cpp中都引用了这个文件,结果生成两个.o,而这两个.o文件都有func的实现(假设在同一个作用域中),一旦他们同时编入到第三个cpp生成的.o中就会导致冲突。
不仅仅是函数,非静态或者非常量变量也是一样的道理。所以一般.h中定义static的变量,让其他cpp用
【这个问题非常严重,而且你不懂这个原理很难解决】
经验
有时候自己写程序,本来已经有了头文件但是还是说某个函数或变量找不到,这时可能是没有使用using的原因。链接
这个是重头戏,搞makefile的人,尤其是在linux对这个过程吃尽了苦头。编译的过程主要检查的是你用一个函数有没有对其进行声明,而链接的过程则是你声明了,有没有给出其实现的方法(即使你没有实现,你给的.so .a 里面也要有实现)如果没有找到就会报
undefined reference的错误,表明找不到函数的定义(注意不是声明,如果是声明的话就是编译时候的问题了)
需要注意的事项
指定一个静态库一般使用静态库绝对路径 如
/path/libxxx.a,动态库一般就
-L /path -lxxx,或者使用 (
-Wl,-Bstatic -lxxx链接静态,
-Wl,-Bdynamic动态 )
动态库的指定可能有一定的顺序, 一般来说最好先是自己的库,然后是三方库,然后是系统库。有时候链接不上但是位置正确,可能会是顺序问题(我觉得深层原因还是库名冲突),最佳的顺序是
g++ ... obj($?) -l(上层逻辑lib) -l(中间封装lib) -l(基础lib) -l(系统lib) -o $@
如果你把库路径写在了环境变量
LD_LIBRARY_PATH中,格式最好是
export LD_LIBRARY_PATH=your_lib_path:$LD_LIBRARY_PATH,就是你自己的库路径优先。这个还有一个好处是,如果你编译成功,但是运行的时候提示某个.so文件 not found, 这样可以解决,否则你在编译的时候用 运行时查找 的编译选项
-Wl,-rpath,your_lib_path。
提醒
链接时候的两大问题,一个是链接不到库,一个是链接不到你想要的库(譬如你不想用系统自带的某个库之类的),其实解决方法是一样的。【要记住上面三点,自行选择】运行
一般编译链接成功就能运行,运行时说缺少某些库那就参照 链接->注意事项的第三点的黑体部分,一般都能解决。编译完成之后其实自己可以用
ldd命令看看可执行程序及.so文件看看有没有not found的,即使没有not found 也看看对不对。
如果你编译生成的是静态库或动态库,你还可以用
readelf -d大致看看是不是你想要的(你想编的有没有编进去)。
g++ 一般编译选项介绍
这个还是搜搜其他博客吧比较全,一方面我也有很多不同,另一方面写多了就主次不分了-Wl后面跟着的参数是传递给链接器ld的
-Wl,--as-needed忽略链接时没有用到的动态库
-g -O0就是生成可调试的程序(且不让编译器优化)之后用
gdb -tui调试即可
--start-group和--end-group放在这两个地方的库会自动解决依赖关系详见——再议GCC编译时的静态库依赖顺序问题。
生成静态库时
一般 ar -cvq -o your.a *.o 就行。【具体查看别的博客吧】
我觉得静态库就是若干个.o文件的组合吧,可用 ar -t 查看一下
生成静态库
需要使用
-shared -fPIC其实这是两个选项,后面那个属于编译阶段的,前面是链接阶段,指明生成链接库。
编译源码的流程
一般是三个步骤最好先mkdir build, cd build,然后进行主要工作
../configure: 这个就是一个 bash 脚本,它主要负责生成makefile【简单理解makefile也就是为了生成长长的g++命令选项 】 一般这个文件是很规范的,比如可以用
--enable-static=yes指明生成静态库,最重要的一个是
--prefix=your_install_path,这个就是说你把软件装在哪里(在第三点细讲)
make这个就是执行上面生成的make,一般为了快速编译用 make -j。生成的可执行程序,库文件都在当前的目录下面。
make install这一步就是安装,第二步生成的东西最后要装在系统的哪个位置呢?如果你不指定一般是在/usr 或 /usr/local 下面(这时大部分就需要用sudo执行了了),如果指定就是第一步中的prefix参数,这样安装的时候就不用root权限了。如果安装在自己的目录中,自行根据使用情况配置环境变量 (其实也可以不安装,毕竟需要的文件在第二步就已经有了)
安装源码一般就是使用其库文件之类的,自己编译的时候就指定你安装的目录的头文件和库文件。
安装好后,删掉build就行。此外 make uninstall 就是卸载了。
Makefile
作用:首先不要把makefile看的太神秘,它主要就是方便你编译,你完全可以自己用脚本写。一定要认识到你的目的是为了调用编辑器(如g++)编译你的程序,写makefile也是这个目的,它就是最终帮你生成了g++这个软件所需要参数,你的编译还是g++啊,编译的报错除了你程序的问题就是传给的g++参数不对啊,而这个参数不对又是因为你makefile写的不对导致的。跳出makefile的外表后,你就知道一般编译个小项目没必要写makefile了,如果是自己写的复杂的项目可以使用cmake。
一个makefile例子,可作为模板
CXX=g++ CXXFLAGS = -I/home/chj/face/program/include -I/home/chj/face/install/opencv2/include -I/home/chj/face/program/3rdparty LDFLAGS = -L/home/chj/face/install/opencv2/lib -lopencv_core -lopencv_highgui -lopencv_features2d STATICLIB = /home/chj/face/program/build/libliblinear.a /home/chj/face/program/build/liblib3000fps.a # 便于直接make all:main TARGET = main # 寻找所有需要的 cpp CHJ_SRCS := $(wildcard ../src/*.cpp) $(wildcard ../src/lbf/*.cpp) # 将上面找到的 .cpp 换成 .o CHJ_OBJS = $(patsubst %cpp, %o, $(CHJ_SRCS)) # $@ 为冒号之前的; $< 为冒号后第一个; $^ 为冒号后所有的 # 编译 %.o:%.cpp # $(warning $@) $(CXX) $(CXXFLAGS) -c $< -o $@ # 链接 (我这里某个文件有int main(),就直接生成可执行程序) $(TARGET):$(CHJ_OBJS) # 顺序很重要, $(STATICLIB) 放在前面还不行 $(CXX) $(LDFLAGS) -o $@ $^ $(STATICLIB)
cmake
cmake等我用在windows上的时候才觉得他真的方便。好多库都提供cmakelist然后直接用win下的cmake程序生成vs的项目,再用vs打开就行。常用的语法
cmake_minimum_required(VERSION 3.5)这个一般必须要确定你的cmake最低版本
project(XXX)给你的项目确定个名字,可能被用作默认的生成名XXX.exe
SET(CMAKE_CXX_FLAGS_RELEASE "${ENV{CXXFLAGS} -O3 -Wall")这个set命令就是赋值的意思,所附的值可能是系统变量也可以是自己的变量。
一些常用的变量如下
下面这些变量一般都有默认值CMAKE_C_COMPILER
CMAKE_CXX_COMPILER
CMAKE_CXX_FLAGS_DEBUG
CXXFLAGS这个就是编译时候的选项
CMAKE_SOURCE_DIR这个是你cmakelist.txt的目录
include_directories()这个是添加头文件的目录,一个可以写多个目录
link_directories添加库文件的目录
add_executable(face_align ${SOURCE_FILES})这就是你添加的生成的程序,后面是.cpp文件
target_link_libraries(face_align ${OpenCV_LIBS} dlib.lib )添加生成你的程序额外需要的库,如果只写库的名字,那么库的目录就必须在
link_directories中给出
命令行中的语法
举个例子cmake -G "NMake Makefiles" -DCMAKE_BUILD_TYPE=release ..
生成nmake的makefile同时使用Release模式
例子:编译库
假设你的.cpp在同一个目录下FILE(GLOB hdrs_base "*.h" ) FILE(GLOB srcs_base "*.cpp") FILE(GLOB hdrs ${hdrs_base} ) FILE(GLOB srcs ${srcs_base} ) ADD_LIBRARY(${PROJECT_NAME} ${srcs} ${hdrs}) # 应该是通过加 SHARED or STATIC 来决定生成什么库
下面进入有关windows下的链接部分。
win7下有关编译目录的整理
windows下和编译连接相关的几个文件为编译器cl.exe 链接器link.exe 资源编译器rc.exe 这里有较全的介绍cl 与 link一般在
C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\bin\[amd64|...]
rc一般在
C:\Program Files (x86)\Windows Kits\8.1\bin\[x64]
注意:上面加中括号表示那时x64位程序的目录所以如果使用cmake生成nmake文件时一定要注意选好合适的编译器。
nmake (win下非IDE编译)
不想细讲语法了,至今没有直接用过,一般直接借助win下的cmake然后用vs了,或者用cmake生成nmake需要的文件,或者装上mingw然后直接make。如果要用nmake就需要指定其目录,可以用相应的vs版本的nmake。比如说如果要用vs2015 64位编译就先在命令行中运行
"C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x64
如果写在batch脚本中就在前面加个 start
用visual studio编译时的配置及经验
一般你编写的项目总是需要依赖很多库的,比如说opencv,因此你的项目需要至少指定opencv 头文件所在的目录,库文件所在的目录,每次这样指定很麻烦,不妨自己写个.props然后在属性管理器中加入。
下面是我的一个模板,自行修改相应的位置(目录最好用绝对路径)
ExecutablePath这个可以不要。
<?xml version="1.0" encoding="utf-8"?> <Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <ImportGroup Label="PropertySheets" /> <PropertyGroup Label="UserMacros" /> <PropertyGroup> <ExecutablePath>opencv\3.1.0\opencv\build\x64\vc12\bin;$(ExecutablePath)</ExecutablePath> </PropertyGroup> <PropertyGroup> <IncludePath>opencv\3.1.0\opencv\build\include;$(IncludePath)</IncludePath> </PropertyGroup> <PropertyGroup> <LibraryPath>opencv\3.1.0\opencv\build\x64\vc12\lib;$(LibraryPath)</LibraryPath> <LibraryWPath>$(LibraryWPath)</LibraryWPath> </PropertyGroup> <ItemDefinitionGroup> <Link> <AdditionalDependencies>opencv_world310d.lib;%(AdditionalDependencies)</AdditionalDependencies> </Link> </ItemDefinitionGroup> <ItemGroup /> </Project>
相关文章推荐
- C/C++中预编译(预处理)、编译、汇编、链接
- Windows下使用nmake编译C/C++的makefile
- Linux C/C++ 多目标文件的链接及其Makefile编写($<与$^)
- C/C++ 编译和链接过程
- 通用Makefile的编写和在项目工程中使用Makefile(包括动态库、静态库的链接、整个工程联合编译)
- Makefile详解-程序的编译和链接
- C++编译与链接
- Dev_c++下解决编译winsock和pthread 的链接问题C++&STL
- g++ 编译链接C++代码, 生成与使用静态库和动态库
- Linux C++ Mysql 编译链接
- C++ 模板的编译与链接
- C++常见编译/链接错误及其解决办法
- C++ 工程实践(12):C++ 编译链接模型精要
- ?C++编译链接时的一个小问题
- C/C++常见gcc编译链接错误解决方法
- C++编译与链接(1)-编译与链接过程
- 关于C++编译时内链接和外链接
- C/C++编译和链接过程详解 (重定向表,导出符号表,未解决符号表)
- C/C++ 跨平台交叉编译、静态库/动态库编译、MinGW、Cygwin、CodeBlocks使用原理及链接参数选项
- C++ 编译和链接