c++ 编译链接,makefile思路整理

来源:互联网 发布:上海交大 知乎 编辑:程序博客网 时间:2024/06/03 13:21

长虹剑自己的一些经验总结,具体请自行根据实际情况决定。
(未完且待细节补充 )
- 2016-10-25 补充了一个makefile模板
- 2016-10-27 补充了cmake及cmake生成nmake,整理了windows下有关编译的目录,有助于理解vs的x86和x64编译过程。

前言

无论在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的错误,表明找不到函数的定义(注意不是声明,如果是声明的话就是编译时候的问题了)

需要注意的事项

  1. 指定一个静态库一般使用静态库绝对路径 如 /path/libxxx.a ,动态库一般就 -L /path -lxxx,或者使用 (-Wl,-Bstatic -lxxx 链接静态, -Wl,-Bdynamic 动态 )
  2. 动态库的指定可能有一定的顺序, 一般来说最好先是自己的库,然后是三方库,然后是系统库。有时候链接不上但是位置正确,可能会是顺序问题(我觉得深层原因还是库名冲突),最佳的顺序是g++ ... obj($?) -l(上层逻辑lib) -l(中间封装lib) -l(基础lib) -l(系统lib) -o $@
  3. 如果你把库路径写在了环境变量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,--as-needed 忽略链接时没有用到的动态库
-g -O0 就是生成可调试的程序(且不让编译器优化)之后用 gdb -tui 调试即可

生成静态库时
一般 ar -cvq -o your.a *.o 就行。【具体查看别的博客吧】
我觉得静态库就是若干个.o文件的组合吧,可用 ar -t 查看一下

生成静态库
需要使用 -shared -fPIC 其实这是两个选项,后面那个属于编译阶段的,前面是链接阶段,指明生成链接库。

编译源码的流程

一般是三个步骤
最好先mkdir build, cd build,然后进行主要工作

  1. ../configure : 这个就是一个 bash 脚本,它主要负责生成makefile【简单理解makefile也就是为了生成长长的g++命令选项 】 一般这个文件是很规范的,比如可以用 --enable-static=yes 指明生成静态库,最重要的一个是--prefix=your_install_path,这个就是说你把软件装在哪里(在第三点细讲)
  2. make 这个就是执行上面生成的make,一般为了快速编译用 make -j。生成的可执行程序,库文件都在当前的目录下面。
  3. 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/3rdpartyLDFLAGS = -L/home/chj/face/install/opencv2/lib -lopencv_core -lopencv_highgui -lopencv_features2dSTATICLIB = /home/chj/face/program/build/libliblinear.a /home/chj/face/program/build/liblib3000fps.a   # 便于直接makeall:mainTARGET = main# 寻找所有需要的 cppCHJ_SRCS := $(wildcard ../src/*.cpp) $(wildcard ../src/lbf/*.cpp)# 将上面找到的 .cpp 换成 .oCHJ_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>
1 0
原创粉丝点击