Loading cmd/soong_build/main.go +1 −2 Original line number Diff line number Diff line Loading @@ -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) } Loading cmd/soong_build/writedocs.go +183 −79 Original line number Diff line number Diff line Loading @@ -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 { Loading @@ -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 } func writeDocs(ctx *android.Context, filename string) error { buf := &bytes.Buffer{} err = tmpl.Execute(buf, moduleTypeList) // 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 } 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) if err != nil { return err } return nil return err } const ( Loading @@ -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"> {{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}} {{- /* 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> </body> </html> {{- /* Property description */ -}} {{- template "properties" $moduleType.Properties -}} {{- end -}} {{define "properties"}} <div class="panel-group" id="accordion" role="tablist" aria-multiselectable="true"> {{define "properties" -}} {{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> {{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 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}} {{- 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> {{- end}} {{- end -}} {{- end -}} </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}} <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> ` ) Loading
cmd/soong_build/main.go +1 −2 Original line number Diff line number Diff line Loading @@ -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) } Loading
cmd/soong_build/writedocs.go +183 −79 Original line number Diff line number Diff line Loading @@ -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 { Loading @@ -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 } func writeDocs(ctx *android.Context, filename string) error { buf := &bytes.Buffer{} err = tmpl.Execute(buf, moduleTypeList) // 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 } 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) if err != nil { return err } return nil return err } const ( Loading @@ -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"> {{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}} {{- /* 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> </body> </html> {{- /* Property description */ -}} {{- template "properties" $moduleType.Properties -}} {{- end -}} {{define "properties"}} <div class="panel-group" id="accordion" role="tablist" aria-multiselectable="true"> {{define "properties" -}} {{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> {{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 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}} {{- 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> {{- end}} {{- end -}} {{- end -}} </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}} <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> ` )