Blueprint based package build system
This is a simple package build system for Fuchsia based on
Blueprint and Ninja that can be used to drive other build systems.
Change-Id: I4215899c2c3c4f2e3624ce4d3d29f375c482460c
diff --git a/README.md b/README.md
index 1212b30..1897392 100644
--- a/README.md
+++ b/README.md
@@ -5,6 +5,61 @@
responsible for invoking the build system of individual projects, handling
dependencies across projects and packaging build artifacts.
+## Usage
+
+After checking out the source, you need to run `toyen` executable to generate
+the `build.ninja` file.
+
+```bash
+$ toyen -src . -out out packages/root.bp
+```
+
+This is only needed after checking out the source for the first time. The
+Ninja files have rules re-generating themselves when necessary.
+
+Therefore, a typical iteration loop is:
+
+1. edit your source
+2. run `ninja -C out`
+
+## Ninja and Blueprint
+
+Toyen build system is implemented on top of Blueprint and Ninja, so the reader
+should first understand how they work. This document provides a brief surface
+exploration of the Blueprint and Ninja.
+
+### Ninja
+
+Ninja provides a simple syntax for defining:
+1. file dependencies (e.g. foo.o depends on foo.c), and
+2. arbitrary commands to run for generating files when they're out of date.
+
+To keep things fast, there are very few other features. There's no branching
+logic, globbing, or decision making of any kind, really. Ninja files can be
+written and read by humans, but that would be pointlessly tedious. Instead,
+a program is used to generate Ninja files.
+
+Check out the Ninja [documentation](http://martine.github.io/ninja/manual.html)
+and [source](https://github.com/martine/ninja) for more information.
+
+### Blueprint
+
+Unlike other build systems (even those that also generate Ninja files, like
+[gn](https://chromium.googlesource.com/chromium/src/tools/gn/)) Blueprint is not
+a stand-alone executable. You don't *run Blueprint*. Blueprint also doesn't
+come with any pre-defined build rules, except for the two that are necessary to
+build Blueprint itself.
+
+Blueprint is a set of Go packages that you use to implement whatever build rules
+your project finds useful. Through these packages, Blueprint provides high
+level constructs that are useful for generating Ninja files. Blueprint also
+provides a parser for reading Blueprint config files.
+
+To use Blueprint, you implement two things: your build modules, and a builder.
+Blueprint comes with basic versions of both of these things that are capable of
+building Blueprint itself. To learn more, read the extensive comments in the
+[Blueprint code](http://github.com/google/blueprint) for complete documentation.
+
## FAQ
### Why the name "Toyen"?
diff --git a/build.go b/build.go
new file mode 100644
index 0000000..05bc666
--- /dev/null
+++ b/build.go
@@ -0,0 +1,557 @@
+// Copyright 2016 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package main
+
+import (
+ "fmt"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "strings"
+
+ "github.com/google/blueprint"
+)
+
+var (
+ pctx = blueprint.NewPackageContext("fuchsia.googlesource.com/toyen")
+
+ cmakeCmd = pctx.StaticVariable("cmakeCmd", "cmake")
+ gnCmd = pctx.StaticVariable("gnCmd", "gn")
+ makeCmd = pctx.StaticVariable("makeCmd", "make")
+ ninjaCmd = pctx.StaticVariable("ninjaCmd", "ninja")
+
+ cmake = pctx.StaticRule("cmake",
+ blueprint.RuleParams{
+ Command: "cd $buildDir && $envVars $cmakeCmd -GNinja " +
+ "$cmakeOptions $cmakeDir",
+ Generator: true,
+ Description: "cmake $cmakeDir",
+ },
+ "envVars", "cmakeOptions", "cmakeDir", "buildDir")
+
+ gn = pctx.StaticRule("gn",
+ blueprint.RuleParams{
+ Command: "$envVars $gnCmd gen $buildDir " +
+ "--root=$gnDir --script-executable=/usr/bin/env --args='$gnArgs'",
+ Generator: true,
+ Description: "gn $gnDir",
+ },
+ "envVars", "gnDir", "gnArgs", "buildDir")
+
+ _make = pctx.StaticRule("make",
+ blueprint.RuleParams{
+ Command: "$envVars $makeCmd -j $Jobs " +
+ "-C $makeDir -f $makeFile $targets",
+ Description: "make $makeDir",
+ },
+ "envVars", "targets", "makeFile", "makeDir")
+
+ ninja = pctx.StaticRule("ninja",
+ blueprint.RuleParams{
+ Command: "$envVars $ninjaCmd -j $Jobs " +
+ "-C $ninjaDir -f $ninjaFile $targets",
+ Description: "ninja $ninjaDir",
+ },
+ "envVars", "targets", "ninjaFile", "ninjaDir")
+
+ script = pctx.StaticRule("script",
+ blueprint.RuleParams{
+ Command: "cd $workingDir && $envVars $scriptCmd $scriptArgs",
+ Description: "sh $in",
+ },
+ "envVars", "scriptCmd", "scriptArgs", "workingDir")
+
+ cp = pctx.StaticRule("cp",
+ blueprint.RuleParams{
+ Command: "cp -vR $in $out",
+ Description: "cp $out",
+ })
+
+ install = pctx.StaticRule("install",
+ blueprint.RuleParams{
+ Command: "install -c $in $out",
+ Description: "install $out",
+ })
+
+ mkdir = pctx.StaticRule("mkdir",
+ blueprint.RuleParams{
+ Command: "mkdir -p $out",
+ Description: "mkdir $out",
+ })
+
+ stamp = pctx.StaticRule("stamp",
+ blueprint.RuleParams{
+ Command: "touch $out",
+ Description: "stamp $out",
+ })
+)
+
+type BuilderModule interface {
+ TargetName() string
+}
+
+type builderModule struct {
+ targetName string
+}
+
+func (bm *builderModule) TargetName() string {
+ return bm.targetName
+}
+
+type Alias struct {
+ builderModule
+ properties struct{}
+ config Config
+}
+
+func newAliasModuleFactory(config Config) func() (blueprint.Module, []interface{}) {
+ return func() (blueprint.Module, []interface{}) {
+ module := &Alias{
+ config: config,
+ }
+ return module, []interface{}{&module.properties}
+ }
+}
+
+func (a *Alias) GenerateBuildActions(ctx blueprint.ModuleContext) {
+ a.targetName = ctx.ModuleName()
+
+ ctx.Build(pctx, blueprint.BuildParams{
+ Rule: blueprint.Phony,
+ Outputs: []string{ctx.ModuleName()},
+ Inputs: getDirectDependencies(ctx),
+ })
+}
+
+type CMake struct {
+ builderModule
+ properties struct {
+ Env []string
+ Options []string
+ Src string
+ BuildDir string
+ }
+ config Config
+}
+
+func newCMakeModuleFactory(config Config) func() (blueprint.Module, []interface{}) {
+ return func() (blueprint.Module, []interface{}) {
+ module := &CMake{
+ config: config,
+ }
+ return module, []interface{}{&module.properties}
+ }
+}
+
+func (c *CMake) GenerateBuildActions(ctx blueprint.ModuleContext) {
+ c.targetName = ctx.ModuleName()
+
+ options := make([]string, len(c.properties.Options))
+ for i := range c.properties.Options {
+ options[i] = fmt.Sprintf("-D%s", c.properties.Options[i])
+ }
+
+ // Add a rule for making the destination directory, in case it doesn't exist.
+ ctx.Build(pctx, blueprint.BuildParams{
+ Rule: mkdir,
+ Outputs: []string{c.properties.BuildDir},
+ Optional: true,
+ })
+
+ cmakeArgs := map[string]string{
+ "cmakeOptions": strings.Join(options, " "),
+ "cmakeDir": c.properties.Src,
+ "envVars": strings.Join(c.properties.Env, " "),
+ "buildDir": c.properties.BuildDir,
+ }
+
+ ninjaFile := filepath.Join(c.properties.BuildDir, "build.ninja")
+
+ // Add a rule to generate the Ninja file.
+ ctx.Build(pctx, blueprint.BuildParams{
+ Rule: cmake,
+ Outputs: []string{ninjaFile},
+ Implicits: getDirectDependencies(ctx),
+ OrderOnly: []string{c.properties.BuildDir},
+ Args: cmakeArgs,
+ })
+}
+
+type Copy struct {
+ builderModule
+ properties struct {
+ Sources []string
+ Destination string
+ }
+ config Config
+}
+
+func newCopyModuleFactory(config Config) func() (blueprint.Module, []interface{}) {
+ return func() (blueprint.Module, []interface{}) {
+ module := &Copy{
+ config: config,
+ }
+ return module, []interface{}{&module.properties}
+ }
+}
+
+func (c *Copy) GenerateBuildActions(ctx blueprint.ModuleContext) {
+ c.targetName = ctx.ModuleName()
+
+ // If multiple source files, destination is a directory, otherwise, it's a file.
+ var destinationDir string
+ destinationFiles := make([]string, len(c.properties.Sources))
+ if len(c.properties.Sources) > 1 {
+ destinationDir = c.properties.Destination
+ for i, src := range c.properties.Sources {
+ destinationFiles[i] = filepath.Join(destinationDir, filepath.Base(src))
+ }
+ } else {
+ destinationDir = filepath.Dir(c.properties.Destination)
+ destinationFiles[0] = c.properties.Destination
+ }
+
+ // Add a rule for making the destination directory, in case it doesn't exist.
+ ctx.Build(pctx, blueprint.BuildParams{
+ Rule: mkdir,
+ Outputs: []string{destinationDir},
+ Optional: true,
+ })
+
+ // Add a rule for each source/destination pair.
+ for i, src := range c.properties.Sources {
+ ctx.Build(pctx, blueprint.BuildParams{
+ Rule: cp,
+ Outputs: []string{destinationFiles[i]},
+ Inputs: []string{src},
+ Implicits: getDirectDependencies(ctx),
+ OrderOnly: []string{destinationDir},
+ Optional: true,
+ })
+ }
+
+ // Add a phony rule to copy all the files with one rule.
+ ctx.Build(pctx, blueprint.BuildParams{
+ Rule: blueprint.Phony,
+ Outputs: []string{ctx.ModuleName()},
+ Inputs: destinationFiles,
+ })
+}
+
+type Gn struct {
+ builderModule
+ properties struct {
+ Env []string
+ Args []string
+ SrcDir string
+ BuildDir string
+ }
+ config Config
+}
+
+func newGnModuleFactory(config Config) func() (blueprint.Module, []interface{}) {
+ return func() (blueprint.Module, []interface{}) {
+ module := &Gn{
+ config: config,
+ }
+ return module, []interface{}{&module.properties}
+ }
+}
+
+func (g *Gn) GenerateBuildActions(ctx blueprint.ModuleContext) {
+ g.targetName = ctx.ModuleName()
+
+ gnArgs := map[string]string{
+ "envVars": strings.Join(g.properties.Env, " "),
+ "gnDir": g.properties.SrcDir,
+ "gnArgs": strings.Join(g.properties.Args, " "),
+ "buildDir": g.properties.BuildDir,
+ }
+
+ ninjaFile := filepath.Join(g.properties.BuildDir, "build.ninja")
+
+ // Add a rule to generate the Ninja file.
+ ctx.Build(pctx, blueprint.BuildParams{
+ Rule: gn,
+ Outputs: []string{ninjaFile},
+ Implicits: getDirectDependencies(ctx),
+ Args: gnArgs,
+ })
+}
+
+type Install struct {
+ builderModule
+ properties struct {
+ Sources []string
+ Destination string
+ }
+ config Config
+}
+
+func newInstallModuleFactory(config Config) func() (blueprint.Module, []interface{}) {
+ return func() (blueprint.Module, []interface{}) {
+ module := &Install{
+ config: config,
+ }
+ return module, []interface{}{&module.properties}
+ }
+}
+
+func (i *Install) GenerateBuildActions(ctx blueprint.ModuleContext) {
+ i.targetName = ctx.ModuleName()
+
+ destinationFiles := make([]string, len(i.properties.Sources))
+ for j, src := range i.properties.Sources {
+ destinationFiles[j] = filepath.Join(i.properties.Destination, filepath.Base(src))
+ }
+
+ // Add a rule for making the destination directory, in case it doesn't exist.
+ ctx.Build(pctx, blueprint.BuildParams{
+ Rule: mkdir,
+ Outputs: []string{i.properties.Destination},
+ Optional: true,
+ })
+
+ // Add a rule to install sources.
+ ctx.Build(pctx, blueprint.BuildParams{
+ Rule: install,
+ Outputs: destinationFiles,
+ Inputs: i.properties.Sources,
+ Implicits: getDirectDependencies(ctx),
+ OrderOnly: []string{i.properties.Destination},
+ })
+}
+
+type Make struct {
+ builderModule
+ properties struct {
+ Env []string
+ Makefile string
+ Targets []string
+ Outputs []string
+ }
+ config Config
+}
+
+func newMakeModuleFactory(config Config) func() (blueprint.Module, []interface{}) {
+ return func() (blueprint.Module, []interface{}) {
+ module := &Make{
+ config: config,
+ }
+ return module, []interface{}{&module.properties}
+ }
+}
+
+func (m *Make) GenerateBuildActions(ctx blueprint.ModuleContext) {
+ m.targetName = ctx.ModuleName()
+
+ makeArgs := map[string]string{
+ "envVars": strings.Join(m.properties.Env, " "),
+ "makeFile": filepath.Base(m.properties.Makefile),
+ "makeDir": filepath.Dir(m.properties.Makefile),
+ }
+ if len(m.properties.Targets) > 0 {
+ makeArgs["targets"] = strings.Join(m.properties.Targets, " ")
+ }
+
+ var outputs []string
+ if len(m.properties.Outputs) > 0 {
+ outputs = m.properties.Outputs
+ } else {
+ outputs = []string{ctx.ModuleName()}
+ }
+
+ // Add a single rule where our touchFile depends on our make rule.
+ ctx.Build(pctx, blueprint.BuildParams{
+ Rule: _make,
+ Outputs: outputs,
+ Implicits: append(getDirectDependencies(ctx), m.properties.Makefile),
+ Args: makeArgs,
+ Optional: true,
+ })
+}
+
+type Ninja struct {
+ builderModule
+ properties struct {
+ Env []string
+ NinjaFile string
+ Targets []string
+ Outputs []string
+ }
+ config Config
+}
+
+func newNinjaModuleFactory(config Config) func() (blueprint.Module, []interface{}) {
+ return func() (blueprint.Module, []interface{}) {
+ module := &Ninja{
+ config: config,
+ }
+ return module, []interface{}{&module.properties}
+ }
+}
+
+func (n *Ninja) GenerateBuildActions(ctx blueprint.ModuleContext) {
+ n.targetName = ctx.ModuleName()
+
+ ninjaArgs := map[string]string{
+ "envVars": strings.Join(n.properties.Env, " "),
+ "ninjaFile": filepath.Base(n.properties.NinjaFile),
+ "ninjaDir": filepath.Dir(n.properties.NinjaFile),
+ }
+ if len(n.properties.Targets) != 0 {
+ ninjaArgs["targets"] = strings.Join(n.properties.Targets, " ")
+ }
+
+ var outputs []string
+ if len(n.properties.Outputs) > 0 {
+ outputs = n.properties.Outputs
+ } else {
+ outputs = []string{ctx.ModuleName()}
+ }
+
+ ctx.Build(pctx, blueprint.BuildParams{
+ Rule: ninja,
+ Outputs: outputs,
+ Implicits: append(getDirectDependencies(ctx), n.properties.NinjaFile),
+ Args: ninjaArgs,
+ Optional: true,
+ })
+}
+
+type Script struct {
+ builderModule
+ properties struct {
+ Script string
+ Outputs []string
+ Inputs []string
+ Args []string
+ WorkingDir string
+ Env []string
+ GenFiles []string
+ }
+ config Config
+}
+
+func newScriptModuleFactory(config Config) func() (blueprint.Module, []interface{}) {
+ return func() (blueprint.Module, []interface{}) {
+ module := &Script{
+ config: config,
+ }
+ return module, []interface{}{&module.properties}
+ }
+}
+
+func (s *Script) GenerateBuildActions(ctx blueprint.ModuleContext) {
+ s.targetName = ctx.ModuleName()
+
+ // Add a rule for making the destination directory, in case it doesn't exist.
+ ctx.Build(pctx, blueprint.BuildParams{
+ Rule: mkdir,
+ Outputs: []string{s.properties.WorkingDir},
+ Optional: true,
+ })
+
+ ruleArgs := map[string]string{
+ "envVars": strings.Join(s.properties.Env, " "),
+ "scriptCmd": s.properties.Script,
+ "scriptArgs": strings.Join(s.properties.Args, " "),
+ "workingDir": s.properties.WorkingDir,
+ }
+
+ implicits := append(
+ s.properties.Inputs,
+ append(getDirectDependencies(ctx), s.properties.Script)...,
+ )
+
+ outputs := append(s.properties.Outputs, s.properties.GenFiles...)
+
+ if len(outputs) != 0 {
+ // Add a rule to run the script to generate the outputs.
+ ctx.Build(pctx, blueprint.BuildParams{
+ Rule: script,
+ Outputs: outputs,
+ Inputs: []string{s.properties.Script},
+ Args: ruleArgs,
+ Implicits: implicits,
+ OrderOnly: []string{s.properties.WorkingDir},
+ Optional: true,
+ })
+
+ // The only default rule should be one with the module name.
+ ctx.Build(pctx, blueprint.BuildParams{
+ Rule: blueprint.Phony,
+ Outputs: []string{ctx.ModuleName()},
+ Inputs: outputs,
+ })
+ } else {
+ // For scripts with no outputs, use a script rule with the module name.
+ ctx.Build(pctx, blueprint.BuildParams{
+ Rule: script,
+ Outputs: []string{ctx.ModuleName()},
+ Inputs: []string{s.properties.Script},
+ Args: ruleArgs,
+ Implicits: implicits,
+ OrderOnly: []string{s.properties.WorkingDir},
+ })
+ }
+}
+
+type Bootstrap struct {
+ config Config
+}
+
+func newBootstrapFactory(config Config) func() blueprint.Singleton {
+ return func() blueprint.Singleton {
+ return &Bootstrap{
+ config: config,
+ }
+ }
+}
+
+func (m *Bootstrap) GenerateBuildActions(ctx blueprint.SingletonContext) {
+ executable, _ := exec.LookPath(os.Args[0])
+
+ builder := ctx.Rule(pctx, "builder",
+ blueprint.RuleParams{
+ Command: fmt.Sprintf("%s $rootBlueprintFile", executable),
+ Description: "Regenerating Ninja files",
+ Generator: true,
+ Depfile: depFile,
+ }, "rootBlueprintFile")
+
+ args := map[string]string{
+ "rootBlueprintFile": rootBlueprintsFile,
+ }
+
+ ctx.Build(pctx, blueprint.BuildParams{
+ Rule: builder,
+ Outputs: []string{outFile},
+ Args: args,
+ })
+}
+
+func isBuilderModule(m blueprint.Module) bool {
+ _, ok := m.(BuilderModule)
+ return ok
+}
+
+func getDirectDependencies(ctx blueprint.ModuleContext) []string {
+ var depTargets []string
+ ctx.VisitDirectDepsIf(isBuilderModule, func(m blueprint.Module) {
+ target := m.(BuilderModule)
+ depTargets = append(depTargets, target.TargetName())
+ })
+ return depTargets
+}
+
+func getAllDependencies(ctx blueprint.ModuleContext) []string {
+ var depTargets []string
+ ctx.VisitDepsDepthFirstIf(isBuilderModule, func(m blueprint.Module) {
+ target := m.(BuilderModule)
+ depTargets = append(depTargets, target.TargetName())
+ })
+ return depTargets
+}
diff --git a/config.go b/config.go
new file mode 100644
index 0000000..e36b299
--- /dev/null
+++ b/config.go
@@ -0,0 +1,94 @@
+// Copyright 2016 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package main
+
+import (
+ "flag"
+ "fmt"
+ "path/filepath"
+ "runtime"
+ "strings"
+)
+
+var (
+ OutDir = pctx.VariableConfigMethod("OutDir", Config.OutDir)
+ SrcDir = pctx.VariableConfigMethod("SrcDir", Config.SrcDir)
+ HostTriple = pctx.VariableConfigMethod("HostTriple", Config.HostTriple)
+ TargetTriple = pctx.VariableConfigMethod("TargetTriple", Config.TargetTriple)
+
+ HostArch = pctx.VariableConfigMethod("HostArch", Config.HostArch)
+ HostOS = pctx.VariableConfigMethod("HostOS", Config.HostOS)
+ TargetArch = pctx.VariableConfigMethod("TargetArch", Config.TargetArch)
+ TargetOS = pctx.VariableConfigMethod("TargetOS", Config.TargetOS)
+
+ jobs int
+ Jobs = pctx.VariableConfigMethod("Jobs", func(c *buildConfig) string {
+ return fmt.Sprintf("%d", jobs)
+ })
+
+ ToolsDir = pctx.StaticVariable("ToolsDir", filepath.Join("root", "tools"))
+)
+
+func init() {
+ flag.IntVar(&jobs, "j", runtime.NumCPU(), "number of parallel jobs")
+}
+
+type Config interface {
+ OutDir() string
+ SrcDir() string
+ HostTriple() string
+ TargetTriple() string
+ HostArch() string
+ HostOS() string
+ TargetArch() string
+ TargetOS() string
+}
+
+type buildConfig struct {
+ srcDir string
+ outDir string
+ hostTriple string
+ targetTriple string
+}
+
+func NewConfig(srcDir string, outDir string, hostTriple string, targetTriple string) *buildConfig {
+ return &buildConfig{srcDir, outDir, hostTriple, targetTriple}
+}
+
+func (c *buildConfig) OutDir() string {
+ return c.outDir
+}
+
+func (c *buildConfig) SrcDir() string {
+ return c.srcDir
+}
+
+func (c *buildConfig) HostTriple() string {
+ return c.hostTriple
+}
+
+func (c *buildConfig) TargetTriple() string {
+ return c.targetTriple
+}
+
+func (c *buildConfig) HostArch() string {
+ s := strings.Split(c.hostTriple, "-")
+ return s[0]
+}
+
+func (c *buildConfig) HostOS() string {
+ s := strings.Split(c.hostTriple, "-")
+ return strings.Title(s[len(s) - 1])
+}
+
+func (c *buildConfig) TargetArch() string {
+ s := strings.Split(c.targetTriple, "-")
+ return s[0]
+}
+
+func (c *buildConfig) TargetOS() string {
+ s := strings.SplitN(c.targetTriple, "-", 3)
+ return strings.Title(s[len(s) - 1])
+}
diff --git a/main.go b/main.go
new file mode 100644
index 0000000..c46656d
--- /dev/null
+++ b/main.go
@@ -0,0 +1,147 @@
+// Copyright 2016 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package main
+
+import (
+ "bytes"
+ "flag"
+ "fmt"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "runtime"
+
+ "github.com/google/blueprint"
+ "github.com/google/blueprint/deptools"
+)
+
+var (
+ rootBlueprintsFile string
+ manifestFile string
+
+ depFile string
+ outFile string
+ hostTriple string
+ targetTriple string
+
+ srcDir string
+ outDir string
+)
+
+func init() {
+ flag.StringVar(&srcDir, "src", ".", "the source directory")
+ flag.StringVar(&outDir, "out", ".", "the build output directory")
+ flag.StringVar(&hostTriple, "host", triple(), "build tools to run on")
+ flag.StringVar(&targetTriple, "target", "", "target triple")
+
+ flag.Usage = func() {
+ fmt.Printf("Usage: toyen [options] <Blueprint file>\n")
+ flag.PrintDefaults()
+ }
+}
+
+func triple() string {
+ arches := map[string]string{
+ "386": "i386",
+ "amd64": "x86_64",
+ "arm": "armv7a",
+ "arm64": "aarch64",
+ }
+
+ var arch string
+ var ok bool
+ if arch, ok = arches[runtime.GOARCH]; !ok {
+ arch = "unknown"
+ }
+
+ return fmt.Sprintf("%s-%s", arch, runtime.GOOS)
+}
+
+func main() {
+ flag.Parse()
+
+ // The top-level Blueprints file is passed as the first argument.
+ if len(flag.Args()) == 0 {
+ flag.Usage()
+ os.Exit(1)
+ }
+
+ rootBlueprintsFile, _ = filepath.Abs(flag.Arg(0))
+
+ srcDir, _ = filepath.Abs(srcDir)
+ outDir, _ = filepath.Abs(outDir)
+
+ outFile = filepath.Join(outDir, "build.ninja")
+ depFile = outFile + ".d"
+
+ config := NewConfig(srcDir, outDir, hostTriple, targetTriple)
+
+ // Create the build context.
+ ctx := blueprint.NewContext()
+
+ // Register custom module types.
+ ctx.RegisterModuleType("alias", newAliasModuleFactory(config))
+ ctx.RegisterModuleType("cmake", newCMakeModuleFactory(config))
+ ctx.RegisterModuleType("copy", newCopyModuleFactory(config))
+ ctx.RegisterModuleType("gn", newGnModuleFactory(config))
+ ctx.RegisterModuleType("install", newInstallModuleFactory(config))
+ ctx.RegisterModuleType("make", newMakeModuleFactory(config))
+ ctx.RegisterModuleType("ninja", newNinjaModuleFactory(config))
+ ctx.RegisterModuleType("script", newScriptModuleFactory(config))
+
+ ctx.RegisterSingletonType("bootstrap", newBootstrapFactory(config))
+
+ deps, errs := ctx.ParseBlueprintsFiles(rootBlueprintsFile)
+ if len(errs) > 0 {
+ fatalErrors(errs)
+ }
+
+ errs = ctx.ResolveDependencies(config)
+ if len(errs) > 0 {
+ fatalErrors(errs)
+ }
+
+ extraDeps, errs := ctx.PrepareBuildActions(config)
+ if len(errs) > 0 {
+ fatalErrors(errs)
+ }
+ deps = append(deps, extraDeps...)
+
+ buf := bytes.NewBuffer(nil)
+ if err := ctx.WriteBuildFile(buf); err != nil {
+ fatalf("error generating Ninja file contents: %s", err)
+ }
+
+ const outFilePermissions = 0666
+
+ if err := ioutil.WriteFile(outFile, buf.Bytes(), outFilePermissions); err != nil {
+ fatalf("error writing %s: %s", outFile, err)
+ }
+
+ if err := deptools.WriteDepFile(depFile, outFile, deps); err != nil {
+ fatalf("error writing depfile: %s", err)
+ }
+}
+
+func fatalf(format string, args ...interface{}) {
+ fmt.Printf(format, args...)
+ fmt.Print("\n")
+ os.Exit(1)
+}
+
+func fatalErrors(errs []error) {
+ red := "\x1b[31m"
+ unred := "\x1b[0m"
+
+ for _, err := range errs {
+ switch err := err.(type) {
+ case *blueprint.Error:
+ fmt.Printf("%serror:%s %s\n", red, unred, err.Error())
+ default:
+ fmt.Printf("%sinternal error:%s %s\n", red, unred, err)
+ }
+ }
+ os.Exit(1)
+}