Vim からでも qmake したい

この記事は Vim Advent Calendar 2012 139日目の記事になります。


さて、Vim を使っていると突然 Qt をビルドしたい!と思うことがあります。
そんな時に quickrun.vim に予め qmake を行う設定を書いておけば Vim で Qt のコードを書いてすぐにビルドし、実行する事が出来ます。

[qmake を使用して Qt をビルドするまでの流れ]

大雑把ですが基本的な流れは下記のような感じだと思います。

$ qmake -project
$ qmake
$ make


-project は最初に .pro ファイルを吐き出す為に使用するだけなので基本的には1回だけでいいです。
あとは .pro ファイルを書き換えた場合に qmake して makefile を吐き出し、コードを書き換えた場合には make する、という流れになると思います。
と、いう事で今回は、

  • .pro ファイルがなければ qmake -project を行い qmake → make を行う
  • .pro ファイルが存在し、.pro ファイルが変更されていれば qmake → make を行う
  • .pro ファイルが存在し、.pro ファイルが変更されていなければ make を行う

というような処理を quickrun.vim で行いたいと思います。

[g:quickrun_config]

let s:hook = {
\   "name" : "qmake",
\   "kind" : "hook",
\   "config" : {
\       "enable" : 0,
\       "directory" : ""
\   }
\}

function! s:hook.init(session)
    if !empty(self.config.directory)
        let a:session.config["hook/cd/enable"] = 1
        let a:session.config["hook/cd/directory"] = self.config.directory
    endif
endfunction

function! s:get_qmake_profile(dir)
    let dir = empty(a:dir) ? "" : a:dir."/"
    return fnamemodify(get(split(glob(dir."*.pro"), "\n"), 0, ""), ":p")
endfunction


" .pro が無ければ           : exec = ["qmake -project", "qmake", "make"]
" .pro が更新されていれば   : exec = ["qmake", "make"]
" .pro が更新されて無ければ : exec = ["make"]
let s:qmake_profile_timestamps = {}
function! s:hook.on_normalized(session, context)
    let profile = s:get_qmake_profile(self.config.directory)
    
    if filereadable(profile)
        let timestamp = getftime(profile)
        if get(s:qmake_profile_timestamps, profile, -1) == timestamp
            let exec = [a:session.config.exec]
        else
            let exec = ["qmake", a:session.config.exec]
            let s:qmake_profile_timestamps[profile] = timestamp
        endif
    else
        let exec = ["qmake -project", "qmake", a:session.config.exec]
    endif

    unlet a:session.config.exec
    let a:session.config.exec = exec
endfunction

call quickrun#module#register(s:hook, 1)
unlet s:hook


" :QuickRun cpp/qmake でビルド
" :QuiCkRun run/qmake_debug で出力したバイナリの実行
" 必要であれば hook/qmake/directory を設定したり
let g:quickrun_config = {
\   "cpp/qmake" : {
\       "exec"   : "make",
\       "hook/qmake/enable" : 1,
\   },
\
\   "run/qmake_debug" : {
\       "runner" : "shell",
\       "exec" : "start %s:p:h:t.exe",
\       "hook/cd/directory" : "debug"
\   }
\}


" QuickRun 中かどうかを判定する
" 自動的に cd を変更している場合はこの設定を使用する
" 詳細:http://d.hatena.ne.jp/osyo-manga/20130316/1363403701
" let g:is_quickrun_started = 0
" 
" let s:hook = {
" \   "name" : "is_started",
" \   "kind" : "hook",
" \}
" 
" function! s:hook.init(...)
"     let g:is_quickrun_started = 1
" endfunction
" 
" function! s:hook.sweep(...)
"     let g:is_quickrun_started = 0
" endfunction
" 
" call quickrun#module#register(s:hook, 1)
" unlet s:hook
" 
" augroup vimrc_group__cd
"     autocmd!
" 
"     " :QuickRun 中はカレントディレクトリを変更しない
"     autocmd BufEnter *
" \   if !get(g:, "is_quickrun_started", 0)
" \|        execute ":lcd " . expand("%:p:h")
" \|    endif
" augroup END


上記でまとめたビルドの流れを hook/qmake 内で処理しています。
最終的に exec に必要なコマンドを複数設定するような形になっています。
MinGW の場合は make ではなくて mingw32-make に変更する必要があります。
そこら辺の調整は環境に合わせて下さい。
実行バイナリの名前もディレクトリ名に依存しているのでもうちょっと何とかしたいところ。
上記の設定以外にも make clean とか設定しておけば捗りそうな感じ。
これで Vim から Qt のビルド、実行がスムーズに行うことが出来ますね