LINUX.ORG.RU

Правило не создаёт файл, но зависимости выполняются как будто файл обновлён.

 ,


0

1
$ cat makefile
b: a
        cp a b
a:
        # noop

$ rm -f a; touch b; make
# noop
cp a b
cp: cannot stat 'a': No such file or directory
make: *** [makefile:2: b] Error 1

По правилу a понятно: target file не существует – выполняем команды. Что команд нет – не наши проблемы.

А по правилу b не понятно ни черта. Оба правила не .PHONY, файл b существует, a не существует – и правило тем не менее выполняется.

Либо make после отработки правила a вообще не считывает mtime(a) повторно, а тупо берёт текущее время – но это крайне дурацкое кроилово для тулзы, которая на каждый чих в шел форкается. Собственно, я тут вообще глупость написал: что mtime, что текущее время – один syscall.

Либо же явно закодировано: если файл-зависимость не существует после выполнения правила-зависимости, то выполняем зависимое как будто его mtime < mtime зависимости. А нахрена?

★★★★★

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

Правило a выполнилось и не вернуло ошибок, значит оно сделало всё что нужно и готово. А дата у файла это закешированный ответ «готово».

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

Вот допустим есть у нас hello.c, hello.o и hello. Если правило .o: .c отработало, но при этом hello.o не перекомпилялся потому что hello.c не изменился, то и hello из hello.o линковать не надо. Все правила отработали без ошибок, но и без выполнения команд. И на каждом шаге сравнивались mtime. А если mtime у .o: .c – закешированный ответ «готово», то бинарник будет пересобираться всегда.

И например вот это описание алгоритма работы make тоже как-то и с вашими ответами, и с описанным мною странным поведением слабо согласуется.

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

Мало ли что там написано.

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

В ходе пересборки проверки не выполняются.

Выше правильно сказали, нужно возвращать код ошибки, чтобы остановить сборку.

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

Например, у меня в одном из проектов make сначала вызывается для компиляции сишных хидеров из файлов с определениями API на отдельном DSL. Команда, выполняющая компиляцию, специально сделана так, что не перезаписывает старые хидеры новыми, если они совпали.

Затем второй вызов make компилирует остальной код с использованием этих хидеров.

Это позволяет не перекомпилировать весь проект, если в определениях API кто-нибудь поправил комменты.

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

Ага, вот это интересно, спасибо.

Команда, выполняющая компиляцию, специально сделана так, что не перезаписывает старые хидеры новыми, если они совпали.

Наш человек. :)

В ходе пересборки проверки не выполняются.

Но почему? Один лишний syscall на цель погоды не сделает, не? Ведь два вызова make подряд – это по сути костыль вокруг корявого решения.

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

Но почему? Один лишний syscall на цель погоды не сделает, не? Ведь два вызова make подряд – это по сути костыль вокруг корявого решения.

Вокруг make вообще полно костылей наделали, одним больше одним меньше уже погоды не сделает (вобщем-то твоё «приверим что .c не изменился и оставим .o старую дату» - тоже костыль). Если сделать как ты хочешь то сломается чей-то ещё костыль, а может и не только - потому что это более крупное изменение чем тебе кажется (см. ниже).

Это не «лишний syscall», а вообще совсем другой порядок принятия решений. Сейчас ты пишешь make b и он сначала решает, нужно ли ему пересобирать b или нет. Если не нужно - сразу выход, если нужно - сначала рекурсивно проверяет не нужно ли пересобрать зависимости, затем пересобирает b. То есть, либо вообще ничего не трогается, либо решение о пересборке принято в самом начале, и только после него делается make a. Ты же хочешь чтобы он сначала пересобрал a (заранее не зная, нужен ли он ему вообще), а потом, может быть, решил пересобрать b.

Чтобы сделать по-нормальному, надо сделать свой make, гарантированно несовместимый с имеющимися и как-то его протолкнуть в массовое использование. Ну или не проталкивать а только самому использовать. У меня уже не раз было такое желание, но ничем не закончилось. Потому что всё продумать, чтоб потом не пришлось оборачивать пачками костылей - тоже непросто.

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

вобщем-то твоё «приверим что .c не изменился и оставим .o старую дату» - тоже костыль

Схренали? Это фундаментальная идея make: пересобирать что-то только если зависимости свежее. Наверное я просто криво выразился.

Сейчас ты пишешь make b и он сначала решает, нужно ли ему пересобирать b или нет. Если не нужно - сразу выход, если нужно - сначала рекурсивно проверяет не нужно ли пересобрать зависимости, затем пересобирает b.

ИМХО всё строго наоборот, а именно deep first. Мы не можем принять решение о пересборе b до тех пор, пока не определимся, нужно ли пересобирать его зависимости. Мы не можем решить, надо ли перелинковывать бинарник на основании сравнения его mtime с объектником, не решив сначала, нужно ли перекомпилять объектник на основании сравнения его mtime с исходником.

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

Чтобы сделать по-нормальному, надо сделать свой make […] У меня уже не раз было такое желание, но ничем не закончилось. Потому что всё продумать, чтоб потом не пришлось оборачивать пачками костылей - тоже непросто.

Бгг, а это идея. Давайте продумаем. Итак: если правило не phony, значит это файл, и по результатам работы правила файл должен существовать. Т.е. вызываем statx(2) на файл дважды: при принятии решения, нужно ли выполнять команды правила, и в конце выполнения команд чтобы считать честный mtime или упасть если файл не существует. Вот это ужесточение требований мне видится ключевым средством от бардака в поведении. Я вообще люблю гайки закручивать. Возражения?

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

Ну вот тут например правило .o: .c будет отрабатывать каждый раз при запуске make, даже если ничего не менять, ведь make будет считать .o устаревшим. И каждый раз твой костыль будет предотвращать компиляцию, но поскольку это именно костыль - make об этом ничего не знает. А мог бы знать, если б это его внутренняя проверка была. А если .c изменился, но .o не изменился (а дата компиляции новая стала)? Тоже ж не надо линковать.

Можно в правило для линковки добавить костыль: сравнивать сначала дату .o с бинарником, и если дата .o больше - сравнивать ещё и хеш .o с где-то сохранённым, как ещё один шанс пропустить линковку. Правда если везде такое сделать, то алгоритмы make для выбора что именно пересобирать окажутся немного отодвинуты, вместо них будут эти.

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

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

Не понял. Где – тут?

А если .c изменился, но .o не изменился (а дата компиляции новая стала)?

Это как? В .c изменились только комментарии? Типа как @wandrien выше написал? Ну так и надо для этого проверять mtime в конце работы каждого правила, а не один раз при запуске make.

алгоритмы make для выбора что именно пересобирать окажутся немного отодвинуты

Во-во. Это ты какую-то уже дичь предлагаешь. Не нужно так делать.

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

Схренали? Это фундаментальная идея make: пересобирать что-то только если зависимости свежее. Наверное я просто криво выразился.

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

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

А, ну пусть так, но главное - сначала всё решаем, потом всё (что решили) пересобираем. Вполне допускаю, что есть много Makefile-ов, с неаккуратно прописанными зависимостями, но тем не менее работающих корректно именно благодаря этой логике. Хотя, если речь про самое популярное «скачал - собрал - удалил исходники» то они всегда будут корректно работать.

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

Не понял. Где – тут?

В комменте на который я отвечал.

Это как? В .c изменились только комментарии?

Ну например. Впрочем да, тогда можно как он и написал - проверять что ничего не поменялось и ставить старую дату .o после этого.

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

а хеши он не сравнивает.

И не может, и не должен. Какие хеши должно сравнивать правило .o: .c? Хеш исходника с хешом объектника? Метры с килограммами то бишь. :) Кешировать хеши – это конечно тоже идея, надо подумать; хотя по ощущениям это будет очень коряво и ненадёжно. Да и в общем случае не особо и нужно. В частных случаях я и сам закостылю. Идея make хороша тем, что она предельно тупая. Почему тут многие ругают cmake в пользу make? Потому что make на порядок проще и прямолинейнее. Я вообще удивлён, как в реализации такой простой идеи могли возникнуть корявости типа обсуждающейся.

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

В комменте на который я отвечал.

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

Тем более не понял. Если однажды .o скомпилят из .c, и .c с тех пор не менялся, то схренали make будет считать .o устаревшим?

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

Если без хешей, то это всё тогда означало?

Правило не создаёт файл, но зависимости выполняются как будто файл обновлён. (комментарий)

Если ненужность перекомпиляции hello.c распознал сам make по датам, то и линковать он разумеется тоже не будет, по датам.

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

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

Блин, а табуляции и в целом кривой синтаксис тебя не удивляют)

Тут всё просто, make делалась в стародавние времена как хак на скорую руку.

А потом стала швятым дефолтом, с которым все последующие решения должны быть совместимы.

Решение тут только одно: запилить принципиально новый make, с новым синтаксисом и семантикой работы, но сохранив простоту конструкции.

Но чо-т у современных программистов получается только cmake по итогу…

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

Тут всё просто, make делалась в стародавние времена как хак на скорую руку.

Да, было такое подозрение. Чисто по индукции, помнится про bash то же самое перетирали. :)

запилить принципиально новый make, с новым синтаксисом и семантикой работы

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

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

Блин, а табуляции и в целом кривой синтаксис тебя не удивляют)

Кстати! А питон тоже делался в стародавние времена? :)

dimgel ★★★★★
() автор топика

Вероятнее всего такое поведение существует потому, что make clean появился раньше чем .phony

khrundel ★★★★
()
19 октября 2022 г.

@firkax, @wandrien

  1. В сабже происходит примерно вот что:
  • Правило «a:» без пререквизитов и без файла на диске таки-считается как .PHONY, и соответственно выполняется и оно само, и его зависимости.

  • И на это поведение дохрена всего завязано, что суммируется в Advanced Auto-Dependency Generation. Основная идея там такая: нам не нужен актуальный .d-файл, нам достаточно такого, чтобы триггернул перекомпиляцию.

  • В частности: если у нас есть main.cpp, который инклудит incl.h, то генерируемый gcc-ёй .d-файл будет таким:

main.o: main.cpp incl.h
main.cpp:
incl.h:

Если какой-то из исходных файлов main.cpp, incl.h пропал, то их пустые правила триггернутся как .PHONY, что приведёт к перекомпиляции main.o

  1. Я таки-запилил хэш-кеши. Оказалось довольно чистенько и простенько, т.е. вполне надёжно: хеши проверяем только для пререквизитов, но не для целей. А у цели беру актуальный mtime до и после её компиляции. Хвастаюсь без сорцов (и даже без скринов; эх, а какие у меня цвета префиксов красивые подобраны!):
[demo]$ ls
cake  cakefile.cpp  cake-hashcache  README.md  src  target

[demo]$ ./cake clean

[demo]$ ls target/
cakefile

[demo]$ ./cake-hashcache ls
EE Cache file not found.

[demo]$ time ./cake -v
>> target/cakefile -v
CC target/build/greeter/greeter.o
CC target/build/main.o
{} mkdir -p target/build
{} mkdir -p target/build/greeter
>> g++ -fno-rtti -march=x86-64 -O2 -flto=auto -s -MMD -MP -c -o target/build/main.o src/main.cpp
>> g++ -fno-rtti -march=x86-64 -O2 -flto=auto -s -MMD -MP -c -o target/build/greeter/greeter.o src/greeter/greeter.cpp
LL target/hello
>> g++ -fno-rtti -march=x86-64 -O2 -flto=auto -s -o target/hello target/build/main.o target/build/greeter/greeter.o

real    0m0.131s
user    0m0.143s
sys     0m0.016s

[demo]$ target/hello world
Hello world!

[demo]$ ./cake-hashcache ls
2 entries
d8c17693514b1f28eb0bb77a0956e685  2022-10-19 20:56:33.808249551  4160  target/build/greeter/greeter.o
4c80b65c06cf59e6cc319ddeeb14e90e  2022-10-19 20:56:33.808249551  6088  target/build/main.o

[demo]$ time ./cake -v
>> target/cakefile -v

real    0m0.004s
user    0m0.000s
sys     0m0.005s

[demo]$ ./cake-hashcache ls
7 entries
ffda614df6ecf3c19cee22d5b2f55989  2022-10-15 08:53:16.048930696   105  src/greeter/greeter.cpp
68318d810aae5485626518b3a8c5081d  2022-10-15 08:52:19.588150385    44  src/greeter/greeter.h
7870fb12a712dbf5751730b8b8da7ffe  2022-10-19 20:52:00.434276397   197  src/main.cpp
68cd1646da7a1f27c2df8fa5c3d53b4f  2022-10-19 20:56:33.808249551   104  target/build/greeter/greeter.d
d8c17693514b1f28eb0bb77a0956e685  2022-10-19 20:56:33.808249551  4160  target/build/greeter/greeter.o
bda20173f0bc8fb367cc92c16c602736  2022-10-19 20:56:33.808249551    79  target/build/main.d
4c80b65c06cf59e6cc319ddeeb14e90e  2022-10-19 20:56:33.808249551  6088  target/build/main.o

[demo]$ ./cake -vv | grep -P 'HashCache.*main.cpp'
// HashCache: mtime & size not changed: `src/main.cpp`

[demo]$ touch src/main.cpp

[demo]$ ./cake -vv | grep -P 'HashCache.*main.cpp'
// HashCache: returning cached mtime: `src/main.cpp`

[demo]$ ./cake-hashcache rm src/main.cpp

[demo]$ ./cake
CC target/build/main.o
LL target/hello
dimgel ★★★★★
() автор топика
Последнее исправление: dimgel (всего исправлений: 3)
Ответ на: комментарий от LINUX-ORG-RU

Гы. :) Да чёт и так и эдак х@# к носу прикладывал, а потом плюнул. Хз имеет ли смысл там какая-либо лицензия вообще.

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

Напиши её сам, мол «разрешаю ознакомиться». Всё вот тебе и лицензия. Такая же лицензия как и любая другая, ппосто оч коротенькая и понятная )

Или да, вообще без. Или исходники гони,а то засужу )))))))))

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

Такая же лицензия как и любая другая

…например GPL-3. :)

Или исходники гони,а то засужу )))))))))

Засуживай. :)

Оно хоть запускается?

Кстати отдельным приколом является вывод ./cake -vv clean. Хоть команды {} rm -rf он, собака такая, и не показывает.

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

Малость допилено до вящей красоты.

dimgel ★★★★★
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.