LINUX.ORG.RU

make игнорирует правила сборки

 , , ,


0

2

Всем привет. Я решил основательно разобраться в make и для этого сделать «универсальную» систему сборки для С-проектов. И столкнулся с проблемой уже на этапе компиляции исходных кодов в объектные. Make переводит только первый файл указанный в SRCS, а остальные пропускает. Тестовый проект для системы сборки выглядит так:

├── compile.mk
├── config.mk
├── Makefile
└── src
    ├── main.c
    ├── Makefile
    ├── module1
    │   ├── module1_bar.c
    │   └── module1_foo.c
    └── module2
        ├── module2_bar.c
        └── module2_foo.c

Содержимое Makefile-ов следующее:

#Makefile верхнего уровня
include config.mk

MAKEFLAGS+=--no-print-directory

all:
        @printf "\x1b[32mStart project compilation\x1b[0m\n"
        @$(foreach target,$(TARGETS), \
                printf "\x1b[32m%s\x1b[0m compilation\n" "$(target)" && \
                $(MAKE) -C $(target) clean && \
                $(MAKE) -C $(target); \
        )

clean:
        $(foreach target,$(TARGETS),$(MAKE) -C $(target) clean;)
#config.mk
export CC=clang

export PROJECT_DIR=$(shell realpath .)

export SRC_DIR=$(PROJECT_DIR)/src
export BUILD_DIR=$(PROJECT_DIR)/build
export INCLUDE_DIR=$(PROJECT_DIR)/include

export LIBS_DIR=$(shell realpath libs)
export LIBS_INCLUDE_DIRS_PATH=$(shell find $(LIBS_DIR) -type d -iname "include")

export TARGETS=$(shell find $(shell realpath .) -type f -iname "Makefile" | xargs dirname | tail -n +2)

export CFLAGS=-I$(INCLUDE_DIR) $(addprefix -I,$(LIBS_INCLUDE_DIRS_PATH))

# $(info CC $(CC))
# $(info SRC_DIR $(SRC_DIR))
# $(info BUILD_DIR $(BUILD_DIR))
# $(info INCLUDE_DIR $(INCLUDE_DIR))
# $(info CFLAGS $(CFLAGS))
#compile.mk
SRC_BASENAMES=$(notdir $(SRCS))
SRC_FULLPATH=$(realpath $(SRCS))

OBJS=$(addprefix $(BUILD_DIR)/,$(addsuffix .o,$(SRC_BASENAMES)))

# $(info SRC_FULLPATH: $(SRC_FULLPATH))
# $(info OBJS: $(OBJS))

define COMPILE_SOURCE_TO_OBJECT
$(1): $(2)
        $(info $(2) > $(1))
        $(CC) $(CFLAGS) -c $(2) -o $(1)
        @echo -e "\x1b[33m$(2)\x1b[0m compiled to \x1b[33m$(1)\x1b[0m"
endef


$(foreach i, $(shell seq 1 $(words $(SRCS))), \
        $(eval $(call COMPILE_SOURCE_TO_OBJECT, \
        $(strip $(word $(i),$(OBJS))), \
        $(strip $(word $(i),$(SRC_FULLPATH))) \
  )); \
)

$(BUILD_DIR)/$(TARGET): $(OBJS)
        printf "\x1b[32m%s\x1b[0m compiled to \x1b[32m%s\x1b[0m" "$^" "$@"


clean:
        rm -rf $(OBJS)
#Makefile в src
TARGET = main
TYPE = application

LIBS = lib1 lib2

SRCS = main.c \
                         module1/module1_foo.c \
                         module1/module1_bar.c \
                         module2/module2_foo.c \
                         module2/module2_bar.c

include ../compile.mk

all: $(BUILD_DIR)/$(TARGET)

После запуска make из корневой директории проекта получаю следующий вывод:

Start project compilation
/home/ren4/Code/make-build-system/src compilation
 /home/ren4/Code/make-build-system/src/main.c  >  /home/ren4/Code/make-build-system/build/main.c.o
 /home/ren4/Code/make-build-system/src/module1/module1_foo.c  >  /home/ren4/Code/make-build-system/build/module1_foo.c.o
 /home/ren4/Code/make-build-system/src/module1/module1_bar.c  >  /home/ren4/Code/make-build-system/build/module1_bar.c.o
 /home/ren4/Code/make-build-system/src/module2/module2_foo.c  >  /home/ren4/Code/make-build-system/build/module2_foo.c.o
 /home/ren4/Code/make-build-system/src/module2/module2_bar.c  >  /home/ren4/Code/make-build-system/build/module2_bar.c.o
rm -rf /home/ren4/Code/make-build-system/build/main.c.o /home/ren4/Code/make-build-system/build/module1_foo.c.o /home/ren4/Code/make-build-system/build/module1_bar.c.o /home/ren4/Code/make-build-system/build/module2_foo.c.o /home/ren4/Code/make-build-system/build/module2_bar.c.o
 /home/ren4/Code/make-build-system/src/main.c  >  /home/ren4/Code/make-build-system/build/main.c.o
 /home/ren4/Code/make-build-system/src/module1/module1_foo.c  >  /home/ren4/Code/make-build-system/build/module1_foo.c.o
 /home/ren4/Code/make-build-system/src/module1/module1_bar.c  >  /home/ren4/Code/make-build-system/build/module1_bar.c.o
 /home/ren4/Code/make-build-system/src/module2/module2_foo.c  >  /home/ren4/Code/make-build-system/build/module2_foo.c.o
 /home/ren4/Code/make-build-system/src/module2/module2_bar.c  >  /home/ren4/Code/make-build-system/build/module2_bar.c.o
clang -I/home/ren4/Code/make-build-system/include -I/home/ren4/Code/make-build-system/libs/lib1/include -I/home/ren4/Code/make-build-system/libs/lib2/include -c  /home/ren4/Code/make-build-system/src/main.c  -o  /home/ren4/Code/make-build-system/build/main.c.o
 /home/ren4/Code/make-build-system/src/main.c  compiled to  /home/ren4/Code/make-build-system/build/main.c.

UPD: Это не тема для того чтобы размышлять зачем я сделал так, а не иначе и почему я написал такую штуку, а не другую. А почему я не использовал что-то что в 10 раз удобнее. У меня игнорируются правила сборки. Подскажите пожалуйста почему так происходит. Это все что я спрашиваю. Я не спрашиваю какие есть альтернативы, как правильно составлять make-файлы, как мне надо учить make.

UPD2: После того как я перенес содержимое compile.mk в src/Makefile make перестал игнорировать правила сборки. Щас буду разираться почему так. Первое предположение косак в путях или make не может обработать нормально относительные пути

UPD3: Проблема оказалась в том, что include ../compile.mk указан до правила all, а не после… :(



Последнее исправление: pro100ren4 (всего исправлений: 3)

Я решил основательно разобраться в make и для этого сделать «универсальную» систему сборки для С-проектов.

make сам по себе и есть такая система. Ты повторяешь уже много раз сделанную ошибку, добавляя к скриптовой системе ещё один скриптовый слой абстракции, написанный на скриптах.

Вместо твоей портянки с генерацией правил по списку надо использовать встроенный функционал make - шаблоны правил. Для каких именно файлов их применять он сам разберётся по дереву зависимостей.

firkax ★★★★★
()

По теме.

Ошибка где-то здесь

define COMPILE_SOURCE_TO_OBJECT
# вот эта часть правила срабатывает
$(1): $(2)
        $(info $(2) > $(1))
# осальная часть отбрасывается (идет как простой текст?)
        $(CC) $(CFLAGS) -c $(2) -o $(1)
        @echo -e "\x1b[33m$(2)\x1b[0m compiled to \x1b[33m$(1)\x1b[0m"
endef

У тебя правило строиться только для первой (под)строки, остальные две строки не срабатывают. Проблема в табулациях, или чего.

anonymous
()
Ответ на: комментарий от anonymous

Типо cmake, ninja и т.п.? У меня не стоит прикладная задача, что-то сделать. Если бы мне надо было собрать какой-то проект, то я бы естественно использовал бы готовые генераторы. Сейчас я хочу просто разобраться с make как минимум потому что он используется как результат генерации cmake и т.п.

pro100ren4
() автор топика
Ответ на: комментарий от sigurd

Я попробовал и ошибка не пропадает. У меня есть предположение, что ошибка в src/Makefile где-то в, потому что вроде он генерирует правила, но просто не создает объектники по этим правилам


include ../compile.mk

all: $(BUILD_DIR)/$(TARGET)
pro100ren4
() автор топика
Ответ на: комментарий от beastie

Понимаю, что написано максимально криво, но в этом и стоит суть учебных проектов, разобраться как оно работает). Это что-то прям сильно сложное. И я так понял что это под BSD(никогда с ним не имел дел, поэтому вообще о нем информации у меня нет) и похоже и него чуть другой синтаксис make

pro100ren4
() автор топика
Последнее исправление: pro100ren4 (всего исправлений: 1)
Ответ на: комментарий от pro100ren4

Если бы мне надо было просто собрать проект я бы естественно воспользовался cmake и т.п.

Очень плохое решение. Авторы cmake сделали тоже самое что и ты - написали бесполезную мусорную скриптоабстракцию поверх других скриптов.

А чтобы разбираться в make пиши мейкфайлы для реальных проектов (которые ты будешь делать). Разбираться ради разбирательства не нужно, к тому же, судя по тому как ты это делаешь, ты таким методом научишься скорее плохому, чем реально полезным вещам.

firkax ★★★★★
()
Ответ на: комментарий от pro100ren4

Есть просто make (не знаю, может быть он posix а может и нет), и есть его расширения: gnu make (в линуксе обычно он везде) и bsd make (он соответственно в bsd). Расширения эти друг с другом во многом не совместимы, да. Но оба умеют работать с файлами для простого make.

Твои foreach, подозреваю, gnu-расширение.

firkax ★★★★★
()
Ответ на: комментарий от pro100ren4

Сейчас я хочу просто разобраться с make как минимум потому что он используется как результат генерации cmake

Cmake не генерирует такие «сложные тьюринг-полные» makefile’ы. Он генерирует простые правила. В (Gnu)Make слишком неудобный скриптовый язык (для генерации правил). Это настолько write-only язык, что потом твои скрипты никто не будет использовать. Даже сам через месяц забудешь, как использовать, не говоря о том, как редактировать.

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

anonymous
()

make ради make? в чём смысл? есть готовые генераторы. чем углубляться во внутрянку для самоудовлетворения лучше потратить время на изучение каких-нибудь новых предметных областей

eeiaao
()
Ответ на: комментарий от pro100ren4

Можно ли посмотреть какие правила сгенерированы?

Вы про make --just-print и make --print-data-base?
Вот неплохая статья про отладку makefile:
https://habr.com/ru/articles/534128/
https://habr.com/ru/articles/534304/

sigurd ★★★★★
()
Последнее исправление: sigurd (всего исправлений: 1)

Честно сказать не очень понимаю какую задачу пытался решить автор

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

nikitalol ★★
()

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

Пользоваться так: положить файл в корень, распихать файлы по указанным папкам, и обозвать в шапке всё как нужно

Файл сам подцепит все файлы в сорцах на 2 уровня и автоматом пересоберёт что нужно

Файл не учитывает массу тонких моментов, не линкует so-шники и a-шники, только компилируемые к проекту, не умеет делать отдельно папки debug и release, не тестировался и вообще изначально писался для микроконтроллеров, на скорую руку передал в обычный

Решена важная проблема самописных мейкфайлов: они часто не чувствуют неизменений в хедерах

# Path you your toolchain installation, leave empty if already in system PATH
# TOOLCHAIN_PATH =
###############################################################################

# Project specific
SRC_DIR = Src
INC_DIR = Inc
BUILD_DIR = Build
DEBUG_DIR = Debug
RELEASE_DIR = Release
OBJ_DIR = Obj
TARGET = app
# Some_nonebin_Lib = 

# Toolchain
ifdef TOOLCHAIN_PATH
CC = $(TOOLCHAIN_PATH)/g++
AS = $(TOOLCHAIN_PATH)/g++ -x assembler-with-cpp
CP = $(TOOLCHAIN_PATH)/objcopy
SZ = $(TOOLCHAIN_PATH)/size -d -G
else
CC = g++
AS = g++ -x assembler-with-cpp
CP = arm-none-eabi-objcopy
SZ = arm-none-eabi-size -d -G
endif

# Project sources
CXX_FILES = $(wildcard $(SRC_DIR)/*.c) $(wildcard $(SRC_DIR)/*/*.c)
CPP_FILES = $(wildcard $(SRC_DIR)/*.cpp) $(wildcard $(SRC_DIR)/*/*.cpp)
ASM_FILES = $(wildcard $(SRC_DIR)/*.s) $(wildcard $(SRC_DIR)/*/*.s)
# LD_SCRIPT =
# Project includes
INCLUDES   = -I$(INC_DIR)

ifdef Some_nonebin_Lib
CXX_FILES += $(wildcard $(Some_nonebin_Lib)/*.c)
CPP_FILES += $(wildcard $(Some_nonebin_Lib)/*.cpp)
INCLUDES  += -I$(Some_nonebin_Lib)
endif
# Compiler Flags

# GCC

# ifdef DEBUG
# $(info [info] debug mode)
# CFLAGS  = -g -Og
# BUILD_MODE = $(DEBUG_DIR)
# else
# $(info [info] nodebug mode)
# CFLAGS  = -O2
# BUILD_MODE = $(RELEASE_DIR)
# endif
CFLAGS = -Wall -Wextra -Warray-bounds
CFLAGS += -std=c11
CFLAGS += -lc -lgcc -lm
# Generate dependency information
CFLAGS += -MMD -MP
#  -MF"$(@:%.o=%.d)"
# CFLAGS += -mfloat-abi=hard -mfpu=fpv4-sp-d16
CFLAGS += -Wno-comment -Wno-unused-parameter
# PROJECT

# Linker Flags
# LFLAGS = -Wl,--gc-sections -Wl,-T$(LD_SCRIPT) --specs=rdimon.specs

###############################################################################

# Unlike the original source, this file throws object files into the correct directory.
OBJECTS  = $(addprefix $(BUILD_DIR)/$(OBJ_DIR)/,$(notdir $(CXX_FILES:.c=.o)))
OBJECTS += $(addprefix $(BUILD_DIR)/$(OBJ_DIR)/,$(notdir $(CPP_FILES:.cpp=.o)))
OBJECTS += $(addprefix $(BUILD_DIR)/$(OBJ_DIR)/,$(notdir $(ASM_FILES:.s=.o)))
DEPENDS  = $(addprefix $(BUILD_DIR)/$(OBJ_DIR)/,$(notdir $(CXX_FILES:.c=.d)))
DEPENDS += $(addprefix $(BUILD_DIR)/$(OBJ_DIR)/,$(notdir $(CPP_FILES:.cpp=.d)))
DEPENDS += $(addprefix $(BUILD_DIR)/$(OBJ_DIR)/,$(notdir $(ASM_FILES:.s=.d)))

.PHONY: clean
vpath %.c $(sort $(dir $(CXX_FILES)))
vpath %.cpp $(sort $(dir $(CPP_FILES)))
vpath %.s $(sort $(dir $(ASM_FILES)))
# .PHONY: clean
all: CFLAGS += -O2
all: $(BUILD_DIR)/$(TARGET)
# all: make
debug: CFLAGS += -g -Og -DDEBUG
debug: $(BUILD_DIR)/$(TARGET)

-include $(DEPENDS)
# Compile asm
$(BUILD_DIR)/$(OBJ_DIR)/%.o: %.s Makefile | $(BUILD_DIR)/$(OBJ_DIR)
	@echo "[AS] $< -> $@"
	@$(AS) -c $< -o $@ $(CFLAGS)
# Compile cpp
$(BUILD_DIR)/$(OBJ_DIR)/%.o: %.cpp Makefile | $(BUILD_DIR)/$(OBJ_DIR)
	@echo "[CC] $< -> $@"
	@$(CC) -c $< -o $@ $(CFLAGS)

# Compile c
$(BUILD_DIR)/$(OBJ_DIR)/%.o: %.c Makefile | $(BUILD_DIR)/$(OBJ_DIR)
	@echo "[CC] $< -> $@"
	@$(CC) -c $< -o $@ $(CFLAGS)

# Link
$(BUILD_DIR)/$(TARGET): $(OBJECTS) Makefile
	@echo "[LD] $@"
	@$(CC) -o $@ $(CFLAGS) $(OBJECTS)
	@$(SZ) $@

# Make directory
# $(BUILD_DIR)/$(DEBUG_DIR)/$(OBJ_DIR): | $(BUILD_DIR)/$(DEBUG_DIR)
# 	@mkdir $@

# $(BUILD_DIR)/$(RELEASE_DIR)/$(OBJ_DIR): | $(BUILD_DIR)/$(RELEASE_DIR)
# 	@mkdir $@

# $(BUILD_DIR)/$(DEBUG_DIR): | $(BUILD_DIR)
# 	@mkdir $@

# $(BUILD_DIR)/$(RELEASE_DIR): | $(BUILD_DIR)
# 	@mkdir $@


$(BUILD_DIR)/$(OBJ_DIR): | $(BUILD_DIR)
	@mkdir $@

$(BUILD_DIR):
	@mkdir $@

# Clean
clean:
	@rm $(BUILD_DIR)/$(TARGET)* $(BUILD_DIR)/$(OBJ_DIR)/*
# @rm $(BUILD_DIR)/$(DEBUG_DIR)/$(TARGET)* \
# 	$(BUILD_DIR)/$(RELEASE_DIR)/$(TARGET)* \
# 	$(BUILD_DIR)/$(DEBUG_DIR)/$(OBJ_DIR)/* \
# 	$(BUILD_DIR)/$(RELEASE_DIR)/$(OBJ_DIR)/*

nikitalol ★★
()
Последнее исправление: nikitalol (всего исправлений: 2)
Ответ на: комментарий от sigurd

Попробуйте SRCS= записать в одну строку без переносов. (make не любит отступов пробелами).

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

debugger ★★★★★
()
Ответ на: комментарий от pro100ren4

Я всё ещё не понимаю какую задачу ты пытаешься решить

Вот прямо совсем не понимаю

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

make, ну в общем есть места где он удобен, но точно не дефолтное приложение

nikitalol ★★
()
Ответ на: комментарий от debugger

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

Нехорошо вырывать цитату из контекста! Я там говорил о плохом форматировании списка SRCS=. Я реально сталкивался с ситуацией, когда make игнорировал команды, которые не начинались с табуляции.
И не вижу ваших советов и предложений автору вопроса - вы сюда просто потролить зашли?

sigurd ★★★★★
()
Последнее исправление: sigurd (всего исправлений: 1)
Ответ на: комментарий от sigurd

Я реально сталкивался с ситуацией, когда make игнорировал команды, которые не начинались с табуляции.

Но ты же не понял, почему это произошло. А меж тем в документации написано английским по белому:

Please note: you need to put a tab character at the beginning of every recipe line!

У тебя была команда, которая обязана начинаться с табуляции, а у ТС — присваивание значения переменной, которое не является командой, и не обязано начинаться с табуляции.

И не вижу ваших советов и предложений автору вопроса…

Когда даёшь советы, убедись, что ты разбираешься в вопросе, чтобы давать советы дельные. Иначе получаются не советы, а деза. Мне лень разбираться в коде ТС, поэтому ему я советов и не давал — советчиков помимо меня здесь достаточно.

debugger ★★★★★
()
Последнее исправление: debugger (всего исправлений: 2)
Ответ на: комментарий от nikitalol

Просто система сборки на make, которая позволяет одним файлом конфигурировать проект, а именно я просто хочу кинуть в папку проекта Makefile в котором напишу какие в нем исходники от каких библиотек он зависит и чтобы он сам уже занялся сборкой исходников, линковкой сборкой библиотек и т.п. Проект написан исключительно с целью разобраться в «серьезных» make-системах. Просто проект из интереса. Если мне надо будет писать серьезный С-проект я возьму cmake и т.п. и просто соберу с помощью него. А так это интереса ради сделано.

pro100ren4
() автор топика