File size: 3,215 Bytes
be11144
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
## This CMake script parses a .ninja_log file (LOGFILE) and prints a list of
## build/link times, sorted longest first.
##
## cmake -DLOGFILE=<.ninja_log file> \
##       -P PrintNinjaBuildTimes.cmake
##
## If LOGFILE is omitted, the current directory's .ninja_log file is used.
################################################################################

cmake_minimum_required(VERSION 3.15)

# Prepend the string with "0" until the string length equals the specified width
function(pad_string_with_zeros string_var width)
  set(local_string "${${string_var}}")
  string(LENGTH "${local_string}" size)
  while(size LESS width)
    string(PREPEND local_string "0")
    string(LENGTH "${local_string}" size)
  endwhile()
  set(${string_var} "${local_string}" PARENT_SCOPE)
endfunction()

################################################################################

if (NOT LOGFILE)
  set(LOGFILE ".ninja_log")
endif()

# Check if logfile exists
if (NOT EXISTS "${LOGFILE}")
  message(FATAL_ERROR "LOGFILE does not exist ('${LOGFILE}').")
endif()

# Read the logfile and generate a map / keylist
set(keys)
file(STRINGS "${LOGFILE}" lines)
foreach(line ${lines})

  # Parse each build time
  string(REGEX MATCH
    "^([0-9]+)\t([0-9]+)\t[0-9]+\t([^\t]+)+\t[0-9a-fA-F]+$" _DUMMY "${line}")

  if (CMAKE_MATCH_COUNT EQUAL 3)
    set(start_ms ${CMAKE_MATCH_1})
    set(end_ms ${CMAKE_MATCH_2})
    set(command "${CMAKE_MATCH_3}")
    math(EXPR runtime_ms "${end_ms} - ${start_ms}")

    # Compute human readable time
    math(EXPR days         "${runtime_ms} / (1000 * 60 * 60 * 24)")
    math(EXPR runtime_ms   "${runtime_ms} - (${days} * 1000 * 60 * 60 * 24)")
    math(EXPR hours        "${runtime_ms} / (1000 * 60 * 60)")
    math(EXPR runtime_ms   "${runtime_ms} - (${hours} * 1000 * 60 * 60)")
    math(EXPR minutes      "${runtime_ms} / (1000 * 60)")
    math(EXPR runtime_ms   "${runtime_ms} - (${minutes} * 1000 * 60)")
    math(EXPR seconds      "${runtime_ms} / 1000")
    math(EXPR milliseconds "${runtime_ms} - (${seconds} * 1000)")

    # Format time components
    pad_string_with_zeros(days 3)
    pad_string_with_zeros(hours 2)
    pad_string_with_zeros(minutes 2)
    pad_string_with_zeros(seconds 2)
    pad_string_with_zeros(milliseconds 3)

    # Construct table entry
    # Later values in the file for the same command overwrite earlier entries
    string(MAKE_C_IDENTIFIER "${command}" key)
    set(ENTRY_${key}
      "${days}d ${hours}h ${minutes}m ${seconds}s ${milliseconds}ms | ${command}"
    )

    # Record the key:
    list(APPEND keys "${key}")
  endif()
endforeach()

list(REMOVE_DUPLICATES keys)

# Build the entry list:
set(entries)
foreach(key ${keys})
  list(APPEND entries "${ENTRY_${key}}")
endforeach()

if (NOT entries)
  message(FATAL_ERROR "LOGFILE contained no build entries ('${LOGFILE}').")
endif()

# Sort in descending order:
list(SORT entries)
list(REVERSE entries)

# Dump table:
message(STATUS "-----------------------+----------------------------")
message(STATUS "Time                   | Command                    ")
message(STATUS "-----------------------+----------------------------")

foreach(entry ${entries})
  message(STATUS ${entry})
endforeach()