Beispiel #1
0
// Build builds the project. If the project was already building, the build
// is restarted.
func (p *Project) Build() {
	builder := &Builder{
		Dir:     p.dir,
		GoFlags: p.goFlags,
		Tags:    p.tags,
	}
	var restarted bool
	p.Lock()
	if p.builder != nil {
		p.builder.Cancel()
		restarted = true
	}
	p.builder = builder
	p.StopMonitoring()
	p.Unlock()
	if err := p.Stop(); err != nil {
		log.Panic(err)
	}
	p.errors = nil
	if !restarted {
		log.Infof("Building %s (%s)", p.Name(), builder.BuildCommandString())
	}
	var err error
	p.errors, err = builder.Build()
	p.Lock()
	defer p.Unlock()
	if p.builder != builder {
		// Canceled by another build
		return
	}
	p.builder = nil
	p.built = time.Now().UTC()
	if err != nil {
		log.Errorf("%d errors building %s", len(p.errors), p.Name())
		p.reloadClients()
	} else {
		if err := p.startLocked(); err != nil {
			log.Panic(err)
		}
	}
	if err := p.StartMonitoring(); err != nil {
		log.Errorf("Error monitoring files for project %s: %s. Development server must be manually restarted.", p.Name(), err)
	}
	// Build dependencies, to speed up future builds
	go func() {
		builder.GoInstallDeps()
	}()
}
Beispiel #2
0
func (b *Builder) Build() ([]*BuildError, error) {
	b.Cancel()
	cmd, err := b.compilerCmd()
	if err != nil {
		return nil, err
	}
	var buf bytes.Buffer
	cmd.Stdout = os.Stdout
	cmd.Stderr = io.MultiWriter(&buf, os.Stderr)
	err = cmd.Run()
	var errs []*BuildError
	if err != nil {
		exitErr, ok := err.(*exec.ExitError)
		if !ok {
			log.Panic(err)
		}
		if es := exitStatus(exitErr.ProcessState); es != 1 && es != 2 {
			// gc returns 1 when it can't find a package, 2 when there are compilation errors
			log.Panic(err)
		}
		r := bufio.NewReader(bytes.NewReader(buf.Bytes()))
		var pkg string
		for {
			eline, err := r.ReadString('\n')
			if err != nil {
				if err == io.EOF {
					break
				}
				log.Panic(err)
			}
			var be *BuildError
			switch {
			case strings.HasPrefix(eline, "package "):
				// package level error, like cyclic or non-allowed
				// (e.g. internal) imports. We need to create an error
				// now, since this line will usually be followed by
				// lines starting with \t
				pkg = strings.TrimSpace(eline[len("package"):])
				be = &BuildError{
					Error: strings.TrimSpace(eline),
				}
			case strings.HasPrefix(eline, "#"):
				// Package name before file level errors
				pkg = strings.TrimSpace(eline[1:])
			case strings.HasPrefix(eline, "\t"):
				// Info related to the previous error. Let it
				// crash if we don't have a previous error, just
				// in case there are any circumstances where a line
				// starting with \t means something else in the future.
				// This way the problem will be easier to catch.
				be := errs[len(errs)-1]
				be.Error += fmt.Sprintf(" (%s)", strings.TrimSpace(eline))
			default:
				parts := strings.SplitN(eline, ":", 3)
				if len(parts) == 3 {
					// file level error => filename:line:error
					filename := filepath.Clean(filepath.Join(b.Dir, parts[0]))
					line, err := strconv.Atoi(parts[1])
					if err != nil {
						// Not a line number, show error message
						be = &BuildError{
							Error: strings.TrimSpace(eline),
						}
						break
					}
					be = &BuildError{
						Filename: filename,
						Line:     line,
						Error:    strings.TrimSpace(parts[2]),
					}
				} else {
					// Unknown error, just show the error message
					be = &BuildError{
						Error: strings.TrimSpace(eline),
					}
				}
			}
			if be != nil {
				be.Package = pkg
				errs = append(errs, be)
			}
		}
	}
	if c := len(errs); c > 0 {
		return errs, fmt.Errorf("%d errors", c)
	}
	return nil, nil
}