blob: de2182fe2877a18b69e06d10f17fc3e11d84e496 [file] [log] [blame]
Colin Crosse87040b2017-12-11 15:52:26 -08001// Copyright 2017 Google Inc. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package main
16
17import (
18 "android/soong/android"
19 "bytes"
20 "html/template"
21 "io/ioutil"
Jaewoong Jung6c296882019-02-20 07:12:30 -080022 "path/filepath"
Colin Cross7089c272019-01-25 22:43:35 -080023 "reflect"
24 "sort"
Colin Crosse87040b2017-12-11 15:52:26 -080025
26 "github.com/google/blueprint/bootstrap"
Colin Cross7089c272019-01-25 22:43:35 -080027 "github.com/google/blueprint/bootstrap/bpdoc"
Colin Crosse87040b2017-12-11 15:52:26 -080028)
29
Jaewoong Jung6c296882019-02-20 07:12:30 -080030type perPackageTemplateData struct {
31 Name string
32 Modules []moduleTypeTemplateData
33}
34
Sasha Smundakff483392019-02-07 12:10:56 -080035type moduleTypeTemplateData struct {
36 Name string
37 Synopsis string
38 Properties []bpdoc.Property
39}
40
41// The properties in this map are displayed first, according to their rank.
42// TODO(jungjw): consider providing module type-dependent ranking
43var propertyRank = map[string]int{
44 "name": 0,
45 "src": 1,
46 "srcs": 2,
47 "defautls": 3,
48 "host_supported": 4,
49 "device_supported": 5,
50}
51
52// For each module type, extract its documentation and convert it to the template data.
Jaewoong Jung6c296882019-02-20 07:12:30 -080053func moduleTypeDocsToTemplates(moduleTypeList []*bpdoc.ModuleType) []moduleTypeTemplateData {
Sasha Smundakff483392019-02-07 12:10:56 -080054 result := make([]moduleTypeTemplateData, 0)
Colin Crosse87040b2017-12-11 15:52:26 -080055
Sasha Smundakff483392019-02-07 12:10:56 -080056 // Combine properties from all PropertyStruct's and reorder them -- first the ones
57 // with rank, then the rest of the properties in alphabetic order.
58 for _, m := range moduleTypeList {
59 item := moduleTypeTemplateData{
60 Name: m.Name,
61 Synopsis: m.Text,
62 Properties: make([]bpdoc.Property, 0),
63 }
64 props := make([]bpdoc.Property, 0)
65 for _, propStruct := range m.PropertyStructs {
66 props = append(props, propStruct.Properties...)
67 }
68 sort.Slice(props, func(i, j int) bool {
69 if rankI, ok := propertyRank[props[i].Name]; ok {
70 if rankJ, ok := propertyRank[props[j].Name]; ok {
71 return rankI < rankJ
72 } else {
73 return true
74 }
75 }
76 if _, ok := propertyRank[props[j].Name]; ok {
77 return false
78 }
79 return props[i].Name < props[j].Name
80 })
81 // Eliminate top-level duplicates. TODO(jungjw): improve bpdoc to handle this.
82 previousPropertyName := ""
83 for _, prop := range props {
84 if prop.Name == previousPropertyName {
85 oldProp := &item.Properties[len(item.Properties)-1].Properties
86 bpdoc.CollapseDuplicateProperties(oldProp, &prop.Properties)
87 } else {
88 item.Properties = append(item.Properties, prop)
89 }
90 previousPropertyName = prop.Name
91 }
92 result = append(result, item)
93 }
94 sort.Slice(result, func(i, j int) bool { return result[i].Name < result[j].Name })
Jaewoong Jung6c296882019-02-20 07:12:30 -080095 return result
Sasha Smundakff483392019-02-07 12:10:56 -080096}
97
98func writeDocs(ctx *android.Context, filename string) error {
Jaewoong Jung6c296882019-02-20 07:12:30 -080099 moduleTypeFactories := android.ModuleTypeFactories()
100 bpModuleTypeFactories := make(map[string]reflect.Value)
101 for moduleType, factory := range moduleTypeFactories {
102 bpModuleTypeFactories[moduleType] = reflect.ValueOf(factory)
103 }
Sasha Smundakff483392019-02-07 12:10:56 -0800104
Jaewoong Jung6c296882019-02-20 07:12:30 -0800105 packages, err := bootstrap.ModuleTypeDocs(ctx.Context, bpModuleTypeFactories)
Sasha Smundakff483392019-02-07 12:10:56 -0800106 if err != nil {
107 return err
108 }
Jaewoong Jung6c296882019-02-20 07:12:30 -0800109
110 // Produce the top-level, package list page first.
111 tmpl, err := template.New("file").Parse(packageListTemplate)
112 if err != nil {
113 return err
114 }
115 buf := &bytes.Buffer{}
Sasha Smundakff483392019-02-07 12:10:56 -0800116 if err == nil {
Jaewoong Jung6c296882019-02-20 07:12:30 -0800117 err = tmpl.Execute(buf, packages)
Colin Crosse87040b2017-12-11 15:52:26 -0800118 }
Sasha Smundakff483392019-02-07 12:10:56 -0800119 if err == nil {
120 err = ioutil.WriteFile(filename, buf.Bytes(), 0666)
Colin Crosse87040b2017-12-11 15:52:26 -0800121 }
Jaewoong Jung6c296882019-02-20 07:12:30 -0800122
123 // Now, produce per-package module lists with detailed information.
124 for _, pkg := range packages {
125 // We need a module name getter/setter function because I couldn't
126 // find a way to keep it in a variable defined within the template.
127 currentModuleName := ""
128 tmpl, err := template.New("file").Funcs(map[string]interface{}{
129 "setModule": func(moduleName string) string {
130 currentModuleName = moduleName
131 return ""
132 },
133 "getModule": func() string {
134 return currentModuleName
135 },
136 }).Parse(perPackageTemplate)
137 if err != nil {
138 return err
139 }
140 buf := &bytes.Buffer{}
141 modules := moduleTypeDocsToTemplates(pkg.ModuleTypes)
142 data := perPackageTemplateData{Name: pkg.Name, Modules: modules}
143 err = tmpl.Execute(buf, data)
144 if err != nil {
145 return err
146 }
147 pkgFileName := filepath.Join(filepath.Dir(filename), pkg.Name+".html")
148 err = ioutil.WriteFile(pkgFileName, buf.Bytes(), 0666)
149 if err != nil {
150 return err
151 }
152 }
Sasha Smundakff483392019-02-07 12:10:56 -0800153 return err
Colin Crosse87040b2017-12-11 15:52:26 -0800154}
155
Jaewoong Jung6c296882019-02-20 07:12:30 -0800156// TODO(jungjw): Consider ordering by name.
Colin Crosse87040b2017-12-11 15:52:26 -0800157const (
Jaewoong Jung6c296882019-02-20 07:12:30 -0800158 packageListTemplate = `
159<html>
160<head>
161<title>Build Docs</title>
162<link rel="stylesheet" href="https://www.gstatic.com/devrel-devsite/vc67ef93e81a468795c57df87eca3f8427d65cbe85f09fbb51c82a12b89aa3d7e/androidsource/css/app.css">
163<style>
164#main {
165 padding: 48px;
166}
167
168table{
169 table-layout: fixed;
170}
171
172td {
173 word-wrap:break-word;
174}
175</style>
176</head>
177<body>
178<div id="main">
179<H1>Soong Modules Reference</H1>
180The latest versions of Android use the Soong build system, which greatly simplifies build
181configuration over the previous Make-based system. This site contains the generated reference
182files for the Soong build system.
183
184<table class="module_types" summary="Table of Soong module types sorted by package">
185 <thead>
186 <tr>
187 <th style="width:20%">Package</th>
188 <th style="width:80%">Module types</th>
189 </tr>
190 </thead>
191 <tbody>
192 {{range $pkg := .}}
193 <tr>
194 <td>{{.Path}}</td>
195 <td>
196 {{range $i, $mod := .ModuleTypes}}{{if $i}}, {{end}}<a href="{{$pkg.Name}}.html#{{$mod.Name}}">{{$mod.Name}}</a>{{end}}
197 </td>
198 </tr>
199 {{end}}
200 </tbody>
201</table>
202</div>
203</body>
204</html>
205`
206)
207
208const (
209 perPackageTemplate = `
Colin Crosse87040b2017-12-11 15:52:26 -0800210<html>
211<head>
212<title>Build Docs</title>
Sasha Smundakff483392019-02-07 12:10:56 -0800213<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.2.1/css/bootstrap.min.css">
214<style>
215.accordion,.simple{margin-left:1.5em;text-indent:-1.5em;margin-top:.25em}
216.collapsible{border-width:0 0 0 1;margin-left:.25em;padding-left:.25em;border-style:solid;
217 border-color:grey;display:none;}
218span.fixed{display: block; float: left; clear: left; width: 1em;}
219ul {
220 list-style-type: none;
221 margin: 0;
222 padding: 0;
223 width: 30ch;
224 background-color: #f1f1f1;
225 position: fixed;
226 height: 100%;
227 overflow: auto;
228}
229li a {
230 display: block;
231 color: #000;
232 padding: 8px 16px;
233 text-decoration: none;
234}
235
236li a.active {
237 background-color: #4CAF50;
238 color: white;
239}
240
241li a:hover:not(.active) {
242 background-color: #555;
243 color: white;
244}
245</style>
Colin Crosse87040b2017-12-11 15:52:26 -0800246</head>
247<body>
Sasha Smundakff483392019-02-07 12:10:56 -0800248{{- /* Fixed sidebar with module types */ -}}
249<ul>
Jaewoong Jung6c296882019-02-20 07:12:30 -0800250<li><h3>{{.Name}} package</h3></li>
251{{range $moduleType := .Modules}}<li><a href="#{{$moduleType.Name}}">{{$moduleType.Name}}</a></li>
Sasha Smundakff483392019-02-07 12:10:56 -0800252{{end -}}
253</ul>
254{{/* Main panel with H1 section per module type */}}
255<div style="margin-left:30ch;padding:1px 16px;">
Jaewoong Jung6c296882019-02-20 07:12:30 -0800256{{range $moduleType := .Modules}}
Sasha Smundakff483392019-02-07 12:10:56 -0800257 {{setModule $moduleType.Name}}
258 <p>
259 <h2 id="{{$moduleType.Name}}">{{$moduleType.Name}}</h2>
260 {{if $moduleType.Synopsis }}{{$moduleType.Synopsis}}{{else}}<i>Missing synopsis</i>{{end}}
261 {{- /* Comma-separated list of module attributes' links module attributes */ -}}
262 <div class="breadcrumb">
263 {{range $i,$prop := $moduleType.Properties }}
264 {{ if gt $i 0 }},&nbsp;{{end -}}
265 <a href=#{{getModule}}.{{$prop.Name}}>{{$prop.Name}}</a>
266 {{- end -}}
Colin Crosse87040b2017-12-11 15:52:26 -0800267 </div>
Sasha Smundakff483392019-02-07 12:10:56 -0800268 {{- /* Property description */ -}}
269 {{- template "properties" $moduleType.Properties -}}
270{{- end -}}
271
272{{define "properties" -}}
273 {{range .}}
274 {{if .Properties -}}
275 <div class="accordion" id="{{getModule}}.{{.Name}}">
276 <span class="fixed">&#x2295</span><b>{{.Name}}</b>
277 {{- range .OtherNames -}}, {{.}}{{- end -}}
278 </div>
279 <div class="collapsible">
280 {{- .Text}} {{range .OtherTexts}}{{.}}{{end}}
281 {{template "properties" .Properties -}}
282 </div>
283 {{- else -}}
284 <div class="simple" id="{{getModule}}.{{.Name}}">
285 <span class="fixed">&nbsp;</span><b>{{.Name}} {{range .OtherNames}}, {{.}}{{end -}}</b>
286 {{- if .Text -}}{{.Text}}{{- end -}}
287 {{- with .OtherTexts -}}{{.}}{{- end -}}<i>{{.Type}}</i>
288 {{- if .Default -}}<i>Default: {{.Default}}</i>{{- end -}}
289 </div>
290 {{- end}}
291 {{- end -}}
292{{- end -}}
Sasha Smundakff483392019-02-07 12:10:56 -0800293</div>
294<script>
295 accordions = document.getElementsByClassName('accordion');
296 for (i=0; i < accordions.length; ++i) {
297 accordions[i].addEventListener("click", function() {
298 var panel = this.nextElementSibling;
299 var child = this.firstElementChild;
300 if (panel.style.display === "block") {
301 panel.style.display = "none";
302 child.textContent = '\u2295';
303 } else {
304 panel.style.display = "block";
305 child.textContent = '\u2296';
306 }
307 });
308 }
309</script>
310</body>
Colin Crosse87040b2017-12-11 15:52:26 -0800311`
312)