func (app *AppShell) startRunner() { for task := range app.taskChan { switch task.taskType { case kTaskBuildImages: app.curError = app.buildImages(task.module) case kTaskBuildStyles: app.curError = app.buildStyles(task.module) case kTaskBuildJavaScripts: app.curError = app.buildJavaScripts(task.module) case kTaskGenAssetsMapping: app.curError = app.genAssetsMapping() case kTaskBinaryTest: app.curError = app.binaryTest(task.module) case kTaskBuildBinary: app.curError = app.buildBinary() case kTaskBinaryRestart: if app.curError == nil { if err := app.kill(); err != nil { loggers.Error("App cannot be killed, maybe you should restart the gobuildweb: %v", err) } else { if err := app.start(); err != nil { loggers.Error("App cannot be started, maybe you should restart the gobuildweb: %v", err) } } } else { loggers.Warn("You have errors with current assets and binary, please fix that ...") } fmt.Println() loggers.Info("Waiting for the file changes ...") } } }
func (pw *ProjectWatcher) updateConfig() { loggers.Info("Reloading the project.toml file ...") var newConfig ProjectConfig if _, err := toml.DecodeFile("project.toml", &newConfig); err != nil { loggers.Error("We found the project.toml has changed, but it contains some error, will omit it.") loggers.Error("TOML Error: %v", err) fmt.Println() loggers.Info("Waiting for the file changes ...") } else { loggers.Succ("Loaded the new project.toml, will update all the dependencies ...") rootConfig.Lock() rootConfig.Package = newConfig.Package rootConfig.Assets = newConfig.Assets rootConfig.Distribution = newConfig.Distribution rootConfig.Unlock() if err := updateGolangDeps(); err != nil { loggers.Error("Failed to load project Go dependencies, %v", err) return } if err := updateAssetsDeps(); err != nil { loggers.Error("Failed to load project assets dependencies, %v", err) return } pw.addTask(kTaskBuildImages, "") pw.addTask(kTaskBuildStyles, "") pw.addTask(kTaskBuildJavaScripts, "") pw.addTask(kTaskGenAssetsMapping, "") pw.addTask(kTaskBuildBinary, "") pw.addTask(kTaskBinaryRestart, "") } }
func commandDist(args []string) error { if err := updateGolangDeps(); err != nil { loggers.Error("Failed to load project #Golang dependencies, %v", err) return err } if err := updateAssetsDeps(); err != nil { loggers.Error("Failed to load project assets dependencies, %v", err) return err } return NewAppShell(args).Dist() }
func (a _Asset) getJsonAssetsMapping() map[string]string { mapping := make(map[string]string) filename := a.config.AssetsMappingJson data, err := ioutil.ReadFile(filename) if err != nil { loggers.Error(fmt.Sprintf("Cannot decoding the assets into json, %v", err)) } else { err = json.Unmarshal(data, &mapping) if err != nil { loggers.Error(fmt.Sprintf("Cannot unmarshal the assets into json, %v", err)) } } return mapping }
func (d _GoPkgMappingDumper) Dump(mapping *AssetsMapping) error { pkgName, targetPath := d.GetPkgPath() mapping.PkgName = pkgName sort.Sort(mapping) if file, err := os.Create(targetPath); err != nil { return fmt.Errorf("Cannot create the assets mapping go file, %+v", err) } else { defer file.Close() if err := tmAssetsMapping.Execute(file, mapping); err != nil { return fmt.Errorf("Cannot generate assets mapping file, %+v", err) } } var out bytes.Buffer cmd := exec.Command("gofmt", "-w", targetPath) cmd.Stderr = &out if err := cmd.Run(); err != nil { loggers.Error("[AssetMapping] failed to gofmt source code, %v", out.String()) return err } loggers.Succ("[AssetMappings] Saved asssets mapping go file: %q", targetPath) return nil }
func updateGolangDeps() error { rootConfig.RLock() defer rootConfig.RUnlock() if rootConfig.Package == nil || len(rootConfig.Package.Dependencies) == 0 { return nil } fmt.Println() loggers.Info("Start to loading Go dependencies...") if hasGetColangDeps() { loggers.Info("Has Loaded Go package dependencies") return nil } params := []string{"get", ""} for _, dep := range rootConfig.Package.Dependencies { params[len(params)-1] = dep loggers.Info("Loading Go package dependency: %v", dep) getCmd := exec.Command("go", params...) getCmd.Stdout = os.Stdout getCmd.Stderr = os.Stderr getCmd.Env = mergeEnv(nil) if err := getCmd.Run(); err != nil { loggers.Error("Error when run go get: go %v, %v", params, err) return err } } loggers.Succ("Loaded Go package dependencies: \n\t%v", strings.Join(rootConfig.Package.Dependencies, "\n\t")) return nil }
func commandRun(args []string) error { if err := updateGolangDeps(); err != nil { loggers.Error("Failed to load project Go dependencies, %v", err) return err } if err := updateAssetsDeps(); err != nil { loggers.Error("Failed to load project assets dependencies, %v", err) return err } fmt.Println() if err := NewProjectWatcher().runAndWatch(".", args); err != nil { loggers.Error("Failed to start watching project changes, %v", err) return err } return nil }
func (app *AppShell) Dist() error { app.isProduction = true fmt.Println() loggers.Info("Creating distribution package for %v-%v", rootConfig.Package.Name, rootConfig.Package.Version) var err error if err = app.buildImages(""); err != nil { loggers.Error("Error when building images, %v", err) } else if err = app.genAssetsMapping(); err != nil { loggers.Error("Error when generating assets mapping source code, %v", err) } else if err = app.buildStyles(""); err != nil { loggers.Error("Error when building stylesheets, %v", err) } else if err = app.clearJavaScriptsAssets(); err != nil { loggers.Error("Error when clear javascripts, %v", err) } else if err = app.buildJavaScripts(APP_SHELL_JS_TASK_INIT_ENTRY_KEY); err != nil { loggers.Error("Error when building javascripts, %v", err) } else if err = app.genAssetsMapping(); err != nil { loggers.Error("Error when generating assets mapping source code, %v", err) } else if err = app.binaryTest(""); err != nil { loggers.Error("You have failed test cases, %v", err) } else if err = app.distExtraCommand(); err != nil { loggers.Error("Error when running the distribution extra command, %v", err) } else if err == nil { goOs, goArch := runtime.GOOS, runtime.GOARCH targets := append(rootConfig.Distribution.CrossTargets, [2]string{goOs, goArch}) visited := make(map[string]struct{}) for _, target := range targets { buildTarget := fmt.Sprintf("%s_%s", target[0], target[1]) if _, ok := visited[buildTarget]; ok { continue } visited[buildTarget] = struct{}{} if err = app.buildBinary(target[:]...); err != nil { loggers.Error("Error when building binary for %v, %v", target, err) } } } if err == nil { err = app.buildPackage() } return err }
func (pw *ProjectWatcher) maybeGoCodeChanged(fname string) { if strings.HasSuffix(fname, ".go") { goModule := path.Dir(fname) if pw.hasGoTests(goModule) { if moduleName, err := pw.goModuleName(goModule); err == nil { pw.addTask(kTaskBinaryTest, moduleName) } else { loggers.Error("Cannot get go module path name, %v", err) } } pw.addTask(kTaskBuildBinary, goModule) pw.addTask(kTaskBinaryRestart, "") } }
func (css _StyleSheet) Build(isProduction bool) error { filename := fmt.Sprintf("assets/stylesheets/%s.styl", css.entry) isStylus := true if exist, _ := css.checkFile(filename, true); !exist { filename = fmt.Sprintf("assets/stylesheets/%s.css", css.entry) if exist, err := css.checkFile(filename, true); !exist { return err } else { isStylus = false } } target := fmt.Sprintf("public/stylesheets/%s.css", css.entry) // * Maybe it's a template using images, styles assets links // TODO // * Maybe we need to call stylus preprocess if isStylus { params := []string{"--use", "nib", filename, "--out", "public/stylesheets"} if isProduction { params = append(params, "--compress") } else { params = append(params, "--sourcemap-inline") } params = append(params, "--include-css") // add asset plugin to change original file path to the finger printed one if css.config.AssetsMappingJson != "" { params = append(params, "--use", getStylusPluginPath()) } cmd := exec.Command("./node_modules/stylus/bin/stylus", params...) loggers.Debug("[CSS][%s] Building asset: %s, %v", css.entry, filename, cmd.Args) cmd.Stderr = os.Stderr cmd.Stdout = os.Stdout cmd.Env = css.getEnv(isProduction) if err := cmd.Run(); err != nil { loggers.Error("[CSS][%s] Error when building asset %v, %v", css.entry, cmd.Args, err) return err } } else { if err := css.copyFile(target, filename); err != nil { return err } } // * generate the hash, clear old bundle, move to target target = css.addFingerPrint("public/stylesheets", css.entry+".css") loggers.Succ("[CSS][%s] Saved assset: %s", css.entry, target) return nil }
func (app *AppShell) distExtraCommand() error { extraCmd := rootConfig.Distribution.ExtraCmd if extraCmd == nil || len(extraCmd) == 0 { return nil } cmd := exec.Command(extraCmd[0], extraCmd[1:]...) cmd.Stderr = os.Stderr cmd.Stdout = os.Stdout if err := cmd.Run(); err != nil { loggers.Error("Error when running distribution extra command, %v, %s", extraCmd, err) return err } loggers.Succ("Run extra command succ: %v", cmd.Args) return nil }
//是否已经下载了go运行所需要的包,是否可以编译成功 func hasGetColangDeps() bool { cmd := exec.Command("go", "build") loggers.Info("Started to go build...") err := cmd.Start() if err != nil { loggers.Error("Failed to go build... %+v", err) return false } done := make(chan error, 1) go func() { done <- cmd.Wait() }() err = <-done if err != nil { loggers.Error("Failed to go build...%+v", err) } else { loggers.Info("Successed to go build...") cmd := exec.Command("rm", "zhiwang_web") cmd.Run() return true } return false }
func (app *AppShell) binaryTest(module string) error { return nil // close the test first, will reconsider this if module == "" { module = "./..." } testCmd := exec.Command("go", "test", "-v", module) testCmd.Stderr = os.Stderr testCmd.Stdout = os.Stdout testCmd.Env = mergeEnv(nil) if err := testCmd.Run(); err != nil { loggers.Error("Error when testing go modules[%s], %v", module, err) return err } loggers.Succ("Module[%s] Test passed: %v", module, testCmd.Args) return nil }
func (app *AppShell) buildBinary(params ...string) error { goOs, goArch := runtime.GOOS, runtime.GOARCH if len(params) == 2 && (goOs != params[0] || goArch != params[1]) { goOs, goArch = params[0], params[1] } rootConfig.RLock() builder := rootConfig.Package.Builder binName := app.binaryName(rootConfig.Package.Name, rootConfig.Package.Version, goOs, goArch) var buildOpts []string if app.isProduction { buildOpts = make([]string, len(rootConfig.Distribution.BuildOpts)) copy(buildOpts, rootConfig.Distribution.BuildOpts) } else { buildOpts = make([]string, len(rootConfig.Package.BuildOpts)) copy(buildOpts, rootConfig.Package.BuildOpts) } rootConfig.RUnlock() cmdName := "go" flags := make([]string, 0, 3+len(buildOpts)) if builder != "" { flags = append(flags, "go") cmdName = builder } flags = append(flags, "build") flags = append(flags, buildOpts...) flags = append(flags, []string{"-o", binName}...) buildCmd := exec.Command(cmdName, flags...) buildCmd.Stderr = os.Stderr buildCmd.Stdout = os.Stdout buildCmd.Env = mergeEnv(map[string]string{ "GOOS": goOs, "GOARCH": goArch, }) loggers.Debug("Running build: %v", buildCmd.Args) start := time.Now() if err := buildCmd.Run(); err != nil { loggers.Error("Building failed") return err } app.binName = binName duration := float64(time.Since(start).Nanoseconds()) / 1e6 loggers.Succ("Got binary built %s, takes=%.3fms", binName, duration) return nil }
func (d _JsonMappingDumper) Dump(mapping *AssetsMapping) error { srcMap := make(map[string]string) for _, m := range mapping.Mappings { srcMap[m.Src] = m.Target } if data, err := json.MarshalIndent(srcMap, "", " "); err != nil { return fmt.Errorf("Cannot encoding the assets into json, %s", err) } else { if err := ioutil.WriteFile(d.jsonFile, data, 0644); err != nil { loggers.Error("[AsssetMapping] failed to write json mapping file, %s", err) return err } } loggers.Succ("[AssetMappings] Saved asssets mapping json file: %q", d.jsonFile) return nil }
func updateAssetsDeps() error { rootConfig.RLock() defer rootConfig.RUnlock() if rootConfig.Assets == nil || len(rootConfig.Assets.Dependencies) == 0 { return nil } fmt.Println() loggers.Info("Start to loading assets dependencies...") checkParams := []string{"list", "--depth", "0", ""} params := []string{"install", ""} deps := make([]string, len(rootConfig.Assets.Dependencies), len(rootConfig.Assets.Dependencies)+1) copy(deps, rootConfig.Assets.Dependencies) // add all dev deps for xxxify deps = append(deps, "browserify", "coffeeify", "envify", "uglifyify", "babelify", "babel-preset-es2015", "babel-preset-react", "nib", "stylus") for _, dep := range deps { checkParams[len(checkParams)-1] = dep listCmd := exec.Command("npm", checkParams...) listCmd.Env = mergeEnv(nil) if err := listCmd.Run(); err == nil { // the module has been installed loggers.Info("Checked npm module: %v", dep) continue } params[len(params)-1] = dep loggers.Info("Loading npm module: %v", dep) installCmd := exec.Command("npm", params...) installCmd.Stdout = os.Stdout installCmd.Stderr = os.Stderr installCmd.Env = mergeEnv(nil) if err := installCmd.Run(); err != nil { loggers.Error("Error when run npm install: npm %v, %v", params, err) return err } } loggers.Succ("Loaded assets dependencies: \n\t%v", strings.Join(deps, "\n\t")) return nil }
func (app *AppShell) binaryTest(module string) error { return nil // close the test first, will reconsider this if module == "" { module = "./..." } builder := rootConfig.Package.Builder cmdName := "go" flags := make([]string, 0) if builder != "" { cmdName = builder flags = append(flags, "go") } flags = append(flags, "test", "-v", module) testCmd := exec.Command(cmdName, flags...) testCmd.Stderr = os.Stderr testCmd.Stdout = os.Stdout testCmd.Env = mergeEnv(nil) if err := testCmd.Run(); err != nil { loggers.Error("Error when testing go modules[%s], %v", module, err) return err } loggers.Succ("Module[%s] Test passed: %v", module, testCmd.Args) return nil }
func (js _JavaScript) Build(isProduction bool) error { if os.Getenv("NODE_ENV") == "production" { isProduction = true } assetEntry, ok := js.config.getAssetEntry(js.entry) if !ok { return nil } suffixJs, suffixCoffee, originDir, targetDir := ".js", ".coffee", "assets/javascripts", "public/javascripts" filename, outfile := path.Join(originDir, js.entry+suffixJs), path.Join(targetDir, js.entry+suffixJs) isCoffee := false if exist, _ := js.checkFile(filename, true); !exist { filename = path.Join(originDir, js.entry+suffixCoffee) if exist, err := js.checkFile(filename, true); !exist { return err } isCoffee = true } // * generate the hash, just loaded if not change outTarget := js.traverEntryFingerPrint(originDir, targetDir, js.entry, filename, suffixJs) mapping := js._Asset.getJsonAssetsMapping() if targetName, ok := mapping[outfile[len("public/"):]]; ok { if targetName == outTarget[len("public/"):] { loggers.Succ("[JavaScript][%s] Loaded assset: %s", js.entry, outTarget) return nil } } // * Maybe it's a template using images, styles assets links // TODO // * run browserify params := []string{filename} for _, require := range assetEntry.Requires { params = append(params, "--require", require) } for _, external := range assetEntry.Externals { if anEntry, ok := js.config.getAssetEntry(external); ok { for _, require := range anEntry.Requires { params = append(params, "--external", require) } } } for _, opt := range assetEntry.BundleOpts { params = append(params, opt) } if isCoffee { params = append(params, "--transform", "coffeeify") } else { params = append(params, "--transform", "[", "babelify", "--presets", "[", "es2015", "react", "]", "]") } params = append(params, "--transform", "envify") if isProduction { params = append(params, "-g", "uglifyify") } else { params = append(params, "--debug") } params = append(params, "--outfile", outfile) cmd := exec.Command("./node_modules/browserify/bin/cmd.js", params...) loggers.Debug("[JavaScript][%s] Building asset: %s, %v", js.entry, filename, cmd.Args) cmd.Stderr = os.Stderr cmd.Stdout = os.Stdout cmd.Env = js.getEnv(isProduction) if err := cmd.Run(); err != nil { loggers.Error("[JavaScript][%s] Error when building asset %v, %v", js.entry, cmd.Args, err) return err } //clear old bundle, move to target js._Asset.removeOldFile(targetDir, js.entry+suffixJs) if err := os.Rename(outfile, outTarget); err != nil { loggers.Error("rename file error, %v", err) } // target = js.addFingerPrint("public/javascripts", js.entry+".js") loggers.Succ("[JavaScript][%s] Saved assset: %s", js.entry, outTarget) return nil }
func (pw *ProjectWatcher) watchProject() { tick := time.Tick(800 * time.Millisecond) for { select { case event := <-pw.watcher.Events: if event.Name == "" || pw.isIgnoredDir(event.Name) || strings.HasSuffix(event.Name, ".swp") || strings.HasSuffix(event.Name, ".DS_Store") { break } loggers.Debug("fsevents: %v", event) if event.Op&fsnotify.Create == fsnotify.Create || event.Op&fsnotify.Write == fsnotify.Write { if fi, err := os.Stat(event.Name); err == nil { if fi.IsDir() { if err := pw.watcher.Add(event.Name); err != nil { loggers.Error("Failed to add new directory into watching list[%v], %v", event.Name, err) } else { loggers.Debug("Watching %s", event.Name) } } else { if event.Name == "project.toml" { pw.updateConfig() } pw.maybeGoCodeChanged(event.Name) pw.maybeAssetsChanged(event.Name) } } } else if event.Op&fsnotify.Remove == fsnotify.Remove || event.Op&fsnotify.Rename == fsnotify.Rename { // maybe remove some dir if fi, err := os.Stat(event.Name); err == nil { if fi.IsDir() { if err := pw.watcher.Remove(event.Name); err != nil { loggers.Error("Failed to remove directory from watching list [%v], %v", event.Name, err) } // if the dir is under assets, we need to rebuild all the assets or sprites // else we take it as a go code directory // TODO } else { if event.Name == "project.toml" { panic("Please don't hurt the project.toml") } // maybe remove some source code // TODO } } } case err := <-pw.watcher.Errors: loggers.Error("Error: %v", err) case <-tick: pw.taskLock.Lock() if len(pw.tasks) > 0 { pw.app.executeTask(pw.tasks...) pw.tasks = make([]AppShellTask, 0) } pw.taskLock.Unlock() } } }
func (js _JavaScript) Build(isProduction bool) error { if os.Getenv("NODE_ENV") == "production" { isProduction = true } assetEntry, ok := js.config.getAssetEntry(js.entry) if !ok { return nil } target := fmt.Sprintf("public/javascripts/%s.js", js.entry) filename := fmt.Sprintf("assets/javascripts/%s.js", js.entry) isCoffee := false if exist, _ := js.checkFile(filename, true); !exist { filename = fmt.Sprintf("assets/javascripts/%s.coffee", js.entry) if exist, err := js.checkFile(filename, true); !exist { return err } isCoffee = true } // * Maybe it's a template using images, styles assets links // TODO // * run browserify params := []string{filename} for _, require := range assetEntry.Requires { params = append(params, "--require", require) } for _, external := range assetEntry.Externals { if anEntry, ok := js.config.getAssetEntry(external); ok { for _, require := range anEntry.Requires { params = append(params, "--external", require) } } } for _, opt := range assetEntry.BundleOpts { params = append(params, opt) } if isCoffee { params = append(params, "--transform", "coffeeify") } else { params = append(params, "--transform", "[", "babelify", "--presets", "[", "es2015", "react", "]", "]") } params = append(params, "--transform", "envify") if isProduction { params = append(params, "-g", "uglifyify") } else { params = append(params, "--debug") } params = append(params, "--outfile", target) cmd := exec.Command("./node_modules/browserify/bin/cmd.js", params...) loggers.Debug("[JavaScript][%s] Building asset: %s, %v", js.entry, filename, cmd.Args) cmd.Stderr = os.Stderr cmd.Stdout = os.Stdout cmd.Env = js.getEnv(isProduction) if err := cmd.Run(); err != nil { loggers.Error("[JavaScript][%s] Error when building asset %v, %v", js.entry, cmd.Args, err) return err } // * generate the hash, clear old bundle, move to target target = js.addFingerPrint("public/javascripts", js.entry+".js") loggers.Succ("[JavaScript][%s] Saved assset: %s", js.entry, target) return nil }