— Вы не скажете сколько сейчас градусов ниже нуля?
— Чего чего?
— Ну я …. тренируюсь.
— Тренируйся лучше..(смотрит на Моргунова) … на кошках.
В статье описывается опыт использования CMAKE и LZ4 c некоторым уклоном на embedded системы. Тем, кто знаком с Makefiles или даже CMAKE можно смело пропускать несколько первых параграфов.
Кошки
В качестве кошек лучше взять небольшой opensource проект в сети состоящий из малого количества файлов и нескольких целей (исполняемых файлов и библиотек). Такой проект найти сложновато, ибо многие популярные проекты уже обросли кучей файлов. Однако, неплохим кандидатом оказался упаковщик LZ4. LZ4 ещё молод, а самое главное, он достаточно прост и направлен на СУПЕР БЫСТРОЕ сжатие информации, может поэтому он мал, так как большой кусок кода не будет быстро работать (не соптимизируется в кэше).
LZ4 может быстро сжимать информацию, что для embedded мира скорее означает не большие требования к производительности и памяти. Мне удалось адаптировать его к сжатию для LPC2478 при этом объём ОЗУ которое он занял составил 12k(4k входной буфер + 4k выходной + 4k словарь). Я думаю, данный объём может быть уменьшен до 256 +256 + 256, естественно за счёт потери коэффициента сжатия. Тем не менее, даже это хороший вариант для того, чтобы вы не городили свои хитрые алгоритмы сжатия истории опроса каких-нибудь систем если у вас идут «почти» повторяющиеся записи размером в 30~100 байт. Тем кого заинтересовал алгоритм сжатия отмечу, LZ4 является заоптимизированным вариантом алгоритма LZ77.
Отдельной похвалы заслуживает функция распаковки. Она очень проста и умещается в десятки строк кода, это позволяет достигать скорости распаковки в 1GB/s на современных ПК.
Для чего нужна система сборки в embedded системах?
Как правило, любой проект в embedded системах начинается с «hellow word», то есть с UartInit()+ uart_put_char(). Однако, он очень быстро разрастается на несколько файлов, ну и конечно у профессионалов есть очень много готового исходного кода, оформленного в виде статических библиотек. Чтобы создать исполняемый образ в любом серёзном проекте на несколько десятков и сотен килобайт кода необходимо пропустить десятки и сотни файлов через компилятор. Как раз этим и занимается система сборки. Схему сборки можно упрощённо изобразить в следующем виде:
Исходный код → система сборки → компилятор → исполняемый(е) файл(ы)
Конечно, ваш новомодный проприетарный IDE всячески постарается выполнить всю работу по сборке за вас, только нажмите «Build all». По мере роста вашей «кодовой базы» вы будете задумываться над тем, чтобы собирать имеющийся код как можно чаще и в различных вариантах.
Причинами наличия нескольких вариантов сборки кода могут быть наличие следующих факторов:
- несколько проектов
- несколько плат
- даже несколько типов контроллеров
- тестирование частей кода на ПК
- нескольких веток разработки из-за большого коллектива
Первой реакцией на такие требования всегда бывает: «а давайте вставим ifdef», однако, очень скоро вы понимаете, что одними ifdef-ами вам не обойтись или обойтись, но код выглядит не очень красиво.
Makefiles
На арену выходит утилита Make, и брутальные мужики берут компилятор и ключи к нему и начинают писать правила сборки. Make достаточно быстро осваивается, однако, если у Вас уже три проекта и много папок, в каждой из которых библиотека, приходиться проявить большее усердие в написании Makefiles. Схема сборки с Make выглядит примерно так:
Исходный код → GNU MAKE → компилятор → исполняемый(е) файл(ы)
СMAKE
CMAKE – это продвинутый make. Исходя из правил сборки описанных в CmakeLists.txt CMAKE генерирует файлы управления сборкой для MAKE и других утилит и сред разработки (список поддерживаемых утилит можно получить путём запуска «cmake –help»). Теперь схема выглядит следующим образом:
Исходный код → CMAKE → GNU MAKE или NMAKE или …. → компилятор → исполняемый(е) файл(ы)
Среди достоинств CMAKE отмечу:
- Сам язык правил сборки Lisp-о подобный и более развит чем у MAKE. Неплохой шанс попробовать что-либо отличное от традиционной парадигмы Си.
- Утилита кросс-платформенна, ваша сборка не будет зависеть от IDE и рабочей OС.
- Генерируются не только правила сборки, но и проекты для Code-blocks, Eclipse и Visual Studio.
- Имеются широкие возможности по генерации самого исходного кода, а также много модулей, что позволяет заменять Automake и Qmake.
- Имеются системы автоматической сборки (CDASH), упаковки (CPACK) и тестирования (CTEST).
- СMAKE популярен. Многие команды разработчиков переводят под него свои проекты (например KDE).
Главным преимуществом в области embedded решений является то, что, в конечном итоге, ваш код становиться более переносимым, вы ещё на шаг ближе к тому, чтобы собрать часть вашего приложения под PC с целью более комфортной отладки. Вы можете устраивать BUILD тесты автоматизированно, не знаю позволяет ли это Code Red или CoCox, но с CMAKE вы не зависите от IDE.
минимальный проект
Ниже приведён простой (но не простейший) файл CmakeLists.txt.
cmake_minimum_required (VERSION 2.8)
PROJECT(LZ4 C)
set(SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/)
ADD_DEFINITIONS("-Wall")
ADD_DEFINITIONS("-W")
ADD_DEFINITIONS("-Wundef")
ADD_DEFINITIONS("-Wcast-align")
ADD_DEFINITIONS("-Wno-implicit-function-declaration")
ADD_DEFINITIONS("-O3 -march=native -std=c99")
INCLUDE_DIRECTORIES (${SRC_DIR})
set(LZ4_SRCS_LIB lz4_decoder.h lz4_encoder.h ${SRC_DIR}lz4.c ${SRC_DIR}lz4hc.c ${SRC_DIR}lz4.h ${SRC_DIR}lz4_format_description.txt)
set(LZ4_SRCS ${LZ4_SRCS_LIB}
${SRC_DIR}xxhash.c
${SRC_DIR}bench.c
${SRC_DIR}lz4c.c
)
set(FUZZER_SRCS
#вставим сюда какой-нибудь комментарий
${LZ4_SRCS_LIB}
${SRC_DIR}fuzzer.c
# ${SRC_DIR}ooops_commented_source_file.c
)
# устанавливает цель lz4c
add_executable(lz4c ${LZ4_SRCS})
# устанавливает цель fuzzer
add_executable(fuzzer ${FUZZER_SRCS})
Сmake_minimum_required задаёт минимальную версию Cmake-а. Команда PROJECT задаёт имя проекта, а также язык(и) программирования.
Команды могут быть написаны как строчными так и прописными буквами. Самая частая команда set – некий аналог присваивания и создания переменных, как и 'Let' в Бэйсике она начинает немного надоедать, но потом привыкаешь. В CMAKE также «заранее» предопределены другие переменные одна из них ${CMAKE_CURRENT_SOURCE_DIR}. Как и в make SRC_DIR – имя переменной, а ${SRC_DIR} – значение. Переменные в CMAKE обрабатываются как списки, но иногда могут быть интерпретированы как строчки или числа. Списки файлов можно спокойно разорвать на несколько строк.
ADD_DEFINITIONS вообще-то должна быть использована для задания макросов препроцессора, но здесь она просто используется для задания ключей к компилятору.
add_executable задаёт цель сборки – исполняемый файл.
Конфигурирование
Уже такой небольшой CmakeLists.txt позволяет собрать упаковщик LZ4 (на сегодняшний день ревизия 94). Открываем командную консоль и запускаем cmake c указанием генератора (см снимок эрана).
Сmake выдаёт некоторые базовые сообщения, которые в данном случае оставим без внимания – ничего нестандартного не произошло – генерируется Makefile и проект под CodeBlocks.
Наличие предварительной фазы настройки в виде запуска Cmake перед make позволяет изменить процесс компиляции исходя из аргументов запуска CMAKE и варианта ПО(ОС, компилятора) на котором происходит сборка. Также можно скопировать файлы, скачать файлы, наложить заплатки, генерировать исходные файлы и прочее прочее. В Cmake есть возможность передавать дополнительные определяемые пользователем аргументы. Сmake также может запускать различные утилиты в процессе конфигурации. Об этом я постараюсь написать в следующей части.
С большой натяжкой можно сказать, что по части сборки CMAKE способен выполнять функционал Portage+automake в Gentoo Linux, но по сравнению с portage Cmake гораздо более компактен чем дистрибутив питона, он более переносим на многие платформы(в первую очередь Windows).
Запуск сборки
Сборку запускается через а)СodeBlocks b)make (советую попробовать также 'make VERBOSE=1' и 'make help'). Дополнительными плюшками над make являются цветной вывод в консоль, а также проценты выполнения.
Мяу!
Теперь давайте поразвлекаемся с нашим упаковщиком lz4.
C:Desktoplz4>lz4c.exe -H *** LZ4 Compression CLI , by Yann Collet (May 1 2013) *** Usage : lz4c.exe [arg] input output Arguments : -c0/-c : Fast compression (default) -c1/-hc: High compression -d : decompression -y : overwrite without prompting -H : Help (this text + advanced options) Advanced options : -t : test compressed file -B# : Block size [4-7](default : 7) -x : enable block checksum (default:disabled) -nx : disable stream checksum (default:enabled) -b# : benchmark files, using # [0-1] compression level -i# : iteration loops [1-9](default : 3), benchmark mode only input : can be 'stdin' (pipe) or a filename output : can be 'stdout'(pipe) or a filename or 'null' example : lz4c -hc stdin compressedfile.lz4
Упаковка самого себя.
C:Desktoplz4>lz4c.exe lz4c.exe lz4c.lz4 *** LZ4 Compression CLI , by Yann Collet (May 1 2013) *** Compressed 98828 bytes into 56586 bytes ==> 57.26% Done in 0.03 s ==> 3.04 MB/s
Запускаем benchmark.
C:Desktoplz4>lz4c.exe -b lz4c.exe lz4c.lz4 *** LZ4 Compression CLI , by Yann Collet (May 1 2013) *** lz4c.exe : 98828 -> 56567 (57.24%), 163.3 MB/s , 467.5 MB/s lz4c.lz4 : 56586 -> 56278 (99.46%), 273.1 MB/s , 1442.1 MB/s TOTAL : 155414 -> 112845 (72.61%), 191.3 MB/s , 620.0 MB/s
Компьютер у меня не очень быстрый, и я не претендую на полноценное тестирование, но и эти цифры весьма впечатляют.
Итого
Итак, надеюсь данная статья поможет Вам в освоении утилиты Cmakе. В следующей части я постараюсь написать о дальнейших изысканиях в вопросе сборки LZ4. Тем кому любопытно, то написанный мною окончательный вариант файл-а был принят автором программы, однако в текущей версии LZ4 есть два файла CmakeLists, один написан мной и используется для сборки, а другой используется для построения библиотек, хотя есть возможность объединить эти два файла у меня пока не дошли руки. Гуру Сmake-а дерзайте!
Автор: irtos