Пример #1
0
// loadPackage scans directory named by arg,
// which is either an import path or a file system path
// (if the latter, must be rooted or begin with . or ..),
// and returns a *Package describing the package
// found in that directory.
func loadPackage(arg string, stk *importStack) *Package {
	stk.push(arg)
	defer stk.pop()

	// Check package cache.
	if p := packageCache[arg]; p != nil {
		return reusePackage(p, stk)
	}

	// Find basic information about package path.
	t, importPath, err := build.FindTree(arg)
	dir := ""
	// Maybe it is a standard command.
	if err != nil && strings.HasPrefix(arg, "cmd/") {
		goroot := build.Path[0]
		p := filepath.Join(goroot.Path, "src", arg)
		if st, err1 := os.Stat(p); err1 == nil && st.IsDir() {
			t = goroot
			importPath = arg
			dir = p
			err = nil
		}
	}
	// Maybe it is a path to a standard command.
	if err != nil && (filepath.IsAbs(arg) || isLocalPath(arg)) {
		arg, _ := filepath.Abs(arg)
		goroot := build.Path[0]
		cmd := filepath.Join(goroot.Path, "src", "cmd") + string(filepath.Separator)
		if st, err1 := os.Stat(arg); err1 == nil && st.IsDir() && strings.HasPrefix(arg, cmd) {
			t = goroot
			importPath = filepath.FromSlash(arg[len(cmd):])
			dir = arg
			err = nil
		}
	}
	if err != nil {
		p := &Package{
			ImportPath: arg,
			Error: &PackageError{
				ImportStack: stk.copy(),
				Err:         err.Error(),
			},
			Incomplete: true,
		}
		packageCache[arg] = p
		return p
	}

	if dir == "" {
		dir = filepath.Join(t.SrcDir(), filepath.FromSlash(importPath))
	}

	// Maybe we know the package by its directory.
	if p := packageCache[dir]; p != nil {
		packageCache[importPath] = p
		return reusePackage(p, stk)
	}

	return scanPackage(&buildContext, t, arg, importPath, dir, stk)
}
Пример #2
0
func TestTool(t *testing.T) {
	if e := Remotize(Value2Spec("github.com/josvazg/remotize/tool", new(ToolTester))); e != nil {
		t.Fatal(e)
	}
	dir, e := build.ScanDir(".", false)
	if e != nil {
		t.Fatal(e)
	}
	dir.GoFiles = append(dir.GoFiles, "tool_test.go")
	//fmt.Println("dir", dir)
	tree, pkg, e := build.FindTree(".")
	if e != nil {
		t.Fatal(e)
	}
	//fmt.Println("tree", tree, "pkg", pkg)
	script, e := build.Build(tree, pkg, dir)
	if e != nil {
		t.Fatal(e)
	}
	//fmt.Println("script", script)
	e = script.Run()
	if e != nil {
		t.Fatal(e)
	}
}
Пример #3
0
func main() {
	flag.Parse()

	// source of unique numbers
	go func() {
		for i := 0; ; i++ {
			uniq <- i
		}
	}()

	// set archChar
	var err os.Error
	archChar, err = build.ArchChar(runtime.GOARCH)
	if err != nil {
		log.Fatal(err)
	}

	// find and serve the go tour files
	t, _, err := build.FindTree(basePkg)
	if err != nil {
		log.Fatalf("Couldn't find tour files: %v", err)
	}
	root := filepath.Join(t.SrcDir(), basePkg)
	root := basePkg
	log.Println("Serving content from", root)
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		if r.URL.Path == "/favicon.ico" || r.URL.Path == "/" {
			fn := filepath.Join(root, "static", r.URL.Path[1:])
			http.ServeFile(w, r, fn)
			return
		}
		http.Error(w, "not found", 404)
	})
	http.Handle("/static/", http.FileServer(http.Dir(root)))
	http.HandleFunc("/kill", kill)

	// set include path for ld and gc
	pkgDir = t.PkgDir()

	if !strings.HasPrefix(*httpListen, "127.0.0.1") &&
		!strings.HasPrefix(*httpListen, "localhost") {
		log.Print(localhostWarning)
	}

	log.Printf("Serving at http://%s/", *httpListen)
	log.Fatal(http.ListenAndServe(*httpListen, nil))
}
Пример #4
0
// show godoc of current package
func document(pkg string) {
	// Don't allow trailing '/'
	if strings.HasSuffix(pkg, "/") {
		errorf("%s should not have trailing '/'\n", pkg)
		return
	}

	// Check whether package is local or remote.
	// If remote, download or update it.
	tree, pkg, err := build.FindTree(pkg)

	var dir, baseDir string
	// Download remote packages if not found or forced with -u flag.
	remote := isRemote(pkg)
	if !remote {
		dir = filepath.Join(tree.SrcDir(), filepath.FromSlash(pkg))
	} else {
		baseDir, _ = ioutil.TempDir("", "godocr")
		_, err = download(pkg, baseDir)
		if err != nil {
			errorf("%s: problem downloading: %s\n", pkg, err)
			return
		}
		dir = filepath.Join(baseDir, filepath.FromSlash(pkg))
	}

	// Run godoc either in the standard library or in the checked
	// out repo.
	out, err := runGodoc(dir)
	if err != nil {
		errorf("%s: godoc: %s\n", pkg, err)
	} else {
		fmt.Print(string(out))
	}

	// only clean up after ourselves if we've downloaded a remote
	// directory
	if remote {
		err = os.RemoveAll(baseDir)
		if err != nil {
			errorf("%s: couldn't clean up after ourselves: %s\n", pkg, err)
		}
	}
}
Пример #5
0
// findPkg returns the filename and package id for an import path.
// If no file was found, an empty filename is returned.
func findPkg(path string) (filename, id string) {
	if len(path) == 0 {
		return
	}

	id = path
	var noext string
	switch path[0] {
	default:
		// "x" -> "$GOPATH/pkg/$GOOS_$GOARCH/x.ext", "x"
		tree, pkg, err := build.FindTree(path)
		if err != nil {
			return
		}
		noext = filepath.Join(tree.PkgDir(), pkg)

	case '.':
		// "./x" -> "/this/directory/x.ext", "/this/directory/x"
		cwd, err := os.Getwd()
		if err != nil {
			return
		}
		noext = filepath.Join(cwd, path)
		id = noext

	case '/':
		// "/x" -> "/x.ext", "/x"
		noext = path
	}

	// try extensions
	for _, ext := range pkgExts {
		filename = noext + ext
		if f, err := os.Stat(filename); err == nil && !f.IsDir() {
			return
		}
	}

	filename = "" // not found
	return
}
Пример #6
0
func main() {
	flag.Parse()

	var pkgs []string
	if flag.NArg() > 0 {
		pkgs = flag.Args()
	} else {
		stds, err := exec.Command("go", "list", "std").Output()
		if err != nil {
			log.Fatal(err)
		}
		pkgs = strings.Fields(string(stds))
	}

	tree, _, err := build.FindTree("os") // some known package
	if err != nil {
		log.Fatalf("failed to find tree: %v", err)
	}

	var featureCtx = make(map[string]map[string]bool) // feature -> context name -> true
	for _, context := range contexts {
		w := NewWalker()
		w.context = context
		w.tree = tree

		for _, pkg := range pkgs {
			w.wantedPkg[pkg] = true
		}

		for _, pkg := range pkgs {
			if strings.HasPrefix(pkg, "cmd/") ||
				strings.HasPrefix(pkg, "exp/") ||
				strings.HasPrefix(pkg, "old/") {
				continue
			}
			if !tree.HasSrc(pkg) {
				log.Fatalf("no source in tree for package %q", pkg)
			}
			w.WalkPackage(pkg)
		}
		ctxName := contextName(context)
		for _, f := range w.Features() {
			if featureCtx[f] == nil {
				featureCtx[f] = make(map[string]bool)
			}
			featureCtx[f][ctxName] = true
		}
	}

	var features []string
	for f, cmap := range featureCtx {
		if len(cmap) == len(contexts) {
			features = append(features, f)
			continue
		}
		comma := strings.Index(f, ",")
		for cname := range cmap {
			f2 := fmt.Sprintf("%s (%s)%s", f[:comma], cname, f[comma:])
			features = append(features, f2)
		}
	}
	sort.Strings(features)

	bw := bufio.NewWriter(os.Stdout)
	defer bw.Flush()

	if *checkFile != "" {
		bs, err := ioutil.ReadFile(*checkFile)
		if err != nil {
			log.Fatalf("Error reading file %s: %v", *checkFile, err)
		}
		v1 := strings.Split(string(bs), "\n")
		sort.Strings(v1)
		v2 := features
		take := func(sl *[]string) string {
			s := (*sl)[0]
			*sl = (*sl)[1:]
			return s
		}
		for len(v1) > 0 || len(v2) > 0 {
			switch {
			case len(v2) == 0 || v1[0] < v2[0]:
				fmt.Fprintf(bw, "-%s\n", take(&v1))
			case len(v1) == 0 || v1[0] > v2[0]:
				fmt.Fprintf(bw, "+%s\n", take(&v2))
			default:
				take(&v1)
				take(&v2)
			}
		}
	} else {
		for _, f := range features {
			fmt.Fprintf(bw, "%s\n", f)
		}
	}
}
Пример #7
0
func main() {
	flag.Usage = usage
	flag.Parse()

	// Check usage: either server and no args, or command line and args
	if (*httpAddr != "") != (flag.NArg() == 0) {
		usage()
	}

	if *tabwidth < 0 {
		log.Fatalf("negative tabwidth %d", *tabwidth)
	}

	// Determine file system to use.
	// TODO(gri) - fs and fsHttp should really be the same. Try to unify.
	//           - fsHttp doesn't need to be set up in command-line mode,
	//             same is true for the http handlers in initHandlers.
	if *zipfile == "" {
		// use file system of underlying OS
		*goroot = filepath.Clean(*goroot) // normalize path separator
		fs = OS
		fsHttp = http.Dir(*goroot)
	} else {
		// use file system specified via .zip file (path separator must be '/')
		rc, err := zip.OpenReader(*zipfile)
		if err != nil {
			log.Fatalf("%s: %s\n", *zipfile, err)
		}
		*goroot = path.Join("/", *goroot) // fsHttp paths are relative to '/'
		fs = NewZipFS(rc)
		fsHttp = NewHttpZipFS(rc, *goroot)
	}

	readTemplates()
	initHandlers()

	if *httpAddr != "" {
		// HTTP server mode.
		var handler http.Handler = http.DefaultServeMux
		if *verbose {
			log.Printf("Go Documentation Server")
			log.Printf("version = %s", runtime.Version())
			log.Printf("address = %s", *httpAddr)
			log.Printf("goroot = %s", *goroot)
			log.Printf("tabwidth = %d", *tabwidth)
			switch {
			case !*indexEnabled:
				log.Print("search index disabled")
			case *maxResults > 0:
				log.Printf("full text index enabled (maxresults = %d)", *maxResults)
			default:
				log.Print("identifier search index enabled")
			}
			if !fsMap.IsEmpty() {
				log.Print("user-defined mapping:")
				fsMap.Fprint(os.Stderr)
			}
			handler = loggingHandler(handler)
		}

		registerPublicHandlers(http.DefaultServeMux)
		if *syncCmd != "" {
			http.Handle("/debug/sync", http.HandlerFunc(dosync))
		}

		// Initialize default directory tree with corresponding timestamp.
		// (Do it in a goroutine so that launch is quick.)
		go initFSTree()

		// Initialize directory trees for user-defined file systems (-path flag).
		initDirTrees()

		// Start sync goroutine, if enabled.
		if *syncCmd != "" && *syncMin > 0 {
			syncDelay.set(*syncMin) // initial sync delay
			go func() {
				for {
					dosync(nil, nil)
					delay, _ := syncDelay.get()
					if *verbose {
						log.Printf("next sync in %dmin", delay.(int))
					}
					time.Sleep(int64(delay.(int)) * 60e9)
				}
			}()
		}

		// Start indexing goroutine.
		if *indexEnabled {
			go indexer()
		}

		// Start http server.
		if err := http.ListenAndServe(*httpAddr, handler); err != nil {
			log.Fatalf("ListenAndServe %s: %v", *httpAddr, err)
		}

		return
	}

	// Command line mode.
	if *html {
		packageText = packageHTML
		searchText = packageHTML
	}

	if *query {
		// Command-line queries.
		for i := 0; i < flag.NArg(); i++ {
			res, err := remoteSearch(flag.Arg(i))
			if err != nil {
				log.Fatalf("remoteSearch: %s", err)
			}
			io.Copy(os.Stdout, res.Body)
		}
		return
	}

	// determine paths
	path := flag.Arg(0)
	if len(path) > 0 && path[0] == '.' {
		// assume cwd; don't assume -goroot
		cwd, _ := os.Getwd() // ignore errors
		path = filepath.Join(cwd, path)
	}
	relpath := path
	abspath := path
	if t, pkg, err := build.FindTree(path); err == nil {
		relpath = pkg
		abspath = filepath.Join(t.SrcDir(), pkg)
	} else if !filepath.IsAbs(path) {
		abspath = absolutePath(path, pkgHandler.fsRoot)
	} else {
		relpath = relativeURL(path)
	}

	var mode PageInfoMode
	if *srcMode {
		// only filter exports if we don't have explicit command-line filter arguments
		if flag.NArg() == 1 {
			mode |= exportsOnly
		}
	} else {
		mode = exportsOnly | genDoc
	}
	// TODO(gri): Provide a mechanism (flag?) to select a package
	//            if there are multiple packages in a directory.
	info := pkgHandler.getPageInfo(abspath, relpath, "", mode)

	if info.IsEmpty() {
		// try again, this time assume it's a command
		if !filepath.IsAbs(path) {
			abspath = absolutePath(path, cmdHandler.fsRoot)
		}
		cmdInfo := cmdHandler.getPageInfo(abspath, relpath, "", mode)
		// only use the cmdInfo if it actually contains a result
		// (don't hide errors reported from looking up a package)
		if !cmdInfo.IsEmpty() {
			info = cmdInfo
		}
	}
	if info.Err != nil {
		log.Fatalf("%v", info.Err)
	}

	// If we have more than one argument, use the remaining arguments for filtering
	if flag.NArg() > 1 {
		args := flag.Args()[1:]
		rx := makeRx(args)
		if rx == nil {
			log.Fatalf("illegal regular expression from %v", args)
		}

		filter := func(s string) bool { return rx.MatchString(s) }
		switch {
		case info.PAst != nil:
			ast.FilterFile(info.PAst, filter)
			// Special case: Don't use templates for printing
			// so we only get the filtered declarations without
			// package clause or extra whitespace.
			for i, d := range info.PAst.Decls {
				if i > 0 {
					fmt.Println()
				}
				if *html {
					var buf bytes.Buffer
					writeNode(&buf, info.FSet, d)
					FormatText(os.Stdout, buf.Bytes(), -1, true, "", nil)
				} else {
					writeNode(os.Stdout, info.FSet, d)
				}
				fmt.Println()
			}
			return

		case info.PDoc != nil:
			info.PDoc.Filter(filter)
		}
	}

	if err := packageText.Execute(os.Stdout, info); err != nil {
		log.Printf("packageText.Execute: %s", err)
	}
}
Пример #8
0
// install installs the package named by path, which is needed by parent.
func install(pkg, parent string) {
	// Make sure we're not already trying to install pkg.
	switch visit[pkg] {
	case done:
		return
	case visiting:
		fmt.Fprintf(os.Stderr, "%s: package dependency cycle\n", argv0)
		printDeps(parent)
		fmt.Fprintf(os.Stderr, "\t%s\n", pkg)
		os.Exit(2)
	}
	parents[pkg] = parent
	visit[pkg] = visiting
	defer func() {
		visit[pkg] = done
	}()

	// Don't allow trailing '/'
	if _, f := filepath.Split(pkg); f == "" {
		errorf("%s should not have trailing '/'\n", pkg)
		return
	}

	// Check whether package is local or remote.
	// If remote, download or update it.
	tree, pkg, err := build.FindTree(pkg)
	// Don't build the standard library.
	if err == nil && tree.Goroot && isStandardPath(pkg) {
		if parent == "" {
			errorf("%s: can not goinstall the standard library\n", pkg)
		} else {
			printf("%s: skipping standard library\n", pkg)
		}
		return
	}
	// Download remote packages if not found or forced with -u flag.
	remote, public := isRemote(pkg), false
	if remote {
		if err == build.ErrNotFound || (err == nil && *update) {
			// Download remote package.
			printf("%s: download\n", pkg)
			public, err = download(pkg, tree.SrcDir())
		} else {
			// Test if this is a public repository
			// (for reporting to dashboard).
			m, _ := findPublicRepo(pkg)
			public = m != nil
		}
	}
	if err != nil {
		terrorf(tree, "%s: %v\n", pkg, err)
		return
	}
	dir := filepath.Join(tree.SrcDir(), pkg)

	// Install prerequisites.
	dirInfo, err := build.ScanDir(dir, parent == "")
	if err != nil {
		terrorf(tree, "%s: %v\n", pkg, err)
		return
	}
	if len(dirInfo.GoFiles)+len(dirInfo.CgoFiles) == 0 {
		terrorf(tree, "%s: package has no files\n", pkg)
		return
	}
	for _, p := range dirInfo.Imports {
		if p != "C" {
			install(p, pkg)
		}
	}
	if errors {
		return
	}

	// Install this package.
	if *useMake {
		err := domake(dir, pkg, tree, dirInfo.IsCommand())
		if err != nil {
			terrorf(tree, "%s: install: %v\n", pkg, err)
			return
		}
	} else {
		script, err := build.Build(tree, pkg, dirInfo)
		if err != nil {
			terrorf(tree, "%s: install: %v\n", pkg, err)
			return
		}
		if *nuke {
			printf("%s: nuke\n", pkg)
			script.Nuke()
		} else if *clean {
			printf("%s: clean\n", pkg)
			script.Clean()
		}
		if *doInstall {
			if script.Stale() {
				printf("%s: install\n", pkg)
				if err := script.Run(); err != nil {
					terrorf(tree, "%s: install: %v\n", pkg, err)
					return
				}
			} else {
				printf("%s: up-to-date\n", pkg)
			}
		}
	}

	if remote {
		// mark package as installed in goinstall.log
		logged := logPackage(pkg, tree)

		// report installation to the dashboard if this is the first
		// install from a public repository.
		if logged && public {
			maybeReportToDashboard(pkg)
		}
	}
}
Пример #9
0
// loadPackage scans directory named by arg,
// which is either an import path or a file system path
// (if the latter, must be rooted or begin with . or ..),
// and returns a *Package describing the package
// found in that directory.
func loadPackage(arg string, stk *importStack) *Package {
	stk.push(arg)
	defer stk.pop()

	// Check package cache.
	if p := packageCache[arg]; p != nil {
		return reusePackage(p, stk)
	}

	// Find basic information about package path.
	isCmd := false
	t, importPath, err := build.FindTree(arg)
	dir := ""
	// Maybe it is a standard command.
	if err != nil && strings.HasPrefix(arg, "cmd/") {
		goroot := build.Path[0]
		p := filepath.Join(goroot.Path, "src", arg)
		if st, err1 := os.Stat(p); err1 == nil && st.IsDir() {
			t = goroot
			importPath = arg
			dir = p
			err = nil
			isCmd = true
		}
	}
	// Maybe it is a path to a standard command.
	if err != nil && (filepath.IsAbs(arg) || isLocalPath(arg)) {
		arg, _ := filepath.Abs(arg)
		goroot := build.Path[0]
		cmd := filepath.Join(goroot.Path, "src", "cmd") + string(filepath.Separator)
		if st, err1 := os.Stat(arg); err1 == nil && st.IsDir() && strings.HasPrefix(arg, cmd) {
			t = goroot
			importPath = filepath.FromSlash(arg[len(cmd):])
			dir = arg
			err = nil
			isCmd = true
		}
	}
	if err != nil {
		p := &Package{
			ImportPath: arg,
			Error: &PackageError{
				ImportStack: stk.copy(),
				Err:         err.Error(),
			},
			Incomplete: true,
		}
		packageCache[arg] = p
		return p
	}

	if dir == "" {
		dir = filepath.Join(t.SrcDir(), filepath.FromSlash(importPath))
	}

	// Maybe we know the package by its directory.
	p := packageCache[dir]
	if p != nil {
		packageCache[importPath] = p
		p = reusePackage(p, stk)
	} else {
		p = scanPackage(&buildContext, t, arg, importPath, dir, stk, false)
	}

	// If we loaded the files from the Go root's cmd/ tree,
	// it must be a command (package main).
	if isCmd && p.Error == nil && p.Name != "main" {
		p.Error = &PackageError{
			ImportStack: stk.copy(),
			Err:         fmt.Sprintf("expected package main in %q; found package %s", dir, p.Name),
		}
	}
	return p
}
Пример #10
0
func main() {
	flag.Parse()

	var pkgs []string
	if flag.NArg() > 0 {
		pkgs = flag.Args()
	} else {
		stds, err := exec.Command("go", "list", "std").Output()
		if err != nil {
			log.Fatal(err)
		}
		pkgs = strings.Fields(string(stds))
	}

	w := NewWalker()
	tree, _, err := build.FindTree("os") // some known package
	if err != nil {
		log.Fatalf("failed to find tree: %v", err)
	}

	for _, pkg := range pkgs {
		if strings.HasPrefix(pkg, "cmd/") ||
			strings.HasPrefix(pkg, "exp/") ||
			strings.HasPrefix(pkg, "old/") {
			continue
		}
		if !tree.HasSrc(pkg) {
			log.Fatalf("no source in tree for package %q", pkg)
		}
		pkgSrcDir := filepath.Join(tree.SrcDir(), filepath.FromSlash(pkg))
		w.WalkPackage(pkg, pkgSrcDir)
	}

	bw := bufio.NewWriter(os.Stdout)
	defer bw.Flush()

	if *checkFile != "" {
		bs, err := ioutil.ReadFile(*checkFile)
		if err != nil {
			log.Fatalf("Error reading file %s: %v", *checkFile, err)
		}
		v1 := strings.Split(string(bs), "\n")
		sort.Strings(v1)
		v2 := w.Features()
		take := func(sl *[]string) string {
			s := (*sl)[0]
			*sl = (*sl)[1:]
			return s
		}
		for len(v1) > 0 || len(v2) > 0 {
			switch {
			case len(v2) == 0 || v1[0] < v2[0]:
				fmt.Fprintf(bw, "-%s\n", take(&v1))
			case len(v1) == 0 || v1[0] > v2[0]:
				fmt.Fprintf(bw, "+%s\n", take(&v2))
			default:
				take(&v1)
				take(&v2)
			}
		}
	} else {
		for _, f := range w.Features() {
			fmt.Fprintf(bw, "%s\n", f)
		}
	}
}
Пример #11
0
Файл: main.go Проект: tav/go
func main() {
	flag.Usage = usage
	flag.Parse()

	// Check usage: either server and no args, command line and args, or index creation mode
	if (*httpAddr != "") != (flag.NArg() == 0) && !*writeIndex {
		usage()
	}

	if *tabwidth < 0 {
		log.Fatalf("negative tabwidth %d", *tabwidth)
	}

	// Determine file system to use.
	// TODO(gri) - fs and fsHttp should really be the same. Try to unify.
	//           - fsHttp doesn't need to be set up in command-line mode,
	//             same is true for the http handlers in initHandlers.
	if *zipfile == "" {
		// use file system of underlying OS
		*goroot = filepath.Clean(*goroot) // normalize path separator
		fs = OS
		fsHttp = http.Dir(*goroot)
	} else {
		// use file system specified via .zip file (path separator must be '/')
		rc, err := zip.OpenReader(*zipfile)
		if err != nil {
			log.Fatalf("%s: %s\n", *zipfile, err)
		}
		defer rc.Close()                  // be nice (e.g., -writeIndex mode)
		*goroot = path.Join("/", *goroot) // fsHttp paths are relative to '/'
		fs = NewZipFS(rc)
		fsHttp = NewHttpZipFS(rc, *goroot)
	}

	readTemplates()
	initHandlers()

	if *writeIndex {
		// Write search index and exit.
		if *indexFiles == "" {
			log.Fatal("no index file specified")
		}

		log.Println("initialize file systems")
		*verbose = true // want to see what happens
		initFSTree()
		initDirTrees()

		*indexThrottle = 1
		updateIndex()

		log.Println("writing index file", *indexFiles)
		f, err := os.Create(*indexFiles)
		if err != nil {
			log.Fatal(err)
		}
		index, _ := searchIndex.get()
		err = index.(*Index).Write(f)
		if err != nil {
			log.Fatal(err)
		}

		log.Println("done")
		return
	}

	if *httpAddr != "" {
		// HTTP server mode.
		var handler http.Handler = http.DefaultServeMux
		if *verbose {
			log.Printf("Go Documentation Server")
			log.Printf("version = %s", runtime.Version())
			log.Printf("address = %s", *httpAddr)
			log.Printf("goroot = %s", *goroot)
			log.Printf("tabwidth = %d", *tabwidth)
			switch {
			case !*indexEnabled:
				log.Print("search index disabled")
			case *maxResults > 0:
				log.Printf("full text index enabled (maxresults = %d)", *maxResults)
			default:
				log.Print("identifier search index enabled")
			}
			if !fsMap.IsEmpty() {
				log.Print("user-defined mapping:")
				fsMap.Fprint(os.Stderr)
			}
			handler = loggingHandler(handler)
		}

		registerPublicHandlers(http.DefaultServeMux)
		if *syncCmd != "" {
			http.Handle("/debug/sync", http.HandlerFunc(dosync))
		}

		// Initialize default directory tree with corresponding timestamp.
		// (Do it in a goroutine so that launch is quick.)
		go initFSTree()

		// Initialize directory trees for user-defined file systems (-path flag).
		initDirTrees()

		// Start sync goroutine, if enabled.
		if *syncCmd != "" && *syncMin > 0 {
			syncDelay.set(*syncMin) // initial sync delay
			go func() {
				for {
					dosync(nil, nil)
					delay, _ := syncDelay.get()
					dt := delay.(time.Duration)
					if *verbose {
						log.Printf("next sync in %s", dt)
					}
					time.Sleep(dt)
				}
			}()
		}

		// Immediately update metadata.
		updateMetadata()
		// Periodically refresh metadata.
		go refreshMetadataLoop()

		// Initialize search index.
		if *indexEnabled {
			go indexer()
		}

		// Start http server.
		if err := http.ListenAndServe(*httpAddr, handler); err != nil {
			log.Fatalf("ListenAndServe %s: %v", *httpAddr, err)
		}

		return
	}

	// Command line mode.
	if *html {
		packageText = packageHTML
		searchText = packageHTML
	}

	if *query {
		// Command-line queries.
		for i := 0; i < flag.NArg(); i++ {
			res, err := remoteSearch(flag.Arg(i))
			if err != nil {
				log.Fatalf("remoteSearch: %s", err)
			}
			io.Copy(os.Stdout, res.Body)
		}
		return
	}

	// determine paths
	const cmdPrefix = "cmd/"
	path := flag.Arg(0)
	var forceCmd bool
	if strings.HasPrefix(path, ".") {
		// assume cwd; don't assume -goroot
		cwd, _ := os.Getwd() // ignore errors
		path = filepath.Join(cwd, path)
	} else if strings.HasPrefix(path, cmdPrefix) {
		path = path[len(cmdPrefix):]
		forceCmd = true
	}
	relpath := path
	abspath := path
	if t, pkg, err := build.FindTree(path); err == nil {
		relpath = pkg
		abspath = filepath.Join(t.SrcDir(), pkg)
	} else if !filepath.IsAbs(path) {
		abspath = absolutePath(path, pkgHandler.fsRoot)
	} else {
		relpath = relativeURL(path)
	}

	var mode PageInfoMode
	if relpath == builtinPkgPath {
		// the fake built-in package contains unexported identifiers
		mode = noFiltering
	}
	if *srcMode {
		// only filter exports if we don't have explicit command-line filter arguments
		if flag.NArg() > 1 {
			mode |= noFiltering
		}
		mode |= showSource
	}
	// TODO(gri): Provide a mechanism (flag?) to select a package
	//            if there are multiple packages in a directory.

	// first, try as package unless forced as command
	var info PageInfo
	if !forceCmd {
		info = pkgHandler.getPageInfo(abspath, relpath, "", mode)
	}

	// second, try as command unless the path is absolute
	// (the go command invokes godoc w/ absolute paths; don't override)
	var cinfo PageInfo
	if !filepath.IsAbs(path) {
		abspath = absolutePath(path, cmdHandler.fsRoot)
		cinfo = cmdHandler.getPageInfo(abspath, relpath, "", mode)
	}

	// determine what to use
	if info.IsEmpty() {
		if !cinfo.IsEmpty() {
			// only cinfo exists - switch to cinfo
			info = cinfo
		}
	} else if !cinfo.IsEmpty() {
		// both info and cinfo exist - use cinfo if info
		// contains only subdirectory information
		if info.PAst == nil && info.PDoc == nil {
			info = cinfo
		} else {
			fmt.Printf("use 'godoc %s%s' for documentation on the %s command \n\n", cmdPrefix, relpath, relpath)
		}
	}

	if info.Err != nil {
		log.Fatalf("%v", info.Err)
	}

	// If we have more than one argument, use the remaining arguments for filtering
	if flag.NArg() > 1 {
		args := flag.Args()[1:]
		rx := makeRx(args)
		if rx == nil {
			log.Fatalf("illegal regular expression from %v", args)
		}

		filter := func(s string) bool { return rx.MatchString(s) }
		switch {
		case info.PAst != nil:
			ast.FilterFile(info.PAst, filter)
			// Special case: Don't use templates for printing
			// so we only get the filtered declarations without
			// package clause or extra whitespace.
			for i, d := range info.PAst.Decls {
				if i > 0 {
					fmt.Println()
				}
				if *html {
					var buf bytes.Buffer
					writeNode(&buf, info.FSet, d)
					FormatText(os.Stdout, buf.Bytes(), -1, true, "", nil)
				} else {
					writeNode(os.Stdout, info.FSet, d)
				}
				fmt.Println()
			}
			return

		case info.PDoc != nil:
			info.PDoc.Filter(filter)
		}
	}

	if err := packageText.Execute(os.Stdout, info); err != nil {
		log.Printf("packageText.Execute: %s", err)
	}
}
Пример #12
0
// install installs the package named by path, which is needed by parent.
func install(pkg, parent string) error {
	// Basic validation of import path string.
	if s := schemeRe.FindString(pkg); s != "" {
		return fmt.Errorf("%q used in import path, try %q\n", s, pkg[len(s):])
	}
	if strings.HasSuffix(pkg, "/") {
		return fmt.Errorf("%q should not have trailing '/'\n", pkg)
	}

	// Make sure we're not already trying to install pkg.
	switch visit[pkg] {
	case done:
		return nil
	case visiting:
		fmt.Fprintf(os.Stderr, "%s: package dependency cycle\n", argv0)
		printDeps(parent)
		fmt.Fprintf(os.Stderr, "\t%s\n", pkg)
		os.Exit(2)
	}
	parents[pkg] = parent
	visit[pkg] = visiting
	defer func() {
		visit[pkg] = done
	}()

	// Check whether package is local or remote.
	// If remote, download or update it.
	tree, pkg, err := build.FindTree(pkg)
	// Don't build the standard library.
	if err == nil && tree.Goroot && isStandardPath(pkg) {
		if parent == "" {
			return &PackageError{pkg, errors.New("cannot goinstall the standard library")}
		}
		return nil
	}

	// Download remote packages if not found or forced with -u flag.
	remote, public := isRemote(pkg), false
	if remote {
		if err == build.ErrNotFound || (err == nil && *update) {
			// Download remote package.
			printf("%s: download\n", pkg)
			public, err = download(pkg, tree.SrcDir())
			if err != nil {
				// only suggest -fix if the bad import was not on the command line
				if e, ok := err.(*errOldGoogleRepo); ok && parent != "" {
					err = fmt.Errorf("%v\nRun goinstall with -fix to gofix the code.", e)
				}
				return &DownloadError{pkg, tree.Goroot, err}
			}
		} else {
			// Test if this is a public repository
			// (for reporting to dashboard).
			repo, e := findPublicRepo(pkg)
			public = repo != nil
			err = e
		}
	}
	if err != nil {
		return &PackageError{pkg, err}
	}

	// Install the package and its dependencies.
	if err := installPackage(pkg, parent, tree, false); err != nil {
		return err
	}

	if remote {
		// mark package as installed in goinstall.log
		logged := logPackage(pkg, tree)

		// report installation to the dashboard if this is the first
		// install from a public repository.
		if logged && public {
			maybeReportToDashboard(pkg)
		}
	}

	return nil
}