Get the source from the GitHub Repository:
git clone git@github.com:fair-acc/gnuradio4.gitTo just compile GNURadio4 without installing any dependencies you can just use the Docker image which is also used by our CI builds. The snippet below uses docker run to start the container with the current directory mapped into the container with the correct user and group IDs.
It then compiles the project and runs the testsuite.
Note that while the binaries inside of ./build can be accessed on the host system, they are linked against the libraries of the container and will most probably not run on the host system.
me@host$ cd gnuradio4
me@host$ docker run \
--user `id -u`:`id -g` \
--volume="/etc/group:/etc/group:ro" \
--volume="/etc/passwd:/etc/passwd:ro" \
--volume="/etc/shadow:/etc/shadow:ro" \
--workdir=/work/src --volume `pwd`:/work/src -it \
ghcr.io/fair-acc/gr4-build-container bash
me@aba123ef$ # export CXX=c++ # uncomment to use clang
me@aba123ef$ cmake -S . -B build
me@aba123ef$ cmake --build build
me@aba123ef$ ctest --test-dir buildSome IDEs provide a simple way to specify a docker container to use for building and executing a project. For example in JetBrains CLion you can set this up in Settings->Build,Execution,Deployment->Toolchains->[+]->Docker, leaving everything as the default except for setting Image to ghcr.io/fair-acc/gr4-build-container.
By default this will use the gcc-14 compiler included in the image, by setting CXX to clang++-18 you can also use clang.
To be able to natively compile some prerequisites have to be installed:
- gcc >= 13 and/or clang >= 17
- cmake >= 3.25.0
- ninja (or GNU make)
- optional for python block support: python3
- optional for soapy (limesdr,rtlsdr) blocks: soapysdr
- optional for compiling to webassembly: emscripten >= 5.0.0
To apply the project's formatting rules, you'll also need the correct formatters, clang-format-18 and cmake-format. With these installed you can use the scripts in the repository to reformat your changes. For smaller changes, the CI will provide you with a patch which will fix the formatting (click on the "Details" link on the failed Restyled.io check), but for bigger changes it's useful to have local formatting.
Once these are installed, you should be able to just compile and run GNURadio4:
me@host$ cd gnuradio4
me@host$ cmake -S . -B build
me@host$ cmake --build build
me@host$ ctest --test-dir buildThe current development environment in Windows uses MSYS2, specifically UCRT64 and CLANG64 to allow for building gnuradio4.
While this is not the desired end development environment for Windows, it currently builds and runs the testsuite.
To set up the MSYS2 environment, navigate to https://www.msys2.org and download the installer, currently msys2-x86_64-20251213.exe.
MSYS2 is a rolling release, so I hope these instructions continue to work as the code gets updated.
Chances are new build packages will cause small breakages but hopefully not insurmountable ones.
To install msys2, follow the instructions on https://www.msys2.org.
Once installed, open either the UCRT64 or CLANG64 environment and update the environment via the pacman package installer.
me@host UCRT64
$ pacman -SyuThis will likely require the closing of the terminal and reopening it.
Once the terminal is reopened, we should install the development programs.
The minimal requirement would be the development programs for UCRT64 as follows.
me@host UCRT64
$ pacman -S git moreutils \
mingw-w64-ucrt-x86_64-ccache \
mingw-w64-ucrt-x86_64-toolchain \
mingw-w64-ucrt-x86_64-python-numpy \
mingw-w64-ucrt-x86_64-cmake \
mingw-w64-ucrt-x86_64-ninja \
mingw-w64-ucrt-x86_64-clang-tools-extra \
mingw-w64-ucrt-x86_64-dlfcn \
mingw-w64-ucrt-x86_64-nodejs \
mingw-w64-ucrt-x86_64-soapysdr \
mingw-w64-ucrt-x86_64-soapyrtlsdrIf one wants to use CLANG64 to build instead of UCRT64, you can use the comamnd that follows.
me@host CLANG64
$ pacman -S git moreutils \
mingw-w64-clang-x86_64-ccache \
mingw-w64-clang-x86_64-toolchain \
mingw-w64-clang-x86_64-python-numpy \
mingw-w64-clang-x86_64-cmake \
mingw-w64-clang-x86_64-ninja \
mingw-w64-clang-x86_64-clang-tools-extra \
mingw-w64-clang-x86_64-dlfcn \
mingw-w64-clang-x86_64-nodejs \
mingw-w64-clang-x86_64-soapysdr \
mingw-w64-clang-x86_64-soapyrtlsdrAnd of course, both can be run to install both environments.
Of note the MINGW64 environment is not being used.
The only difference between it and UCRT64 is the use of the msvcrt C library instead of ucrt.
Nonetheless, there were problems building for this environment, so instructions for it are not included in this document.
Initial build for ucrt64 and clang64 environments.
We will need to set -DWARNINGS_AS_ERRORS=OFF as both g++ and clang++ generate warnings during the build.
Also, we need to set -DCMAKE_EXPORT_COMPILE_COMMANDS=ON to be able to use the full features of the language server protocol of nvim.
me@host CLANG64
$ cmake -DCMAKE_BUILD_TYPE=Debug \
-DCMAKE_VERBOSE_MAKEFILE=ON \
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON \
-DWARNINGS_AS_ERRORS=OFF \
-DCMAKE_INSTALL_PREFIX=/home/$USER/gr4 \
-S . -B buildThen follow the rest of the build steps outlined above. Build gnuradio4 by running the following:
me@host CLANG64
$ cmake --build buildOnce the system is built, run the testsuite:
me@host CLANG64
$ ctest --timeout 120 --test-dir buildThe following optional section describes a way to set up a development environment with code completion and formatting support based on neovim.
me@host CLANG64
$ pacman -S mingw-w64-clang-x86_64-neovimTo configure nvim to properly use clangd and format your work properly, some modifications must be made to the end of your .bashrc.
Add the following code snippet:
export LANG=en_US.UTF-8
# Set history
shopt -s histappend
export HISTSIZE=999999
export HISTFILESIZE=999999
HISTCONTROL=erasedups
PROMPT_COMMAND="history -w; $PROMPT_COMMAND"
tac $HISTFILE | awk '!x[$0]++' | tac | sponge $HISTFILE
export EDITOR=nvim
export XDG_DATA_HOME="$HOME/.local/share"
export XDG_CONFIG_HOME="$HOME/.config"
export XDG_CACHE_HOME="$HOME/.cache"
export XDG_STATE_HOME="$HOME/.local/state"
export XDG_RUNTIME_DIR="/tmp/$USER-runtime-dir"Now close the terminal and reopen a new one to update the bash environment variables.
The next program to configure is neovim or nvim.
To configure it, make the directory $HOME/.config/nvim and put the following script into it as init.lua.
When nvim is launched after the script is put in place, it will download and setup lazy.nvim, mason.nvim, mason-lspconfig, nvim-lspconfig, and nvim-cmp.
These helper programs or scripts for nvim allow it to use clangd, perform autocompletion, and load headers or examine functions by making a pair of keystrokes (gd) over the header or function name in the file you're editing.
It also allows for formatting CMakeLists.txt files properly.
-- Bootstrap lazy.nvim
local lazypath = vim.fn.stdpath("data") .. "/lazy/lazy.nvim"
if not vim.loop.fs_stat(lazypath) then
vim.fn.system({
"git", "clone", "--filter=blob:none",
"https://github.com/folke/lazy.nvim.git",
lazypath,
})
end
vim.opt.rtp:prepend(lazypath)
-- Install and configure plugins
require("lazy").setup({
-- Mason, keeping for other tools, but not clangd
{
"williamboman/mason.nvim",
config = true,
},
-- Mason-lspconfig setup
{
"williamboman/mason-lspconfig.nvim",
dependencies = { "williamboman/mason.nvim" },
config = function()
require("mason-lspconfig").setup({
ensure_installed = {},
})
end,
},
-- LSP setup
{
"neovim/nvim-lspconfig",
},
-- Autocompletion
{
"hrsh7th/nvim-cmp",
dependencies = {
"hrsh7th/cmp-nvim-lsp",
"hrsh7th/cmp-buffer",
"hrsh7th/cmp-path",
"hrsh7th/cmp-cmdline",
"L3MON4D3/LuaSnip",
"saadparwaiz1/cmp_luasnip",
},
config = function()
local cmp = require("cmp")
local luasnip = require("luasnip")
cmp.setup({
snippet = {
expand = function(args)
luasnip.lsp_expand(args.body)
end,
},
mapping = cmp.mapping.preset.insert({
["<Tab>"] = cmp.mapping.select_next_item(),
["<S-Tab>"] = cmp.mapping.select_prev_item(),
["<CR>"] = cmp.mapping.confirm({ select = true }),
}),
sources = {
{ name = "nvim_lsp" },
{ name = "luasnip" },
{ name = "buffer" },
{ name = "path" },
},
})
end,
},
})
-- lsp keybindings
local on_attach = function(_, bufnr)
local opts = { noremap = true, silent = true, buffer = bufnr }
vim.keymap.set("n", "gd", vim.lsp.buf.definition, opts)
vim.keymap.set("n", "gD", vim.lsp.buf.declaration, opts)
vim.keymap.set("n", "gi", vim.lsp.buf.implementation, opts)
vim.keymap.set("n", "gr", vim.lsp.buf.references, opts)
vim.keymap.set("n", "K", vim.lsp.buf.hover, opts)
vim.keymap.set("n", "<leader>rn", vim.lsp.buf.rename, opts)
vim.keymap.set("n", "<leader>ca", vim.lsp.buf.code_action, opts)
vim.keymap.set("n", "[d", vim.diagnostic.goto_prev, opts)
vim.keymap.set("n", "]d", vim.diagnostic.goto_next, opts)
vim.api.nvim_create_autocmd("BufWritePre", {
buffer = bufnr,
callback = function()
vim.lsp.buf.format({ async = false })
end,
})
end
-- Setup clangd
vim.api.nvim_create_autocmd("FileType", {
pattern = { "c", "cpp", "objc", "objcpp" },
callback = function()
vim.lsp.start({
name = "clangd",
cmd = {
"clangd",
"--clang-tidy",
"--header-insertion=never",
"--fallback-style=LLVM",
"--background-index",
"--completion-style=detailed",
"--cross-file-rename",
"--suggest-missing-includes",
},
root_dir = vim.fs.root(0, {
"compile_commands.json",
"compile_flags.txt",
".clangd",
".git",
}) or vim.fn.getcwd(),
capabilities = require("cmp_nvim_lsp").default_capabilities(),
on_attach = on_attach,
init_options = {
fallbackFlags = { "-std=c++17" },
clangdFileStatus = true,
},
})
end,
})
-- Function to go to the last cursor position
vim.api.nvim_create_autocmd("BufReadPost", {
pattern = "*",
callback = function()
local last_pos = vim.fn.line("'\"")
if last_pos > 0 and last_pos <= vim.fn.line("$") then
vim.cmd('normal! g`"')
end
end,
})
-- Autocommand to trigger the fuction when a buffer is read
vim.api.nvim_create_autocmd("BufWritePost", {
pattern = { "*.cmake", "CMakeLists.txt" },
callback = function()
-- Get full path with proper escaping
local file = vim.fn.expand("%:p")
-- Use raw vim.fm.system to call cmake-format directly
local result = vim.fn.system({ "cmake-format", "-i", file })
-- Show stderr or error
if vim.v.shell_error ~= 0 then
vim.notify("cmake-format failed:\n" .. result, vim.log.levels.ERROR)
else
vim.cmd("edit!")
end
end,
})Once the compile_commands.json is created in the cmake configure step, copy it to the root gnuradio directory so it can be used by nvim.
me@host CLANG64
$ cp build/compile_commands.json .To improve the fonts in the mintty edit $HOME/.minttyrc and add the following to it. This step is cosmetic and not strictly necessary.
FontHeight=9
Font=DejaVu Sans Mono
Locale=en_US
Charset=UTF-8
CtrlShiftShortcuts=no
CursorType=block