Commits

Greg Ward committed 32635d1

runtime: add build() builtin for defining build rules

Of course you can already define build rules with the special syntax
for them. This is really intended for plugins, which can't be written
in Fubsy syntax; they'll need programmatic access to define build
rules. Supporting it in the DSL is optional, but seems like a nice
touch for completeness.

  • Participants
  • Parent commits 72cacf3

Comments (0)

Files changed (5)

File src/fubsy/dag/listnode.go

 	return node
 }
 
+func ListNodeFromNodes(nodes []Node) *ListNode {
+	names := make([]string, len(nodes))
+	for i, node := range nodes {
+		names[i] = node.Name()
+	}
+	name := strings.Join(names, ",")
+	lnode := &ListNode{
+		nodebase:     makenodebase(name),
+		types.FuList: make(types.FuList, len(nodes)),
+	}
+	for i, node := range nodes {
+		lnode.FuList[i] = node
+	}
+	return lnode
+}
+
 func MakeListNode(dag *DAG, member ...types.FuObject) *ListNode {
 	node := newListNode(member...)
 	node = dag.AddNode(node).(*ListNode)

File src/fubsy/runtime/action.go

 
 func (self *SequenceAction) AddCommand(command *dsl.ASTString) {
 	raw := types.FuString(command.Value())
-	self.AddAction(&CommandAction{raw: raw})
+	self.AddAction(NewCommandAction(raw))
 }
 
 func (self *SequenceAction) AddAssignment(assignment *dsl.ASTAssignment) {
 	self.AddAction(&FunctionCallAction{fcall: fcall})
 }
 
+func NewCommandAction(cmd types.FuObject) *CommandAction {
+	return &CommandAction{raw: cmd}
+}
+
 func (self *CommandAction) String() string {
 	return self.raw.String()
 }

File src/fubsy/runtime/buildrule.go

 package runtime
 
 import (
+	"errors"
+	"fmt"
+
 	"fubsy/dag"
 	"fubsy/log"
 	"fubsy/types"
 
 type BuildRule struct {
 	runtime *Runtime
-	targets []dag.Node
-	sources []dag.Node
+	targets *dag.ListNode
+	sources *dag.ListNode
 	action  Action
 	locals  types.ValueMap
+	attrs   types.ValueMap
 }
 
 func NewBuildRule(runtime *Runtime, targets, sources []dag.Node) *BuildRule {
-	return &BuildRule{
+	rule := &BuildRule{
 		runtime: runtime,
-		targets: targets,
-		sources: sources,
+		targets: dag.ListNodeFromNodes(targets),
+		sources: dag.ListNodeFromNodes(sources),
 	}
+	rule.attrs = types.NewValueMap()
+	rule.attrs["targets"] = rule.targets
+	rule.attrs["sources"] = rule.sources
+	return rule
 }
 
 func (self *BuildRule) Execute() ([]dag.Node, []error) {
 	log.Debug(log.BUILD, "value stack:")
 	log.DebugDump(log.BUILD, stack)
 	err := self.action.Execute(self.runtime)
-	return self.targets, err
+	return self.targets.Nodes(), err
 }
 
 func (self *BuildRule) ActionString() string {
 }
 
 func (self *BuildRule) setLocals(ns types.Namespace) {
-	// Convert each slice-of-nodes to a FuList
-	targets := make(types.FuList, len(self.targets))
-	for i, tnode := range self.targets {
-		targets[i] = tnode
-	}
-	sources := make(types.FuList, len(self.sources))
-	for i, snode := range self.sources {
-		sources[i] = snode
-	}
-
-	ns.Assign("TARGETS", targets)
-	ns.Assign("SOURCES", sources)
+	ns.Assign("TARGETS", self.targets)
+	ns.Assign("SOURCES", self.sources)
 
 	// these are really only meaningful for rules with one target or
 	// one source... but such rules are pretty common, so these are
 	// frequently handy
-	ns.Assign("TARGET", targets[0])
-	ns.Assign("SOURCE", sources[0])
+	ns.Assign("TARGET", self.targets.Nodes()[0])
+	ns.Assign("SOURCE", self.sources.Nodes()[0])
 }
+
+// Implement FuObject so we can expose BuildRules to the DSL
+func (self *BuildRule) String() string {
+	return fmt.Sprintf("%v: %v {%v}", self.targets, self.sources, self.action)
+}
+
+func (self *BuildRule) ValueString() string {
+	return self.String()
+}
+
+func (self *BuildRule) CommandString() string {
+	return self.String()
+}
+
+func (self *BuildRule) Equal(other_ types.FuObject) bool {
+	other, ok := other_.(*BuildRule)
+	return ok && self == other
+}
+
+func (self *BuildRule) Add(other types.FuObject) (types.FuObject, error) {
+	return nil, errors.New("BuildRule objects cannot be added")
+}
+
+func (self *BuildRule) Lookup(name string) (types.FuObject, bool) {
+	value, ok := self.attrs[name]
+	return value, ok
+}
+
+func (self *BuildRule) List() []types.FuObject {
+	return []types.FuObject{self}
+}
+
+func (self *BuildRule) ActionExpand(
+	ns types.Namespace, ctx *types.ExpandContext) (
+	types.FuObject, error) {
+	return nil, errors.New("BuildRule objects cannot be expanded")
+}
+
+func (self *BuildRule) Typename() string {
+	return "BuildRule"
+}

File src/fubsy/runtime/builtins.go

 package runtime
 
 import (
+	"errors"
+	"fmt"
 	"os"
 
 	"fubsy/dag"
 		types.NewVariadicFunction("mkdir", 0, -1, fn_mkdir),
 		types.NewVariadicFunction("remove", 0, -1, fn_remove),
 
+		types.NewFixedFunction("build", 3, fn_build),
+
 		// node factories
 		types.NewFixedFunction("FileNode", 1, fn_FileNode),
 		types.NewFixedFunction("ActionNode", 1, fn_ActionNode),
 		if i > 0 {
 			os.Stdout.WriteString(" ")
 		}
-		_, err := os.Stdout.WriteString(val.ValueString())
+		var s string
+		if val == nil {
+			s = "(nil)"
+		} else {
+			s = val.ValueString()
+		}
+		_, err := os.Stdout.WriteString(s)
 		if err != nil {
 			// this shouldn't happen, so bail immediately
 			return nil, []error{err}
 	return nil, errs
 }
 
+func fn_build(args types.ArgSource) (types.FuObject, []error) {
+	rt := args.(FunctionArgs).runtime
+	targets := rt.nodify(args.Arg(0))
+	sources := rt.nodify(args.Arg(1))
+	actionobj := args.Arg(2)
+
+	fmt.Printf(
+		"fn_build():\n"+
+			"  targets: %T %v\n"+
+			"  sources: %T %v\n"+
+			"  actions: %T %v\n",
+		targets, targets, sources, sources, actionobj, actionobj)
+
+	var errs []error
+	var action Action
+	if actionstr, ok := actionobj.(types.FuString); ok {
+		action = NewCommandAction(actionstr)
+	} else {
+		errs = append(errs, errors.New("build(): only command strings supported as actions right now, sorry"))
+	}
+
+	rule := NewBuildRule(rt, targets, sources)
+	rule.action = action
+
+	return rule, errs
+}
+
 func fn_FileNode(args types.ArgSource) (types.FuObject, []error) {
 	name := args.Arg(0).ValueString()
 	graph := args.(FunctionArgs).Graph()

File src/fubsy/runtime/runtime.go

 }
 
 func (self *Runtime) addRule(rule *BuildRule) {
+	targets := rule.targets.Nodes()
+	sources := rule.sources.Nodes()
 
 	// Attach the rule to each target node.
-	for _, tnode := range rule.targets {
+	for _, tnode := range targets {
 		tnode.SetBuildRule(rule)
 	}
 
 	// And connect the nodes to each other (every source is a parent
 	// of every target).
-	self.dag.AddManyParents(rule.targets, rule.sources)
+	self.dag.AddManyParents(targets, sources)
 }
 
 // Convert a single FuObject (possibly a FuList) to a list of Nodes and