Beispiel #1
0
func copyCssFile() error {
	conf := config.Current()
	target := conf.Gss.CurTarget()

	if conf.Gss == nil {
		return nil
	}

	srcName := filepath.Join(conf.Build, config.CSS_NAME)
	filename := target.Output
	if strings.Contains(filename, "{sha1}") {
		sha1, err := calcFileSha1(srcName)
		if err != nil {
			return err
		}
		filename = strings.Replace(filename, "{sha1}", sha1, -1)
	}

	mapping[config.SelectedTarget+"-css"] = filename

	if err := copyFile(srcName, filename); err != nil {
		return err
	}

	return nil
}
Beispiel #2
0
// Scans folder recursively search for files with the ext
// extension and returns the whole list.
func Do(folder string, ext string) ([]string, error) {
	conf := config.Current()

	var library bool
	if conf.Library != nil {
		library = strings.Contains(folder, conf.Library.Root)
	}

	if library {
		r, ok := libraryCache[folder]
		if ok {
			return r, nil
		}
	}

	v := &visitor{[]string{}}
	if err := v.scan(folder, ext); err != nil {
		return nil, err
	}

	if library {
		libraryCache[folder] = v.results
	}

	return v.results, nil
}
Beispiel #3
0
// 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
}
Beispiel #4
0
func (s *DepsSuite) BenchmarkGeneration(c *C) {
	conf := config.Current()

	for i := 0; i < c.N; i++ {
		depstree, err := NewDepsTree("compile")
		if err != nil {
			c.Error(err)
			return
		}

		namespaces := []string{}
		for _, input := range conf.Js.Inputs {
			if strings.Contains(input.File, "_test") {
				continue
			}

			ns, err := depstree.GetProvides(input.File)
			if err != nil {
				c.Error(err)
				return
			}
			namespaces = append(namespaces, ns...)
		}

		if _, err := depstree.GetDependencies(namespaces); err != nil {
			c.Error(err)
			return
		}
	}
}
Beispiel #5
0
func copyJsFile() error {
	conf := config.Current()
	target := conf.Js.CurTarget()

	if conf.Js == nil {
		return nil
	}

	srcName := filepath.Join(conf.Build, config.JS_NAME)

	filename := filepath.Join(conf.Js.Root, target.Output)
	if strings.Contains(filename, "{sha1}") {
		sha1, err := calcFileSha1(srcName)
		if err != nil {
			return err
		}
		filename = strings.Replace(filename, "{sha1}", sha1, -1)
	}

	mapping[config.SelectedTarget+"-js"] = filename

	files := []string{}
	for _, n := range conf.Js.Prepends {
		files = append(files, filepath.Join(conf.Js.Root, n.File))
	}
	files = append(files, srcName)

	if err := copyFiles(files, filename); err != nil {
		return err
	}

	return nil
}
Beispiel #6
0
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
}
Beispiel #7
0
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)
}
Beispiel #8
0
func compile(r *app.Request) error {
	conf := config.Current()
	target := conf.Js.CurTarget()

	if target == nil || target.Mode == "RAW" {
		return RawOutput(r)
	}

	return js.CompiledJs(r)
}
Beispiel #9
0
// Returns true if the directory name is worth scanning.
// It checks too the list of ignored files.
func (v *visitor) validDir(path, name string) bool {
	conf := config.Current()
	if conf.Ignores != nil && path != "" {
		for _, ignore := range conf.Ignores {
			if strings.HasPrefix(path, ignore.Path) {
				return false
			}
		}
	}

	return name != ".svn" && name != ".hg" && name != ".git"
}
Beispiel #10
0
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)
}
Beispiel #11
0
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
}
Beispiel #12
0
// Adds a new JS source file to the tree
func (tree *DepsTree) AddSource(filename string) error {
	// Build the source
	src, cached, err := domain.NewSource(tree.dest, filename, tree.basePath)
	if err != nil {
		return err
	}

	// If it's the base file, save it
	if src.Base {
		tree.base = src
	}

	conf := config.Current()

	// Scan all the previous sources searching for repeated
	// namespaces. We ignore closure library files because they're
	// supposed to be correct and tested by other methods
	if conf.Library == nil || !strings.HasPrefix(filename, conf.Library.Root) {
		for k, source := range tree.sources {
			for _, provide := range source.Provides {
				if In(src.Provides, provide) {
					return app.Errorf("multiple provide %s: %s and %s", provide, k, filename)
				}
			}
		}
	}

	// Files without the goog.provide directive
	// use a trick to provide its own name. It fullfills the need
	// to compile things apart from the Closure style (Angular, ...).
	if len(src.Provides) == 0 {
		src.Provides = []string{filename}
	}

	// Add all the provides to the map
	for _, provide := range src.Provides {
		tree.provides[provide] = src
	}

	// Save the source
	tree.sources[filename] = src

	// Update the MustCompile flag
	tree.MustCompile = tree.MustCompile || !cached

	return nil
}
Beispiel #13
0
// 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
}
Beispiel #14
0
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
}
Beispiel #15
0
// 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
}
Beispiel #16
0
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
}
Beispiel #17
0
// 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
}
Beispiel #18
0
// Build a dependency tree that allows the client to know the order of
// compilation
// Dest will be "compile" or "input" depending on the use.
func NewDepsTree(dest string) (*DepsTree, error) {
	conf := config.Current()

	// Initialize the tree
	depstree := &DepsTree{
		sources:  map[string]*domain.Source{},
		provides: map[string]*domain.Source{},
		dest:     dest,
	}

	if conf.Library != nil {
		depstree.basePath = path.Join(conf.Library.Root, "closure", "goog", "base.js")
	}

	// Build the deps tree scanning each root directory recursively
	roots := BaseJSPaths()
	for _, root := range roots {
		// Scan the sources
		src, err := Do(root, ".js")
		if err != nil {
			return nil, err
		}

		// Add them to the tree
		for _, s := range src {
			if err := depstree.AddSource(s); err != nil {
				return nil, err
			}
		}
	}

	// Check the integrity of the tree
	if err := depstree.Check(); err != nil {
		return nil, err
	}

	return depstree, nil
}
Beispiel #19
0
// Base paths, all routes to a JS must start from one
// of these ones.
// The order is important, the paths will be scanned as
// they've been written.
func BaseJSPaths() []string {
	conf := config.Current()

	p := []string{}

	if conf.Library != nil {
		p = append(p, path.Join(conf.Library.Root, "closure", "goog"))
		p = append(p, conf.Library.Root)
	}

	if conf.Js != nil {
		p = append(p, conf.Js.Root)
	}

	if conf.Soy != nil {
		path.Join(conf.Soy.Compiler, "javascript")
		if conf.Soy.Root != "" {
			p = append(p, path.Join(conf.Build, "templates"))
		}
	}

	return p
}
Beispiel #20
0
func Compile() error {
	conf := config.Current()
	target := conf.Js.CurTarget()

	if conf.Js == nil {
		return nil
	}

	if len(conf.Js.Inputs) == 0 {
		return nil
	}

	deps, _, err := GenerateDeps("compile")
	if err != nil {
		return err
	}

	args := []string{
		"-jar", path.Join(conf.Js.Compiler, "build", "compiler.jar"),
		"--js_output_file", path.Join(conf.Build, config.JS_NAME),
	}

	if conf.Library != nil {
		args = append(args,
			"--js", path.Join(conf.Library.Root, "closure", "goog", "base.js"),
			"--js", path.Join(conf.Library.Root, "closure", "goog", "deps.js"),
		)
	}

	args = append(args,
		"--js", filepath.Join(conf.Build, config.DEPS_NAME),
		"--js", filepath.Join(conf.Build, config.RENAMING_MAP_NAME),
	)

	if conf.Js.SideEffects == "" {
		args = append(args, "--output_wrapper", `(function(){%output%})();`)
	}

	for _, dep := range deps {
		if !strings.Contains(dep.Filename, "_test.js") {
			args = append(args, "--js", dep.Filename)
		}
	}

	if target.Defines != nil {
		for _, define := range target.Defines {
			// If it's not a boolean, quote it
			if define.Value != "true" && define.Value != "false" {
				define.Value = "\"" + define.Value + "\""
			}
			args = append(args, "--define", define.Name+"="+define.Value)
		}
	}

	if target.Mode == "ADVANCED" {
		args = append(args, "--compilation_level", "ADVANCED_OPTIMIZATIONS")
	} else if target.Mode == "SIMPLE" {
		args = append(args, "--compilation_level", "SIMPLE_OPTIMIZATIONS")
	} else if target.Mode == "WHITESPACE" {
		args = append(args, "--compilation_level", "WHITESPACE_ONLY")
	} else {
		return app.Errorf("RAW mode not allowed while compiling")
	}

	args = append(args, "--warning_level", target.Level)

	if conf.Js.Checks != nil {
		for _, check := range conf.Js.Checks.Errors {
			args = append(args, "--jscomp_error", check.Name)
		}
		for _, check := range conf.Js.Checks.Warnings {
			args = append(args, "--jscomp_warning", check.Name)
		}
		for _, check := range conf.Js.Checks.Offs {
			args = append(args, "--jscomp_off", check.Name)
		}
	}

	for _, extern := range conf.Js.Externs {
		args = append(args, "--externs", extern.File)
	}

	if conf.Js.Language != "" {
		args = append(args, "--language_in", conf.Js.Language)
	}

	if conf.Js.Formatting != "" {
		args = append(args, "--formatting", conf.Js.Formatting)
		args = append(args, "--debug", "true")
	}

	log.Println("Compiling JS:", target.Name)

	// Prepare the command
	cmd := exec.Command("java", args...)

	// Output it if asked to
	if config.OutputCmd {
		fmt.Println("java", strings.Join(cmd.Args, " "))
	}

	// Run the JS compiler
	output, err := cmd.CombinedOutput()
	if err != nil {
		if len(output) != 0 {
			fmt.Println(string(output))
		}

		return app.Errorf("exec error: %s", err)
	}

	if len(output) > 0 {
		log.Println("Output from JS compiler:\n", string(output))
	}

	log.Println("Done compiling JS!")

	return nil
}
Beispiel #21
0
// 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
}
Beispiel #22
0
// Compiles the .gss files
func Compile() error {
	conf := config.Current()
	target := conf.Gss.CurTarget()

	// Output early if there's no GSS files.
	if conf.Gss == nil {
		if err := cleanRenamingMap(); err != nil {
			return err
		}

		return nil
	}

	// Check if the cached version is still ok
	modified := false
	for _, input := range conf.Gss.Inputs {
		if m, err := cache.Modified("compile", input.File); err != nil {
			return err
		} else if m {
			modified = true
			break
		}
	}

	if !modified {
		return nil
	}

	log.Println("Compiling GSS:", target.Name)

	if err := cleanRenamingMap(); err != nil {
		return err
	}

	// Prepare the list of non-standard functions.
	funcs := []string{}
	for _, f := range conf.Gss.Funcs {
		funcs = append(funcs, "--allowed-non-standard-function")
		funcs = append(funcs, f.Name)
	}

	// Prepare the renaming map args
	renaming := []string{}
	if target.Rename == "true" {
		renaming = []string{
			"--output-renaming-map-format", "CLOSURE_COMPILED",
			"--rename", "CLOSURE",
			"--output-renaming-map", path.Join(conf.Build, config.RENAMING_MAP_NAME),
		}
	}

	// Prepare the defines
	defines := []string{}
	for _, define := range target.Defines {
		defines = append(defines, "--define", define.Name)
	}

	// Prepare the inputs
	inputs := []string{}
	for _, input := range conf.Gss.Inputs {
		inputs = append(inputs, input.File)
	}

	// Prepare the command
	cmd := exec.Command(
		"java",
		"-jar", path.Join(conf.Gss.Compiler, "build", "closure-stylesheets.jar"),
		"--output-file", filepath.Join(conf.Build, config.CSS_NAME))
	cmd.Args = append(cmd.Args, funcs...)
	cmd.Args = append(cmd.Args, renaming...)
	cmd.Args = append(cmd.Args, inputs...)
	cmd.Args = append(cmd.Args, defines...)

	// Output the command if asked to
	if config.OutputCmd {
		fmt.Println("java", strings.Join(cmd.Args, " "))
	}

	// Run the compiler
	output, err := cmd.CombinedOutput()
	if err != nil {
		if len(output) != 0 {
			fmt.Println(string(output))
		}

		return app.Errorf("exec error: %s", err)
	}

	if len(output) > 0 {
		log.Println("Output from GSS compiler:\n", string(output))
	}

	log.Println("Done compiling GSS!")

	return nil
}