forked from sakura-editor/sakura
-
Notifications
You must be signed in to change notification settings - Fork 0
/
parse-buildlog.py
338 lines (273 loc) · 9.35 KB
/
parse-buildlog.py
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
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
# -*- coding: utf-8 -*-
import sys
import re
import os
import csv
import platform
import appveyor_env
# 解析結果を格納するハッシュのキー
logHashKeys = [
'errType',
'code',
'source',
'dest',
'path',
'lineNumber',
'message',
'relpath',
'blobURL',
]
csvKeys = [
'errType',
'code',
'source',
'dest',
'path',
'lineNumber',
'message',
'blobURL',
]
excelKeys = [
'errType',
'code',
'source',
'dest',
'relpath',
'message',
'path',
'lineNumber',
]
colors_YELLOW = "FFFF00"
colors_BLUE = "0000FF"
# infile: msbuild のビルドログ・ファイル名
# 戻り値: ログの解析結果が入ったハッシュの配列
def parse_buildlog(infile):
# ファイル名に対する正規表現: 例 c:\hogehoge\hoge.c
regLilePath = r'(?P<filePath>[a-zA-Z]:([^(]+))'
# 行番号に対する正規表現: 例 (100)
regLineNumer = r'\((?P<lineNumber>\d+)\)'
# エラーコードに対する正規表現: 例 warning C4267
regError = r'\s*(?P<errType>\w+)\s+(?P<code>\w+)\s*'
# エラーメッセージ: 例 'argument': conversion from 'size_t' to 'int', possible loss of data [C:\projects\sakura\sakura\sakura.vcxproj]
regMessage = r'(?P<message>.+)$'
# 解析対象の正規表現
# 例
# c:\projects\sakura\sakura_core\basis\cmystring.h(39): warning C4267: 'argument': conversion from 'size_t' to 'int', possible loss of data [C:\projects\sakura\sakura\sakura.vcxproj]
regEx = regLilePath + regLineNumer + r':' + regError + r':' + regMessage
# 型変換に対する警告メッセージ部分に対する正規表現: 例 'size_t' to 'int'
regFromTo = r"from '(?P<source>[^']+)' to '(?P<dest>[^']+)'"
data = []
with open(infile, "r") as fin:
# msbuild-xxx-xxx.log のログに警告が重複して出現することに対する Workaround 用
duplicateCheck = {}
appveyor = appveyor_env.AppveyorEnv()
print ("open " + infile)
for line in fin:
text = line.replace('\n','')
text = text.replace('\r','')
match = re.search(regEx, text)
if match:
path = match.group('filePath')
lineNumber = match.group('lineNumber')
errType = match.group('errType')
code = match.group('code')
message = match.group('message')
entry = {}
entry['errType'] = errType
entry['code'] = code
match2 = re.search(regFromTo, text)
if match2:
source = match2.group('source')
dest = match2.group('dest')
entry['source'] = source
entry['dest'] = dest
else:
entry['source'] = r''
entry['dest'] = r''
entry['path'] = path
entry['lineNumber'] = lineNumber
entry['message'] = message
(blobURL, relpath) = appveyor.getBlobURLWithLine(path, lineNumber)
entry['relpath'] = relpath
entry['blobURL'] = blobURL
temp = []
for key in logHashKeys:
temp.append(entry[key])
entry['key'] = ' '.join(temp)
# msbuild-xxx-xxx.log のログに同じ警告が重複して出現することに対する Workaround
# entry['key'] で警告を一意に識別できるので同じ値の場合に CSV に出力しない
logKey = entry['key']
duplicateCheck[logKey] = duplicateCheck.get(logKey, 0) + 1
if duplicateCheck[logKey] == 1:
data.append(entry)
# ソート対象のハッシュ配列で 'key' というキーを元にソートする。
from operator import itemgetter
data = sorted(data, key=itemgetter('key'))
return data
# outfile: 出力ファイル名
# data : ログの解析結果が入ったハッシュの配列 ( parse_buildlog() の戻り値 )
def writeToCSV(outfile, data):
# 解析結果を CSV ファイルに出力する
with open(outfile, "w") as fout:
writer = csv.writer(fout, lineterminator='\n')
writer.writerow(csvKeys)
for entry in data:
temp = []
for key in csvKeys:
temp.append(entry[key])
writer.writerow(temp)
print ("wrote " + outfile)
# outfile: 出力ファイル名
# data : ログの解析結果が入ったハッシュの配列 ( parse_buildlog() の戻り値 )
def writeToXLSX(outfile, data):
# CELL に設定する値を変換する関数を返す
def getEntryConverter():
def converterPython3(value):
return value
def converterPython2(value):
return value.decode('shiftjis').encode('utf_8')
if sys.version_info.major >= 3:
return converterPython3
return converterPython2
try:
import openpyxl
from openpyxl.styles import colors
from openpyxl.styles import Font, Color
from openpyxl.styles.fills import PatternFill
wb = openpyxl.Workbook()
ws = wb.active
# 列幅に必要なサイズを保持する配列
maxWidths = []
# ヘッダ部分を設定する
y = 0
for x, item in enumerate(excelKeys):
cell = ws.cell(row=y+1, column=x+1)
cell.value = item
cell.fill = PatternFill(patternType='solid', start_color=colors_YELLOW, end_color=colors_YELLOW)
maxWidths.append(len(cell.value) + 1)
y = y + 1
# 各エントリーを設定するときのコンバーターを取得する (python 2/3 の違いを吸収するためのもの)
converter = getEntryConverter()
# エラーごとにまとめて表示するための情報
errorSummary = {}
# ログの解析結果を設定する
for entry in data:
# ファイルパスを取り除いた関連エラーメッセージをまとめる
message = converter(entry['message'])
message = re.sub(r'((\S*)\)\s*)?\[(.+?)\]', r'', message)
# エラータイプ
errType = entry['errType']
# エラーコード
code = entry['code']
# サマリーを管理するハッシュ用のキー
errorKey = ' '.join([errType, code, message])
if errorKey not in errorSummary:
errorSummary[errorKey] = {}
errorSummary[errorKey]["description"] = message
errorSummary[errorKey]["entries"] = []
errorSummary[errorKey]["errType"] = errType
errorSummary[errorKey]["code"] = code
errorSummary[errorKey]["entries"].append(entry)
for x, key in enumerate(excelKeys):
cell = ws.cell(row=y+1, column=x+1)
if key == "relpath":
val = entry['relpath'] + " line: " + entry['lineNumber']
cell.hyperlink = entry['blobURL']
cell.font = Font(u='single', color=colors_BLUE)
else:
entryKey = entry[key]
val = converter(entry[key])
# 列幅を設定するために必要なサイズを計算する
width = len(val) + 1
if maxWidths[x] < width:
maxWidths[x] = width
# セルに値を設定する
if val.isdigit():
cell.value = int(val)
else:
cell.value = val
# 行番号を更新する
y = y + 1
# 列幅を設定する
for x, item in enumerate(excelKeys):
ws.column_dimensions[openpyxl.utils.get_column_letter(x+1)].width = maxWidths[x]
# Excel の列にフィルタを設定する
start = openpyxl.utils.get_column_letter(1)
end = openpyxl.utils.get_column_letter(len(excelKeys))
ws.auto_filter.ref = start + ":" + end
# ウィンドウ枠を固定
ws.freeze_panes = 'F2'
#############################################################################
# エラーのサマリーシート用のコード
#############################################################################
worksheetIndex = 0
errorKeys = sorted(errorSummary.keys())
for errorKey in errorKeys:
worksheetIndex = worksheetIndex + 1
# 列幅に必要なサイズを保持する配列
maxWidths = []
message = errorSummary[errorKey]["description"]
entries = errorSummary[errorKey]["entries"]
errType = errorSummary[errorKey]["errType"]
code = errorSummary[errorKey]["code"]
wsError = wb.create_sheet()
wsError.title = str(worksheetIndex) + "(" + code + ")"
x = 0
y = 0
# エラーメッセージをセットする
if entries:
entry = entries[0]
cell = wsError.cell(row=y+1, column=x+1)
cell.value = message
y = y + 1
# 空行を入れる
y = y + 1
outputKeys = [
'path',
'lineNumber',
'blobURL',
]
# ヘッダ部分を設定
for x, key in enumerate(outputKeys):
cell = wsError.cell(row=y+1, column=x+1)
cell.value = key
cell.fill = PatternFill(patternType='solid', start_color=colors_YELLOW, end_color=colors_YELLOW)
maxWidths.append(len(cell.value) + 1)
y = y + 1
# 各エラー箇所を設定する
for entry in entries:
for x, key in enumerate(outputKeys):
cell = wsError.cell(row=y+1, column=x+1)
if key == "blobURL":
cell.hyperlink = entry[key]
cell.font = Font(u='single', color=colors_BLUE)
val = converter(entry[key])
if val.isdigit():
cell.value = int(val)
else:
cell.value = val
# 列幅を設定するために必要なサイズを計算する
width = len(val) + 1
if maxWidths[x] < width:
maxWidths[x] = width
y = y + 1
# 列幅を設定する
for x, item in enumerate(maxWidths):
wsError.column_dimensions[openpyxl.utils.get_column_letter(x+1)].width = item
wb.save(outfile)
print ("wrote " + outfile)
except ImportError:
print ("please run '<python root>\\Scripts\\pip install openpyxl --user'")
# main 関数
def main():
if len(sys.argv) < 2:
print ("usage: " + sys.argv[0] + " <logfile name>")
sys.exit(1)
infile = sys.argv[1]
outcsvfile = infile + '.csv'
outxlsxfile = infile + '.xlsx'
data = parse_buildlog(infile)
writeToCSV(outcsvfile, data)
writeToXLSX(outxlsxfile, data)
if __name__ == '__main__':
main()