mirror of https://github.com/ethereum/go-ethereum
commit
c1908c7d91
@ -0,0 +1,12 @@ |
|||||||
|
Jeffrey Wilcke <jeffrey@ethereum.org> |
||||||
|
Jeffrey Wilcke <jeffrey@ethereum.org> <geffobscura@gmail.com> |
||||||
|
Jeffrey Wilcke <jeffrey@ethereum.org> <obscuren@obscura.com> |
||||||
|
Jeffrey Wilcke <jeffrey@ethereum.org> <obscuren@users.noreply.github.com> |
||||||
|
|
||||||
|
Viktor Trón <viktor.tron@gmail.com> |
||||||
|
|
||||||
|
Joseph Goulden <joegoulden@gmail.com> |
||||||
|
|
||||||
|
Nick Savers <nicksavers@gmail.com> |
||||||
|
|
||||||
|
Maran Hidskes <maran.hidskes@gmail.com> |
@ -0,0 +1,247 @@ |
|||||||
|
// +build none
|
||||||
|
/* |
||||||
|
This command generates GPL license headers on top of all source files. |
||||||
|
You can run it once per month, before cutting a release or just |
||||||
|
whenever you feel like it. |
||||||
|
|
||||||
|
go run update-license.go |
||||||
|
|
||||||
|
The copyright in each file is assigned to any authors for which git |
||||||
|
can find commits in the file's history. It will try to follow renames |
||||||
|
throughout history. The author names are mapped and deduplicated using |
||||||
|
the .mailmap file. You can use .mailmap to set the canonical name and |
||||||
|
address for each author. See git-shortlog(1) for an explanation |
||||||
|
of the .mailmap format. |
||||||
|
|
||||||
|
Please review the resulting diff to check whether the correct |
||||||
|
copyright assignments are performed. |
||||||
|
*/ |
||||||
|
package main |
||||||
|
|
||||||
|
import ( |
||||||
|
"bufio" |
||||||
|
"bytes" |
||||||
|
"fmt" |
||||||
|
"io/ioutil" |
||||||
|
"os" |
||||||
|
"os/exec" |
||||||
|
"path" |
||||||
|
"regexp" |
||||||
|
"runtime" |
||||||
|
"sort" |
||||||
|
"strings" |
||||||
|
"sync" |
||||||
|
"text/template" |
||||||
|
) |
||||||
|
|
||||||
|
var ( |
||||||
|
// only files with these extensions will be considered
|
||||||
|
extensions = []string{".go", ".js", ".qml"} |
||||||
|
|
||||||
|
// paths with any of these prefixes will be skipped
|
||||||
|
skipPrefixes = []string{"tests/files/", "cmd/mist/assets/ext/", "cmd/mist/assets/muted/"} |
||||||
|
|
||||||
|
// paths with this prefix are licensed as GPL. all other files are LGPL.
|
||||||
|
gplPrefixes = []string{"cmd/"} |
||||||
|
|
||||||
|
// this regexp must match the entire license comment at the
|
||||||
|
// beginning of each file.
|
||||||
|
licenseCommentRE = regexp.MustCompile(`(?s)^/\*\s*(Copyright|This file is part of) .*?\*/\n*`) |
||||||
|
|
||||||
|
// this line is used when git doesn't find any authors for a file
|
||||||
|
defaultCopyright = "Copyright (C) 2014 Jeffrey Wilcke <jeffrey@ethereum.org>" |
||||||
|
) |
||||||
|
|
||||||
|
// this template generates the license comment.
|
||||||
|
// its input is an info structure.
|
||||||
|
var licenseT = template.Must(template.New("").Parse(`/* |
||||||
|
{{.Copyrights}} |
||||||
|
|
||||||
|
This file is part of go-ethereum |
||||||
|
|
||||||
|
go-ethereum is free software: you can redistribute it and/or modify |
||||||
|
it under the terms of the GNU {{.License}} as published by |
||||||
|
the Free Software Foundation, either version 3 of the License, or |
||||||
|
(at your option) any later version. |
||||||
|
|
||||||
|
go-ethereum 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 {{.License}} for more details. |
||||||
|
|
||||||
|
You should have received a copy of the GNU {{.License}} |
||||||
|
along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/ |
||||||
|
|
||||||
|
`)) |
||||||
|
|
||||||
|
type info struct { |
||||||
|
file string |
||||||
|
mode os.FileMode |
||||||
|
authors map[string][]string // map keys are authors, values are years
|
||||||
|
gpl bool |
||||||
|
} |
||||||
|
|
||||||
|
func (i info) Copyrights() string { |
||||||
|
var lines []string |
||||||
|
for name, years := range i.authors { |
||||||
|
lines = append(lines, "Copyright (C) "+strings.Join(years, ", ")+" "+name) |
||||||
|
} |
||||||
|
if len(lines) == 0 { |
||||||
|
lines = []string{defaultCopyright} |
||||||
|
} |
||||||
|
sort.Strings(lines) |
||||||
|
return strings.Join(lines, "\n\t") |
||||||
|
} |
||||||
|
|
||||||
|
func (i info) License() string { |
||||||
|
if i.gpl { |
||||||
|
return "General Public License" |
||||||
|
} else { |
||||||
|
return "Lesser General Public License" |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func (i info) ShortLicense() string { |
||||||
|
if i.gpl { |
||||||
|
return "GPL" |
||||||
|
} else { |
||||||
|
return "LGPL" |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func (i *info) addAuthorYear(name, year string) { |
||||||
|
for _, y := range i.authors[name] { |
||||||
|
if y == year { |
||||||
|
return |
||||||
|
} |
||||||
|
} |
||||||
|
i.authors[name] = append(i.authors[name], year) |
||||||
|
sort.Strings(i.authors[name]) |
||||||
|
} |
||||||
|
|
||||||
|
func main() { |
||||||
|
files := make(chan string) |
||||||
|
infos := make(chan *info) |
||||||
|
wg := new(sync.WaitGroup) |
||||||
|
|
||||||
|
go getFiles(files) |
||||||
|
for i := runtime.NumCPU(); i >= 0; i-- { |
||||||
|
// getting file info is slow and needs to be parallel
|
||||||
|
wg.Add(1) |
||||||
|
go getInfo(files, infos, wg) |
||||||
|
} |
||||||
|
go func() { wg.Wait(); close(infos) }() |
||||||
|
writeLicenses(infos) |
||||||
|
} |
||||||
|
|
||||||
|
func getFiles(out chan<- string) { |
||||||
|
cmd := exec.Command("git", "ls-tree", "-r", "--name-only", "HEAD") |
||||||
|
err := doLines(cmd, func(line string) { |
||||||
|
for _, p := range skipPrefixes { |
||||||
|
if strings.HasPrefix(line, p) { |
||||||
|
return |
||||||
|
} |
||||||
|
} |
||||||
|
ext := path.Ext(line) |
||||||
|
for _, wantExt := range extensions { |
||||||
|
if ext == wantExt { |
||||||
|
goto send |
||||||
|
} |
||||||
|
} |
||||||
|
return |
||||||
|
|
||||||
|
send: |
||||||
|
out <- line |
||||||
|
}) |
||||||
|
if err != nil { |
||||||
|
fmt.Println("error getting files:", err) |
||||||
|
} |
||||||
|
close(out) |
||||||
|
} |
||||||
|
|
||||||
|
func getInfo(files <-chan string, out chan<- *info, wg *sync.WaitGroup) { |
||||||
|
for file := range files { |
||||||
|
stat, err := os.Lstat(file) |
||||||
|
if err != nil { |
||||||
|
fmt.Printf("ERROR %s: %v\n", file, err) |
||||||
|
continue |
||||||
|
} |
||||||
|
if !stat.Mode().IsRegular() { |
||||||
|
continue |
||||||
|
} |
||||||
|
info, err := fileInfo(file) |
||||||
|
if err != nil { |
||||||
|
fmt.Printf("ERROR %s: %v\n", file, err) |
||||||
|
continue |
||||||
|
} |
||||||
|
info.mode = stat.Mode() |
||||||
|
out <- info |
||||||
|
} |
||||||
|
wg.Done() |
||||||
|
} |
||||||
|
|
||||||
|
func fileInfo(file string) (*info, error) { |
||||||
|
info := &info{file: file, authors: make(map[string][]string)} |
||||||
|
for _, p := range gplPrefixes { |
||||||
|
if strings.HasPrefix(file, p) { |
||||||
|
info.gpl = true |
||||||
|
break |
||||||
|
} |
||||||
|
} |
||||||
|
cmd := exec.Command("git", "log", "--follow", "--find-copies", "--pretty=format:%aI | %aN <%aE>", "--", file) |
||||||
|
err := doLines(cmd, func(line string) { |
||||||
|
sep := strings.IndexByte(line, '|') |
||||||
|
year, name := line[:4], line[sep+2:] |
||||||
|
info.addAuthorYear(name, year) |
||||||
|
}) |
||||||
|
return info, err |
||||||
|
} |
||||||
|
|
||||||
|
func writeLicenses(infos <-chan *info) { |
||||||
|
buf := new(bytes.Buffer) |
||||||
|
for info := range infos { |
||||||
|
content, err := ioutil.ReadFile(info.file) |
||||||
|
if err != nil { |
||||||
|
fmt.Printf("ERROR: couldn't read %s: %v\n", info.file, err) |
||||||
|
continue |
||||||
|
} |
||||||
|
|
||||||
|
// construct new file content
|
||||||
|
buf.Reset() |
||||||
|
licenseT.Execute(buf, info) |
||||||
|
if m := licenseCommentRE.FindIndex(content); m != nil && m[0] == 0 { |
||||||
|
buf.Write(content[m[1]:]) |
||||||
|
} else { |
||||||
|
buf.Write(content) |
||||||
|
} |
||||||
|
|
||||||
|
if !bytes.Equal(content, buf.Bytes()) { |
||||||
|
fmt.Println("writing", info.ShortLicense(), info.file) |
||||||
|
if err := ioutil.WriteFile(info.file, buf.Bytes(), info.mode); err != nil { |
||||||
|
fmt.Printf("ERROR: couldn't write %s: %v", info.file, err) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func doLines(cmd *exec.Cmd, f func(string)) error { |
||||||
|
stdout, err := cmd.StdoutPipe() |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
if err := cmd.Start(); err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
s := bufio.NewScanner(stdout) |
||||||
|
for s.Scan() { |
||||||
|
f(s.Text()) |
||||||
|
} |
||||||
|
if s.Err() != nil { |
||||||
|
return s.Err() |
||||||
|
} |
||||||
|
if err := cmd.Wait(); err != nil { |
||||||
|
return fmt.Errorf("%v (for %s)", err, strings.Join(cmd.Args, " ")) |
||||||
|
} |
||||||
|
return nil |
||||||
|
} |
Loading…
Reference in new issue