add license tool and initial mailmap
This commit is contained in:
		
							parent
							
								
									032ab66529
								
							
						
					
					
						commit
						ec7cb9981c
					
				
							
								
								
									
										12
									
								
								.mailmap
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								.mailmap
									
									
									
									
									
										Normal file
									
								
							| @ -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> | ||||
							
								
								
									
										248
									
								
								update-license.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										248
									
								
								update-license.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,248 @@ | ||||
| // +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-licenses.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) error { | ||||
| 	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) | ||||
| 		} | ||||
| 
 | ||||
| 		// 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 { | ||||
| 				return err | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| 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
	
	Block a user