Конвертация CVS в Mercurial

В системе контроля версий CVS есть так называемые модули с амперсандом, представляющие собой поддиректорию общую для нескольких модулей. Концепция похожая на сабмодули в git или сабрепозитории в mercurial.

Поскольку CVS немного устарел, нужно куда-то переехать. К сожалению, правильную работу с моделями с амперсандом не поддерживает ни одна из найденных утилит миграции. Об инкрементальной миграции (когда существуют два параллельных репозитория (разных систем контроля), которые каким-то чудом остаются друг с другом синхронизированы) тоже придется забыть.

Краткая схема действий. Сначала все модули из CVS конвертируются в формат fastimport, далее полученные дампы преобразуются и конвертируются в mercurial. Mercurial имеет в данной ситуации преимущество над git в том, что сабрепозитории управляются просто служебными файлами в дереве репозитория.

Все CVS модули можно разделить на две категории: те, которые содержат в себе модули с амперсандом, и те, которые нет (сами являются ими). Второй набор конвертируется просто. Для преобразования первого набора нужно пойти в cvsroot и вставить символические ссылки в те места, где должны были быть директории, соответствующие модулям с амперсандом. После преобразования каждый модуль из первого набора потеряет связь со своими вложенными модулями, и файлы с путями, соответствующими им, окажутся преобразованы несколько раз — один раз в репозитории относящимся к вложенному модулю и по разу в каждом репозитории модуля первого типа. Перед импортом в mercurial данная связь будет восстановлена.

Использование cvs2git из пакета cvs2svn не требует особой внимательности, кроме параметра COMMIT_THRESHOLD который склеивает коммиты. Дело в том, что в CVS каждый файл имеет свою историю, а в формате fast-import (как в git и mercurial) историю имеет весь репозиторий, поэтому отдельные изменения файлов надо слепить в прообразы будущих коммитов. По-умолчанию COMMIT_THRESHOLD равен 5 минутам, это значит, что изменения всех файлов попадающих в пятиминутное окно будут слеплены в один коммит.
Кроме того, можно задать отображение тегов, отображение авторов и другие параметры.

Для преобразования из формата fastimport в репозиторий mercurial используется расширение hg-fastimport. Отметим, что изначальный автор забросил этот проект и считает его неудачным, а более правильным подходом к преобразованию называет другой свой проект. Видимо поэтому расширение очень странным образом обрабатывает TAG.FIXUP ветви (в CVS можно было накладывать теги на те ревизии файлов, которые во времени друг с другом вообще никогда не встречались, поэтому для отображения таких тегов создается отдельная специальная ветка), что обычно требует подправления кода.

Формат fastimport представляет из себя файл (или несколько), задающий дерево репозитория в топологическом порядке. Каждый узел дерева, который затем станет коммитом, помечается уникальным текстовым идентификатором, например ":1000000003". После преобразования модулей второго типа в соответствующих новых репозиториях будет лежать файл .hg/shamap, с отображением коммитов mercurial в идентификаторы fastimport. Кроме того, каждый узел дерева в текстовом виде содержит список измененных файлов, например
M 100644 :30 sub/Makefile
здесь :30 — маркер блоба, т.е. фактически содержания файла. Используя путь файла можно идентифицировать все файлы занесенные к нам из вложенных модулей. Нам бы хотелось преобразовать fastimport дамп таким образом чтобы в этих местах возникли ссылки на внешние репозитории, например так
M 160000 2d1eb0523f244f7bb1d58f016583a25ba3d40426 sub

Для этого предлагается следующих алгоритм. У нас есть два дерева в виде дампов fastimport: дерево вложенного репозитория и дерево основного. Кроме того, есть отображение маркеров узлов вложенного репозитория на хэш коммита. Обойдя каждое из деревьев для каждого узла, мы можем полностью восстановить состояние, т.е. набор всех существовавших файлов и их содержимое. Кроме того, для основного репозитория возможно определить состояние вложенной директории (будущего сабрепозитория). На рисунке схематично изображены узлы основного дерева (пронумерованы арабскими цифрами) и узлы дерева сабрепозитория (пронумерованы латинскими буквами). Черными стрелками показана очередность коммитов, стрелка идет от более нового коммита к его предку.
Далее, сравнивая состояния вложенного репозитория и соответствующие под-состояния основного репозитория, можно построить отображение узлов основного дерева на вложенное (красные стрелки на рисунке).

Возможны ситуации когда связь многозначная. Вообразим, что в предложенном примере G откатывает F, поэтому имеет такое же содержимое что и E, кроме того, в другой ветке есть C с таким же содержимым. Сообщения коммитов могут отсутствовать, время коммитов может отличаеться. В данной ситуации предлагаются следующие критерии разрешения конфликта.

Так как основное дерево обходится в топологическом порядке, то к моменту когда мы оказывается в узле 5 узел 4 уже обработан и мы знаем что ему соответствует узел D. Для всех возможных кандидатов C, E, G рассчитаем расстояние между ними и узлом D. Узел D не достижим из C, поэтому C отбрасывается. Оставшийся выбор производится на основании сообщения коммита и близости времени. Либо можно представить простой алгоритм, который за не полиномиальное время назначит соответствие, однако без информации о времени и сообщениях коммитов все-равно не обойтись.

Таким образом, у нас есть отображение меток дерева основного репозитория на метки (или хэши) вложенного репозитория. Этой информации достаточно, чтобы заменить все нужные вхождения об изменениях файлов в дампе. После этого, дамп можно скормить в hg-fastimport.

Наиболее удобным способом расположения иерархии репозиториев на сервере оказался следующий. Каждый вложенный репозиторий кладется в корень иерархии, кроме того, в каждый проект. Причем этим вторичным клонам мы запретим запись. А на первичный репозиторий навесим хуки, которые будут дергать pull во всех клонах.
[extensions]
hgext.acl=

[hooks]
pretxnchangegroup.acl = python:hgext.acl.hook

[acl]
sources = serve push bundle

[acl.deny]
** = *

[paths]
default = /repos/subrepo

OBS: локальная сборка под ARM

Когда требуется локально пересобрать пакет под одну из архитектур ARM, например для тестирования сборки, можно воспользоваться следующим медленным подходом.

osc build --clean --alternative-project openSUSE:Factory:ARM qemu armv7l name.spec

Здесь указывается, что используется альтернативный проект для сборки — openSUSE:Factory:ARM (версию подставить по желанию), используется репозиторий qemu, который есть в этом проекте. Поэтому и указывается этот альтернативный проект.

Несмотря на то, что в данном примере указана архитектура armv7l, технически, это x86_64. В среду сборки будет установлен бинарный транслятор qemu, работающий через подсистему ядра binfmt. При инициализации ядро настраивается таким образом, что при виде сигнатуры ELF для архитектуры ARM вызывает бинарик как qemu-arm /usr/bin/..., подобно тому, как при виде символов "#!" в начале исполняемого файла вызывается указанный интерпретатор. Работает поэтому достаточно медленно.

К сожалению, с пакетами kiwi такое не проходит.

Сетевой стек на ATSAMD20

В предыдущих выпусках рассматривалась демонстрационная плата на базе 32-битного микроконтроллера ATSAMD20. Далее рассмотрим как прикрутить к нему сетевое приложение. Так как сам микроконтроллер не снабжен интерфейсом Ethernet, возьмем какое-нибудь готовое решение на базе ENC28J60 и прикрутим его через SPI.

Нам понадобится:
  • Atmel Software Framework — распространяемая под странным образом модифицированной BSD лицензией библиотека, реализующая удобный уровень индирекции при работе с переферией микроконтроллеров Atmel. Мы ей воспользуемся для работы через SPI. Внутри библиотеки весьма красивый код, понятный API и гора документации, исполненной в doxygen. Поэтому, основываясь на чувстве прекрасного, будем её использовать.
  • ec28j60driver — драйвер для ENC28J60, реализует высокоуровневые функции работы с Ethernet путем обмена через SPI. Возьмем этот, потому-что написан он не левой пяткой и был достаточно легко портирован для работы с Atmel Software Framework. (см. ветку asf)
  • lwIP — легковесный стек TCP/IP. Он отсылает или принимает Ethernet-пакеты через драйвер устройства и дальше разбирается что-же там внутри пришло. На сайте присутствует куча примеров.

Настройки для lwIP следующие:

lwoptions.h:
#define MEM_LIBC_MALLOC 1
#define MEMP_MEM_MALLOC 1
#define MEM_ALIGNMENT 4

cc.h:
#define BYTE_ORDER  LITTLE_ENDIAN

Стиль программирования приложений, определяемый использованием RAW API библиотеки lwIP, через 5 минут создает полное ощущение, что ты пишешь gen_server на erlang, но последний почему-то кривой, неудобный и недоделанный.

Далее в таблице приводится размер кода прошивки, получаемый при различных вариантах конфигурации:
config -O3 -Os
TCP/IP + DHCP + DEBUG(printf) 96k 82k
TCP/IP + DHCP 54k 40k
TCP/IP 45k 36k
UDP/IP + SNMP 54k 43k
UDP/IP 28k 24k

Как-то так оно и работает.

openSUSE 13.1 на BeagleBone Black

BeagleBone Black — одноплатный компьютер на основе процессора TI AM3358 (Cortex-A8), выпущенный весной прошлого года:

На нем вполне успешно может загрузиться и даже работать openSUSE 13.1. Готовые образы для нанесения на microSD карточку находятся здесь. Эти же самые образы должны в теории работать и на BeagleBone. Конечно, пока все на этапе интеграции и поиска ошибок, поэтому имеется подробная инструкция по запуску:
  1. Скачать, распаковать и сделать dd на карточку образ системы *.raw.xz
  2. Подключить отладочный TTL-RS232 порт (можно использовать, например, такой, но важно помнить, что разработчики BeagleBone Black не поставили на свою плату преобразователь уровней и сигнал с отладочного RS232 идет в виде 3.3V. Подключение через обычный COM-порт на большом компьютере обречено на провал.), руководствуясь распиновкой. Используя screen /dev/ttyUSB0 115200, во-первых, можно будет легко видеть все этапы начиная от работы u-boot. Во-вторых, запустится Yast2 firstboot и попросит вас задать root пароль, завести пользователя, установить временную зону и системное время (аналогично тому, как это происходит на соответствующем этапе установки с DVD), а для этого потребуется интерактивное взаимодействие.
  3. Помните, что для загрузки с внешней карты памяти при подаче питания на устройство нужно удерживать кнопку S2 (она же boot select switch). Подсказка — загрузившись с SD карты можно подмонтировать раздел встроенной MMC памяти и переименовать или удалить файл загрузчика MLO, после этого загрузка будет всегда начинаться с SD, но загрузиться с MMC больше не получится.

Технические подробности (тем, кто хочет продолжить ковыряния) изложены в списке рассылки: http://lists.opensuse.org/opensuse-arm/2014-01/msg00001.html.

Atmel ATSAMD20-XPRO: собираем компилятор

Поскольку сборка компилятора привела к боли и страданиям, она будет тут сохранена для истории (такими историями гугл полнится).

Возьмем последний binutils-2.24:
./configure  --target=arm-none-eabi -enable-interwork   --enable-multilib --disable-nls  --disable-libssp --prefix=/opt/arm-none-eabi
make
make install-strip
export PATH=$PATH:/opt/arm-none-eabi/bin

Возьмем newlib-2.0.0 и распакуем его, пусть проветривается.

Возьмем gcc-4.8.2 который слышал о cortex-m0plus. Возьмем дебиановские патчи отсюда. Наложим их и скомпилируем:
mkdir build
../configure  --target=arm-none-eabi --prefix=/opt/arm-none-eabi --enable-languages="c,c++" --enable-interwork --enable-multilib  --with-
newlib --with-headers=/path/to/src/newlib-2.0.0/newlib/libc/include/  --disable-nls --disable-libssp  --with-system-zlib --with-multilib-lis
t=armv6-m
# в этом месте ./configure может ругаться, надо проследить чтобы он заголовки смог переписать на место
make
make all-gcc
make install-gcc

newlib собирается со следующими ключами (без --disable-newlib-supplied-syscalls не собираются некоторые куски для armv6-m, и atmel-овский asf ругается на дублирующиеся определения функций из syscalls):
./configure --target=arm-none-eabi -enable-interwork --enable-multilib --disable-libssp --disable-nls --prefix=/opt/arm-none-eabi --disable-newlib-supplied-syscalls  --enable-newlib-register-fini --enable-newlib-io-long-long
make
make install

После доделываем gcc:
cd build
make all
make install

Atmel ATSAMD20-XPRO


ATSAMD20-XPRO оценочный набор для новой серии микроконтроллеров Atmel ATSAMD20 на ядре Cortex-M0+. Выглядит следующим образом:
Купить в России можно например тут: Дельта Электроника.

Этот пост о том как подключать эту плату в GNU/Linux. Плата снабжена интерфейсом micro-USB для отладки, реализуемым через отдельную микросхему. Atmel называет это «Embedded Debugger», анонсируется наличие виртуального последовательного интерфейса (прикрученного к основному чипу) и собственно средств отладки и программирования.

При подключении появляется устройство:
Bus 001 Device 010: ID 03eb:2111 Atmel Corp.

[13761.329235] usb 1-1.5: New USB device found, idVendor=03eb, idProduct=2111
[13761.329240] usb 1-1.5: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[13761.329242] usb 1-1.5: Product: EDBG CMSIS-DAP
[13761.329245] usb 1-1.5: Manufacturer: Atmel Corp.
[13761.329247] usb 1-1.5: SerialNumber: ATML1873040200000110
[13761.330868] hid-generic 0003:03EB:2111.000A: hiddev0,hidraw3: USB HID v1.11 Device [Atmel Corp. EDBG CMSIS-DAP] on usb-0000:00:1a.0-1.5/input0
[13761.330964] cdc_acm 1-1.5:1.1: ttyACM0: USB ACM device

Устройство, как видно, отдает нам виртуальный последовательный интерфейс на /dev/ttyACM? и интерфейс CMSIS-DAP на /dev/hid*.

В openocd недавно добавили поддержку этого интерфейса. Пока пакеты openocd с поддержкой CMSIS-DAP доступны здесь:

В пакетах есть небольшой баг, связанный с тем, что udev стремится поставить на /dev/hidraw* группу владельца plugdev, которая по-умолчанию в системе не существует и в пакете не создается. openocd будет искать свой devel-project и потом попадет в Factory.

Запускаем и убеждается, что таки оно работает:
> openocd -f /usr/share/openocd/scripts/board/atmel_samd20_xplained_pro.cfg 
Open On-Chip Debugger 0.8.0-dev-snapshot (2013-12-13-18:45)
Licensed under GNU GPL v2
For bug reports, read
        http://openocd.sourceforge.net/doc/doxygen/bugs.html
Info : only one transport option; autoselect 'cmsis-dap'
Info : CMSIS-DAP: SWD  Supported
Info : CMSIS-DAP: Interface Initialised (SWD)
adapter speed: 500 kHz
adapter_nsrst_delay: 100
cortex_m reset_config sysresetreq
Info : CMSIS-DAP: FW Version = 01.0F.00DB
Info : SWCLK/TCK = 1 SWDIO/TMS = 1 TDI = 1 TDO = 1 nTRST = 0 nRESET = 1
Info : DAP_SWJ Sequence (reset: 50+ '1' followed by 0)
Info : CMSIS-DAP: Interface ready
Info : clock speed 500 kHz
Info : IDCODE 0x0bc11477
Info : at91samd20j18.cpu: hardware has 4 breakpoints, 2 watchpoints

Быстродействие UNIX сокетов

Сравнение скорости работы unix-сокетов. Выполнялась передача большого объема данных от одного процесса другому через unix-сокет. Изменялся размер данных, передаваемых за один вызов write/read (chunk). С каждым размером производилось пять измерений, результат усреднялся.


На графике черная кривая — производительность unix-сокетов, красная и зеленая — для сравнения копирование памяти memcpy в рамках одного процесса и, соответственно, копирование из разделяемой (shared) памяти. Если рост производительности при увеличении "размера пакета" для сокетов не является чем-то не очевидным, то падение производительности операции memcpy при росте значения третьего аргумента функции не тривиально.

Характеристики тестового стенда. Процессор: Intel(R) Core(TM) i5-3470 CPU @ 3.20GHz, память: DDR3 Kingston HyperX KHX1600C9D3P1K2/8G. Ядро: 3.7.10.