Skip to content

Commit

Permalink
Generate server boilerplate (#500)
Browse files Browse the repository at this point in the history
* Generate server boilerplate

* fix
  • Loading branch information
DanG100 authored Nov 18, 2024
1 parent 1384841 commit e49cd40
Show file tree
Hide file tree
Showing 106 changed files with 38,689 additions and 2 deletions.
11 changes: 10 additions & 1 deletion dataplane/apigen/apigen.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ func parse(headers []string, includePaths ...string) (*cc.AST, error) {
var (
saiPath = flag.String("sai_path", "bazel-lemming/external/com_github_opencomputeproject_sai", "Path to SAI repo")
clientOutDir = flag.String("client_out_dir", "dataplane/standalone/sai", "Output directory for C++ client code")
_ = flag.String("server_out_dir", "dataplane/standalone/saiserver", "Output directory for C++ server code")
serverOutDir = flag.String("server_out_dir", "dataplane/standalone/saiserver", "Output directory for C++ server code")
protoOutDir = flag.String("proto_out_dir", "dataplane/proto/sai", "Output dirrectory for proto code")
protoPackage = flag.String("proto_package", "lemming.dataplane.sai", "Package for generated proto code")
protoGoPackage = flag.String("proto_go_package", "github.com/openconfig/lemming/dataplane/proto/sai", "Go package option in proto code")
Expand Down Expand Up @@ -95,6 +95,10 @@ func generate() error {
if err != nil {
return err
}
serverFiles, err := ccgen.GenServer(xmlInfo, sai, *protoOutDir, *serverOutDir)
if err != nil {
return err
}

for file, content := range protos {
if err := os.WriteFile(filepath.Join(*protoOutDir, file), []byte(content), 0o600); err != nil {
Expand All @@ -106,6 +110,11 @@ func generate() error {
return err
}
}
for file, content := range serverFiles {
if err := os.WriteFile(filepath.Join(*serverOutDir, file), []byte(content), 0o600); err != nil {
return err
}
}

return nil
}
Expand Down
5 changes: 4 additions & 1 deletion dataplane/apigen/ccgen/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library")

go_library(
name = "ccgen",
srcs = ["ccgen.go"],
srcs = [
"ccgen.go",
"servergen.go",
],
importpath = "github.com/openconfig/lemming/dataplane/apigen/ccgen",
visibility = ["//visibility:public"],
deps = [
Expand Down
188 changes: 188 additions & 0 deletions dataplane/apigen/ccgen/servergen.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
// Copyright 2024 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package ccgen

import (
"slices"
"strings"
"text/template"

"github.com/openconfig/lemming/dataplane/apigen/docparser"
"github.com/openconfig/lemming/dataplane/apigen/saiast"
"github.com/openconfig/lemming/dataplane/apigen/typeinfo"
)

func GenServer(doc *docparser.SAIInfo, sai *saiast.SAIAPI, protoOutDir, ccOutDir string) (map[string]string, error) {
files := make(map[string]string)
enums := enumFile{
ProtoOutDir: protoOutDir,
CCOutDir: ccOutDir,
}
d, err := typeinfo.Data(doc, sai, "", "", ccOutDir, protoOutDir)
if err != nil {
return nil, err
}
for apiName, iface := range d.APIs {
var headerBuilder, implBuilder strings.Builder
if err := serverHdrTmpl.Execute(&headerBuilder, iface); err != nil {
return nil, err
}
if err := serverCCTmpl.Execute(&implBuilder, iface); err != nil {
return nil, err
}
files[apiName+".h"] = headerBuilder.String()
files[apiName+".cc"] = implBuilder.String()
enums.ProtoIncludes = append(enums.ProtoIncludes, apiName+".pb")
}
for name, enumDesc := range doc.Enums {
e := enum{
SAIName: name,
ProtoName: saiast.TrimSAIName(name, true, false),
UnspecifiedName: saiast.TrimSAIName(name, false, true) + "_UNSPECIFIED",
}
if len(enumDesc) == 0 {
continue
}
seenVal := map[int]struct{}{}
for _, enumElemDesc := range enumDesc {
if _, ok := seenVal[enumElemDesc.Value]; ok {
continue
}
seenVal[enumElemDesc.Value] = struct{}{}
name := strings.TrimPrefix(enumElemDesc.Name, "SAI_")
// If the SAI name conflicts with unspecified proto name, then add SAI prefix,
// that way the proto enum value is always 1 greater than the c enum.
if name == e.UnspecifiedName {
name = strings.TrimSuffix(saiast.TrimSAIName(name, false, true), "_UNSPECIFIED") + "_SAI_UNSPECIFIED"
}
e.Elems = append(e.Elems, enumElem{
SAIName: enumElemDesc.Name,
ProtoName: name,
})
}
e.DefaultReturn = e.Elems[0].SAIName
enums.Enums = append(enums.Enums, e)
}
for name, attr := range doc.Attrs {
if _, ok := unsupportedEnum[name]; ok {
continue
}
e := enum{
SAIName: "sai_" + strings.ToLower(name) + "_attr_t",
ProtoName: saiast.TrimSAIName(name+"_ATTR", true, false),
UnspecifiedName: saiast.TrimSAIName(name+"_ATTR", false, true) + "_UNSPECIFIED",
}
for _, val := range attr.ReadFields {
name := strings.TrimPrefix(val.EnumName, "SAI_")
// If the SAI name conflicts with unspecified proto name, then add SAI prefix,
// that way the proto enum value is always 1 greater than the c enum.
if name == e.UnspecifiedName {
name = strings.TrimSuffix(saiast.TrimSAIName(name+"_ATTR", false, true), "_UNSPECIFIED") + "_SAI_UNSPECIFIED"
}
e.Elems = append(e.Elems, enumElem{
SAIName: val.EnumName,
ProtoName: name,
})
}
e.DefaultReturn = e.Elems[0].SAIName
enums.Enums = append(enums.Enums, e)
}
slices.SortFunc(enums.Enums, func(a, b enum) int { return strings.Compare(a.SAIName, b.SAIName) })
var headerBuilder, implBuilder strings.Builder
if err := enumHeaderTmpl.Execute(&headerBuilder, enums); err != nil {
return nil, err
}
if err := enumCCTmpl.Execute(&implBuilder, enums); err != nil {
return nil, err
}
files["enum.h"] = headerBuilder.String()
files["enum.cc"] = implBuilder.String()

return files, nil
}

var (
serverHdrTmpl = template.Must(template.New("header").Parse(`// Copyright 2024 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef {{ .IncludeGuard }}
#define {{ .IncludeGuard }}
#include "{{ .ProtoOutDir }}/common.pb.h"
#include "{{ .ProtoOutDir }}/{{ .ProtoInclude }}.h"
#include "{{ .ProtoOutDir }}/{{ .GRPCInclude }}.h"
extern "C" {
#include "inc/sai.h"
}
extern "C" {
#include "experimental/saiextensions.h"
}
class {{ .ServiceName }} final : public lemming::dataplane::sai::{{ .ServiceName }}::Service {
public:
{{ range .Funcs }}
{{- if and .ProtoRPCName (not .IsStreaming) }}
grpc::Status {{ .ProtoRPCName }}(grpc::ServerContext* context, const lemming::dataplane::sai::{{ .ProtoRequestType }}* req, lemming::dataplane::sai::{{ .ProtoResponseType }}* resp);
{{- end }}
{{ end }}
{{ .APIType }} *api;
};
#endif // {{ .IncludeGuard }}
`))
serverCCTmpl = template.Must(template.New("header").Parse(`// Copyright 2024 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "{{ .CCOutDir }}/{{ .Header }}"
#include "{{ .CCOutDir }}/common.h"
#include "{{ .CCOutDir }}/enum.h"
#include "{{ .ProtoOutDir }}/common.pb.h"
#include "{{ .ProtoOutDir }}/{{ .ProtoInclude }}.h"
#include <glog/logging.h>
{{ range .Funcs }}
{{- if and .ProtoRPCName (not .IsStreaming) }}
grpc::Status {{ $.ServiceName }}::{{ .ProtoRPCName }}(grpc::ServerContext* context, const lemming::dataplane::sai::{{ .ProtoRequestType }}* req, lemming::dataplane::sai::{{ .ProtoResponseType }}* resp) {
return grpc::Status::OK;
}
{{- end }}
{{ end }}
`))
)
3 changes: 3 additions & 0 deletions dataplane/apigen/typeinfo/typeinfo.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ type GenFunc struct {
EntryVar string
ConvertFunc string
AttrConvertInsert string
IsStreaming bool
}

type APITemplate struct {
Expand Down Expand Up @@ -98,6 +99,7 @@ func Data(doc *docparser.SAIInfo, sai *saiast.SAIAPI, protoPackage, protoGoPacka
APIType: iface.Name,
APIName: apiName,
ProtoInclude: apiName + ".pb",
GRPCInclude: apiName + ".grpc.pb",
CCOutDir: ccOutDir,
}
}
Expand Down Expand Up @@ -578,6 +580,7 @@ func genStreamingRPC(docInfo *docparser.SAIInfo, apiName string, meta *saiast.Fu
ProtoRequestType: req.Name,
ProtoResponseType: "stream " + resp.Name,
ProtoRPCName: strcase.UpperCamelCase(name),
IsStreaming: true,
})
}
}
Expand Down
16 changes: 16 additions & 0 deletions dataplane/standalone/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -72,3 +72,19 @@ cc_binary(
":entrypoint",
],
)

cc_binary(
name = "server",
srcs = ["server.cc"],
linkopts = [
"-lprotobuf",
"-lgrpc++",
"-lglog",
"-lsai",
],
tags = ["manual"],
deps = [
"//dataplane/standalone/saiserver",
"@com_github_grpc_grpc//:grpc++",
],
)
14 changes: 14 additions & 0 deletions dataplane/standalone/saiserver/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
cc_library(
name = "saiserver",
srcs = glob(["*.cc"]),
hdrs = glob(
["*.h"],
),
visibility = ["//visibility:public"],
deps = [
"//dataplane/proto/sai:sai_cc_grpc",
"//dataplane/proto/sai:sai_cc_proto",
"@com_github_google_glog//:glog",
"@com_github_opencomputeproject_sai//:sai",
],
)
Loading

0 comments on commit e49cd40

Please sign in to comment.