Почему важно собирать С-код из MakeFile(ов)

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

В период с 199x по 201x развелось очень много программистов-микроконтроллеров, которые никогда не вылазили из всяческих IDE (IAR, KEIL, Code Composer Studio, AtilocTrueStudio). Как по мне, дак это очень печально. Во многом потому, что специалист в Keil не сможет быстро понять как работать в IAR и наоборот. Миграция на другую IDE тоже вызывает большую трудность, так как это сводится к мышковозне в GUI. Каждая версия IAR не совместима с более новой версией IDE.

Дело в том, что GUI IDE появились в 199x...201x, когда не было расцвета DevOps(а), программист работал один и все действия выполнялись вручную мышкой. В то время работа в GUI казалась программистам-микроконтроллеров веселее, ведь в IDE много стразиков.

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

код отдельно, конфиги отдельно

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

Какие недостатки сборки исходников из-под IDE?

1--IDE отжирают много ресурсов компьютера, как RAM как и CPU, IDE же надо много оперативки, чтобы отрисовывать окошки со стразиками.

2--IDE монолитные и неделимые. Если вы захотите поменять препроцессор, компилятор или компоновщик, а остальные фазы ToolChain(а) оставить как есть, то ничего из этого не выйдет, так как капот IDE закрыт на замок.

3--IDE стоят дорого, порядка 3500 EUR на один компьютер

4--IDEшные xml очень слабо документированы или не документированы вовсе. У всех вендоров xml имеет свой особенный язык разметки. При внесении незначительных изменений появляется огромный git diff.

5--Затруднена сборка из консоли. В основном инициировать сборку в IDE можно мышкой или горячими клавишами.

6--Обратная несовместимость с новыми версиями IDE

7--В условиях технологического эмбарго законно купить IDE европейского вендора невозможно.

В общем распространение IDE это яркий пример известного ныне "технологического диктата" запада для стран второго и третьего мира.

Мы вам даём песочницу (IDE), а вы сидите там за бортиками и лепите свои куличики (прошивки).

Понятное дело, что в таких рамках на "сделать что-то серьезное" рассчитывать не приходится.

Пришлось думать. Хорошим решением оказалось сделать шаг назад в 197x 198x когда на компьютерах всё делали из консоли. Собирать сорцы из скриптов. Можно вообще bat файл написать и он в общем-то инициирует запуск нужных утилит, однако исторически С-код собирали утилитой make.

В чем достоинства сборки С-кода из make файлов?

1-- Makefile это самый гибкий способ управлять модульностью. Можно буквально одной строчкой добавлять или исключать один конкретный программный компонент (десятки файлов) из десятков сборок. В случае же сборки из-под IDE вам бы пришлось вручную редактировать .xml для каждой сборки.

2--Сборку из Makefile очень легко автоматизировать. Достаточно в консоли выполнить make all и у вас инициируется процесс сборка.

3--После сборки из скриптов вы получите полный лог сборки, в то время как IDE обычно показывают последние 3-4 экрана.

4--В MakeFile очень просто менять компилятор. Это буквально заменить одну строчку. С GCC на Clang. Вот типичный основной makefile для любой сборки на ARM Cortex-M

mkfile_path := $(abspath $(lastword $(MAKEFILE_LIST)))
$(info Build  $(mkfile_path) )
BUILD_DIR = build

#@echo $(error SOURCES_C= $(SOURCES_C))
INCDIR := $(subst /cygdrive/c/,C:/, $(INCDIR))
#@echo $(error INCDIR=$(INCDIR))
SOURCES_C := $(subst /cygdrive/c/,C:/, $(SOURCES_C))
#@echo $(error SOURCES_C=$(SOURCES_C))
SOURCES_ASM := $(subst /cygdrive/c/,C:/, $(SOURCES_ASM))
LIBS  := $(subst /cygdrive/c/,C:/, $(LIBS))
LDSCRIPT := $(subst /cygdrive/c/,C:/, $(LDSCRIPT))
#@echo $(error SOURCES_ASM=$(SOURCES_ASM))

# binaries
PREFIX = arm-none-eabi-
GCC_PATH="C:/Program Files (x86)/GNU Arm Embedded Toolchain/10 2021.10/bin"
$(info GCC_PATH=$(GCC_PATH))

# The gcc compiler bin path can be either defined in make command via GCC_PATH variable (> make GCC_PATH=xxx)
# either it can be added to the PATH environment variable.
ifdef GCC_PATH
  CC = $(GCC_PATH)/$(PREFIX)gcc
  AS = $(GCC_PATH)/$(PREFIX)gcc -x assembler-with-cpp
  CP = $(GCC_PATH)/$(PREFIX)objcopy
  SZ = $(GCC_PATH)/$(PREFIX)size
else
  CC = $(PREFIX)gcc
  AS = $(PREFIX)gcc -x assembler-with-cpp
  CP = $(PREFIX)objcopy
  SZ = $(PREFIX)size
endif
HEX = $(CP) -O ihex
BIN = $(CP) -O binary -S
 
# float-abi
ifeq ($(NRF5340), Y)
    ifeq ($(CORE_NET), Y)
        FLOAT-ABI = -mfloat-abi=soft
        OPT += -fsingle-precision-constant
    endif
    
    ifeq ($(CORE_APP), Y)
        FLOAT-ABI = -mfloat-abi=hard
    endif
else
   FLOAT-ABI = -mfloat-abi=hard
endif

# mcu
MCU = $(CPU) -mthumb $(FPU) $(FLOAT-ABI)

# macros for gcc
#CSTANDARD = -std=c11
CSTANDARD = -std=gnu99
# AS defines
AS_DEFS = 

# AS includes
AS_INCLUDES = 

ifeq ($(DEBUG), Y)
    #@echo $(error DEBUG=$(DEBUG))
    CFLAGS += -g3 -gdwarf-2 -ggdb
    OPT += -O0 
else
    OPT += -Os
endif
OPT += -fmessage-length=0    
OPT += -fsigned-char
OPT += -fno-common
OPT += -fstack-usage
OPT += -finline-small-functions

#Perform dead code elimination
OPT += -fdce

#Perform dead store elimination
OPT += -fdse

# compile gcc flags
ASFLAGS = $(MCU) $(AS_DEFS) $(AS_INCLUDES) $(OPT) -Wall -fdata-sections -ffunction-sections

CFLAGS += $(CSTANDARD)
CFLAGS += -Wall
#CFLAGS += -Wformat-overflow=1
CFLAGS += $(MCU) $(OPT) -fdata-sections -ffunction-sections $(INCDIR)  

# Generate dependency information
CFLAGS += -MMD -MP -MF"$(@:%.o=%.d)"

# LDFLAGS

# libraries
LINKER_FLAGS += -Xlinker --gc-sections 
ifeq ($(MBR), Y)
    #@echo $(error MBR=$(MBR))
    LIBS += -lnosys
    LDFLAGS += -specs=nano.specs
else
    LINKER_FLAGS += -u _scanf_float
    LINKER_FLAGS += -u _printf_float
endif
#LINKER_FLAGS += -lrdimon --specs=rdimon.specs

ifeq ($(LIBC), Y)
    #@echo $(error LIBC=$(LIBC))
    LIBS += -lc
endif

ifeq ($(MATH), Y)
    #@echo $(error MATH=$(MATH))
    LIBS += -lm 
endif


#@echo $(error LDSCRIPT=$(LDSCRIPT))
LIBDIR = 

LDFLAGS += $(MCU) -T$(LDSCRIPT) $(LIBDIR) $(LIBS) -Wl,-Map=$(BUILD_DIR)/$(TARGET).map,--cref -Wl,--gc-sections $(LINKER_FLAGS)

# default action: build all
all: $(BUILD_DIR)/$(TARGET).elf $(BUILD_DIR)/$(TARGET).hex $(BUILD_DIR)/$(TARGET).bin


# build the application
# list of objects
OBJECTS = $(addprefix $(BUILD_DIR)/,$(notdir $(SOURCES_C:.c=.o)))
vpath %.c $(sort $(dir $(SOURCES_C)))
# list of ASM program objects
OBJECTS += $(addprefix $(BUILD_DIR)/,$(notdir $(SOURCES_ASM:.S=.o)))
vpath %.S $(sort $(dir $(SOURCES_ASM)))

$(BUILD_DIR)/%.o: %.c Makefile | $(BUILD_DIR) 
	$(CC) -c $(CFLAGS) -Wa,-a,-ad,-alms=$(BUILD_DIR)/$(notdir $(<:.c=.lst)) $< -o $@

$(BUILD_DIR)/%.o: %.S Makefile | $(BUILD_DIR)
	$(AS) -c $(CFLAGS) $< -o $@

$(BUILD_DIR)/$(TARGET).elf: $(OBJECTS) Makefile
	$(CC) $(OBJECTS) $(LDFLAGS) -o $@
	$(SZ) $@

$(BUILD_DIR)/%.hex: $(BUILD_DIR)/%.elf | $(BUILD_DIR)
	$(HEX) $< $@
	
$(BUILD_DIR)/%.bin: $(BUILD_DIR)/%.elf | $(BUILD_DIR)
	$(BIN) $< $@	
	
$(BUILD_DIR):
	mkdir $@		

# clean up
clean:
	-rm -fR $(BUILD_DIR)
  
# dependencies
-include $(wildcard $(BUILD_DIR)/*.d)

# *** EOF ***

5--Когда вы собираете из Make вы можете не только собирать исходники, но и собирать документацию, строить графы зависимостей на dot, построить схему ToolChain(а). Вызвать Latex, Doxyden. Утилите make всё равно какие консольные утилиты вызывать. Это универсальный способ определения программных конвейеров.

6--Для каждой сборки надо самим писать крохотный Makefile

MK_PATH:=$(dir $(realpath $(lastword $(MAKEFILE_LIST))))
#@echo $(error MK_PATH=$(MK_PATH))
WORKSPACE_LOC:=$(MK_PATH)../../

INCDIR += -I$(MK_PATH)
INCDIR += -I$(WORKSPACE_LOC)

#@echo $(error SOURCES_C=$(SOURCES_C))
include $(MK_PATH)config.mk
include $(MK_PATH)cli_config.mk
include $(MK_PATH)diag_config.mk
include $(MK_PATH)test_config.mk

include $(WORKSPACE_LOC)code_base.mk
include $(WORKSPACE_LOC)rules.mk
 

и конфиг для сборки.


mkfile_path := $(abspath $(lastword $(MAKEFILE_LIST)))
$(info Build  $(mkfile_path) )

TARGET=pastilda_r1_1_generic
#@echo $(error TARGET=$(TARGET))
AES256=Y
ALLOCATOR=Y

......

USB_HOST_HS=Y
USB_HOST_PROC=Y
UTILS=Y
XML=Y

Для каждого компонента *.mk файл. Язык make простой и это, в сущности, bash. Вот типичный *.mk файл для драйвера DW1000

ifneq ($(DWM1000_MK_INC),Y)
    DWM1000_MK_INC=Y

    DWM1000_DIR = $(DRIVERS_DIR)/dwm1000
    mkfile_path := $(abspath $(lastword $(MAKEFILE_LIST)))
    $(info Build $(mkfile_path) )
    $(info + DWM1000)

    INCDIR += -I$(DWM1000_DIR)
    OPT += -DHAS_DWM1000
    OPT += -DHAS_DWM1000_PROC
    OPT += -DHAS_UWB

    DWM1000_RANGE_DIAG=Y
    DWM1000_RANGE_COMMANDS=Y

    DWM1000_OTP_COMMANDS=Y
    DWM1000_OTP_DIAG=Y

    SOURCES_C += $(DWM1000_DIR)/dwm1000_drv.c

    include $(DWM1000_DIR)/otp/dwm1000_otp.mk
    include $(DWM1000_DIR)/registers/dwm1000_registers.mk

    ifeq ($(DWM1000_RANGE),Y)
        include $(DWM1000_DIR)/range/dwm1000_range.mk
    endif

    ifeq ($(DIAG),Y)
        ifeq ($(DWM1000_DIAG),Y)
            $(info +DWM1000_DIAG)
            OPT += -DHAS_DWM1000_DIAG
            SOURCES_C += $(DWM1000_DIR)/dwm1000_diag.c
        endif
    endif

    ifeq ($(CLI),Y)
        ifeq ($(DWM1000_COMMANDS),Y)
            $(info +DWM1000_COMMANDS)
            OPT += -DHAS_DWM1000_COMMANDS
            SOURCES_C += $(DWM1000_DIR)/dwm1000_commands.c
        endif
    endif
endif

7--Сборка из Make стимулирует придерживаться модульности, изоляции программных компонентов и прослеживанию зависимостей между компонентами. Если вы собираете из make, то очень вероятно, что у вас будет чистый аккуратный репозиторий.

8--Makefile(лы) хороши тем, что можно добавить много проверок зависимостей и assert(ов) на фазе Make-скриптов прямо в *.mk файлах еще до компиляции самого кода, даже до запуска препроцессора, так как язык программирования make поддерживает условные операторы и функции. Можно очень много ошибок отловить на этапе отработки утилиты make.

9--Язык make очень прост. Вся спека GNU Make это 226 страниц. Cтю Фельдман (автор make) просто гений.

10--Makefile(лы) прозрачные. Всегда видно, где опции препроцессора, где ключи для компилятора, а где для компоновщика. Всё что нужно можно найти утилитой grep в той же консоли.

11--Конфига для сборки можно формировать как раз на стадии make файлов и передавать их как ключи для препроцессора. Таким образом конфиги будут видны в каждом *.с файле проекта.

Вывод

Make это как пуговицы. Старая, простая и очень полезная вещь. Собираете свои прошивки из make в этом нет ничего сложного.

Links

https://www.youtube.com/watch?v=vmuO4bHjTSo&t=7s

https://habr.com/ru/post/47513/

https://habr.com/ru/post/111691/

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.
Вы собираете код с помощью утилиты Make?
0% да 0
0% нет 0
Никто еще не голосовал. Воздержавшихся нет.
Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.
Вы собираете прошивки из-под IDE?
0% да 0
0% нет 0
Никто еще не голосовал. Воздержавшихся нет.
Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.
Вы собираете код с помощью утилиты СMake?
0% да 0
0% нет 0
Никто еще не голосовал. Воздержавшихся нет.
Источник: https://habr.com/ru/post/723054/


Интересные статьи

Интересные статьи

Никогда не пытайтесь объяснить незнакомцу на английском, как найти магазин «Шитье» на улице Фартовой, рядом с кинотеатром «Факел». Сегодня мы расскажем об эффекте Телепорно, кот...
Эх, fork(). Одни процессы порождают другие. Кажется, у меня есть история об этом. Форк может глюкануть. Понимаете? В самое деле, понимаете? Это очень серьёзно. Форк может глюкануть...
Многие сетевые приложения состоят из веб-сервера, обрабатывающего трафик в реальном времени, и дополнительного обработчика, запускаемого в фоне асинхронно. Есть множество отличных советов по пр...
Halo компании Bungie или GoldenEye компании Rare часто упоминаются как игры, впервые по-настоящему перенёсшие на консольные платформы высокотехнологичные, качественные FPS, но с этой точки зрен...
Месяц назад Apple присоедились к Cloud Native Computing Foundation. Разбираемся, что это значит.