diff options
author | Sasha Smundak <asmundak@google.com> | 2019-02-07 12:10:56 -0800 |
---|---|---|
committer | Sasha Smundak <asmundak@google.com> | 2019-02-13 21:40:21 -0800 |
commit | ff483393ac55fca8144ea226cbaca21ca465d2b5 (patch) | |
tree | 432e061ba039aac48ced26920b64a2f1b1df567f /cmd | |
parent | dfa4a486eb0146582fe015ac4466dc39ae0e5f7d (diff) | |
download | build_soong-ff483393ac55fca8144ea226cbaca21ca465d2b5.tar.gz build_soong-ff483393ac55fca8144ea226cbaca21ca465d2b5.tar.bz2 build_soong-ff483393ac55fca8144ea226cbaca21ca465d2b5.zip |
Improve documentation page layout
It now has module list on the left and the main panel containing
a section for each module. Each section contains the navigable list
of the module's attributes, grouped by attribute's origin.
Test: Manual
Bug: 120512870
Change-Id: Ib62ef58c61daa88950f0934a70ba8ed16f433bf2
Diffstat (limited to 'cmd')
-rw-r--r-- | cmd/soong_build/main.go | 3 | ||||
-rw-r--r-- | cmd/soong_build/writedocs.go | 262 |
2 files changed, 184 insertions, 81 deletions
diff --git a/cmd/soong_build/main.go b/cmd/soong_build/main.go index 1f6002ee..41c7d46c 100644 --- a/cmd/soong_build/main.go +++ b/cmd/soong_build/main.go @@ -75,8 +75,7 @@ func main() { bootstrap.Main(ctx.Context, configuration, configuration.ConfigFileName, configuration.ProductVariablesFileName) if docFile != "" { - err := writeDocs(ctx, docFile) - if err != nil { + if err := writeDocs(ctx, docFile); err != nil { fmt.Fprintf(os.Stderr, "%s", err) os.Exit(1) } diff --git a/cmd/soong_build/writedocs.go b/cmd/soong_build/writedocs.go index 8f86b332..74c854a3 100644 --- a/cmd/soong_build/writedocs.go +++ b/cmd/soong_build/writedocs.go @@ -26,7 +26,25 @@ import ( "github.com/google/blueprint/bootstrap/bpdoc" ) -func writeDocs(ctx *android.Context, filename string) error { +type moduleTypeTemplateData struct { + Name string + Synopsis string + Properties []bpdoc.Property +} + +// The properties in this map are displayed first, according to their rank. +// TODO(jungjw): consider providing module type-dependent ranking +var propertyRank = map[string]int{ + "name": 0, + "src": 1, + "srcs": 2, + "defautls": 3, + "host_supported": 4, + "device_supported": 5, +} + +// For each module type, extract its documentation and convert it to the template data. +func moduleTypeDocsToTemplates(ctx *android.Context) ([]moduleTypeTemplateData, error) { moduleTypeFactories := android.ModuleTypeFactories() bpModuleTypeFactories := make(map[string]reflect.Value) for moduleType, factory := range moduleTypeFactories { @@ -35,39 +53,83 @@ func writeDocs(ctx *android.Context, filename string) error { packages, err := bootstrap.ModuleTypeDocs(ctx.Context, bpModuleTypeFactories) if err != nil { - return err + return []moduleTypeTemplateData{}, err } - - buf := &bytes.Buffer{} - var moduleTypeList []*bpdoc.ModuleType for _, pkg := range packages { moduleTypeList = append(moduleTypeList, pkg.ModuleTypes...) } - sort.Slice(moduleTypeList, func(i, j int) bool { return moduleTypeList[i].Name < moduleTypeList[j].Name }) - unique := 0 + result := make([]moduleTypeTemplateData, 0) - tmpl, err := template.New("file").Funcs(map[string]interface{}{ - "unique": func() int { - unique++ - return unique - }}).Parse(fileTemplate) - if err != nil { - return err + // Combine properties from all PropertyStruct's and reorder them -- first the ones + // with rank, then the rest of the properties in alphabetic order. + for _, m := range moduleTypeList { + item := moduleTypeTemplateData{ + Name: m.Name, + Synopsis: m.Text, + Properties: make([]bpdoc.Property, 0), + } + props := make([]bpdoc.Property, 0) + for _, propStruct := range m.PropertyStructs { + props = append(props, propStruct.Properties...) + } + sort.Slice(props, func(i, j int) bool { + if rankI, ok := propertyRank[props[i].Name]; ok { + if rankJ, ok := propertyRank[props[j].Name]; ok { + return rankI < rankJ + } else { + return true + } + } + if _, ok := propertyRank[props[j].Name]; ok { + return false + } + return props[i].Name < props[j].Name + }) + // Eliminate top-level duplicates. TODO(jungjw): improve bpdoc to handle this. + previousPropertyName := "" + for _, prop := range props { + if prop.Name == previousPropertyName { + oldProp := &item.Properties[len(item.Properties)-1].Properties + bpdoc.CollapseDuplicateProperties(oldProp, &prop.Properties) + } else { + item.Properties = append(item.Properties, prop) + } + previousPropertyName = prop.Name + } + result = append(result, item) } + sort.Slice(result, func(i, j int) bool { return result[i].Name < result[j].Name }) + return result, err +} - err = tmpl.Execute(buf, moduleTypeList) - if err != nil { - return err - } +func writeDocs(ctx *android.Context, filename string) error { + buf := &bytes.Buffer{} - err = ioutil.WriteFile(filename, buf.Bytes(), 0666) + // We need a module name getter/setter function because I couldn't + // find a way to keep it in a variable defined within the template. + currentModuleName := "" + data, err := moduleTypeDocsToTemplates(ctx) if err != nil { return err } - - return nil + tmpl, err := template.New("file").Funcs(map[string]interface{}{ + "setModule": func(moduleName string) string { + currentModuleName = moduleName + return "" + }, + "getModule": func() string { + return currentModuleName + }, + }).Parse(fileTemplate) + if err == nil { + err = tmpl.Execute(buf, data) + } + if err == nil { + err = ioutil.WriteFile(filename, buf.Bytes(), 0666) + } + return err } const ( @@ -75,70 +137,112 @@ const ( <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> +<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.2.1/css/bootstrap.min.css"> +<style> +.accordion,.simple{margin-left:1.5em;text-indent:-1.5em;margin-top:.25em} +.collapsible{border-width:0 0 0 1;margin-left:.25em;padding-left:.25em;border-style:solid; + border-color:grey;display:none;} +span.fixed{display: block; float: left; clear: left; width: 1em;} +ul { + list-style-type: none; + margin: 0; + padding: 0; + width: 30ch; + background-color: #f1f1f1; + position: fixed; + height: 100%; + overflow: auto; +} +li a { + display: block; + color: #000; + padding: 8px 16px; + text-decoration: none; +} + +li a.active { + background-color: #4CAF50; + color: white; +} + +li a:hover:not(.active) { + background-color: #555; + color: white; +} +</style> </head> <body> -<h1>Build Docs</h1> -<div class="panel-group" id="accordion" role="tablist" aria-multiselectable="true"> +{{- /* Fixed sidebar with module types */ -}} +<ul> +<li><h3>Module Types:</h3></li> +{{range $moduleType := .}}<li><a href="#{{$moduleType.Name}}">{{$moduleType.Name}}</a></li> +{{end -}} +</ul> +{{/* Main panel with H1 section per module type */}} +<div style="margin-left:30ch;padding:1px 16px;"> +<H1>Soong Modules Reference</H1> +The latest versions of Android use the Soong build system, which greatly simplifies build +configuration over the previous Make-based system. This site contains the generated reference +files for the Soong build system. +<p> +See the <a href=https://source.android.com/setup/build/build-system>Android Build System</a> +description for an overview of Soong and examples for its use. + +{{range $imodule, $moduleType := .}} + {{setModule $moduleType.Name}} + <p> + <h2 id="{{$moduleType.Name}}">{{$moduleType.Name}}</h2> + {{if $moduleType.Synopsis }}{{$moduleType.Synopsis}}{{else}}<i>Missing synopsis</i>{{end}} + {{- /* Comma-separated list of module attributes' links module attributes */ -}} + <div class="breadcrumb"> + {{range $i,$prop := $moduleType.Properties }} + {{ if gt $i 0 }}, {{end -}} + <a href=#{{getModule}}.{{$prop.Name}}>{{$prop.Name}}</a> + {{- end -}} + </div> + {{- /* Property description */ -}} + {{- template "properties" $moduleType.Properties -}} +{{- end -}} + +{{define "properties" -}} {{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> + {{if .Properties -}} + <div class="accordion" id="{{getModule}}.{{.Name}}"> + <span class="fixed">⊕</span><b>{{.Name}}</b> + {{- range .OtherNames -}}, {{.}}{{- end -}} + </div> + <div class="collapsible"> + {{- .Text}} {{range .OtherTexts}}{{.}}{{end}} + {{template "properties" .Properties -}} </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}} + {{- else -}} + <div class="simple" id="{{getModule}}.{{.Name}}"> + <span class="fixed"> </span><b>{{.Name}} {{range .OtherNames}}, {{.}}{{end -}}</b> + {{- if .Text -}}{{.Text}}{{- end -}} + {{- with .OtherTexts -}}{{.}}{{- end -}}<i>{{.Type}}</i> + {{- if .Default -}}<i>Default: {{.Default}}</i>{{- end -}} </div> - </div> - {{end}} + {{- end}} + {{- end -}} +{{- end -}} + </div> +<script> + accordions = document.getElementsByClassName('accordion'); + for (i=0; i < accordions.length; ++i) { + accordions[i].addEventListener("click", function() { + var panel = this.nextElementSibling; + var child = this.firstElementChild; + if (panel.style.display === "block") { + panel.style.display = "none"; + child.textContent = '\u2295'; + } else { + panel.style.display = "block"; + child.textContent = '\u2296'; + } + }); + } +</script> </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}} ` ) |