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