
"Переносимая кодовая база - это плацдарм для будущих разработок."
Пролог
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 |
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 позволит Вам достаточно легко сделать много других полезностей:
№ |
Задача |
1 |
|
2 |
|
3 |
запустить всяческие кодогенераторы (для любителей кодогенераторов) |
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
и многое другое, всё, что Вашей душе угодно |
У сборок из скриптов самое главное достоинство - это простота масштабирования. В этом вся соль. Немного усилий в начале зато потом существенный выигрыш в производительности.

Ссылки
# |
Название |
URL |
1 |
Инструкция к GNU make на русском |
|
2 |
GNU make |
|
3 |
Магия makefile на простых примерах |
https://microsin.net/programming/arm/learning-makefile-with-simple-examples.html |
4 |
Перевод Makefile Mini HOWTO |
|
5 |
Everything You Never Wanted To Know About Linker Script |
|
6 |
Пример Makefile |
|
7 |
Эффективное использование GNU Make |
|
8 |
Обновление Прошивки из Make Скрипта |
|
9 |
Настройка ToolChain(а) для Win10++С+Makefile+ARM Cortex-Mx+GDB |
|
12 |
Как собрать Си программу в OS Windows |
|
10 |
GNU Make может больше чем ты думаешь |
|
11 |
Почему важно собирать код из скриптов |
Комментарии (81)
Jijiki
16.05.2025 22:19я мечтаю сделать компилятор проекта, образно файл lua в нем таблица - это титульник - титльник это имя проекта - его окружение, так вот, хочу писать в титульник все нюансы сборки по окружению, а файлик либо мой тоесть на каком-то ЯП, или sh будет его брать и готовить проект), а так пока использую просто sh)
aabzel Автор
16.05.2025 22:19Компилятор проекта? Странный термин. Обычно говорят компилятор какого-н языка программирования.
aabzel Автор
16.05.2025 22:19а так пока использую просто sh
sh всегда будет собирать всё.
А make только то, что было отредактировано с момента последнего вызова.
Это и есть главная причина появления make на фоне bash. Make смотрит, что поменялось и только это собирает.
Jijiki
16.05.2025 22:19вот пример окружения, может я путаюсь в понятиях, но сюда отдельной таблицей можно закинуть сами типы билдов, и написать мини-парсер
Скрытый текст
многое появление строк не станет неожиданностью, а будут из таблицы как и должны будут быть в будущем, если докодиться до будущего с бд)
более того таблица заставляет делать правильное именование читаемое
это что-то типо перфокарты проекта или манифеста(что где, какой нюанс или имя)
VADemon
16.05.2025 22:19Искомое слово: декларативный. Декларативная конфигурация сборки. При этом Lua легко позволил бы и ручное приправление сборки кодом.
mpa4b
16.05.2025 22:19У кого-нибудь есть пример, как интегрировать в gnu make сборку precompiled header для gcc? Ну то есть нужно, чтоб во-1 зависимости инклуда, который предкомпилируется, прогенерились (по аналогии с тем, как можно автоматически генерить зависимости .c файлов от .h), а во-2 чтоб при изменении любого инклуда, от которого зависит precompiled header или при изменении его самого всё пересобиралось, в т.ч. зависимости.
FreemanIsAlive
16.05.2025 22:19Скорее всего, меня закидают тапками, но мне после прочтения статьи кажется, что смысла существования на текущий момент у Make не особо больше, чем у GCC - строго для очень специфических задач, потому что для другого есть лучше.
Достоинство make в том, что он пере собирает только те файлы , которые были изменены.Времена последней модификации, читаются от файловой системы.
Очень интересно, как это работает. Большинство современных языков программирования вообще собираются одной командой - по крайней мере, иное мне не встречалось - и в таком случае Make надо заменять собой компиляторы всех языков мира, и все это должно быть написано вручную.
А последнее вещь крайне сомнительная. Не отрицаю, что вычислять хеш каждого файла было бы несколько накладно, не говоря уже про то, чтобы хранить это, но очень много какие программы творят с датами дичь. Проводник Windows, например, при распаковке архивов время создания ставит на время распаковки. Это конкретно тут не особо навредит, просто первый пришедший в голову пример. По сути, один скрипт на Python, меняющий даты файлов на более старые, чем последняя сборка, и все сломано.
Универсальность это, конечно, хорошо, но многим ли нужна запускаемая из терминала сборка пассажирского авиалайнера без пересбора уже собранных (такая вот тавтология, уж извините) деталей, даже если предположить, что эта шайтан-программа магическим образом сможет обнаружить, какие детали уже сделаны?
Буду очень рада, если меня просветят, но пока что выглядит, что как GCC сборник компиляторов для архаичных языков (C/C++ разработчики, не в вашу сторону, я очень редко видела использование GCC для этих языков, обычно используется clang), так и это вещь для специфической задачи - компиляция для языков с очень старой архитектурой. Разумеется, моё мнение C#-огузка, которая ещё unsafe (почти) не нюхала, не является истиной в последней инстанции, но это как раз то, для чего у комментариев тоже есть комментарии.
aabzel Автор
16.05.2025 22:19Очень приятно на форумах по программированию читать комментарии с глаголами на (ла)
mpa4b
16.05.2025 22:19Если забить на гцц, то через несколько лет можно оказаться в той же ситуации что и с firefox vs хромой -- т.е. в корпоративном вендорлоке
FreemanIsAlive
16.05.2025 22:19Я предположу, что это относится к C/C++, потому что в других языках (кроме Go), поддерживаемых GCC, сложно забить (единственные поддерживаемые компиляторы).
Странное сравнение, если честно. Одно дело браузер, который браузер и браузер, просто поставивший себе по умолчанию в поисковик Google (который и так по умолчанию везде) ради выживания, а другое исходный код. В дополнение clang управляется не корпорацией, а обычным человеком. В дополнение его исходный код открытый, даже если резко сменят лицензию, ничего не мешает сделать форк с последней версии, которая по старой лицензии.
mpa4b
16.05.2025 22:19В дополнение clang управляется не корпорацией, а обычным человеком.
Во-1 надо, чтоб было чем управлять. Программисты. Для проектов уровня шланга ли, файрфокса ли -- их надо много. Во-2, чтоб все эти программисты делали чонадо а не чохотят, им надо бабло платить. А бабло надо где-то брать. А вот лично вы давно ли донатили на развитие шланга? Правильно. Донатят корпорации, даже не донатят а платят за то, чтобы делали что им нужно. И тут уже "всегда можно форкнуть" перестаёт работать, т.к. такие громадные проекты форкнуть конечно можно, но вот мейнтейнить (апдейтить под новые стандарты с\c++, фиксить баги, добавлять платформы и языки) уже в 1 лицо не получится. Через несколько лет форк превратится в тыкву.
Всякие там го и расты -- как раз примеры вендорлоков, которые развиваются непойми кем и существуют в виде единственной имплементации (про го пишут что там несколько имплементаций, так что тут не точно). ГНУшники вроде пытаются свой растокомпилятор выкатить, но при отсутствии официального стандарта на язык им можно подкладывать свинью за свиньёй и их реализация никогда не сравнится с "настоящей", например.
Всё это означает, что сохранять и поддерживать гцц при наличии шланга -- крайне важно.
randomsimplenumber
16.05.2025 22:19Любопытный вброс ;) Надо понимать, в шарпе магический компилятор сам, без костылей умеет определять, в каком порядке билдить 1 000 000 файлов с исходниками. и 1 000 000 подсчётов хеша не занимает ни времени ни памяти ;)
FreemanIsAlive
16.05.2025 22:19Не вброс, честное слово, моё искреннее недоумение xd
За ответ спасибо, не знала, что в GCC так. Любопытно, почему и там не сделали.
Ну и последнее вопрос уже тогда скорее к Make, почему там используется то, что может быть свободно изменено чем угодно с правами доступа к файлам.
randomsimplenumber
16.05.2025 22:19не знала, что в GCC так
Если думаете что в clang сильно иначе, то у вас впереди много чудесных открытий ;)
почему там используется то, что может быть свободно изменено чем угодно с правами доступа к файлам.
Ну, если у вас при сборке какие то программы меняют timestamps исходников, то они могут и исходники на лету изменить.
Andrey4ik
Ну я сделал для себя шаблончик на мейке который build (в .exe) запускает скрипт (run) а так же deploy (Inno Setup например)
Если что, на питоне)