Vim: Setting up a Build System and Code Completion for C and C++

A guide on integrating CMake and the YouCompleteMe engine with Vim

Image for post
Image for post

Introduction

Image for post
Image for post
Preview of Vim running CMake and YCM.

Starting Vim Configuration

Project Source Files

$ git clone https://github.com/danebulat/vim-cmake-boilerplate

Project Structure and Initial CMake Script

- root
--> bin # Executables
--> build # Build system files
--> config # Vim configuration
--> include # Application header files
--> lib # Library source files
--> src # Application source files
$ mkdir vim-cmake-boilerplate && cd vim-cmake-boilerplate
$ mkdir bin build config include lib src
The initial src/main.cpp file.

CMake Installation

$ sudo pacman -S cmake      # Arch
$ sudo apt install cmake # Debian and Ubuntu
$ sudo dnf install cmake # Fedora and CentOS
$ cmake --version
-| cmake version 3.19.1 ...
$ man cmake

Initial Build Script

The initial CMakeLists.txt script.
# Enter build directory
$ cd build
# Read CMakeLists.txt and generate native build files
$ cmake ..
# Build project and generate executable in bin/ directory
$ cmake --build .
# Run the executable
$ ../bin/Boilerplate
-| Hello, CMake!

Configuration Header

configure_file(<input> <output>)
#pragma once
#define VERSION_MAJOR @Boilerplate_VERSION_MAJOR@
#define VERSION_MINOR @Boilerplate_VERSION_MINOR@
   set(CMAKE_CXX_STANDARD_REQUIRED True)>> configure_file(include/Config.h.in 
"${CMAKE_CURRENT_SOURCE_DIR}/include/Config.h")
   add_executable(Boilerplate 
"${CMAKE_CURRENT_SOURCE_DIR}/src/main.cpp")

>> target_include_directories(Boilerplate PUBLIC
"${CMAKE_CURRENT_SOURCE_DIR}/include")
The src/main.cpp program outputting its version number.

YouCompleteMe Installation

Installation

$ vim --version | awk 'NR==1 {print; exit}'
-| VIM - Vi IMproved 8.2 (2019 Dec 12, compiled ... 16:46:37)
# Arch
$ sudo pacman -S base-devel python clang llvm
# Debian and Ubuntu
$ sudo apt install build-essential python3-dev llvm-defaults
# Fedora
$ sudo dnf install gcc-c++ make python3-devel clang
clang-tools-extra
" Add YCM repository to plugin list
Plugin 'ycm-core/YouCompleteMe'
" Refresh configuration
:source ~/.vimrc
" Download YCM
:PluginInstall
$ cd ~/.vim/plugged/YouCompleteMe
$ cat .gitmodules
-| [submodule "third_party/requests-futures"]
path = third_party/requests-futures
url = https://github.com/ross/requests-futures
...
# Clone submodules
$ git submodule update --init --recursive
# Install YCM
$ python3 install.py --clangd-completer

Generating a Compiler Flags Database with CMake

   set(CMAKE_CXX_STANDARD_REQUIRED True)
>> set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
$ cd build
$ cmake .. && cmake --build .
$ ls compile_commands.json
-| compile_commands.json
$ cd .. && pwd
-| /home/dane/Github/vim-cpp-completion
$ ln -s build/compile_commands.json compile_commands.json
:YcmRestartServer
Image for post
Image for post
Testing the default behavior of the YCM plug-in.

Configuring YCM

YCM configuration settings in .vimrc.
" Make Vim always render the sign column:
set signcolumn=yes

Setting Up Debug and Release Configurations

Image for post
Image for post
Checking the project’s default compiler flags.

Updating the Project Structure

# build directory
$ cd build && rm -fr *
$ mkdir Debug Release
# bin directory
$ cd ../bin && rm -fr *
$ mkdir Debug Release
$ cd ..
set_target_properties(Boilerplate
PROPERTIES
RUNTIME_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/bin/$<CONFIG>")
$ cd build/Debug
$ cmake -DCMAKE_BUILD_TYPE=Debug ../..
$ cmake --build .
$ ../../bin/Debug/Boilerplate
-| Running: ../../bin/Debug/Boilerplate
Version: 1.0
Hello, CMake!
$ cd ../Release
$ cmake -DCMAKE_BUILD_TYPE=Release ../..
$ cmake --build .
$ ../../bin/Release/Boilerplate
-| Running: ../../bin/Debug/Boilerplate
Version: 1.0
Hello, CMake!
$ rm compile_commands.json
$ ln -s build/Debug/compile_commands.json compile_commands.json

Setting Compiler Flags

Compiler flags section in CMakeLists.txt.
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)

Vim Mappings for Automatic Building and Running

Plugin 'tpope/vim-dispatch'
:source ~/.vimrc
:PluginInstall
" Open vim-dispatch window and scroll to bottom
nnoremap <C-m>m :Copen<CR> <bar> G
" Build debug and release targets
nnoremap <C-m>bd :Dispatch! make -C build/Debug<CR>
nnoremap <C-m>br :Dispatch! make -C build/Release<CR>
Functions in .vimrc for setting up F6 and F7 mappings.
:call SetBinaryDebug("Boilerplate")
-| <F6> will run: /home/.../bin/Debug/Boilerplate
:call SetBinaryRelease("Boilerplate")
-| <F7> will run: /home/.../bin/Release/Boilerplate
# Can now press F6 and F7 to run the executables

Compiling and Linking a Dynamic Library

$ cd lib && mkdir interp && cd interp$ curl -L https://raw.githubusercontent.com/danebulat/vim-cmake-boilerplate/master/lib/interp/interpolate.h > interpolate.h$ curl -L https://raw.githubusercontent.com/danebulat/vim-cmake-boilerplate/master/lib/interp/interpolate.cpp > interpolate.cpp
The lib/interp/CMakeLists.txt script.
   set(CMAKE_EXPORT_COMPILE_COMMANDS ON)>> option(BUILD_SHARED_LIBS "Build using shared libraries" ON)
>> add_subdirectory(lib/interp)
   add_executable(Boilerplate 
"${CMAKE_CURRENT_SOURCE_DIR}/src/main.cpp")
>> target_link_libraries(Boilerplate interp)
target_compile_options(interp PRIVATE
"$<${gcc_like_cxx}:$<BUILD_INTERFACE:${gcc_flags}>>"
"$<${msvc_cxx}: $<BUILD_INTERFACE:${msvc_flags}>>")
# Clean build and bin directories
$ cd build && rm -fr * && mkdir Debug Release
$ cd ..
$ cd bin && rm -fr * && mkdir Debug Release
$ cd ..
# Build debug configuration and re-link compiler flags database
$ cd build/Debug && cmake -DCMAKE_BUILD_TYPE=Debug ../..
$ cmake --build .
$ cd ../..
$ rm compile_commands.json
$ ln -s build/Debug/compile_commands.json compile_commands.json
# Configure release configuration
$ cd build/Release
$ cmake -DCMAKE_BUILD_TYPE=Release ../..
$ cd ../..
The final src/main.cpp file.
$ ldd bin/Debug/Boilerplate
-| libinterp.so => <path to shared library>

Final Tweaks

Plugin 'sainnhe/edge'
:source ~/.vimrc
:PluginInstall
let g:edge_style = 'aura'
let g:edge_enable_italic = 0
let g:edge_disable_italic_comment = 1
if has('termguicolors')
set termguicolors
endif
set guifont=Hack\ 11
set background=dark
colorscheme edge

In Conclusion

MSc. Programmer and fan of open source software.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store