您的位置:首页 > 编程语言 > C语言/C++

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选项,关于库的依赖顺序

前言

无论在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>
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  makefile 编译链接