github.com/rminnich/u-root@v7.0.0+incompatible/cmds/core/man/gen/gen.go (about)

     1  // Copyright 2019 the u-root Authors. All rights reserved
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package main
     6  
     7  import (
     8  	"encoding/json"
     9  	"fmt"
    10  	"go/ast"
    11  	"go/parser"
    12  	"go/token"
    13  	"log"
    14  	"os"
    15  	"path/filepath"
    16  	"regexp"
    17  	"strings"
    18  )
    19  
    20  const mainStr = "main"
    21  
    22  func uncomment(s string) string {
    23  	s = strings.TrimSpace(s)
    24  	if len(s) == 0 {
    25  		return ""
    26  	}
    27  	if strings.HasPrefix(s, "/*") {
    28  		return strings.TrimSpace(s[2 : len(s)-2])
    29  	}
    30  	// comment starts with '//'
    31  	s = s[2:]
    32  	s = strings.Replace(s, "\n// ", "\n", -1)
    33  	s = strings.Replace(s, "\n//", "\n", -1)
    34  	return strings.TrimSpace(s)
    35  }
    36  
    37  func extractMan(name string) (string, error) {
    38  	fset := token.NewFileSet()
    39  	src, err := parser.ParseFile(fset, name, nil, parser.ParseComments)
    40  	if err != nil {
    41  		return "", err
    42  	}
    43  
    44  	if src.Name.Name != mainStr {
    45  		return "", fmt.Errorf("not a main package")
    46  	}
    47  
    48  	hasMainFunc := false
    49  	for _, decl := range src.Decls {
    50  		f, ok := decl.(*ast.FuncDecl)
    51  		hasMainFunc = hasMainFunc || ok && f.Name.Name == mainStr
    52  	}
    53  	if !hasMainFunc {
    54  		return "", fmt.Errorf("file doesn't contain func main()")
    55  	}
    56  	if len(src.Comments) == 0 {
    57  		return "", fmt.Errorf("file doesn't contain comments")
    58  	}
    59  	var man string
    60  	// First comment group is expected to be copyright notice.
    61  	for _, cg := range src.Comments[1:] {
    62  		for _, comment := range cg.List {
    63  			if comment.Slash > src.Name.Pos() {
    64  				break
    65  			}
    66  			man += comment.Text + "\n"
    67  		}
    68  	}
    69  	return uncomment(man), nil
    70  }
    71  
    72  func walk(mans map[string]string, root string) (err error) {
    73  	re, err := regexp.Compile(`_.*\.go$`)
    74  	if err != nil {
    75  		return fmt.Errorf("error compiling regexp: %v", err)
    76  	}
    77  	err = filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
    78  		if err != nil {
    79  			return err
    80  		}
    81  		if info.IsDir() {
    82  			return nil
    83  		}
    84  		dir := filepath.Dir(path)
    85  		// Depth 1.
    86  		if filepath.Dir(dir) != root {
    87  			return nil
    88  		}
    89  		name := info.Name()
    90  		if !strings.HasSuffix(name, ".go") {
    91  			return nil
    92  		}
    93  		if re.MatchString(name) {
    94  			return nil
    95  		}
    96  		cmd := filepath.Base(dir)
    97  		man, err := extractMan(path)
    98  		if err == nil && len(man) > 0 {
    99  			mans[cmd] = man
   100  		}
   101  		return nil
   102  	})
   103  	if err != nil {
   104  		return fmt.Errorf("error walking the path %q: %v", root, err)
   105  	}
   106  	return nil
   107  }
   108  
   109  // writeFile saves man data to a go file in JSON format.
   110  // JSON is used to simplify the process of escaping
   111  // special characters.
   112  func writeFile(fname string, mans map[string]string) error {
   113  	dir := filepath.Dir(fname)
   114  	if _, err := os.Stat(dir); os.IsNotExist(err) {
   115  		if err := os.Mkdir(dir, 0775); err != nil {
   116  			return err
   117  		}
   118  	}
   119  	f, err := os.Create(fname)
   120  	if err != nil {
   121  		return err
   122  	}
   123  	defer f.Close()
   124  	b, err := json.Marshal(mans)
   125  	if err != nil {
   126  		return err
   127  	}
   128  	if _, err := fmt.Fprintln(f, "// Code generated by man/gen/gen.go. DO NOT EDIT."); err != nil {
   129  		return err
   130  	}
   131  	if _, err := fmt.Fprintln(f, "package data"); err != nil {
   132  		return err
   133  	}
   134  	if _, err := fmt.Fprintln(f); err != nil {
   135  		return err
   136  	}
   137  	_, err = fmt.Fprintf(f, "var Data = %q\n", b)
   138  	return err
   139  }
   140  
   141  func main() {
   142  	mans := make(map[string]string)
   143  	l := len(os.Args)
   144  	for _, root := range os.Args[1 : l-1] {
   145  		if err := walk(mans, root); err != nil {
   146  			log.Fatal(err)
   147  		}
   148  	}
   149  	dest := os.Args[l-1]
   150  	if err := writeFile(dest, mans); err != nil {
   151  		log.Fatal(err)
   152  	}
   153  }