blob: 18adcc89a2d8a560cf51ff39bc6d89a69f0f2de9 [file] [log] [blame]
// 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",
})
rm = pctx.StaticRule("rm",
blueprint.RuleParams{
Command: "rm -rf $files",
Description: "rm $out",
},
"files")
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 Clean struct {
builderModule
properties struct {
Dirs []string
}
config Config
}
func newCleanModuleFactory(config Config) func() (blueprint.Module, []interface{}) {
return func() (blueprint.Module, []interface{}) {
module := &Clean{
config: config,
}
return module, []interface{}{&module.properties}
}
}
func (c *Clean) GenerateBuildActions(ctx blueprint.ModuleContext) {
c.targetName = ctx.ModuleName()
if len(c.properties.Dirs) != 0 {
// Add a rule for deleting all the specified files
ctx.Build(pctx, blueprint.BuildParams{
Rule: rm,
Outputs: []string{ctx.ModuleName()},
Args: map[string]string{
"files": strings.Join(c.properties.Dirs, " "),
},
Implicits: getDirectDependencies(ctx),
})
} else {
ctx.Build(pctx, blueprint.BuildParams{
Rule: blueprint.Phony,
Outputs: []string{ctx.ModuleName()},
Implicits: 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
}