blob: d35dffe8d71ad2b9b12a2184fce4c66b0d618ab4 [file] [log] [blame]
// Copyright 2019 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 cli
import (
"encoding/json"
"flag"
"fmt"
"log"
"os"
"strings"
"go.fuchsia.dev/fuchsia/tools/fidl/gidl/lib/config"
"go.fuchsia.dev/fuchsia/tools/fidl/gidl/lib/ir"
"go.fuchsia.dev/fuchsia/tools/fidl/gidl/lib/parser"
"go.fuchsia.dev/fuchsia/tools/fidl/lib/fidlgen"
)
// A Generator generates code given GIDL IR, FIDL IR, and config.
type Generator func(ir.All, fidlgen.Root, config.GeneratorConfig) ([]byte, error)
type listOfStrings []string
func (l *listOfStrings) String() string {
return strings.Join(*l, " ")
}
func (l *listOfStrings) Set(s string) error {
*l = append(*l, s)
return nil
}
func parseGidlIr(filename string) ir.All {
f, err := os.Open(filename)
if err != nil {
panic(err)
}
defer f.Close()
config := parser.Config{
Languages: ir.AllLanguages(),
WireFormats: ir.AllWireFormats(),
}
result, err := parser.NewParser(filename, f, config).Parse()
if err != nil {
panic(err)
}
return result
}
func parseFidlJSONIr(filename string) fidlgen.Root {
bytes, err := os.ReadFile(filename)
if err != nil {
panic(err)
}
var result fidlgen.Root
if err := json.Unmarshal(bytes, &result); err != nil {
panic(err)
}
return result
}
func Main(generatorMap map[ir.OutputType]map[ir.Language]Generator) {
// Omit timestamps from logs.
log.SetFlags(0)
var allOutputTypes, allLanguages []string
allLanguagesMap := map[ir.Language]struct{}{}
for outputType, subMap := range generatorMap {
allOutputTypes = append(allOutputTypes, string(outputType))
for language := range subMap {
if _, ok := allLanguagesMap[language]; !ok {
allLanguagesMap[language] = struct{}{}
allLanguages = append(allLanguages, string(language))
}
}
}
jsonPath := flag.String("json", "",
"relative path to the FIDL intermediate representation.")
outputTypeStr := flag.String("type", "",
fmt.Sprintf("output type (one of: %s)", strings.Join(allOutputTypes, ", ")))
languageStr := flag.String("language", "",
fmt.Sprintf("target language (one of: %s)", strings.Join(allLanguages, ", ")))
outputPath := flag.String("out", "", "path to write output to")
rustBenchmarksFidlLibrary := flag.String("rust-benchmarks-fidl-library", "",
"name for the fidl library used in the rust benchmarks")
cppBenchmarksFidlLibrary := flag.String("cpp-benchmarks-fidl-library", "",
"name for the fidl library used in the cpp benchmarks")
fuzzerCorpusHostDir := flag.String("fuzzer-corpus-host-dir", "",
"output directory for fuzzer_corpus")
fuzzerCorpusPackageDataDir := flag.String("fuzzer-corpus-package-data-dir", "",
"directory to which fuzzer_corpus output files are mapped in their fuchsia package's data directory")
var filterTypes listOfStrings
flag.Var(&filterTypes, "filter-type", "List of types to filter to (used with -type measure_tape)")
flag.Parse()
if flag.NArg() == 0 {
log.Fatal("must provide at least one .gidl file")
}
if *jsonPath == "" {
log.Fatal("must provide -json")
}
if *outputPath == "" {
log.Fatal("must provide -out")
}
outputType := ir.OutputType(*outputTypeStr)
subGeneratorMap, ok := generatorMap[outputType]
if !ok {
log.Fatalf("%s: invalid output type", *outputTypeStr)
}
language := ir.Language(*languageStr)
if _, ok := allLanguagesMap[language]; !ok {
log.Fatalf("%s: invalid language", *languageStr)
}
generator, ok := subGeneratorMap[language]
if !ok {
log.Fatalf("-type %s -language %s: unsupported combination", *outputTypeStr, *languageStr)
}
root := parseFidlJSONIr(*jsonPath)
var parsedGidlFiles []ir.All
for _, path := range flag.Args() {
parsedGidlFiles = append(parsedGidlFiles, parseGidlIr(path))
}
gidl := ir.FilterByLanguage(ir.Merge(parsedGidlFiles), language)
ir.ValidateByOutputType(gidl, outputType)
// For simplicity, we do not allow FIDL that GIDL depends on to have
// dependent libraries, with the exception of zx. This makes it much simpler
// to have everything in the IR, and avoid cross-references.
if len(root.Libraries) == 1 && root.Libraries[0].Name == "zx" {
root.Libraries = make([]fidlgen.Library, 0)
}
if len(root.Libraries) != 0 {
var libs []string
for _, l := range root.Libraries {
libs = append(libs, string(l.Name))
}
log.Fatalf("GIDL does not work with FIDL libraries with dependents, found: %s",
strings.Join(libs, ","))
}
config := config.GeneratorConfig{
RustBenchmarksFidlLibrary: *rustBenchmarksFidlLibrary,
CppBenchmarksFidlLibrary: *cppBenchmarksFidlLibrary,
FuzzerCorpusHostDir: *fuzzerCorpusHostDir,
FuzzerCorpusPackageDataDir: *fuzzerCorpusPackageDataDir,
FilterTypes: filterTypes,
}
mainFile, err := generator(gidl, root, config)
if err != nil {
log.Fatal(err)
}
if language == ir.LanguageFuzzerCorpus {
// The fuzzer corpus manifest must always be written so that the build
// system tries to rebuild the package. The individual files within the
// corpus aren't tracked by the build system.
err = os.WriteFile(*outputPath, mainFile, 0666)
} else {
err = fidlgen.WriteFileIfChanged(*outputPath, mainFile)
}
if err != nil {
log.Fatal(err)
}
}