Make выбирает один и тот же исходный файл для разных объектных файлов. Оба представляют собой список файлов, только с разными именами файлов. Делайте переключения между объектными файлами, но не исходными файлами.
Я уже пробовал некоторые ответы на StackOverflow со связанными проблемами, хотя эти решения либо кажутся слишком сложными для того, что нужно, некоторые не работают, а другим нужно, чтобы файлы находились в одном каталоге.
Я также пытался скомпилировать файлы вместе за один раз (с помощью gcc), но это создает некоторые проблемы со связыванием остальной части файла.
$(OBJFILES): $(SRCFILES)
$(CC) $(CCFLAGS) -c $< -o $@
$(OBJFILES) содержит следующие файлы:
src/kernel.o src/screen/screen_basic.o
И $(SRCFILES) содержит эти файлы:
src/kernel.c src/screen/screen_basic.c
По сути, src/kernel.c компилируется как в src/kernel.o, так и в src/screen/screen_basic.o, а src/screen/screen_basic.c никогда не компилируется.
Что запускается make (я заменил параметры gcc именами переменных, чтобы сделать их краткими):
i686-elf-gcc $(CFLAGS) $(WARNINGS) -c src/kernel.c -o src/kernel.o
i686-elf-gcc $(CFLAGS) $(WARNINGS) -c src/kernel.c -o src/screen/screen_basic.o
Я действительно не знаю, что вам нужно, чтобы увидеть, что происходит не так. Итак, исходные файлы (все они) находятся по адресу https://github.com/m44rtn/vireo-ядро. Может быть приятно узнать, что это переписывание проекта. В предыдущей «версии» я вручную добавлял имена файлов в make-файл, и это работало отлично, но неудобно, когда вам нужно добавлять новые файлы или когда вы их перемещаете. Этот make-файл находится в основной ветке (которая больше НЕ является веткой по умолчанию).
Версия make самая новая:
GNU Make 4.2.1
Built for x86_64-pc-linux-gnu
Итак, я ожидал, что это сработает просто великолепно. Я думал, что он просто скомпилирует все файлы в списке. К сожалению, это не так. Я действительно не знаю, что здесь происходит не так.
Он компилирует kernel.c как в kernel.o, так и в screen_basic.o. Я, конечно, надеялся, что он скомпилирует kernel.c в kernel.o и screen_basic.c в screen_basic.o.
Позже эти два файла будут связаны. Однако, поскольку они одинаковы, компоновщик выдает ошибки, поскольку все определяется дважды, что не идеально.
Я попытался решить эту проблему, скомпилировав каждый файл C за один раз, но это вызвало некоторые проблемы со связыванием файлов сборки с файлами C (иногда делая его не мультизагрузочным GRUB, что необходимо иметь в моем случае).
Я не знаю, что не так с make-файлом, чтобы он вел себя так.
Все решения от переполнения стека, которые я пробовал:
Некоторые решения включают в себя перенос всех файлов в корневой каталог и просто использование:
%.o: %.c
(..)
Однако в этом проекте будет много файлов. Это делает наличие всего в одном каталоге очень раздражающим, очень быстрым. Я думаю, что это тоже не сработало, но я не знаю, правда ли это или просто мой мозг лгал мне. Извини.
Я что-то слышал о «статических правилах»:
$(OBJFILES): %.o: %.c
(..)
Это не сработало, однако я, возможно, использовал это неправильно. Я не знаю.
Я предпочитаю, чтобы make-файл оставался неизменным, насколько это возможно, потому что он очень удобен (он просто автоматически определяет все файлы).
Я действительно надеюсь, что предоставил достаточно информации, и что этот вопрос еще не задан. Если было, заранее извиняюсь.
Если вам нужна дополнительная информация, пожалуйста, спросите! :)
--РЕДАКТИРОВАТЬ-- Я новичок в этом, хотя пользуюсь им уже пять лет. Я всегда использовал его неправильно. Возможно, мой make-файл очень уродлив или плох. И я использовал пример для написания make-файла.





Рассмотрим правило...
$(OBJFILES): $(SRCFILES)
$(CC) $(CCFLAGS) -c $< -o $@
Скажем, ради аргумента, что у нас есть...
SRCFILES := a.c b.c
OBJFILES := a.o b.o
Если вы развернете правило вручную, оно станет...
a.o b.o: a.c b.c
$(CC) $(CCFLAGS) -c $< -o $@
Я думаю (поправьте меня, если я ошибаюсь), у вас сложилось впечатление, что это будет интерпретировано make как a.o зависит от a.c и, отдельно, b.o зависит от b.c. Это не так. На самом деле в нем говорится, что оба a.oиb.o зависят от обаa.c и b.c.
Итак, когда make пытается обновить цель a.o, он видит полный список предварительных требований a.c и b.c и присваивает первому из них, a.c, встроенную переменную $<. Вот почему вы всегда видите, что первый исходный файл в $(SRCFILES) компилируется.
Лучший способ решить эту проблему, вероятно, зависит от того, как вы собираетесь структурировать иерархию исходных файлов и объектные файлы, но вы можете взглянуть на использование одной или нескольких директив vpath.
упс, я не знал, что <ENTER> разместит комментарий. :) vpath кажется немного сложным, и я действительно не знаю, как его использовать. Спасибо, в любом случае! :)
Правило шаблона не помещает все объекты в корневой каталог.
CFILES := path/to/a.c b.c
OBJFILES := $(foreach f,$(CFILES),$(f:%.c=%.o))
all: $(OBJFILES)
%.o: %.c
$(CC) $(CCFLAGS) -c $< -o $@
Вот что вы получаете:
cc -c path/to/a.c -o path/to/a.o
cc -c b.c -o b.o
Следующее является не рекомендацией, а своего рода упражнением в программировании makefile.
Если у вас есть $(SRCFILES) и вы хотите скомпилировать их по одному, вы можете использовать:
define compile
$1.o: $1.c
$(CC) $(CCFLAGS) -c $$< -o $$@
endef
$(foreach src,$(SRCFILES),$(eval $(call compile, $(src:%.c=%))))
Если соответствие списков источников и объектов не по названию, а только по размещению в списке, можно уничтожить список CFILES
define compile
src := $(CFILES)
CFILES := $(wordlist 2, $(words $(CFILES)), $(CFILES))
$1: $(src)
$(CC) $(CCFLAGS) -c $$< -o $$@
endef
$(foreach obj,$(OBJFILES),$(eval $(call compile, $(obj))))
Или вы можете использовать вспомогательный список, чтобы сохранить CFILES без изменений:
helperlist := $(CFILES)
define compile
src := $(firstword $(helperlist))
helperlist := $(wordlist 2, $(words $(helperlist)), $(helperlist))
$1: $(src)
$(CC) $(CCFLAGS) -c $$< -o $$@
endef
$(foreach obj,$(OBJFILES),$(eval $(call compile, $(obj))))
Это странно, вместо этого вы должны использовать базовое правило шаблона.
@tripleee, вы правы, я добавил уведомление об этом
Это решило мою проблему. Я знаю, почему в прошлый раз это не сработало: я не использовал $(foreach...). Теперь компилируется корректно. Спасибо!
Да, я думал, что они оба были просто списком, который он будет повторять.