github.com/linapex/ethereum-go-chinese@v0.0.0-20190316121929-f8b7a73c3fa1/build/update-license.go (about)

     1  
     2  //<developer>
     3  //    <name>linapex 曹一峰</name>
     4  //    <email>linapex@163.com</email>
     5  //    <wx>superexc</wx>
     6  //    <qqgroup>128148617</qqgroup>
     7  //    <url>https://jsq.ink</url>
     8  //    <role>pku engineer</role>
     9  //    <date>2019-03-16 19:16:32</date>
    10  //</624450066853138432>
    11  
    12  
    13  //+不建
    14  
    15  /*
    16  此命令在所有源文件的基础上生成GPL许可证头。
    17  您可以每月运行一次,在剪切发布之前或只是
    18  无论什么时候你喜欢。
    19  
    20   运行update-license.go
    21  
    22  所有作者(贡献代码的人)都列在
    23  作者档案。使用
    24  邮件映射文件。可以使用.mailmap设置规范名称和
    25  每个作者的地址。有关
    26  .mailmap格式。
    27  
    28  请查看结果差异,以检查是否正确
    29  执行版权分配。
    30  **/
    31  
    32  
    33  package main
    34  
    35  import (
    36  	"bufio"
    37  	"bytes"
    38  	"fmt"
    39  	"io/ioutil"
    40  	"log"
    41  	"os"
    42  	"os/exec"
    43  	"path/filepath"
    44  	"regexp"
    45  	"runtime"
    46  	"sort"
    47  	"strconv"
    48  	"strings"
    49  	"sync"
    50  	"text/template"
    51  	"time"
    52  )
    53  
    54  var (
    55  //只考虑具有这些扩展名的文件
    56  	extensions = []string{".go", ".js", ".qml"}
    57  
    58  //将跳过具有任何这些前缀的路径
    59  	skipPrefixes = []string{
    60  //镗孔材料
    61  		"vendor/", "tests/testdata/", "build/",
    62  //不要保留自动贩卖的来源
    63  		"cmd/internal/browser",
    64  		"consensus/ethash/xor.go",
    65  		"crypto/bn256/",
    66  		"crypto/ecies/",
    67  		"crypto/secp256k1/curve.go",
    68  		"crypto/sha3/",
    69  		"internal/jsre/deps",
    70  		"log/",
    71  		"common/bitutil/bitutil",
    72  //不许可生成的文件
    73  		"contracts/chequebook/contract/code.go",
    74  	}
    75  
    76  //具有此前缀的路径被授权为gpl。所有其他文件都是lgpl。
    77  	gplPrefixes = []string{"cmd/"}
    78  
    79  //此regexp必须与
    80  //每个文件的开头。
    81  licenseCommentRE = regexp.MustCompile(`^//\s*(版权此文件是的一部分)。*?n?//*?\n)*\n*`
    82  
    83  //
    84  	authorsFileHeader = "# This is the official list of go-ethereum authors for copyright purposes.\n\n"
    85  )
    86  
    87  //此模板生成许可证注释。
    88  //它的输入是一个信息结构。
    89  var licenseT = template.Must(template.New("").Parse(`
    90  //版权.year Go Ethereum作者
    91  //此文件是.whole false的一部分。
    92  //
    93  //.整个真是免费软件:您可以重新发布和/或修改它
    94  //根据GNU许可证的条款
    95  //自由软件基金会,或者许可证的第3版,或者
    96  //(由您选择)任何更高版本。
    97  //
    98  //。整个真分布的目的是希望它有用,
    99  //但没有任何保证;甚至没有
   100  //适销性或特定用途的适用性。见
   101  //GNU许可证了解更多详细信息。
   102  //
   103  //您应该已经收到一份GNU许可证的副本。
   104  //连同。整个错误。如果没有,请参见<http://www.gnu.org/licenses/>。
   105  
   106  `[1:]))
   107  
   108  type info struct {
   109  	file string
   110  	Year int64
   111  }
   112  
   113  func (i info) License() string {
   114  	if i.gpl() {
   115  		return "General Public License"
   116  	}
   117  	return "Lesser General Public License"
   118  }
   119  
   120  func (i info) ShortLicense() string {
   121  	if i.gpl() {
   122  		return "GPL"
   123  	}
   124  	return "LGPL"
   125  }
   126  
   127  func (i info) Whole(startOfSentence bool) string {
   128  	if i.gpl() {
   129  		return "go-ethereum"
   130  	}
   131  	if startOfSentence {
   132  		return "The go-ethereum library"
   133  	}
   134  	return "the go-ethereum library"
   135  }
   136  
   137  func (i info) gpl() bool {
   138  	for _, p := range gplPrefixes {
   139  		if strings.HasPrefix(i.file, p) {
   140  			return true
   141  		}
   142  	}
   143  	return false
   144  }
   145  
   146  func main() {
   147  	var (
   148  		files = getFiles()
   149  		filec = make(chan string)
   150  		infoc = make(chan *info, 20)
   151  		wg    sync.WaitGroup
   152  	)
   153  
   154  	writeAuthors(files)
   155  
   156  	go func() {
   157  		for _, f := range files {
   158  			filec <- f
   159  		}
   160  		close(filec)
   161  	}()
   162  	for i := runtime.NumCPU(); i >= 0; i-- {
   163  //获取文件信息很慢,需要并行处理。
   164  //它遍历每个文件的git历史记录。
   165  		wg.Add(1)
   166  		go getInfo(filec, infoc, &wg)
   167  	}
   168  	go func() {
   169  		wg.Wait()
   170  		close(infoc)
   171  	}()
   172  	writeLicenses(infoc)
   173  }
   174  
   175  func skipFile(path string) bool {
   176  	if strings.Contains(path, "/testdata/") {
   177  		return true
   178  	}
   179  	for _, p := range skipPrefixes {
   180  		if strings.HasPrefix(path, p) {
   181  			return true
   182  		}
   183  	}
   184  	return false
   185  }
   186  
   187  func getFiles() []string {
   188  	cmd := exec.Command("git", "ls-tree", "-r", "--name-only", "HEAD")
   189  	var files []string
   190  	err := doLines(cmd, func(line string) {
   191  		if skipFile(line) {
   192  			return
   193  		}
   194  		ext := filepath.Ext(line)
   195  		for _, wantExt := range extensions {
   196  			if ext == wantExt {
   197  				goto keep
   198  			}
   199  		}
   200  		return
   201  	keep:
   202  		files = append(files, line)
   203  	})
   204  	if err != nil {
   205  		log.Fatal("error getting files:", err)
   206  	}
   207  	return files
   208  }
   209  
   210  var authorRegexp = regexp.MustCompile(`\s*[0-9]+\s*(.*)`)
   211  
   212  func gitAuthors(files []string) []string {
   213  	cmds := []string{"shortlog", "-s", "-n", "-e", "HEAD", "--"}
   214  	cmds = append(cmds, files...)
   215  	cmd := exec.Command("git", cmds...)
   216  	var authors []string
   217  	err := doLines(cmd, func(line string) {
   218  		m := authorRegexp.FindStringSubmatch(line)
   219  		if len(m) > 1 {
   220  			authors = append(authors, m[1])
   221  		}
   222  	})
   223  	if err != nil {
   224  		log.Fatalln("error getting authors:", err)
   225  	}
   226  	return authors
   227  }
   228  
   229  func readAuthors() []string {
   230  	content, err := ioutil.ReadFile("AUTHORS")
   231  	if err != nil && !os.IsNotExist(err) {
   232  		log.Fatalln("error reading AUTHORS:", err)
   233  	}
   234  	var authors []string
   235  	for _, a := range bytes.Split(content, []byte("\n")) {
   236  		if len(a) > 0 && a[0] != '#' {
   237  			authors = append(authors, string(a))
   238  		}
   239  	}
   240  //通过.mailmap重新传输现有作者。
   241  //这将捕获电子邮件地址更改。
   242  	authors = mailmapLookup(authors)
   243  	return authors
   244  }
   245  
   246  func mailmapLookup(authors []string) []string {
   247  	if len(authors) == 0 {
   248  		return nil
   249  	}
   250  	cmds := []string{"check-mailmap", "--"}
   251  	cmds = append(cmds, authors...)
   252  	cmd := exec.Command("git", cmds...)
   253  	var translated []string
   254  	err := doLines(cmd, func(line string) {
   255  		translated = append(translated, line)
   256  	})
   257  	if err != nil {
   258  		log.Fatalln("error translating authors:", err)
   259  	}
   260  	return translated
   261  }
   262  
   263  func writeAuthors(files []string) {
   264  	merge := make(map[string]bool)
   265  //添加Git作为贡献者X报告的作者。
   266  //这是作者信息的主要来源。
   267  	for _, a := range gitAuthors(files) {
   268  		merge[a] = true
   269  	}
   270  //从文件添加现有作者。这应该确保我们
   271  //永远不要失去作者,即使Git不再列出他们。我们也可以
   272  //以这种方式手动添加作者。
   273  	for _, a := range readAuthors() {
   274  		merge[a] = true
   275  	}
   276  //将排序的作者列表写回文件。
   277  	var result []string
   278  	for a := range merge {
   279  		result = append(result, a)
   280  	}
   281  	sort.Strings(result)
   282  	content := new(bytes.Buffer)
   283  	content.WriteString(authorsFileHeader)
   284  	for _, a := range result {
   285  		content.WriteString(a)
   286  		content.WriteString("\n")
   287  	}
   288  	fmt.Println("writing AUTHORS")
   289  	if err := ioutil.WriteFile("AUTHORS", content.Bytes(), 0644); err != nil {
   290  		log.Fatalln(err)
   291  	}
   292  }
   293  
   294  func getInfo(files <-chan string, out chan<- *info, wg *sync.WaitGroup) {
   295  	for file := range files {
   296  		stat, err := os.Lstat(file)
   297  		if err != nil {
   298  			fmt.Printf("ERROR %s: %v\n", file, err)
   299  			continue
   300  		}
   301  		if !stat.Mode().IsRegular() {
   302  			continue
   303  		}
   304  		if isGenerated(file) {
   305  			continue
   306  		}
   307  		info, err := fileInfo(file)
   308  		if err != nil {
   309  			fmt.Printf("ERROR %s: %v\n", file, err)
   310  			continue
   311  		}
   312  		out <- info
   313  	}
   314  	wg.Done()
   315  }
   316  
   317  func isGenerated(file string) bool {
   318  	fd, err := os.Open(file)
   319  	if err != nil {
   320  		return false
   321  	}
   322  	defer fd.Close()
   323  	buf := make([]byte, 2048)
   324  	n, _ := fd.Read(buf)
   325  	buf = buf[:n]
   326  	for _, l := range bytes.Split(buf, []byte("\n")) {
   327  if bytes.HasPrefix(l, []byte("//代码生成”));
   328  			return true
   329  		}
   330  	}
   331  	return false
   332  }
   333  
   334  //fileinfo查找提交给定文件的最低年份。
   335  func fileInfo(file string) (*info, error) {
   336  	info := &info{file: file, Year: int64(time.Now().Year())}
   337  	cmd := exec.Command("git", "log", "--follow", "--find-renames=80", "--find-copies=80", "--pretty=format:%ai", "--", file)
   338  	err := doLines(cmd, func(line string) {
   339  		y, err := strconv.ParseInt(line[:4], 10, 64)
   340  		if err != nil {
   341  			fmt.Printf("cannot parse year: %q", line[:4])
   342  		}
   343  		if y < info.Year {
   344  			info.Year = y
   345  		}
   346  	})
   347  	return info, err
   348  }
   349  
   350  func writeLicenses(infos <-chan *info) {
   351  	for i := range infos {
   352  		writeLicense(i)
   353  	}
   354  }
   355  
   356  func writeLicense(info *info) {
   357  	fi, err := os.Stat(info.file)
   358  	if os.IsNotExist(err) {
   359  		fmt.Println("skipping (does not exist)", info.file)
   360  		return
   361  	}
   362  	if err != nil {
   363  		log.Fatalf("error stat'ing %s: %v\n", info.file, err)
   364  	}
   365  	content, err := ioutil.ReadFile(info.file)
   366  	if err != nil {
   367  		log.Fatalf("error reading %s: %v\n", info.file, err)
   368  	}
   369  //构造新的文件内容。
   370  	buf := new(bytes.Buffer)
   371  	licenseT.Execute(buf, info)
   372  	if m := licenseCommentRE.FindIndex(content); m != nil && m[0] == 0 {
   373  		buf.Write(content[:m[0]])
   374  		buf.Write(content[m[1]:])
   375  	} else {
   376  		buf.Write(content)
   377  	}
   378  //将其写入文件。
   379  	if bytes.Equal(content, buf.Bytes()) {
   380  		fmt.Println("skipping (no changes)", info.file)
   381  		return
   382  	}
   383  	fmt.Println("writing", info.ShortLicense(), info.file)
   384  	if err := ioutil.WriteFile(info.file, buf.Bytes(), fi.Mode()); err != nil {
   385  		log.Fatalf("error writing %s: %v", info.file, err)
   386  	}
   387  }
   388  
   389  func doLines(cmd *exec.Cmd, f func(string)) error {
   390  	stdout, err := cmd.StdoutPipe()
   391  	if err != nil {
   392  		return err
   393  	}
   394  	if err := cmd.Start(); err != nil {
   395  		return err
   396  	}
   397  	s := bufio.NewScanner(stdout)
   398  	for s.Scan() {
   399  		f(s.Text())
   400  	}
   401  	if s.Err() != nil {
   402  		return s.Err()
   403  	}
   404  	if err := cmd.Wait(); err != nil {
   405  		return fmt.Errorf("%v (for %s)", err, strings.Join(cmd.Args, " "))
   406  	}
   407  	return nil
   408  }
   409