func (p *Project) startLocked() error { p.port = randomFreePort() cmd := p.ProjectCmd() log.Infof("Starting %s (%s)", p.Name(), cmdString(cmd)) p.cmd = cmd p.out.Reset() p.runError = nil p.exitCode = 0 err := cmd.Start() go func() { werr := cmd.Wait() if cmd == p.cmd { // Othewise the process was intentionally killed if s := cmd.ProcessState; s != nil { exitCode := exitStatus(s) p.Lock() defer p.Unlock() p.runError = werr p.exitCode = exitCode log.Warningf("%s exited with code %d", p.Name(), exitCode) } } }() time.AfterFunc(100*time.Millisecond, p.projectStarted) return err }
// Timings returns the available timings for the current // goroutine. Note that calling Timings will end any events // which have been set to automatically end (with Timed.AutoEnd) // so this function should only be called at the end of the // request lifecycle. func Timings() []*Timing { var timings map[string]*Timing contexts.RLock() ctx := contexts.data[goroutineId()] if ctx != nil { timings = make(map[string]*Timing) ctx.Lock() for _, v := range ctx.events { if v.ended.IsZero() { if v.autoend { v.End() } else { if len(v.notes) > 0 { log.Warningf("unfinished %q event (%s)", v.name, v.notes) } else { log.Warningf("unfinished %q event", v.name) } continue } } timing := timings[v.name] if timing == nil { timing = &Timing{Name: v.name} timings[v.name] = timing } timing.Events = append(timing.Events, &Event{v.started, v.ended, v.notes}) } ctx.Unlock() } contexts.RUnlock() ret := make([]*Timing, 0, len(timings)) for _, v := range timings { ret = append(ret, v) } return ret }
// Handler returns an http.handlerFunc which serves the assets from this // Manager. To avoid circular imports, this function returns an http.HandlerFunc // rather than a gnd.la/app.Handler. To obtain a gnd.la/app.Handler use // gnd.la/app.HandlerFromHTTPFunc. func (m *Manager) Handler() http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { p := m.Path(r.URL) f, err := m.Load(p) if err != nil { log.Warningf("error serving %s: %s", r.URL, err) return } seeker, err := Seeker(f) if err != nil { log.Warningf("error serving %s: %s", r.URL, err) return } var modtime time.Time if st, err := m.VFS().Stat(p); err == nil { modtime = st.ModTime() } if r.URL.RawQuery != "" { httpserve.NeverExpires(w) } http.ServeContent(w, r, r.URL.Path, modtime, seeker) f.Close() } }
func (t *Template) prepareAssets() error { groups, err := t.preparedAssetsGroups(t.vars, t, nil) if err != nil { return err } if err := sortGroups(groups); err != nil { return err } var top bytes.Buffer var bottom bytes.Buffer for _, group := range groups { // Only bundle and use CDNs in non-debug mode if !t.Debug { if group[0].Options.Bundle() || group[0].Options.Bundable() { bundled, err := assets.Bundle(group, group[0].Options) if err == nil { group = []*assets.Group{ &assets.Group{ Manager: group[0].Manager, Assets: []*assets.Asset{bundled}, Options: group[0].Options, }, } } else { var names []string for _, g := range group { for _, a := range g.Assets { names = append(names, a.Name) } } log.Errorf("error bundling assets %s: %s - using individual assets", names, err) } } else if group[0].Options.Cdn() { for _, g := range group { var groupAssets []*assets.Asset for _, a := range g.Assets { cdnAssets, err := assets.CdnAssets(g.Manager, a) if err != nil { if !g.Manager.Has(a.Name) { return fmt.Errorf("could not find CDN for asset %q: %s", a.Name, err) } log.Warningf("could not find CDN for asset %q: %s - using local copy", a.Name, err) groupAssets = append(groupAssets, a) continue } groupAssets = append(groupAssets, cdnAssets...) } g.Assets = groupAssets } } } for _, g := range group { for _, v := range g.Assets { switch v.Position { case assets.Top: if err := assets.RenderTo(&top, g.Manager, v); err != nil { return fmt.Errorf("error rendering asset %q", v.Name) } top.WriteByte('\n') case assets.Bottom: if err := assets.RenderTo(&bottom, g.Manager, v); err != nil { return fmt.Errorf("error rendering asset %q", v.Name) } bottom.WriteByte('\n') default: return fmt.Errorf("asset %q has invalid position %s", v.Name, v.Position) } } } } t.topAssets = top.Bytes() t.bottomAssets = bottom.Bytes() return nil }
func Bundle(groups []*Group, opts Options) (*Asset, error) { assetType := Type(-1) var names []string for _, group := range groups { for _, v := range group.Assets { if v.Type == TypeOther { return nil, fmt.Errorf("asset %q does not specify a Type and can't be bundled", v.Name) } if assetType < 0 { assetType = v.Type } else if assetType != v.Type { return nil, fmt.Errorf("asset %q has different code type %s (first asset is of type %s)", v.Name, v.Type, assetType) } names = append(names, v.Name) } } bundler := bundlers[assetType] if bundler == nil { return nil, fmt.Errorf("no bundler for %s", assetType) } // Prepare the code, changing relative paths if required name, err := bundleName(groups, assetType.Ext(), opts) if err != nil { return nil, err } // The bundle is output to the first manager m := groups[0].Manager // Check if the code has been already bundled if m.Has(name) { log.Debugf("%s already bundled into %s and up to date", names, name) } else { dir := path.Dir(name) log.Debugf("bundling %v", names) var code []string for _, group := range groups { for _, v := range group.Assets { c, err := v.Code(group.Manager) if err != nil { return nil, fmt.Errorf("error getting code for asset %q: %s", v.Name, err) } if vd := path.Dir(v.Name); vd != dir { if assetType == TypeCSS { log.Debugf("asset %q will move from %v to %v, rewriting relative paths...", v.Name, vd, dir) c = replaceRelativePaths(c, vd, dir) } else { log.Warningf("asset %q will move from %v to %v, relative paths might not work", v.Name, vd, dir) } } code = append(code, c) } } // Bundle to a buf first. We don't want to create // the file if the bundling fails. var buf bytes.Buffer allCode := strings.Join(code, "\n\n") reader := strings.NewReader(allCode) if err := bundler.Bundle(&buf, reader, opts); err != nil { return nil, err } s := makeLinksCacheable(m, dir, buf.Bytes()) initial := len(allCode) final := len(s) var percent float64 if initial != 0 { percent = float64(final) / float64(initial) * 100 } log.Debugf("reduced size from %s to %s (%.2f%%)", formatutil.Size(uint64(initial)), formatutil.Size(uint64(final)), percent) w, err := m.Create(name, true) if err == nil { if _, err := io.Copy(w, strings.NewReader(s)); err != nil { w.Close() return nil, err } if err := w.Close(); err != nil { return nil, err } } else { // If the file exists, is up to date if !os.IsExist(err) { return nil, err } } } return &Asset{ Name: name, Type: assetType, Attributes: groups[0].Assets[0].Attributes, Position: groups[0].Assets[0].Position, }, nil }