Настройка VSCode для работы с кросс-тулчейном в Docker

Настройка VSCode для работы с кросс-тулчейном в Docker
Venti Views / Unsplash

В этом посте разберём настройку VSCode для разработки C/ASM проекта, сборка которого происходит с помощью кросс-компилятора внутри образа Docker.

Недавно я вспомнил про свой старый проект EtherOS. Когда-то давно я его начал разрабатывать в IDE CLion от JetBrains. В CLion разработка через тулчейн в Docker настраивается практически автоматически, но вот в VSCode нам придётся сделать пару телодвижений, чтобы добиться нормальной интеграции IntelliSense и всяких других фич нашей любимой псевдо-IDE. Сам проект содержит код NASM и C, а собирается с помощью кросс-компилятора Binutils и GCC под архитектуру AMD64. Настраивать сборку такого проекта на разных платформах – дело гиблое, потому я и сделал образ и автосборку через внутреннюю утилиту.

Подготовка

Итак, лучший вариант, который мы можем провернуть с VSCode – открыть репозиторий как VSCode Dev Container. Редактор будет работать внутри уже собранного образа Docker, а исходники останутся примонтированные с хоста.

В качестве примера мы будем рассматривать dev контейнер на основе моего текущего Dockerfile:

FROM debian:bookworm-slim AS etheros-toolchain

# Env
ENV ETHER_TC="true"
ENV BROOT="/opt"
ENV TARGET="x86_64-elf"
ENV PREFIX="/usr/local/cross"
ENV PATH="$PREFIX/bin:$BROOT/toolchain:$PATH"
ENV TMP="/tmp/cross"
ENV V_BINUTILS="2.42"
ENV V_GCC="14.1.0"
ENV V_GDB="15.1"
ENV CORES=8

# Set mirror to Yandex
RUN echo 'deb http://mirror.yandex.ru/debian/ bookworm main contrib non-free non-free-firmware \
    deb-src http://mirror.yandex.ru/debian/ bookworm main contrib non-free non-free-firmware \
    \
    deb http://security.debian.org/debian-security bookworm-security main contrib non-free non-free-firmware \
    deb-src http://security.debian.org/debian-security bookworm-security main contrib non-free non-free-firmware \
    \
    deb http://mirror.yandex.ru/debian/ bookworm-updates main contrib non-free non-free-firmware \
    deb-src http://mirror.yandex.ru/debian/ bookworm-updates main contrib non-free non-free-firmware' > /etc/apt/sources.list

# Install packages
RUN apt-get update && \
    apt-get install -y \
            bison flex \
            libgmp3-dev libmpc-dev libmpfr-dev libisl-dev \
            texinfo wget \
            cmake make ninja-build g++ \
            grub-pc-bin xorriso \
            build-essential

# Create work directory for cross toolchain and building
RUN mkdir -p $PREFIX && mkdir -p $TMP

# Download source archives and create directories for building
WORKDIR $TMP
RUN wget http://ftp.gnu.org/gnu/binutils/binutils-$V_BINUTILS.tar.gz && \
    tar xvf binutils-$V_BINUTILS.tar.gz
RUN wget http://ftp.gnu.org/gnu/gcc/gcc-$V_GCC/gcc-$V_GCC.tar.gz && \
    tar xvf gcc-$V_GCC.tar.gz
RUN mkdir -p $TMP/{build-binutils,build-gcc,build-gdb}

# Build binutils
WORKDIR $TMP/build-binutils
RUN ../binutils-$V_BINUTILS/configure --target=$TARGET --prefix="$PREFIX" --with-sysroot --disable-nls --disable-werror && \
    make -j $CORES && \
    make install

# Build GCC
WORKDIR $TMP/build-gcc
RUN ../gcc-$V_GCC/configure --target=$TARGET --prefix="$PREFIX" --disable-nls --enable-languages=c --without-headers && \
    make -j $CORES all-gcc && \
    make -j $CORES all-target-libgcc && \
    make install-gcc && \
    make install-target-libgcc

# Download GDB
WORKDIR $TMP
RUN wget http://ftp.gnu.org/gnu/gdb/gdb-$V_GDB.tar.gz && \
    tar xvf gdb-$V_GDB.tar.gz && \
    mkdir -p $TMP/build-gdb

# Prepare for python installation
RUN apt-get install -y \
            build-essential \
            libsqlite3-dev \
            zlib1g-dev \
            libncurses5-dev \
            libgdbm-dev \
            libbz2-dev \
            libssl-dev \
            libdb-dev

RUN apt-get install gdb nasm git python3 -y
RUN ln -s /usr/bin/python3 /usr/bin/python

RUN apt-get install uuid-dev subversion iasl python3-distutils -y

# Setup volume and cleanup temp
VOLUME $BROOT
WORKDIR $BROOT
RUN rm -rf $TMP

Он содержит полную сборку Binutils, GCC, GDB, а также собирается на основе образа Debian Bookworm.

Перед тем, как мы начнём непосредственно настраивать среду, понадобится установить плагин "Dev Containers".

Объявление контейнера

Для того, чтобы VSCode вообще знал, что за среду мы хотим получить, нужно создать файл-дескриптор dev контейнера. Для этого, в корневой директории проекта мы создадим директорию .devcontainer, а внутри этой директории - devcontainer.json.

Содержимое имеем следующее:

{
	"name": "EtherOS Toolchain",
	"image": "etheros-toolchain",
	"workspaceMount": "source=${localWorkspaceFolder},target=/opt,type=bind",
	"workspaceFolder": "/opt",
	"customizations": {
		"vscode": {
			"extensions": [
				"ms-vscode.cpptools",
				"ms-vscode.cmake-tools"
			],
			"settings": {
				"C_Cpp.default.compilerPath": "/usr/local/cross/bin/x86_64-elf-gcc",
				"C_Cpp.default.includePath": [
					"${workspaceFolder}/include"
				],
				"C_Cpp.default.cStandard": "c11",
				"C_Cpp.default.intelliSenseMode": "linux-gcc-x64",
				"cmake.generator": "Ninja",
				"cmake.sourceDirectory": "${workspaceFolder}",
				"cmake.buildDirectory": "${workspaceFolder}/build"
			}
		}
	}
}

Это достаточно индивидуальный файл контейнера, но, в целом, можно разобрать следующие поля:

  • name - видимое название дев-контейнера
  • image - непосредственно название образа Docker
  • workspaceMount - монтируемые в образ директории. В нашем случае корень проекта, который мы монтируем в /opt
  • workspaceFolder - рабочая директория внутри контейнера
  • customizations - дополнительные настройки контейнера, которые позволяют пробросить используемые расширения и настройки для них

Прыжок в контейнер

Файл заполнен, но контейнер ещё не импортирован. Чтобы дело пошло, открываем "Command Palette" (Ctrl + Shift + P или Cmd + Shift + P для MacOS), вбиваем "reopen in container". Полный пункт должен выглядеть примерно так: "Dev Containers: Reopen in Container"

После этого проект "переоткроется", но уже внутри Docker-окружения, о чём будет говорить отсутствующие расширения, которые не добавлены в файл, а также терминал с PWD равной /opt

Вуаля! Теперь, если вы используете CMake в проекте, как и я, то стоит удалить директорию build и выполнить переконфигурацию проекта, чтобы сборка не падала из-за неверных путей в кеше CMake.

Read more

Печатаем чипы на клавиатуре: Как Verilog сделал "железо" доступным каждому

Печатаем чипы на клавиатуре: Как Verilog сделал "железо" доступным каждому

Привет! Возможно, ты когда-нибудь задумывался о том, каким образом человечество, получившее цифровой компьютер лишь в середине прошлого века, в наши дни уже может симулировать настоящий мозг мухи, строить виртуальных ассистентов и поддерживать сложнейшую инфраструктуру, созданную человеком – интернет. Или каким образом люди могут начать с комфортом программировать и добиваться неплохих результатов,

By Samsonium
Как работает программа в ОС на самом деле

Как работает программа в ОС на самом деле

Когда пользователь запускает программу, ему кажется, что компьютер просто начинает её выполнять. Но файл на диске — это всего лишь набор байтов. Процессор не умеет выполнять файлы. Он умеет выполнять инструкции только из памяти. Возникает вопрос: кто переносит программу с диска в память? Кто выделяет ей память? Кто следит за тем,

By Samsonium
Настройка API клиента с Dio

Настройка API клиента с Dio

Чтобы код масштабировался, тестировался и был в целом надёжным, хорошим вариантом будет делать API клиент через паттерн репозиторий. Архитектура и зависимости Поделим код на три слоя: * Сетевой слой - Dio, AuthInterceptor, RemoteDataSources * Слой данных - Repositories (абстрация, работа с DTO, маппинг в доменные модели и обработка ошибок) * Доменный слой -

By Samsonium