github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/tools/cmd/godoc/main.go (about) 1 // Copyright 2009 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // godoc: Go Documentation Server 6 7 // Web server tree: 8 // 9 // http://godoc/ main landing page 10 // http://godoc/doc/ serve from $GOROOT/doc - spec, mem, etc. 11 // http://godoc/src/ serve files from $GOROOT/src; .go gets pretty-printed 12 // http://godoc/cmd/ serve documentation about commands 13 // http://godoc/pkg/ serve documentation about packages 14 // (idea is if you say import "compress/zlib", you go to 15 // http://godoc/pkg/compress/zlib) 16 // 17 // Command-line interface: 18 // 19 // godoc packagepath [name ...] 20 // 21 // godoc compress/zlib 22 // - prints doc for package compress/zlib 23 // godoc crypto/block Cipher NewCMAC 24 // - prints doc for Cipher and NewCMAC in package crypto/block 25 26 // +build !appengine 27 28 package main 29 30 import ( 31 "archive/zip" 32 _ "expvar" // to serve /debug/vars 33 "flag" 34 "fmt" 35 "go/build" 36 "log" 37 "net/http" 38 "net/http/httptest" 39 _ "net/http/pprof" // to serve /debug/pprof/* 40 "net/url" 41 "os" 42 "path/filepath" 43 "regexp" 44 "runtime" 45 "strings" 46 47 "golang.org/x/tools/godoc" 48 "golang.org/x/tools/godoc/analysis" 49 "golang.org/x/tools/godoc/static" 50 "golang.org/x/tools/godoc/vfs" 51 "golang.org/x/tools/godoc/vfs/gatefs" 52 "golang.org/x/tools/godoc/vfs/mapfs" 53 "golang.org/x/tools/godoc/vfs/zipfs" 54 ) 55 56 const ( 57 defaultAddr = ":6060" // default webserver address 58 toolsPath = "golang.org/x/tools/cmd/" 59 ) 60 61 var ( 62 // file system to serve 63 // (with e.g.: zip -r go.zip $GOROOT -i \*.go -i \*.html -i \*.css -i \*.js -i \*.txt -i \*.c -i \*.h -i \*.s -i \*.png -i \*.jpg -i \*.sh -i favicon.ico) 64 zipfile = flag.String("zip", "", "zip file providing the file system to serve; disabled if empty") 65 66 // file-based index 67 writeIndex = flag.Bool("write_index", false, "write index to a file; the file name must be specified with -index_files") 68 69 analysisFlag = flag.String("analysis", "", `comma-separated list of analyses to perform (supported: type, pointer). See http://golang.org/lib/godoc/analysis/help.html`) 70 71 // network 72 httpAddr = flag.String("http", "", "HTTP service address (e.g., '"+defaultAddr+"')") 73 serverAddr = flag.String("server", "", "webserver address for command line searches") 74 75 // layout control 76 html = flag.Bool("html", false, "print HTML in command-line mode") 77 srcMode = flag.Bool("src", false, "print (exported) source in command-line mode") 78 urlFlag = flag.String("url", "", "print HTML for named URL") 79 80 // command-line searches 81 query = flag.Bool("q", false, "arguments are considered search queries") 82 83 verbose = flag.Bool("v", false, "verbose mode") 84 85 // file system roots 86 // TODO(gri) consider the invariant that goroot always end in '/' 87 goroot = flag.String("goroot", runtime.GOROOT(), "Go root directory") 88 89 // layout control 90 tabWidth = flag.Int("tabwidth", 4, "tab width") 91 showTimestamps = flag.Bool("timestamps", false, "show timestamps with directory listings") 92 templateDir = flag.String("templates", "", "directory containing alternate template files") 93 showPlayground = flag.Bool("play", false, "enable playground in web interface") 94 showExamples = flag.Bool("ex", false, "show examples in command line mode") 95 declLinks = flag.Bool("links", true, "link identifiers to their declarations") 96 97 // search index 98 indexEnabled = flag.Bool("index", false, "enable search index") 99 indexFiles = flag.String("index_files", "", "glob pattern specifying index files; if not empty, the index is read from these files in sorted order") 100 indexInterval = flag.Duration("index_interval", 0, "interval of indexing; 0 for default (5m), negative to only index once at startup") 101 maxResults = flag.Int("maxresults", 10000, "maximum number of full text search results shown") 102 indexThrottle = flag.Float64("index_throttle", 0.75, "index throttle value; 0.0 = no time allocated, 1.0 = full throttle") 103 104 // source code notes 105 notesRx = flag.String("notes", "BUG", "regular expression matching note markers to show") 106 ) 107 108 func usage() { 109 fmt.Fprintf(os.Stderr, 110 "usage: godoc package [name ...]\n"+ 111 " godoc -http="+defaultAddr+"\n") 112 flag.PrintDefaults() 113 os.Exit(2) 114 } 115 116 func loggingHandler(h http.Handler) http.Handler { 117 return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { 118 log.Printf("%s\t%s", req.RemoteAddr, req.URL) 119 h.ServeHTTP(w, req) 120 }) 121 } 122 123 func handleURLFlag() { 124 // Try up to 10 fetches, following redirects. 125 urlstr := *urlFlag 126 for i := 0; i < 10; i++ { 127 // Prepare request. 128 u, err := url.Parse(urlstr) 129 if err != nil { 130 log.Fatal(err) 131 } 132 req := &http.Request{ 133 URL: u, 134 } 135 136 // Invoke default HTTP handler to serve request 137 // to our buffering httpWriter. 138 w := httptest.NewRecorder() 139 http.DefaultServeMux.ServeHTTP(w, req) 140 141 // Return data, error, or follow redirect. 142 switch w.Code { 143 case 200: // ok 144 os.Stdout.Write(w.Body.Bytes()) 145 return 146 case 301, 302, 303, 307: // redirect 147 redirect := w.HeaderMap.Get("Location") 148 if redirect == "" { 149 log.Fatalf("HTTP %d without Location header", w.Code) 150 } 151 urlstr = redirect 152 default: 153 log.Fatalf("HTTP error %d", w.Code) 154 } 155 } 156 log.Fatalf("too many redirects") 157 } 158 159 func main() { 160 flag.Usage = usage 161 flag.Parse() 162 163 playEnabled = *showPlayground 164 165 // Check usage: either server and no args, command line and args, or index creation mode 166 if (*httpAddr != "" || *urlFlag != "") != (flag.NArg() == 0) && !*writeIndex { 167 usage() 168 } 169 170 var fsGate chan bool 171 fsGate = make(chan bool, 20) 172 173 // Determine file system to use. 174 if *zipfile == "" { 175 // use file system of underlying OS 176 rootfs := gatefs.New(vfs.OS(*goroot), fsGate) 177 fs.Bind("/", rootfs, "/", vfs.BindReplace) 178 } else { 179 // use file system specified via .zip file (path separator must be '/') 180 rc, err := zip.OpenReader(*zipfile) 181 if err != nil { 182 log.Fatalf("%s: %s\n", *zipfile, err) 183 } 184 defer rc.Close() // be nice (e.g., -writeIndex mode) 185 fs.Bind("/", zipfs.New(rc, *zipfile), *goroot, vfs.BindReplace) 186 } 187 if *templateDir != "" { 188 fs.Bind("/lib/godoc", vfs.OS(*templateDir), "/", vfs.BindBefore) 189 } else { 190 fs.Bind("/lib/godoc", mapfs.New(static.Files), "/", vfs.BindReplace) 191 } 192 193 // Bind $GOPATH trees into Go root. 194 for _, p := range filepath.SplitList(build.Default.GOPATH) { 195 fs.Bind("/src", gatefs.New(vfs.OS(p), fsGate), "/src", vfs.BindAfter) 196 } 197 198 httpMode := *httpAddr != "" 199 200 var typeAnalysis, pointerAnalysis bool 201 if *analysisFlag != "" { 202 for _, a := range strings.Split(*analysisFlag, ",") { 203 switch a { 204 case "type": 205 typeAnalysis = true 206 case "pointer": 207 pointerAnalysis = true 208 default: 209 log.Fatalf("unknown analysis: %s", a) 210 } 211 } 212 } 213 214 corpus := godoc.NewCorpus(fs) 215 corpus.Verbose = *verbose 216 corpus.MaxResults = *maxResults 217 corpus.IndexEnabled = *indexEnabled && httpMode 218 if *maxResults == 0 { 219 corpus.IndexFullText = false 220 } 221 corpus.IndexFiles = *indexFiles 222 corpus.IndexDirectory = indexDirectoryDefault 223 corpus.IndexThrottle = *indexThrottle 224 corpus.IndexInterval = *indexInterval 225 if *writeIndex { 226 corpus.IndexThrottle = 1.0 227 corpus.IndexEnabled = true 228 } 229 if *writeIndex || httpMode || *urlFlag != "" { 230 if err := corpus.Init(); err != nil { 231 log.Fatal(err) 232 } 233 } 234 235 pres = godoc.NewPresentation(corpus) 236 pres.TabWidth = *tabWidth 237 pres.ShowTimestamps = *showTimestamps 238 pres.ShowPlayground = *showPlayground 239 pres.ShowExamples = *showExamples 240 pres.DeclLinks = *declLinks 241 pres.SrcMode = *srcMode 242 pres.HTMLMode = *html 243 if *notesRx != "" { 244 pres.NotesRx = regexp.MustCompile(*notesRx) 245 } 246 247 readTemplates(pres, httpMode || *urlFlag != "") 248 registerHandlers(pres) 249 250 if *writeIndex { 251 // Write search index and exit. 252 if *indexFiles == "" { 253 log.Fatal("no index file specified") 254 } 255 256 log.Println("initialize file systems") 257 *verbose = true // want to see what happens 258 259 corpus.UpdateIndex() 260 261 log.Println("writing index file", *indexFiles) 262 f, err := os.Create(*indexFiles) 263 if err != nil { 264 log.Fatal(err) 265 } 266 index, _ := corpus.CurrentIndex() 267 _, err = index.WriteTo(f) 268 if err != nil { 269 log.Fatal(err) 270 } 271 272 log.Println("done") 273 return 274 } 275 276 // Print content that would be served at the URL *urlFlag. 277 if *urlFlag != "" { 278 handleURLFlag() 279 return 280 } 281 282 if httpMode { 283 // HTTP server mode. 284 var handler http.Handler = http.DefaultServeMux 285 if *verbose { 286 log.Printf("Go Documentation Server") 287 log.Printf("version = %s", runtime.Version()) 288 log.Printf("address = %s", *httpAddr) 289 log.Printf("goroot = %s", *goroot) 290 log.Printf("tabwidth = %d", *tabWidth) 291 switch { 292 case !*indexEnabled: 293 log.Print("search index disabled") 294 case *maxResults > 0: 295 log.Printf("full text index enabled (maxresults = %d)", *maxResults) 296 default: 297 log.Print("identifier search index enabled") 298 } 299 fs.Fprint(os.Stderr) 300 handler = loggingHandler(handler) 301 } 302 303 // Initialize search index. 304 if *indexEnabled { 305 go corpus.RunIndexer() 306 } 307 308 // Start type/pointer analysis. 309 if typeAnalysis || pointerAnalysis { 310 go analysis.Run(pointerAnalysis, &corpus.Analysis) 311 } 312 313 // Start http server. 314 if err := http.ListenAndServe(*httpAddr, handler); err != nil { 315 log.Fatalf("ListenAndServe %s: %v", *httpAddr, err) 316 } 317 318 return 319 } 320 321 if *query { 322 handleRemoteSearch() 323 return 324 } 325 326 if err := godoc.CommandLine(os.Stdout, fs, pres, flag.Args()); err != nil { 327 log.Print(err) 328 } 329 }