Initial Blueprint commit.
Blueprint is a build system component that reads Blueprints files defining
modules to be built, and generates a Ninja build manifest that can be used to
perform all the build actions. It does not dictate or implement much build
policy itself, but rather provides a framework to ease the process of defining
build logic in Go.
The "blueprint" and "blueprint/parser" Go packages contain the functionality
for reading Blueprint files and invoking build logic functions defined in other
Go packages.
The "blueprint/bootstrap" Go package contains just enough build logic to build
a binary that includes Blueprint and any pure-Go (i.e. no cgo) build logic
defined in external Go packages. This can be used to create a minimal Ninja
file that's capable of bootstrapping a Blueprint-based build system from
source.
The "blueprint/bootstrap/minibp" Go package contains code for a minimal binary
that includes the build logic defined in the "blueprint/bootstrap" package.
This binary can then create the Ninja file for the bootstrapping process.
Change-Id: I8d8390042372a72d225785cda738525001b009f1
diff --git a/blueprint/ninja_defs.go b/blueprint/ninja_defs.go
new file mode 100644
index 0000000..3f3329d
--- /dev/null
+++ b/blueprint/ninja_defs.go
@@ -0,0 +1,299 @@
+package blueprint
+
+import (
+ "fmt"
+ "strconv"
+ "strings"
+)
+
+type Deps int
+
+const (
+ DepsNone Deps = iota
+ DepsGCC
+ DepsMSVC
+)
+
+func (d Deps) String() string {
+ switch d {
+ case DepsNone:
+ return "none"
+ case DepsGCC:
+ return "gcc"
+ case DepsMSVC:
+ return "msvc"
+ default:
+ panic(fmt.Sprintf("unknown deps value: %d", d))
+ }
+}
+
+type PoolParams struct {
+ Comment string
+ Depth int
+}
+
+type RuleParams struct {
+ Comment string
+ Command string
+ Depfile string
+ Deps Deps
+ Description string
+ Generator bool
+ Pool Pool
+ Restat bool
+ Rspfile string
+ RspfileContent string
+}
+
+type BuildParams struct {
+ Rule Rule
+ Outputs []string
+ Inputs []string
+ Implicits []string
+ OrderOnly []string
+ Args map[string]string
+}
+
+// A poolDef describes a pool definition. It does not include the name of the
+// pool.
+type poolDef struct {
+ Comment string
+ Depth int
+}
+
+func parsePoolParams(scope variableLookup, params *PoolParams) (*poolDef,
+ error) {
+
+ def := &poolDef{
+ Comment: params.Comment,
+ Depth: params.Depth,
+ }
+
+ return def, nil
+}
+
+func (p *poolDef) WriteTo(nw *ninjaWriter, name string) error {
+ if p.Comment != "" {
+ err := nw.Comment(p.Comment)
+ if err != nil {
+ return err
+ }
+ }
+
+ err := nw.Pool(name)
+ if err != nil {
+ return err
+ }
+
+ return nw.ScopedAssign("depth", strconv.Itoa(p.Depth))
+}
+
+// A ruleDef describes a rule definition. It does not include the name of the
+// rule.
+type ruleDef struct {
+ Comment string
+ Pool Pool
+ Variables map[string]*ninjaString
+}
+
+func parseRuleParams(scope variableLookup, params *RuleParams) (*ruleDef,
+ error) {
+
+ r := &ruleDef{
+ Comment: params.Comment,
+ Pool: params.Pool,
+ Variables: make(map[string]*ninjaString),
+ }
+
+ if params.Command == "" {
+ return nil, fmt.Errorf("encountered rule params with no command " +
+ "specified")
+ }
+
+ value, err := parseNinjaString(scope, params.Command)
+ if err != nil {
+ return nil, fmt.Errorf("error parsing Command param: %s", err)
+ }
+ r.Variables["command"] = value
+
+ if params.Depfile != "" {
+ value, err = parseNinjaString(scope, params.Depfile)
+ if err != nil {
+ return nil, fmt.Errorf("error parsing Depfile param: %s", err)
+ }
+ r.Variables["depfile"] = value
+ }
+
+ if params.Deps != DepsNone {
+ r.Variables["deps"] = simpleNinjaString(params.Deps.String())
+ }
+
+ if params.Description != "" {
+ value, err = parseNinjaString(scope, params.Description)
+ if err != nil {
+ return nil, fmt.Errorf("error parsing Description param: %s", err)
+ }
+ r.Variables["description"] = value
+ }
+
+ if params.Generator {
+ r.Variables["generator"] = simpleNinjaString("true")
+ }
+
+ if params.Restat {
+ r.Variables["restat"] = simpleNinjaString("true")
+ }
+
+ if params.Rspfile != "" {
+ value, err = parseNinjaString(scope, params.Rspfile)
+ if err != nil {
+ return nil, fmt.Errorf("error parsing Rspfile param: %s", err)
+ }
+ r.Variables["rspfile"] = value
+ }
+
+ if params.RspfileContent != "" {
+ value, err = parseNinjaString(scope, params.RspfileContent)
+ if err != nil {
+ return nil, fmt.Errorf("error parsing RspfileContent param: %s",
+ err)
+ }
+ r.Variables["rspfile_content"] = value
+ }
+
+ return r, nil
+}
+
+func (r *ruleDef) WriteTo(nw *ninjaWriter, name string,
+ pkgNames map[*pkg]string) error {
+
+ if r.Comment != "" {
+ err := nw.Comment(r.Comment)
+ if err != nil {
+ return err
+ }
+ }
+
+ err := nw.Rule(name)
+ if err != nil {
+ return err
+ }
+
+ if r.Pool != nil {
+ err = nw.ScopedAssign("pool", r.Pool.fullName(pkgNames))
+ if err != nil {
+ return err
+ }
+ }
+
+ for name, value := range r.Variables {
+ err = nw.ScopedAssign(name, value.Value(pkgNames))
+ if err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+// A buildDef describes a build target definition.
+type buildDef struct {
+ Rule Rule
+ Outputs []*ninjaString
+ Inputs []*ninjaString
+ Implicits []*ninjaString
+ OrderOnly []*ninjaString
+ Args map[Variable]*ninjaString
+}
+
+func parseBuildParams(scope variableLookup, params *BuildParams) (*buildDef,
+ error) {
+
+ rule := params.Rule
+
+ b := &buildDef{
+ Rule: rule,
+ }
+
+ var err error
+ b.Outputs, err = parseNinjaStrings(scope, params.Outputs)
+ if err != nil {
+ return nil, fmt.Errorf("error parsing Outputs param: %s", err)
+ }
+
+ b.Inputs, err = parseNinjaStrings(scope, params.Inputs)
+ if err != nil {
+ return nil, fmt.Errorf("error parsing Inputs param: %s", err)
+ }
+
+ b.Implicits, err = parseNinjaStrings(scope, params.Implicits)
+ if err != nil {
+ return nil, fmt.Errorf("error parsing Implicits param: %s", err)
+ }
+
+ b.OrderOnly, err = parseNinjaStrings(scope, params.OrderOnly)
+ if err != nil {
+ return nil, fmt.Errorf("error parsing OrderOnly param: %s", err)
+ }
+
+ argNameScope := rule.scope()
+
+ if len(params.Args) > 0 {
+ b.Args = make(map[Variable]*ninjaString)
+ for name, value := range params.Args {
+ if !rule.isArg(name) {
+ return nil, fmt.Errorf("unknown argument %q", name)
+ }
+
+ argVar, err := argNameScope.LookupVariable(name)
+ if err != nil {
+ // This shouldn't happen.
+ return nil, fmt.Errorf("argument lookup error: %s", err)
+ }
+
+ ninjaValue, err := parseNinjaString(scope, value)
+ if err != nil {
+ return nil, fmt.Errorf("error parsing variable %q: %s", name,
+ err)
+ }
+
+ b.Args[argVar] = ninjaValue
+ }
+ }
+
+ return b, nil
+}
+
+func (b *buildDef) WriteTo(nw *ninjaWriter, pkgNames map[*pkg]string) error {
+ var (
+ rule = b.Rule.fullName(pkgNames)
+ outputs = valueList(b.Outputs, pkgNames, outputEscaper)
+ explicitDeps = valueList(b.Inputs, pkgNames, inputEscaper)
+ implicitDeps = valueList(b.Implicits, pkgNames, inputEscaper)
+ orderOnlyDeps = valueList(b.OrderOnly, pkgNames, inputEscaper)
+ )
+ err := nw.Build(rule, outputs, explicitDeps, implicitDeps, orderOnlyDeps)
+ if err != nil {
+ return err
+ }
+
+ for argVar, value := range b.Args {
+ name := argVar.fullName(pkgNames)
+ err = nw.ScopedAssign(name, value.Value(pkgNames))
+ if err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+func valueList(list []*ninjaString, pkgNames map[*pkg]string,
+ escaper *strings.Replacer) []string {
+
+ result := make([]string, len(list))
+ for i, ninjaStr := range list {
+ result[i] = ninjaStr.ValueWithEscaper(pkgNames, escaper)
+ }
+ return result
+}