C/C++ 头文件路径在编译时及工具中的设置

使用 C/CPP, 避免不了要和各种头文件打交道, 系统库还好, 基本上不需要操心, 已经被自动预置入头文件列表中了. 棘手的是使用第三方库, 这时就要手动指定其头文件位置与库文件位置. 本文记录下在终端中手工编译与某些工具内编译的设置方式.

himg

终端中使用 gcc/clang/makefile 手工编译

通常情况下, 我们可以使用 gcc -I/include -c test.c -o test.ogcc test.o -L/libs -o test 命令来分别指定头文件与库文件位置, 但是对于一个比较大的第三方库, 其头文件和库文件的数量是比较多的. 如果我们一个个手动地写, 那将是相当麻烦的. 所以, pkg-config 就应运而生了

简言之, pkg-config 为库提供编译与链接 flag 的功能.

pkg-config 安装

brew install pkg-config

pkg-config 常用命令

  • pkg-config --help: 查看帮助
  • pkg-config --list-all: 列出目前系统上所有支持 pkg-config 的库
  • pkg-config --cflags glib-2.0: 指定头文件
  • pkg-config --libs glib-2.0: 指定库文件

pkg-config 自然还有其他的功能, 可以通过 pkg-config --help 看到所有可用命令

使用 pkg-config 在终端进行编译

最基础的用法就是直接将 pkg-config --cflags --libs glib-2.0 作为 gcc 的参数之一写在其后.

# 注意 ` 是 grave/tilde 键, 不是单引号
$gcc main.c `pkg-config --cflags --libs glib-2.0` -o main

pkg-config 的原理及自定义位置

其实 pkg-config 所做的事情非常简单, 它通过第三方库定义的 .pc 文件进行头文件与库文件的定位, 例如 glib 的 pc 文件如下:

prefix=/usr/local/Cellar/glib/2.66.7
libdir=${prefix}/lib
includedir=${prefix}/include

bindir=${prefix}/bin
glib_genmarshal=${bindir}/glib-genmarshal
gobject_query=${bindir}/gobject-query
glib_mkenums=${bindir}/glib-mkenums

Name: GLib
Description: C Utility Library
Version: 2.66.7
Requires.private: libpcre >=  8.31
Libs: -L${libdir} -lglib-2.0 -L/usr/local/opt/gettext/lib -lintl
Libs.private: -Wl,-framework,CoreFoundation -Wl,-framework,Carbon -Wl,-framework,Foundation -Wl,-framework,AppKit -liconv -lm
Cflags: -I${includedir}/glib-2.0 -I${libdir}/glib-2.0/include -I/usr/local/opt/gettext/include

可以很清楚地看出, glib 在其 /usr/local/Cellar/glib/2.66.7/lib/pkgconfig/glib-2.0.pc 中已经定义好了相关的关键信息, 如 LibsCflags. 默认情况下 glib 会将其 pc 文件做一个软链接放置到 /usr/local/lib/pkgconfig

himg

某些软件可能没有自动创建软链接的功能, 或者我们自定义一个 .pc 文件, 那么这时就需要在 .zshrc 中添加如下配置:

# 添加自定义的 pkg-config 路径, 默认的路径为 /usr/local/lib/pkgconfig
PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/usr/local/opt/zlib/lib/pkgconfig
PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/usr/local/opt/ruby/lib/pkgconfig
export PKG_CONFIG_PATH

这样当我们使用 gcc main.c 'pkg-config --cflags --libs zlib' -o main 的时候 pkg-config 就会自动找到相应的 .pc 文件了

vim 中配置头文件

youcompleteme 依赖的头文件路径

ycm 所依赖的补全依赖的头文件路径有:

  • 系统的 C_INCLUDE_PATH / CPP_INCLUDE_PATH

  • ~/.vimrc 中定义的 set path=***

  • ycm 中定义的 .ycm_extra_conf 文件

    ycm 的 .ycm_extra_conf.py 我通常定义在 .vimrc 中, 作为一个固定配置

    # ~/.vimrc
    let g:ycm_global_ycm_extra_conf = '~/.ycm_extra_conf.py' " 默认配置文件路径
    

    ~/.ycm_extra_conf.py 文件中, flags 表示我们使用的配置选项, 其中的 /usr/local/include 就代表了将 Homebrew 安装的头文件进行补全提示, 如果不够还可以直接添加自定义路径

    # ~/.ycm_extra_conf.py
    flags = [
    '-Wall',
    '-Wextra',
    '-Werror',
    '-fexceptions',
    '-DNDEBUG',
    '-std=c11',
    '-x',
    'c',
    '-isystem',
    '/usr/include',
    '-isystem',
    '/usr/local/include', # 重要, 通过 homebrew 安装的头文件大部分都在这里
    '-isystem',
    '/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../lib/c++/v1',
    '-isystem',
    '/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include',
    ]
    

    当然如果每个第三方库的路径都需要手动添加的话那就太麻烦了, 我们可以通过简单的一个 python 方法将系统的 pkg-config 输出作为路径导入到 flags

    # ~/.ycm_extra_conf.py
    # 通过 pkg-config 便捷添加头文件路径到 ycm 补全
    def pkg_config(pkg):
      def not_whitespace(string):
        return not (string == '' or string == '\n')
      output = subprocess.check_output(['pkg-config', '--cflags', pkg]).decode().strip()
      return list(filter(not_whitespace, output.split(' ')))
    
    flags += pkg_config('glib-2.0')
    

以上路径都可以起到辅助 ycm 进行补全的作用.

ale 所依赖的头文件路径

alelint 工具, 可以支持很多种语言. 实际上 ale 就是多种 linter 的一个集合平台, 针对于不同的语言提供了多种 linter 进行支持, 我们选择其中的一种即可. 比如我选择了 clangd 作为 c / cpp 的 linter. ale 对每一个 linter 都提供了设置选项, 自然 clangd 也不例外:

# ~/.vimrc
# 添加自定义的pkg-config路径, 默认的路径为 /usr/local/lib/pkgconfig
PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/usr/local/opt/zlib/lib/pkgconfig
PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/usr/local/opt/ruby/lib/pkgconfig
export PKG_CONFIG_PATH

CPPFLAGS+=$(pkg-config --cflags glib-2.0 zlib ruby-3.0)
export CPPFLAGS

CFLAGS+=$(pkg-config --cflags glib-2.0 zlib ruby-3.0)
export CFLAGS

LDFLAGS+="-I/usr/local/opt/openjdk/include"
LDFLAGS+=$(pkg-config --libs glib-2.0 zlib ruby-3.0)
export LDFLAGS
let g:ale_linters = {
            \   'c': ['clangd'],
            \   'cpp': ['clangd'],
            \   'markdown': ['markdownlint'],
            \}

let g:ale_c_clangd_options = $CFLAGS
let g:ale_cpp_clangd_options = $CFLAGS
let g:ale_markdown_markdownlint_options='-c $HOME/.markdownlint.json'

这里我在 ~/.zshrc 中使用 CFLAGS 导出自定义的 header 配置, 可以达到 一处定义, 多处使用的 的效果

编译运行所依赖的头文件路径

通常我使用 skywind3000/asyncrun 来执行相关 c / cpp 文件, 那么相关的头文件定义就很简单了, 直接在相关命令处加上 pkg-config 的相关参数即可, 如下:

map <F2> : call Run()<CR>

func Run()
    exec 'w'
    if &filetype == 'c'
        exec 'AsyncRun! gcc `pkg-config --cflags --libs glib-2.0` -Wall -O2 "$(VIM_FILEPATH)" -o "$HOME/.cache/build/C/$(VIM_FILENOEXT)" && "$HOME/.cache/build/C/$(VIM_FILENOEXT)"'
    elseif &filetype == 'java'
        exec 'AsyncRun! javac %; time java %<'
    elseif &filetype == 'sh'
        exec "AsyncRun! time bash %"
    elseif &filetype == 'python'
        exec 'AsyncRun! time python3 "%"'
    endif
endfunc

这样在 c 文件下, 只需要按下 <F2> 即可立即执行

Xcode 中配置头文件及库文件

TARGETS -> Build Settings -> Search Path -> Header Search Paths / Library Search Paths 中设置相应的 header 及库文件路径即可

himg

总结

万变不离其宗, 虽然对于头文件与库文件有各种不同的配置方式, 但是都是围绕着如何方便地列出头文件与库文件的路径以供编译链接使用. 掌握了这个点, 我们即使在使用其他工具的时候也可以按照这个思路去尝试, 去解决.

参考

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,311评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,339评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,671评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,252评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,253评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,031评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,340评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,973评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,466评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,937评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,039评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,701评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,254评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,259评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,485评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,497评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,786评论 2 345

推荐阅读更多精彩内容