github.com/varialus/godfly@v0.0.0-20130904042352-1934f9f095ab/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  	"path/filepath"
    23  	"strconv"
    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  	isGoDeveloper := exec.Command("hg", "pq").Run() == nil
    41  	if !isGoDeveloper && !forceAPICheck() {
    42  		fmt.Println("Skipping cmd/api checks; hg codereview extension not available and GO_FORCE_API_CHECK not set")
    43  		return
    44  	}
    45  
    46  	gopath := prepGoPath()
    47  
    48  	cmd := exec.Command("go", "install", "--tags=api_tool", "cmd/api")
    49  	cmd.Env = append([]string{"GOPATH=" + gopath}, filterOut(os.Environ(), "GOARCH")...)
    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"),
    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  // GO_FORCE_API_CHECK is set by builders.
    91  func forceAPICheck() bool {
    92  	v, _ := strconv.ParseBool(os.Getenv("GO_FORCE_API_CHECK"))
    93  	return v
    94  }
    95  
    96  // prepGoPath returns a GOPATH for the "go" tool to compile the API tool with.
    97  // It tries to re-use a go.tools checkout from a previous run if possible,
    98  // else it hg clones it.
    99  func prepGoPath() string {
   100  	const tempBase = "go.tools.TMP"
   101  
   102  	// The GOPATH we'll return
   103  	gopath := filepath.Join(os.TempDir(), "gopath-api", goToolsVersion)
   104  
   105  	// cloneDir is where we run "hg clone".
   106  	cloneDir := filepath.Join(gopath, "src", "code.google.com", "p")
   107  
   108  	// The dir we clone into. We only atomically rename it to finalDir on
   109  	// clone success.
   110  	tmpDir := filepath.Join(cloneDir, tempBase)
   111  
   112  	// finalDir is where the checkout will live once it's complete.
   113  	finalDir := filepath.Join(cloneDir, "go.tools")
   114  
   115  	if goToolsCheckoutGood(finalDir) {
   116  		return gopath
   117  	}
   118  	os.RemoveAll(finalDir) // in case it's there but corrupt
   119  	os.RemoveAll(tmpDir)   // in case of aborted hg clone before
   120  
   121  	if err := os.MkdirAll(cloneDir, 0700); err != nil {
   122  		log.Fatal(err)
   123  	}
   124  	cmd := exec.Command("hg",
   125  		"clone", "--rev="+goToolsVersion,
   126  		"https://code.google.com/p/go.tools",
   127  		tempBase)
   128  	cmd.Dir = cloneDir
   129  	out, err := cmd.CombinedOutput()
   130  	if err != nil {
   131  		if _, err := http.Head("http://ip.appspot.com/"); err != nil {
   132  			log.Printf("# Skipping API check; network appears to be unavailable")
   133  			os.Exit(0)
   134  		}
   135  		log.Fatalf("Error running hg clone on go.tools: %v\n%s", err, out)
   136  	}
   137  	if err := os.Rename(tmpDir, finalDir); err != nil {
   138  		log.Fatal(err)
   139  	}
   140  	return gopath
   141  }
   142  
   143  func goToolsCheckoutGood(dir string) bool {
   144  	if _, err := os.Stat(dir); err != nil {
   145  		return false
   146  	}
   147  
   148  	cmd := exec.Command("hg", "id", "--id")
   149  	cmd.Dir = dir
   150  	out, err := cmd.Output()
   151  	if err != nil {
   152  		return false
   153  	}
   154  	id := strings.TrimSpace(string(out))
   155  	if id != goToolsVersion {
   156  		return false
   157  	}
   158  
   159  	cmd = exec.Command("hg", "status")
   160  	cmd.Dir = dir
   161  	out, err = cmd.Output()
   162  	if err != nil || len(out) > 0 {
   163  		return false
   164  	}
   165  
   166  	return true
   167  }