blob: fc8e88f5b5474a808ad2034bdc4757b63d4b4707 [file] [log] [blame]
// 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
}
}