Beispiel #1
0
// The heart of Fubsy: do a depth-first walk of the dependency graph
// to discover nodes in topological order, then (re)build nodes that
// are stale or missing. Skip target nodes that are "tainted" by
// upstream failure. Returns a single error object summarizing what
// (if anything) went wrong; error details are reported "live" as
// builds fail (e.g. to the console or a GUI window) so the user gets
// timely feedback.
func (self *BuildState) BuildTargets(targets *dag.NodeSet) error {
	// What sort of nodes do we check for changes?
	self.setChangeStates()
	log.Debug(log.BUILD, "building %d targets", targets.Length())

	builderr := new(BuildError)
	visit := func(node dag.Node) error {
		if node.State() == dag.SOURCE {
			// can't build original source nodes!
			return nil
		}
		if node.BuildRule() == nil {
			panic("node is a target, but has no build rule: " + node.Name())
		}

		checkInitialState(node)

		// do we need to build this node? can we?
		build, tainted, err := self.considerNode(node)
		log.Debug(log.BUILD, "node %s: build=%v, tainted=%v err=%v\n",
			node, build, tainted, err)
		if err != nil {
			return err
		}

		if tainted {
			node.SetState(dag.TAINTED)
		} else if build {
			ok := self.buildNode(node, builderr)
			if !ok && !self.keepGoing() {
				// attempts counter is not very useful when we break
				// out of the build early
				builderr.attempts = -1
				return builderr
			}
			if ok {
				err = self.recordNode(node)
				if err != nil {
					return err
				}
			}
		}
		return nil
	}
	err := self.graph.DFS(targets, visit)
	if err == nil && len(builderr.failed) > 0 {
		// build failures in keep-going mode
		err = builderr
	}
	return err
}
Beispiel #2
0
func main() {
	if filepath.Base(os.Args[0]) == "fubsydebug" {
		debugmain()
		return
	}

	args := parseArgs()
	script, err := findScript(args.scriptFile)
	if err != nil {
		fmt.Fprintln(os.Stderr, "fubsy: error: "+err.Error())
		os.Exit(2)
	}
	log.SetVerbosity(args.verbosity)
	err = log.EnableDebugTopics(args.debugTopics)
	if err != nil {
		fmt.Fprintln(os.Stderr, "fubsy: error: "+err.Error())
		os.Exit(2)
	}

	ast, errors := dsl.Parse(script)
	if ast == nil && len(errors) == 0 {
		panic("ast == nil && len(errors) == 0")
	}
	checkErrors("parse error:", errors)
	log.Debug(log.AST, "ast:\n")
	log.DebugDump(log.AST, ast)

	rt := runtime.NewRuntime(args.options, script, ast)
	errors = rt.RunScript()
	checkErrors("error:", errors)
}
Beispiel #3
0
// Build user's requested targets according to the dependency graph in
// self.dag (as constructed by runMainPhase()).
func (self *Runtime) runBuildPhase() []error {
	var errs []error

	errs = self.dag.ExpandNodes(self.stack)
	if len(errs) > 0 {
		return errs
	}
	self.dag.MarkSources()

	log.Debug(log.DAG, "dependency graph:")
	log.DebugDump(log.DAG, self.dag)

	goal, errs := self.dag.MatchTargets(self.options.Targets)
	if len(errs) > 0 {
		return errs
	}

	bdb, err := openBuildDB()
	if err != nil {
		errs = append(errs, err)
		return errs
	}
	defer bdb.Close()

	bstate := build.NewBuildState(self.dag, bdb, self.options)
	err = bstate.BuildTargets(goal)
	if err != nil {
		errs = append(errs, err)
	}
	return errs
}
Beispiel #4
0
func (self *BuildRule) Execute() ([]dag.Node, []error) {
	stack := self.runtime.stack
	locals := types.NewValueMap()
	stack.Push(locals)
	defer stack.Pop()

	self.setLocals(locals)
	log.Debug(log.BUILD, "value stack:")
	log.DebugDump(log.BUILD, stack)
	err := self.action.Execute(self.runtime)
	return self.targets.Nodes(), err
}
Beispiel #5
0
func (self KyotoDB) WriteNode(nodename string, record *BuildRecord) error {
	log.Debug(log.DB, "writing record for node %s", nodename)
	key := makekey(PREFIX_NODE, nodename)
	val, err := record.encode()
	if err != nil {
		return err
	}
	err = self.kcdb.Set(key, val)
	if err != nil {
		return err
	}
	return nil
}
Beispiel #6
0
//export callBuiltin
func callBuiltin(
	pfunc unsafe.Pointer, numargs C.int, cargs unsafe.Pointer) (
	*C.char, *C.char) {

	log.Debug(log.PLUGINS, "callBuiltin: calling Go function at %p", pfunc)
	var fn types.FuCode

	fuargs := make([]types.FuObject, numargs)
	for i := uintptr(0); i < uintptr(numargs); i++ {
		// cargs is really a C char **, i.e. a pointer to an array of
		// char *. argp is a pointer to the i'th member of cargs. This
		// is just C-style array lookup with pointer arithmetic, but
		// in Go syntax.
		argp := unsafe.Pointer(uintptr(cargs) + i*unsafe.Sizeof(cargs))
		arg := C.GoString(*(**C.char)(argp))
		fuargs[i] = types.MakeFuString(arg)
	}
	args := types.MakeBasicArgs(nil, fuargs, nil)

	fn = *(*types.FuCode)(unsafe.Pointer(&pfunc))
	log.Debug(log.PLUGINS, "followed unsafe.Pointer to get %p", fn)
	result, err := fn(args)

	if len(err) > 0 {
		errmsgs := make([]string, len(err))
		for i, err := range err {
			errmsgs[i] = err.Error()
		}
		return nil, C.CString(strings.Join(errmsgs, "\n"))
	}
	var cresult *C.char
	if result != nil {
		cresult = C.CString(result.String())
	}
	return cresult, nil
}
Beispiel #7
0
func (self *BuildState) recordNode(node dag.Node) error {
	log.Debug(log.BUILD, "recording successful build of %s %s", node.Typename(), node)
	sig, err := node.Signature()
	log.Debug(log.BUILD, "sig=%v, err=%v", sig, err)
	if err != nil {
		return fmt.Errorf("could not compute signature of target %s: %s",
			node, err)
	}

	record := db.NewBuildRecord()
	record.SetTargetSignature(sig)
	for _, parent := range self.graph.ParentNodes(node) {
		sig, err = parent.Signature()
		if err != nil {
			return err
		}
		record.AddParent(parent.Name(), sig)
	}
	err = self.db.WriteNode(node.Name(), record)
	if err != nil {
		return err
	}
	return nil
}
Beispiel #8
0
func (self KyotoDB) LookupNode(nodename string) (*BuildRecord, error) {
	log.Debug(log.DB, "loading record for node %s", nodename)
	key := makekey(PREFIX_NODE, nodename)
	val, err := self.kcdb.Get(key)

	if val == nil && (err == nil || kyotoNoRecord(err)) {
		return nil, nil
	}
	if err != nil {
		return nil, err
	}
	result := &BuildRecord{}
	err = result.decode(val)
	if err != nil {
		return nil, err
	}
	//log.DebugDump(log.DB, result)
	return result, nil
}
Beispiel #9
0
func (self *Runtime) RunScript() []error {
	var errors []error
	for _, plugin := range self.ast.FindImports() {
		log.Debug(log.PLUGINS, "loading plugin '%s'", strings.Join(plugin, "."))
	}

	errors = self.runInlinePlugins()
	if len(errors) > 0 {
		return errors
	}

	errors = self.runMainPhase()
	if len(errors) > 0 {
		return errors
	}

	errors = self.runBuildPhase()
	return errors
}
Beispiel #10
0
func (self *Runtime) runInlinePlugins() []error {
	var errs []error
	var err error
	var meta plugins.MetaPlugin

	inlines := self.ast.FindInlinePlugins()
	ns := self.stack.Inner()
	for _, inline := range inlines {
		meta, err = plugins.LoadMetaPlugin(inline.Language(), self.builtins)
		if err != nil {
			errs = append(errs, MakeLocationError(inline, err))
			continue
		}
		log.Debug(log.PLUGINS, "running %s inline plugin", inline.Language())
		values, err := meta.Run(inline.Content())
		if err != nil {
			errs = append(errs, MakeLocationError(inline, err))
		}
		for name, val := range values {
			ns.Assign(name, val)
		}
	}
	return errs
}
Beispiel #11
0
// Inspect node and its parents to see if we need to build it. Return
// build=true if we should build it, tainted=true if we should skip
// building this node due to upstream failure. Return non-nil err if
// there were unexpected node errors (error checking existence or
// change status).
func (self *BuildState) considerNode(node dag.Node) (
	build bool, tainted bool, err error) {

	var exists, changed bool
	exists, err = node.Exists() // obvious rebuild (unless tainted)
	if err != nil {
		return
	}
	missing := !exists

	var record *db.BuildRecord
	if !missing {
		// skip DB lookup for missing nodes: the only thing that will
		// stop us from rebuilding them is a failed parent, and that
		// check comes later
		record, err = self.db.LookupNode(node.Name())
		if err != nil {
			return
		}
		if record != nil {
			log.Debug(log.BUILD, "old parents of %s:", node)
			log.DebugDump(log.BUILD, record)
		}
	}

	build = missing
	parents := self.graph.ParentNodes(node)

	// Check if any of node's former parents have been removed.
	if !build && record != nil {
		build = parentsRemoved(parents, record)
	}

	for _, parent := range parents {
		pstate := parent.State()
		if pstate == dag.FAILED || pstate == dag.TAINTED {
			build = false
			tainted = true
			return // no further inspection required
		}

		var oldsig []byte
		if record != nil {
			oldsig = record.SourceSignature(parent.Name())
		}
		if oldsig == nil {
			// New parent for this node: rebuild unless another
			// parent is failed/tainted.
			build = true
		}

		if build {
			continue
		}
		changed, err = self.parentChanged(parent, pstate, oldsig)
		if err != nil {
			return
		}
		if changed {
			// Do NOT return here: we need to continue inspecting
			// parents to make sure they don't taint this node with
			// upstream failure.
			build = true
		}
	}
	return
}