Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add PathStruct definition and child ctor generating code for ypathgen #315

Merged
merged 2 commits into from
Oct 1, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 14 additions & 6 deletions demo/getting_started/interfaces_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,13 @@ func TestGenerateCode(t *testing.T) {
}{{
name: "openconfig interfaces",
inConfig: &ygen.GeneratorConfig{
CompressOCPaths: true,
ExcludeModules: []string{"ietf-interfaces"},
GenerateFakeRoot: true,
ParseOptions: ygen.ParseOpts{
ExcludeModules: []string{"ietf-interfaces"},
},
TransformationOptions: ygen.TransformationOpts{
CompressOCPaths: true,
GenerateFakeRoot: true,
},
GenerateJSONSchema: true,
},
inFiles: []string{
Expand All @@ -39,9 +43,13 @@ func TestGenerateCode(t *testing.T) {
}, {
name: "openconfig interfaces with no compression",
inConfig: &ygen.GeneratorConfig{
CompressOCPaths: false,
ExcludeModules: []string{"ietf-interfaces"},
GenerateFakeRoot: true,
ParseOptions: ygen.ParseOpts{
ExcludeModules: []string{"ietf-interfaces"},
},
TransformationOptions: ygen.TransformationOpts{
CompressOCPaths: false,
GenerateFakeRoot: true,
},
GenerateJSONSchema: true,
},
inFiles: []string{
Expand Down
20 changes: 12 additions & 8 deletions generator/generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -232,15 +232,20 @@ func main() {

// Perform the code generation.
cg := ygen.NewYANGCodeGenerator(&ygen.GeneratorConfig{
CompressOCPaths: *compressPaths,
ExcludeModules: modsExcluded,
ParseOptions: ygen.ParseOpts{
ExcludeModules: modsExcluded,
YANGParseOptions: yang.Options{
IgnoreSubmoduleCircularDependencies: *ignoreCircDeps,
},
ExcludeState: *excludeState,
},
TransformationOptions: ygen.TransformationOpts{
CompressOCPaths: *compressPaths,
GenerateFakeRoot: *generateFakeRoot,
FakeRootName: *fakeRootName,
},
PackageName: *packageName,
GenerateFakeRoot: *generateFakeRoot,
FakeRootName: *fakeRootName,
GenerateJSONSchema: *generateSchema,
YANGParseOptions: yang.Options{
IgnoreSubmoduleCircularDependencies: *ignoreCircDeps,
},
GoOptions: ygen.GoOpts{
YgotImportPath: *ygotImportPath,
YtypesImportPath: *ytypesImportPath,
Expand All @@ -254,7 +259,6 @@ func main() {
GenerateLeafGetters: *generateLeafGetters,
IncludeModelData: *includeModelData,
},
ExcludeState: *excludeState,
})

generatedGoCode, err := cg.GenerateGoCode(generateModules, includePaths)
Expand Down
22 changes: 13 additions & 9 deletions proto_generator/protogenerator.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,15 +92,20 @@ func main() {

// Perform the code generation.
cg := ygen.NewYANGCodeGenerator(&ygen.GeneratorConfig{
CompressOCPaths: *compressPaths,
ExcludeModules: modsExcluded,
PackageName: *packageName,
GenerateFakeRoot: *generateFakeRoot,
FakeRootName: *fakeRootName,
Caller: *callerName,
YANGParseOptions: yang.Options{
IgnoreSubmoduleCircularDependencies: *ignoreCircDeps,
ParseOptions: ygen.ParseOpts{
ExcludeModules: modsExcluded,
YANGParseOptions: yang.Options{
IgnoreSubmoduleCircularDependencies: *ignoreCircDeps,
},
ExcludeState: *excludeState,
},
TransformationOptions: ygen.TransformationOpts{
CompressOCPaths: *compressPaths,
GenerateFakeRoot: *generateFakeRoot,
FakeRootName: *fakeRootName,
},
PackageName: *packageName,
Caller: *callerName,
ProtoOptions: ygen.ProtoOpts{
BaseImportPath: *baseImportPath,
YwrapperPath: *ywrapperPath,
Expand All @@ -110,7 +115,6 @@ func main() {
NestedMessages: !*packageHierarchy,
EnumPackageName: *enumPackageName,
},
ExcludeState: *excludeState,
})

generatedProtoCode, err := cg.GenerateProto3(generateModules, includePaths)
Expand Down
138 changes: 100 additions & 38 deletions ygen/codegen.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,31 +45,12 @@ type YANGCodeGenerator struct {

// GeneratorConfig stores the configuration options used for code generation.
type GeneratorConfig struct {
// CompressOCPaths indicates whether paths should be compressed in the output
// of an OpenConfig schema.
CompressOCPaths bool
// ExcludeModules specifies any modules that are included within the set of
// modules that should have code generated for them that should be ignored during
// code generation. This is due to the fact that some schemas (e.g., OpenConfig
// interfaces) currently result in overlapping entities (e.g., /interfaces).
ExcludeModules []string
// PackageName is the name that should be used for the generating package.
PackageName string
// Caller is the name of the binary calling the generator library, it is
// included in the header of output files for debugging purposes. If a
// string is not specified, the location of the library is utilised.
Caller string
// YANGParseOptions provides the options that should be handed to the
// github.com/openconfig/goyang/pkg/yang library. These specify how the
// input YANG files should be parsed.
YANGParseOptions yang.Options
// GenerateFakeRoot specifies whether an entity that represents the
// root of the YANG schema tree should be generated in the generated
// code.
GenerateFakeRoot bool
// FakeRootName specifies the name of the struct that should be generated
// representing the root.
FakeRootName string
// GenerateJSONSchema stores a boolean which defines whether to generate
// the JSON corresponding to the YANG schema parsed to generate the
// output code.
Expand All @@ -78,18 +59,65 @@ type GeneratorConfig struct {
// generation function, such that it can be handled by an external
// library.
StoreRawSchema bool
// ParseOptions contains parsing options for a given set of schema files.
ParseOptions ParseOpts
// TransformationOptions contains options for how the generated code
// may be transformed from a simple 1:1 mapping with respect to the
// given YANG schema.
TransformationOptions TransformationOpts
// GoOptions stores a struct which stores Go code generation specific
// options for the code generaton.
GoOptions GoOpts
// ProtoOptions stores a struct which contains Protobuf specific options.
ProtoOptions ProtoOpts
}

// DirectoryGenConfig contains the configuration necessary to generate a set of
// Directory objects for a given schema. The set of Directory objects is the
// intermediate representation generated by ygen, which can be useful for
// external code generation libraries which can make use of it.
type DirectoryGenConfig struct {
// ParseOptions contains parsing options for a given set of schema files.
ParseOptions ParseOpts
// TransformationOptions contains options for how the generated code
// may be transformed from a simple 1:1 mapping with respect to the
// given YANG schema.
TransformationOptions TransformationOpts
}

// ParseOpts contains parsing configuration for a given schema.
type ParseOpts struct {
// ExcludeModules specifies any modules that are included within the set of
// modules that should have code generated for them that should be ignored during
// code generation. This is due to the fact that some schemas (e.g., OpenConfig
// interfaces) currently result in overlapping entities (e.g., /interfaces).
ExcludeModules []string
// YANGParseOptions provides the options that should be handed to the
// github.com/openconfig/goyang/pkg/yang library. These specify how the
// input YANG files should be parsed.
YANGParseOptions yang.Options
// ExcludeState specifies whether config false values should be
// included in the generated code output. When set, all values that are
// not writeable (i.e., config false) within the YANG schema and their
// children are excluded from the generated code.
ExcludeState bool
}

// TransformationOpts specifies transformations to the generated code with
// respect to the input schema.
type TransformationOpts struct {
// CompressOCPaths indicates whether paths should be compressed in the output
// of an OpenConfig schema.
CompressOCPaths bool
// GenerateFakeRoot specifies whether an entity that represents the
// root of the YANG schema tree should be generated in the generated
// code.
GenerateFakeRoot bool
// FakeRootName specifies the name of the struct that should be generated
// representing the root.
FakeRootName string
}

// GoOpts stores Go specific options for the code generation library.
type GoOpts struct {
// SchemaVarName is the name for the variable which stores the compressed
Expand Down Expand Up @@ -297,13 +325,13 @@ func (cg *YANGCodeGenerator) GenerateGoCode(yangFiles, includePaths []string) (*
// Store the returned schematree within the state for this code generation.
cg.state.schematree = mdef.schemaTree

directory, errs := cg.state.buildDirectoryDefinitions(mdef.directoryEntries, cg.Config.CompressOCPaths, cg.Config.GenerateFakeRoot, golang, cg.Config.ExcludeState)
directory, errs := cg.state.buildDirectoryDefinitions(mdef.directoryEntries, cg.Config.TransformationOptions.CompressOCPaths, cg.Config.TransformationOptions.GenerateFakeRoot, golang, cg.Config.ParseOptions.ExcludeState)
if errs != nil {
return nil, errs
}

var rootName string
if rootName = resolveRootName(cg.Config.FakeRootName, defaultRootName, cg.Config.GenerateFakeRoot); rootName != "" {
if rootName = resolveRootName(cg.Config.TransformationOptions.FakeRootName, defaultRootName, cg.Config.TransformationOptions.GenerateFakeRoot); rootName != "" {
if r, ok := directory[fmt.Sprintf("/%s", rootName)]; ok {
rootName = r.Name
}
Expand All @@ -329,7 +357,7 @@ func (cg *YANGCodeGenerator) GenerateGoCode(yangFiles, includePaths []string) (*
var structSnippets []GoStructCodeSnippet
for _, directoryName := range orderedDirNames {
structOut, errs := writeGoStruct(dirNameMap[directoryName], directory, cg.state,
cg.Config.CompressOCPaths, cg.Config.GenerateJSONSchema, cg.Config.GoOptions)
cg.Config.TransformationOptions.CompressOCPaths, cg.Config.GenerateJSONSchema, cg.Config.GoOptions)
if errs != nil {
codegenErr = util.AppendErrs(codegenErr, errs)
continue
Expand All @@ -343,7 +371,7 @@ func (cg *YANGCodeGenerator) GenerateGoCode(yangFiles, includePaths []string) (*
}
}

goEnums, errs := cg.state.findEnumSet(mdef.enumEntries, cg.Config.CompressOCPaths, false)
goEnums, errs := cg.state.findEnumSet(mdef.enumEntries, cg.Config.TransformationOptions.CompressOCPaths, false)
if errs != nil {
codegenErr = util.AppendErrs(codegenErr, errs)
return nil, codegenErr
Expand All @@ -359,7 +387,7 @@ func (cg *YANGCodeGenerator) GenerateGoCode(yangFiles, includePaths []string) (*
var enumTypeMapCode string
if cg.Config.GenerateJSONSchema {
var err error
rawSchema, err = buildJSONTree(mdef.modules, cg.state.uniqueDirectoryNames, mdef.directoryEntries["/"], cg.Config.CompressOCPaths)
rawSchema, err = buildJSONTree(mdef.modules, cg.state.uniqueDirectoryNames, mdef.directoryEntries["/"], cg.Config.TransformationOptions.CompressOCPaths)
if err != nil {
util.AppendErr(codegenErr, fmt.Errorf("error marshalling JSON schema: %v", err))
}
Expand Down Expand Up @@ -392,11 +420,45 @@ func (cg *YANGCodeGenerator) GenerateGoCode(yangFiles, includePaths []string) (*
}, nil
}

// generateEnumCode takes a map of enumerated-type entries keyed by their
// names, and returns a slice of their definition snippets and an enum
// string-value look-up snippet. The look-up snippet defines a map keyed by the
// enums' type names, whose value is another map which can be used to look up a
// given enum value's string representation given its integer representation.
// GetDirectories parses YANG files and returns a path-keyed map of Directory
// entries that is the intermediate representation used by ygen for subsequent
// code generation. This representation may be useful to external code
// generation libraries that make use of such information, e.g. if their output
// code depends on a ygen-generated type. yangFiles is a slice of strings
// containing the path to a set of YANG files which contain YANG modules.
// includePaths is slice of strings which specifies the set of paths that are
// to be searched for associated models (e.g., modules that are included by the
// specified set of modules, or submodules of those modules).
// Any errors encountered during code generation are returned.
func (dcg *DirectoryGenConfig) GetDirectories(yangFiles, includePaths []string) (map[string]*Directory, util.Errors) {
if !dcg.TransformationOptions.CompressOCPaths {
return nil, util.Errors{fmt.Errorf("GetDirectories currently does not have unit tests for CompressOCPaths=false; if support needed, add unit tests and remove this error")}
}

cg := &GeneratorConfig{ParseOptions: dcg.ParseOptions, TransformationOptions: dcg.TransformationOptions}
// Extract the entities to be mapped into structs and enumerations in the output
// Go code. Extract the schematree from the modules provided such that it can be
// used to reference entities within the tree.
mdef, errs := mappedDefinitions(yangFiles, includePaths, cg)
if errs != nil {
return nil, errs
}

dirsToProcess := map[string]*yang.Entry(mdef.directoryEntries)

// TODO(wenbli): need to use state to return data about the returned directories (e.g. names & type names used in the generated Go code).
state := newGenState()

// Store the returned schematree within the state for this code generation.
state.schematree = mdef.schemaTree

directory, errs := state.buildDirectoryDefinitions(dirsToProcess, cg.TransformationOptions.CompressOCPaths, cg.TransformationOptions.GenerateFakeRoot, golang, cg.ParseOptions.ExcludeState)
if errs != nil {
return nil, errs
}
return directory, nil
}

func generateEnumCode(goEnums map[string]*yangEnum) ([]string, string, util.Errors) {
// orderedEnumNames is used to get the enumerated types that have been
// identified in alphabetical order, such that they are returned in a
Expand Down Expand Up @@ -456,7 +518,7 @@ func (cg *YANGCodeGenerator) GenerateProto3(yangFiles, includePaths []string) (*

cg.state.schematree = mdef.schemaTree

penums, errs := cg.state.findEnumSet(mdef.enumEntries, cg.Config.CompressOCPaths, true)
penums, errs := cg.state.findEnumSet(mdef.enumEntries, cg.Config.TransformationOptions.CompressOCPaths, true)
if errs != nil {
return nil, errs
}
Expand All @@ -465,7 +527,7 @@ func (cg *YANGCodeGenerator) GenerateProto3(yangFiles, includePaths []string) (*
return nil, errs
}

protoMsgs, errs := cg.state.buildDirectoryDefinitions(mdef.directoryEntries, cg.Config.CompressOCPaths, cg.Config.GenerateFakeRoot, protobuf, cg.Config.ExcludeState)
protoMsgs, errs := cg.state.buildDirectoryDefinitions(mdef.directoryEntries, cg.Config.TransformationOptions.CompressOCPaths, cg.Config.TransformationOptions.GenerateFakeRoot, protobuf, cg.Config.ParseOptions.ExcludeState)
if errs != nil {
return nil, errs
}
Expand Down Expand Up @@ -525,7 +587,7 @@ func (cg *YANGCodeGenerator) GenerateProto3(yangFiles, includePaths []string) (*
m := msgMap[n]

genMsg, errs := writeProto3Msg(m, protoMsgs, cg.state, &protoMsgConfig{
compressPaths: cg.Config.CompressOCPaths,
compressPaths: cg.Config.TransformationOptions.CompressOCPaths,
basePackageName: basePackageName,
enumPackageName: enumPackageName,
baseImportPath: cg.Config.ProtoOptions.BaseImportPath,
Expand Down Expand Up @@ -578,7 +640,7 @@ func (cg *YANGCodeGenerator) GenerateProto3(yangFiles, includePaths []string) (*
Imports: stringKeys(pkgImports[n]),
SourceYANGFiles: yangFiles,
SourceYANGIncludePaths: includePaths,
CompressPaths: cg.Config.CompressOCPaths,
CompressPaths: cg.Config.TransformationOptions.CompressOCPaths,
CallerName: cg.Config.Caller,
YwrapperPath: ywrapperPath,
YextPath: yextPath,
Expand Down Expand Up @@ -684,14 +746,14 @@ type mappedYANGDefinitions struct {
// It returns a mappedYANGDefinitions struct populated with the directory, enum
// entries in the input schemas as well as the calculated schema tree.
func mappedDefinitions(yangFiles, includePaths []string, cfg *GeneratorConfig) (*mappedYANGDefinitions, util.Errors) {
modules, errs := processModules(yangFiles, includePaths, cfg.YANGParseOptions)
modules, errs := processModules(yangFiles, includePaths, cfg.ParseOptions.YANGParseOptions)
if errs != nil {
return nil, errs
}

// Build a map of excluded modules to simplify lookup.
excluded := map[string]bool{}
for _, e := range cfg.ExcludeModules {
for _, e := range cfg.ParseOptions.ExcludeModules {
excluded[e] = true
}

Expand All @@ -701,7 +763,7 @@ func mappedDefinitions(yangFiles, includePaths []string, cfg *GeneratorConfig) (
enums := map[string]*yang.Entry{}
var rootElems, treeElems []*yang.Entry
for _, module := range modules {
errs = append(errs, findMappableEntities(module, dirs, enums, cfg.ExcludeModules, cfg.CompressOCPaths, modules)...)
errs = append(errs, findMappableEntities(module, dirs, enums, cfg.ParseOptions.ExcludeModules, cfg.TransformationOptions.CompressOCPaths, modules)...)
if module == nil {
errs = append(errs, errors.New("found a nil module in the returned module set"))
continue
Expand All @@ -728,8 +790,8 @@ func mappedDefinitions(yangFiles, includePaths []string, cfg *GeneratorConfig) (

// If we were asked to generate a fake root entity, then go and find the top-level entities that
// we were asked for.
if cfg.GenerateFakeRoot {
if err := createFakeRoot(dirs, rootElems, cfg.FakeRootName, cfg.CompressOCPaths); err != nil {
if cfg.TransformationOptions.GenerateFakeRoot {
if err := createFakeRoot(dirs, rootElems, cfg.TransformationOptions.FakeRootName, cfg.TransformationOptions.CompressOCPaths); err != nil {
return nil, []error{err}
}
}
Expand Down
Loading