Skip to content

Commit 6046c05

Browse files
authored
Merge pull request #7040 from xmake-io/progress
Improve multi-row progress
2 parents 867cac2 + 800e417 commit 6046c05

File tree

5 files changed

+124
-16
lines changed

5 files changed

+124
-16
lines changed

xmake/modules/async/runjobs.lua

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,19 +49,19 @@ function _init_progress(state, opt)
4949
end
5050

5151
-- init progress wrapper
52-
state.finished_count = 0
52+
state.progress_finished_count = 0
5353
state.progress_factor = opt.progress_factor or 1.0
5454
local progress_wrapper = {}
5555
progress_wrapper.current = function ()
56-
return state.finished_count
56+
return state.progress_finished_count
5757
end
5858
progress_wrapper.total = function ()
5959
return state.total
6060
end
6161
progress_wrapper.percent = function ()
6262
local total = state.total
6363
if total and total > 0 then
64-
return math.floor((state.finished_count * state.progress_factor * 100) / total)
64+
return math.floor((state.progress_finished_count * state.progress_factor * 100) / total)
6565
else
6666
return 0
6767
end
@@ -203,8 +203,14 @@ function _consume_jobs_loop(state, run_in_remote)
203203
if curdir then
204204
os.cd(curdir)
205205
end
206+
207+
-- to avoid running the same task repeatedly,
208+
-- we need to update the completion count in advance.
206209
state.finished_count = state.finished_count + 1
207210
job_func(job_index, total, {progress = state.progress_wrapper})
211+
212+
-- update progress
213+
state.progress_finished_count = state.progress_finished_count + 1
208214
end
209215
state.running_jobs_indices[job_index] = nil
210216
co_running:data_set("runjobs.running", false)

xmake/modules/private/action/build/target.lua

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import("async.runjobs", {alias = "async_runjobs"})
2828
import("async.jobgraph", {alias = "async_jobgraph"})
2929
import("private.utils.batchcmds")
3030
import("private.utils.rule", {alias = "rule_utils"})
31+
import("utils.progress", {alias = "progress_utils"})
3132

3233
-- clean target for rebuilding
3334
function _clean_target(target)
@@ -791,9 +792,22 @@ function run_targetjobs(targets_root, opt)
791792
local jobgraph = get_targetjobs(targets_root, opt)
792793
if jobgraph and not jobgraph:empty() then
793794
local curdir = os.curdir()
794-
async_runjobs(job_kind, jobgraph, {
795-
comax = opt.jobs or option.get("jobs") or 1, curdir = curdir,
796-
distcc = opt.distcc, remote_only = opt.remote_only, progress_factor = opt.progress_factor})
795+
local runjobs_opt = {
796+
comax = opt.jobs or option.get("jobs") or 1,
797+
curdir = curdir,
798+
distcc = opt.distcc,
799+
remote_only = opt.remote_only,
800+
progress_factor = opt.progress_factor
801+
}
802+
-- Only set timer for multirow progress mode
803+
if progress_utils.is_multirow() then
804+
runjobs_opt.timeout = 1000
805+
runjobs_opt.on_timer = function (running_indices)
806+
-- Periodically refresh multirow progress to update elapsed time
807+
progress_utils.refresh()
808+
end
809+
end
810+
async_runjobs(job_kind, jobgraph, runjobs_opt)
797811
os.cd(curdir)
798812
return true
799813
end
@@ -806,9 +820,22 @@ function run_filejobs(targets_root, opt)
806820
local jobgraph = get_filejobs(targets_root, opt)
807821
if jobgraph and not jobgraph:empty() then
808822
local curdir = os.curdir()
809-
async_runjobs(job_kind, jobgraph, {
810-
comax = opt.jobs or option.get("jobs") or 1, curdir = curdir,
811-
distcc = opt.distcc, remote_only = opt.remote_only, progress_factor = opt.progress_factor})
823+
local runjobs_opt = {
824+
comax = opt.jobs or option.get("jobs") or 1,
825+
curdir = curdir,
826+
distcc = opt.distcc,
827+
remote_only = opt.remote_only,
828+
progress_factor = opt.progress_factor
829+
}
830+
-- Only set timer for multirow progress mode
831+
if progress_utils.is_multirow() then
832+
runjobs_opt.timeout = 1000
833+
runjobs_opt.on_timer = function (running_indices)
834+
-- Periodically refresh multirow progress to update elapsed time
835+
progress_utils.refresh()
836+
end
837+
end
838+
async_runjobs(job_kind, jobgraph, runjobs_opt)
812839
os.cd(curdir)
813840
return true
814841
end

xmake/modules/private/check/checkers/clang/tidy.lua

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ end
8585

8686
-- check a single sourcefile
8787
function _check_sourcefile(clang_tidy, sourcefile, opt)
88-
progress.show(opt.progress_percent, "clang-tidy.analyzing %s", sourcefile)
88+
progress.show(opt.progress, "clang-tidy.analyzing %s", sourcefile)
8989
try
9090
{
9191
function ()
@@ -155,15 +155,28 @@ function _check_sourcefiles(clang_tidy, sourcefiles, opt)
155155

156156
-- run clang-tidy
157157
local analyze_time = os.mclock()
158+
local runjobs_opt = {
159+
total = #sourcefiles,
160+
comax = opt.jobs or os.default_njob(),
161+
showtips = false
162+
}
163+
-- Only set timer for multirow progress mode
164+
if progress.is_multirow() then
165+
runjobs_opt.timeout = 1000
166+
runjobs_opt.on_timer = function (running_indices)
167+
-- Periodically refresh multirow progress to update elapsed time
168+
progress.refresh()
169+
end
170+
end
158171
runjobs("checker.tidy", function (index, total, job_opt)
159172
local sourcefile = sourcefiles[index]
160173
local tidy_argv = table.join(argv, {sourcefile})
161174
_check_sourcefile(clang_tidy, sourcefile, {
162175
tidy_argv = tidy_argv,
163176
projectdir = projectdir,
164-
progress_percent = index * 100 / total
177+
progress = job_opt.progress
165178
})
166-
end, {total = #sourcefiles, comax = opt.jobs or os.default_njob()})
179+
end, runjobs_opt)
167180
analyze_time = os.mclock() - analyze_time
168181
progress.show(100, "${color.success}clang-tidy analyzed %d files, spent %.3fs", #sourcefiles, analyze_time / 1000)
169182
end

xmake/modules/utils/progress.lua

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -202,11 +202,22 @@ function _display_subprocess_lines(order_lineinfos)
202202
linecount = linecount + 1
203203
end
204204
end
205+
-- clear the left lines
206+
local left_linecount = #order_lineinfos - linecount
207+
if left_linecount > 0 then
208+
for i = 1, left_linecount do
209+
tty.erase_line().cr()
210+
print("")
211+
end
212+
tty.cursor_move_up(left_linecount)
213+
end
205214
_g.linecount = linecount
206215
end
207216

208217
-- redraw the multirow progress area (internal helper)
209-
function _redraw_multirow_progress(maxwidth)
218+
-- @param maxwidth: window width
219+
-- @param current_time: optional current time (to avoid repeated os.mclock() calls)
220+
function _redraw_multirow_progress(maxwidth, current_time)
210221
local last_total_progress = _g.last_total_progress
211222
if not last_total_progress then
212223
return
@@ -217,7 +228,9 @@ function _redraw_multirow_progress(maxwidth)
217228
cprint(last_total_progress)
218229

219230
-- build and display the subprocess lines
220-
local current_time = os.mclock()
231+
if not current_time then
232+
current_time = os.mclock()
233+
end
221234
local order_lineinfos = _build_ordered_subprocess_lineinfos(maxwidth, current_time)
222235
_display_subprocess_lines(order_lineinfos)
223236
io.flush()
@@ -266,6 +279,7 @@ function _show_progress_with_multirow_refresh(progress, format, ...)
266279
-- save the total progress line and progress value for potential redraw in show_output
267280
_g.last_total_progress = progress_line
268281
_g.last_total_progress_value = progress
282+
_g.last_show_time = current_time
269283

270284
-- update the current progress info
271285
local current_lineinfo = progress_lineinfos[running]
@@ -382,6 +396,41 @@ function show_output(format, ...)
382396
end
383397
end
384398

399+
-- check if multirow refresh mode is enabled
400+
function is_multirow()
401+
return _is_multirow_refresh()
402+
end
403+
404+
-- refresh the multirow progress display to update elapsed time
405+
-- this is useful for long-running tasks to keep the elapsed time updated
406+
function refresh()
407+
local refresh_mode = _g.refresh_mode
408+
if refresh_mode == "multirow" then
409+
-- get current time once and reuse it
410+
local current_time = os.mclock()
411+
412+
-- only refresh if more than 500ms has passed since last show
413+
-- this avoids too frequent refreshes
414+
local last_show_time = _g.last_show_time
415+
if last_show_time then
416+
local elapsed = current_time - last_show_time
417+
if elapsed <= 500 then
418+
return
419+
end
420+
end
421+
422+
-- move cursor back to the top of progress area to avoid scrolling
423+
local linecount = _g.linecount or 0
424+
if linecount > 0 then
425+
tty.cursor_move_up(linecount + 1)
426+
end
427+
428+
-- redraw the progress area immediately, passing current_time to avoid repeated calls
429+
local maxwidth = os.getwinsize().width
430+
_redraw_multirow_progress(maxwidth, current_time)
431+
end
432+
end
433+
385434
-- abort the progress display mode, used for error exit or early termination
386435
-- this function cleans up the progress display area and restores the terminal state
387436
function show_abort()

xmake/plugins/format/main.lua

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -216,12 +216,25 @@ function main()
216216
jobs = os.default_njob()
217217
end
218218
local format_time = os.mclock()
219+
local runjobs_opt = {
220+
total = #sourcefiles,
221+
comax = jobs,
222+
showtips = false
223+
}
224+
-- Only set timer for multirow progress mode
225+
if progress.is_multirow() then
226+
runjobs_opt.timeout = 1000
227+
runjobs_opt.on_timer = function (running_indices)
228+
-- Periodically refresh multirow progress to update elapsed time
229+
progress.refresh()
230+
end
231+
end
219232
runjobs("clang-format", function (index, total, opt)
220233
local sourcefile = sourcefiles[index]
221234
local format_argv = table.join(argv, {sourcefile})
222-
progress.show(index * 100 / total, "clang-format.formatting %s", sourcefile)
235+
progress.show(opt.progress, "clang-format.formatting %s", sourcefile)
223236
os.execv(clang_format.program, format_argv, {curdir = projectdir})
224-
end, {total = #sourcefiles, comax = jobs})
237+
end, runjobs_opt)
225238
format_time = os.mclock() - format_time
226239
progress.show(100, "${color.success}clang-format formatted %d files, spent %.3fs", #sourcefiles, format_time / 1000)
227240
end

0 commit comments

Comments
 (0)