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

Все передают конфиги по-разному. Это один из религиозных аспектов в программировании микроконтроллеров.

1--Junior разработчики прописываю (про)hard(коженные) константы в каждом файле проекта или пихают всё в config.h, который потом вручную подключают #include(ом) во все *.с файлы,

2--Middle программисты передают конфиги через переменные окружения, которые определяют в скриптах сборки (Make, Cmake и т.п.).

3--Senior(ы) вообще передают конфиги через Device Tree и механизм Kconfig.

Тут мы не будет рассуждать как лучше и правильнее передавать конфиги в сборки. Поговорим лишь о том как поступать во втором случае, когда конфиги прописаны как переменные окружения в отдельном make файле по имени config.mk.

Терминология

Конфиг - набор определенных переменных окружения, которые используются при сборке программы (например на Си).

Цель - последовательность процессов, которые запускает утилита 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--При командной разработке разные разработчики не глядя на существующий конфиг повторно добавляют новые строчки переменных окружения, которые дублируют старые переменные окружения. Иногда они добавляют вперед и их переменная окружения не применяется. Переменная окружения перетирается предшественником. Если же добавляют переменную окружения в конец файла кофига, то она обнуляет предыдущую переменную окружения. В общем это порождает анархию и взаимную ненависть.

Когда конфиг отсортирован, то его проще просматривать и искать в нем что-то. Также легче найти место для вставки новой переменной окружения методом визуального бинарного поиска.

В связи с этим возникают две задачи:

1--Удалить повторяющиеся переменные окружения.

2--Отсортировать переменные окружения по алфавиту внутри файла config.mk.

Как отсортировать конфиги?

1--Понятное дело, что вручную никто не хочет сортировать 150 строчек в файле. Дело в том что очень мало программистов вообще знают сколько букв содержится в английском алфавите и еще меньше людей могут сходу взять карандаш и правильно написать порядок букв английского алфавита от A до Z. И это нормально! Надо просто автоматизировать процесс сортировки файла.

2--Можно написать отдельный *.bat файл для сортировки когфигов культовой консольной утилитой sort.exe. Утилиту sort можно установить из пакета CygWin. Однако вам придется при добавлении каждой новой сборки также прописывать новую строчку в этом *.bat файле сортировки. По факту оказалось, что людям просто лень этим заниматься. И это тоже нормально!

3--Самое правильное решение - это встроить сортировку конфигов в сам процесс сборки артефактов. К счастью, мы традиционно используем систему сборки GNU Make и тут это сделать очень просто. Для этого надо всего лишь определить ещё одну крохотную цель sort_config.

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 означает, что утилита sort будет удалять повторения, а конструкция $(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 в том, что он общий на все 150-350 сборок в нашем репозитории. И нам не нужно ничего больше прописывать в настройках каждого проекта, как если бы мы программировали микроконтроллер в пресловутом IAR или Keil.

Теперь если Вы откроете папку сборки в консоли и напишите make all, Вы через минуту получите не только артефакты, но и аккуратный причёсанный конфиг!

Сортировка нужна не только в сортировке конфигов. Автоматический форматировщик отступов в Си коде clang-format.exe, например, еще сортирует #include по алфавиту.

Общая канва в том, что в программировании надо сортировать всё, что перечисляется и где порядок не имеет существенного значения. Это перечисление прототипов фунцнкий, перечисление констант emumeratios, перечисление include(ов), перечисление конфигов и т.п.

Итоги

Сортировка всегда хороша, когда от нее есть польза, например как тут это дает сокращение времени поиска сравнения конфигов и исключает ошибки на фазе прописывания этих самых конфигов.

При этом сортировка это признак наличия какого-то разума и культуры. Например посвящённые страны сортируют даже бытовой мусор.

Как видите, использование сборки из-под скриптов позволяет Вам помимо самой сборки артефактов еще и выполнять всяческую инфраструктурную DevOps(овую) работу. Это особенно просто и легко делается в GNU Make.

GNU Make вообще всё равно с каким языком программирования работать. Более того, GNU Make всё равно даже какие консольные утилиты запускать.

Links

https://ru.wikipedia.org/wiki/Sort

Генерация зависимостей внутри программы
https://habr.com/ru/articles/765424/

Сборка firmware для CC2652 из Makefile
https://habr.com/ru/articles/726352/

Почему Важно Собирать Код из Скриптов
https://habr.com/ru/articles/723054/

Настройка ToolChain(а) для Win10+GCC+С+Makefile+ARM Cortex-Mx+GDB
https://habr.com/ru/articles/673522/

Only registered users can participate in poll. Log in, please.
Вы использовали утилиту sort для чего-либо?
55.56%да10
44.44%нет8
18 users voted. 1 user abstained.
Only registered users can participate in poll. Log in, please.
Вы собирали программы из GNU Make?
90.48%да19
9.52%нет2
21 users voted. 1 user abstained.
Only registered users can participate in poll. Log in, please.
Как Вы передаете конфигурации для сборки С/C++ программ/прошивок?
30%через прохардкоженные константы прямо в коде6
65%через *.h файлики13
15%через скрипты сборки GNU make3
10%через механизм Device Tree2
10%через механизм Kconfig2
45%через скрипты Сmake9
30%через конфигурационный структуры в Си коде6
0%через скрипты ninja0
10%через xml(ку) для GUI-IDE2
20 users voted. 3 users abstained.
Only registered users can participate in poll. Log in, please.
Вы пользуетесь утилитой WinMerge?
35.29%да6
64.71%нет11
17 users voted. Nobody abstained.