blob: b7c260c6a4d7ef94bdbee5ec0690d58c7c276126 [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 "sort"
Colin Crosse87040b2017-12-11 15:52:26 -080024
25 "github.com/google/blueprint/bootstrap"
Colin Cross7089c272019-01-25 22:43:35 -080026 "github.com/google/blueprint/bootstrap/bpdoc"
Colin Crosse87040b2017-12-11 15:52:26 -080027)
28
Jaewoong Jung6c296882019-02-20 07:12:30 -080029type perPackageTemplateData struct {
30 Name string
31 Modules []moduleTypeTemplateData
32}
33
Sasha Smundakff483392019-02-07 12:10:56 -080034type moduleTypeTemplateData struct {
35 Name string
Jaewoong Jung238be382019-03-11 14:35:41 -070036 Synopsis template.HTML
Sasha Smundakff483392019-02-07 12:10:56 -080037 Properties []bpdoc.Property
38}
39
40// The properties in this map are displayed first, according to their rank.
41// TODO(jungjw): consider providing module type-dependent ranking
42var propertyRank = map[string]int{
43 "name": 0,
44 "src": 1,
45 "srcs": 2,
Liz Kammere7211dd2020-11-02 22:01:10 +000046 "exclude_srcs": 3,
47 "defaults": 4,
48 "host_supported": 5,
49 "device_supported": 6,
Sasha Smundakff483392019-02-07 12:10:56 -080050}
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
Lukacs T. Berki89e9a162021-03-12 08:31:32 +010098func getPackages(ctx *android.Context, config interface{}) ([]*bpdoc.Package, error) {
Colin Cross9aed5bc2020-12-28 15:15:34 -080099 moduleTypeFactories := android.ModuleTypeFactoriesForDocs()
Lukacs T. Berki89e9a162021-03-12 08:31:32 +0100100 return bootstrap.ModuleTypeDocs(ctx.Context, config, moduleTypeFactories)
Jingwen Chend8004ef2020-08-27 09:40:43 +0000101}
Sasha Smundakff483392019-02-07 12:10:56 -0800102
Lukacs T. Berki89e9a162021-03-12 08:31:32 +0100103func writeDocs(ctx *android.Context, config interface{}, filename string) error {
104 packages, err := getPackages(ctx, config)
Sasha Smundakff483392019-02-07 12:10:56 -0800105 if err != nil {
106 return err
107 }
Jaewoong Jung6c296882019-02-20 07:12:30 -0800108
109 // Produce the top-level, package list page first.
Jaewoong Jung90e11552019-02-22 13:44:38 -0800110 tmpl := template.Must(template.Must(template.New("file").Parse(packageListTemplate)).Parse(copyBaseUrl))
Jaewoong Jung6c296882019-02-20 07:12:30 -0800111 buf := &bytes.Buffer{}
Jaewoong Jung90e11552019-02-22 13:44:38 -0800112 err = tmpl.Execute(buf, packages)
Sasha Smundakff483392019-02-07 12:10:56 -0800113 if err == nil {
114 err = ioutil.WriteFile(filename, buf.Bytes(), 0666)
Colin Crosse87040b2017-12-11 15:52:26 -0800115 }
Jaewoong Jung6c296882019-02-20 07:12:30 -0800116
Andrew Walbran75bba112021-05-07 16:31:30 +0000117 // Now, produce per-package module lists with detailed information, and a list
118 // of keywords.
119 keywordsTmpl := template.Must(template.New("file").Parse(keywordsTemplate))
120 keywordsBuf := &bytes.Buffer{}
Jaewoong Jung6c296882019-02-20 07:12:30 -0800121 for _, pkg := range packages {
122 // We need a module name getter/setter function because I couldn't
123 // find a way to keep it in a variable defined within the template.
124 currentModuleName := ""
Jaewoong Jung90e11552019-02-22 13:44:38 -0800125 tmpl := template.Must(
126 template.Must(template.New("file").Funcs(map[string]interface{}{
127 "setModule": func(moduleName string) string {
128 currentModuleName = moduleName
129 return ""
130 },
131 "getModule": func() string {
132 return currentModuleName
133 },
134 }).Parse(perPackageTemplate)).Parse(copyBaseUrl))
Jaewoong Jung6c296882019-02-20 07:12:30 -0800135 buf := &bytes.Buffer{}
136 modules := moduleTypeDocsToTemplates(pkg.ModuleTypes)
137 data := perPackageTemplateData{Name: pkg.Name, Modules: modules}
138 err = tmpl.Execute(buf, data)
139 if err != nil {
140 return err
141 }
142 pkgFileName := filepath.Join(filepath.Dir(filename), pkg.Name+".html")
143 err = ioutil.WriteFile(pkgFileName, buf.Bytes(), 0666)
144 if err != nil {
145 return err
146 }
Andrew Walbran75bba112021-05-07 16:31:30 +0000147 err = keywordsTmpl.Execute(keywordsBuf, data)
148 if err != nil {
149 return err
150 }
Jaewoong Jung6c296882019-02-20 07:12:30 -0800151 }
Andrew Walbran75bba112021-05-07 16:31:30 +0000152
153 // Write out list of keywords. This includes all module and property names, which is useful for
154 // building syntax highlighters.
155 keywordsFilename := filepath.Join(filepath.Dir(filename), "keywords.txt")
156 err = ioutil.WriteFile(keywordsFilename, keywordsBuf.Bytes(), 0666)
157
Sasha Smundakff483392019-02-07 12:10:56 -0800158 return err
Colin Crosse87040b2017-12-11 15:52:26 -0800159}
160
Jaewoong Jung6c296882019-02-20 07:12:30 -0800161// TODO(jungjw): Consider ordering by name.
Colin Crosse87040b2017-12-11 15:52:26 -0800162const (
Jaewoong Jung6c296882019-02-20 07:12:30 -0800163 packageListTemplate = `
164<html>
165<head>
166<title>Build Docs</title>
Jaewoong Jung6c296882019-02-20 07:12:30 -0800167<style>
168#main {
169 padding: 48px;
170}
171
172table{
173 table-layout: fixed;
174}
175
176td {
177 word-wrap:break-word;
178}
Jaewoong Jung5f867c02019-04-15 15:09:16 -0700179
180/* The following entries are copied from source.android.com's css file. */
181td,td code {
182 color: #202124
183}
184
185th,th code {
186 color: #fff;
187 font: 500 16px/24px Roboto,sans-serif
188}
189
190td,table.responsive tr:not(.alt) td td:first-child,table.responsive td tr:not(.alt) td:first-child {
191 background: rgba(255,255,255,.95);
192 vertical-align: top
193}
194
195td,td code {
196 padding: 7px 8px 8px
197}
198
199tr {
200 border: 0;
201 background: #78909c;
202 border-top: 1px solid #cfd8dc
203}
204
205th,td {
206 border: 0;
207 margin: 0;
208 text-align: left
209}
210
211th {
212 height: 48px;
213 padding: 8px;
214 vertical-align: middle
215}
216
217table {
218 border: 0;
219 border-collapse: collapse;
220 border-spacing: 0;
221 font: 14px/20px Roboto,sans-serif;
222 margin: 16px 0;
223 width: 100%
224}
225
226h1 {
227 color: #80868b;
228 font: 300 34px/40px Roboto,sans-serif;
229 letter-spacing: -0.01em;
230 margin: 40px 0 20px
231}
232
233h1,h2,h3,h4,h5,h6 {
234 overflow: hidden;
235 padding: 0;
236 text-overflow: ellipsis
237}
238
239:link,:visited {
240 color: #039be5;
241 outline: 0;
242 text-decoration: none
243}
244
245body,html {
246 color: #202124;
247 font: 400 16px/24px Roboto,sans-serif;
248 -moz-osx-font-smoothing: grayscale;
249 -webkit-font-smoothing: antialiased;
250 height: 100%;
251 margin: 0;
252 -webkit-text-size-adjust: 100%;
253 -moz-text-size-adjust: 100%;
254 -ms-text-size-adjust: 100%;
255 text-size-adjust: 100%
256}
257
258html {
259 -webkit-box-sizing: border-box;
260 box-sizing: border-box
261}
262
263*,*::before,*::after {
264 -webkit-box-sizing: inherit;
265 box-sizing: inherit
266}
267
268body,div,dl,dd,form,img,input,figure,menu {
269 margin: 0;
270 padding: 0
271}
Jaewoong Jung6c296882019-02-20 07:12:30 -0800272</style>
Jaewoong Jung90e11552019-02-22 13:44:38 -0800273{{template "copyBaseUrl"}}
Jaewoong Jung6c296882019-02-20 07:12:30 -0800274</head>
275<body>
276<div id="main">
277<H1>Soong Modules Reference</H1>
278The latest versions of Android use the Soong build system, which greatly simplifies build
279configuration over the previous Make-based system. This site contains the generated reference
280files for the Soong build system.
281
282<table class="module_types" summary="Table of Soong module types sorted by package">
283 <thead>
284 <tr>
285 <th style="width:20%">Package</th>
286 <th style="width:80%">Module types</th>
287 </tr>
288 </thead>
289 <tbody>
290 {{range $pkg := .}}
291 <tr>
292 <td>{{.Path}}</td>
293 <td>
294 {{range $i, $mod := .ModuleTypes}}{{if $i}}, {{end}}<a href="{{$pkg.Name}}.html#{{$mod.Name}}">{{$mod.Name}}</a>{{end}}
295 </td>
296 </tr>
297 {{end}}
298 </tbody>
299</table>
300</div>
301</body>
302</html>
303`
Jaewoong Jung6c296882019-02-20 07:12:30 -0800304
Jaewoong Jung6c296882019-02-20 07:12:30 -0800305 perPackageTemplate = `
Colin Crosse87040b2017-12-11 15:52:26 -0800306<html>
307<head>
308<title>Build Docs</title>
Sasha Smundakff483392019-02-07 12:10:56 -0800309<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.2.1/css/bootstrap.min.css">
310<style>
311.accordion,.simple{margin-left:1.5em;text-indent:-1.5em;margin-top:.25em}
312.collapsible{border-width:0 0 0 1;margin-left:.25em;padding-left:.25em;border-style:solid;
313 border-color:grey;display:none;}
314span.fixed{display: block; float: left; clear: left; width: 1em;}
315ul {
316 list-style-type: none;
317 margin: 0;
318 padding: 0;
319 width: 30ch;
320 background-color: #f1f1f1;
321 position: fixed;
322 height: 100%;
323 overflow: auto;
324}
325li a {
326 display: block;
327 color: #000;
328 padding: 8px 16px;
329 text-decoration: none;
330}
331
332li a.active {
333 background-color: #4CAF50;
334 color: white;
335}
336
337li a:hover:not(.active) {
338 background-color: #555;
339 color: white;
340}
341</style>
Jaewoong Jung90e11552019-02-22 13:44:38 -0800342{{template "copyBaseUrl"}}
Colin Crosse87040b2017-12-11 15:52:26 -0800343</head>
344<body>
Sasha Smundakff483392019-02-07 12:10:56 -0800345{{- /* Fixed sidebar with module types */ -}}
346<ul>
Jaewoong Jung6c296882019-02-20 07:12:30 -0800347<li><h3>{{.Name}} package</h3></li>
Jaewoong Jungd10f4842019-02-27 11:15:00 -0800348{{range $moduleType := .Modules}}<li><a href="{{$.Name}}.html#{{$moduleType.Name}}">{{$moduleType.Name}}</a></li>
Sasha Smundakff483392019-02-07 12:10:56 -0800349{{end -}}
350</ul>
351{{/* Main panel with H1 section per module type */}}
352<div style="margin-left:30ch;padding:1px 16px;">
Jaewoong Jung6c296882019-02-20 07:12:30 -0800353{{range $moduleType := .Modules}}
Sasha Smundakff483392019-02-07 12:10:56 -0800354 {{setModule $moduleType.Name}}
355 <p>
356 <h2 id="{{$moduleType.Name}}">{{$moduleType.Name}}</h2>
357 {{if $moduleType.Synopsis }}{{$moduleType.Synopsis}}{{else}}<i>Missing synopsis</i>{{end}}
358 {{- /* Comma-separated list of module attributes' links module attributes */ -}}
359 <div class="breadcrumb">
360 {{range $i,$prop := $moduleType.Properties }}
361 {{ if gt $i 0 }},&nbsp;{{end -}}
Jaewoong Jungd10f4842019-02-27 11:15:00 -0800362 <a href={{$.Name}}.html#{{getModule}}.{{$prop.Name}}>{{$prop.Name}}</a>
Sasha Smundakff483392019-02-07 12:10:56 -0800363 {{- end -}}
Colin Crosse87040b2017-12-11 15:52:26 -0800364 </div>
Sasha Smundakff483392019-02-07 12:10:56 -0800365 {{- /* Property description */ -}}
366 {{- template "properties" $moduleType.Properties -}}
367{{- end -}}
368
369{{define "properties" -}}
370 {{range .}}
371 {{if .Properties -}}
372 <div class="accordion" id="{{getModule}}.{{.Name}}">
373 <span class="fixed">&#x2295</span><b>{{.Name}}</b>
374 {{- range .OtherNames -}}, {{.}}{{- end -}}
375 </div>
376 <div class="collapsible">
377 {{- .Text}} {{range .OtherTexts}}{{.}}{{end}}
378 {{template "properties" .Properties -}}
379 </div>
380 {{- else -}}
381 <div class="simple" id="{{getModule}}.{{.Name}}">
382 <span class="fixed">&nbsp;</span><b>{{.Name}} {{range .OtherNames}}, {{.}}{{end -}}</b>
Jaewoong Jung12c02a62019-03-12 13:28:25 -0700383 <i>{{.Type}}</i>
384 {{- if .Text -}}{{if ne .Text "\n"}}, {{end}}{{.Text}}{{- end -}}
385 {{- with .OtherTexts -}}{{.}}{{- end -}}
Sasha Smundakff483392019-02-07 12:10:56 -0800386 {{- if .Default -}}<i>Default: {{.Default}}</i>{{- end -}}
387 </div>
388 {{- end}}
389 {{- end -}}
390{{- end -}}
Sasha Smundakff483392019-02-07 12:10:56 -0800391</div>
392<script>
393 accordions = document.getElementsByClassName('accordion');
394 for (i=0; i < accordions.length; ++i) {
395 accordions[i].addEventListener("click", function() {
396 var panel = this.nextElementSibling;
397 var child = this.firstElementChild;
398 if (panel.style.display === "block") {
399 panel.style.display = "none";
400 child.textContent = '\u2295';
401 } else {
402 panel.style.display = "block";
403 child.textContent = '\u2296';
404 }
405 });
406 }
407</script>
408</body>
Colin Crosse87040b2017-12-11 15:52:26 -0800409`
Jaewoong Jung90e11552019-02-22 13:44:38 -0800410
411 copyBaseUrl = `
412{{define "copyBaseUrl"}}
413<script type="text/javascript">
414window.addEventListener('message', (e) => {
415 if (e != null && e.data != null && e.data.type === "SET_BASE" && e.data.base != null) {
416 const existingBase = document.querySelector('base');
417 if (existingBase != null) {
418 existingBase.parentElement.removeChild(existingBase);
419 }
420
421 const base = document.createElement('base');
422 base.setAttribute('href', e.data.base);
423 document.head.appendChild(base);
424 }
425});
426</script>
427{{end}}
428`
Andrew Walbran75bba112021-05-07 16:31:30 +0000429
430 keywordsTemplate = `
431{{range $moduleType := .Modules}}{{$moduleType.Name}}:{{range $property := $moduleType.Properties}}{{$property.Name}},{{end}}
432{{end}}
433`
Colin Crosse87040b2017-12-11 15:52:26 -0800434)