Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
R
riscv-gcc-1
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
lvzhengyang
riscv-gcc-1
Commits
01f5295d
Commit
01f5295d
authored
Aug 02, 2017
by
Yury Gribov
Committed by
Yury Gribov
Aug 02, 2017
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Added Python implementation of mklog.
From-SVN: r250836
parent
e1df0c91
Show whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
474 additions
and
0 deletions
+474
-0
contrib/ChangeLog
+4
-0
contrib/mklog
+470
-0
No files found.
contrib/ChangeLog
View file @
01f5295d
2017
-
08
-
02
Yury
Gribov
<
tetra2005
@gmail
.
com
>
*
mklog
:
Added
new
implementation
.
2017
-
08
-
02
Yury
Gribov
<
tetra2005
@gmail
.
com
>
*
mklog
:
Rename
to
...
*
mklog
.
pl
:
this
.
...
...
contrib/mklog
0 → 100755
View file @
01f5295d
#!/usr/bin/python
# Copyright (C) 2017 Free Software Foundation, Inc.
#
# This file is part of GCC.
#
# GCC is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3, or (at your option)
# any later version.
#
# GCC is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with GCC; see the file COPYING. If not, write to
# the Free Software Foundation, 51 Franklin Street, Fifth Floor,
# Boston, MA 02110-1301, USA.
# This script parses a .diff file generated with 'diff -up' or 'diff -cp'
# and adds a skeleton ChangeLog file to the file. It does not try to be
# too smart when parsing function names, but it produces a reasonable
# approximation.
#
# This is a straightforward adaptation of original Perl script.
#
# Author: Yury Gribov <tetra2005@gmail.com>
import
sys
import
re
import
os.path
import
os
import
getopt
import
tempfile
import
time
import
shutil
from
subprocess
import
Popen
,
PIPE
me
=
os
.
path
.
basename
(
sys
.
argv
[
0
])
def
error
(
msg
):
sys
.
stderr
.
write
(
"
%
s: error:
%
s
\n
"
%
(
me
,
msg
))
sys
.
exit
(
1
)
def
warn
(
msg
):
sys
.
stderr
.
write
(
"
%
s: warning:
%
s
\n
"
%
(
me
,
msg
))
class
RegexCache
(
object
):
"""Simple trick to Perl-like combined match-and-bind."""
def
__init__
(
self
):
self
.
last_match
=
None
def
match
(
self
,
p
,
s
):
self
.
last_match
=
re
.
match
(
p
,
s
)
if
isinstance
(
p
,
str
)
else
p
.
match
(
s
)
return
self
.
last_match
def
search
(
self
,
p
,
s
):
self
.
last_match
=
re
.
search
(
p
,
s
)
if
isinstance
(
p
,
str
)
else
p
.
search
(
s
)
return
self
.
last_match
def
group
(
self
,
n
):
return
self
.
last_match
.
group
(
n
)
cache
=
RegexCache
()
def
print_help_and_exit
():
print
"""
\
Usage:
%
s [-i | --inline] [PATCH]
Generate ChangeLog template for PATCH.
PATCH must be generated using diff(1)'s -up or -cp options
(or their equivalent in Subversion/git).
When PATCH is - or missing, read standard input.
When -i is used, prepends ChangeLog to PATCH.
If PATCH is not stdin, modifies PATCH in-place, otherwise writes
to stdout.
"""
%
me
sys
.
exit
(
1
)
def
run
(
cmd
,
die_on_error
):
"""Simple wrapper for Popen."""
proc
=
Popen
(
cmd
.
split
(
' '
),
stderr
=
PIPE
,
stdout
=
PIPE
)
(
out
,
err
)
=
proc
.
communicate
()
if
die_on_error
and
proc
.
returncode
!=
0
:
error
(
"`
%
s` failed:
\n
"
%
(
cmd
,
proc
.
stderr
))
return
proc
.
returncode
,
out
,
err
def
read_user_info
():
dot_mklog_format_msg
=
"""
\
The .mklog format is:
NAME = ...
EMAIL = ...
"""
# First try to read .mklog config
mklog_conf
=
os
.
path
.
expanduser
(
'~/.mklog'
)
if
os
.
path
.
exists
(
mklog_conf
):
attrs
=
{}
f
=
open
(
mklog_conf
,
'rb'
)
for
s
in
f
:
if
cache
.
match
(
r'^\s*([a-zA-Z0-9_]+)\s*=\s*(.*?)\s*$'
,
s
):
attrs
[
cache
.
group
(
1
)]
=
cache
.
group
(
2
)
f
.
close
()
if
'NAME'
not
in
attrs
:
error
(
"'NAME' not present in .mklog"
)
if
'EMAIL'
not
in
attrs
:
error
(
"'EMAIL' not present in .mklog"
)
return
attrs
[
'NAME'
],
attrs
[
'EMAIL'
]
# Otherwise go with git
rc1
,
name
,
_
=
run
(
'git config user.name'
,
False
)
name
=
name
.
rstrip
()
rc2
,
email
,
_
=
run
(
'git config user.email'
,
False
)
email
=
email
.
rstrip
()
if
rc1
!=
0
or
rc2
!=
0
:
error
(
"""
\
Could not read git user.name and user.email settings.
Please add missing git settings, or create a
%
s.
"""
%
mklog_conf
)
return
name
,
email
def
get_parent_changelog
(
s
):
"""See which ChangeLog this file change should go to."""
if
s
.
find
(
'
\\
'
)
==
-
1
and
s
.
find
(
'/'
)
==
-
1
:
return
"ChangeLog"
,
s
gcc_root
=
os
.
path
.
dirname
(
os
.
path
.
dirname
(
os
.
path
.
realpath
(
__file__
)))
d
=
s
while
d
:
clname
=
d
+
"/ChangeLog"
if
os
.
path
.
exists
(
gcc_root
+
'/'
+
clname
)
or
os
.
path
.
exists
(
clname
):
relname
=
s
[
len
(
d
)
+
1
:]
return
clname
,
relname
d
,
_
=
os
.
path
.
split
(
d
)
return
"Unknown ChangeLog"
,
s
class
FileDiff
:
"""Class to represent changes in a single file."""
def
__init__
(
self
,
filename
):
self
.
filename
=
filename
self
.
hunks
=
[]
self
.
clname
,
self
.
relname
=
get_parent_changelog
(
filename
);
def
dump
(
self
):
print
"Diff for
%
s:
\n
ChangeLog =
%
s
\n
rel name =
%
s
\n
"
%
(
self
.
filename
,
self
.
clname
,
self
.
relname
)
for
i
,
h
in
enumerate
(
self
.
hunks
):
print
"Next hunk
%
d:"
%
i
h
.
dump
()
class
Hunk
:
"""Class to represent a single hunk of changes."""
def
__init__
(
self
,
hdr
):
self
.
hdr
=
hdr
self
.
lines
=
[]
self
.
ctx_diff
=
is_ctx_hunk_start
(
hdr
)
def
dump
(
self
):
print
'
%
s'
%
self
.
hdr
print
'
%
s'
%
'
\n
'
.
join
(
self
.
lines
)
def
is_file_addition
(
self
):
"""Does hunk describe addition of file?"""
if
self
.
ctx_diff
:
for
line
in
self
.
lines
:
if
re
.
match
(
r'^\*\*\* 0 \*\*\*\*'
,
line
):
return
True
else
:
return
re
.
match
(
r'^@@ -0,0 \+1.* @@'
,
self
.
hdr
)
def
is_file_removal
(
self
):
"""Does hunk describe removal of file?"""
if
self
.
ctx_diff
:
for
line
in
self
.
lines
:
if
re
.
match
(
r'^--- 0 ----'
,
line
):
return
True
else
:
return
re
.
match
(
r'^@@ -1.* \+0,0 @@'
,
self
.
hdr
)
def
is_file_diff_start
(
s
):
# Don't be fooled by context diff line markers:
# *** 385,391 ****
return
((
s
.
startswith
(
'***'
)
and
not
s
.
endswith
(
'***'
))
or
(
s
.
startswith
(
'---'
)
and
not
s
.
endswith
(
'---'
)))
def
is_ctx_hunk_start
(
s
):
return
re
.
match
(
r'^\*\*\*\*\*\**'
,
s
)
def
is_uni_hunk_start
(
s
):
return
re
.
match
(
r'^@@ .* @@'
,
s
)
def
is_hunk_start
(
s
):
return
is_ctx_hunk_start
(
s
)
or
is_uni_hunk_start
(
s
)
def
remove_suffixes
(
s
):
if
s
.
startswith
(
'a/'
)
or
s
.
startswith
(
'b/'
):
s
=
s
[
2
:]
if
s
.
endswith
(
'.jj'
):
s
=
s
[:
-
3
]
return
s
def
find_changed_funs
(
hunk
):
"""Find all functions touched by hunk. We don't try too hard
to find good matches. This should return a superset
of the actual set of functions in the .diff file.
"""
fns
=
[]
fn
=
None
if
(
cache
.
match
(
r'^\*\*\*\*\*\** ([a-zA-Z0-9_].*)'
,
hunk
.
hdr
)
or
cache
.
match
(
r'^@@ .* @@ ([a-zA-Z0-9_].*)'
,
hunk
.
hdr
)):
fn
=
cache
.
group
(
1
)
for
i
,
line
in
enumerate
(
hunk
.
lines
):
# Context diffs have extra whitespace after first char;
# remove it to make matching easier.
if
hunk
.
ctx_diff
:
line
=
re
.
sub
(
r'^([-+! ]) '
,
r'\1'
,
line
)
# Remember most recent identifier in hunk
# that might be a function name.
if
cache
.
match
(
r'^[-+! ]([a-zA-Z0-9_#].*)'
,
line
):
fn
=
cache
.
group
(
1
)
change
=
line
and
re
.
match
(
r'^[-+!][^-]'
,
line
)
# Top-level comment can not belong to function
if
re
.
match
(
r'^[-+! ]\/\*'
,
line
):
fn
=
None
if
change
and
fn
:
if
cache
.
match
(
r'^((class|struct|union|enum)\s+[a-zA-Z0-9_]+)'
,
fn
):
# Struct declaration
fn
=
cache
.
group
(
1
)
elif
cache
.
search
(
r'#\s*define\s+([a-zA-Z0-9_]+)'
,
fn
):
# Macro definition
fn
=
cache
.
group
(
1
)
elif
cache
.
match
(
'^DEF[A-Z0-9_]+
\
s*
\
(([a-zA-Z0-9_]+)'
,
fn
):
# Supermacro
fn
=
cache
.
group
(
1
)
elif
cache
.
search
(
r'([a-zA-Z_][^()\s]*)\s*\([^*]'
,
fn
):
# Discard template and function parameters.
fn
=
cache
.
group
(
1
)
fn
=
re
.
sub
(
r'<[^<>]*>'
,
''
,
fn
)
fn
=
fn
.
rstrip
()
else
:
fn
=
None
if
fn
and
fn
not
in
fns
:
# Avoid dups
fns
.
append
(
fn
)
fn
=
None
return
fns
def
parse_patch
(
contents
):
"""Parse patch contents to a sequence of FileDiffs."""
diffs
=
[]
lines
=
contents
.
split
(
'
\n
'
)
i
=
0
while
i
<
len
(
lines
):
line
=
lines
[
i
]
# Diff headers look like
# --- a/gcc/tree.c
# +++ b/gcc/tree.c
# or
# *** gcc/cfgexpand.c 2013-12-25 20:07:24.800350058 +0400
# --- gcc/cfgexpand.c 2013-12-25 20:06:30.612350178 +0400
if
is_file_diff_start
(
line
):
left
=
re
.
split
(
r'\s+'
,
line
)[
1
]
else
:
i
+=
1
continue
left
=
remove_suffixes
(
left
);
i
+=
1
line
=
lines
[
i
]
if
not
cache
.
match
(
r'^[+-][+-][+-] +(\S+)'
,
line
):
error
(
"expected filename in line
%
d"
%
i
)
right
=
remove_suffixes
(
cache
.
group
(
1
));
# Extract real file name from left and right names.
filename
=
None
if
left
==
right
:
filename
=
left
elif
left
==
'/dev/null'
:
filename
=
right
;
elif
right
==
'/dev/null'
:
filename
=
left
;
else
:
comps
=
[]
while
left
and
right
:
left
,
l
=
os
.
path
.
split
(
left
)
right
,
r
=
os
.
path
.
split
(
right
)
if
l
!=
r
:
break
comps
.
append
(
l
)
if
not
comps
:
error
(
"failed to extract common name for
%
s and
%
s"
%
(
left
,
right
))
comps
.
reverse
()
filename
=
'/'
.
join
(
comps
)
d
=
FileDiff
(
filename
)
diffs
.
append
(
d
)
# Collect hunks for current file.
hunk
=
None
i
+=
1
while
i
<
len
(
lines
):
line
=
lines
[
i
]
# Create new hunk when we see hunk header
if
is_hunk_start
(
line
):
if
hunk
is
not
None
:
d
.
hunks
.
append
(
hunk
)
hunk
=
Hunk
(
line
)
i
+=
1
continue
# Stop when we reach next diff
if
(
is_file_diff_start
(
line
)
or
line
.
startswith
(
'diff '
)
or
line
.
startswith
(
'Index: '
)):
i
-=
1
break
if
hunk
is
not
None
:
hunk
.
lines
.
append
(
line
)
i
+=
1
d
.
hunks
.
append
(
hunk
)
return
diffs
def
main
():
name
,
email
=
read_user_info
()
try
:
opts
,
args
=
getopt
.
getopt
(
sys
.
argv
[
1
:],
'hiv'
,
[
'help'
,
'verbose'
,
'inline'
])
except
getopt
.
GetoptError
,
err
:
error
(
str
(
err
))
inline
=
False
verbose
=
0
for
o
,
a
in
opts
:
if
o
in
(
'-h'
,
'--help'
):
print_help_and_exit
()
elif
o
in
(
'-i'
,
'--inline'
):
inline
=
True
elif
o
in
(
'-v'
,
'--verbose'
):
verbose
+=
1
else
:
assert
False
,
"unhandled option"
if
len
(
args
)
==
0
:
args
=
[
'-'
]
if
len
(
args
)
==
1
and
args
[
0
]
==
'-'
:
input
=
sys
.
stdin
elif
len
(
args
)
==
1
:
input
=
open
(
args
[
0
],
'rb'
)
else
:
error
(
"too many arguments; for more details run with -h"
)
contents
=
input
.
read
()
diffs
=
parse_patch
(
contents
)
if
verbose
:
print
"Parse results:"
for
d
in
diffs
:
d
.
dump
()
# Generate template ChangeLog.
logs
=
{}
for
d
in
diffs
:
log_name
=
d
.
clname
logs
.
setdefault
(
log_name
,
''
)
logs
[
log_name
]
+=
'
\t
*
%
s'
%
d
.
relname
change_msg
=
''
# Check if file was removed or added.
# Two patterns for context and unified diff.
if
len
(
d
.
hunks
)
==
1
:
hunk0
=
d
.
hunks
[
0
]
if
hunk0
.
is_file_addition
():
if
re
.
search
(
r'testsuite.*(?<!\.exp)$'
,
d
.
filename
):
change_msg
=
': New test.
\n
'
else
:
change_msg
=
": New file.
\n
"
elif
hunk0
.
is_file_removal
():
change_msg
=
": Remove.
\n
"
_
,
ext
=
os
.
path
.
splitext
(
d
.
filename
)
if
not
change_msg
and
ext
in
[
'.c'
,
'.cpp'
,
'.C'
,
'.cc'
,
'.h'
,
'.inc'
,
'.def'
]:
fns
=
[]
for
hunk
in
d
.
hunks
:
for
fn
in
find_changed_funs
(
hunk
):
if
fn
not
in
fns
:
fns
.
append
(
fn
)
for
fn
in
fns
:
if
change_msg
:
change_msg
+=
"
\t
(
%
s):
\n
"
%
fn
else
:
change_msg
=
" (
%
s):
\n
"
%
fn
logs
[
log_name
]
+=
change_msg
if
change_msg
else
":
\n
"
if
inline
and
args
[
0
]
!=
'-'
:
# Get a temp filename, rather than an open filehandle, because we use
# the open to truncate.
fd
,
tmp
=
tempfile
.
mkstemp
(
"tmp.XXXXXXXX"
)
os
.
close
(
fd
)
# Copy permissions to temp file
# (old Pythons do not support shutil.copymode)
shutil
.
copymode
(
args
[
0
],
tmp
)
# Open the temp file, clearing contents.
out
=
open
(
tmp
,
'wb'
)
else
:
tmp
=
None
out
=
sys
.
stdout
# Print log
date
=
time
.
strftime
(
'
%
Y-
%
m-
%
d'
)
for
log_name
,
msg
in
sorted
(
logs
.
iteritems
()):
out
.
write
(
"""
\
%
s:
%
s
%
s <
%
s>
%
s
\n
"""
%
(
log_name
,
date
,
name
,
email
,
msg
))
if
inline
:
# Append patch body
out
.
write
(
contents
)
if
args
[
0
]
!=
'-'
:
# Write new contents atomically
out
.
close
()
shutil
.
move
(
tmp
,
args
[
0
])
if
__name__
==
'__main__'
:
main
()
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment