Как стать автором
Обновить

Основы по GNU Make

Уровень сложностиПростой
Время на прочтение12 мин
Количество просмотров3.3K

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

Пролог

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/

10

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

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

11

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

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


Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.
Используете ли Вы самостоятельно написанные make скрипты для сборки программ?
66.67% да36
33.33% нет18
Проголосовали 54 пользователя. Воздержались 3 пользователя.
Теги:
Хабы:
+17
Комментарии15

Публикации

Работа

DevOps инженер
31 вакансия
Программист С
33 вакансии

Ближайшие события