Example #1
0
func Test_FileNode_Expand(t *testing.T) {
	ns := types.NewValueMap()
	node := NewFileNode("foobar")
	xnode, err := node.ActionExpand(ns, nil)
	assert.Nil(t, err)
	assert.Equal(t, node, xnode)

	err = node.NodeExpand(ns)
	assert.Nil(t, err)
	assert.Equal(t, "foobar", node.Name())

	// test that ActionExpand() follows variable references
	node = NewFileNode("$foo$bar")
	xnode, err = node.ActionExpand(ns, nil)
	assert.Equal(t, "undefined variable 'foo' in string", err.Error())

	// make it so "$foo$bar" expands to "$foo", and ensure that
	// expansion stops there
	// XXX argh: currently this expands to "'$'foo": hmmmmm
	// ns.Assign("foo", types.MakeFuString("$"))
	// ns.Assign("bar", types.MakeFuString("foo"))
	// xnode, err = node.ActionExpand(ns, nil)
	// assert.Nil(t, err)
	// assert.Equal(t, "$foo", xnode.String())
}
Example #2
0
func (self PythonPlugin) Run(content string) (types.ValueMap, error) {
	var ccontent = C.CString(content)
	var cerror *C.char
	var cvalues *C.valuelist_t

	defer C.free(unsafe.Pointer(ccontent))
	if C.run_python(ccontent, &cerror, &cvalues) < 0 {
		return nil, errors.New(C.GoString(cerror))
	}
	defer C.free_valuelist(cvalues)

	valuemap := types.NewValueMap()
	names := uintptr(unsafe.Pointer(cvalues.names))
	values := uintptr(unsafe.Pointer(cvalues.values))
	var cname *C.char
	var name string
	var cvalue *C.PyObject

	for i := uintptr(0); i < uintptr(cvalues.numitems); i++ {
		offset := i * unsafe.Sizeof(i)
		cname = *(**C.char)(unsafe.Pointer(names + offset))
		cvalue = *(**C.PyObject)(unsafe.Pointer(values + offset))
		name = C.GoString(cname)
		valuemap.Assign(name, PythonCallable{name: name, callable: cvalue})
	}
	return valuemap, nil
}
Example #3
0
func Test_FinderNode_Expand_vars(t *testing.T) {
	// imagine code like this:
	//   srcdir = "src/stuff"
	//   files = <$srcdir/*.c>
	//   "myapp": "$srcdir/main.c" {
	//       "cc -c $files"
	//   }
	// ...i.e. a FinderNode that is not in the DAG, so variable
	// references do not get expanded by DAG.ExpandNodes(). This is
	// clearly a bogus build script, but that's beside the point. We
	// need to ensure that the wildcard expanded is not "$srcdir/*.c"
	// but "src/stuff/*.c".

	cleanup := testutils.Chtemp()
	defer cleanup()

	testutils.TouchFiles(
		"lib1/foo.c", "lib1/sub/blah.c", "include/bop.h", "include/bip.h")

	ns := types.NewValueMap()
	ns.Assign("libsrc", types.MakeFuString("lib1"))
	finder := NewFinderNode("$libsrc/**/*.c")
	expect := []string{
		"lib1/foo.c",
		"lib1/sub/blah.c",
	}
	assertExpand(t, ns, expect, finder)
}
Example #4
0
// return a barebones Runtime with almost nothing in it -- variable
// assignment and lookup works, but not much else
func minimalRuntime() *Runtime {
	stack := types.NewValueStack()
	locals := types.NewValueMap()
	stack.Push(locals)
	return &Runtime{
		stack: &stack,
		dag:   dag.NewDAG(),
	}
}
Example #5
0
func NewBuildRule(runtime *Runtime, targets, sources []dag.Node) *BuildRule {
	rule := &BuildRule{
		runtime: runtime,
		targets: dag.ListNodeFromNodes(targets),
		sources: dag.ListNodeFromNodes(sources),
	}
	rule.attrs = types.NewValueMap()
	rule.attrs["targets"] = rule.targets
	rule.attrs["sources"] = rule.sources
	return rule
}
Example #6
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
}
Example #7
0
func assertExpand(
	t *testing.T, ns types.Namespace, expect []string, obj types.FuObject) {
	if ns == nil {
		ns = types.NewValueMap()
	}
	actualobj, err := obj.ActionExpand(ns, nil)
	assert.Nil(t, err)

	// convert FuList of FileNode to slice of string
	actualstr := make([]string, len(actualobj.List()))
	for i, obj := range actualobj.List() {
		actualstr[i] = obj.ValueString()
	}
	assert.Equal(t, expect, actualstr)
}
Example #8
0
func Test_FinderNode_expand_cycle(t *testing.T) {
	ns := types.NewValueMap()
	ns.Assign("a", types.MakeFuString("$b"))
	ns.Assign("b", types.MakeFuString("$c$d"))
	ns.Assign("c", types.MakeFuString("$a"))

	var err error
	finder := NewFinderNode("src/$a/*.h")

	_, err = finder.ActionExpand(ns, nil)
	assert.Equal(t, "cyclic variable reference: a -> b -> c -> a", err.Error())

	err = finder.NodeExpand(ns)
	assert.Equal(t, "cyclic variable reference: a -> b -> c -> a", err.Error())
}
Example #9
0
func Test_ListNode_expand_cycle(t *testing.T) {
	ns := types.NewValueMap()
	ns.Assign("a", types.MakeFuString("$b"))
	ns.Assign("b", types.MakeFuString("$a"))

	var err error
	inner := NewStubNode("foo/$a")
	list := newListNode(inner)

	// XXX ooops, ActionExpand() does not expand variable refs!
	// _, err = list.ActionExpand(ns, nil)
	// assert.Equal(t, "cyclic variable reference: a -> b -> a", err.Error())

	err = list.NodeExpand(ns)
	assert.Equal(t, "cyclic variable reference: a -> b -> a", err.Error())
}
Example #10
0
func Test_ListNode_ActionExpand(t *testing.T) {
	ns := types.NewValueMap()
	assertExpand := func(expect []Node, list *ListNode) {
		actualobj, err := list.ActionExpand(ns, nil)
		assert.Nil(t, err)
		actual := make([]Node, len(actualobj.List()))
		for i, obj := range actualobj.List() {
			actual[i] = obj.(Node)
		}
		if len(expect) == len(actual) {
			for i, enode := range expect {
				anode := actual[i]
				if !enode.Equal(anode) {
					t.Errorf("ListNode[%d]: expected <%T: %s> but got <%T: %s>",
						i, enode, enode, anode, anode)
				}
			}
		} else {
			t.Errorf(
				"ListNode %v: expected ActionExpand() to return %d Nodes, "+
					"but got %d: %v",
				list, len(expect), len(actual), actual)
		}
	}

	// a single empty ListNode yields an empty slice of Nodes
	list := newListNode()
	assertExpand([]Node{}, list)

	// a ListNode containing boring ordinary non-expanding nodes just
	// returns them
	node0 := NewStubNode("0")
	node1 := NewStubNode("1")
	list = newListNode(node0, node1)
	assertExpand([]Node{node0, node1}, list)

	// a ListNode with expanding nodes expands them (and flattens the
	// resulting list)
	list = newListNode(node1, list, node0)
	assertExpand([]Node{node1, node0, node1, node0}, list)

	ns.Assign("a", types.MakeFuString("argghh"))
	list = newListNode(node1, NewStubNode("say $a"), node0)
	assertExpand([]Node{node1, NewStubNode("say argghh"), node0}, list)
}
Example #11
0
func Test_DAG_ExpandNodes(t *testing.T) {
	ns := types.NewValueMap()
	ns.Assign("sdir", types.MakeFuString("src/tool1"))
	ns.Assign("ext", types.MakeFuString("cpp"))

	tdag := NewTestDAG()
	tdag.Add("bin/tool1", "$sdir/main.$ext", "$sdir/util.$ext", "$sdir/foo.h")
	tdag.Add("$sdir/foo.h", "$sdir/foo.h.in")
	tdag.Add("$sdir/main.$ext")
	tdag.Add("$sdir/util.$ext", "$sdir/foo.h")
	tdag.Add("$sdir/foo.h.in")
	dag := tdag.Finish()

	expect := []string{
		"bin/tool1",
		"src/tool1/foo.h",
		"src/tool1/main.cpp",
		"src/tool1/util.cpp",
		"src/tool1/foo.h.in",
	}
	errs := dag.ExpandNodes(ns)
	assert.Equal(t, 0, len(errs))
	for i, node := range dag.nodes {
		assert.Equal(t, expect[i], node.Name())
	}
	assert.Equal(t, len(expect), len(dag.nodes))

	tdag = NewTestDAG()
	tdag.Add("foo/$bogus/blah", "bam")
	tdag.Add("bam", "$flop/bop")
	tdag.Add("$flop/bop")
	dag = tdag.Finish()
	errs = dag.ExpandNodes(ns)
	if len(errs) == 2 {
		assert.Equal(t, "undefined variable 'bogus' in string", errs[0].Error())
		assert.Equal(t, "undefined variable 'flop' in string", errs[1].Error())
	} else {
		t.Errorf("expected %d errors, but got %d:\n%v", 2, len(errs), errs)
	}
}
Example #12
0
func NewRuntime(
	options build.BuildOptions, script string, ast *dsl.ASTRoot) *Runtime {
	stack := types.NewValueStack()

	builtins := defineBuiltins()
	stack.Push(builtins)

	// Local variables are per-script, but we only support a single
	// script right now. So might as well initialize the script-local
	// namespace right here.
	locals := types.NewValueMap()
	stack.Push(locals)

	return &Runtime{
		options:  options,
		script:   script,
		ast:      ast,
		builtins: builtins,
		stack:    &stack,
		dag:      dag.NewDAG(),
	}
}
Example #13
0
func Test_BuildRule_setLocals(t *testing.T) {
	targets := []dag.Node{dag.NewStubNode("foo")}
	sources := []dag.Node{dag.NewStubNode("bar"), dag.NewStubNode("qux")}
	ns := types.NewValueMap()
	rule := NewBuildRule(nil, targets, sources)

	rule.setLocals(ns)
	var val types.FuObject
	var ok bool

	val, ok = ns.Lookup("whatever")
	assert.False(t, ok)
	val, ok = ns.Lookup("target")
	assert.False(t, ok)
	val, ok = ns.Lookup("targets")
	assert.False(t, ok)

	val, ok = ns.Lookup("TARGET")
	assert.True(t, ok)
	assert.Equal(t, "foo", val.ValueString())
	assert.Equal(t, "foo", val.(*dag.StubNode).Name())

	val, ok = ns.Lookup("SOURCE")
	assert.True(t, ok)
	assert.Equal(t, "bar", val.ValueString())
	assert.Equal(t, "bar", val.(*dag.StubNode).Name())

	val, ok = ns.Lookup("TARGETS")
	assert.True(t, ok)
	assert.Equal(t, 1, len(val.List()))
	assert.Equal(t, `["foo"]`, val.String())

	val, ok = ns.Lookup("SOURCES")
	assert.True(t, ok)
	assert.Equal(t, 2, len(val.List()))
	assert.Equal(t, `["bar", "qux"]`, val.String())
}
Example #14
0
func Test_evaluateCall_method(t *testing.T) {
	// construct AST for "a.b.c(x)"
	astargs := []dsl.ASTExpression{dsl.NewASTName("x")}
	astcall := dsl.NewASTFunctionCall(
		dsl.NewASTSelection(
			dsl.NewASTSelection(dsl.NewASTName("a"), "b"), "c"),
		astargs)

	// make sure a is an object with attributes, and b is one of them
	// (N.B. having FileNodes be attributes of one another is weird
	// and would never happen in a real Fubsy script, but it's a
	// convenient way to setup this method call)
	aobj := dag.NewFileNode("a.txt")
	bobj := dag.NewFileNode("b.txt")
	aobj.ValueMap = types.NewValueMap()
	aobj.Assign("b", bobj)

	// make sure a.b.c is a method
	calls := make([]string, 0) // list of function names
	var meth_c types.FuCode
	meth_c = func(argsource types.ArgSource) (types.FuObject, []error) {
		args := argsource.Args()
		if len(args) != 1 {
			panic("c() called with wrong number of args")
		}
		calls = append(calls, "c")
		robj := argsource.Receiver()
		return nil, []error{
			fmt.Errorf("c failed: receiver: %s %v, arg: %s %v",
				robj.Typename(), robj, args[0].Typename(), args[0])}
	}
	bobj.ValueMap = types.NewValueMap()
	bobj.Assign("c", types.NewFixedFunction("c", 1, meth_c))

	rt := minimalRuntime()
	ns := rt.Namespace()
	ns.Assign("a", aobj)
	ns.Assign("x", types.MakeFuString("hello"))

	// what the hell, let's test the precall feature too
	var precalledCallable types.FuCallable
	var precalledArgs types.ArgSource
	precall := func(callable types.FuCallable, argsource types.ArgSource) {
		precalledCallable = callable
		precalledArgs = argsource
	}

	callable, args, errs := rt.prepareCall(astcall)
	assert.Equal(t, "c", callable.(*types.FuFunction).Name())
	assert.True(t, args.Receiver() == bobj)
	assert.Equal(t, 0, len(errs))

	result, errs := rt.evaluateCall(callable, args, precall)
	assert.Equal(t, precalledCallable, callable)
	assert.Equal(t, precalledArgs.Args(), types.MakeStringList("hello").List())
	assert.Nil(t, result)
	if len(errs) == 1 {
		assert.Equal(t,
			"c failed: receiver: FileNode \"b.txt\", arg: string \"hello\"",
			errs[0].Error())
	} else {
		t.Errorf("expected exactly 1 error, but got: %v", errs)
	}
}