В некоторых российских организациях есть внутренний стандарт оформления исходных кодов на языке программирования Си. В частности и у нас тоже есть много требований к внешнему виду кода.
У меня уже был текст Синхронизация порядка объявлений и определений глобальных функций. Однако аналогичная история также актуальна для static функций. Среди 463х правил внутреннего code style у нас есть и такое обязательное требование к оформлению кода:
Последовательность объявления static функций должна совпадать с последовательностью определения static функций в конце *.с файла.
Я подозреваю, всем здесь понятно, что, если бы static си-функции были определены в начале си-файла, то этого отчаянного правила бы вообще не существовало в принципе. Однако здесь вам не тут! У нас также есть ещё одно обязательное правило
Все static функции должны быть определены в самом низу *.с файла.
В связи с этим, чтобы код вообще собирался теперь приходится ещё и искусственно объявлять повторно прототипы этих самых static функций в начале *.с файла. Нормально так да? А раз так, то пусть тогда и порядок совпадает. Цирк с конями, да и только.
Это ярчайший пример того, как буквально на пустом месте можно создать себе и коллегам палки в колёса на много-много лет вперёд. Это классический пример ничем не обоснованной IT муштры.
Дело ясное, что поведение кода будет одним и тем же, что отсортированы static прототипы что не отсортированы, погоду это не изменит. Компилятору это всё равно. Модульные тесты (если они есть) в любом случае будут проходить одинаково.
Можно подумать, что нам тут ну просто больше заняться не чем, кроме как перетасовывать местами определения функций. Бантики какие-то привязывать, вместо реальной работы.
У нас нет ни одного модульного теста. Мы собирает код в Arduino-образной IDE, У нас в репозитории одна единственная сборка-франкенштейн сразу для трех разных электронных плат, у нас нет UART-CLI, у нас в прошивке нет загрузчика, у нас в прошивке нет NVRAM. Зато мы заботимся о том, чтобы прядок объявления static функций совпадал с последовательностью определения static функций в конце *.с файла. Это как?
Как там говорят: «Один ноль в пользу Байдона»?
Однако правило такое тут существует, и чтобы фиксации с кодом прошили цензуру на Gerrit(е) и попали в общак, как ни крути, надо это требование исполнять.
В чем проблема?
Проблема ещё и в том, что тот интеллектуальный сотрудник, который нафантазировал все эти правила, поленился накропать утилиты для автоматического контроля всего своего этого законотворчества. Наверное в случае разработки утилит-надзирателей его пыл бы быстро подугас.
Однако правило обязательное и с этим, как ни крути, приходится жить.
Любая разработка начинается только тогда, когда появляются полноценные средства для отладки.
Очевидно же, что вручную выслеживать эти нарушения порядка объявлений и определений — это очень утомительно, рутинно долго и дорого. Поэтому программисты долгие годы лелеяли мечту на появление волшебной палочки: консольной программы, которая автоматически находит эти пресловутые нарушения порядка static объявлений.
И, как водится, такой утилиты в открытом доступе до сегодняшнего дня не существовало в природе, подобно тому как чистого плутония не было в земной коре до появления первых рукотворных атомных реакторов.
Поэтому я и написал такую утилиту-локатор. Назвал её prototype_check с ключом csp. Пришлю утилиту всем коллегам по несчастью.
Что надо из софтвера?
Вам надо будет установить CygWin чтобы извлечь из него вот эти утилиты:
# |
Название утилиты |
Для чего эта утилита |
1 |
awk / gawk |
анализатор и обработчик csv строчек |
2 |
winMerge |
Сравнение текстовых файлов |
3 |
sed |
удаление и замена текста в файле |
4* |
ctags |
извлекатор текстовых токенов из исходников в разных языках программирования |
5 |
cmp |
утилита сравнения файлов |
Каков план?
Я предлагаю реализовать вот такой программный конвейер. Всего 4 стадии. Фронт работ такой:
Реализация
Ядром всего этого решения является старинная утилита ctags. У ctags тут главная роль. Поясним, те опции утилиты ctags, которые нам понадобятся в этом решении здесь и сейчас.
Опция ctags |
Расшифровка |
-x —c-types=f |
извлечь из *.с файла только функции |
-x —c-kinds=p |
извлечь из *.с файла прототипы функций |
—sort=no |
не сортировать в отчёте найденные токены (функций) |
-fcTagReport.txt |
сохранить отчет в файл сTagReport.txt |
А теперь пояснения к алгоритму работы утилиты. Итак, танцуем от печки…
Фаза 1: Показать все функции в *.c файле
Чтобы показать только функции надо написать
ctags.exe --sort=no -fcTagReport.txt -x --c-types=f i2c_drv.c
Кристаллизовался вот такой сырой лог
Скрытый текст
I2C_Init function 783 C:\projects\source\I2C\i2c_drv.c STD_RESULT I2C_Init(void) I2C_DeInit function 896 C:\projects\source\I2C\i2c_drv.c STD_RESULT I2C_DeInit(void) I2C_StartReading function 968 C:\projects\source\I2C\i2c_drv.c STD_RESULT I2C_StartReading(const U8 nBus, I2C_HighLevel_RX_ISR function 1758 C:\projects\source\I2C\i2c_drv.c void I2C_HighLevel_RX_ISR(const U8 nBus) I2C_HighLevel_TX_ISR function 1775 C:\projects\source\I2C\i2c_drv.c void I2C_HighLevel_TX_ISR(const U8 nBus) I2C_HighLevel_Error_ISR function 1932 C:\projects\source\I2C\i2c_drv.c void I2C_HighLevel_Error_ISR(const U8 nBus) I2C_LL_GPIOInit function 2048 C:\projects\source\I2C\i2c_drv.c static void I2C_LL_GPIOInit(const U8 nArgBus) I2C_LL_GPIODeInit function 2144 C:\projects\source\I2C\i2c_drv.c static void I2C_LL_GPIODeInit(const U8 nArgBus) I2C_SetConfigTransfer function 2246 C:\projects\source\I2C\i2c_drv.c static STD_RESULT I2C_SetConfigTransfer(const I2C_BUS nArgBus, I2C_SendData function 2417 C:\projects\source\I2C\i2c_drv.c static STD_RESULT I2C_SendData(const I2C_BUS nArgBus, I2C_ReceiveData function 2470 C:\projects\source\I2C\i2c_drv.c static STD_RESULT I2C_ReceiveData(const I2C_BUS nArgBus, I2C_ResetCtrl2Register function 2527 C:\projects\source\I2C\i2c_drv.c static STD_RESULT I2C_ResetCtrl2Register(const I2C_BUS nArgBus) I2C_ClearFlags function 2593 C:\projects\source\I2C\i2c_drv.c static STD_RESULT I2C_ClearFlags(const I2C_BUS nArgBus, I2C_CheckFlag function 2661 C:\projects\source\I2C\i2c_drv.c static STD_RESULT I2C_CheckFlag(const U8 nBus, I2C_SetInterruptState function 2728 C:\projects\source\I2C\i2c_drv.c static STD_RESULT I2C_SetInterruptState(const U8 nArgBus, I2C_RefreshTxdtRegister function 2783 C:\projects\source\I2C\i2c_drv.c static void I2C_RefreshTxdtRegister(I2C_tag* const pArgHw) I2C_ConversionCfgIndex function 2810 C:\projects\source\I2C\i2c_drv.c static STD_RESULT I2C_ConversionCfgIndex(const U8 nArgBus,
Фаза 2: Удалить все не static строчки
В отчете ctags надо оставить только static функции. Или надо удалить все строки, которые не содержат ключевого слова static. Это можно сделать консольной утилитой sed
sed -i '/ static /!d' cTagReport.txt
Остались только определения static функций. Получается вот такой лог
Скрытый текст
I2C_LL_GPIOInit function 2048 C:\projects\source\third_party\I2C\i2c_drv.c static void I2C_LL_GPIOInit(const U8 nArgBus) I2C_LL_GPIODeInit function 2144 C:\projects\source\third_party\I2C\i2c_drv.c static void I2C_LL_GPIODeInit(const U8 nArgBus) I2C_SetConfigTransfer function 2246 C:\projects\source\third_party\I2C\i2c_drv.c static STD_RESULT I2C_SetConfigTransfer(const I2C_BUS nArgBus, I2C_SendData function 2417 C:\projects\source\third_party\I2C\i2c_drv.c static STD_RESULT I2C_SendData(const I2C_BUS nArgBus, I2C_ReceiveData function 2470 C:\projects\source\third_party\I2C\i2c_drv.c static STD_RESULT I2C_ReceiveData(const I2C_BUS nArgBus, I2C_ResetCtrl2Register function 2527 C:\projects\source\third_party\I2C\i2c_drv.c static STD_RESULT I2C_ResetCtrl2Register(const I2C_BUS nArgBus) I2C_ClearFlags function 2593 C:\projects\source\third_party\I2C\i2c_drv.c static STD_RESULT I2C_ClearFlags(const I2C_BUS nArgBus, I2C_CheckFlag function 2661 C:\projects\source\third_party\I2C\i2c_drv.c static STD_RESULT I2C_CheckFlag(const U8 nBus, I2C_SetInterruptState function 2728 C:\projects\source\third_party\I2C\i2c_drv.c static STD_RESULT I2C_SetInterruptState(const U8 nArgBus, I2C_RefreshTxdtRegister function 2783 C:\projects\source\third_party\I2C\i2c_drv.c static void I2C_RefreshTxdtRegister(I2C_tag* const pArgHw) I2C_ConversionCfgIndex function 2810 C:\projects\source\third_party\I2C\i2c_drv.c static STD_RESULT I2C_ConversionCfgIndex(const U8 nArgBus,
Фаза 3: Из отчёта надо выделить только первую колонку
Выделять колонки из текстовых файлов можно утилитой awk. Вот такой командой.
gawk '{print $1}' cTagReport.txt > definitions.txt
Фаза 4: Извлечь прототипы функций
Чтобы извлечь из src.c в cTagReport.txt только прототипы надо выполнить вот такую команду
ctags --sort=no -fcTagReport.txt -x --c-kinds=p src.c
Получится вот такой файл со списком прототипов.
Скрытый текст
I2C_LL_GPIOInit prototype 719 C:\projects\code_base_firmware\source\I2C\i2c_drv.c static void I2C_LL_GPIOInit(const U8 nArgBus); I2C_LL_GPIODeInit prototype 722 C:\projects\code_base_firmware\source\I2C\i2c_drv.c static void I2C_LL_GPIODeInit(const U8 nArgBus); I2C_SetConfigTransfer prototype 725 C:\projects\code_base_firmware\source\I2C\i2c_drv.c static STD_RESULT I2C_SetConfigTransfer(const I2C_BUS nArgBus, I2C_SendData prototype 730 C:\projects\code_base_firmware\source\I2C\i2c_drv.c static STD_RESULT I2C_SendData(const I2C_BUS nArgBus, I2C_ReceiveData prototype 734 C:\projects\code_base_firmware\source\I2C\i2c_drv.c static STD_RESULT I2C_ReceiveData(const I2C_BUS nArgBus, I2C_ResetCtrl2Register prototype 738 C:\projects\code_base_firmware\source\I2C\i2c_drv.c static STD_RESULT I2C_ResetCtrl2Register(const I2C_BUS nArgBus); I2C_ClearFlags prototype 741 C:\projects\code_base_firmware\source\I2C\i2c_drv.c static STD_RESULT I2C_ClearFlags(const I2C_BUS nArgBus, I2C_CheckFlag prototype 745 C:\projects\code_base_firmware\source\I2C\i2c_drv.c static STD_RESULT I2C_CheckFlag(const U8 nBus, I2C_SetInterruptState prototype 750 C:\projects\code_base_firmware\source\I2C\i2c_drv.c static STD_RESULT I2C_SetInterruptState(const U8 nArgBus, I2C_RefreshTxdtRegister prototype 755 C:\projects\code_base_firmware\source\I2C\i2c_drv.c static void I2C_RefreshTxdtRegister(I2C_tag* const pArgHw); I2C_ConversionCfgIndex prototype 758 C:\projects\code_base_firmware\source\I2C\i2c_drv.c static STD_RESULT I2C_ConversionCfgIndex(const U8 nArgBus,
Фаза 5: Из отчета прототипов выделить только имена функций
Теперь надо выделить только имена функций. Это по сути первый столбец. Такие задачи решаются культовой утилитой awk
gawk '{print $1}' cTagReport.txt > declarations.txt
Внутри текстового фaйла declarations.txt кристаллизовался список функций так, как он представлен в части, где происходит объявления прототипов static функций.
Фаза 6: сравнить списки функций
На этом этапе задача свелась к обыкновенному сравнению двух текстовых файлов. По идее они должны быть одинаковые, как две капли воды. Сравнивать текстовые файлы можно утилитой cmp
cmp -s declarations.txt definitions.txt
Результат сравнения и покажет аномалии.
Вот полный скрипт который делает проверку
cls echo off set srcFile=some_driver.c set cTagFile=cTagReport.txt set StativRepDef=StativRepDef.txt set StativRepDec=StativRepDec.txt rm -f %cTagFile% rm -f %StativRepDef% rm -f %StativRepDec% set options=--sort=no set options=%options% -f%cTagFile% set options=%options% -x --c-types=f ctags.exe %options% %srcFile% sed -i '/ static /!d' %cTagFile% gawk '{print $1}' %cTagFile% > %StativRepDef% rm -f %cTagFile% set options=--sort=no set options=%options% -f%cTagFile% set options=%options% -x --c-kinds=p ctags %options% %srcFile% gawk '{print $1}' %cTagFile% > %StativRepDec% cmp -s %StativRepDec% %StativRepDef% echo errorlevel=%errorlevel% if "%errorlevel%"=="0" (echo same) else (echo diff)
Так как скрипт нужен очень часто, то я инкапсулировал его в консольную утилиту prototype_check. Эта утилита некоторый аналог утилиты BusyBox. По ключу вызываются другие утилиты. Скрипт проверки static прототипов будет вызываться по ключу csp (check static prototype).
Отладка
Вот так выглядит лог утилиты в случае успеха
А вот такой лог будет в случае отрицательного успеха
Утилита prototype_check производит тщательный контроль создает два текстовых файла, чтобы увидеть недочёт. Показывает какие именно static функции обитают не на своём месте. Tool(а) в самом деле обнаружила рассинхрон в определениях. Это наглядно видно утилитой WinMerge.
Таким образом программист получает целеуказание на те static функции, которые надо так или иначе подвинуть. Теперь понятно что делать.
Итоги
В сухом остатке, мне удалось разработать уникальную утилиту-надзиратель, которая как лакмусовая бумажка автоматически показывает потери синхронизации между порядком объявления и порядком определения static функций в *.с файле на языке программирования Си.
Любо братцы жить
С нашей утилитой не приходится тужить
2x
Надеюсь, что утилита prototype_check или cmd скрипт помогут другим программистам микроконтроллеров тоже повысить эффективность своей работы в вопросе приведения кода к внутренним стандартам оформления.
Если нужна эта утилита обращайтесь.
Словарь
акроним |
Расшифровка |
CSV |
Comma-separated values |
csp |
Check Static Prototyes |
Ссылки
ссылка на оригинал статьи https://habr.com/ru/articles/846020/
Добавить комментарий