Посмотрите на этот make-файл, в нем есть какая-то примитивная индикация прогресса (могла быть индикатором выполнения).
Пожалуйста, дайте мне предложения / комментарии по этому поводу!
# BUILD is initially undefined
ifndef BUILD
# max equals 256 x's
sixteen := x x x x x x x x x x x x x x x x
MAX := $(foreach x,$(sixteen),$(sixteen))
# T estimates how many targets we are building by replacing BUILD with a special string
T := $(shell $(MAKE) -nrRf $(firstword $(MAKEFILE_LIST)) $(MAKECMDGOALS) \
BUILD = "COUNTTHIS" | grep -c "COUNTTHIS")
# N is the number of pending targets in base 1, well in fact, base x :-)
N := $(wordlist 1,$T,$(MAX))
# auto-decrementing counter that returns the number of pending targets in base 10
counter = $(words $N)$(eval N := $(wordlist 2,$(words $N),$N))
# BUILD is now defined to show the progress, this also avoids redefining T in loop
BUILD = @echo $(counter) of $(T)
endif
# dummy phony targets
.PHONY: all clean
all: target
@echo done
clean:
@rm -f target *.c
# dummy build rules
target: a.c b.c c.c d.c e.c f.c g.c
@touch $@
$(BUILD)
%.c:
@touch $@
$(BUILD)
Все предложения приветствуются!
Я могу. На Gentoo это было бы хорошо. cmake имеет встроенный счетчик [файл x / n файлов]. Но было бы неплохо иметь индикатор выполнения без переполнения экрана каждой командной строкой.





Хороший трюк! (-:
Но не очень масштабируемый для растущих проектов, которые распределены по множеству каталогов с большим количеством make-файлов.
Я был бы более склонен к тому, чтобы журналирование было пропущено через [Mm] akefiles * в вашем проекте и использовалось для отслеживания прогресса.
Просто мысль. Кстати, спасибо, что поделились этим.
Редактировать: Просто подумал. Это может быть полезно в модифицированной форме для отображения пульсирующего индикатора, показывающего прогресс, пока выполняется длинная задача, например, распаковка большого архива дистрибутива вместо простого указания опции -v для команды tar. Все еще немного сахара, но и весело. (-:
ваше здоровье,
Роб
Спасибо за комментарий, не могли бы вы объяснить, почему вы думаете, что это не масштабируется? Хорошо, код - это просто набросок, но MAX может содержать 64k x, если необходимо, и вычисление переменной T выполняется довольно быстро.
Он не масштабируется, потому что он не будет работать, если вы используете make для нескольких каталогов, причем каждый подкаталог имеет свой собственный make-файл.
@ ScottieT812, ура, это основная причина моего комментария! (-: Другой постоянно должен вычислять и обновлять количество x, необходимых по мере расширения проекта. Тем не менее, хороший трюк.
Роб, ты не понял мой сценарий. Он РАБОТАЕТ, если у вас есть make-файлы в нескольких каталогах (хотя я не рекомендую рекурсивный make), и x НЕ являются количеством целей, это просто верхняя граница (256, может быть больше). Автоматически определяется точное количество целей (см. Код $ T)
Этот менее навязчивый и более интересный.
ifneq ($(words $(MAKECMDGOALS)),1)
.DEFAULT_GOAL = all
%:
@$(MAKE) $@ --no-print-directory -rRf $(firstword $(MAKEFILE_LIST))
else
ifndef ECHO
T := $(shell $(MAKE) $(MAKECMDGOALS) --no-print-directory \
-nrRf $(firstword $(MAKEFILE_LIST)) \
ECHO = "COUNTTHIS" | grep -c "COUNTTHIS")
N := x
C = $(words $N)$(eval N := x $N)
ECHO = echo "`expr " [\`expr $C '*' 100 / $T\`" : '.*\(....\)$$'`%]"
endif
.PHONY: all clean
all: target
@$(ECHO) All done
clean:
@rm -f target *.c
# @$(ECHO) Clean done
target: a.c b.c c.c d.c e.c
@$(ECHO) Linking $@
@sleep 0.1
@touch $@
%.c:
@$(ECHO) Compiling $@
@sleep 0.1
@touch $@
endif
Чтобы заставить его переписать предыдущую строку, используйте это для строки ECHO: ECHO = echo -ne "\r `` expr " [\ `` expr $C '*' 100 / $T\ `` " : '.*\(....\)$$' `` %]" Он работает в bash, может не работать и с другими * nix'ами. Зависит от того, поддерживает ли echo-e и -n.
Обратите внимание на предыдущий комментарий: двойные обратные кавычки должны быть одинарными обратными кавычками, как в посте. Единственным добавлением был -ne после эха и \r после кавычек.
Это хорошо, но при использовании опции -j процентные значения не учитываются. Например [28%] Компиляция b.c [14%] Компиляция a.c ...
Это небольшая модификация отличного отвечать @ GiovanniFunchal.
Поэтому я хотел понять это лучше и заставить его работать на <10%, поэтому я покопался в документация и узнал больше о expr.
# PLACE AT THE TOP OF YOUR MAKEFILE
#---------------------------------
# Progress bar defs
#--------------------------------
# words = count the number of words
ifneq ($(words $(MAKECMDGOALS)),1) # if no argument was given to make...
.DEFAULT_GOAL = all # set the default goal to all
# http://www.gnu.org/software/make/manual/make.html
# $@ = target name
# %: = last resort recipe
# --no-print-directory = don't print enter/leave messages for each output grouping
# MAKEFILE_LIST = has a list of all the parsed Makefiles that can be found *.mk, Makefile, etc
# -n = dry run, just print the recipes
# -r = no builtin rules, disables implicit rules
# -R = no builtin variables, disables implicit variables
# -f = specify the name of the Makefile
%: # define a last resort default rule
@$(MAKE) $@ --no-print-directory -rRf $(firstword $(MAKEFILE_LIST)) # recursive make call,
else
ifndef ECHO
# execute a dry run of make, defining echo beforehand, and count all the instances of "COUNTTHIS"
T := $(shell $(MAKE) $(MAKECMDGOALS) --no-print-directory \
-nrRf $(firstword $(MAKEFILE_LIST)) \
ECHO = "COUNTTHIS" | grep -c "COUNTTHIS")
# eval = evaluate the text and read the results as makefile commands
N := x
# Recursively expand C for each instance of ECHO to count more x's
C = $(words $N)$(eval N := x $N)
# Multipy the count of x's by 100, and divide by the count of "COUNTTHIS"
# Followed by a percent sign
# And wrap it all in square brackets
ECHO = echo -ne "\r [`expr $C '*' 100 / $T`%]"
endif
#------------------
# end progress bar
#------------------
# REST OF YOUR MAKEFILE HERE
#----- Progressbar endif at end Makefile
endif
Я избавился от части : '.*\(....\)$$'. Он вернет последние 4 символа внутренней команды expr, но потерпит неудачу, если будет меньше 4. И теперь он работает для менее 10%!
А вот версия без комментариев:
ifneq ($(words $(MAKECMDGOALS)),1) # if no argument was given to make...
.DEFAULT_GOAL = all # set the default goal to all
%: # define a last resort default rule
@$(MAKE) $@ --no-print-directory -rRf $(firstword $(MAKEFILE_LIST)) # recursive make call,
else
ifndef ECHO
T := $(shell $(MAKE) $(MAKECMDGOALS) --no-print-directory \
-nrRf $(firstword $(MAKEFILE_LIST)) \
ECHO = "COUNTTHIS" | grep -c "COUNTTHIS")
N := x
C = $(words $N)$(eval N := x $N)
ECHO = echo -ne "\r [`expr $C '*' 100 / $T`%]"
endif
# ...
endif
Надеюсь, это поможет.
Я использовал это в make-файле ядра Linux, и он подавляет вывод! сборка по-прежнему выполняется в фоновом режиме, но я ничего не вижу в STDOUT.
Вероятно, это \r в эхо ... Это держит его каждый раз на одной и той же строке, или -ne часть эха.
проблема сохраняется даже после их удаления, возможно, сценарии ядра KBUILD делают что-то особенное, чего этот код не покрывает?
На самом деле не было вопроса, поэтому это не просто отдельный ответ, а скорее расширение решение Джованни Фунчай. Этот вопрос является первым результатом Google для "GNU Make Progress", поэтому я пришел сюда, чтобы узнать, как это сделать.
Как указал Роб Уэллс, решение не работает для <10%, но метод может быть расширен за счет форматирования печати, выполняемого вспомогательным скриптом на любом языке, который, по вашему мнению, является достаточно переносимым для вашей сборки. Например, используя вспомогательный скрипт python:
echo_progress.py:
"""
Print makefile progress
"""
import argparse
import math
import sys
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--stepno", type=int, required=True)
parser.add_argument("--nsteps", type=int, required=True)
parser.add_argument("remainder", nargs=argparse.REMAINDER)
args = parser.parse_args()
nchars = int(math.log(args.nsteps, 10)) + 1
fmt_str = "[{:Xd}/{:Xd}]({:6.2f}%)".replace("X", str(nchars))
progress = 100 * args.stepno / args.nsteps
sys.stdout.write(fmt_str.format(args.stepno, args.nsteps, progress))
for item in args.remainder:
sys.stdout.write(" ")
sys.stdout.write(item)
sys.stdout.write("\n")
if __name__ == "__main__":
main()
И модифицированный Makefile:
_mkfile_path := $(abspath $(lastword $(MAKEFILE_LIST)))
I := $(patsubst %/,%,$(dir $(_mkfile_path)))
ifneq ($(words $(MAKECMDGOALS)),1)
.DEFAULT_GOAL = all
%:
@$(MAKE) $@ --no-print-directory -rRf $(firstword $(MAKEFILE_LIST))
else
ifndef ECHO
T := $(shell $(MAKE) $(MAKECMDGOALS) --no-print-directory \
-nrRf $(firstword $(MAKEFILE_LIST)) \
ECHO = "COUNTTHIS" | grep -c "COUNTTHIS")
N := x
C = $(words $N)$(eval N := x $N)
ECHO = python $(I)/echo_progress.py --stepno=$C --nsteps=$T
endif
.PHONY: all clean
all: target
@$(ECHO) All done
clean:
@rm -f target *.c
# @$(ECHO) Clean done
target: a.c b.c c.c d.c e.c f.c g.c h.c i.c j.c k.c l.c m.c n.c o.c p.c q.c \
r.c s.c t.c u.c v.c w.c x.c y.c z.c
@$(ECHO) Linking $@
@sleep 0.01
@touch $@
%.c:
@$(ECHO) Compiling $@
@sleep 0.01
@touch $@
endif
дает:
$ make
[ 1/28]( 3.57%) Compiling a.c
[ 2/28]( 7.14%) Compiling b.c
[ 3/28]( 10.71%) Compiling c.c
[ 4/28]( 14.29%) Compiling d.c
[ 5/28]( 17.86%) Compiling e.c
[ 6/28]( 21.43%) Compiling f.c
[ 7/28]( 25.00%) Compiling g.c
[ 8/28]( 28.57%) Compiling h.c
[ 9/28]( 32.14%) Compiling i.c
[10/28]( 35.71%) Compiling j.c
[11/28]( 39.29%) Compiling k.c
[12/28]( 42.86%) Compiling l.c
[13/28]( 46.43%) Compiling m.c
[14/28]( 50.00%) Compiling n.c
[15/28]( 53.57%) Compiling o.c
[16/28]( 57.14%) Compiling p.c
[17/28]( 60.71%) Compiling q.c
[18/28]( 64.29%) Compiling r.c
[19/28]( 67.86%) Compiling s.c
[20/28]( 71.43%) Compiling t.c
[21/28]( 75.00%) Compiling u.c
[22/28]( 78.57%) Compiling v.c
[23/28]( 82.14%) Compiling w.c
[24/28]( 85.71%) Compiling x.c
[25/28]( 89.29%) Compiling y.c
[26/28]( 92.86%) Compiling z.c
[27/28]( 96.43%) Linking target
[28/28](100.00%) All done
Можно даже напечатать причудливый индикатор выполнения с символами Юникода.
Модифицированный echo_progress.py:
"""
Print makefile progress
"""
import argparse
import math
import sys
def get_progress_bar(numchars, fraction=None, percent=None):
"""
Return a high resolution unicode progress bar
"""
if percent is not None:
fraction = percent / 100.0
if fraction >= 1.0:
return "█" * numchars
blocks = [" ", "▏", "▎", "▍", "▌", "▋", "▊", "▉", "█"]
length_in_chars = fraction * numchars
n_full = int(length_in_chars)
i_partial = int(8 * (length_in_chars - n_full))
n_empty = max(numchars - n_full - 1, 0)
return ("█" * n_full) + blocks[i_partial] + (" " * n_empty)
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--stepno", type=int, required=True)
parser.add_argument("--nsteps", type=int, required=True)
parser.add_argument("remainder", nargs=argparse.REMAINDER)
args = parser.parse_args()
nchars = int(math.log(args.nsteps, 10)) + 1
fmt_str = "\r[{:Xd}/{:Xd}]({:6.2f}%) ".replace("X", str(nchars))
progress = 100 * args.stepno / args.nsteps
sys.stdout.write(fmt_str.format(args.stepno, args.nsteps, progress))
sys.stdout.write(get_progress_bar(20, percent=progress))
remainder_str = " ".join(args.remainder)
sys.stdout.write(" {:20s}".format(remainder_str[:20]))
if args.stepno == args.nsteps:
sys.stdout.write("\n")
if __name__ == "__main__":
main()
В результате получится что-то вроде этого:
$ make clean && make
[12/28]( 42.86%) ███████▊ Compiling k.c
во время прогресса и:
$ make clean && make
[28/28](100.00%) ████████████████████ All done
по завершении.
Хороший ! Должен быть наверху, но я думаю, что у вашего второго кода на Python есть недостаток. Он использует символы, отличные от ASCII, но не имеет объявления кодировки. Вы должны добавить, например, # coding: utf-8 в верхней части скрипта. См. это. Тем не менее, спасибо так много :)
Большое спасибо @Giovanni Funchal и @phyatt за вопросы и ответы!
Я просто упростил его еще больше для лучшего понимания.
ifndef ECHO
HIT_TOTAL != ${MAKE} ${MAKECMDGOALS} --dry-run ECHO = "HIT_MARK" | grep -c "HIT_MARK"
HIT_COUNT = $(eval HIT_N != expr ${HIT_N} + 1)${HIT_N}
ECHO = echo "[`expr ${HIT_COUNT} '*' 100 / ${HIT_TOTAL}`%]"
endif
!= назначает из команды оболочки= оценивает переменную каждый раз, когда она используетсяeval выполняет свой аргумент без выводаexpr позволяет производить арифметические вычисления(Не знаю, какой подход быстрее: вызвать оболочку с помощью expr или подсчитать x с помощью make.)
Использование такое же:
target:
@$(ECHO) $@
Приятно видеть, что спустя 10 лет после моей первоначальной публикации это все еще привлекает внимание;)
Симпатичный трюк, но я не вижу, чтобы этого захотелось.