Merge "updatelibrary: new command that will replace misc/get_closure.pl"

This commit is contained in:
mpl 2013-06-18 12:45:31 +00:00 committed by Gerrit Code Review
commit 0fc12f5eb3
1 changed files with 363 additions and 0 deletions

363
third_party/closure/updatelibrary.go vendored Normal file
View File

@ -0,0 +1,363 @@
/*
Copyright 2013 The Camlistore Authors.
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.
*/
// The updatelibrary command allows to selectively download
// from the closure library git repository (at a chosen revision)
// the resources needed by the Camlistore ui.
package main
import (
"flag"
"fmt"
"io"
"log"
"os"
"os/exec"
"path/filepath"
"strings"
"camlistore.org/pkg/osutil"
)
const (
gitRepo = "https://code.google.com/p/closure-library/"
gitHash = "1389e13"
)
// fileList is the list of resources from the closure library that
// are required by the ui pages of Camlistore. It was generated
// from the error messages given in the javascript console.
// TODO(mpl): Better way to do that generation.
// See http://camlistore.org/issue/149
var fileList = []string{
"AUTHORS",
"LICENSE",
"README",
"closure/goog/a11y/aria/announcer.js",
"closure/goog/a11y/aria/aria.js",
"closure/goog/array/array.js",
"closure/goog/asserts/asserts.js",
"closure/goog/base.js",
"closure/goog/css/common.css",
"closure/goog/css/toolbar.css",
"closure/goog/debug/debug.js",
"closure/goog/debug/entrypointregistry.js",
"closure/goog/debug/errorhandler.js",
"closure/goog/debug/errorhandlerweakdep.js",
"closure/goog/debug/error.js",
"closure/goog/debug/logbuffer.js",
"closure/goog/debug/logger.js",
"closure/goog/debug/logrecord.js",
"closure/goog/debug/tracer.js",
"closure/goog/deps.js",
"closure/goog/dom/a11y.js",
"closure/goog/dom/browserfeature.js",
"closure/goog/dom/classes.js",
"closure/goog/dom/dom.js",
"closure/goog/dom/tagname.js",
"closure/goog/dom/vendor.js",
"closure/goog/disposable/disposable.js",
"closure/goog/disposable/idisposable.js",
"closure/goog/events/browserevent.js",
"closure/goog/events/browserfeature.js",
"closure/goog/events/eventhandler.js",
"closure/goog/events/event.js",
"closure/goog/events/events.js",
"closure/goog/events/eventtarget.js",
"closure/goog/events/eventtype.js",
"closure/goog/events/eventwrapper.js",
"closure/goog/events/filedrophandler.js",
"closure/goog/events/keycodes.js",
"closure/goog/events/keyhandler.js",
"closure/goog/events/listenable.js",
"closure/goog/events/listener.js",
"closure/goog/fx/transition.js",
"closure/goog/iter/iter.js",
"closure/goog/json/json.js",
"closure/goog/math/box.js",
"closure/goog/math/coordinate.js",
"closure/goog/math/math.js",
"closure/goog/math/rect.js",
"closure/goog/math/size.js",
"closure/goog/net/errorcode.js",
"closure/goog/net/eventtype.js",
"closure/goog/net/httpstatus.js",
"closure/goog/net/wrapperxmlhttpfactory.js",
"closure/goog/net/xhrio.js",
"closure/goog/net/xmlhttpfactory.js",
"closure/goog/net/xmlhttp.js",
"closure/goog/object/object.js",
"closure/goog/positioning/abstractposition.js",
"closure/goog/positioning/anchoredposition.js",
"closure/goog/positioning/anchoredviewportposition.js",
"closure/goog/positioning/clientposition.js",
"closure/goog/positioning/menuanchoredposition.js",
"closure/goog/positioning/positioning.js",
"closure/goog/positioning/viewportclientposition.js",
"closure/goog/reflect/reflect.js",
"closure/goog/string/string.js",
"closure/goog/structs/collection.js",
"closure/goog/structs/map.js",
"closure/goog/structs/set.js",
"closure/goog/structs/simplepool.js",
"closure/goog/structs/structs.js",
"closure/goog/style/bidi.js",
"closure/goog/style/style.js",
"closure/goog/timer/timer.js",
"closure/goog/ui/button.js",
"closure/goog/ui/buttonrenderer.js",
"closure/goog/ui/buttonside.js",
"closure/goog/ui/component.js",
"closure/goog/ui/container.js",
"closure/goog/ui/containerrenderer.js",
"closure/goog/ui/controlcontent.js",
"closure/goog/ui/control.js",
"closure/goog/ui/controlrenderer.js",
"closure/goog/ui/cssnames.js",
"closure/goog/ui/custombuttonrenderer.js",
"closure/goog/ui/decorate.js",
"closure/goog/ui/idgenerator.js",
"closure/goog/ui/menubutton.js",
"closure/goog/ui/menubuttonrenderer.js",
"closure/goog/ui/menuheader.js",
"closure/goog/ui/menuheaderrenderer.js",
"closure/goog/ui/menuitem.js",
"closure/goog/ui/menuitemrenderer.js",
"closure/goog/ui/menu.js",
"closure/goog/ui/menurenderer.js",
"closure/goog/ui/menuseparator.js",
"closure/goog/ui/menuseparatorrenderer.js",
"closure/goog/ui/nativebuttonrenderer.js",
"closure/goog/ui/popupbase.js",
"closure/goog/ui/popupmenu.js",
"closure/goog/ui/registry.js",
"closure/goog/ui/separator.js",
"closure/goog/ui/textarea.js",
"closure/goog/ui/textarearenderer.js",
"closure/goog/ui/toolbarbutton.js",
"closure/goog/ui/toolbarbuttonrenderer.js",
"closure/goog/ui/toolbar.js",
"closure/goog/ui/toolbarmenubutton.js",
"closure/goog/ui/toolbarmenubuttonrenderer.js",
"closure/goog/ui/toolbarrenderer.js",
"closure/goog/ui/toolbarseparatorrenderer.js",
"closure/goog/uri/uri.js",
"closure/goog/uri/utils.js",
"closure/goog/useragent/product.js",
"closure/goog/useragent/useragent.js",
}
var (
currentRevCmd = newCmd("git", "rev-parse", "--short", "HEAD")
gitFetchCmd = newCmd("git", "fetch")
gitResetCmd = newCmd("git", "reset", gitHash)
gitCloneCmd = newCmd("git", "clone", "-n", gitRepo, ".")
gitCheckoutCmd = newCmd("git", "checkout", "HEAD")
)
var (
verbose bool
closureGitDir string // where we do the cloning/updating: camliRoot + tmp/closure-lib/
destDir string // install dir: camliRoot + third_party/closure/lib/
)
func init() {
flag.BoolVar(&verbose, "verbose", false, "verbose output")
}
type command struct {
program string
args []string
}
func newCmd(program string, args ...string) *command {
return &command{program, args}
}
func (c *command) String() string {
return fmt.Sprintf("%v %v", c.program, c.args)
}
// run runs the command and returns the output if it succeeds.
// On error, the process dies.
func (c *command) run() []byte {
cmd := exec.Command(c.program, c.args...)
b, err := cmd.Output()
if err != nil {
log.Fatalf("Could not run %v: %v", c, err)
}
return b
}
func resetAndCheckout() {
gitResetCmd.run()
args := gitCheckoutCmd.args
args = append(args, fileList...)
partialCheckoutCmd := newCmd(gitCheckoutCmd.program, args...)
if verbose {
log.Printf("%v", partialCheckoutCmd)
}
partialCheckoutCmd.run()
}
func update() {
err := os.Chdir(closureGitDir)
if err != nil {
log.Fatalf("Could not chdir to %v: %v", closureGitDir, err)
}
output := strings.TrimSpace(string(currentRevCmd.run()))
if string(output) != gitHash {
gitFetchCmd.run()
} else {
if verbose {
log.Printf("Already at rev %v, fetching not needed.", gitHash)
}
}
resetAndCheckout()
}
func clone() {
err := os.Chdir(closureGitDir)
if err != nil {
log.Fatalf("Could not chdir to %v: %v", closureGitDir, err)
}
gitCloneCmd.run()
resetAndCheckout()
}
func cpDir(src, dst string) error {
return filepath.Walk(src, func(path string, fi os.FileInfo, err error) error {
if err != nil {
return err
}
suffix, err := filepath.Rel(closureGitDir, path)
if err != nil {
return fmt.Errorf("Failed to find Rel(%q, %q): %v", closureGitDir, path, err)
}
base := fi.Name()
if fi.IsDir() {
if base == ".git" {
return filepath.SkipDir
}
return nil
}
return cpFile(path, filepath.Join(dst, suffix))
})
}
func cpFile(src, dst string) error {
sfi, err := os.Stat(src)
if err != nil {
return err
}
if !sfi.Mode().IsRegular() {
return fmt.Errorf("cpFile can't deal with non-regular file %s", src)
}
dstDir := filepath.Dir(dst)
if err := os.MkdirAll(dstDir, 0755); err != nil {
return err
}
df, err := os.Create(dst)
if err != nil {
return err
}
sf, err := os.Open(src)
if err != nil {
return err
}
defer sf.Close()
n, err := io.Copy(df, sf)
if err == nil && n != sfi.Size() {
err = fmt.Errorf("copied wrong size for %s -> %s: copied %d; want %d", src, dst, n, sfi.Size())
}
cerr := df.Close()
if err == nil {
err = cerr
}
return err
}
func cpToDestDir() {
err := os.RemoveAll(destDir)
if err != nil {
log.Fatalf("could not remove %v: %v", destDir, err)
}
err = cpDir(closureGitDir, destDir)
if err != nil {
log.Fatalf("could not cp %v to %v : %v", closureGitDir, destDir, err)
}
}
// setup checks if the camlistore root can be found,
// then sets up closureGitDir and destDir, and returns whether
// we should clone or update in closureGitDir (depending on
// if a .git dir was found).
func setup() string {
camliRootPath, err := osutil.GoPackagePath("camlistore.org")
if err != nil {
log.Fatal("Package camlistore.org not found in $GOPATH (or $GOPATH not defined).")
}
destDir = filepath.Join(camliRootPath, "third_party", "closure", "lib")
closureGitDir = filepath.Join(camliRootPath, "tmp", "closure-lib")
op := "update"
_, err = os.Stat(closureGitDir)
if err != nil {
if os.IsNotExist(err) {
err = os.MkdirAll(closureGitDir, 0755)
if err != nil {
log.Fatalf("Could not create %v: %v", closureGitDir, err)
}
op = "clone"
} else {
log.Fatalf("Could not stat %v: %v", closureGitDir, err)
}
}
dotGitPath := filepath.Join(closureGitDir, ".git")
_, err = os.Stat(dotGitPath)
if err != nil {
if os.IsNotExist(err) {
op = "clone"
} else {
log.Fatalf("Could not stat %v: %v", dotGitPath, err)
}
}
return op
}
func main() {
flag.Parse()
op := setup()
switch op {
case "clone":
if verbose {
fmt.Printf("cloning from %v at rev %v\n", gitRepo, gitHash)
}
clone()
case "update":
if verbose {
fmt.Printf("updating to rev %v\n", gitHash)
}
update()
default:
log.Fatalf("Unsupported operation: %v", op)
}
cpToDestDir()
}