"Переносимая кодовая база - это плацдарм для будущих разработок."

Пролог

GNU Make - это консольная утилита, которая запускает другие консольные утилиты в желаемой последовательности. Только и всего. Конфигом для утилиты make является текстовый файл-скрипт хранящийся в файле по имени Makefile. Скрипт - это программа для интерпретатора. Поэтому утилиту GNU Make называют системой сборки. Можно сказать, что Make - это интерпретатор языка make.

Прелесть утилиты make в том, что ей абсолютно всё равно с каким языком программирования работать. Более того, утилите make всё равно с какие утилиты вызывать.
Утилита make всеядная. Понимаете?

С математической точки зрения, make делает топологическую сортировку ориентированного графа. Makefile скрипт прописывает список смежности вершин. Далее запускается команда make all, и утилита проходит все вершины в порядке ориентированного графа. По сути makefile определяет конвейер вызова утилит для метаморфоза файлов из одного расширения в другое расширение прямо на жестком диске PC.

При помощи make можно даже автоматически синтезировать инструкции по сборке пассажирских авиалайнеров или домов из миллионов деталей. Достаточно просто в make файле атомарно указать, что надо соединить с чем. Затем вызвать make all и у вас появится текстовый файл с логом корректной инструкций сборки этого, условно, самолёта. Корректная инструкция в том плане, что у вас не будет такой ситуации, что очередная деталь упирается в узкий проём и её не вставить, из-за чего надо опять разбирать, скажем, пол двигателя.

Всё то же самое происходит и в сборке компьютерных программ. Нет смысла генерировать bin файл, когда нет elf файла. Нет смысла генерировать elf файл, когда нет obj файла.
И тому подобное.

Достоинство make в том, что он пере собирает только те файлы , которые были изменены.
Времена последней модификации, читаются от файловой системы. То есть в make с самого начала в 1976 были заложены элементы контроля версий.

Синтаксис и cемантика

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

В make есть переменные, функции и файлы. Всё как в настоящем языке программирования.

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

$(error CFLAGS=$(CFLAGS))

Заложены и сокращенные, специальные переменные

специальные переменные

пояснение

$@

Имя цели обрабатываемого правила

$^

Список всех зависимостей обрабатываемого правила

$<

Имя первой зависимости обрабатываемого правила

$*

Основа целевого имени файла. Основа — это обычно имя файла без суффикса.

$?

Цепочке имен файлов, которые оказались более новыми, чем целевой

$+

Подобно $^, это имена всех зависимостей, разделенных пробелами, за исключением того,
что $+ включает дубликаты.
Эта переменная была создана для особых ситуаций, таких как аргументы для
линкеров, где дубликаты значений имеют смысл.

$%

Элемент имени файла спецификации члена архива.

Как организовать скрипты сборки GNU Make?

Любой GNU Make скрипт начинается с Makefile. У каждой сборки есть свой Makefile.
По-хорошему Makefile должен быть очень маленьким. Все общие скрипты должны быть вынесены за скобки в отдельные *.mk файлы. Да, GNU Make поддерживает свой собственный препроцессор. Поэтому вы будете часто встречать ключевое слово include. Это нормально.

# Makefile

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

DEPENDENCIES_GRAPHVIZ=Y
TARGET=boardname_configname_gcc_m

include $(MK_PATH)/config.mk

ifeq ($(CLI),Y)
    include $(MK_PATH)/cli_config.mk
endif

ifeq ($(DIAG),Y)
    include $(MK_PATH)/diag_config.mk
endif

ifeq ($(TEST),Y)
    include $(MK_PATH)/test_config.mk
endif

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

Тут функция realpath вычисляет абсолютный путь учитывая арифметику над путями. При сборке из make по сути все сборки в репозитории отличаются только одним лишь файликом.
Это config.mk. Вот так он выглядит.

# config.mk

CLI=Y
DEBUG=Y
DEPENDENCIES_GRAPHVIZ=Y
GENERIC=Y
GPIO=Y
LED_MONO=Y
.............
TIME=Y
UART2=Y
UNIT_TEST=Y
YTM32B1ME05G0MLQ=Y
YTM32B1M_EVB_0144_REV_B=Y

Внутри config.mk декларативно перечисляется из каких программных компонентов
должна состоять данная программа. Конфиг для диагностики (diag_config.mk).

#   diag_config.mk

$(info Add Diag)

DIAG=Y
LOG_DIAG=Y

ifeq ($(ALLOCATOR),Y)
    ALLOCATOR_DIAG=Y
endif

ifeq ($(CORTEX_M33),Y)
    CORTEX_M33_DIAG=Y
endif

........


ifeq ($(TIMER),Y)
    TIMER_DIAG=Y
endif

ifeq ($(WATCHDOG),Y)
    WATCHDOG_DIAG=Y
endif

\end{lstlisting}

Конфиг для CLI cli_config.mk

# cli_config.mk

$(info CLI_CONFIG_MK_INC=$(CLI_CONFIG_MK_INC) )
ifneq ($(CLI_CONFIG_MK_INC),Y)
    CLI_CONFIG_MK_INC=Y

    CLI_CMD_HISTORY=Y
    CLI=Y
    
    ifeq ($(BUTTON),Y)
        BUTTON_COMMANDS=Y
    endif
    
    ifeq ($(NVIC),Y)
        NVIC_COMMANDS=Y
    endif
.......
    
    ifeq ($(UART),Y)
        UART_COMMANDS=Y
    endif

    ifeq ($(UNIT_TEST),Y)
        UNIT_TEST_COMMANDS=Y
    endif
    
    ifeq ($(WATCHDOG),Y)
        WATCHDOG_COMMANDS=Y
    endif
endif

У каждого программного компонента свой отдельный make скрипт сборки. Имеет расширение *.mk. Все они будут выглядеть структурно одинаково. Отличие только в одном ключевом слове. Вот, например button.mk

# button.mk 

$(info BUTTON_MK_INC=$(BUTTON_MK_INC))

ifneq ($(BUTTON_MK_INC),Y)
    BUTTON_MK_INC=Y

    BUTTON_DIR = $(SENSITIVITY_DIR)/button
    #@echo $(error BUTTON_DIR=$(BUTTON_DIR))

    INCDIR += -I$(BUTTON_DIR)
    SOURCES_C += $(BUTTON_DIR)/button_drv.c

    BUTTON=Y
    OPT += -DHAS_BUTTON
    OPT += -DHAS_BUTTON_PROC

    ifeq ($(BUTTON_DIAG),Y)
        OPT += -DHAS_BUTTON_DIAG
        SOURCES_C += $(BUTTON_DIR)/button_diag.c
    endif

    ifeq ($(CLI),Y)
        ifeq ($(BUTTON_COMMANDS),Y)
            OPT += -DHAS_BUTTON_COMMANDS
            SOURCES_C += $(BUTTON_DIR)/button_commands.c
        endif
    endif
endif

Это скрипт с правилами сборки проекта (rules.mk). Его прелесть в том, что он общий для всех 300 сборок в репозитории! Обратите внимание на ключевое слово vpath. Оно позволяет перенаправлять объектные файлы в папку build и, тем самым, не засорять репозиторий временными объектными файлами.

# rules.mk

CSTANDARD = -std=c99
#CSTANDARD = -std=c11 

mkfile_path := $(abspath $(lastword $(MAKEFILE_LIST)))
$(info mkfile_path:$(mkfile_path) )
MK_PATH := $(subst /cygdrive/c/,C:/, $(MK_PATH))
$(info MK_PATH=$(MK_PATH))

BUILD_DIR=build

EXTRA_TARGETS=

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

SOURCES_ASM := $(subst /cygdrive/c/,C:/, $(SOURCES_ASM))

LIBS  := $(subst /cygdrive/c/,C:/, $(LIBS))
LDSCRIPT := $(subst /cygdrive/c/,C:/, $(LDSCRIPT))

WORKSPACE_LOC := $(realpath  $(WORKSPACE_LOC))
WORKSPACE_LOC := $(subst /cygdrive/c/,C:/, $(WORKSPACE_LOC))

include $(WORKSPACE_LOC)/make_scripts/toolchain.mk

AS_DEFS = 
AS_INCLUDES = 

MICROPROCESSOR +=  $(CPU)
MICROPROCESSOR +=  $(FPU) 
MICROPROCESSOR +=  $(FLOAT-ABI)

include $(WORKSPACE_LOC)/make_scripts/compiler_options.mk
include $(WORKSPACE_LOC)/make_scripts/linker_options.mk

ASFLAGS += $(MCU)
ASFLAGS += $(AS_DEFS)
ASFLAGS += $(AS_INCLUDES)
ASFLAGS += $(OPT)
ASFLAGS += $(COMPILE_OPT)
ASFLAGS += -Wall
ASFLAGS +=-fdata-sections
ASFLAGS += -ffunction-sections

CPP_FLAGS += $(CSTANDARD) $(INCDIR)  $(OPT) $(COMPILE_OPT)

EXTRA_TARGETS += generate_definitions

ARTIFACTS += $(BUILD_DIR)/$(TARGET).bin
ARTIFACTS += $(BUILD_DIR)/$(TARGET).hex
ARTIFACTS += $(BUILD_DIR)/$(TARGET).elf

.PHONY: all

all: $(EXTRA_TARGETS) $(ARTIFACTS)

.PHONY: generate_definitions

generate_definitions:
    $(info GenerateDefinitions...)
    $(PREPROCESSOR_TOOL) $(CPP_FLAGS) $(WORKSPACE_LOC)/empty_source.c -dM -E> c_defines_generated.h
    $(SORTER_TOOL) -u c_defines_generated.h -o c_defines_generated.h

OBJECTS = $(addprefix $(BUILD_DIR)/,$(notdir $(SOURCES_TOTAL_C:.c=.o)))

vpath %.c $(sort $(dir $(SOURCES_TOTAL_C)))

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

TOTAL_FILES := $(words $(OBJECTS))
$(info TOTAL_FILES:$(TOTAL_FILES) )

$(BUILD_DIR)/%.o: %.c Makefile | $(BUILD_DIR) 
    $(eval CURRENT_CNT=$(shell echo $$(($(CURRENT_CNT)+1))))
    @echo Compiling $(CURRENT_CNT)/$(TOTAL_FILES) $@
    @$(CC) -c -MD $(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 -p $@

.PHONY: clean

clean:
    -rm -fR $(BUILD_DIR)

# dependencies
-include $(wildcard $(BUILD_DIR)/*.d)

Как можно заметить, в зависимости от значения переменных окружения, make скрипт добавит в сборку те или иные программные компоненты. Каждый программный компонент оформляется как .mk файл. Внутри .mk файла происходит добавление файлов исходников в переменную окружения SOURCES_C и происходит добавление макро определений в переменную окружения OPT.

ifneq ($(CODE_BASE_MK),Y)
    CODE_BASE_MK=Y

    include $(WORKSPACE_LOC)/make_scripts/code_base_preconfig.mk
    include $(WORKSPACE_LOC)/make_scripts/verify_build.mk

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

    GIT_SHA := $(shell git rev-parse --short HEAD)
    OPT += -DGIT_SHA=0x0$(GIT_SHA)

    ifeq ($(DEBUG),Y)
        OPT += -DHAS_DEBUG
    endif

    ifeq ($(MBR),Y)
        include $(WORKSPACE_LOC)/make_scripts/mbr.mk
    endif

    ifeq ($(GENERIC),Y)
        include $(WORKSPACE_LOC)/make_scripts/generic.mk
    endif

    ifeq ($(MICROCONTROLLER),Y)
        FIRMWARE=Y
        include $(WORKSPACE_LOC)/microcontroller/microcontroller.mk
    endif

    ifeq ($(BOARD),Y)
        include $(WORKSPACE_LOC)/boards/boards.mk
    endif

    ifeq ($(PROTOTYPE),Y)
        include $(WORKSPACE_LOC)/prototypes/prototypes.mk
    endif


    ifeq ($(X86),Y)
        SUPER_CYCLE=Y
        OPT += -DX86
        OPT += -DHAS_X86
        FLOAT_UTILS=Y
    endif

    ifeq ($(X86_64),Y)
        SUPER_CYCLE=Y
        #@echo $(error stop)
        OPT += -DX86_64
        OPT += -DHAS_X86_64
    endif

    ifeq ($(THIRD_PARTY),Y)
        include $(WORKSPACE_LOC)/third_party/third_party.mk
    endif

    ifeq ($(CORE),Y)
        include $(WORKSPACE_LOC)/core/core.mk
    endif

    ifeq ($(APPLICATIONS),Y)
        include $(WORKSPACE_LOC)/applications/applications.mk
    endif

    ifeq ($(MCAL),Y)
        include $(WORKSPACE_LOC)/mcal/mcal.mk
    endif

    ifeq ($(ADT),Y)
        include $(WORKSPACE_LOC)/adt/adt.mk
    endif

    ifeq ($(CONNECTIVITY),Y)
        include $(WORKSPACE_LOC)/connectivity/connectivity.mk
    endif

    ifeq ($(CONTROL),Y)
        include $(WORKSPACE_LOC)/control/control.mk
    endif
    
    ifeq ($(COMPONENTS),Y)
        include $(WORKSPACE_LOC)/components/components.mk
    endif

    ifeq ($(COMPUTING),Y)
        include $(WORKSPACE_LOC)/computing/computing.mk
    endif

    include $(WORKSPACE_LOC)/compiler/compiler.mk
        
    ifeq ($(SENSITIVITY),Y)
        include $(WORKSPACE_LOC)/sensitivity/sensitivity.mk
    endif

    ifeq ($(STORAGE),Y)
        include $(WORKSPACE_LOC)/storage/storage.mk
    endif

    ifeq ($(SECURITY),Y)
        include $(WORKSPACE_LOC)/security/security.mk
    endif

    ifeq ($(ASICS),Y)
        include $(WORKSPACE_LOC)/asics/asics.mk
    endif

    ifeq ($(MISCELLANEOUS),Y)
        include $(WORKSPACE_LOC)/miscellaneous/miscellaneous.mk
    endif

    ifeq ($(UNIT_TEST),Y)  
        include $(WORKSPACE_LOC)/unit_tests/unit_test.mk
    endif

    SOURCES_C += $(WORKSPACE_LOC)/main.c
endif

Сборка прошивок - это всегда коросс-компиляция. Поэтому надо в make скрипте явно указать каким именно компилятором мы будем собирать исходные тексты программ (сорцы).

PREFIX = arm-none-eabi-
$(info GCC_PATH=$(GCC_PATH))

ifdef GCC_PATH
    $(info WithPath)
    PREPROCESSOR_TOOL =$(GCC_PATH)/$(PREFIX)cpp
    CC = $(GCC_PATH)/$(PREFIX)gcc
    CPP = $(GCC_PATH)/$(PREFIX)g++
    AS = $(GCC_PATH)/$(PREFIX)gcc -x assembler-with-cpp
    CP = $(GCC_PATH)/$(PREFIX)objcopy
    SZ = $(GCC_PATH)/$(PREFIX)size
else
    $(info WithOutPath)
    PREPROCESSOR_TOOL = $(PREFIX)cpp
    CC = $(PREFIX)gcc
    CPP = $(PREFIX)g++
    AS = $(PREFIX)gcc -x assembler-with-cpp
    CP = $(PREFIX)objcopy
    SZ = $(PREFIX)size
endif

HEX = $(CP) -O ihex
BIN = $(CP) -O binary -S

Компилятор - это консольная утилита. Как и у любой консольной утилиты в gcc есть опции командной строки. Вот типичный пучок опций для сборки прошивок на MCU (compiler_options.mk). Что значит каждая опция компилятора можно посмотреть в документе Using the GNU Compiler Collection.

# compiler_options.mk

COMPILE_OPT += -Wall
COMPILE_OPT += -fdata-sections 
COMPILE_OPT += -ffunction-sections  
COMPILE_OPT += -Werror=address
COMPILE_OPT += -Werror=switch
COMPILE_OPT += -Werror=array-bounds=1
COMPILE_OPT += -Werror=comment
COMPILE_OPT += -Werror=div-by-zero
COMPILE_OPT += -Werror=duplicated-cond
COMPILE_OPT += -Werror=shift-negative-value
COMPILE_OPT += -Werror=duplicate-decl-specifier
COMPILE_OPT += -Werror=enum-compare
COMPILE_OPT += -Werror=uninitialized
COMPILE_OPT += -Werror=empty-body
COMPILE_OPT += -Werror=unused-but-set-parameter
COMPILE_OPT += -Werror=unused-but-set-variable
COMPILE_OPT += -Werror=float-equal
COMPILE_OPT += -Werror=logical-op
COMPILE_OPT += -Werror=implicit-int
COMPILE_OPT += -Werror=implicit-function-declaration
COMPILE_OPT += -Werror=incompatible-pointer-types
COMPILE_OPT += -Werror=int-conversion
COMPILE_OPT += -Werror=old-style-declaration
COMPILE_OPT += -Werror=maybe-uninitialized
COMPILE_OPT += -Werror=redundant-decls
COMPILE_OPT += -Werror=sizeof-pointer-div
COMPILE_OPT += -Werror=misleading-indentation
COMPILE_OPT += -Werror=missing-declarations
COMPILE_OPT += -Werror=missing-parameter-type
COMPILE_OPT += -Werror=overflow
COMPILE_OPT += -Werror=parentheses
COMPILE_OPT += -Werror=pointer-sign
COMPILE_OPT += -Werror=return-type
COMPILE_OPT += -Werror=shift-count-overflow
COMPILE_OPT += -Werror=strict-prototypes
COMPILE_OPT += -Werror=unused-but-set-variable
COMPILE_OPT += -Werror=unused-function
COMPILE_OPT += -Werror=unused-variable
COMPILE_OPT += -Werror=type-limits
COMPILE_OPT += -Werror=override-init
COMPILE_OPT += -Werror=duplicate-decl-specifier
COMPILE_OPT += -Werror=int-conversion
COMPILE_OPT += -Wno-stringop-truncation
COMPILE_OPT += -Wno-format-truncation
COMPILE_OPT += -Wno-restrict
COMPILE_OPT += -Wno-format
COMPILE_OPT += -Wno-cpp #TODO temp
COMPILE_OPT += -Wno-discarded-qualifiers
COMPILE_OPT += -Wmissing-prototypes
COMPILE_OPT += -Werror=traditional
COMPILE_OPT += -Werror=missing-prototypes
COMPILE_OPT += -fdce
COMPILE_OPT += -fdse
COMPILE_OPT += -fmessage-length=0
COMPILE_OPT += -fsigned-char
COMPILE_OPT += -fno-common
COMPILE_OPT += -fstack-usage
COMPILE_OPT += -fzero-initialized-in-bss
COMPILE_OPT += -finline-small-functions
COMPILE_OPT += -Wmissing-field-initializers
COMPILE_OPT += -Werror=missing-field-initializers
COMPILE_OPT += -Werror=unused-but-set-variable
COMPILE_OPT += -Werror=implicit-function-declaration
COMPILE_OPT += -Werror=unused-variable
COMPILE_OPT += -Wformat-overflow=1

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

ifeq ($(DEBUG), Y)
    COMPILE_OPT += -O0
    COMPILE_OPT += -g3 
else
    COMPILE_OPT += -Os
endif


COMPILE_OPT += $(CSTANDARD)
COMPILE_OPT += $(MICROPROCESSOR)  
COMPILE_OPT += $(INCDIR)

Подобно компилятору, ключи надо передавать и компоновщику. Вот типичный пучок опций для настройки компоновщика (linker_options.mk). Что значит каждая опция компоновщика можно посмотреть в доке The GNU linker.


LINKER_FLAGS += -Xlinker --gc-sections 
LINKER_FLAGS += -Xlinker --print-memory-usage

ifeq ($(LIBC_NANO), Y)
    LINKER_FLAGS += --specs=nano.specs 
endif

ifeq ($(LIBC_RDIMON), Y)
    LINKER_FLAGS +=  --specs=rdimon.specs
endif

ifeq ($(LIBC), Y)
    LIBS += -lc
endif

ifeq ($(MATH_LIB), Y)
    LIBS += -lm
endif

LIBDIR += 

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

В переменную окружения LDFLAGS через ключ -T передается скрипт компоновщику. Он сообщает компоновщику, как организовать код из входных объектов.

Структура сборки

При сборке из самоcтоятельно написанных скриптов make у вас для каждой прошивки (сборки) в папке projects будет папка, в которой будет лежать вот эти файлы. По факту достаточно только двух файлов: Makefile и config.mk. Остальное - это уже специфика прошивки и настройки для вашего текстового редактора.

название файла или папки

обязательно

пояснение

1

Makefile

1

make файл для данной сборки

2

build_from_make.bat

скрипт который просто выpывает make all

3

config.mk

1

конфигурация для функционала прошивки

4

diag_config.mk

конфигурация для диагностики

5

cli_config.mk

конфигурация UART-CLI консоли

6

flash_hex.bat

скрипт для вызова make flash

7

test_config.mk

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

8

sys_config.h

конфиг для выбора электронной платы

9

version.h

версия прошивки

10

build

1

папка куда будут попадать сгенерированные артефакты (*.elf, *.hex, *.bin, .map) и .o файлы

11

.cproject

Настройка Eclipse, как системы сборки

12

.project

Настройка Eclipse, как текстового редактора

Итоги

Вот теперь и Вы обладаете минимальными необходимыми навыками и сноровкой для ручного написания make скриптов, полноценной работы с системой сборки GNU Make и можете учить этому других.

Как видите, в GNU Make нет, ровным счетом, ничего сложного.

Помимо сборки сорцов make позволит Вам достаточно легко сделать много других полезностей:


У сборок из скриптов самое главное достоинство - это простота масштабирования. В этом вся соль. Немного усилий в начале зато потом существенный выигрыш в производительности.

Ссылки

#

Название

URL

1

Инструкция к GNU make на русском

https://www.opennet.ru/soft/ruprog/make.txt

2

GNU make

https://www.gnu.org/software/make/manual/make.html

3

Магия makefile на простых примерах

https://microsin.net/programming/arm/learning-makefile-with-simple-examples.html

4

Перевод Makefile Mini HOWTO

https://www.opennet.ru/base/dev/mini_make.txt.html

5

Everything You Never Wanted To Know About Linker Script

https://mcyoung.xyz/2021/06/01/linker-script/

6

Пример Makefile

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

7

Эффективное использование GNU Make

https://www.opennet.ru/docs/RUS/gnumake/

8

Обновление Прошивки из Make Скрипта

https://habr.com/ru/articles/857416

9

Настройка ToolChain(а) для Win10++С+Makefile+ARM Cortex-Mx+GDB

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

12

Как собрать Си программу в OS Windows

https://habr.com/ru/articles/754972/

10

GNU Make может больше чем ты думаешь

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

11

Почему важно собирать код из скриптов

https://habr.com/ru/articles/723054/

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


  1. Andrey4ik
    16.05.2025 22:19

    Ну я сделал для себя шаблончик на мейке который build (в .exe) запускает скрипт (run) а так же deploy (Inno Setup например)

    Если что, на питоне)



  1. Jijiki
    16.05.2025 22:19

    я мечтаю сделать компилятор проекта, образно файл lua в нем таблица - это титульник - титльник это имя проекта - его окружение, так вот, хочу писать в титульник все нюансы сборки по окружению, а файлик либо мой тоесть на каком-то ЯП, или sh будет его брать и готовить проект), а так пока использую просто sh)


    1. aabzel Автор
      16.05.2025 22:19

      Компилятор проекта? Странный термин. Обычно говорят компилятор какого-н языка программирования.


    1. aabzel Автор
      16.05.2025 22:19

      а так пока использую просто sh

      sh всегда будет собирать всё.
      А make только то, что было отредактировано с момента последнего вызова.

      Это и есть главная причина появления make на фоне bash. Make смотрит, что поменялось и только это собирает.


    1. Jijiki
      16.05.2025 22:19

      вот пример окружения, может я путаюсь в понятиях, но сюда отдельной таблицей можно закинуть сами типы билдов, и написать мини-парсер

      Скрытый текст

      многое появление строк не станет неожиданностью, а будут из таблицы как и должны будут быть в будущем, если докодиться до будущего с бд)

      более того таблица заставляет делать правильное именование читаемое

      это что-то типо перфокарты проекта или манифеста(что где, какой нюанс или имя)


      1. VADemon
        16.05.2025 22:19

        Искомое слово: декларативный. Декларативная конфигурация сборки. При этом Lua легко позволил бы и ручное приправление сборки кодом.


    1. Daazay
      16.05.2025 22:19

      Попробуйте посмотреть на premake5. Кажется вы что-то такое описываете.


    1. gohrytt
      16.05.2025 22:19

      Попробуйте zig и его build.zig, возможно понравится


  1. mpa4b
    16.05.2025 22:19

    У кого-нибудь есть пример, как интегрировать в gnu make сборку precompiled header для gcc? Ну то есть нужно, чтоб во-1 зависимости инклуда, который предкомпилируется, прогенерились (по аналогии с тем, как можно автоматически генерить зависимости .c файлов от .h), а во-2 чтоб при изменении любого инклуда, от которого зависит precompiled header или при изменении его самого всё пересобиралось, в т.ч. зависимости.


    1. aabzel Автор
      16.05.2025 22:19

      Хороший вопрос.


    1. randomsimplenumber
      16.05.2025 22:19

      cmake ?


    1. unreal_undead2
      16.05.2025 22:19

      gcc -MM, дальше доработать напильником


  1. FreemanIsAlive
    16.05.2025 22:19

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

    Достоинство make в том, что он пере собирает только те файлы , которые были изменены.Времена последней модификации, читаются от файловой системы.

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

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

    Универсальность это, конечно, хорошо, но многим ли нужна запускаемая из терминала сборка пассажирского авиалайнера без пересбора уже собранных (такая вот тавтология, уж извините) деталей, даже если предположить, что эта шайтан-программа магическим образом сможет обнаружить, какие детали уже сделаны?

    Буду очень рада, если меня просветят, но пока что выглядит, что как GCC сборник компиляторов для архаичных языков (C/C++ разработчики, не в вашу сторону, я очень редко видела использование GCC для этих языков, обычно используется clang), так и это вещь для специфической задачи - компиляция для языков с очень старой архитектурой. Разумеется, моё мнение C#-огузка, которая ещё unsafe (почти) не нюхала, не является истиной в последней инстанции, но это как раз то, для чего у комментариев тоже есть комментарии.


    1. aabzel Автор
      16.05.2025 22:19

      Очень приятно на форумах по программированию читать комментарии с глаголами на (ла)


    1. mpa4b
      16.05.2025 22:19

      Если забить на гцц, то через несколько лет можно оказаться в той же ситуации что и с firefox vs хромой -- т.е. в корпоративном вендорлоке


      1. FreemanIsAlive
        16.05.2025 22:19

        Я предположу, что это относится к C/C++, потому что в других языках (кроме Go), поддерживаемых GCC, сложно забить (единственные поддерживаемые компиляторы).

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


        1. mpa4b
          16.05.2025 22:19

          В дополнение clang управляется не корпорацией, а обычным человеком.

          Во-1 надо, чтоб было чем управлять. Программисты. Для проектов уровня шланга ли, файрфокса ли -- их надо много. Во-2, чтоб все эти программисты делали чонадо а не чохотят, им надо бабло платить. А бабло надо где-то брать. А вот лично вы давно ли донатили на развитие шланга? Правильно. Донатят корпорации, даже не донатят а платят за то, чтобы делали что им нужно. И тут уже "всегда можно форкнуть" перестаёт работать, т.к. такие громадные проекты форкнуть конечно можно, но вот мейнтейнить (апдейтить под новые стандарты с\c++, фиксить баги, добавлять платформы и языки) уже в 1 лицо не получится. Через несколько лет форк превратится в тыкву.

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

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


          1. FreemanIsAlive
            16.05.2025 22:19

            Могу только согласиться. Конкуренция должна быть всегда.


    1. randomsimplenumber
      16.05.2025 22:19

      Любопытный вброс ;) Надо понимать, в шарпе магический компилятор сам, без костылей умеет определять, в каком порядке билдить 1 000 000 файлов с исходниками. и 1 000 000 подсчётов хеша не занимает ни времени ни памяти ;)


      1. FreemanIsAlive
        16.05.2025 22:19

        Не вброс, честное слово, моё искреннее недоумение xd

        За ответ спасибо, не знала, что в GCC так. Любопытно, почему и там не сделали.

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


        1. randomsimplenumber
          16.05.2025 22:19

          не знала, что в GCC так

          Если думаете что в clang сильно иначе, то у вас впереди много чудесных открытий ;)

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

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