blob: 6973b041395cd4ff1e88b64bd112b3d8c263cdb3 [file] [log] [blame]
// Copyright 2018 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 codegen
import (
"io"
"os"
"path/filepath"
"text/template"
fidl "go.fuchsia.dev/fuchsia/tools/fidl/lib/fidlgen"
cpp "go.fuchsia.dev/fuchsia/tools/fidl/lib/fidlgen_cpp"
)
// CodeGenerationMode controls which subset of bindings code to generate.
type CodeGenerationMode int
const (
_ CodeGenerationMode = iota
// Monolithic generates all of HLCPP bindings in one header/source pair.
Monolithic
// OnlyGenerateDomainObjects generates only the domain objects
// (structs, tables, unions, enums, bits, request/response codecs, etc.)
OnlyGenerateDomainObjects
// TODO(fxbug.dev/64093): Add a third generation mode which only generates
// proxies and stubs.
)
// Config is the configuration data passed to the HLCPP generator.
type Config struct {
// The base file name for files generated by this generator.
OutputBase string
// The directory to which C and C++ includes should be relative.
IncludeBase string
// The path suffix after the library path when referencing includes.
IncludeStem string
}
type FidlGenerator struct {
tmpls *template.Template
CodeGenerationMode
}
func NewFidlGenerator(mode CodeGenerationMode) *FidlGenerator {
tmpls := template.New("CPPTemplates").Funcs(template.FuncMap{
"Kinds": func() interface{} { return cpp.Kinds },
"Eq": func(a interface{}, b interface{}) bool { return a == b },
"IncludeDomainObjects": func() bool { return mode == Monolithic || mode == OnlyGenerateDomainObjects },
"IncludeProxiesAndStubs": func() bool { return mode == Monolithic },
})
// Natural types templates
template.Must(tmpls.Parse(bitsTemplate))
template.Must(tmpls.Parse(constTemplate))
template.Must(tmpls.Parse(enumTemplate))
template.Must(tmpls.Parse(protocolTemplateNaturalTypes))
template.Must(tmpls.Parse(structTemplate))
template.Must(tmpls.Parse(tableTemplate))
template.Must(tmpls.Parse(unionTemplate))
// Proxies and stubs templates
template.Must(tmpls.Parse(protocolTemplateProxiesAndStubs))
template.Must(tmpls.Parse(serviceTemplate))
// File templates
template.Must(tmpls.Parse(headerTemplate))
template.Must(tmpls.Parse(implementationTemplate))
template.Must(tmpls.Parse(testBaseTemplate))
return &FidlGenerator{
tmpls: tmpls,
CodeGenerationMode: mode,
}
}
// GenerateHeader generates the C++ bindings header.
func (gen *FidlGenerator) GenerateHeader(wr io.Writer, tree cpp.Root) error {
return gen.tmpls.ExecuteTemplate(wr, "Header", tree)
}
// GenerateSource generates the C++ bindings source, i.e. implementation.
func (gen *FidlGenerator) GenerateSource(wr io.Writer, tree cpp.Root) error {
return gen.tmpls.ExecuteTemplate(wr, "Implementation", tree)
}
func (gen *FidlGenerator) GenerateTestBase(wr io.Writer, tree cpp.Root) error {
return gen.tmpls.ExecuteTemplate(wr, "TestBase", tree)
}
func (gen *FidlGenerator) generateFile(filepath, clangFormatPath string, generate func(w io.Writer) error) error {
file, err := os.Create(filepath)
if err != nil {
return err
}
formatterPipe, err := cpp.NewClangFormatter(clangFormatPath).FormatPipe(file)
if err != nil {
return err
}
if err := generate(formatterPipe); err != nil {
return err
}
return formatterPipe.Close()
}
// GenerateFidl generates all files required for the C++ bindings.
func (gen *FidlGenerator) GenerateFidl(fidl fidl.Root, config *Config, clangFormatPath string) error {
tree := cpp.CompileHL(fidl)
relStem, err := filepath.Rel(config.IncludeBase, config.OutputBase)
if err != nil {
return err
}
tree.PrimaryHeader = relStem + ".h"
tree.IncludeStem = config.IncludeStem
headerPath := config.OutputBase + ".h"
sourcePath := config.OutputBase + ".cc"
testBasePath := config.OutputBase + "_test_base.h"
if err := os.MkdirAll(filepath.Dir(config.OutputBase), os.ModePerm); err != nil {
return err
}
err = gen.generateFile(headerPath, clangFormatPath, func(w io.Writer) error {
return gen.GenerateHeader(w, tree)
})
if err != nil {
return err
}
err = gen.generateFile(sourcePath, clangFormatPath, func(w io.Writer) error {
return gen.GenerateSource(w, tree)
})
if err != nil {
return err
}
if gen.CodeGenerationMode == OnlyGenerateDomainObjects {
// TestBase header only applies to protocols definitions.
return nil
}
return gen.generateFile(testBasePath, clangFormatPath, func(w io.Writer) error {
return gen.GenerateTestBase(w, tree)
})
}