github.com/golang/dep@v0.5.4/hack/licenseok/main.go (about) 1 // +build ignore 2 3 // Copyright 2017 The Go Authors. All rights reserved. 4 // Use of this source code is governed by a BSD-style 5 // license that can be found in the LICENSE file. 6 7 // Checks if all files have the license header, a lot of this is based off 8 // https://github.com/google/addlicense. 9 package main 10 11 import ( 12 "bytes" 13 "flag" 14 "fmt" 15 "io/ioutil" 16 "log" 17 "os" 18 "path/filepath" 19 "sync" 20 "time" 21 ) 22 23 const helpText = `Usage: licenseok [flags] pattern [pattern ...] 24 This program ensures source code files have copyright license headers 25 by scanning directory patterns recursively. 26 The pattern argument can be provided multiple times, and may also refer 27 to single files. 28 Flags: 29 ` 30 31 const tmpl = `The Go Authors. All rights reserved. 32 Use of this source code is governed by a BSD-style 33 license that can be found in the LICENSE file.` 34 35 var ( 36 update bool 37 ) 38 39 type file struct { 40 path string 41 mode os.FileMode 42 } 43 44 func init() { 45 flag.BoolVar(&update, "u", false, "modifies all source files in place and avoids adding a license header to any file that already has one.") 46 47 flag.Usage = func() { 48 fmt.Fprintln(os.Stderr, helpText) 49 flag.PrintDefaults() 50 } 51 52 flag.Parse() 53 54 if flag.NArg() == 0 { 55 flag.Usage() 56 os.Exit(1) 57 } 58 } 59 60 func main() { 61 exitStatus := 0 62 63 // process at most 1000 files in parallel 64 ch := make(chan *file, 1000) 65 done := make(chan struct{}) 66 go func() { 67 var wg sync.WaitGroup 68 for f := range ch { 69 wg.Add(1) 70 go func(f *file) { 71 b, err := ioutil.ReadFile(f.path) 72 if err != nil { 73 log.Printf("%s: %v", f.path, err) 74 exitStatus = 1 75 } 76 77 if !hasLicense(b) { 78 if !update { 79 fmt.Fprintln(os.Stderr, f.path) 80 exitStatus = 1 81 } else { 82 fmt.Fprintln(os.Stdout, f.path) 83 if err := addLicense(b, f.path, f.mode); err != nil { 84 log.Printf("%s: %v", f.path, err) 85 exitStatus = 1 86 } 87 } 88 } 89 90 wg.Done() 91 }(f) 92 } 93 wg.Wait() 94 close(done) 95 }() 96 97 for _, d := range flag.Args() { 98 walk(ch, d) 99 } 100 close(ch) 101 <-done 102 os.Exit(exitStatus) 103 } 104 105 func walk(ch chan<- *file, start string) { 106 filepath.Walk(start, func(path string, fi os.FileInfo, err error) error { 107 if err != nil { 108 log.Printf("%s error: %v", path, err) 109 return nil 110 } 111 if fi.IsDir() { 112 return nil 113 } 114 ch <- &file{path, fi.Mode()} 115 return nil 116 }) 117 } 118 119 func addLicense(b []byte, path string, fmode os.FileMode) error { 120 var lic []byte 121 var err error 122 switch filepath.Ext(path) { 123 default: 124 return nil 125 case ".c", ".h": 126 lic, err = prefix("/*", " * ", " */") 127 case ".js", ".css": 128 lic, err = prefix("/**", " * ", " */") 129 case ".cc", ".cpp", ".cs", ".go", ".hh", ".hpp", ".java", ".m", ".mm", ".proto", ".rs", ".scala", ".swift", ".dart": 130 lic, err = prefix("", "// ", "") 131 case ".py", ".sh": 132 lic, err = prefix("", "# ", "") 133 case ".el", ".lisp": 134 lic, err = prefix("", ";; ", "") 135 case ".erl": 136 lic, err = prefix("", "% ", "") 137 case ".hs": 138 lic, err = prefix("", "-- ", "") 139 case ".html", ".xml": 140 lic, err = prefix("<!--", " ", "-->") 141 case ".php": 142 lic, err = prefix("<?php", "// ", "?>") 143 } 144 if err != nil || lic == nil { 145 return err 146 } 147 148 line := hashBang(b) 149 if len(line) > 0 { 150 b = b[len(line):] 151 if line[len(line)-1] != '\n' { 152 line = append(line, '\n') 153 } 154 lic = append(line, lic...) 155 } 156 b = append(lic, b...) 157 return ioutil.WriteFile(path, b, fmode) 158 } 159 160 func hashBang(b []byte) []byte { 161 var line = make([]byte, 0, len(b)) 162 for _, c := range b { 163 line = append(line, c) 164 if c == '\n' { 165 break 166 } 167 } 168 if bytes.HasPrefix(line, []byte("#!")) { 169 return line 170 } 171 return nil 172 } 173 174 func hasLicense(b []byte) bool { 175 n := 100 176 if len(b) < 100 { 177 n = len(b) 178 } 179 return bytes.Contains(bytes.ToLower(b[:n]), []byte("copyright")) 180 } 181 182 // prefix will execute a license template and prefix the result with top, middle and bottom. 183 func prefix(top, mid, bot string) ([]byte, error) { 184 buf := bytes.NewBufferString(fmt.Sprintf("Copyright %d %s", time.Now().Year(), tmpl)) 185 var out bytes.Buffer 186 if top != "" { 187 out.WriteString(top) 188 out.WriteRune('\n') 189 } 190 out.WriteString(mid) 191 for _, c := range buf.Bytes() { 192 out.WriteByte(c) 193 if c == '\n' { 194 out.WriteString(mid) 195 } 196 } 197 if bot != "" { 198 out.WriteRune('\n') 199 out.WriteString(bot) 200 } 201 out.Write([]byte{'\n', '\n'}) 202 return out.Bytes(), nil 203 }