В некоторых компаниях есть внутренние требования к стилю оформления исходных кодов на Си.

Вот буквально некоторые пункты из типичного внутреннего code-style:

  1. Объявления функций должны в точности соответствовать их определениям

  2. Базовые типы данных не должны использоваться в исходном коде, вместо них – переопределения

  3. Все объявления и определения идентификаторов должны быть прокомментированы

  4. В конце оператора выбора switch должен быть default case

  5. Все локальные переменные функций должны быть проинициализированы до первого использования

  6. Все параметры функций должны быть перечислены 'в столбик'

  7. В исходном коде не должно быть 'магических чисел'

  8. В исходном тексте не должно быть символов табуляции

  9. В сложных выражениях везде должны стоять скобки

  10. Для всех объявлений глобальных функций и переменных в h-файле обязательно ключевое слово 'extern'

  11. Ширина строки должна быть не более 80 символов

  12. Все параметры макросов должны быть заключены в скобки

  13. Функции без параметров должны содержать в скобках void

  14. Тело конструкций if, else if, switch, for, while, do..while должно быть заключено в фигурные скобки

  15. В одной строке должно быть не более одного определения

  16. Исходный текст не должен содержать закомментированный код

  17. Ко всем параметрам функций должен быть по возможности применен спецификатор 'const'

  18. В исходном коде не должно быть 'магических чисел': все символьные константы должны иметь осмысленные имена

  19. и тому подобное вплоть до номера 463

У нас в организации есть даже python скрипт для автоматического выявления нарушений первых 18 правил оформления исходников. Не знаю кто его написал, но скрипт реально работает и находит некоторые нарушения. Процентов 10 от общего количества правил. Хоть что-то.

Постановка задачи

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

Что надо из софта?

Утилита

назначение

1

autoverifier.py

утилита для проверки внутреннего стандарта оформления программ

2

make

Утилита управления программным конвейером

3

cat

печать файлов в консоль

5

sed

консольная авто замена внутри текстовых файлов

6

python

интерпретатор языка phython

7

cmd

интерпретатор консоли в Windows

Реализация

У меня есть Python скрипт autoverifier.py. Если запустить его на исполнение передав абсолютный путь к файлу с исходником, то в stdout вывалится лог о том, что скрипт упал

Однако в директории, из которой был вызван этот скрипт появится текстовый файл common_verifier_results.txt с отчетом о нарушениях и соответствиях code style.

========C:\xxx\xxx\xxx\xxxx\xxxx\xxxx\xxx\xxx\i2s_drv.c========
  |      +  В исходном тексте не должно быть символов табуляции (СООТВЕТСТВУЕТ)
  |      +  Ширина строки должна быть не более 80 символов (СООТВЕТСТВУЕТ)
  |      +  Базовые типы данных не должны использоваться в исходном коде, вместо них – переопределения (СООТВЕТСТВУЕТ)
  |      +  Функции без параметров должны содержать в скобках void (СООТВЕТСТВУЕТ)
  |      +  Все параметры функций должны быть перечислены 'в столбик' (СООТВЕТСТВУЕТ)
  |      +  В конце оператора выбора switch должен быть default case (СООТВЕТСТВУЕТ)
  |      +  Тело конструкций if, else if, switch, for, while, do..while должно быть заключено в фигурные скобки (СООТВЕТСТВУЕТ)
  |      +  В одной строке должно быть не более одного определения (СООТВЕТСТВУЕТ)
  |      +  Исходный текст не должен содержать закомментированный код (СООТВЕТСТВУЕТ)
  |     --- В исходном коде не должно быть 'магических чисел': все символьные константы должны иметь осмысленные имена (НЕ СООТВЕТСТВУЕТ) (НЕ СООТВЕТСТВУЕТ)
  |       ->    В строке 753 обнаружено 'магическое число'
  |       ->    В строке 825 обнаружено 'магическое число'
  |       ->    В строке 915 обнаружено 'магическое число'
  |       ->    В строке 1435 обнаружено 'магическое число'
  |       ->    В строке 1494 обнаружено 'магическое число'
  |     --- Ко всем параметрам функций должен быть по возможности применен спецификатор 'const' (НЕ СООТВЕТСТВУЕТ)
  |       ->    В строке 1001 обнаружены аргументы ['Node'], к которым можно применить спецификатор 'const'
  |       ->    В строке 1053 обнаружены аргументы ['clockBus'], к которым можно применить спецификатор 'const'
  |      +  Все параметры макросов должны быть заключены в скобки (СООТВЕТСТВУЕТ)
  |     [WW] Для данного исходного файла не был найден соответсвующий ему заголовочный файл
  |     [WW] Для данного исходного файла не был найден соответсвующий ему заголовочный файл
  |      +  Объявления функций должны в точности соответствовать их определениям (СООТВЕТСТВУЕТ)
  |     --- Все объявления и определения идентификаторов должны быть прокомментированы (НЕ СООТВЕТСТВУЕТ)
  |       ->    В строке 187 идентификатор не прокомментирован
  |       ->    В строке 1401 идентификатор не прокомментирован
  |       ->    В строке 1461 идентификатор не прокомментирован
  |     --- В сложных выражениях везде должны стоять скобки (НЕ СООТВЕТСТВУЕТ)
  |       ->    В строке 708 обнаружено сложное выражение без достаточного количества скобок
  |       ->    В строке 753 обнаружено сложное выражение без достаточного количества скобок
  |      +  Все локальные переменные функций должны быть проинициализированы до первого использования (СООТВЕТСТВУЕТ)

Как видно со знаком + скрипт пишет что хорошо и со знаком - пишет, что плохо. Если вызвать этот скрипт еще раз только уже для другого файла, то файл common_verifier_results.txt перетрётся и информация будет потеряна.

Напомню, что мне нужен полный отчет для множества файлов. В связи с этим, надо после каждого запуска autoverifier.py сбрасывать лог по файлу в общий текстовый файл.

При этом, меня не интересует, что хорошо. Меня интересует по каким пунктам code-style надо что-то исправлять. Поэтому надо также автоматически поудалять из локального отчета те строчки, которые содержат символ плюс.

Суммируя вышесказанное, надо как-то собрать вот такой программный конвейер.

Самое простое решение это воспользоваться системой сборки GNU Make. Вот так это выглядит в коде на языке программирования make.

$(info CodeStyleEhalScript)

include $(THIRD_PARTY_DIR)/code_style_files_to_check.mk

TOOLS_DIR = $(WORKSPACE_LOC)../tool
TOOLS_DIR := $(subst /cygdrive/c/,C:/, $(TOOLS_DIR))

AUTO_VERIFIER_DIR=$(TOOLS_DIR)/AutoVerifier/Software
AUTO_VERIFIER=$(AUTO_VERIFIER_DIR)/autoverifier.py


CODE_STYLE_TOOL = python $(AUTO_VERIFIER)
SOURCES_CODE_STYLE_CSC := $(subst .c,.ccs, $(SOURCES_CODE_STYLE_C))
SOURCES_CODE_STYLE_CSH := $(subst .h,.csh, $(SOURCES_CODE_STYLE_H))


#@echo $(error SOURCES_CODE_STYLE__CS= $(SOURCES_CODE_STYLE__CS))

CODE_STYLE_TOTAL_REPORT = $(BUILD_DIR)/$(TARGET)_code_style_report.txt

CODE_STYLE_ONE_REPORT=$(MK_PATH)common_verifier_results.txt

clean_report: $(SOURCES_CODE_STYLE_H)
	$(info RunCleanCodeStyleReport) 
	"" > $(CODE_STYLE_TOTAL_REPORT)


%.csh: %.h
	$(info RunCodeStyleCheck for H file) 
	rm -f $(CODE_STYLE_ONE_REPORT)
	$(CODE_STYLE_TOOL) $<
	sed -i '/ + /d' $(CODE_STYLE_ONE_REPORT)
	cat $(CODE_STYLE_ONE_REPORT) >> $(CODE_STYLE_TOTAL_REPORT)

%.ccs: %.c
	$(info RunCodeStyleCheck for C file) 
	rm -f $(CODE_STYLE_ONE_REPORT)
	$(CODE_STYLE_TOOL) $<
	sed -i '/ + /d' $(CODE_STYLE_ONE_REPORT)
	cat $(CODE_STYLE_ONE_REPORT) >> $(CODE_STYLE_TOTAL_REPORT)

.PHONY: code_style
code_style: $(SOURCES_CODE_STYLE_CSC) $(SOURCES_CODE_STYLE_CSH) 
	$(info CodeStyleCheckDone)

Буквально несколько комментариев по коду...

Файлы, которые надо анализировать, можно явно указать в отдельном make скрипте code_style_files_to_check.mk

$(info AddFilesToCheckCodeStyle)

SOURCES_CODE_STYLE_C += $(THIRD_PARTY_DIR)/asics/nau8814/nau8814_driver.c
SOURCES_CODE_STYLE_H += $(THIRD_PARTY_DIR)/asics/nau8814/nau8814_driver.h
   ......
SOURCES_CODE_STYLE_C += $(THIRD_PARTY_DIR)/computing/dds/dds.c
SOURCES_CODE_STYLE_H += $(THIRD_PARTY_DIR)/computing/dds/dds.h
 

SOURCES_CODE_STYLE_H := $(subst /cygdrive/c/,C:/, $(SOURCES_CODE_STYLE_H))
SOURCES_CODE_STYLE_C := $(subst /cygdrive/c/,C:/, $(SOURCES_CODE_STYLE_C))

чтобы удалить из файла все строчки с символом + надо исполнить это

	sed -i '/ + /d' file.txt

Я создаю мнимые расширения *.csh *.cсs только для того чтобы make догадался пробежаться по всем *.h *.c файлам. На самом деле *.csh *.cсs - это не файлы, а просто текст в переменной окружения.

Когда скрипт готов, то достаточно просто открыть в консоли cmd папку с проектом прошивки и выполнить команду

make -i code_style

После чего в папке с артефактами trunk\source\projects\config_name_m\build появится файл config_name_code_style_report.txt с полным отчетом по нарушениям внутреннего стандарта оформления исходных кодов для тех файлов, которые вы перечислили в code_style_files_to_check.mk.

Как видите, система сборки Make в очередной раз удружила. Оказывается, что через make скрипты можно не только собирать *.bin(ари), но и ещё можно добавить в сборку автоматическое генерирование отчета о соответствии вашего внутреннего code style.

Кто бы мог подумать...

Итоги

Удалось встроить скрипт автоматического выявления нарушений внутреннего code-style прямо в основной скрипт сборки прошивки.

Возможность подмешивать к сборке прошивки всяческие вспомогательные действия является отличной причиной собирать код не из-под GUI-IDE, а самостоятельно написанными скриптами сборки.

Ссылки

Комментарии (32)


  1. devprodest
    17.09.2024 15:09
    +1

    Базовые типы данных не должны использоваться в исходном коде, вместо них – переопределения

    К слову, typedef не переопределяет тип и не создает тип, а лишь определяет псевдоним.


  1. devprodest
    17.09.2024 15:09
    +1

    Функции без параметров должны содержать в скобках void

    Зависит от стандарта языка, который вы используете. Для си >= с2х это не нужно


    1. aabzel Автор
      17.09.2024 15:09
      +1

      У нас используется С99.


      1. devprodest
        17.09.2024 15:09
        +1

        Принимайте мои соболезнования


  1. devprodest
    17.09.2024 15:09
    +2

    Кодстайл и статический анализ это две разные задачи и не должны смешиваться


    1. aabzel Автор
      17.09.2024 15:09

      Как по-вашему тогда следует называть утилиту, которая следит за нарушениями code-style?


      1. devprodest
        17.09.2024 15:09
        +1

        Для этого можно использовать clang-format, он может генерировать отчет о несоответствии шаблону. И им же можно форматировать.


        1. aabzel Автор
          17.09.2024 15:09

          clang-format не может выставить аргументы функции в стоблик.


          1. devprodest
            17.09.2024 15:09
            +2

            Умеет:

            https://clang.llvm.org/docs/ClangFormatStyleOptions.html#allowallparametersofdeclarationonnextline

            В чем необходимость компоновки аргументов функций именно таким образом?


            1. aabzel Автор
              17.09.2024 15:09

              В чем необходимость компоновки аргументов функций именно таким образом?

              Хороший вопрос. Я не знаю. У нас в организации просто такое обязательное внутреннее требование.

              "Все параметры функций должны быть перечислены 'в столбик' "


      1. Andy_Big
        17.09.2024 15:09
        +1

        Стилистический анализатор? Но точно не статический.


      1. redfox0
        17.09.2024 15:09

        Линтер кода. Не путать с линковщиком (компоновщиком).


  1. randomsimplenumber
    17.09.2024 15:09
    +1

    Не знаю кто его написал

    git log смотрели? ;)

    скрипт реально работает и находит некоторые нарушения. Процентов 10 от общего количества правил

    Правило 10/90 но наоборот?


    1. aabzel Автор
      17.09.2024 15:09

      Я zip архив с кодом нашел на расшаренном диске компании .


  1. 9241304
    17.09.2024 15:09
    +3

    Ох уж эти корпоративные костыли...


    1. aabzel Автор
      17.09.2024 15:09

      А что есть отдельные утилиты для проверки code style?


      1. 9241304
        17.09.2024 15:09
        +2

        Не знаю. Я корпоративными костылями не увлекаюсь. Мой интерес в том, чтобы люди писали читаемый понятный код, поэтому требований к оформлению минимум. У кого другие интересы, пусть ищут утилиты. Хотя не понятно, зачем писать на сях. Возьмите питон или голанг)


        1. aabzel Автор
          17.09.2024 15:09

          Мой интерес в том, чтобы люди писали читаемый понятный код, поэтому требований к оформлению минимум.

          Так и надо.


  1. ya_ne_znau
    17.09.2024 15:09
    +1

    При наличии у вас исходников скрипта-надзорщика можно и сам скрипт потыкать: 1) чтоб не вылетал, 2) чтоб писал куда нужно системе сборки, 3) чтоб писал только нужное.

    За некоторые правила из набора соболезную.


    1. aabzel Автор
      17.09.2024 15:09

      За некоторые правила из набора соболезную.

      За какие именно?


      1. ya_ne_znau
        17.09.2024 15:09

        Прошёлся ещё раз по списку и не заметил того, что показалось мне странным при первом прочтении.

        Могу разве что выделить требования к стилю кода (как и написано, code style) по типу "без символов табуляции", "длина ≤80 символов", "параметры функций в столбик". Такие решаются форматировщиком.

        И ещё одно — про закомментированный код, но, наверное, это уже у меня какие-то проблемы с оставлением ненужного функционала.


        1. aabzel Автор
          17.09.2024 15:09

          , "параметры функций в столбик". Такие решаются форматировщиком

          Вы в курсе ,что clang format не может принудительно выставить параметры в столбик?


          1. ya_ne_znau
            17.09.2024 15:09

            1. я не упоминал именно clang-format.

            2. clang-format, насколько я понял по документации, на такое способен (https://clang.llvm.org/docs/ClangFormatStyleOptions.html#binpackparameters).


            1. aabzel Автор
              17.09.2024 15:09

              clang-format, насколько я понял по документации, на такое способен

              А по факту он разбивает только если длинна строки не позволяет, а не всегда.
              Вот про это даже есть задекларированный баг

              https://github.com/llvm/llvm-project/issues/54220


            1. aabzel Автор
              17.09.2024 15:09

              1. я не упоминал именно clang-format.

              Тогда какие именно выравниватели тогда вы подразумевали?


              1. ya_ne_znau
                17.09.2024 15:09
                +1

                Сам лично использую artistic style, но мир форматировщиков кода на C/C++ не ограничивается ни clang-format'ом, ни им.

                Насколько знаю, форматировщики также есть в CLion, Visual Studio. Но это ide'шные, они внутри.

                Возможно, есть и другие (гнушный какой-нибудь), но я ими не пользовался, а потому не знаю ни про их существование, ни их возможностей.


                1. aabzel Автор
                  17.09.2024 15:09

                  Сам лично использую artistic style, но мир форматировщиков кода на C/C++ не ограничивается ни clang-format'ом, ни им.

                  Ок, и какой же ключ утилиты Artistic Style принудительно выравнивает аргeменты С-функции в столбик?


                  1. ya_ne_znau
                    17.09.2024 15:09
                    +1

                    Чтож, я не смог найти такого ключа и теперь не уверен в поддержке такого функционала в используемых мной средствах.

                    В связи с этим могу предложить несколько альтернатив:

                    1. Использовать наработки в существующем скрипте (обнаружение аргументов функций не в столбик) и приспособление вашего кода к форматированию. (Сделайте свой собственный форматировщик).

                    2. Поиск тех решений, которые обладают функционалом разбиения параметров в столбик, но таких, насколько понимаю, в широком доступе нет, либо способ достижения нужного эффекта слишком сложен.

                    3. Правка чужих открытых для модификации решений либо использование уже правленых решений (добавить функционал в тот же clang-format или найти его ветку/форк с работающей опцией).

                    4. (Самый неподходящий) Уточнение/пересмотр требований. В комментариях не нуждается.


            1. aabzel Автор
              17.09.2024 15:09

              clang-format, насколько я понял по документации, на такое способен

              Опция

              BinPackParametersStyle: AlwaysOnePerLine , вообще не работает . Вот доказательство:


              1. ya_ne_znau
                17.09.2024 15:09
                +1

                Чтож, попытался сам, получилось через раз.

                Опция называется "BinPackParameters", но поддерживает только true/false. При этом clang-format сам в процессе выбирает, оставить всё на одной строке или разбить. Разбивает только если они все не вмещаются на одну линию, наверное.

                Собственно, поэтому я больше не использую clang-format — нечеткий форматтер мне не подошёл, а пытаться найти комбинации из 3-5 опций чтобы добиться нужного эффекта или узнать, что такого ещё не сделали, не хочется.


                1. aabzel Автор
                  17.09.2024 15:09

                  Собственно, поэтому я больше не использую clang-format — нечеткий форматтер 

                  GNU indent ещё хуже.


  1. redfox0
    17.09.2024 15:09

    Имхо, проще питоновский скрипт переписать, чем сверху фильтрацию городить.

    Сам писал линтер на pl/sql для pl/sql. Умеет находить неиспользуемые переменные, нарушения кодестайла и возможные мини-оптимизации кода. Можно заглушить предупреждение в коде комментарием noqa. Пример выхлопа:

    XXX_PACK:531 тип varchar вместо varchar2
    XXX_PACK:532 не указан char
    XXX_PACK:630 неиспользуемая аргумент P_ID_IP
    XXX_PACK:649 неиспользуемая переменная ID_IP
    XXX_PACK:649 имя переменной ID_IP не начинается с префикса l_
    XXX_PACK:1494 имя аргумента L_DIS_UP не начинается с префикса p_
    XXX_PACK:1530 имя курсора ANK_LIST не равно l_c или не начинается с префикса c_
    XXX_PACK:1643 вызов тяжёлой pl/sql функции v() внутри sql
    XXX_PACK:1860 вызов тяжёлой pl/sql функции v()
    XXX_PACK:6591 вызов pl/sql функции nvl() вместо sql функции coalesce()