Add self-documenting support
The primary builder will now generate a rule to call itself with --docs=.bootstrap/docs/<name>.html to produce an automatically generated documentation file. The documentation generation process is: - Call each factory once to get empty property structs associated with the module type - Use reflection to determine the names of the type of each property struct - Use the bootstrap_go_package modules from reading the Blueprints files to find the source files for each Go package used to build the primary builder - Use the go/parser module to find the type declaration for each property struct - Extract comments for the property struct and each property declaration - Format all the comments into HTML Change-Id: Icae9307cc10549a30bfc14d6922824099de5a9b0
This commit is contained in:
parent
4adc819490
commit
4572edddfa
10 changed files with 948 additions and 44 deletions
14
Blueprints
14
Blueprints
|
@ -73,6 +73,7 @@ bootstrap_go_package(
|
|||
"blueprint",
|
||||
"blueprint-deptools",
|
||||
"blueprint-pathtools",
|
||||
"blueprint-bootstrap-bpdoc",
|
||||
],
|
||||
pkgPath = "github.com/google/blueprint/bootstrap",
|
||||
srcs = [
|
||||
|
@ -81,6 +82,19 @@ bootstrap_go_package(
|
|||
"bootstrap/command.go",
|
||||
"bootstrap/config.go",
|
||||
"bootstrap/doc.go",
|
||||
"bootstrap/writedocs.go",
|
||||
],
|
||||
)
|
||||
|
||||
bootstrap_go_package(
|
||||
name = "blueprint-bootstrap-bpdoc",
|
||||
deps = [
|
||||
"blueprint",
|
||||
"blueprint-proptools",
|
||||
],
|
||||
pkgPath = "github.com/google/blueprint/bootstrap/bpdoc",
|
||||
srcs = [
|
||||
"bootstrap/bpdoc/bpdoc.go",
|
||||
],
|
||||
)
|
||||
|
||||
|
|
|
@ -16,12 +16,13 @@ package bootstrap
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/google/blueprint"
|
||||
"github.com/google/blueprint/pathtools"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/google/blueprint"
|
||||
"github.com/google/blueprint/pathtools"
|
||||
)
|
||||
|
||||
const bootstrapDir = ".bootstrap"
|
||||
|
@ -116,6 +117,8 @@ var (
|
|||
|
||||
BinDir = filepath.Join(bootstrapDir, "bin")
|
||||
minibpFile = filepath.Join(BinDir, "minibp")
|
||||
|
||||
docsDir = filepath.Join(bootstrapDir, "docs")
|
||||
)
|
||||
|
||||
type goPackageProducer interface {
|
||||
|
@ -505,13 +508,13 @@ func (s *singleton) GenerateBuildActions(ctx blueprint.SingletonContext) {
|
|||
// creating the binary that we'll use to generate the non-bootstrap
|
||||
// build.ninja file.
|
||||
var primaryBuilders []*goBinary
|
||||
var allGoBinaries []string
|
||||
var rebootstrapDeps []string
|
||||
ctx.VisitAllModulesIf(isBootstrapBinaryModule,
|
||||
func(module blueprint.Module) {
|
||||
binaryModule := module.(*goBinary)
|
||||
binaryModuleName := ctx.ModuleName(binaryModule)
|
||||
binaryModulePath := filepath.Join(BinDir, binaryModuleName)
|
||||
allGoBinaries = append(allGoBinaries, binaryModulePath)
|
||||
rebootstrapDeps = append(rebootstrapDeps, binaryModulePath)
|
||||
if binaryModule.properties.PrimaryBuilder {
|
||||
primaryBuilders = append(primaryBuilders, binaryModule)
|
||||
}
|
||||
|
@ -552,6 +555,9 @@ func (s *singleton) GenerateBuildActions(ctx blueprint.SingletonContext) {
|
|||
mainNinjaFile := filepath.Join(bootstrapDir, "main.ninja.in")
|
||||
mainNinjaDepFile := mainNinjaFile + ".d"
|
||||
bootstrapNinjaFile := filepath.Join(bootstrapDir, "bootstrap.ninja.in")
|
||||
docsFile := filepath.Join(docsDir, primaryBuilderName+".html")
|
||||
|
||||
rebootstrapDeps = append(rebootstrapDeps, docsFile)
|
||||
|
||||
if s.config.generatingBootstrapper {
|
||||
// We're generating a bootstrapper Ninja file, so we need to set things
|
||||
|
@ -564,6 +570,24 @@ func (s *singleton) GenerateBuildActions(ctx blueprint.SingletonContext) {
|
|||
// two Ninja processes try to write to the same log concurrently.
|
||||
ctx.SetBuildDir(pctx, bootstrapDir)
|
||||
|
||||
// Generate build system docs for the primary builder. Generating docs reads the source
|
||||
// files used to build the primary builder, but that dependency will be picked up through
|
||||
// the dependency on the primary builder itself. There are no dependencies on the
|
||||
// Blueprints files, as any relevant changes to the Blueprints files would have caused
|
||||
// a rebuild of the primary builder.
|
||||
bigbpDocs := ctx.Rule(pctx, "bigbpDocs",
|
||||
blueprint.RuleParams{
|
||||
Command: fmt.Sprintf("%s %s --docs $out %s", primaryBuilderFile,
|
||||
primaryBuilderExtraFlags, topLevelBlueprints),
|
||||
Description: fmt.Sprintf("%s docs $out", primaryBuilderName),
|
||||
})
|
||||
|
||||
ctx.Build(pctx, blueprint.BuildParams{
|
||||
Rule: bigbpDocs,
|
||||
Outputs: []string{docsFile},
|
||||
Implicits: []string{primaryBuilderFile},
|
||||
})
|
||||
|
||||
// We generate the depfile here that includes the dependencies for all
|
||||
// the Blueprints files that contribute to generating the big build
|
||||
// manifest (build.ninja file). This depfile will be used by the non-
|
||||
|
@ -584,7 +608,7 @@ func (s *singleton) GenerateBuildActions(ctx blueprint.SingletonContext) {
|
|||
Rule: bigbp,
|
||||
Outputs: []string{mainNinjaFile},
|
||||
Inputs: []string{topLevelBlueprints},
|
||||
Implicits: allGoBinaries,
|
||||
Implicits: rebootstrapDeps,
|
||||
})
|
||||
|
||||
// When the current build.ninja file is a bootstrapper, we always want
|
||||
|
@ -639,13 +663,12 @@ func (s *singleton) GenerateBuildActions(ctx blueprint.SingletonContext) {
|
|||
Args: args,
|
||||
})
|
||||
} else {
|
||||
var allGoTests []string
|
||||
ctx.VisitAllModulesIf(isGoTestProducer,
|
||||
func(module blueprint.Module) {
|
||||
testModule := module.(goTestProducer)
|
||||
target := testModule.GoTestTarget()
|
||||
if target != "" {
|
||||
allGoTests = append(allGoTests, target)
|
||||
rebootstrapDeps = append(rebootstrapDeps, target)
|
||||
}
|
||||
})
|
||||
|
||||
|
@ -661,8 +684,8 @@ func (s *singleton) GenerateBuildActions(ctx blueprint.SingletonContext) {
|
|||
// rule. We do this by depending on that file and then setting up a
|
||||
// phony rule to generate it that uses the depfile.
|
||||
buildNinjaDeps := []string{"$bootstrapCmd", mainNinjaFile}
|
||||
buildNinjaDeps = append(buildNinjaDeps, allGoBinaries...)
|
||||
buildNinjaDeps = append(buildNinjaDeps, allGoTests...)
|
||||
buildNinjaDeps = append(buildNinjaDeps, rebootstrapDeps...)
|
||||
|
||||
ctx.Build(pctx, blueprint.BuildParams{
|
||||
Rule: rebootstrap,
|
||||
Outputs: []string{"build.ninja"},
|
||||
|
@ -679,6 +702,12 @@ func (s *singleton) GenerateBuildActions(ctx blueprint.SingletonContext) {
|
|||
},
|
||||
})
|
||||
|
||||
ctx.Build(pctx, blueprint.BuildParams{
|
||||
Rule: phony,
|
||||
Outputs: []string{docsFile},
|
||||
Implicits: []string{primaryBuilderFile},
|
||||
})
|
||||
|
||||
// If the bootstrap Ninja invocation caused a new bootstrapNinjaFile to be
|
||||
// generated then that means we need to rebootstrap using it instead of
|
||||
// the current bootstrap manifest. We enable the Ninja "generator"
|
||||
|
|
698
bootstrap/bpdoc/bpdoc.go
Normal file
698
bootstrap/bpdoc/bpdoc.go
Normal file
|
@ -0,0 +1,698 @@
|
|||
package bpdoc
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/doc"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"io/ioutil"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"text/template"
|
||||
|
||||
"github.com/google/blueprint"
|
||||
"github.com/google/blueprint/proptools"
|
||||
)
|
||||
|
||||
type DocCollector struct {
|
||||
pkgFiles map[string][]string // Map of package name to source files, provided by constructor
|
||||
|
||||
mutex sync.Mutex
|
||||
pkgDocs map[string]*doc.Package // Map of package name to parsed Go AST, protected by mutex
|
||||
docs map[string]*PropertyStructDocs // Map of type name to docs, protected by mutex
|
||||
}
|
||||
|
||||
func NewDocCollector(pkgFiles map[string][]string) *DocCollector {
|
||||
return &DocCollector{
|
||||
pkgFiles: pkgFiles,
|
||||
pkgDocs: make(map[string]*doc.Package),
|
||||
docs: make(map[string]*PropertyStructDocs),
|
||||
}
|
||||
}
|
||||
|
||||
// Return the PropertyStructDocs associated with a property struct type. The type should be in the
|
||||
// format <package path>.<type name>
|
||||
func (dc *DocCollector) Docs(pkg, name string, defaults reflect.Value) (*PropertyStructDocs, error) {
|
||||
docs := dc.getDocs(name)
|
||||
|
||||
if docs == nil {
|
||||
pkgDocs, err := dc.packageDocs(pkg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, t := range pkgDocs.Types {
|
||||
if t.Name == name {
|
||||
docs, err = newDocs(t)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
docs = dc.putDocs(name, docs)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if docs == nil {
|
||||
return nil, fmt.Errorf("package %q type %q not found", pkg, name)
|
||||
}
|
||||
|
||||
docs = docs.Clone()
|
||||
docs.SetDefaults(defaults)
|
||||
|
||||
return docs, nil
|
||||
}
|
||||
|
||||
func (dc *DocCollector) getDocs(name string) *PropertyStructDocs {
|
||||
dc.mutex.Lock()
|
||||
defer dc.mutex.Unlock()
|
||||
|
||||
return dc.docs[name]
|
||||
}
|
||||
|
||||
func (dc *DocCollector) putDocs(name string, docs *PropertyStructDocs) *PropertyStructDocs {
|
||||
dc.mutex.Lock()
|
||||
defer dc.mutex.Unlock()
|
||||
|
||||
if dc.docs[name] != nil {
|
||||
return dc.docs[name]
|
||||
} else {
|
||||
dc.docs[name] = docs
|
||||
return docs
|
||||
}
|
||||
}
|
||||
|
||||
type PropertyStructDocs struct {
|
||||
Name string
|
||||
Text string
|
||||
Properties []PropertyDocs
|
||||
}
|
||||
|
||||
type PropertyDocs struct {
|
||||
Name string
|
||||
OtherNames []string
|
||||
Type string
|
||||
Tag reflect.StructTag
|
||||
Text string
|
||||
OtherTexts []string
|
||||
Properties []PropertyDocs
|
||||
Default string
|
||||
}
|
||||
|
||||
func (docs *PropertyStructDocs) Clone() *PropertyStructDocs {
|
||||
ret := *docs
|
||||
ret.Properties = append([]PropertyDocs(nil), ret.Properties...)
|
||||
for i, prop := range ret.Properties {
|
||||
ret.Properties[i] = prop.Clone()
|
||||
}
|
||||
|
||||
return &ret
|
||||
}
|
||||
|
||||
func (docs *PropertyDocs) Clone() PropertyDocs {
|
||||
ret := *docs
|
||||
ret.Properties = append([]PropertyDocs(nil), ret.Properties...)
|
||||
for i, prop := range ret.Properties {
|
||||
ret.Properties[i] = prop.Clone()
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func (docs *PropertyDocs) Equal(other PropertyDocs) bool {
|
||||
return docs.Name == other.Name && docs.Type == other.Type && docs.Tag == other.Tag &&
|
||||
docs.Text == other.Text && docs.Default == other.Default &&
|
||||
stringArrayEqual(docs.OtherNames, other.OtherNames) &&
|
||||
stringArrayEqual(docs.OtherTexts, other.OtherTexts) &&
|
||||
docs.SameSubProperties(other)
|
||||
}
|
||||
|
||||
func (docs *PropertyStructDocs) SetDefaults(defaults reflect.Value) {
|
||||
setDefaults(docs.Properties, defaults)
|
||||
}
|
||||
|
||||
func setDefaults(properties []PropertyDocs, defaults reflect.Value) {
|
||||
for i := range properties {
|
||||
prop := &properties[i]
|
||||
fieldName := proptools.FieldNameForProperty(prop.Name)
|
||||
f := defaults.FieldByName(fieldName)
|
||||
if (f == reflect.Value{}) {
|
||||
panic(fmt.Errorf("property %q does not exist in %q", fieldName, defaults.Type()))
|
||||
}
|
||||
|
||||
if reflect.DeepEqual(f.Interface(), reflect.Zero(f.Type()).Interface()) {
|
||||
continue
|
||||
}
|
||||
|
||||
if f.Type().Kind() == reflect.Interface {
|
||||
f = f.Elem()
|
||||
}
|
||||
|
||||
if f.Type().Kind() == reflect.Ptr {
|
||||
f = f.Elem()
|
||||
}
|
||||
|
||||
if f.Type().Kind() == reflect.Struct {
|
||||
setDefaults(prop.Properties, f)
|
||||
} else {
|
||||
prop.Default = fmt.Sprintf("%v", f.Interface())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func stringArrayEqual(a, b []string) bool {
|
||||
if len(a) != len(b) {
|
||||
return false
|
||||
}
|
||||
|
||||
for i := range a {
|
||||
if a[i] != b[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (docs *PropertyDocs) SameSubProperties(other PropertyDocs) bool {
|
||||
if len(docs.Properties) != len(other.Properties) {
|
||||
return false
|
||||
}
|
||||
|
||||
for i := range docs.Properties {
|
||||
if !docs.Properties[i].Equal(other.Properties[i]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (docs *PropertyStructDocs) GetByName(name string) *PropertyDocs {
|
||||
return getByName(name, "", &docs.Properties)
|
||||
}
|
||||
|
||||
func getByName(name string, prefix string, props *[]PropertyDocs) *PropertyDocs {
|
||||
for i := range *props {
|
||||
if prefix+(*props)[i].Name == name {
|
||||
return &(*props)[i]
|
||||
} else if strings.HasPrefix(name, prefix+(*props)[i].Name+".") {
|
||||
return getByName(name, prefix+(*props)[i].Name+".", &(*props)[i].Properties)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (prop *PropertyDocs) Nest(nested *PropertyStructDocs) {
|
||||
//prop.Name += "(" + nested.Name + ")"
|
||||
//prop.Text += "(" + nested.Text + ")"
|
||||
prop.Properties = append(prop.Properties, nested.Properties...)
|
||||
}
|
||||
|
||||
func newDocs(t *doc.Type) (*PropertyStructDocs, error) {
|
||||
typeSpec := t.Decl.Specs[0].(*ast.TypeSpec)
|
||||
docs := PropertyStructDocs{
|
||||
Name: t.Name,
|
||||
Text: t.Doc,
|
||||
}
|
||||
|
||||
structType, ok := typeSpec.Type.(*ast.StructType)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("type of %q is not a struct", t.Name)
|
||||
}
|
||||
|
||||
var err error
|
||||
docs.Properties, err = structProperties(structType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &docs, nil
|
||||
}
|
||||
|
||||
func structProperties(structType *ast.StructType) (props []PropertyDocs, err error) {
|
||||
for _, f := range structType.Fields.List {
|
||||
//fmt.Printf("%T %#v\n", f, f)
|
||||
for _, n := range f.Names {
|
||||
var name, typ, tag, text string
|
||||
var innerProps []PropertyDocs
|
||||
if n != nil {
|
||||
name = proptools.PropertyNameForField(n.Name)
|
||||
}
|
||||
if f.Doc != nil {
|
||||
text = f.Doc.Text()
|
||||
}
|
||||
if f.Tag != nil {
|
||||
tag, err = strconv.Unquote(f.Tag.Value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
switch a := f.Type.(type) {
|
||||
case *ast.ArrayType:
|
||||
typ = "list of strings"
|
||||
case *ast.InterfaceType:
|
||||
typ = "interface"
|
||||
case *ast.Ident:
|
||||
typ = a.Name
|
||||
case *ast.StructType:
|
||||
innerProps, err = structProperties(a)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
default:
|
||||
typ = fmt.Sprintf("%T", f.Type)
|
||||
}
|
||||
|
||||
props = append(props, PropertyDocs{
|
||||
Name: name,
|
||||
Type: typ,
|
||||
Tag: reflect.StructTag(tag),
|
||||
Text: text,
|
||||
Properties: innerProps,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return props, nil
|
||||
}
|
||||
|
||||
func (docs *PropertyStructDocs) ExcludeByTag(key, value string) {
|
||||
filterPropsByTag(&docs.Properties, key, value, true)
|
||||
}
|
||||
|
||||
func (docs *PropertyStructDocs) IncludeByTag(key, value string) {
|
||||
filterPropsByTag(&docs.Properties, key, value, false)
|
||||
}
|
||||
|
||||
func filterPropsByTag(props *[]PropertyDocs, key, value string, exclude bool) {
|
||||
// Create a slice that shares the storage of props but has 0 length. Appending up to
|
||||
// len(props) times to this slice will overwrite the original slice contents
|
||||
filtered := (*props)[:0]
|
||||
for _, x := range *props {
|
||||
tag := x.Tag.Get(key)
|
||||
for _, entry := range strings.Split(tag, ",") {
|
||||
if (entry == value) == !exclude {
|
||||
filtered = append(filtered, x)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*props = filtered
|
||||
}
|
||||
|
||||
// Package AST generation and storage
|
||||
func (dc *DocCollector) packageDocs(pkg string) (*doc.Package, error) {
|
||||
pkgDocs := dc.getPackageDocs(pkg)
|
||||
if pkgDocs == nil {
|
||||
if files, ok := dc.pkgFiles[pkg]; ok {
|
||||
var err error
|
||||
pkgAST, err := NewPackageAST(files)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pkgDocs = doc.New(pkgAST, pkg, doc.AllDecls)
|
||||
pkgDocs = dc.putPackageDocs(pkg, pkgDocs)
|
||||
} else {
|
||||
return nil, fmt.Errorf("unknown package %q", pkg)
|
||||
}
|
||||
}
|
||||
return pkgDocs, nil
|
||||
}
|
||||
|
||||
func (dc *DocCollector) getPackageDocs(pkg string) *doc.Package {
|
||||
dc.mutex.Lock()
|
||||
defer dc.mutex.Unlock()
|
||||
|
||||
return dc.pkgDocs[pkg]
|
||||
}
|
||||
|
||||
func (dc *DocCollector) putPackageDocs(pkg string, pkgDocs *doc.Package) *doc.Package {
|
||||
dc.mutex.Lock()
|
||||
defer dc.mutex.Unlock()
|
||||
|
||||
if dc.pkgDocs[pkg] != nil {
|
||||
return dc.pkgDocs[pkg]
|
||||
} else {
|
||||
dc.pkgDocs[pkg] = pkgDocs
|
||||
return pkgDocs
|
||||
}
|
||||
}
|
||||
|
||||
func NewPackageAST(files []string) (*ast.Package, error) {
|
||||
asts := make(map[string]*ast.File)
|
||||
|
||||
fset := token.NewFileSet()
|
||||
for _, file := range files {
|
||||
ast, err := parser.ParseFile(fset, file, nil, parser.ParseComments)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
asts[file] = ast
|
||||
}
|
||||
|
||||
pkg, _ := ast.NewPackage(fset, asts, nil, nil)
|
||||
return pkg, nil
|
||||
}
|
||||
|
||||
func Write(filename string, pkgFiles map[string][]string,
|
||||
moduleTypePropertyStructs map[string][]interface{}) error {
|
||||
|
||||
docSet := NewDocCollector(pkgFiles)
|
||||
|
||||
var moduleTypeList []*moduleTypeDoc
|
||||
for moduleType, propertyStructs := range moduleTypePropertyStructs {
|
||||
mtDoc, err := getModuleTypeDoc(docSet, moduleType, propertyStructs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
removeEmptyPropertyStructs(mtDoc)
|
||||
collapseDuplicatePropertyStructs(mtDoc)
|
||||
collapseNestedPropertyStructs(mtDoc)
|
||||
combineDuplicateProperties(mtDoc)
|
||||
moduleTypeList = append(moduleTypeList, mtDoc)
|
||||
}
|
||||
|
||||
sort.Sort(moduleTypeByName(moduleTypeList))
|
||||
|
||||
buf := &bytes.Buffer{}
|
||||
|
||||
unique := 0
|
||||
|
||||
tmpl, err := template.New("file").Funcs(map[string]interface{}{
|
||||
"unique": func() int {
|
||||
unique++
|
||||
return unique
|
||||
}}).Parse(fileTemplate)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = tmpl.Execute(buf, moduleTypeList)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = ioutil.WriteFile(filename, buf.Bytes(), 0666)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getModuleTypeDoc(docSet *DocCollector, moduleType string,
|
||||
propertyStructs []interface{}) (*moduleTypeDoc, error) {
|
||||
mtDoc := &moduleTypeDoc{
|
||||
Name: moduleType,
|
||||
//Text: docSet.ModuleTypeDocs(moduleType),
|
||||
}
|
||||
|
||||
for _, s := range propertyStructs {
|
||||
v := reflect.ValueOf(s).Elem()
|
||||
t := v.Type()
|
||||
|
||||
// Ignore property structs with unexported or unnamed types
|
||||
if t.PkgPath() == "" {
|
||||
continue
|
||||
}
|
||||
psDoc, err := docSet.Docs(t.PkgPath(), t.Name(), v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
psDoc.ExcludeByTag("blueprint", "mutated")
|
||||
|
||||
for nested, nestedValue := range nestedPropertyStructs(v) {
|
||||
nestedType := nestedValue.Type()
|
||||
|
||||
// Ignore property structs with unexported or unnamed types
|
||||
if nestedType.PkgPath() == "" {
|
||||
continue
|
||||
}
|
||||
nestedDoc, err := docSet.Docs(nestedType.PkgPath(), nestedType.Name(), nestedValue)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nestedDoc.ExcludeByTag("blueprint", "mutated")
|
||||
nestPoint := psDoc.GetByName(nested)
|
||||
if nestPoint == nil {
|
||||
return nil, fmt.Errorf("nesting point %q not found", nested)
|
||||
}
|
||||
|
||||
key, value, err := blueprint.HasFilter(nestPoint.Tag)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if key != "" {
|
||||
nestedDoc.IncludeByTag(key, value)
|
||||
}
|
||||
|
||||
nestPoint.Nest(nestedDoc)
|
||||
}
|
||||
mtDoc.PropertyStructs = append(mtDoc.PropertyStructs, psDoc)
|
||||
}
|
||||
|
||||
return mtDoc, nil
|
||||
}
|
||||
|
||||
func nestedPropertyStructs(s reflect.Value) map[string]reflect.Value {
|
||||
ret := make(map[string]reflect.Value)
|
||||
var walk func(structValue reflect.Value, prefix string)
|
||||
walk = func(structValue reflect.Value, prefix string) {
|
||||
typ := structValue.Type()
|
||||
for i := 0; i < structValue.NumField(); i++ {
|
||||
field := typ.Field(i)
|
||||
if field.PkgPath != "" {
|
||||
// The field is not exported so just skip it.
|
||||
continue
|
||||
}
|
||||
|
||||
fieldValue := structValue.Field(i)
|
||||
|
||||
switch fieldValue.Kind() {
|
||||
case reflect.Bool, reflect.String, reflect.Slice, reflect.Int, reflect.Uint:
|
||||
// Nothing
|
||||
case reflect.Struct:
|
||||
walk(fieldValue, prefix+proptools.PropertyNameForField(field.Name)+".")
|
||||
case reflect.Ptr, reflect.Interface:
|
||||
if !fieldValue.IsNil() {
|
||||
// We leave the pointer intact and zero out the struct that's
|
||||
// pointed to.
|
||||
elem := fieldValue.Elem()
|
||||
if fieldValue.Kind() == reflect.Interface {
|
||||
if elem.Kind() != reflect.Ptr {
|
||||
panic(fmt.Errorf("can't get type of field %q: interface "+
|
||||
"refers to a non-pointer", field.Name))
|
||||
}
|
||||
elem = elem.Elem()
|
||||
}
|
||||
if elem.Kind() != reflect.Struct {
|
||||
panic(fmt.Errorf("can't get type of field %q: points to a "+
|
||||
"non-struct", field.Name))
|
||||
}
|
||||
nestPoint := prefix + proptools.PropertyNameForField(field.Name)
|
||||
ret[nestPoint] = elem
|
||||
walk(elem, nestPoint+".")
|
||||
}
|
||||
default:
|
||||
panic(fmt.Errorf("unexpected kind for property struct field %q: %s",
|
||||
field.Name, fieldValue.Kind()))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
walk(s, "")
|
||||
return ret
|
||||
}
|
||||
|
||||
// Remove any property structs that have no exported fields
|
||||
func removeEmptyPropertyStructs(mtDoc *moduleTypeDoc) {
|
||||
for i := 0; i < len(mtDoc.PropertyStructs); i++ {
|
||||
if len(mtDoc.PropertyStructs[i].Properties) == 0 {
|
||||
mtDoc.PropertyStructs = append(mtDoc.PropertyStructs[:i], mtDoc.PropertyStructs[i+1:]...)
|
||||
i--
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Squashes duplicates of the same property struct into single entries
|
||||
func collapseDuplicatePropertyStructs(mtDoc *moduleTypeDoc) {
|
||||
var collapsedDocs []*PropertyStructDocs
|
||||
|
||||
propertyStructLoop:
|
||||
for _, from := range mtDoc.PropertyStructs {
|
||||
for _, to := range collapsedDocs {
|
||||
if from.Name == to.Name {
|
||||
collapseDuplicateProperties(&to.Properties, &from.Properties)
|
||||
continue propertyStructLoop
|
||||
}
|
||||
}
|
||||
collapsedDocs = append(collapsedDocs, from)
|
||||
}
|
||||
mtDoc.PropertyStructs = collapsedDocs
|
||||
}
|
||||
|
||||
func collapseDuplicateProperties(to, from *[]PropertyDocs) {
|
||||
propertyLoop:
|
||||
for _, f := range *from {
|
||||
for i := range *to {
|
||||
t := &(*to)[i]
|
||||
if f.Name == t.Name {
|
||||
collapseDuplicateProperties(&t.Properties, &f.Properties)
|
||||
continue propertyLoop
|
||||
}
|
||||
}
|
||||
*to = append(*to, f)
|
||||
}
|
||||
}
|
||||
|
||||
// Find all property structs that only contain structs, and move their children up one with
|
||||
// a prefixed name
|
||||
func collapseNestedPropertyStructs(mtDoc *moduleTypeDoc) {
|
||||
for _, ps := range mtDoc.PropertyStructs {
|
||||
collapseNestedProperties(&ps.Properties)
|
||||
}
|
||||
}
|
||||
|
||||
func collapseNestedProperties(p *[]PropertyDocs) {
|
||||
var n []PropertyDocs
|
||||
|
||||
for _, parent := range *p {
|
||||
var containsProperty bool
|
||||
for j := range parent.Properties {
|
||||
child := &parent.Properties[j]
|
||||
if len(child.Properties) > 0 {
|
||||
collapseNestedProperties(&child.Properties)
|
||||
} else {
|
||||
containsProperty = true
|
||||
}
|
||||
}
|
||||
if containsProperty || len(parent.Properties) == 0 {
|
||||
n = append(n, parent)
|
||||
} else {
|
||||
for j := range parent.Properties {
|
||||
child := parent.Properties[j]
|
||||
child.Name = parent.Name + "." + child.Name
|
||||
n = append(n, child)
|
||||
}
|
||||
}
|
||||
}
|
||||
*p = n
|
||||
}
|
||||
|
||||
func combineDuplicateProperties(mtDoc *moduleTypeDoc) {
|
||||
for _, ps := range mtDoc.PropertyStructs {
|
||||
combineDuplicateSubProperties(&ps.Properties)
|
||||
}
|
||||
}
|
||||
|
||||
func combineDuplicateSubProperties(p *[]PropertyDocs) {
|
||||
var n []PropertyDocs
|
||||
propertyLoop:
|
||||
for _, child := range *p {
|
||||
if len(child.Properties) > 0 {
|
||||
combineDuplicateSubProperties(&child.Properties)
|
||||
for i := range n {
|
||||
s := &n[i]
|
||||
if s.SameSubProperties(child) {
|
||||
s.OtherNames = append(s.OtherNames, child.Name)
|
||||
s.OtherTexts = append(s.OtherTexts, child.Text)
|
||||
continue propertyLoop
|
||||
}
|
||||
}
|
||||
}
|
||||
n = append(n, child)
|
||||
}
|
||||
|
||||
*p = n
|
||||
}
|
||||
|
||||
type moduleTypeByName []*moduleTypeDoc
|
||||
|
||||
func (l moduleTypeByName) Len() int { return len(l) }
|
||||
func (l moduleTypeByName) Less(i, j int) bool { return l[i].Name < l[j].Name }
|
||||
func (l moduleTypeByName) Swap(i, j int) { l[i], l[j] = l[j], l[i] }
|
||||
|
||||
type moduleTypeDoc struct {
|
||||
Name string
|
||||
Text string
|
||||
PropertyStructs []*PropertyStructDocs
|
||||
}
|
||||
|
||||
var (
|
||||
fileTemplate = `
|
||||
<html>
|
||||
<head>
|
||||
<title>Build Docs</title>
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
|
||||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
|
||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Build Docs</h1>
|
||||
<div class="panel-group" id="accordion" role="tablist" aria-multiselectable="true">
|
||||
{{range .}}
|
||||
{{ $collapseIndex := unique }}
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading" role="tab" id="heading{{$collapseIndex}}">
|
||||
<h2 class="panel-title">
|
||||
<a class="collapsed" role="button" data-toggle="collapse" data-parent="#accordion" href="#collapse{{$collapseIndex}}" aria-expanded="false" aria-controls="collapse{{$collapseIndex}}">
|
||||
{{.Name}}
|
||||
</a>
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
<div id="collapse{{$collapseIndex}}" class="panel-collapse collapse" role="tabpanel" aria-labelledby="heading{{$collapseIndex}}">
|
||||
<div class="panel-body">
|
||||
<p>{{.Text}}</p>
|
||||
{{range .PropertyStructs}}
|
||||
<p>{{.Text}}</p>
|
||||
{{template "properties" .Properties}}
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
{{define "properties"}}
|
||||
<div class="panel-group" id="accordion" role="tablist" aria-multiselectable="true">
|
||||
{{range .}}
|
||||
{{$collapseIndex := unique}}
|
||||
{{if .Properties}}
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading" role="tab" id="heading{{$collapseIndex}}">
|
||||
<h4 class="panel-title">
|
||||
<a class="collapsed" role="button" data-toggle="collapse" data-parent="#accordion" href="#collapse{{$collapseIndex}}" aria-expanded="false" aria-controls="collapse{{$collapseIndex}}">
|
||||
{{.Name}}{{range .OtherNames}}, {{.}}{{end}}
|
||||
</a>
|
||||
</h4>
|
||||
</div>
|
||||
</div>
|
||||
<div id="collapse{{$collapseIndex}}" class="panel-collapse collapse" role="tabpanel" aria-labelledby="heading{{$collapseIndex}}">
|
||||
<div class="panel-body">
|
||||
<p>{{.Text}}</p>
|
||||
{{range .OtherTexts}}<p>{{.}}</p>{{end}}
|
||||
{{template "properties" .Properties}}
|
||||
</div>
|
||||
</div>
|
||||
{{else}}
|
||||
<div>
|
||||
<h4>{{.Name}}{{range .OtherNames}}, {{.}}{{end}}</h4>
|
||||
<p>{{.Text}}</p>
|
||||
{{range .OtherTexts}}<p>{{.}}</p>{{end}}
|
||||
<p><i>Type: {{.Type}}</i></p>
|
||||
{{if .Default}}<p><i>Default: {{.Default}}</i></p>{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
`
|
||||
)
|
|
@ -33,6 +33,7 @@ var (
|
|||
depFile string
|
||||
checkFile string
|
||||
manifestFile string
|
||||
docFile string
|
||||
cpuprofile string
|
||||
runGoTests bool
|
||||
)
|
||||
|
@ -42,6 +43,7 @@ func init() {
|
|||
flag.StringVar(&depFile, "d", "", "the dependency file to output")
|
||||
flag.StringVar(&checkFile, "c", "", "the existing file to check against")
|
||||
flag.StringVar(&manifestFile, "m", "", "the bootstrap manifest file")
|
||||
flag.StringVar(&docFile, "docs", "", "build documentation file to output")
|
||||
flag.StringVar(&cpuprofile, "cpuprofile", "", "write cpu profile to file")
|
||||
flag.BoolVar(&runGoTests, "t", false, "build and run go tests during bootstrap")
|
||||
}
|
||||
|
@ -90,6 +92,19 @@ func Main(ctx *blueprint.Context, config interface{}, extraNinjaFileDeps ...stri
|
|||
// Add extra ninja file dependencies
|
||||
deps = append(deps, extraNinjaFileDeps...)
|
||||
|
||||
errs = ctx.ResolveDependencies(config)
|
||||
if len(errs) > 0 {
|
||||
fatalErrors(errs)
|
||||
}
|
||||
|
||||
if docFile != "" {
|
||||
err := writeDocs(ctx, filepath.Dir(bootstrapConfig.topLevelBlueprintsFile), docFile)
|
||||
if err != nil {
|
||||
fatalErrors([]error{err})
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
extraDeps, errs := ctx.PrepareBuildActions(config)
|
||||
if len(errs) > 0 {
|
||||
fatalErrors(errs)
|
||||
|
|
59
bootstrap/writedocs.go
Normal file
59
bootstrap/writedocs.go
Normal file
|
@ -0,0 +1,59 @@
|
|||
package bootstrap
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/google/blueprint"
|
||||
"github.com/google/blueprint/bootstrap/bpdoc"
|
||||
"github.com/google/blueprint/pathtools"
|
||||
)
|
||||
|
||||
func writeDocs(ctx *blueprint.Context, srcDir, filename string) error {
|
||||
// Find the module that's marked as the "primary builder", which means it's
|
||||
// creating the binary that we'll use to generate the non-bootstrap
|
||||
// build.ninja file.
|
||||
var primaryBuilders []*goBinary
|
||||
var minibp *goBinary
|
||||
ctx.VisitAllModulesIf(isBootstrapBinaryModule,
|
||||
func(module blueprint.Module) {
|
||||
binaryModule := module.(*goBinary)
|
||||
if binaryModule.properties.PrimaryBuilder {
|
||||
primaryBuilders = append(primaryBuilders, binaryModule)
|
||||
}
|
||||
if ctx.ModuleName(binaryModule) == "minibp" {
|
||||
minibp = binaryModule
|
||||
}
|
||||
})
|
||||
|
||||
if minibp == nil {
|
||||
panic("missing minibp")
|
||||
}
|
||||
|
||||
var primaryBuilder *goBinary
|
||||
switch len(primaryBuilders) {
|
||||
case 0:
|
||||
// If there's no primary builder module then that means we'll use minibp
|
||||
// as the primary builder.
|
||||
primaryBuilder = minibp
|
||||
|
||||
case 1:
|
||||
primaryBuilder = primaryBuilders[0]
|
||||
|
||||
default:
|
||||
return fmt.Errorf("multiple primary builder modules present")
|
||||
}
|
||||
|
||||
pkgFiles := make(map[string][]string)
|
||||
ctx.VisitDepsDepthFirst(primaryBuilder, func(module blueprint.Module) {
|
||||
switch m := module.(type) {
|
||||
case (*goPackage):
|
||||
pkgFiles[m.properties.PkgPath] = pathtools.PrefixPaths(m.properties.Srcs,
|
||||
filepath.Join(srcDir, ctx.ModuleDir(m)))
|
||||
default:
|
||||
panic(fmt.Errorf("unknown dependency type %T", module))
|
||||
}
|
||||
})
|
||||
|
||||
return bpdoc.Write(filename, pkgFiles, ctx.ModuleTypePropertyStructs())
|
||||
}
|
|
@ -84,17 +84,39 @@ build $
|
|||
${g.bootstrap.srcDir}/bootstrap/cleanup.go $
|
||||
${g.bootstrap.srcDir}/bootstrap/command.go $
|
||||
${g.bootstrap.srcDir}/bootstrap/config.go $
|
||||
${g.bootstrap.srcDir}/bootstrap/doc.go | ${g.bootstrap.gcCmd} $
|
||||
${g.bootstrap.srcDir}/bootstrap/doc.go $
|
||||
${g.bootstrap.srcDir}/bootstrap/writedocs.go | ${g.bootstrap.gcCmd} $
|
||||
.bootstrap/blueprint-parser/pkg/github.com/google/blueprint/parser.a $
|
||||
.bootstrap/blueprint-pathtools/pkg/github.com/google/blueprint/pathtools.a $
|
||||
.bootstrap/blueprint-proptools/pkg/github.com/google/blueprint/proptools.a $
|
||||
.bootstrap/blueprint/pkg/github.com/google/blueprint.a $
|
||||
.bootstrap/blueprint-deptools/pkg/github.com/google/blueprint/deptools.a
|
||||
incFlags = -I .bootstrap/blueprint-parser/pkg -I .bootstrap/blueprint-pathtools/pkg -I .bootstrap/blueprint-proptools/pkg -I .bootstrap/blueprint/pkg -I .bootstrap/blueprint-deptools/pkg
|
||||
.bootstrap/blueprint-deptools/pkg/github.com/google/blueprint/deptools.a $
|
||||
.bootstrap/blueprint-bootstrap-bpdoc/pkg/github.com/google/blueprint/bootstrap/bpdoc.a
|
||||
incFlags = -I .bootstrap/blueprint-parser/pkg -I .bootstrap/blueprint-pathtools/pkg -I .bootstrap/blueprint-proptools/pkg -I .bootstrap/blueprint/pkg -I .bootstrap/blueprint-deptools/pkg -I .bootstrap/blueprint-bootstrap-bpdoc/pkg
|
||||
pkgPath = github.com/google/blueprint/bootstrap
|
||||
default $
|
||||
.bootstrap/blueprint-bootstrap/pkg/github.com/google/blueprint/bootstrap.a
|
||||
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# Module: blueprint-bootstrap-bpdoc
|
||||
# Variant:
|
||||
# Type: bootstrap_go_package
|
||||
# Factory: github.com/google/blueprint/bootstrap.func·002
|
||||
# Defined: Blueprints:89:1
|
||||
|
||||
build $
|
||||
.bootstrap/blueprint-bootstrap-bpdoc/pkg/github.com/google/blueprint/bootstrap/bpdoc.a $
|
||||
: g.bootstrap.gc ${g.bootstrap.srcDir}/bootstrap/bpdoc/bpdoc.go | $
|
||||
${g.bootstrap.gcCmd} $
|
||||
.bootstrap/blueprint-parser/pkg/github.com/google/blueprint/parser.a $
|
||||
.bootstrap/blueprint-pathtools/pkg/github.com/google/blueprint/pathtools.a $
|
||||
.bootstrap/blueprint-proptools/pkg/github.com/google/blueprint/proptools.a $
|
||||
.bootstrap/blueprint/pkg/github.com/google/blueprint.a
|
||||
incFlags = -I .bootstrap/blueprint-parser/pkg -I .bootstrap/blueprint-pathtools/pkg -I .bootstrap/blueprint-proptools/pkg -I .bootstrap/blueprint/pkg
|
||||
pkgPath = github.com/google/blueprint/bootstrap/bpdoc
|
||||
default $
|
||||
.bootstrap/blueprint-bootstrap-bpdoc/pkg/github.com/google/blueprint/bootstrap/bpdoc.a
|
||||
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# Module: blueprint-deptools
|
||||
# Variant:
|
||||
|
@ -159,7 +181,7 @@ default $
|
|||
# Variant:
|
||||
# Type: bootstrap_go_binary
|
||||
# Factory: github.com/google/blueprint/bootstrap.func·003
|
||||
# Defined: Blueprints:96:1
|
||||
# Defined: Blueprints:110:1
|
||||
|
||||
build .bootstrap/bpfmt/obj/bpfmt.a: g.bootstrap.gc $
|
||||
${g.bootstrap.srcDir}/bpfmt/bpfmt.go | ${g.bootstrap.gcCmd} $
|
||||
|
@ -181,7 +203,7 @@ default .bootstrap/bin/bpfmt
|
|||
# Variant:
|
||||
# Type: bootstrap_go_binary
|
||||
# Factory: github.com/google/blueprint/bootstrap.func·003
|
||||
# Defined: Blueprints:102:1
|
||||
# Defined: Blueprints:116:1
|
||||
|
||||
build .bootstrap/bpmodify/obj/bpmodify.a: g.bootstrap.gc $
|
||||
${g.bootstrap.srcDir}/bpmodify/bpmodify.go | ${g.bootstrap.gcCmd} $
|
||||
|
@ -203,7 +225,7 @@ default .bootstrap/bin/bpmodify
|
|||
# Variant:
|
||||
# Type: bootstrap_go_binary
|
||||
# Factory: github.com/google/blueprint/bootstrap.func·003
|
||||
# Defined: Blueprints:108:1
|
||||
# Defined: Blueprints:122:1
|
||||
|
||||
build .bootstrap/gotestmain/obj/gotestmain.a: g.bootstrap.gc $
|
||||
${g.bootstrap.srcDir}/gotestmain/gotestmain.go | ${g.bootstrap.gcCmd}
|
||||
|
@ -222,7 +244,7 @@ default .bootstrap/bin/gotestmain
|
|||
# Variant:
|
||||
# Type: bootstrap_go_binary
|
||||
# Factory: github.com/google/blueprint/bootstrap.func·003
|
||||
# Defined: Blueprints:87:1
|
||||
# Defined: Blueprints:101:1
|
||||
|
||||
build .bootstrap/minibp/obj/minibp.a: g.bootstrap.gc $
|
||||
${g.bootstrap.srcDir}/bootstrap/minibp/main.go | ${g.bootstrap.gcCmd} $
|
||||
|
@ -231,14 +253,15 @@ build .bootstrap/minibp/obj/minibp.a: g.bootstrap.gc $
|
|||
.bootstrap/blueprint-proptools/pkg/github.com/google/blueprint/proptools.a $
|
||||
.bootstrap/blueprint/pkg/github.com/google/blueprint.a $
|
||||
.bootstrap/blueprint-deptools/pkg/github.com/google/blueprint/deptools.a $
|
||||
.bootstrap/blueprint-bootstrap-bpdoc/pkg/github.com/google/blueprint/bootstrap/bpdoc.a $
|
||||
.bootstrap/blueprint-bootstrap/pkg/github.com/google/blueprint/bootstrap.a
|
||||
incFlags = -I .bootstrap/blueprint-parser/pkg -I .bootstrap/blueprint-pathtools/pkg -I .bootstrap/blueprint-proptools/pkg -I .bootstrap/blueprint/pkg -I .bootstrap/blueprint-deptools/pkg -I .bootstrap/blueprint-bootstrap/pkg
|
||||
incFlags = -I .bootstrap/blueprint-parser/pkg -I .bootstrap/blueprint-pathtools/pkg -I .bootstrap/blueprint-proptools/pkg -I .bootstrap/blueprint/pkg -I .bootstrap/blueprint-deptools/pkg -I .bootstrap/blueprint-bootstrap-bpdoc/pkg -I .bootstrap/blueprint-bootstrap/pkg
|
||||
pkgPath = minibp
|
||||
default .bootstrap/minibp/obj/minibp.a
|
||||
|
||||
build .bootstrap/minibp/obj/a.out: g.bootstrap.link $
|
||||
.bootstrap/minibp/obj/minibp.a | ${g.bootstrap.linkCmd}
|
||||
libDirFlags = -L .bootstrap/blueprint-parser/pkg -L .bootstrap/blueprint-pathtools/pkg -L .bootstrap/blueprint-proptools/pkg -L .bootstrap/blueprint/pkg -L .bootstrap/blueprint-deptools/pkg -L .bootstrap/blueprint-bootstrap/pkg
|
||||
libDirFlags = -L .bootstrap/blueprint-parser/pkg -L .bootstrap/blueprint-pathtools/pkg -L .bootstrap/blueprint-proptools/pkg -L .bootstrap/blueprint/pkg -L .bootstrap/blueprint-deptools/pkg -L .bootstrap/blueprint-bootstrap-bpdoc/pkg -L .bootstrap/blueprint-bootstrap/pkg
|
||||
default .bootstrap/minibp/obj/a.out
|
||||
|
||||
build .bootstrap/bin/minibp: g.bootstrap.cp .bootstrap/minibp/obj/a.out
|
||||
|
@ -248,6 +271,10 @@ default .bootstrap/bin/minibp
|
|||
# Singleton: bootstrap
|
||||
# Factory: github.com/google/blueprint/bootstrap.func·008
|
||||
|
||||
rule s.bootstrap.bigbpDocs
|
||||
command = .bootstrap/bin/minibp -p --docs ${out} ${g.bootstrap.srcDir}/Blueprints
|
||||
description = minibp docs ${out}
|
||||
|
||||
rule s.bootstrap.bigbp
|
||||
command = .bootstrap/bin/minibp -p -d .bootstrap/main.ninja.in.d -m ${g.bootstrap.bootstrapManifest} -o ${out} ${in}
|
||||
depfile = .bootstrap/main.ninja.in.d
|
||||
|
@ -259,10 +286,13 @@ rule s.bootstrap.minibp
|
|||
description = minibp ${out}
|
||||
generator = true
|
||||
|
||||
build .bootstrap/docs/minibp.html: s.bootstrap.bigbpDocs | $
|
||||
.bootstrap/bin/minibp
|
||||
default .bootstrap/docs/minibp.html
|
||||
build .bootstrap/main.ninja.in: s.bootstrap.bigbp $
|
||||
${g.bootstrap.srcDir}/Blueprints | .bootstrap/bin/bpfmt $
|
||||
.bootstrap/bin/bpmodify .bootstrap/bin/gotestmain $
|
||||
.bootstrap/bin/minibp
|
||||
.bootstrap/bin/minibp .bootstrap/docs/minibp.html
|
||||
default .bootstrap/main.ninja.in
|
||||
build .bootstrap/notAFile: phony
|
||||
default .bootstrap/notAFile
|
||||
|
|
70
context.go
70
context.go
|
@ -1000,7 +1000,12 @@ func (c *Context) addModules(modules []*moduleInfo) (errs []error) {
|
|||
// objects via the Config method on the DynamicDependerModuleContext objects
|
||||
// passed to their DynamicDependencies method.
|
||||
func (c *Context) ResolveDependencies(config interface{}) []error {
|
||||
errs := c.resolveDependencies(config)
|
||||
errs := c.runEarlyMutators(config)
|
||||
if len(errs) > 0 {
|
||||
return errs
|
||||
}
|
||||
|
||||
errs = c.resolveDependencies(config)
|
||||
if len(errs) > 0 {
|
||||
return errs
|
||||
}
|
||||
|
@ -1361,11 +1366,6 @@ func (c *Context) updateDependencies() (errs []error) {
|
|||
func (c *Context) PrepareBuildActions(config interface{}) (deps []string, errs []error) {
|
||||
c.buildActionsReady = false
|
||||
|
||||
errs = c.runEarlyMutators(config)
|
||||
if len(errs) > 0 {
|
||||
return nil, errs
|
||||
}
|
||||
|
||||
if !c.dependenciesReady {
|
||||
errs := c.ResolveDependencies(config)
|
||||
if len(errs) > 0 {
|
||||
|
@ -2006,6 +2006,64 @@ func (c *Context) AllTargets() (map[string]string, error) {
|
|||
return targets, nil
|
||||
}
|
||||
|
||||
// ModuleTypePropertyStructs returns a mapping from module type name to a list of pointers to
|
||||
// property structs returned by the factory for that module type.
|
||||
func (c *Context) ModuleTypePropertyStructs() map[string][]interface{} {
|
||||
ret := make(map[string][]interface{})
|
||||
for moduleType, factory := range c.moduleFactories {
|
||||
_, ret[moduleType] = factory()
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func (c *Context) ModuleName(logicModule Module) string {
|
||||
module := c.moduleInfo[logicModule]
|
||||
return module.properties.Name
|
||||
}
|
||||
|
||||
func (c *Context) ModuleDir(logicModule Module) string {
|
||||
module := c.moduleInfo[logicModule]
|
||||
return filepath.Dir(module.relBlueprintsFile)
|
||||
}
|
||||
|
||||
func (c *Context) BlueprintFile(logicModule Module) string {
|
||||
module := c.moduleInfo[logicModule]
|
||||
return module.relBlueprintsFile
|
||||
}
|
||||
|
||||
func (c *Context) ModuleErrorf(logicModule Module, format string,
|
||||
args ...interface{}) error {
|
||||
|
||||
module := c.moduleInfo[logicModule]
|
||||
return &Error{
|
||||
Err: fmt.Errorf(format, args...),
|
||||
Pos: module.pos,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Context) VisitAllModules(visit func(Module)) {
|
||||
c.visitAllModules(visit)
|
||||
}
|
||||
|
||||
func (c *Context) VisitAllModulesIf(pred func(Module) bool,
|
||||
visit func(Module)) {
|
||||
|
||||
c.visitAllModulesIf(pred, visit)
|
||||
}
|
||||
|
||||
func (c *Context) VisitDepsDepthFirst(module Module,
|
||||
visit func(Module)) {
|
||||
|
||||
c.visitDepsDepthFirst(c.moduleInfo[module], visit)
|
||||
}
|
||||
|
||||
func (c *Context) VisitDepsDepthFirstIf(module Module,
|
||||
pred func(Module) bool, visit func(Module)) {
|
||||
|
||||
c.visitDepsDepthFirstIf(c.moduleInfo[module], pred, visit)
|
||||
}
|
||||
|
||||
// WriteBuildFile writes the Ninja manifeset text for the generated build
|
||||
// actions to w. If this is called before PrepareBuildActions successfully
|
||||
// completes then ErrBuildActionsNotReady is returned.
|
||||
|
|
|
@ -30,6 +30,15 @@ func PropertyNameForField(fieldName string) string {
|
|||
return propertyName
|
||||
}
|
||||
|
||||
func FieldNameForProperty(propertyName string) string {
|
||||
r, size := utf8.DecodeRuneInString(propertyName)
|
||||
fieldName := string(unicode.ToUpper(r))
|
||||
if len(propertyName) > size {
|
||||
fieldName += propertyName[size:]
|
||||
}
|
||||
return fieldName
|
||||
}
|
||||
|
||||
func CloneProperties(structValue reflect.Value) reflect.Value {
|
||||
result := reflect.New(structValue.Type())
|
||||
CopyProperties(result.Elem(), structValue)
|
||||
|
|
|
@ -16,7 +16,6 @@ package blueprint
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
type Singleton interface {
|
||||
|
@ -71,28 +70,21 @@ func (s *singletonContext) Config() interface{} {
|
|||
}
|
||||
|
||||
func (s *singletonContext) ModuleName(logicModule Module) string {
|
||||
module := s.context.moduleInfo[logicModule]
|
||||
return module.properties.Name
|
||||
return s.context.ModuleName(logicModule)
|
||||
}
|
||||
|
||||
func (s *singletonContext) ModuleDir(logicModule Module) string {
|
||||
module := s.context.moduleInfo[logicModule]
|
||||
return filepath.Dir(module.relBlueprintsFile)
|
||||
return s.context.ModuleDir(logicModule)
|
||||
}
|
||||
|
||||
func (s *singletonContext) BlueprintFile(logicModule Module) string {
|
||||
module := s.context.moduleInfo[logicModule]
|
||||
return module.relBlueprintsFile
|
||||
return s.context.BlueprintFile(logicModule)
|
||||
}
|
||||
|
||||
func (s *singletonContext) ModuleErrorf(logicModule Module, format string,
|
||||
args ...interface{}) {
|
||||
|
||||
module := s.context.moduleInfo[logicModule]
|
||||
s.errs = append(s.errs, &Error{
|
||||
Err: fmt.Errorf(format, args...),
|
||||
Pos: module.pos,
|
||||
})
|
||||
s.errs = append(s.errs, s.context.ModuleErrorf(logicModule, format, args...))
|
||||
}
|
||||
|
||||
func (s *singletonContext) Errorf(format string, args ...interface{}) {
|
||||
|
@ -153,25 +145,25 @@ func (s *singletonContext) SetBuildDir(pctx *PackageContext, value string) {
|
|||
}
|
||||
|
||||
func (s *singletonContext) VisitAllModules(visit func(Module)) {
|
||||
s.context.visitAllModules(visit)
|
||||
s.context.VisitAllModules(visit)
|
||||
}
|
||||
|
||||
func (s *singletonContext) VisitAllModulesIf(pred func(Module) bool,
|
||||
visit func(Module)) {
|
||||
|
||||
s.context.visitAllModulesIf(pred, visit)
|
||||
s.context.VisitAllModulesIf(pred, visit)
|
||||
}
|
||||
|
||||
func (s *singletonContext) VisitDepsDepthFirst(module Module,
|
||||
visit func(Module)) {
|
||||
|
||||
s.context.visitDepsDepthFirst(s.context.moduleInfo[module], visit)
|
||||
s.context.VisitDepsDepthFirst(module, visit)
|
||||
}
|
||||
|
||||
func (s *singletonContext) VisitDepsDepthFirstIf(module Module,
|
||||
pred func(Module) bool, visit func(Module)) {
|
||||
|
||||
s.context.visitDepsDepthFirstIf(s.context.moduleInfo[module], pred, visit)
|
||||
s.context.VisitDepsDepthFirstIf(module, pred, visit)
|
||||
}
|
||||
|
||||
func (s *singletonContext) AddNinjaFileDeps(deps ...string) {
|
||||
|
|
|
@ -230,7 +230,7 @@ func unpackStructValue(namePrefix string, structValue reflect.Value,
|
|||
fallthrough
|
||||
case reflect.Struct:
|
||||
localFilterKey, localFilterValue := filterKey, filterValue
|
||||
if k, v, err := hasFilter(field); err != nil {
|
||||
if k, v, err := HasFilter(field.Tag); err != nil {
|
||||
errs = append(errs, err)
|
||||
if len(errs) >= maxErrors {
|
||||
return errs
|
||||
|
@ -337,8 +337,8 @@ func hasTag(field reflect.StructField, name, value string) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
func hasFilter(field reflect.StructField) (k, v string, err error) {
|
||||
tag := field.Tag.Get("blueprint")
|
||||
func HasFilter(field reflect.StructTag) (k, v string, err error) {
|
||||
tag := field.Get("blueprint")
|
||||
for _, entry := range strings.Split(tag, ",") {
|
||||
if strings.HasPrefix(entry, "filter") {
|
||||
if !strings.HasPrefix(entry, "filter(") || !strings.HasSuffix(entry, ")") {
|
||||
|
|
Loading…
Reference in a new issue