| // Copyright 2019 The LUCI Authors. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| // Command docgen is the documentation generator. |
| // |
| // It is used by go:generate in "docs" directory. Not really supposed to be |
| // used standalone. |
| package main |
| |
| import ( |
| "flag" |
| "fmt" |
| "io/ioutil" |
| "os" |
| "path/filepath" |
| "strings" |
| |
| "go.chromium.org/luci/lucicfg/docgen" |
| ) |
| |
| func main() { |
| templates := flag.String("templates", "", "a directory with input *.mdt templates") |
| out := flag.String("out", "", "where to put generated files") |
| starlark := flag.String("starlark", "", "directory with the starlark source code") |
| |
| flag.Parse() |
| |
| if err := run(*templates, *starlark, *out); err != nil { |
| fmt.Fprintf(os.Stderr, "docgen: %s\n", err) |
| os.Exit(1) |
| } |
| } |
| |
| func run(templates, starlark, outDir string) error { |
| // All input templates with paths relative to 'templates' dir. |
| var files []string |
| err := filepath.Walk(templates, func(path string, info os.FileInfo, err error) error { |
| if err == nil && strings.HasSuffix(path, ".mdt") { |
| var rel string |
| if rel, err = filepath.Rel(templates, path); err == nil { |
| files = append(files, rel) |
| } |
| } |
| return err |
| }) |
| if err != nil { |
| return fmt.Errorf("failed to walk %q - %s", templates, err) |
| } |
| |
| // Prepare the generator that can load starlark modules to extract docs from |
| // them. |
| gen := docgen.Generator{Starlark: sourceProvider(starlark)} |
| |
| // For each input template spit out the corresponding generated *.md file. |
| haveErrs := false |
| for _, f := range files { |
| in := filepath.Join(templates, f) |
| out := filepath.Join(outDir, f) |
| |
| // Replace ".mdt" with ".md". |
| out = out[:len(out)-1] |
| |
| fmt.Printf("Rendering %s\n", f) |
| if err := generate(&gen, in, out); err != nil { |
| fmt.Fprintf(os.Stderr, "%s\n", err) |
| haveErrs = true |
| } |
| } |
| |
| if haveErrs { |
| return fmt.Errorf("failed") |
| } |
| return nil |
| } |
| |
| func generate(gen *docgen.Generator, in, out string) error { |
| body, err := ioutil.ReadFile(in) |
| if err != nil { |
| return err |
| } |
| output, err := gen.Render(string(body)) |
| if err != nil { |
| return err |
| } |
| return ioutil.WriteFile(out, output, 0666) |
| } |
| |
| func sourceProvider(root string) func(string) (string, error) { |
| return func(module string) (src string, err error) { |
| // 'module' here is something like "@stdlib//path". |
| chunks := strings.SplitN(module, "//", 2) |
| if len(chunks) != 2 || !strings.HasPrefix(chunks[0], "@") { |
| return "", fmt.Errorf("unrecognized module path %s", module) |
| } |
| pkg := chunks[0][1:] |
| path := chunks[1] |
| |
| // @proto package is not explorable for now. Can potentially auto-generate |
| // it from proto descriptors, if necessary. |
| if pkg == "proto" { |
| return "", nil |
| } |
| |
| fmt.Printf(" loading %q\n", module) |
| body, err := ioutil.ReadFile(filepath.Join(root, pkg, path)) |
| if err != nil { |
| return "", err |
| } |
| return string(body), nil |
| } |
| } |