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)

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

sigurd ★★★★★
()

Я решил основательно разобраться в 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
()

Не по теме.

Зачем писать (псевдо)генератор мейкфайлов на (gnu)make? Используй готовые генераторы.

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

Проблема в отдельной build директории, чтобы не мусорить в исходниках. Не так-то просто написать.

anonymous
()

возьми cmake. серьезные дядьки с gnu make не возятся давно.

тем, чем ты занимаешься сейчас, занимались лет 30 назад и лили сльозы.

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

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

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

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


include ../compile.mk

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

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

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)

Про флаг  -d  у make в курсе?

anonymous
()

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

Могу разве что ссылку на свой шаблонный проект для 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 ★★★★★
()
Ответ на: комментарий от nikitalol

Вы мне подкинули идею. Ща попробую просто весь код в один файл скинуть. Думаю так как минимум будет проще отлаживать

pro100ren4
() автор топика
Ответ на: комментарий от 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
() автор топика
Для того чтобы оставить комментарий войдите или зарегистрируйтесь.