blob: acb3094158fd7d67e70836b937f3f33cbcacf88e [file] [log] [blame]
// Copyright 2022 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package main
import (
"archive/zip"
"flag"
"io"
"log"
"os"
"path/filepath"
"sort"
"strings"
"go.fuchsia.dev/fuchsia/tools/cppdocgen/clangdoc"
"go.fuchsia.dev/fuchsia/tools/cppdocgen/docgen"
)
var flags struct {
inDir string
inZip string
outDir string
outZip string
libName string
sourceRoot string
buildDir string
includeDir string
repoBaseUrl string
tocPath string
overviewFile string
}
func init() {
flag.StringVar(&flags.inDir, "in-dir", "", "Input directory")
flag.StringVar(&flags.inZip, "in-zip", "", "Input zip file")
flag.StringVar(&flags.outDir, "out-dir", "", "Output directory")
flag.StringVar(&flags.outZip, "out-zip", "", "Output zip file")
flag.StringVar(&flags.libName, "lib-name", "", "User-visible library name")
flag.StringVar(&flags.sourceRoot, "source-root", "", "Repository root directory.")
flag.StringVar(&flags.buildDir, "build-dir", "",
"Build directory from whence clang-doc paths are relative.")
flag.StringVar(&flags.includeDir, "include-dir", "",
"The directory where #includes are relative to (used for generating titles and #include examples).")
flag.StringVar(&flags.repoBaseUrl, "source-url", "",
"URL of code repo for paths will be appended to for generating source links.")
flag.StringVar(&flags.tocPath, "toc-path", "",
"Absolute path on devsite where this code will be hosted, for paths in _toc.yaml.")
flag.StringVar(&flags.overviewFile, "overview", "",
"Path of the file that will comprise the top of the index.")
}
func main() {
log.SetPrefix("cppdocgen: ")
flag.Parse()
if len(flags.repoBaseUrl) == 0 {
log.Fatal("No repo base URL (-u) specified")
}
if !strings.HasSuffix(flags.repoBaseUrl, "/") {
// The base URL should always end in a slash for appending file paths.
flags.repoBaseUrl += "/"
}
if len(flags.libName) == 0 {
log.Fatal("No library name (--lib-name) specified")
}
if len(flags.sourceRoot) == 0 {
log.Fatal("No respository source root (--source-root) specified")
}
if len(flags.buildDir) == 0 {
log.Fatal("No build directory (--build-dir) specified")
}
if len(flags.includeDir) == 0 {
log.Fatal("No include directory (--include-dir) specified")
}
tocPath := flags.tocPath
if len(tocPath) == 0 {
log.Fatal("No --toc-path specified")
}
if !strings.HasSuffix(tocPath, "/") {
tocPath += "/"
}
var overviewContents []byte
if flags.overviewFile != "" {
inContents, err := os.ReadFile(flags.overviewFile)
if err != nil {
log.Fatal(err)
}
overviewContents = inContents
}
buildRelSourceRoot, err := filepath.Rel(flags.buildDir, flags.sourceRoot)
if err != nil {
log.Fatal("Can't rebase source root: %s", err)
}
buildRelIncludeDir, err := filepath.Rel(flags.buildDir, flags.includeDir)
if err != nil {
log.Fatal("Can't rebase include dir: %s", err)
}
writeSettings := docgen.WriteSettings{
LibName: flags.libName,
BuildRelSourceRoot: buildRelSourceRoot,
BuildRelIncludeDir: buildRelIncludeDir,
RepoBaseUrl: flags.repoBaseUrl,
TocPath: tocPath,
OverviewContents: overviewContents,
}
// All other args are the list of headers we want to index.
indexSettings := docgen.MakeIndexSettings(flags.buildDir)
for _, a := range flag.Args() {
indexSettings.Headers[a] = struct{}{}
}
// Validate input flags.
if flags.inZip != "" && flags.inDir != "" {
log.Fatal("Can't specify both --in-dir and --in-zip")
} else if flags.inZip == "" && flags.inDir == "" {
log.Fatal("Must specify either --in-dir=<dir> or --in-zip=<filename>.zip")
}
// Validate output flags.
if flags.outZip != "" && flags.outDir != "" {
log.Fatal("Can't specify both --out-dir and --out-zip")
} else if flags.outZip == "" && flags.outDir == "" {
log.Fatal("Must specify either --out-dir=<dir> or --out-zip=<filename>.zip")
}
// Set up output. The addFile() lambda will create the given file in the output format
// requested.
var addFile func(string) io.Writer
if len(flags.outZip) != 0 {
// Create a zip file.
zipFile, err := os.Create(flags.outZip)
if err != nil {
log.Fatal("Can't create output file '%s':\n%v", flags.outZip, err)
}
defer zipFile.Close()
zipWriter := zip.NewWriter(zipFile)
defer zipWriter.Close()
addFile = func(path string) io.Writer {
file, err := zipWriter.Create(path)
if err != nil {
log.Fatal("Can't add '%s' to zip file\n%v", path, err)
}
return file
}
} else {
// Create a directory.
err := os.MkdirAll(flags.outDir, 0o755)
if err != nil {
log.Fatal("Can't create output directory '%s':\n%v", flags.outDir, err)
}
addFile = func(name string) io.Writer {
file, err := os.Create(flags.outDir + "/" + name)
if err != nil {
log.Fatal(err)
}
return file
}
}
var root *clangdoc.NamespaceInfo
if flags.inDir != "" {
root = clangdoc.LoadDir(flags.inDir)
} else {
root = clangdoc.LoadZip(flags.inZip)
}
index := docgen.MakeIndex(indexSettings, root)
indexFile := addFile("index.md")
docgen.WriteIndex(writeSettings, &index, indexFile)
tocFile := addFile("_toc.yaml")
docgen.WriteToc(writeSettings, &index, tocFile)
// Header references.
// Sort them for deterministic outputs.
sorted_headers := make([]string, 0, len(index.Headers))
for header := range index.Headers {
sorted_headers = append(sorted_headers, header)
}
sort.Strings(sorted_headers)
for _, header_name := range sorted_headers {
h := index.Headers[header_name]
n := h.ReferenceFileName()
headerFile := addFile(n)
docgen.WriteHeaderReference(writeSettings, &index, h, headerFile)
}
}