
Пролог
В программировании микроконтроллеров часто приходится писать программные компоненты которые, в общем-то, очень похожие друг на друга по своей структуре и API. Как у нас говорят
Сделай по образу и подобию
Вот вам яркий пример. Надо написать драйвер SPI на основе драйвера UART. Чтобы функции HAL примерно одинаково выглядели за исключением, конечно же, самого названия программного компонента. Это соответствует принципу наименьшего удивления.
В чем проблема?
Как обычно это происходит. Берут драйвер UART копируют его, переименовывают слово UART на SPI и далее вручную прорабатывают конкретные места в коде.
Проблема в то, что переименовывать файлы и ключевые слова это весьма рутинная и утомительная процедура. Очевидно, что надо её как-то автоматизировать.
Терминология
токен - строка без пробела
консистентность - согласованность данных друг с другом
Постановка задачи
Написать консольную утилиту r.exe, которая рекурсивно заменяет одно ключевое слово (токен) на другое ключевое слово (токен) в той папке, в которой вызвали эту утилиту r
1--при замене учитывать регистр в токене
2--заменять ключевое слово в названии файла
3--Утилита должна называться одной буквой r, чтобы её было легко запомнить (rename all) и быстро набирать
4--У утилиты r должно быть только два ключа old new
--> r old_word new_word
Реализация.
Как известно есть bash команды, которые делают эту работу .
Фаза 1 Переименовать ключевое слово в файлах
1--Рекурсивно заменить слово oldtext на newtext во всех файлах внутри директории.
grep -rl oldtext . | xargs sed -i 's/oldtext/newtext/g'
или так
find . -type f -exec sed -i 's/odtext/newtext/g' {} +
Тут для grep -r означает, что искать внутри папок рекурсивно, -l означает показывать только пути к файлам. Для sed -i означает, что замена слова будет произведена прямо в этом же файле (in place), s означает, что надо заменить первый токен на второй токен, флаг g заменяет все вхождения заменяемого токена.
У этой команды есть три недостатка:
1--токен oldtext надо указывать два раза
2--команда длинная и ее сложно запомнить
3--команда длинная и ее долго набирать
Фаза 2: Переименовать имена файлов
--Взять все файлы в этой папке и в каждом имени файла переименовать подстроку at24c02mtr на at24cxx
find . -type f -exec rename -v at24c02mtr at24cxx {} \;
Эта очень удобная команда, когда надо написать программный компонент по образу и подобию другого программного компонента. Сначала переименовываем названия файлов (find+rename)

Исходный код
Исходный код тут тривиальный так как основную работу делают внешние утилиты sed, grep, find и rename
#include "sed.h"
#include <stdio.h>
#include <string.h>
#include "log.h"
#include "win_utils.h"
char* getcwd(char* buf, size_t size);
bool win_cmd_run(const char* const command) {
bool res = false;
if(command) {
LOG_DEBUG(PC, "ExeCmd:[%s],Size:%u", command, strlen(command));
int ret = system(command);
if(0 == ret) {
res = true;
} else {
LOG_ERROR(PC, "Ret,%d=%s", ret, CmdRetToStr(ret));
}
}
return res;
}
bool sed_replace(const char* const old, const char* const new) {
bool res = false;
if(old) {
if(new) {
LOG_INFO(SED, "Replace:[%s]->[%s]", old, new);
char CurDir[500] = {0};
char* str = getcwd(CurDir, sizeof(CurDir));
if(str) {
LOG_INFO(SED, "Current working dir: %s\n", CurDir);
} else {
LOG_ERROR(SED, "getcwd() error");
}
char replaceToken[1000] = {0};
char format[100] = "grep -rl %s . | xargs sed -i 's/%s/%s/g'";
snprintf(replaceToken, sizeof(replaceToken), format, old, old, new);
LOG_INFO(SED, "CMD,replaceToken:[%s]", replaceToken);
res = win_cmd_run(replaceToken);
log_res(SED, res, "CmdT");
char repl_file[1000] = {0};
char formatF[100] = "find . -type f -exec rename -v %s %s {} \;";
snprintf(repl_file, sizeof(repl_file), formatF, old, new);
LOG_INFO(SED, "CMD,ReplaceFileName:[%s]", repl_file);
res = win_cmd_run(repl_file);
log_res(SED, res, "CmdF");
}
}
return res;
}
Проверка работы утилиты r
Вот так отработала утилита r.exe
C:\workspace\code_base\source\mcal\mcal_yuntu\uart1>r uart spi
I,[SYS] argc 3
I,[SYS] Arg0 [r]
I,[SYS] Arg1 [uart]
I,[SYS] Arg2 [spi]
I,[Sed] Replace:[uart]->[spi]
I,[Sed] Current working dir: C:\workspace\code_base\source\mcal\mcal_yuntu\uart1
I,[Sed] CMD,replaceToken:[grep -rl uart . | xargs sed -i 's/uart/spi/g']
I,[Sed] CMD,ReplaceFileName:[find . -type f -exec rename -v uart spi {} ;]
`./uart.gvi' -> `./spi.gvi'
`./uart.mk' -> `./spi.mk'
`./uart_custom_commands.c' -> `./spi_custom_commands.c'
`./uart_custom_commands.h' -> `./spi_custom_commands.h'
`./uart_custom_const.h' -> `./spi_custom_const.h'
`./uart_custom_diag.h' -> `./spi_custom_diag.h'
`./uart_custom_drv.h' -> `./spi_custom_drv.h'
`./uart_custom_isr.c' -> `./spi_custom_isr.c'
`./uart_custom_isr.h' -> `./spi_custom_isr.h'
`./uart_custom_types.h' -> `./spi_custom_types.h'
`./uart_drv_old21.c' -> `./spi_drv_old21.c'
`./uart_mcal.c' -> `./spi_mcal.c'
`./uart_preconfig.mk' -> `./spi_preconfig.mk'
`./uart_verify.txt' -> `./spi_verify.txt'
C:\workspace\code_base\source\mcal\mcal_yuntu\uart1>
Разумеется после скачивания утилиты надо прописать к ней путь в переменную PATH.
Итог
Удалось написать работающую и полезную утилиту авто замены ключевых слов, которая повышает производительность разработки программного обеспечения и помогает улучшать консистентность репозитория.
Как видите, программирование микроконтроллеров - это не только варить прошивки. Это ещё и разработка самих средств разработки. Да... Это могут быть утилиты loader-ы, обновители версий, стилистические анализаторы и прочее.
Если у Вас есть пожелания к улучшению утилиты r, то пишите в комментариях.
Ссылки
Название | URL |
Полезные Заготовки Вызова Утилит Командной Строки | |
Бинарь утилиты r.exe | |
Стилистический-Анализатор: Проверка Наличия Комментария в Конце Фигурной Скобки | |
Стилистический анализатор: синхронизация объявлений и определений static функций | |
Стилистический Анализатор: Синхронизация порядка объявлений и определений функций | |
Автоматическое Обновление Версии Прошивки | |
C Language: system function (Perform Operating System Command) | https://www.techonthenet.com/c_language/standard_library_functions/stdlib_h/system.php |