| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384 |
- // +build ignore
- /*
- Copyright 2013 Brandon Philips
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- */
- // This program builds a project and is a copy of third_party.go. See
- // github.com/philips/third_party.go
- //
- // $ go run third_party.go
- //
- // See the README file for more details.
- package main
- import (
- "fmt"
- "io"
- "io/ioutil"
- "log"
- "os"
- "os/exec"
- "path"
- "path/filepath"
- "strings"
- )
- const (
- DefaultThirdParty = "third_party"
- )
- // thirdPartyDir creates a string path to the third_party directory based on
- // the current working directory.
- func thirdPartyDir() string {
- root, err := os.Getwd()
- if err != nil {
- log.Fatalf("Failed to get the current working directory: %v", err)
- }
- return path.Join(root, DefaultThirdParty)
- }
- func srcDir() string {
- return path.Join(thirdPartyDir(), "src")
- }
- // binDir creates a string path to the GOBIN directory based on the current
- // working directory.
- func binDir() string {
- root, err := os.Getwd()
- if err != nil {
- log.Fatalf("Failed to get the current working directory: %v", err)
- }
- return path.Join(root, "bin")
- }
- // run execs a command like a shell script piping everything to the parent's
- // stderr/stdout and uses the given environment.
- func run(name string, arg ...string) *os.ProcessState {
- cmd := exec.Command(name, arg...)
- stdout, err := cmd.StdoutPipe()
- if err != nil {
- fmt.Fprintf(os.Stderr, err.Error())
- os.Exit(1)
- }
- stderr, err := cmd.StderrPipe()
- if err != nil {
- fmt.Fprintf(os.Stderr, err.Error())
- os.Exit(1)
- }
- err = cmd.Start()
- if err != nil {
- fmt.Fprintf(os.Stderr, err.Error())
- os.Exit(1)
- }
- go io.Copy(os.Stdout, stdout)
- go io.Copy(os.Stderr, stderr)
- cmd.Wait()
- return cmd.ProcessState
- }
- // setupProject does the initial setup of the third_party src directory
- // including setting up the symlink to the cwd from the src directory.
- func setupProject(pkg string) {
- root, err := os.Getwd()
- if err != nil {
- log.Fatalf("Failed to get the current working directory: %v", err)
- }
- src := path.Join(thirdPartyDir(), "src", pkg)
- srcdir := path.Dir(src)
- os.MkdirAll(srcdir, 0755)
- rel, err := filepath.Rel(srcdir, root)
- if err != nil {
- log.Fatalf("creating relative third party path: %v", err)
- }
- err = os.Symlink(rel, src)
- if err != nil && os.IsExist(err) == false {
- log.Fatalf("creating project third party symlink: %v", err)
- }
- }
- func getVc(root string) versionControl {
- for _, v := range []string{".git", ".hg"} {
- r := path.Join(root, v)
- info, err := os.Stat(r)
- if err != nil || !info.IsDir() {
- continue
- }
- base := path.Base(r)
- switch base {
- case ".git":
- return vcGit(r)
- case ".hg":
- return vcHg(r)
- }
- }
- return new(vcNoop)
- }
- type versionControl interface {
- commit() string
- update(string) error
- }
- // Performs noops on all VC operations.
- type vcNoop struct{}
- func (v *vcNoop) commit() string {
- return ""
- }
- func (v *vcNoop) update(dir string) error {
- return nil
- }
- type vcHg string
- // vcHg.commit returns the current HEAD commit hash for a given hg dir.
- func (v vcHg) commit() string {
- out, err := exec.Command("hg", "id", "-i", "-R", string(v)).Output()
- if err != nil {
- return ""
- }
- return string(out)
- }
- // vcHg.udpate updates the given hg dir to ref.
- func (v vcHg) update(ref string) error {
- _, err := exec.Command("hg",
- "update",
- "-r", ref,
- "-R", string(v),
- "--cwd", path.Dir(string(v)),
- ).Output()
- if err != nil {
- return err
- }
- return nil
- }
- type vcGit string
- // vcGit.commit returns the current HEAD commit hash for a given git dir.
- func (v vcGit) commit() string {
- out, err := exec.Command("git", "--git-dir="+string(v), "rev-parse", "HEAD").Output()
- if err != nil {
- return ""
- }
- return string(out)
- }
- // vcHg.udpate updates the given git dir to ref.
- func (v vcGit) update(ref string) error {
- _, err := exec.Command("git",
- "--work-tree="+path.Dir(string(v)),
- "--git-dir="+string(v),
- "reset", "--hard", ref,
- ).Output()
- if err != nil {
- return err
- }
- return nil
- }
- // commit grabs the commit id from hg or git as a string.
- func commit(dir string) string {
- return getVc(dir).commit()
- }
- // removeVcs removes a .git or .hg directory from the given root if it exists.
- func removeVcs(root string) (bool, string) {
- for _, v := range []string{".git", ".hg"} {
- r := path.Join(root, v)
- info, err := os.Stat(r)
- if err != nil {
- continue
- }
- // We didn't find it, next!
- if info.IsDir() == false {
- continue
- }
- // We found it, grab the commit and remove the directory
- c := commit(root)
- err = os.RemoveAll(r)
- if err != nil {
- log.Fatalf("removeVcs: %v", err)
- }
- return true, c
- }
- return false, ""
- }
- // bump takes care of grabbing a package, getting the package git hash and
- // removing all of the version control stuff.
- func bump(pkg, version string) {
- tpd := thirdPartyDir()
- temp, err := ioutil.TempDir(tpd, "bump")
- if err != nil {
- log.Fatalf("bump: %v", err)
- }
- defer os.RemoveAll(temp)
- os.Setenv("GOPATH", temp)
- run("go", "get", "-u", "-d", pkg)
- for {
- root := path.Join(temp, "src", pkg) // the temp installation root
- home := path.Join(tpd, "src", pkg) // where the package will end up
- if version != "" {
- err := getVc(root).update(version)
- if err != nil {
- log.Fatalf("bump: %v", err)
- }
- }
- ok, c := removeVcs(root)
- if ok {
- // Create the path leading up to the package
- err := os.MkdirAll(path.Dir(home), 0755)
- if err != nil {
- log.Fatalf("bump: %v", err)
- }
- // Remove anything that might have been there
- err = os.RemoveAll(home)
- if err != nil {
- log.Fatalf("bump: %v", err)
- }
- // Finally move the package
- err = os.Rename(root, home)
- if err != nil {
- log.Fatalf("bump: %v", err)
- }
- fmt.Printf("%s %s\n", pkg, strings.TrimSpace(c))
- break
- }
- // Pop off and try to find this directory!
- pkg = path.Dir(pkg)
- if pkg == "." {
- return
- }
- }
- }
- // validPkg uses go list to decide if the given path is a valid go package.
- // This is used by the bumpAll walk to bump all of the existing packages.
- func validPkg(pkg string) bool {
- env := append(os.Environ(),
- )
- cmd := exec.Command("go", "list", pkg)
- cmd.Env = env
- out, err := cmd.Output()
- if err != nil {
- return false
- }
- if pkg == strings.TrimSpace(string(out)) {
- return true
- }
- return false
- }
- // bumpWalk walks the third_party directory and bumps all of the packages that it finds.
- func bumpWalk(path string, info os.FileInfo, err error) error {
- if err != nil {
- return nil
- }
- // go packages are always directories
- if info.IsDir() == false {
- return nil
- }
- parts := strings.Split(path, srcDir()+"/")
- if len(parts) == 1 {
- return nil
- }
- pkg := parts[1]
- if validPkg(pkg) == false {
- return nil
- }
- bump(pkg, "")
- return nil
- }
- func bumpAll() {
- err := filepath.Walk(srcDir(), bumpWalk)
- if err != nil {
- log.Fatalf(err.Error())
- }
- }
- func main() {
- log.SetFlags(0)
- // third_party manages GOPATH, no one else
- os.Setenv("GOPATH", thirdPartyDir())
- os.Setenv("GOBIN", binDir())
- if len(os.Args) <= 1 {
- log.Fatalf("No command")
- }
- cmd := os.Args[1]
- if cmd == "setup" && len(os.Args) > 2 {
- setupProject(os.Args[2])
- return
- }
- if cmd == "bump" && len(os.Args) > 2 {
- ref := ""
- if len(os.Args) > 3 {
- ref = os.Args[3]
- }
- bump(os.Args[2], ref)
- return
- }
- if cmd == "bump-all" && len(os.Args) > 1 {
- bumpAll()
- return
- }
- ps := run("go", os.Args[1:]...)
- if ps.Success() == false {
- os.Exit(1)
- }
- }
|