github.com/slspeek/camlistore_namedsearch@v0.0.0-20140519202248-ed6f70f7721a/third_party/closure/updatelibrary.go (about)

     1  /*
     2  Copyright 2013 The Camlistore Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8       http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  // The updatelibrary command allows to selectively download
    18  // from the closure library git repository (at a chosen revision)
    19  // the resources needed by the Camlistore ui.
    20  package main
    21  
    22  import (
    23  	"bytes"
    24  	"flag"
    25  	"fmt"
    26  	"io"
    27  	"log"
    28  	"net/http"
    29  	"os"
    30  	"os/exec"
    31  	"path/filepath"
    32  	"sort"
    33  	"strings"
    34  
    35  	"camlistore.org/pkg/misc/closure"
    36  	"camlistore.org/pkg/osutil"
    37  )
    38  
    39  const (
    40  	gitRepo = "https://code.google.com/p/closure-library/"
    41  	gitHash = "ab89cf45c216"
    42  )
    43  
    44  var (
    45  	currentRevCmd  = newCmd("git", "rev-parse", "--short", "HEAD")
    46  	gitFetchCmd    = newCmd("git", "fetch")
    47  	gitResetCmd    = newCmd("git", "reset", gitHash)
    48  	gitCloneCmd    = newCmd("git", "clone", "-n", gitRepo, ".")
    49  	gitCheckoutCmd = newCmd("git", "checkout", "HEAD")
    50  )
    51  
    52  var (
    53  	verbose       bool
    54  	closureGitDir string // where we do the cloning/updating: camliRoot + tmp/closure-lib/
    55  	destDir       string // install dir: camliRoot + third_party/closure/lib/
    56  )
    57  
    58  func init() {
    59  	flag.BoolVar(&verbose, "verbose", false, "verbose output")
    60  }
    61  
    62  // fileList parses deps.js from the closure repo, as well as the similar
    63  // dependencies generated for the UI js files, and compiles the list of
    64  // js files from the closure lib required for the UI.
    65  func fileList() ([]string, error) {
    66  	camliRootPath, err := osutil.GoPackagePath("camlistore.org")
    67  	if err != nil {
    68  		log.Fatal("Package camlistore.org not found in $GOPATH (or $GOPATH not defined).")
    69  	}
    70  	uiDir := filepath.Join(camliRootPath, "server", "camlistored", "ui")
    71  	closureDepsFile := filepath.Join(closureGitDir, "closure", "goog", "deps.js")
    72  
    73  	f, err := os.Open(closureDepsFile)
    74  	if err != nil {
    75  		return nil, err
    76  	}
    77  	defer f.Close()
    78  	allClosureDeps, err := closure.DeepParseDeps(f)
    79  	if err != nil {
    80  		return nil, err
    81  	}
    82  
    83  	uiDeps, err := closure.GenDeps(http.Dir(uiDir))
    84  	if err != nil {
    85  		return nil, err
    86  	}
    87  	_, requ, err := closure.ParseDeps(bytes.NewReader(uiDeps))
    88  	if err != nil {
    89  		return nil, err
    90  	}
    91  
    92  	nameDone := make(map[string]bool)
    93  	jsfilesDone := make(map[string]bool)
    94  	for _, deps := range requ {
    95  		for _, dep := range deps {
    96  			if _, ok := nameDone[dep]; ok {
    97  				continue
    98  			}
    99  			jsfiles := allClosureDeps[dep]
   100  			for _, filename := range jsfiles {
   101  				if _, ok := jsfilesDone[filename]; ok {
   102  					continue
   103  				}
   104  				jsfilesDone[filename] = true
   105  			}
   106  			nameDone[dep] = true
   107  		}
   108  	}
   109  	jsfiles := []string{
   110  		"AUTHORS",
   111  		"LICENSE",
   112  		"README",
   113  		filepath.Join("closure", "goog", "base.js"),
   114  		filepath.Join("closure", "goog", "bootstrap", "nodejs.js"),
   115  		filepath.Join("closure", "goog", "bootstrap", "webworkers.js"),
   116  		filepath.Join("closure", "goog", "css", "common.css"),
   117  		filepath.Join("closure", "goog", "css", "toolbar.css"),
   118  		filepath.Join("closure", "goog", "deps.js"),
   119  	}
   120  	prefix := filepath.Join("closure", "goog")
   121  	for k, _ := range jsfilesDone {
   122  		jsfiles = append(jsfiles, filepath.Join(prefix, k))
   123  	}
   124  	sort.Strings(jsfiles)
   125  	return jsfiles, nil
   126  }
   127  
   128  type command struct {
   129  	program string
   130  	args    []string
   131  }
   132  
   133  func newCmd(program string, args ...string) *command {
   134  	return &command{program, args}
   135  }
   136  
   137  func (c *command) String() string {
   138  	return fmt.Sprintf("%v %v", c.program, c.args)
   139  }
   140  
   141  // run runs the command and returns the output if it succeeds.
   142  // On error, the process dies.
   143  func (c *command) run() []byte {
   144  	cmd := exec.Command(c.program, c.args...)
   145  	b, err := cmd.Output()
   146  	if err != nil {
   147  		log.Fatalf("Could not run %v: %v", c, err)
   148  	}
   149  	return b
   150  }
   151  
   152  func resetAndCheckout() {
   153  	gitResetCmd.run()
   154  	// we need deps.js to build the list of files, so we get it first
   155  	args := gitCheckoutCmd.args
   156  	args = append(args, filepath.Join("closure", "goog", "deps.js"))
   157  	depsCheckoutCmd := newCmd(gitCheckoutCmd.program, args...)
   158  	depsCheckoutCmd.run()
   159  	files, err := fileList()
   160  	if err != nil {
   161  		log.Fatalf("Could not generate files list: %v", err)
   162  	}
   163  	args = gitCheckoutCmd.args
   164  	args = append(args, files...)
   165  	partialCheckoutCmd := newCmd(gitCheckoutCmd.program, args...)
   166  	if verbose {
   167  		fmt.Printf("%v\n", partialCheckoutCmd)
   168  	}
   169  	partialCheckoutCmd.run()
   170  }
   171  
   172  func update() {
   173  	err := os.Chdir(closureGitDir)
   174  	if err != nil {
   175  		log.Fatalf("Could not chdir to %v: %v", closureGitDir, err)
   176  	}
   177  	output := strings.TrimSpace(string(currentRevCmd.run()))
   178  	if string(output) != gitHash {
   179  		gitFetchCmd.run()
   180  	} else {
   181  		if verbose {
   182  			log.Printf("Already at rev %v, fetching not needed.", gitHash)
   183  		}
   184  	}
   185  	resetAndCheckout()
   186  }
   187  
   188  func clone() {
   189  	err := os.Chdir(closureGitDir)
   190  	if err != nil {
   191  		log.Fatalf("Could not chdir to %v: %v", closureGitDir, err)
   192  	}
   193  	gitCloneCmd.run()
   194  	resetAndCheckout()
   195  }
   196  
   197  func cpDir(src, dst string) error {
   198  	return filepath.Walk(src, func(path string, fi os.FileInfo, err error) error {
   199  		if err != nil {
   200  			return err
   201  		}
   202  		suffix, err := filepath.Rel(closureGitDir, path)
   203  		if err != nil {
   204  			return fmt.Errorf("Failed to find Rel(%q, %q): %v", closureGitDir, path, err)
   205  		}
   206  		base := fi.Name()
   207  		if fi.IsDir() {
   208  			if base == ".git" {
   209  				return filepath.SkipDir
   210  			}
   211  			return nil
   212  		}
   213  		return cpFile(path, filepath.Join(dst, suffix))
   214  	})
   215  }
   216  
   217  func cpFile(src, dst string) error {
   218  	sfi, err := os.Stat(src)
   219  	if err != nil {
   220  		return err
   221  	}
   222  	if !sfi.Mode().IsRegular() {
   223  		return fmt.Errorf("cpFile can't deal with non-regular file %s", src)
   224  	}
   225  
   226  	dstDir := filepath.Dir(dst)
   227  	if err := os.MkdirAll(dstDir, 0755); err != nil {
   228  		return err
   229  	}
   230  
   231  	df, err := os.Create(dst)
   232  	if err != nil {
   233  		return err
   234  	}
   235  	sf, err := os.Open(src)
   236  	if err != nil {
   237  		return err
   238  	}
   239  	defer sf.Close()
   240  
   241  	n, err := io.Copy(df, sf)
   242  	if err == nil && n != sfi.Size() {
   243  		err = fmt.Errorf("copied wrong size for %s -> %s: copied %d; want %d", src, dst, n, sfi.Size())
   244  	}
   245  	cerr := df.Close()
   246  	if err == nil {
   247  		err = cerr
   248  	}
   249  	return err
   250  }
   251  
   252  func cpToDestDir() {
   253  	err := os.RemoveAll(destDir)
   254  	if err != nil {
   255  		log.Fatalf("could not remove %v: %v", destDir, err)
   256  	}
   257  	err = cpDir(closureGitDir, destDir)
   258  	if err != nil {
   259  		log.Fatalf("could not cp %v to %v : %v", closureGitDir, destDir, err)
   260  	}
   261  }
   262  
   263  // setup checks if the camlistore root can be found,
   264  // then sets up closureGitDir and destDir, and returns whether
   265  // we should clone or update in closureGitDir (depending on
   266  // if a .git dir was found).
   267  func setup() string {
   268  	camliRootPath, err := osutil.GoPackagePath("camlistore.org")
   269  	if err != nil {
   270  		log.Fatal("Package camlistore.org not found in $GOPATH (or $GOPATH not defined).")
   271  	}
   272  	destDir = filepath.Join(camliRootPath, "third_party", "closure", "lib")
   273  	closureGitDir = filepath.Join(camliRootPath, "tmp", "closure-lib")
   274  	op := "update"
   275  	_, err = os.Stat(closureGitDir)
   276  	if err != nil {
   277  		if os.IsNotExist(err) {
   278  			err = os.MkdirAll(closureGitDir, 0755)
   279  			if err != nil {
   280  				log.Fatalf("Could not create %v: %v", closureGitDir, err)
   281  			}
   282  			op = "clone"
   283  		} else {
   284  			log.Fatalf("Could not stat %v: %v", closureGitDir, err)
   285  		}
   286  	}
   287  	dotGitPath := filepath.Join(closureGitDir, ".git")
   288  	_, err = os.Stat(dotGitPath)
   289  	if err != nil {
   290  		if os.IsNotExist(err) {
   291  			op = "clone"
   292  		} else {
   293  			log.Fatalf("Could not stat %v: %v", dotGitPath, err)
   294  		}
   295  	}
   296  	return op
   297  }
   298  
   299  func main() {
   300  	flag.Parse()
   301  
   302  	op := setup()
   303  	switch op {
   304  	case "clone":
   305  		if verbose {
   306  			fmt.Printf("cloning from %v at rev %v\n", gitRepo, gitHash)
   307  		}
   308  		clone()
   309  	case "update":
   310  		if verbose {
   311  			fmt.Printf("updating to rev %v\n", gitHash)
   312  		}
   313  		update()
   314  	default:
   315  		log.Fatalf("Unsupported operation: %v", op)
   316  	}
   317  
   318  	cpToDestDir()
   319  }