В период с 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/