platform_build_blueprint/bootstrap/writedocs.go
Jaewoong Jung 781f6b2896 bpdoc improvements
1. Extract module type documentation.
2. Support primary builder customization of factory function to use for
documentation for each module type.
3. Change the ModuleType list order so that they are grouped by package.

This is basically minor refactoring + readability improvement done on
top of https://github.com/google/blueprint/pull/232.

Change-Id: If7413e5ac23486b85f18d02fb3ba288a38730c32
2019-02-08 15:48:27 -08:00

184 lines
5.4 KiB
Go

package bootstrap
import (
"bytes"
"fmt"
"html/template"
"io/ioutil"
"path/filepath"
"reflect"
"github.com/google/blueprint"
"github.com/google/blueprint/bootstrap/bpdoc"
"github.com/google/blueprint/pathtools"
)
// ModuleTypeDocs returns a list of bpdoc.ModuleType objects that contain information relevant
// to generating documentation for module types supported by the primary builder.
func ModuleTypeDocs(ctx *blueprint.Context, factories map[string]reflect.Value) ([]*bpdoc.Package, 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 nil, 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))
}
})
mergedFactories := make(map[string]reflect.Value)
for moduleType, factory := range factories {
mergedFactories[moduleType] = factory
}
for moduleType, factory := range ctx.ModuleTypeFactories() {
if _, exists := mergedFactories[moduleType]; !exists {
mergedFactories[moduleType] = reflect.ValueOf(factory)
}
}
return bpdoc.AllPackages(pkgFiles, mergedFactories, ctx.ModuleTypePropertyStructs())
}
func writeDocs(ctx *blueprint.Context, filename string) error {
moduleTypeList, err := ModuleTypeDocs(ctx, nil)
if err != nil {
return err
}
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
}
const (
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 .}}
<p>{{.Text}}</p>
{{range .ModuleTypes}}
{{ $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}}
{{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}}
`
)