C/C++ 頭文件路徑在編譯時及工具中的設置

使用 C/CPP, 避免不了要和各類頭文件打交道, 系統庫還好, 基本上不須要操心, 已經被自動預置入頭文件列表中了. 棘手的是使用第三方庫, 這時就要手動指定其頭文件位置與庫文件位置. 本文記錄下在終端中手工編譯與某些工具內編譯的設置方式.html

himg

終端中使用 gcc/clang/makefile 手工編譯

一般狀況下, 咱們可使用 gcc -I/include -c test.c -o test.ogcc test.o -L/libs -o test 命令來分別指定頭文件與庫文件位置, 可是對於一個比較大的第三方庫, 其頭文件和庫文件的數量是比較多的. 若是咱們一個個手動地寫, 那將是至關麻煩的. 因此, pkg-config 就應運而生了java

簡言之, pkg-config 爲庫提供編譯與連接 flag 的功能.python

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 看到全部可用命令c++

使用 pkg-config 在終端進行編譯

最基礎的用法就是直接將 pkg-config --cflags --libs glib-2.0 做爲 gcc 的參數之一寫在其後.git

# 注意 ` 是 grave/tilde 鍵, 不是單引號
$gcc main.c `pkg-config --cflags --libs glib-2.0` -o main
複製代碼

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

其實 pkg-config 所作的事情很是簡單, 它經過第三方庫定義的 .pc 文件進行頭文件與庫文件的定位, 例如 glib 的 pc 文件以下:github

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/pkgconfigjson

himg

某些軟件可能沒有自動建立軟連接的功能, 或者咱們自定義一個 .pc 文件, 那麼這時就須要在 .zshrc 中添加以下配置:vim

# 添加自定義的 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 文件了ruby

vim 中配置頭文件

youcompleteme 依賴的頭文件路徑

ycm 所依賴的補全依賴的頭文件路徑有:bash

  • 系統的 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

總結

萬變不離其宗, 雖然對於頭文件與庫文件有各類不一樣的配置方式, 可是都是圍繞着如何方便地列出頭文件與庫文件的路徑以供編譯連接使用. 掌握了這個點, 咱們即便在使用其餘工具的時候也能夠按照這個思路去嘗試, 去解決.

參考

相關文章
相關標籤/搜索