Skip to content

Instantly share code, notes, and snippets.

@Andrei-Pozolotin
Last active February 11, 2026 15:56
Show Gist options
  • Select an option

  • Save Andrei-Pozolotin/d4bb92cfec676ca2f1bd622139e1787e to your computer and use it in GitHub Desktop.

Select an option

Save Andrei-Pozolotin/d4bb92cfec676ca2f1bd622139e1787e to your computer and use it in GitHub Desktop.
FreeCAD development with Zed on Arch Linux
Diagnostics:
Suppress: ["*"]

FreeCAD development with Zed on Arch Linux

This documents a working setup to hack on FreeCAD using the Zed editor, clangd, and CMake on Arch Linux.

It focuses on four pieces:

  • A reproducible build configuration (conf.sh, build.sh)
  • A compile_commands.json flow that keeps clangd happy
  • Local clangd/clang-tidy noise control (.clangd)
  • File locations and symlinks that make everything line up

Prerequisites

  • Arch Linux (or derivative) with:
    • base-devel, cmake, ninja, git
    • clang, clang-tools-extra (for clangd / clang-tidy)
    • FreeCAD build dependencies as per the FreeCAD wiki / Developers Handbook
  • Zed editor
  • A FreeCAD source checkout

Example layout:

$HOME/source/git/FreeCAD        # FreeCAD git clone
$HOME/source/git/FreeCAD-build  # (optional) out-of-tree build dir

This gist assumes an in-tree build/zed-relwithdebinfo directory inside the FreeCAD repo:

FreeCAD/
  build/
    zed-relwithdebinfo/
      compile_commands.json

Files in this gist

.zed/ directory

Location: FreeCAD/.zed/

Contains Zed-specific development helpers:

  • conf.sh - Configures CMake build directory with CMAKE_EXPORT_COMPILE_COMMANDS=ON.
  • build.sh - Convenience wrapper to rebuild with Ninja.
  • tasks.json - Zed tasks (optional, for reference).
  • settings.json - Zed settings (optional, for reference).

These files are automatically excluded from git by FreeCAD's .gitignore .* pattern.

.clangd

Location: FreeCAD/.clangd (at repo root)

Per-checkout clangd configuration file that:

  • Points clangd at the correct compilation database if needed
    (for example if compile_commands.json lives only in the build dir).
  • Tunes diagnostics and clang-tidy checks to reduce noise while keeping important issues.

Also excluded by FreeCAD's .gitignore .* pattern (intended to be local-only).

Example .clangd (to adapt):

CompileFlags:
  # If you do NOT symlink compile_commands.json to the repo root,
  # uncomment the following to point clangd at the build dir explicitly.
  # CompilationDatabase: build/zed-relwithdebinfo

Diagnostics:
  # Example: tone down some clang-tidy noise while keeping important diagnostics.
  Suppress:
    - 'clang-tidy:readability-isolate-declaration'
    - 'clang-tidy:readability-else-after-return'
    - 'clang-tidy:cppcoreguidelines-avoid-magic-numbers'
    - 'clang-tidy:cppcoreguidelines-narrowing-conversions'

ClangTidy:
  # Let clangd pick up the project's .clang-tidy, but we can explicitly
  # remove or add checks here if needed.
  Add: []
  Remove: []

Optional: compile_commands.json symlink

clangd will automatically look for compile_commands.json starting at the directory of the file you open and walking up parent directories.

Two common approaches:

  1. Symlink to the build dir (simple and explicit):

    cd $HOME/source/git/FreeCAD
    ln -sf build/zed-relwithdebinfo/compile_commands.json .

    With this, clangd sees compile_commands.json at the repo root and you usually don't need CompilationDatabase in .clangd.

  2. Use CompilationDatabase in .clangd:

    If you don't like the symlink, keep compile_commands.json only under build/zed-relwithdebinfo and set:

    CompileFlags:
      CompilationDatabase: build/zed-relwithdebinfo

Zed integration

This gist does not contain Zed config files, but it assumes:

  • Zed is using system clangd.
  • The FreeCAD repo root is opened as the project in Zed.
  • Diagnostics severity is tuned to your preference (e.g. only errors/warnings) via Zed's settings.

Typical project layout with all pieces in place:

$HOME/source/git/FreeCAD/
  .zed/
    conf.sh                  # from PKGBUILD
    build.sh                 # from this gist
    tasks.json               # VS Code reference (optional)
    settings.json            # VS Code reference (optional)
  .clangd                   # local-only clangd config (untracked)
  compile_commands.json     # symlink to build/zed-relwithdebinfo/compile_commands.json
  build/
    zed-relwithdebinfo/
      compile_commands.json
  src/
    ...

With this:

  • conf.sh prepares the build directory and compilation database.
  • build.sh keeps rebuilds one command away.
  • compile_commands.json + .clangd ensure clangd (and thus Zed's C++ LSP) uses the same flags/includes as your real FreeCAD build.
  • You can further customize .clangd to suppress or tweak clang-tidy diagnostics without changing the upstream FreeCAD configuration.
#!/usr/bin/env bash
#
# Origin:
# https://gitlab.archlinux.org/archlinux/packaging/packages/freecad/-/blob/main/PKGBUILD
#
set -euo pipefail
BUILD_TYPE="${1:-RelWithDebInfo}"
JOBS="${2:-8}"
BUILD_DIR="build/zed-$(echo "${BUILD_TYPE}" | tr '[:upper:]' '[:lower:]')"
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
cd "${REPO_ROOT}"
LOG_DIR="${SCRIPT_DIR}"
BUILD_LOG="${LOG_DIR}/build.log"
echo ">>> [build] Build type : ${BUILD_TYPE}"
echo ">>> [build] Build dir : ${BUILD_DIR}"
echo ">>> [build] Jobs : ${JOBS}"
start_time=$(date +%s)
cmake --build "${BUILD_DIR}" -j "${JOBS}" | tee "${BUILD_LOG}"
end_time=$(date +%s)
elapsed=$(( end_time - start_time ))
mins=$(( elapsed / 60 ))
secs=$(( elapsed % 60 ))
echo ">>> [build] Build time: ${mins}m ${secs}s"
#!/usr/bin/env bash
#
# Origin:
# https://gitlab.archlinux.org/archlinux/packaging/packages/freecad/-/blob/main/PKGBUILD
#
set -euo pipefail
BUILD_TYPE="${1:-RelWithDebInfo}"
BUILD_DIR="build/zed-$(echo "${BUILD_TYPE}" | tr '[:upper:]' '[:lower:]')"
LINKER_TYPE="${LINKER_TYPE:-bfd}" # e.g. bfd (default), lld, mold
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
cd "${REPO_ROOT}"
LOG_DIR="${SCRIPT_DIR}"
CONF_LOG="${LOG_DIR}/conf.log"
# Arch PKGBUILD 'depends' (runtime deps)
arch_depends=(
boost-libs
coin
fmt
glew
libspnav
med
opencascade
openmpi
pugixml
pyside6
yaml-cpp
python-markdown
python-matplotlib
python-pip
python-pivy
python-ply
python-yaml
qt6-svg
qt6-tools
verdict
vtk
xerces-c
)
# Arch PKGBUILD 'makedepends' (build-only deps)
arch_makedepends=(
boost
cgns
cmake
eigen
git
libharu
liblas
ninja
nlohmann-json
openvdb
openvr
ospray
pdal
postgresql-libs
python-mpi4py
shiboken6
swig
utf8cpp
dos2unix
microsoft-gsl
fast_float
)
if command -v pacman >/dev/null 2>&1; then
echo ">>> Ensuring Arch runtime deps are installed (requires sudo)..."
sudo pacman -S --needed "${arch_depends[@]}"
echo ">>> Ensuring Arch build deps are installed (requires sudo)..."
sudo pacman -S --needed "${arch_makedepends[@]}"
fi
echo ">>> [conf] Build type : ${BUILD_TYPE}"
echo ">>> [conf] Build dir : ${BUILD_DIR}"
echo ">>> [conf] Linker : ${LINKER_TYPE}"
mkdir -p "${BUILD_DIR}"
start_time=$(date +%s)
cmake -S . -B "${BUILD_DIR}" \
-D BUILD_FLAT_MESH=ON \
-D BUILD_ENABLE_CXX_STD=C++17 \
-D BUILD_DESIGNER_PLUGIN=ON \
-D CMAKE_BUILD_TYPE="${BUILD_TYPE}" \
-D CMAKE_EXPORT_COMPILE_COMMANDS=ON \
-D CMAKE_EXE_LINKER_FLAGS="-fuse-ld=${LINKER_TYPE}" \
-D CMAKE_SHARED_LINKER_FLAGS="-fuse-ld=${LINKER_TYPE}" \
-D FREECAD_USE_EXTERNAL_PIVY=ON \
-D FREECAD_USE_OCC_VARIANT='Official Version' \
-D FREECAD_USE_QT_FILEDIALOG=ON \
-D FREECAD_QT_VERSION=6 \
-G Ninja \
-W no-dev | tee "${CONF_LOG}"
# Symlink compile_commands.json into project root for clangd/Zed
if [ -f "${BUILD_DIR}/compile_commands.json" ]; then
ln -sf "${BUILD_DIR}/compile_commands.json" "${REPO_ROOT}/compile_commands.json"
fi
end_time=$(date +%s)
elapsed=$(( end_time - start_time ))
mins=$(( elapsed / 60 ))
secs=$(( elapsed % 60 ))
echo ">>> [conf] Configure time: ${mins}m ${secs}s"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment