blob: d011e77fe67d1892c1a354eb8eccafd46b6658f0 [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
Jaewoong Jung238be382019-03-11 14:35:41 -070037 Synopsis template.HTML
Sasha Smundakff483392019-02-07 12:10:56 -080038 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.
Jaewoong Jung90e11552019-02-22 13:44:38 -0800111 tmpl := template.Must(template.Must(template.New("file").Parse(packageListTemplate)).Parse(copyBaseUrl))
Jaewoong Jung6c296882019-02-20 07:12:30 -0800112 buf := &bytes.Buffer{}
Jaewoong Jung90e11552019-02-22 13:44:38 -0800113 err = tmpl.Execute(buf, packages)
Sasha Smundakff483392019-02-07 12:10:56 -0800114 if err == nil {
115 err = ioutil.WriteFile(filename, buf.Bytes(), 0666)
Colin Crosse87040b2017-12-11 15:52:26 -0800116 }
Jaewoong Jung6c296882019-02-20 07:12:30 -0800117
118 // Now, produce per-package module lists with detailed information.
119 for _, pkg := range packages {
120 // We need a module name getter/setter function because I couldn't
121 // find a way to keep it in a variable defined within the template.
122 currentModuleName := ""
Jaewoong Jung90e11552019-02-22 13:44:38 -0800123 tmpl := template.Must(
124 template.Must(template.New("file").Funcs(map[string]interface{}{
125 "setModule": func(moduleName string) string {
126 currentModuleName = moduleName
127 return ""
128 },
129 "getModule": func() string {
130 return currentModuleName
131 },
132 }).Parse(perPackageTemplate)).Parse(copyBaseUrl))
Jaewoong Jung6c296882019-02-20 07:12:30 -0800133 buf := &bytes.Buffer{}
134 modules := moduleTypeDocsToTemplates(pkg.ModuleTypes)
135 data := perPackageTemplateData{Name: pkg.Name, Modules: modules}
136 err = tmpl.Execute(buf, data)
137 if err != nil {
138 return err
139 }
140 pkgFileName := filepath.Join(filepath.Dir(filename), pkg.Name+".html")
141 err = ioutil.WriteFile(pkgFileName, buf.Bytes(), 0666)
142 if err != nil {
143 return err
144 }
145 }
Sasha Smundakff483392019-02-07 12:10:56 -0800146 return err
Colin Crosse87040b2017-12-11 15:52:26 -0800147}
148
Jaewoong Jung6c296882019-02-20 07:12:30 -0800149// TODO(jungjw): Consider ordering by name.
Colin Crosse87040b2017-12-11 15:52:26 -0800150const (
Jaewoong Jung6c296882019-02-20 07:12:30 -0800151 packageListTemplate = `
152<html>
153<head>
154<title>Build Docs</title>
155<link rel="stylesheet" href="https://www.gstatic.com/devrel-devsite/vc67ef93e81a468795c57df87eca3f8427d65cbe85f09fbb51c82a12b89aa3d7e/androidsource/css/app.css">
156<style>
157#main {
158 padding: 48px;
159}
160
161table{
162 table-layout: fixed;
163}
164
165td {
166 word-wrap:break-word;
167}
168</style>
Jaewoong Jung90e11552019-02-22 13:44:38 -0800169{{template "copyBaseUrl"}}
Jaewoong Jung6c296882019-02-20 07:12:30 -0800170</head>
171<body>
172<div id="main">
173<H1>Soong Modules Reference</H1>
174The latest versions of Android use the Soong build system, which greatly simplifies build
175configuration over the previous Make-based system. This site contains the generated reference
176files for the Soong build system.
177
178<table class="module_types" summary="Table of Soong module types sorted by package">
179 <thead>
180 <tr>
181 <th style="width:20%">Package</th>
182 <th style="width:80%">Module types</th>
183 </tr>
184 </thead>
185 <tbody>
186 {{range $pkg := .}}
187 <tr>
188 <td>{{.Path}}</td>
189 <td>
190 {{range $i, $mod := .ModuleTypes}}{{if $i}}, {{end}}<a href="{{$pkg.Name}}.html#{{$mod.Name}}">{{$mod.Name}}</a>{{end}}
191 </td>
192 </tr>
193 {{end}}
194 </tbody>
195</table>
196</div>
197</body>
198</html>
199`
Jaewoong Jung6c296882019-02-20 07:12:30 -0800200
Jaewoong Jung6c296882019-02-20 07:12:30 -0800201 perPackageTemplate = `
Colin Crosse87040b2017-12-11 15:52:26 -0800202<html>
203<head>
204<title>Build Docs</title>
Sasha Smundakff483392019-02-07 12:10:56 -0800205<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.2.1/css/bootstrap.min.css">
206<style>
207.accordion,.simple{margin-left:1.5em;text-indent:-1.5em;margin-top:.25em}
208.collapsible{border-width:0 0 0 1;margin-left:.25em;padding-left:.25em;border-style:solid;
209 border-color:grey;display:none;}
210span.fixed{display: block; float: left; clear: left; width: 1em;}
211ul {
212 list-style-type: none;
213 margin: 0;
214 padding: 0;
215 width: 30ch;
216 background-color: #f1f1f1;
217 position: fixed;
218 height: 100%;
219 overflow: auto;
220}
221li a {
222 display: block;
223 color: #000;
224 padding: 8px 16px;
225 text-decoration: none;
226}
227
228li a.active {
229 background-color: #4CAF50;
230 color: white;
231}
232
233li a:hover:not(.active) {
234 background-color: #555;
235 color: white;
236}
237</style>
Jaewoong Jung90e11552019-02-22 13:44:38 -0800238{{template "copyBaseUrl"}}
Colin Crosse87040b2017-12-11 15:52:26 -0800239</head>
240<body>
Sasha Smundakff483392019-02-07 12:10:56 -0800241{{- /* Fixed sidebar with module types */ -}}
242<ul>
Jaewoong Jung6c296882019-02-20 07:12:30 -0800243<li><h3>{{.Name}} package</h3></li>
Jaewoong Jungd10f4842019-02-27 11:15:00 -0800244{{range $moduleType := .Modules}}<li><a href="{{$.Name}}.html#{{$moduleType.Name}}">{{$moduleType.Name}}</a></li>
Sasha Smundakff483392019-02-07 12:10:56 -0800245{{end -}}
246</ul>
247{{/* Main panel with H1 section per module type */}}
248<div style="margin-left:30ch;padding:1px 16px;">
Jaewoong Jung6c296882019-02-20 07:12:30 -0800249{{range $moduleType := .Modules}}
Sasha Smundakff483392019-02-07 12:10:56 -0800250 {{setModule $moduleType.Name}}
251 <p>
252 <h2 id="{{$moduleType.Name}}">{{$moduleType.Name}}</h2>
253 {{if $moduleType.Synopsis }}{{$moduleType.Synopsis}}{{else}}<i>Missing synopsis</i>{{end}}
254 {{- /* Comma-separated list of module attributes' links module attributes */ -}}
255 <div class="breadcrumb">
256 {{range $i,$prop := $moduleType.Properties }}
257 {{ if gt $i 0 }},&nbsp;{{end -}}
Jaewoong Jungd10f4842019-02-27 11:15:00 -0800258 <a href={{$.Name}}.html#{{getModule}}.{{$prop.Name}}>{{$prop.Name}}</a>
Sasha Smundakff483392019-02-07 12:10:56 -0800259 {{- end -}}
Colin Crosse87040b2017-12-11 15:52:26 -0800260 </div>
Sasha Smundakff483392019-02-07 12:10:56 -0800261 {{- /* Property description */ -}}
262 {{- template "properties" $moduleType.Properties -}}
263{{- end -}}
264
265{{define "properties" -}}
266 {{range .}}
267 {{if .Properties -}}
268 <div class="accordion" id="{{getModule}}.{{.Name}}">
269 <span class="fixed">&#x2295</span><b>{{.Name}}</b>
270 {{- range .OtherNames -}}, {{.}}{{- end -}}
271 </div>
272 <div class="collapsible">
273 {{- .Text}} {{range .OtherTexts}}{{.}}{{end}}
274 {{template "properties" .Properties -}}
275 </div>
276 {{- else -}}
277 <div class="simple" id="{{getModule}}.{{.Name}}">
278 <span class="fixed">&nbsp;</span><b>{{.Name}} {{range .OtherNames}}, {{.}}{{end -}}</b>
Jaewoong Jung12c02a62019-03-12 13:28:25 -0700279 <i>{{.Type}}</i>
280 {{- if .Text -}}{{if ne .Text "\n"}}, {{end}}{{.Text}}{{- end -}}
281 {{- with .OtherTexts -}}{{.}}{{- end -}}
Sasha Smundakff483392019-02-07 12:10:56 -0800282 {{- if .Default -}}<i>Default: {{.Default}}</i>{{- end -}}
283 </div>
284 {{- end}}
285 {{- end -}}
286{{- end -}}
Sasha Smundakff483392019-02-07 12:10:56 -0800287</div>
288<script>
289 accordions = document.getElementsByClassName('accordion');
290 for (i=0; i < accordions.length; ++i) {
291 accordions[i].addEventListener("click", function() {
292 var panel = this.nextElementSibling;
293 var child = this.firstElementChild;
294 if (panel.style.display === "block") {
295 panel.style.display = "none";
296 child.textContent = '\u2295';
297 } else {
298 panel.style.display = "block";
299 child.textContent = '\u2296';
300 }
301 });
302 }
303</script>
304</body>
Colin Crosse87040b2017-12-11 15:52:26 -0800305`
Jaewoong Jung90e11552019-02-22 13:44:38 -0800306
307 copyBaseUrl = `
308{{define "copyBaseUrl"}}
309<script type="text/javascript">
310window.addEventListener('message', (e) => {
311 if (e != null && e.data != null && e.data.type === "SET_BASE" && e.data.base != null) {
312 const existingBase = document.querySelector('base');
313 if (existingBase != null) {
314 existingBase.parentElement.removeChild(existingBase);
315 }
316
317 const base = document.createElement('base');
318 base.setAttribute('href', e.data.base);
319 document.head.appendChild(base);
320 }
321});
322</script>
323{{end}}
324`
Colin Crosse87040b2017-12-11 15:52:26 -0800325)