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 }