blob: 81332d48b30a3db637a6886f8b4c1975d5e3e5dc [file] [log] [blame]
// Copyright 2020 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 (
"bytes"
"flag"
"fmt"
"io/ioutil"
"log"
"os"
"regexp"
"strings"
"go.fuchsia.dev/fuchsia/tools/fidl/lib/fidlgen"
"go.fuchsia.dev/fuchsia/tools/fidl/measure-tape/src/hlcpp"
"go.fuchsia.dev/fuchsia/tools/fidl/measure-tape/src/measurer"
"go.fuchsia.dev/fuchsia/tools/fidl/measure-tape/src/rust"
)
type paths []string
func (filenames *paths) String() string {
return strings.Join(*filenames, " ")
}
func (filenames *paths) Set(filename string) error {
*filenames = append(*filenames, filename)
return nil
}
var jsonFiles paths
var targetBinding = flag.String("target-binding", "",
"Target binding for which to generate the measure tape")
var targetType = flag.String("target-type", "",
"Target type to measure, e.g. fuchsia.ui.scenic/Command")
var outCc = flag.String("out-cc", "",
"Write path for .cc file\nRequired for target binding hlcpp")
var outH = flag.String("out-h", "",
"Write path for .h file\nRequired for target binding hlcpp")
var hIncludePath = flag.String("h-include-path", "",
"Include path for the .h file\nRequired for target binding hlcpp")
var onlyCheckToFile = flag.String("only-check-to-file", "",
"Enables verification only mode, which checks the .cc and .h\nRequired for target binding hlcpp")
var outRs = flag.String("out-rs", "",
"Write path for .rs file\nRequired for target binding rust")
func flagsValid() bool {
if len(jsonFiles) == 0 {
return false
}
if len(*targetType) == 0 {
return false
}
switch *targetBinding {
case "hlcpp":
if len(*outCc) == 0 {
return false
}
if len(*outH) == 0 {
return false
}
if len(*hIncludePath) == 0 {
return false
}
case "rust":
if len(*outRs) == 0 {
return false
}
default:
return false
}
return true
}
func main() {
flag.Var(&jsonFiles, "json", "Path(s) to JSON IR")
flag.Parse()
if !flag.Parsed() || !flagsValid() {
flag.PrintDefaults()
os.Exit(1)
}
var roots []fidlgen.Root
for _, filename := range jsonFiles {
root, err := fidlgen.ReadJSONIr(filename)
if err != nil {
log.Fatal(err)
}
roots = append(roots, root)
}
m := measurer.NewMeasurer(roots)
targetMt, err := m.MeasuringTapeFor(*targetType)
if err != nil {
panic(err)
}
allMethods := measurer.NewCodeGenerator(targetMt).Generate()
switch *targetBinding {
case "hlcpp":
hlcppGen(m, targetMt, allMethods)
case "rust":
rustGen(m, targetMt, allMethods)
}
}
func hlcppGen(m *measurer.Measurer, targetMt *measurer.MeasuringTape,
allMethods map[measurer.MethodID]*measurer.Method) {
var (
printer = hlcpp.NewPrinter(m, *hIncludePath)
bufH, bufCc bytes.Buffer
)
printer.WriteH(&bufH, targetMt)
printer.WriteCc(&bufCc, targetMt, allMethods)
if len(*onlyCheckToFile) == 0 {
writeFile(*outH, bufH.Bytes())
writeFile(*outCc, bufCc.Bytes())
} else {
verifyMeasureTape(bufH.Bytes(), bufCc.Bytes())
}
}
func writeFile(path string, data []byte) {
if err := ioutil.WriteFile(path, data, 0644); err != nil {
panic(err)
}
}
func verifyMeasureTape(expectedH, expectedCc []byte) {
actualH, err := ioutil.ReadFile(*outH)
if err != nil {
panic(err)
}
actualCc, err := ioutil.ReadFile(*outCc)
if err != nil {
panic(err)
}
if bytes.Compare(actualH, expectedH) != 0 || bytes.Compare(actualCc, expectedCc) != 0 {
fmt.Fprintf(os.Stderr, "%s and/or %s is out of date! Please run the following\n\n", *outH, *outCc)
skipUntil := 0
for i, arg := range os.Args {
if matched, _ := regexp.MatchString("^-?-only-check-to-file$", arg); matched {
skipUntil = i + 2
continue
}
if i < skipUntil {
continue
}
if i != 0 {
fmt.Fprintf(os.Stderr, " \\\n\t")
}
fmt.Fprintf(os.Stderr, "%s", arg)
}
fmt.Fprintf(os.Stderr, "\n\n")
os.Exit(1)
}
writeFile(*onlyCheckToFile, []byte{})
}
func rustGen(m *measurer.Measurer, targetMt *measurer.MeasuringTape,
allMethods map[measurer.MethodID]*measurer.Method) {
var bufRs bytes.Buffer
rust.WriteRs(&bufRs, m, targetMt, allMethods)
writeFile(*outRs, bufRs.Bytes())
}