Сортировка Конфигов для Make Сборок
Как известно любая большая программа на Си содержит много программных компонентов и, как следствие, много настроек: констант, макросов, конфигурационных структур и прочего. Всё это можно назвать одним словом: конфиги.
Все передают конфиги по-разному. Это один из религиозных аспектов в программировании микроконтроллеров. Junior разработчики прописываю прохардкоженные константы в каждом файле проекта или пихают всё в config.h, который подом вручную подключают #include (ом) во все файлы, Middle программисты передают конфиги через переменные окружения, Senior (ы) вообще передают конфиги через Device Tree и механизм Kconfig.
Терминология
Конфиг — набор определенных переменных окружения, которые используются при сборке программы (например на Си).
Цель — последовательность процессов, которые запускает утилита make, чтобы получить какой-то файл или просто выполнить действие.
Сборка — это папка с настройками конкретного проекта программы и артефакты. Обычно сборка это комплект следующих файлов: Makefile, config.mk, version.h. Если Вы работаете в Eclipse, то к файлам сборки также относятся настройки текстового редактора Eclipse для этой конкретной сборки. Это файлы *.project *.cproject. Всё перечисленное следует подвергать версионному контролю (например в GIT). Сборка порождает артефакты. Это файлы *.hex, *.bin, *.map, *.elf. Хорошей практикой считается, когда в папке со сборкой даже нет ни одного *.с файлика! Всё, что нужно сборка берёт из общей пере используемой кодовой базы, которая лежит в другой папке.
Пролог
У нас в репозитории есть много сборок (порядка 350) на все случаи жизни, где конфиги передаются прямо через переменные окружения прописанные в make скриптах.
У каждой сборки есть файл config.mk в котором перечислены программные компоненты из которых должна собираться эта конкретная сборка. Содержимое этого файла обычно выглядит так.
#Do not include .mk files here. That is general global configuration
ADC=Y
ADC_ISR=Y
ADT=Y
ALLOCATOR=Y
ARRAY=Y
ASICS=Y
....
TASK=Y
TBFP=Y
TERMINAL=Y
TIME=Y
UART0=Y
UART2=Y
UNIT_TEST=Y
UTILS=Y
UWB=Y
Это просто атомарные строчки. Тут происходит определение переменных окружения. Декларативно перечисляется из чего собирать прошивку. У другой сборки есть свой такой же декларативный config.mk файл и свой собственный набор переменных окружения.
В чем проблема?
1--Проблема в том что, когда сборок становится слишком много, то их приходится создавать по образу и подобию других сборок. Потом внезапно возникает потребность сравнить конфиги и выясняется, что конфиги отличаются не потому, что состоят из разных программных компонентов, а потому, что конфиг файлы перечислены в случайном порядке (не отсортированы). Как известно, правда в том, что минимальный diff будет у двух отсортированных файлов.
2--При командной разработке разные разработчики не глядя на существующий конфиг повторно добавляют новые конфиги, которые дублируют старые конфиги. Иногда они добавляют вперед и их конфиг не применяется. Когфиг перетирается предшественником. Если же добавляют конфиг в конец и он обнуляет конфиг предшественника. В общем это порождает анархию и взаимную ненависть.
3--Когда конфиг отсортирован, то его проще просматривать и искать в нем что -то. Также легче найти место для вставки нового нужного конфига методом визуального бинарного поиска.
В связи с этим возникают две задачи:
1--Удалить повторяющиеся конфиги.
2--Отсортировать конфиги по алфавиту.
Как отсортировать конфиги?
1--Понятное дело что вручную никто не хочет сортировать 150 строчек в файле. Дело в том что очень мало российских программистов вообще знают сколько букв содержится в английском алфавите и еще меньше людей могут сходу взять карандаш и правильно написать порядок букв английского алфавита. И это нормально! Надо просто автоматизировать процесс сортировки файла.
2--Можно написать отдельный *.bat файл для сортировки когфигов культовой консольной утилитой sort.exe. Утилиту sort можно установить из пакета CygWin. Однако вам придется при добавлении каждой новой сборки также прописывать новую строчку в этом *.bat файле сортировки. По факту оказалось что людям просто лень этим заниматься. И это тоже нормально!
3--Самое правильное решение — это встроить сортировку конфигов в сам процесс сборки артефактов. К счастью мы использует GNU Make и тут это сделать очень просто. Для этого надо всего лишь определить ещё одну крохотную цель.
SORTER_TOOL=C:/cygwin64/bin/sort.exe
$(info Sort Program config)
$(info MK_PATH=$(MK_PATH))
MK_PATH_WIN := $(subst /cygdrive/c/,C:/, $(MK_PATH))
$(info MK_PATH_WIN=$(MK_PATH_WIN))
$(info WORKSPACE_LOC=$(WORKSPACE_LOC))
PROJECT_DIR=$(MK_PATH_WIN)
CONFIG_FILE=$(PROJECT_DIR)/config.mk
$(info CONFIG_FILE=$(CONFIG_FILE))
#CONFIG_FILE:=$(subst /cygdrive/c/,C:/, $(CONFIG_FILE))
#$(info CONFIG_FILE=$(CONFIG_FILE))
sort_config: $(CONFIG_FILE)
$(info SortConfig...)
$(SORTER_TOOL) -u $(CONFIG_FILE) -o $(CONFIG_FILE)
Тут опция -u означает что утилита будет удалять повторения, конструкция $(CONFIG_FILE) -o $(CONFIG_FILE) означает, что имя файла на выходе будет совпадать с именем файла на входе. Получается сортировка in place.
Затем надо открыть основной rule.mk и встроить туда новую цель sort_config. Перед сборкой сорцов отсортировать конфиг
EXTRA_TARGETS += sort_config
EXTRA_TARGETS += $(DEPENDENCY_GRAPH)
EXTRA_TARGETS += $(STATIC_ANALYSIS_TARGET)
EXTRA_TARGETS += $(CLI_COMMAMD_LIST_GENERATE)
EXTRA_TARGETS += $(PREPROCESS_CODE_BASE)
EXTRA_TARGETS += generate_definitions
# default action: build all
all: $(EXTRA_TARGETS) $(BUILD_DIR)/$(TARGET).elf $(BUILD_DIR)/$(TARGET).hex $(BUILD_DIR)/$(TARGET).bin
generate_definitions:
cpp $(CPP_FLAGS) $(WORKSPACE_LOC)empty_sourse.c -dM -E> c_defines_generated.h
Достоинство rule.mk в том, что он общий на все 350 сборок в нашем репозитории. И нам не нужно ничего больше прописывать в настройках каждого проекта, как если бы мы программировали микроконтроллер в пресловутом IAR или Keil.
Теперь если вы откроете папку сборки в консоли и напишите make all, Вы через минуту получите не только артефакты, но и чистый конфиг!
Сортировка нужна не только в сортировке конфигов. Автоматический форматировщик отступов в Си коде clang-format.exe, например, еще сортирует #include по алфавиту.
Итоги
Сортировка всегда хороша, когда от нее есть польза, например как тут это дает сокращение времени поиска сравнения конфигов и исключает ошибки на фазе прописывания этих самых конфигов.
При этом сортировка это признак наличия какого-то разума и культуры. Например посвящённые страны сортируют даже бытовые отходы.
Как видите, использование сборки из-под скриптов позволяет Вам помимо самой сборки артефактов еще и выполнять всяческую инфраструктурную DevOps (овую) работу. Это особенно просто и легко делается в GNU Make.
Links
https://ru.wikipedia.org/wiki/Sort
Почему Важно Собирать Код из Скриптов https://habr.com/ru/articles/723054/