// Load the caches from a file. func Load() error { conf := config.Current() filename := filepath.Join(conf.Build, CACHE_FILENAME) if config.NoCache { return nil } f, err := os.Open(filename) if err != nil && os.IsNotExist(err) { return nil } else if err != nil { return app.Error(err) } defer f.Close() log.Println("Reading cache:", filename) d := gob.NewDecoder(f) if err := d.Decode(&modificationCache); err != nil { return app.Error(err) } if err := d.Decode(&dataCache); err != nil { return app.Error(err) } log.Println("Read", len(modificationCache), "modifications and", len(dataCache), "datas!") return nil }
func copyFiles(from []string, to string) error { srcs := []io.Reader{} for _, f := range from { src, err := os.Open(f) if err != nil { return app.Error(err) } defer src.Close() srcs = append(srcs, src) } src := io.MultiReader(srcs...) dest, err := os.Create(to) if err != nil { return app.Error(err) } defer dest.Close() if _, err = io.Copy(dest, src); err != nil { return app.Error(err) } return nil }
func Input(r *app.Request) error { name := mux.Vars(r.Req)["name"] if name == config.DEPS_NAME { if err := hooks.PreCompile(); err != nil { return err } if err := soy.Compile(); err != nil { return err } if _, _, err := js.GenerateDeps("input"); err != nil { return err } conf := config.Current() f, err := os.Open(path.Join(conf.Build, config.DEPS_NAME)) if err != nil { return app.Error(err) } defer f.Close() r.W.Header().Set("Content-Type", "text/javascript") if _, err := io.Copy(r.W, f); err != nil { return app.Error(err) } if err := hooks.PostCompile(); err != nil { return err } return nil } // Otherwise serve the file if it can be found paths := scan.BaseJSPaths() for _, p := range paths { f, err := os.Open(path.Join(p, name)) if err != nil && !os.IsNotExist(err) { return app.Error(err) } else if err == nil { defer f.Close() r.W.Header().Set("Content-Type", "text/javascript") io.Copy(r.W, f) return nil } } return app.Errorf("file not found: %s", name) }
func GenerateDeps(dest string) ([]*domain.Source, []string, error) { log.Println("Scanning deps...") conf := config.Current() depstree, err := scan.NewDepsTree(dest) if err != nil { return nil, nil, err } namespaces := []string{} if conf.Js != nil { for _, input := range conf.Js.Inputs { if dest != "input" && strings.Contains(input.File, "_test") { continue } file := filepath.Join(conf.Js.Root, input.File) ns, err := depstree.GetProvides(file) if err != nil { return nil, nil, err } namespaces = append(namespaces, ns...) } } if len(namespaces) == 0 { return nil, nil, app.Errorf("no namespaces provided in the input files") } if dest == "input" { // Add the necesary namespaces for the multi-test runner namespaces = append(namespaces, "goog.style") namespaces = append(namespaces, "goog.userAgent.product") namespaces = append(namespaces, "goog.testing.MultiTestRunner") namespaces = append(namespaces, depstree.GetTestingNamespaces()...) } else if dest == "input-production" { namespaces = append(namespaces, "goog.style") } deps, err := depstree.GetDependencies(namespaces) if err != nil { return nil, nil, err } f, err := os.Create(filepath.Join(conf.Build, config.DEPS_NAME)) if err != nil { return nil, nil, app.Error(err) } defer f.Close() if err := scan.WriteDeps(f, deps); err != nil { return nil, nil, err } log.Println("Done scanning deps!") return deps, namespaces, nil }
func RawOutput(r *app.Request) error { if err := hooks.PreCompile(); err != nil { return err } if err := gss.Compile(); err != nil { return err } if err := soy.Compile(); err != nil { return err } _, namespaces, err := js.GenerateDeps("input-production") if err != nil { return err } log.Println("Output RAW mode") conf := config.Current() content := bytes.NewBuffer(nil) base := path.Join(conf.Library.Root, "closure", "goog", "base.js") if err := addFile(content, base); err != nil { return err } if err := addFile(content, path.Join(conf.Build, config.RENAMING_MAP_NAME)); err != nil { return err } if err := addFile(content, path.Join(conf.Build, config.DEPS_NAME)); err != nil { return err } if err := hooks.PostCompile(); err != nil { return err } css := make([]byte, 0) if conf.Gss != nil { css, err = ioutil.ReadFile(filepath.Join(conf.Build, config.CSS_NAME)) if err != nil { return app.Error(err) } } data := map[string]interface{}{ "Content": template.HTML(string(content.Bytes())), "Port": config.Port, "LT": template.HTML("<"), "Namespaces": template.HTML("'" + strings.Join(namespaces, "', '") + "'"), "Css": template.HTML(template.JSEscapeString(string(css))), } r.W.Header().Set("Content-Type", "text/javascript") return r.ExecuteTemplate([]string{"raw"}, data) }
func calcFileSha1(filename string) (string, error) { cmd := exec.Command("sha1sum", filename) output, err := cmd.CombinedOutput() if err != nil { return "", app.Error(err) } return strings.Split(string(output), " ")[0], nil }
func copyFile(from, to string) error { src, err := os.Open(from) if err != nil { return app.Error(err) } defer src.Close() dest, err := os.Create(to) if err != nil { return app.Error(err) } defer dest.Close() if _, err = io.Copy(dest, src); err != nil { return app.Error(err) } return nil }
func outputMap() error { conf := config.Current() if conf.Map == nil { return nil } f, err := os.Create(conf.Map.File) if err != nil { return app.Error(err) } defer f.Close() fmt.Fprintf(f, "var mapping = ") if err := json.NewEncoder(f).Encode(&mapping); err != nil { return app.Error(err) } return nil }
func CompiledJs(r *app.Request) error { r.W.Header().Set("Content-Type", "text/javascript") conf := config.Current() if err := FullCompile(); err != nil { return err } f, err := os.Open(path.Join(conf.Build, config.JS_NAME)) if err != nil { return app.Error(err) } defer f.Close() if _, err := io.Copy(r.W, f); err != nil { return app.Error(err) } return nil }
func addFile(w io.Writer, name string) error { f, err := os.Open(name) if err != nil { return app.Error(err) } defer f.Close() io.Copy(w, f) return nil }
// Save the caches to a file. func Dump() error { conf := config.Current() f, err := os.Create(filepath.Join(conf.Build, CACHE_FILENAME)) if err != nil { return app.Error(err) } defer f.Close() log.Println("Write", len(modificationCache), "modifications and", len(dataCache), "datas!") e := gob.NewEncoder(f) if err := e.Encode(&modificationCache); err != nil { return app.Error(err) } if err := e.Encode(&dataCache); err != nil { return app.Error(err) } return nil }
func cleanRenamingMap() error { conf := config.Current() // Create/Clean the renaming map file to avoid compilation errors (the JS // compiler assumes there's a file with this name there). f, err := os.Create(path.Join(conf.Build, config.RENAMING_MAP_NAME)) if err != nil { return app.Error(err) } f.Close() return nil }
func Load() error { if globalConf != nil && !NoCache { info, err := os.Lstat(ConfPath) if err != nil { return app.Error(err) } if info.ModTime() == lastModification { return nil } } f, err := os.Open(ConfPath) if err != nil { return app.Error(err) } defer f.Close() conf := new(Config) if err := xml.NewDecoder(f).Decode(&conf); err != nil { return app.Error(err) } // Assign it before validating, because we need it to // inherit targets. globalConf = conf if err := conf.validate(); err != nil { return err } info, err := os.Lstat(ConfPath) if err != nil { return app.Error(err) } lastModification = info.ModTime() return nil }
// Called before each compilation task. It load the caches // and reload the confs if needed. func PreCompile() error { if err := config.Load(); err != nil { return err } conf := config.Current() if err := os.MkdirAll(conf.Build, 0755); err != nil { return app.Error(err) } var err error loadCacheOnce.Do(func() { err = cache.Load() }) return err }
// Search for "_test.js" files and relativize them to // the root directory. It replaces the .js ext with .html. func scanTests() ([]string, error) { conf := config.Current() tests, err := scan.Do(conf.Js.Root, "_test.js") if err != nil { return nil, err } for i, test := range tests { // Relativize the path adding .html instead of .js p, err := filepath.Rel(conf.Js.Root, test[:len(test)-2]+"html") if err != nil { return nil, app.Error(err) } tests[i] = p } return tests, nil }
// Checks if filename has been modified since the last time // it was scanned. It so, or if it's not present in the cache, // it returns true and stores the new time. func Modified(dest, filename string) (bool, error) { if config.NoCache { return true, nil } name := dest + filename info, err := os.Lstat(filename) if err != nil { return false, app.Error(err) } modified, ok := modificationCache[name] if !ok || info.ModTime() != modified { modificationCache[name] = info.ModTime() return true, nil } return false, nil }
func (v *visitor) scan(file string, ext string) error { ls, err := ioutil.ReadDir(file) if err != nil { return app.Error(err) } for _, entry := range ls { fullpath := filepath.Join(file, entry.Name()) if entry.IsDir() { if v.validDir(fullpath, entry.Name()) { if err := v.scan(fullpath, ext); err != nil { return err } } } else if strings.HasSuffix(entry.Name(), ext) { v.results = append(v.results, fullpath) } } return nil }
// Compile all modified templates func Compile() error { conf := config.Current() if conf.Soy == nil || conf.Soy.Root == "" { return nil } if err := os.MkdirAll(path.Join(conf.Build, "templates"), 0755); err != nil { return app.Error(err) } buildPrefix := filepath.Join(conf.Build, "templates") oldSoy, err := scan.Do(buildPrefix, ".js") if err != nil { return err } soy, err := scan.Do(conf.Soy.Root, ".soy") if err != nil { return err } indexed := map[string]bool{} for _, f := range soy { f = f[len(conf.Soy.Root):] indexed[f] = true } // Delete compiled templates no longer present in the sources for _, f := range oldSoy { compare := f[len(buildPrefix) : len(f)-3] if _, ok := indexed[compare]; !ok { if err := os.Remove(f); err != nil { return app.Error(err) } } } if len(soy) == 0 { return nil } for _, t := range soy { if modified, err := cache.Modified("compile", t); err != nil { return err } else if !modified { continue } prel, err := filepath.Rel(conf.Soy.Root, t) if err != nil { return app.Error(err) } out := path.Join(conf.Build, "templates", prel+".js") if err := os.MkdirAll(path.Dir(out), 0755); err != nil { return app.Error(err) } log.Println("Compiling template", t, "...") // Run the compiler command cmd := exec.Command( "java", "-jar", path.Join(conf.Soy.Compiler, "build", "SoyToJsSrcCompiler.jar"), "--outputPathFormat", out, "--shouldGenerateJsdoc", "--shouldProvideRequireSoyNamespaces", "--cssHandlingScheme", "goog", t) output, err := cmd.CombinedOutput() if err != nil { return app.Errorf("exec error with %s: %s\n%s", t, err, string(output)) } log.Println("Done compiling template!") } return nil }
// Creates a new source. Returns the source, if it has been // loaded from cache or not, and an error. func NewSource(dest, filename, base string) (*Source, bool, error) { src := cache.ReadData(dest+filename, new(Source)).(*Source) // Return the file from cache if possible if modified, err := cache.Modified(dest, filename); err != nil { return nil, false, err } else if !modified { return src, true, nil } // Reset the source info src.Provides = []string{} src.Requires = []string{} src.Base = (filename == base) src.Filename = filename // Open the file f, err := os.Open(filename) if err != nil { return nil, false, app.Error(err) } defer f.Close() r := bufio.NewReader(f) for { // Read it line by line line, _, err := r.ReadLine() if err != nil { if err == io.EOF { break } return nil, false, err } // Find the goog.provide() calls if strings.Contains(string(line), "goog.provide") { matchs := provideRe.FindSubmatch(line) if matchs != nil { src.Provides = append(src.Provides, string(matchs[1])) continue } } // Find the goog.require() calls if strings.Contains(string(line), "goog.require") { matchs := requiresRe.FindSubmatch(line) if matchs != nil { src.Requires = append(src.Requires, string(matchs[1])) continue } } } // Validates the base file if src.Base { if len(src.Provides) > 0 || len(src.Requires) > 0 { return nil, false, app.Errorf("base files should not provide or require namespaces: %s", filename) } src.Provides = append(src.Provides, "goog") } return src, false, nil }