Categories
bsd operating systems

FreeBSD Dev

Hey! Welcome back. This write up is going to consist of “things to do” after you have FreeBSD installed. While not necessarily a set of standards, it is essentially what I did to get my development environment up and running on FreeBSD.

I will not walk through installation or setting up a desktop. Everyone has different hardware and I do not have time to troubleshoot if something goes awry, unfortunately. The FreeBSD community is fantastic, though. There is a chance your question has already been answered or your problem already solved. Browse around and if you can’t find what you’re looking for, create an account and ask your question.

After setting up a desktop the first thing I typically do is set up a hypervisor. I may not run VMs 24/7, but I do like the option to fire up another environment on the fly. FreeBSD comes with an awesome hypervisor called Bhyve, and a great management tool is just a package install away.

Setting up Bhyve

Most of the information for the Bhyve setup I took from here. In fact, that’s pretty much word for word what I did (go to quickstart) with the exception of the FreeBSD version and the vm console command.

Install the vm-bhyve package and setup the environment. Note: I have sudo installed and the wheel group in the sudoers file. I opted for sudo over doas because “persist” is not an option in FreeBSD.

pkg install vm-bhyve # installing the package
zfs create adlabs/vm # adlabs is an existing pool
# zroot/vm is the default option 
sysrc vm_enable="YES" # enable bhyve on startup
sysrc vm_dir="zfs:adlabs/vm" # directory for the vm cmd
vm init # start bhyve

From here I followed the instructions to copy config files and create a bridged network. Then, I pulled down the latest FreeBSD iso:

sudo vm iso https://download.freebsd.org/ftp/releases/ISO-IMAGES/12.1/FreeBSD-12.1-RELEASE-amd64-disc1.iso

This stores the image in /adlabs/vm/.iso/

Before installing the VM, I strongly recommend taking a snapshot in case the VM gets locked or something crazy happens. With ZFS, this is a simple command:

zfs snapshot pool/path@descriptor

For my environment the command was:

sudo zfs snapshot adlabs/vm@pre-vm

You can confirm the snapshot with:

zfs list -t snapshot

Now you’re set to install your FreeBSD virtual machine. If you want to configure something like Debian or Ubuntu, there is a template in /pool/vm/.templates you can use or copy to create your own config. I copied the default and changed the CPU to 2 and RAM to 2048. For Linux installations you will need sysutils/grub2-bhyve and sysutils/bhyve-firmware packages. To apply a template:

sudo vm create -t my_template vm_name

The ‘my_template’ will use /pool/vm/.templates/my_template.conf

After that you’re ready to start the installation:

sudo vm install vm_name isofile.iso

Huzzah. Then jump into the installation (unless you’re a genius and used a PXEboot/Kickstart file):

sudo vm console vm_name

Proceed with the installation. To exit the console session, ~ then Control + D. That’s shift and key next to the 1/escape key. I used vt100 connection when asked.

After setup, your bridged network will help you ssh/RDP/VNC.

Dev Environment

If you’ve been listening to the podcast, you know I’ve spent a great deal of time with the C and Go programming languages. FreeBSD has clang out of the box, but one of the dev tools I use requires Python3 and a Python3 utility.

sudo pkg install -y python38 py38-setuptools

I set an alias in my ~/.cshrc file:

alias python python3.8

It helps!

From here I install vim and git:

sudo pkg install -y vim git

I use Vim as my editor and I add a lot to change it up. It goes from this:

To this:

Line numbers, syntax highlighting, autocomplete, helpful references, inline errors, and more.

I use Vundle to manage the plugins for Vim, but you can use any plugin manager really. First we need to edit ~/.vimrc to get basic editor functionality.

filetype plugin indent on
set expandtab
set shiftwidth=4
set tabstop=4

set number
syntax enable

This will give your vim editor auto indent, tabs equating to four spaces, line numbers, and syntax highlighting. On some operating systems the syntax is automatic. I include the entry just in case. For FreeBSD and YouCompleteMe, set encoding=utf-8 is required in your configuration as well.

To set up Vundle, you need to clone it and add the entry into your vim config.

git clone https://github.com/VundleVim/Vundle.vim.git ~/.vim/bundle/Vundle.vim

Now I don’t copy everything from the setup, just the boilerplate. The following includes everything from above and the Vundle begin/end.

set nocompatible              " be iMproved, required
filetype off                  " required

" set the runtime path to include Vundle and initialize
set rtp+=~/.vim/bundle/Vundle.vim
call vundle#begin()

" let Vundle manage Vundle, required
Plugin 'VundleVim/Vundle.vim'
" All of your Plugins must be added before the following line
call vundle#end()            " required
filetype plugin indent on
set expandtab
set shiftwidth=4
set tabstop=4

set number
set encoding=utf-8
syntax enable

Look at that, easy peasy. Now let’s add some plugins!

I use Vim Awesome to shop for plugins. I tend to not add anything that hasn’t been worked on in a while. My favorites are vim-surround, vim-fugitive, vim-airline, YouCompleteMe, and vim-go. Search for what you utilize most and add them after the VundleVim plugin. There are plugins for Dockerfiles, YAML, JavaScript, all kinds of stuff. My completed file looks like this:

set nocompatible              " be iMproved, required
filetype off                  " required

" set the runtime path to include Vundle and initialize
set rtp+=~/.vim/bundle/Vundle.vim
call vundle#begin()

" let Vundle manage Vundle, required
Plugin 'VundleVim/Vundle.vim'
Plugin 'tpope/vim-surround'
Plugin 'tpope/vim-fugitive'
Plugin 'fatih/vim-go'
Plugin 'vim-airline/vim-airline'
Plugin 'vim-airline/vim-airline-themes'
Plugin 'Valloric/YouCompleteMe'

" All of your Plugins must be added before the following line
call vundle#end()            " required

filetype plugin indent on
set expandtab
set shiftwidth=4
set tabstop=4

set number
set encoding=utf-8
syntax enable

let g:airline_theme='deus'
let g:ycm_global_ycm_extra_conf='~/.vim/.ycm_extra_conf.py'

The second to last option is 100% personal preference. The last option is for YouCompleteMe. But first, we need to install it.

If you’re going to do anything other than C and Go, I recommend looking through the link above and grabbing what’s required. My installation is specifically for C, C++, and Golang.

Open up vim and run :PluginInstall to pull down the plugins. The default location for these are ~/.vim/bundle

After the command completes, head over to your YouCompleteMe directory and run the installer.

cd ~/.vim/bundle/YouCompleteMe
python install.py --clangd-completer --go-completer
# python is the alias for python3.8, the entries after the install are for C/C++ and Go. Look for java, all, etc. if you have other environments. 

After that you need to setup your .ycm_extra_conf.py file shown above. Now, this requires a bit of work and research. I recommend looking here and here. The first link provides a great example and the comments go into a lot of detail. The second link is a bit of an older blog post but still shows what you can do with the file.

As seen above in my vim config, I added the YouCompleteMe home path and threw my file into ~/.vim/

Here is my complete file. Again, I recommend reading through the examples, seeing what works for you specifically, and playing with settings.

import os
import ycm_core

# These are the compilation flags that will be used in case there's no
# compilation database set (by default, one is not set).
# CHANGE THIS LIST OF FLAGS. YES, THIS IS THE DROID YOU HAVE BEEN LOOKING FOR.
flags = [
'-Wall',
'-Wextra',
'-Werror',
# THIS IS IMPORTANT! Without a "-std=<something>" flag, clang won't know which
# language to use when compiling headers. So it will guess. Badly. So C++
# headers will be compiled as C headers. You don't want that so ALWAYS specify
# a "-std=<something>".
# For a C project, you would set this to something like 'c99' instead of
# 'c++11'.
'-std=c++14',
# ...and the same thing goes for the magic -x option which specifies the
# language that the files to be compiled are written in. This is mostly
# relevant for c++ headers.
# For a C project, you would set this to 'c' instead of 'c++'.
'-x',
'c',
'-isystem',
'/usr/include',
'-isystem',
'/usr/local/include',
]


# Set this to the absolute path to the folder (NOT the file!) containing the
# compile_commands.json file to use that instead of 'flags'. See here for
# more details: http://clang.llvm.org/docs/JSONCompilationDatabase.html
#
# Most projects will NOT need to set this to anything; you can just change the
# 'flags' list of compilation flags.
compilation_database_folder = ''

if os.path.exists( compilation_database_folder ):
  database = ycm_core.CompilationDatabase( compilation_database_folder )
else:
  database = None

SOURCE_EXTENSIONS = [ '.go', '.cpp', '.cxx', '.cc', '.c', '.m', '.mm' ]

def DirectoryOfThisScript():
  return os.path.dirname( os.path.abspath( __file__ ) )


def IsHeaderFile( filename ):
  extension = os.path.splitext( filename )[ 1 ]
  return extension in [ '.h', '.hxx', '.hpp', '.hh' ]


def GetCompilationInfoForFile( filename ):
  # The compilation_commands.json file generated by CMake does not have entries
  # for header files. So we do our best by asking the db for flags for a
  # corresponding source file, if any. If one exists, the flags for that file
  # should be good enough.
  if IsHeaderFile( filename ):
    basename = os.path.splitext( filename )[ 0 ]
    for extension in SOURCE_EXTENSIONS:
      replacement_file = basename + extension
      if os.path.exists( replacement_file ):
        compilation_info = database.GetCompilationInfoForFile(
          replacement_file )
        if compilation_info.compiler_flags_:
          return compilation_info
    return None
  return database.GetCompilationInfoForFile( filename )


# This is the entry point; this function is called by ycmd to produce flags for
# a file.
def FlagsForFile( filename, **kwargs ):
  if not database:
    return {
      'flags': flags,
      'include_paths_relative_to_dir': DirectoryOfThisScript()
    }

  compilation_info = GetCompilationInfoForFile( filename )
  if not compilation_info:
    return None

  # Bear in mind that compilation_info.compiler_flags_ does NOT return a
  # python list, but a "list-like" StringVec object.
  return {
    'flags': list( compilation_info.compiler_flags_ ),
    'include_paths_relative_to_dir': compilation_info.compiler_working_dir_
  }

You should be able to open vim, create a file, and go to town. Some of the Go stuff requires Control + Space to activate the autocomplete. C/C++ sometimes require this as well. Tab completion works out of the box.

So now you have a FreeBSD Bhyve host and a mediocre development environment. Congratulations! Let’s start hacking.

In my next post I am going to draft up how I configured my OpenBSD web server and how to utilize httpd (not to be confused with Apache on RHEL family). Do you have the BSD bug yet?

Leave a Reply

Your email address will not be published. Required fields are marked *