| // 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) |
| }) |
| } |