Makefile запускает команду, казалось бы, случайным образом

#makefile #x86 #gnu-make

#makefile #x86 #gnu-make

Вопрос:

Итак, у меня есть проект с GNU make. Иногда, если я выполняю make all , он выполняет сборку, но после этого снова создает файл символов отладки, а иногда он просто отлично работает и выдает отчеты Nothing to be done for 'all' .

Работает так, как ожидалось:

 [cad@cordev kernel32]$ make clean
make -C boot clean
make[1]: Entering directory '/home/cad/Desktop/kernel/kernel32/boot'
rm -f main.o boot.o floppy_errs.o install_ints.o drivers/i8259A.o drivers/i8042o drivers/vga.o  *.ld.m4
make[1]: Leaving directory '/home/cad/Desktop/kernel/kernel32/boot'
make -C kernel clean
make[1]: Entering directory '/home/cad/Desktop/kernel/kernel32/kernel'
make[1]: Nothing to be done for 'clean'.
make[1]: Leaving directory '/home/cad/Desktop/kernel/kernel32/kernel'
rm -f *.out *.img *.sym

[cad@cordev kernel32]$ make all
make -C boot all
make[1]: Entering directory '/home/cad/Desktop/kernel/kernel32/boot'
m4 ../m4/global.m4 m4/boot.m4 m4/asm.m4 ../m4/asm.m4 main.S  | 
as -o main.o --32 -g
m4 ../m4/global.m4 m4/boot.m4 m4/asm.m4 ../m4/asm.m4 boot.S  | 
as -o boot.o --32 -g
m4 ../m4/global.m4 m4/boot.m4 m4/asm.m4 ../m4/asm.m4 floppy_errs.S  | 
as -o floppy_errs.o --32 -g
m4 ../m4/global.m4 m4/boot.m4 m4/asm.m4 ../m4/asm.m4 install_ints.S  | 
as -o install_ints.o --32 -g
m4 ../m4/global.m4 m4/boot.m4 m4/asm.m4 ../m4/asm.m4 drivers/i8259A.S  | 
as -o drivers/i8259A.o --32 -g
m4 ../m4/global.m4 m4/boot.m4 m4/asm.m4 ../m4/asm.m4 drivers/i8042.S  | 
as -o drivers/i8042.o --32 -g
m4 ../m4/global.m4 m4/boot.m4 m4/asm.m4 ../m4/asm.m4 drivers/vga.S  | 
as -o drivers/vga.o --32 -g
m4 ../m4/global.m4 m4/boot.m4 boot.ld  > boot.ld.m4
ld -T boot.ld.m4 main.o boot.o floppy_errs.o install_ints.o drivers/i8259A.o drvers/i8042.o drivers/vga.o  -melf_i386
make[1]: Leaving directory '/home/cad/Desktop/kernel/kernel32/boot'
objcopy --only-keep-debug boot.out boot.sym
objcopy --strip-debug --strip-unneeded boot.out
objcopy -O binary boot.out boot.img

[cad@cordev kernel32]$ make all
make: Nothing to be done for 'all'.
  

Работает странно:

 [cad@cordev kernel32]$ make clean
make -C boot clean
make[1]: Entering directory '/home/cad/Desktop/kernel/kernel32/boot'
rm -f main.o boot.o floppy_errs.o install_ints.o drivers/i8259A.o drivers/i8042o drivers/vga.o  *.ld.m4
make[1]: Leaving directory '/home/cad/Desktop/kernel/kernel32/boot'
make -C kernel clean
make[1]: Entering directory '/home/cad/Desktop/kernel/kernel32/kernel'
make[1]: Nothing to be done for 'clean'.
make[1]: Leaving directory '/home/cad/Desktop/kernel/kernel32/kernel'
rm -f *.out *.img *.sym

[cad@cordev kernel32]$ make all
make -C boot all
make[1]: Entering directory '/home/cad/Desktop/kernel/kernel32/boot'
m4 ../m4/global.m4 m4/boot.m4 m4/asm.m4 ../m4/asm.m4 main.S  | 
as -o main.o --32 -g
m4 ../m4/global.m4 m4/boot.m4 m4/asm.m4 ../m4/asm.m4 boot.S  | 
as -o boot.o --32 -g
m4 ../m4/global.m4 m4/boot.m4 m4/asm.m4 ../m4/asm.m4 floppy_errs.S  | 
as -o floppy_errs.o --32 -g
m4 ../m4/global.m4 m4/boot.m4 m4/asm.m4 ../m4/asm.m4 install_ints.S  | 
as -o install_ints.o --32 -g
m4 ../m4/global.m4 m4/boot.m4 m4/asm.m4 ../m4/asm.m4 drivers/i8259A.S  | 
as -o drivers/i8259A.o --32 -g
m4 ../m4/global.m4 m4/boot.m4 m4/asm.m4 ../m4/asm.m4 drivers/i8042.S  | 
as -o drivers/i8042.o --32 -g
m4 ../m4/global.m4 m4/boot.m4 m4/asm.m4 ../m4/asm.m4 drivers/vga.S  | 
as -o drivers/vga.o --32 -g
m4 ../m4/global.m4 m4/boot.m4 boot.ld  > boot.ld.m4
ld -T boot.ld.m4 main.o boot.o floppy_errs.o install_ints.o drivers/i8259A.o drvers/i8042.o drivers/vga.o  -melf_i386
make[1]: Leaving directory '/home/cad/Desktop/kernel/kernel32/boot'
objcopy --only-keep-debug boot.out boot.sym
objcopy --strip-debug --strip-unneeded boot.out
objcopy -O binary boot.out boot.img

[cad@cordev kernel32]$ make all
objcopy --only-keep-debug boot.out boot.sym # HERE

[cad@cordev kernel32]$ make all
make: Nothing to be done for 'all'.
  

(Я пометил соответствующую строку # HERE . Я также вставил пустые строки для улучшения читаемости.)

Дело в том, что это происходит, по-видимому, случайным образом. На самом деле, эти два примера, которые я привел, произошли последовательно.
Я заметил, что более длительное время между make clean , make all и make all иногда приводило к избыточному этапу сборки, тогда как выполнение команд совместно никогда этого не вызывало. Я просто попробовал, как 10x, хотя.

Makefile выглядит следующим образом:

 # Parameters passed to GNU make.
# `dbg=y|n': decides whether debugging mode is on or not.
# `sep_boot=y|n': decides whether bootloader and kernel should be seperate
# images or not.
# TODO: add functionality for these parameters

SHELL = /bin/bash

%.sym: %.out
        objcopy --only-keep-debug $< $@

%.img: %.out
        objcopy --strip-debug --strip-unneeded $<
        objcopy -O binary $< $@

%.out:
        make -C $* all

# TODO: merge run and debug targets and decide to debug or not using the dbg
# parameter.

boot_debug: boot.sym boot.img
        qemu-system-i386 -fda boot.img -s -S amp;
        gdb -x dbg.gdb

all: boot.sym boot.img #kernel.img kernel.sym

boot.sym boot.img: boot.out

kernel.sym kernel.img: kernel.out


clean:
        make -C boot clean
        make -C kernel clean
        rm -f *.out *.img *.sym


.PHONY: boot_debug all clean
  

Если вы хотите воспроизвести самостоятельно, клон git можно загрузить здесь.

Итак, в чем здесь проблема? И где корреляция между дополнительным шагом сборки и временем, прошедшим между командами (или это просто неправильное наблюдение)? GNU make полагается на временные метки для определения новейшей версии файла… может быть, это как-то связано?

Ответ №1:

Ну, первая строка рецепта этого правила:

 %.img: %.out
        objcopy --strip-debug --strip-unneeded $<
        objcopy -O binary $< $@
  

изменяет свое предварительное условие ( boot.out ), которое обновляет его временную метку. В зависимости от того, когда именно это произойдет, какая у вас файловая система (например, поддерживает ли она временные метки с точностью до секунды) и т.д., Может случиться так, что после выполнения этого правила временная метка для boot.out теперь имеет более новую временную метку, чем ранее созданная boot.sym , и поэтому при повторном запуске make она решит перестроить boot.sym .

Вам нужно переписать это правило таким образом, чтобы оно не изменяло предварительное условие; возможно:

 %.img: %.out
        objcopy --strip-debug --strip-unneeded $< $<.tmp
        objcopy -O binary $<.tmp $@
        rm -f $<.tmp
  

Я не уверен, будет ли objcopy записывать в стандартный вывод / считывать из стандартного интерфейса, но если это так, вы можете использовать конвейер вместо этого.