github.com/hongwozai/go-src-1.4.3@v0.0.0-20191127132709-dc3fce3dbccb/src/cmd/api/run.go (about)

     1  // Copyright 2013 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  // +build ignore
     6  
     7  // The run program is invoked via "go run" from src/run.bash or
     8  // src/run.bat conditionally builds and runs the cmd/api tool.
     9  //
    10  // TODO(bradfitz): the "conditional" condition is always true.
    11  // We should only do this if the user has the hg codereview extension
    12  // enabled and verifies that the go.tools subrepo is checked out with
    13  // a suitably recently version. In prep for the cmd/api rewrite.
    14  package main
    15  
    16  import (
    17  	"fmt"
    18  	"log"
    19  	"net/http"
    20  	"os"
    21  	"os/exec"
    22  	"os/user"
    23  	"path/filepath"
    24  	"runtime"
    25  	"strings"
    26  )
    27  
    28  // goToolsVersion is the hg revision of the go.tools subrepo we need
    29  // to build cmd/api.  This only needs to be updated whenever a go/types
    30  // bug fix is needed by the cmd/api tool.
    31  const goToolsVersion = "6698ca2900e2"
    32  
    33  var goroot string
    34  
    35  func main() {
    36  	log.SetFlags(0)
    37  	goroot = os.Getenv("GOROOT") // should be set by run.{bash,bat}
    38  	if goroot == "" {
    39  		log.Fatal("No $GOROOT set.")
    40  	}
    41  	_, err := exec.LookPath("hg")
    42  	if err != nil {
    43  		fmt.Println("Skipping cmd/api checks; hg not available")
    44  		return
    45  	}
    46  
    47  	gopath := prepGoPath()
    48  
    49  	cmd := exec.Command("go", "install", "--tags=api_tool", "cmd/api")
    50  	cmd.Env = append(filterOut(os.Environ(), "GOARCH", "GOPATH"), "GOPATH="+gopath)
    51  	out, err := cmd.CombinedOutput()
    52  	if err != nil {
    53  		log.Fatalf("Error installing cmd/api: %v\n%s", err, out)
    54  	}
    55  
    56  	out, err = exec.Command("go", "tool", "api",
    57  		"-c", file("go1", "go1.1", "go1.2", "go1.3", "go1.4"),
    58  		"-next", file("next"),
    59  		"-except", file("except")).CombinedOutput()
    60  	if err != nil {
    61  		log.Fatalf("Error running API checker: %v\n%s", err, out)
    62  	}
    63  	fmt.Print(string(out))
    64  }
    65  
    66  // filterOut returns a copy of the src environment without environment
    67  // variables from remove.
    68  // TODO: delete when issue 6201 is fixed.
    69  func filterOut(src []string, remove ...string) (out []string) {
    70  S:
    71  	for _, s := range src {
    72  		for _, r := range remove {
    73  			if strings.HasPrefix(s, r) && strings.HasPrefix(s, r+"=") {
    74  				continue S
    75  			}
    76  		}
    77  		out = append(out, s)
    78  	}
    79  	return
    80  }
    81  
    82  // file expands s to $GOROOT/api/s.txt.
    83  // If there are more than 1, they're comma-separated.
    84  func file(s ...string) string {
    85  	if len(s) > 1 {
    86  		return file(s[0]) + "," + file(s[1:]...)
    87  	}
    88  	return filepath.Join(goroot, "api", s[0]+".txt")
    89  }
    90  
    91  // prepGoPath returns a GOPATH for the "go" tool to compile the API tool with.
    92  // It tries to re-use a go.tools checkout from a previous run if possible,
    93  // else it hg clones it.
    94  func prepGoPath() string {
    95  	const tempBase = "go.tools.TMP"
    96  
    97  	username := ""
    98  	u, err := user.Current()
    99  	if err == nil {
   100  		username = u.Username
   101  	} else {
   102  		username = os.Getenv("USER")
   103  		if username == "" {
   104  			username = "nobody"
   105  		}
   106  	}
   107  
   108  	// The GOPATH we'll return
   109  	gopath := filepath.Join(os.TempDir(), "gopath-api-"+cleanUsername(username)+"-"+cleanUsername(strings.Fields(runtime.Version())[0]), goToolsVersion)
   110  
   111  	// cloneDir is where we run "hg clone".
   112  	cloneDir := filepath.Join(gopath, "src", "code.google.com", "p")
   113  
   114  	// The dir we clone into. We only atomically rename it to finalDir on
   115  	// clone success.
   116  	tmpDir := filepath.Join(cloneDir, tempBase)
   117  
   118  	// finalDir is where the checkout will live once it's complete.
   119  	finalDir := filepath.Join(cloneDir, "go.tools")
   120  
   121  	if goToolsCheckoutGood(finalDir) {
   122  		return gopath
   123  	}
   124  	os.RemoveAll(finalDir) // in case it's there but corrupt
   125  	os.RemoveAll(tmpDir)   // in case of aborted hg clone before
   126  
   127  	if err := os.MkdirAll(cloneDir, 0700); err != nil {
   128  		log.Fatal(err)
   129  	}
   130  	cmd := exec.Command("hg",
   131  		"clone", "--rev="+goToolsVersion,
   132  		"https://code.google.com/p/go.tools",
   133  		tempBase)
   134  	cmd.Dir = cloneDir
   135  	out, err := cmd.CombinedOutput()
   136  	if err != nil {
   137  		if _, err := http.Head("http://ip.appspot.com/"); err != nil {
   138  			log.Printf("# Skipping API check; network appears to be unavailable")
   139  			os.Exit(0)
   140  		}
   141  		log.Fatalf("Error running hg clone on go.tools: %v\n%s", err, out)
   142  	}
   143  	if err := os.Rename(tmpDir, finalDir); err != nil {
   144  		log.Fatal(err)
   145  	}
   146  	return gopath
   147  }
   148  
   149  func cleanUsername(n string) string {
   150  	b := make([]rune, len(n))
   151  	for i, r := range n {
   152  		if r == '\\' || r == '/' || r == ':' {
   153  			b[i] = '_'
   154  		} else {
   155  			b[i] = r
   156  		}
   157  	}
   158  	return string(b)
   159  }
   160  
   161  func goToolsCheckoutGood(dir string) bool {
   162  	if _, err := os.Stat(dir); err != nil {
   163  		return false
   164  	}
   165  
   166  	cmd := exec.Command("hg", "id", "--id")
   167  	cmd.Dir = dir
   168  	out, err := cmd.Output()
   169  	if err != nil {
   170  		return false
   171  	}
   172  	id := strings.TrimSpace(string(out))
   173  	if id != goToolsVersion {
   174  		return false
   175  	}
   176  
   177  	cmd = exec.Command("hg", "status")
   178  	cmd.Dir = dir
   179  	out, err = cmd.Output()
   180  	if err != nil || len(out) > 0 {
   181  		return false
   182  	}
   183  
   184  	return true
   185  }