1package bootstrap 2 3import ( 4 "bytes" 5 "fmt" 6 "html/template" 7 "io/ioutil" 8 "path/filepath" 9 "reflect" 10 11 "github.com/google/blueprint" 12 "github.com/google/blueprint/bootstrap/bpdoc" 13 "github.com/google/blueprint/pathtools" 14) 15 16// ModuleTypeDocs returns a list of bpdoc.ModuleType objects that contain information relevant 17// to generating documentation for module types supported by the primary builder. 18func ModuleTypeDocs(ctx *blueprint.Context, factories map[string]reflect.Value) ([]*bpdoc.Package, error) { 19 // Find the module that's marked as the "primary builder", which means it's 20 // creating the binary that we'll use to generate the non-bootstrap 21 // build.ninja file. 22 var primaryBuilders []*goBinary 23 var minibp *goBinary 24 ctx.VisitAllModulesIf(isBootstrapBinaryModule, 25 func(module blueprint.Module) { 26 binaryModule := module.(*goBinary) 27 if binaryModule.properties.PrimaryBuilder { 28 primaryBuilders = append(primaryBuilders, binaryModule) 29 } 30 if ctx.ModuleName(binaryModule) == "minibp" { 31 minibp = binaryModule 32 } 33 }) 34 35 if minibp == nil { 36 panic("missing minibp") 37 } 38 39 var primaryBuilder *goBinary 40 switch len(primaryBuilders) { 41 case 0: 42 // If there's no primary builder module then that means we'll use minibp 43 // as the primary builder. 44 primaryBuilder = minibp 45 46 case 1: 47 primaryBuilder = primaryBuilders[0] 48 49 default: 50 return nil, fmt.Errorf("multiple primary builder modules present") 51 } 52 53 pkgFiles := make(map[string][]string) 54 ctx.VisitDepsDepthFirst(primaryBuilder, func(module blueprint.Module) { 55 switch m := module.(type) { 56 case (*goPackage): 57 pkgFiles[m.properties.PkgPath] = pathtools.PrefixPaths(m.properties.Srcs, 58 filepath.Join(SrcDir, ctx.ModuleDir(m))) 59 default: 60 panic(fmt.Errorf("unknown dependency type %T", module)) 61 } 62 }) 63 64 mergedFactories := make(map[string]reflect.Value) 65 for moduleType, factory := range factories { 66 mergedFactories[moduleType] = factory 67 } 68 69 for moduleType, factory := range ctx.ModuleTypeFactories() { 70 if _, exists := mergedFactories[moduleType]; !exists { 71 mergedFactories[moduleType] = reflect.ValueOf(factory) 72 } 73 } 74 75 return bpdoc.AllPackages(pkgFiles, mergedFactories, ctx.ModuleTypePropertyStructs()) 76} 77 78func writeDocs(ctx *blueprint.Context, filename string) error { 79 moduleTypeList, err := ModuleTypeDocs(ctx, nil) 80 if err != nil { 81 return err 82 } 83 84 buf := &bytes.Buffer{} 85 86 unique := 0 87 88 tmpl, err := template.New("file").Funcs(map[string]interface{}{ 89 "unique": func() int { 90 unique++ 91 return unique 92 }}).Parse(fileTemplate) 93 if err != nil { 94 return err 95 } 96 97 err = tmpl.Execute(buf, moduleTypeList) 98 if err != nil { 99 return err 100 } 101 102 err = ioutil.WriteFile(filename, buf.Bytes(), 0666) 103 if err != nil { 104 return err 105 } 106 107 return nil 108} 109 110const ( 111 fileTemplate = ` 112<html> 113<head> 114<title>Build Docs</title> 115<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css"> 116<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script> 117<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script> 118</head> 119<body> 120<h1>Build Docs</h1> 121<div class="panel-group" id="accordion" role="tablist" aria-multiselectable="true"> 122 {{range .}} 123 <p>{{.Text}}</p> 124 {{range .ModuleTypes}} 125 {{ $collapseIndex := unique }} 126 <div class="panel panel-default"> 127 <div class="panel-heading" role="tab" id="heading{{$collapseIndex}}"> 128 <h2 class="panel-title"> 129 <a class="collapsed" role="button" data-toggle="collapse" data-parent="#accordion" href="#collapse{{$collapseIndex}}" aria-expanded="false" aria-controls="collapse{{$collapseIndex}}"> 130 {{.Name}} 131 </a> 132 </h2> 133 </div> 134 </div> 135 <div id="collapse{{$collapseIndex}}" class="panel-collapse collapse" role="tabpanel" aria-labelledby="heading{{$collapseIndex}}"> 136 <div class="panel-body"> 137 <p>{{.Text}}</p> 138 {{range .PropertyStructs}} 139 <p>{{.Text}}</p> 140 {{template "properties" .Properties}} 141 {{end}} 142 </div> 143 </div> 144 {{end}} 145 {{end}} 146</div> 147</body> 148</html> 149 150{{define "properties"}} 151 <div class="panel-group" id="accordion" role="tablist" aria-multiselectable="true"> 152 {{range .}} 153 {{$collapseIndex := unique}} 154 {{if .Properties}} 155 <div class="panel panel-default"> 156 <div class="panel-heading" role="tab" id="heading{{$collapseIndex}}"> 157 <h4 class="panel-title"> 158 <a class="collapsed" role="button" data-toggle="collapse" data-parent="#accordion" href="#collapse{{$collapseIndex}}" aria-expanded="false" aria-controls="collapse{{$collapseIndex}}"> 159 {{.Name}}{{range .OtherNames}}, {{.}}{{end}} 160 </a> 161 </h4> 162 </div> 163 </div> 164 <div id="collapse{{$collapseIndex}}" class="panel-collapse collapse" role="tabpanel" aria-labelledby="heading{{$collapseIndex}}"> 165 <div class="panel-body"> 166 <p>{{.Text}}</p> 167 {{range .OtherTexts}}<p>{{.}}</p>{{end}} 168 {{template "properties" .Properties}} 169 </div> 170 </div> 171 {{else}} 172 <div> 173 <h4>{{.Name}}{{range .OtherNames}}, {{.}}{{end}}</h4> 174 <p>{{.Text}}</p> 175 {{range .OtherTexts}}<p>{{.}}</p>{{end}} 176 <p><i>Type: {{.Type}}</i></p> 177 {{if .Default}}<p><i>Default: {{.Default}}</i></p>{{end}} 178 </div> 179 {{end}} 180 {{end}} 181 </div> 182{{end}} 183` 184) 185