Есть такая утилита для автоматического выравнивания отступов в исходных кодах. Называется clang-format.exe. Надо признать, что сейчас де факто основной утилитой автоматического выравнивания отступов является именно утилита clang-format.exe из LLVM. Её достоинство в том что ей всё равно в каком текстовом редакторе вы пишите код. Что в Eclipse, что Notepad++, что MS Visual Code. Сlang-format он выровняет всё, что ему подадут на вход в соответствии с указанным конфигом в опциях командной строки.

Есть еще тоже консольный GNU indent, однако indent очень устарел и слаб. Плюс падает от обнаружения препроцессора.

Цель данного текста- показать, как интегрировать clang-format в процесс сборки прошивки.

Почему обычно делают форматирование отступов в исходниках?

На то я вижу минимум три причины:

1--Для однообразности и красоты. В каждой российской компании свой собственный, внутренний, ни на кого больше не похожий стандарт оформления исходных текстов программ на Си. Причем отличается обычно на 80%..90% от других организаций.

2--Чтобы был минимальный diff при сравнении разных по времени версий одного и того же куска Си кода

3--Чтобы можно было составлять простые и предсказуемые регулярные выражения для поиска шаблонов кусков кода утилитой grep и find в репозитории с кодом.

В чем проблема?

Проблем тут две:

1--Первая проблема в том, что вручную выставлять отступы это очень утомительно и рутинно. Однако эта проблема решается, как раз, утилитами автоматического выставления отступов.

2--Вторая проблема в том, что для автоматического форматирования кода утилитами приходится составлять *.bat файл и явно прописывать внутри какой файл надо форматировать. Это тоже очень рутинно с учетом, что файлов в сборке порядка нескольких сотен. Вот у меня типичная сборка собирает 237 с-файликов. И что, мне прописывать 237 строчки в bat файле что ли?

Очевидно же, что надо как-то автоматизировать процесс прогона с-файлов через утилиту форматировщик clang-format.

Решение

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

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

#

Название утилиты

Назначение

1

grep

рекурсивный поиск по файловой системе

2

sed

утилита авто замены ключевых слов в файлах

3

make

утилита управления системой сборки

4

clang-format

утилита автоматического выравнивания исходных кодов

Можно написать отдельный make файл для вызова этой утилиты. Вот по такой схеме.

Вот такой Make скрипт сам производит выравнивание тех же самых файлов, что участвовали в сборке прошивки.

$(info ClangFormatScript)

CLANG_FORMAT_TOOL =C:/cygwin64/bin/clang-format.exe
SOURCES_CF := $(subst .c,.cf, $(SOURCES_C))

#$(error SOURCES_CF=$(SOURCES_CF))

MCAL_STYLE="{
MCAL_STYLE+= BreakBeforeBraces: Attach,
MCAL_STYLE+= ColumnLimit: 120,
MCAL_STYLE+= IndentWidth: 4,
MCAL_STYLE+= PointerAlignment: Left,
MCAL_STYLE+= SortUsingDeclarations: true,
MCAL_STYLE+= SpaceBeforeParens: Never,
MCAL_STYLE+= SortIncludes: true,
MCAL_STYLE+= TabWidth: 4,
MCAL_STYLE+= UseTab: Never,
MCAL_STYLE+=}"
#$(error MCAL_STYLE=$(MCAL_STYLE))

%.cf: %.c
	$(info RunClangFormat)
	$(CLANG_FORMAT_TOOL) -verbose -i -style=$(MCAL_STYLE) $<

.PHONY: clang_format
clang_format: $(SOURCES_CF)
	$(info ClangFormatDone)

Однако есть один момент. Не все файлы исходников следует подвергать автоматическому выравниванию. Дело в том, что если ты меняешь форматирование в файле, то ты, как бы, автоматически становишься владельцем этого кода. Оно тебе надо? Ты же не хочешь нести ответственность за этот странный чужой код просто потому, что ты там поменял TAB на 4 пробела? Поэтому существует такое негласное правило буравчика:

Ни в коем случае нельзя менять форматирование в чужом коде!

Очевидно, что в скриптах сборки надо как-то пометить те файлы сорцов, которые мы не будем форматировать. А в системе сборки make сделать это очень просто. Надо всего-навсего проиндексировать чужие сорцы в переменную окружения SOURCES_THIRD_PARTY_C.

ifneq ($(FAT_FS_MK_INC),Y)
    FAT_FS_MK_INC=Y

    FAT_FS_DIR += $(THIRD_PARTY_DIR)/fat_fs
    #@echo $(error FAT_FS_DIR= $(FAT_FS_DIR))
    INCDIR += -I$(FAT_FS_DIR)
    INCDIR += -I$(FAT_FS_DIR)/src
    INCDIR += -I$(FAT_FS_DIR)/src/options

    SOURCES_THIRD_PARTY_C += $(FAT_FS_DIR)/src/diskio.c
    #SOURCES_THIRD_PARTY_C += $(FAT_FS_DIR)/src/option/unicode.c
    SOURCES_THIRD_PARTY_C += $(FAT_FS_DIR)/src/option/ccsbcs.c
    SOURCES_THIRD_PARTY_C += $(FAT_FS_DIR)/src/ff.c
endif

Если лень это производить вручную, то можно применить этот bash скрипт

grep -rl SOURCES_C . | xargs sed -i 's/SOURCES_C/SOURCES_THIRD_PARTY_C/g'

Далее в rules.mk просуммировать те сорцы, которые мы форматируем SOURCES_C и те, которые мы не форматируем SOURCES_THIRD_PARTY_C в одну переменную окружения SOURCES_TOTAL_C


SOURCES_TOTAL_C += $(SOURCES_C)
SOURCES_TOTAL_C += $(SOURCES_THIRD_PARTY_C)
SOURCES_TOTAL_C := $(subst /cygdrive/c/,C:/, $(SOURCES_TOTAL_C))

Вот так получается, что THIRD PARTY код останется без изменений. Всё что надо - это открыть папку с проектом из консоли и набрать

make clang_format

Все нужные с-файлы станут с выровненными отступами. Автоматически...

Итоги

Как видите, сборка из скриптов (в частности make) дает такие преимущества как автоматическое выравнивание отступов исходного кода.

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

А с Make удалось с полпинка вмонтировать утилиту clang-format в основной конвейер сборки прошивки. Успех!

Словарь

акроним

расшифровка

LLVM

Low Level Virtual Machine

bash

Bourne again shell

GNU

GNU’s Not UNIX

sed

Stream EDitor

grep

search Globally for lines matching the Regular Expression, and Print them

Ссылки

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


  1. randomsimplenumber
    03.08.2024 05:06
    +12

    Смысл действия непонятен. Компилятору, который собирает бинарник, все равно как отформатирован исходник. Человек, которому не всё равно, читает исходники до сборки, а не после.

    Я слышал, форматирование в IDE завезли.


    1. aabzel Автор
      03.08.2024 05:06

      Дело в том, что определённое форматирование требуют в компании. Якобы чтобы всё было однообразно и предсказуемо.
      Я работал в нескольких компаниях и в 3 из 5 был свой стандарт отступов в коде.


      1. Flammmable
        03.08.2024 05:06

        Я работал в нескольких компаниях и в 3 из 5 был...

        Вы за 12 лет работы сменили не менее 5 компаний?


        1. aabzel Автор
          03.08.2024 05:06

          Задач по программировать микроконтроллеры в России очень мало.

          Даже если Вы найдёте работу, то Вы сделаете всё за год, даже если будете писать всего лишь по 150 строчек кода в день. У вас буквально Flash память закончится за год. Прошивки пишутся быстро, особенно когда есть заготовки программных компонентов: MCAL, драйверы ASIC чипов, connectivity, sensitivity, computing, storage и control.

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

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

          В этой профессии все пути тупиковые:
          1—Если Вы будете бестолковым программистом, то Вас будут изживать из организации как безнадежного растяпу.

          2—Если Вы будете очень толковым сотрудником, то Вас тоже будут изживать из организации ещё более интенсивнее, только уже как явного конкурента.

          Программисты микроконтроллеров в России коммерческому рынку нужны, но только как сезонные работники на одну прошивку (полгода-год), подобно тому как в Финляндии сезонно нанимают сборщиков клубники. А потом тебе снова метаться по собеседованиям, искать фриланс, out source.

          Многие будут склонять тебя организовать ИП или работать удалённо как самозанятого. Это называется "предприниматель в спальне".

          Поэтому да, в среднем программисты-микроконтроллеров в России за 10 лет меняют работу от 7 до 15 раз...


  1. SnakeSolid
    03.08.2024 05:06

    Я придерживаюсь следующей стратегии:

    • Озвучить всем стиль написания кода, включая отступы, расположение скобок и форматирование аргументов. В идеале, предоставить правила форматирования для популярных IDE.

    • Добавить прекоммит-хук, который будет запрещать заливать или мержить неправильно отформатированный код. В качестве альтернативы можно автоматически добавлять комментарии о неправильном форматировании в ревью.

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


    1. Hamletghost
      03.08.2024 05:06
      +2

      Добавлю, что правила можно положить сразу в репу, современные ide отлично его применяют перед комитом сами, в прекомите можно оставить только проверку. Ну и в ci должна быть lint стадия, где форматирование тоже должно провериться (если залили без хуков)


      1. NN1
        03.08.2024 05:06
        +2

        Дополню ответ утилитой pre-commit или ручной pre commit hook чтобы не забыть отформатировать перед комитом в случае правки в другом редакторе.


        1. aabzel Автор
          03.08.2024 05:06

          Проблема в том, что не все используют git c его hook(ами).
          У других Perforсe, Arcadia, SVN, Mercurial, или вовсе под в ZIP архиве (привет военное НИИ).

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


          1. NN1
            03.08.2024 05:06
            +2

            У некоторых перечисленных уже есть precommit hook.

            А ещё можно использовать git обёртку во многих случаях.

            Zip архив ведь как-то создавался с системой контроля версий. Вот там и добавить.

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


  1. Caefah
    03.08.2024 05:06
    +1

    clang-format очень тонко кастомизируемый инструмент под множество стандартов или личных предпочтений пользователя. В статье полно абсолютно ненужной «воды», но при этом отсутствует главное — то самое заявленное в заголовке и поэтому ожидаемое описание как работать с clang-format, используя всю его мощь и множество настроек.


    1. aabzel Автор
      03.08.2024 05:06

      Не согласен. Сlang-format не настолько гибкий как надо.
      Сlang-format, например, не может принудительно выравнивать аргументы функций в столбик.


      1. Caefah
        03.08.2024 05:06

        Я нигде и не заявлял что clang-format «гибкий как надо», идеален или абсолютно универсален. Поэтому не совсем понятно с чем именно Вы не согласны.

        Это просто инструмент. Кому-то он подходит, кому-то — нет. Лично мне — нет. Я хочу вот так, а он этого не умеет:

        void add_string_to_array(
          char ***array,
          const char *another_array
        ){
          int size = 0;
        
          if(array != NULL)
          {
            while(array[size] != NULL)
            {
              size++;
            }
          }
        }
        


        1. aabzel Автор
          03.08.2024 05:06

          А зачем выдумывать свое форматирование, если в Clang format нет для него опций?
          Это же лишняя морока потом вручную всё расставлять.


        1. aabzel Автор
          03.08.2024 05:06

          Лично мне — нет. Я хочу вот так, а он этого не умеет:

          И чем Вы тогда пользуетесь?


          1. Caefah
            03.08.2024 05:06
            +1

            Пока ничем. В IDE, только, использую удаление лишних табуляторов, пробелов и пустых строк. Ищу консольный инструмент, который бы мне подходил и его можно было бы использовать в Makefile. Но пока все ранее перепробованные не удовлетворяют требованиям. Где-то код отформатируют как надо, а в других, самых неожиданных местах, только всё испортят.


        1. aabzel Автор
          03.08.2024 05:06

          Вам не жалко вертикального пространства монитора, из за переноса фигурных скобок на новую строку?


          1. Caefah
            03.08.2024 05:06

            Для меня важно то, что строка с
            while(array[si...

            визуально не прилипает к вышестоящей строке с if

            if(arr...

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


            Вам не жалко вертикального пространства монитора

            Мне не жалко. У меня для кода отдельный, вертикально ориентированный монитор. Очень удобно!


            И очевидно, что пользователям моего ПО будет вообще безразлично оформление. Никого из них не заботит изящность. Зато волнуют ошибки, которые могут появиться по вине разработчика из-за плохой читабельности кода. Поэтому если есть выбор, то код я оформлю так, чтобы для меня было удобно кодить, благодаря чему мне и только мне будет комфортно ориентироваться в написанном.


    1. aabzel Автор
      03.08.2024 05:06

      Цель данного текста не научить пользоваться опциями clang-format , а показать, как интегрировать clang-format в процесс сборки прошивки.