Skip to content

Commit

Permalink
Add support for generating CGO flags
Browse files Browse the repository at this point in the history
  • Loading branch information
lassilaiho committed Sep 22, 2023
1 parent d395caa commit ef874c0
Show file tree
Hide file tree
Showing 10 changed files with 110 additions and 18 deletions.
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ generate:
--message-module-prefix "github.com/tiiuae/rclgo/$$dest_path" \
--license-header-path ./license-header.txt \
--include-go-package-deps ./... \
--cgo-flags-path "" \
|| exit 1
rm "$$dest_path/msgs.gen.go" || exit 1
go run ./cmd/rclgo-gen generate-rclgo \
Expand Down
5 changes: 5 additions & 0 deletions cmd/rclgo-gen/cmd/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,9 @@ var generateCmd = &cobra.Command{
if err := gen.GenerateROS2AllMessagesImporter(); err != nil {
return fmt.Errorf("failed to generate all importer: %w", err)
}
if err := gen.GenerateCGOFlags(); err != nil {
return fmt.Errorf("failed to generate CGO flags: %w", err)
}
return nil
},
Args: validateGenerateArgs,
Expand Down Expand Up @@ -125,6 +128,7 @@ func configureFlags(cmd *cobra.Command, destPathDefault string) {
cmd.PersistentFlags().StringArray("include-go-package-deps", nil, "Include only packages which are dependencies of listed Go packages. Can be passed multiple times. If multiple include options are passed, the union of the matches is generated.")
cmd.PersistentFlags().Bool("ignore-ros-distro-mismatch", false, "If true, ignores possible mismatches in sourced and supported ROS distro")
cmd.PersistentFlags().String("license-header-path", "", "Path to a file containing a license header to be added to generated files. By default no license is added.")
cmd.PersistentFlags().String("cgo-flags-path", "cgo-flags.env", `Path to file where CGO flags are written. If empty, no flags are written. If "-", flags are written to stdout.`)
bindPFlags(cmd)
}

Expand Down Expand Up @@ -188,6 +192,7 @@ func getGogenConfig(cmd *cobra.Command) (*gogen.Config, error) {
MessageModulePrefix: modulePrefix,
RootPaths: getRootPaths(cmd),
DestPath: destPath,
CGOFlagsPath: getString(cmd, "cgo-flags-path"),

RegexIncludes: rules,
ROSPkgIncludes: viper.GetStringSlice(getPrefix(cmd) + "include-package-deps"),
Expand Down
1 change: 1 addition & 0 deletions examples/custom_message_package/greeter/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ replace github.com/tiiuae/rclgo v0.0.0 => ../../..
require github.com/tiiuae/rclgo v0.0.0

require (
github.com/alessio/shellescape v1.4.2 // indirect
github.com/fsnotify/fsnotify v1.6.0 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
Expand Down
2 changes: 2 additions & 0 deletions examples/custom_message_package/greeter/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3f
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/alessio/shellescape v1.4.2 h1:MHPfaU+ddJ0/bYWpgIeUnQUqKrlJ1S7BfEYPM4uEoM0=
github.com/alessio/shellescape v1.4.2/go.mod h1:PZAiSCk0LJaZkiCSkPv8qIobYglO3FPpyFjDCtHLS30=
github.com/bradleyjkemp/cupaloy/v2 v2.8.0 h1:any4BmKE+jGIaMpnU8YgH/I2LPiLBufr6oMMlVBbn9M=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
Expand Down
2 changes: 1 addition & 1 deletion examples/custom_message_package/greeter/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import (
"github.com/tiiuae/rclgo/pkg/rclgo"
)

//go:generate go run github.com/tiiuae/rclgo/cmd/rclgo-gen generate -d msgs --include-go-package-deps ./...
//go:generate go run github.com/tiiuae/rclgo/cmd/rclgo-gen generate -d msgs --include-go-package-deps ./... --cgo-flags-path ""

func run() error {
rclArgs, restArgs, err := rclgo.ParseArgs(os.Args[1:])
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module github.com/tiiuae/rclgo
go 1.20

require (
github.com/alessio/shellescape v1.4.2
github.com/bradleyjkemp/cupaloy/v2 v2.8.0
github.com/kivilahtio/go-re v0.1.8
github.com/mitchellh/go-homedir v1.1.0
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3f
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/alessio/shellescape v1.4.2 h1:MHPfaU+ddJ0/bYWpgIeUnQUqKrlJ1S7BfEYPM4uEoM0=
github.com/alessio/shellescape v1.4.2/go.mod h1:PZAiSCk0LJaZkiCSkPv8qIobYglO3FPpyFjDCtHLS30=
github.com/bradleyjkemp/cupaloy/v2 v2.8.0 h1:any4BmKE+jGIaMpnU8YgH/I2LPiLBufr6oMMlVBbn9M=
github.com/bradleyjkemp/cupaloy/v2 v2.8.0/go.mod h1:bm7JXdkRd4BHJk9HpwqAI8BoAY1lps46Enkdqw6aRX0=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
Expand Down
90 changes: 84 additions & 6 deletions pkg/gogen/gogen.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ package gogen
import (
"errors"
"fmt"
"io"
"io/fs"
"os"
"path"
Expand All @@ -21,6 +22,7 @@ import (
"strings"
"text/template"

"github.com/alessio/shellescape"
"github.com/kivilahtio/go-re/v0"
)

Expand Down Expand Up @@ -52,6 +54,7 @@ type Config struct {
MessageModulePrefix string
RootPaths []string
DestPath string
CGOFlagsPath string

RegexIncludes RuleSet
ROSPkgIncludes []string
Expand Down Expand Up @@ -103,7 +106,7 @@ func (g *Generator) GenerateRclgoFlags() error {
"rclgo flags",
filepath.Join(g.config.DestPath, "pkg/rclgo/flags.gen.go"),
rclgoFlags,
nil,
templateData{"ROSIncludes": rclgoROSIncludes},
)
}

Expand Down Expand Up @@ -142,6 +145,75 @@ func (g *Generator) GenerateROS2AllMessagesImporter() error {
)
}

var rclgoROSIncludes = []string{
"rcl",
"rmw",
"rosidl_runtime_c",
"rosidl_typesupport_interface",
"rcutils",
"rcl_action",
"action_msgs",
"unique_identifier_msgs",
"builtin_interfaces",
"rcl_yaml_param_parser",
}

func includeDirFlag(rootPath, rosPkg string) string {
return fmt.Sprintf("-I%s", filepath.Join(rootPath, "include", rosPkg))
}

func libDirFlag(rootPath string) string {
return fmt.Sprintf("-L%[1]s -Wl,-rpath=%[1]s", filepath.Join(rootPath, "lib"))
}

func prependFlagEnv(envVar string, flags []string) string {
return strings.Join(flags, " ") + " " + os.Getenv(envVar)
}

func writeFlagEnv(w io.Writer, envVar string, flags stringSet) error {
_, err := fmt.Fprintf(w, "export CGO_%s=%s\n", envVar, shellescape.Quote(prependFlagEnv(envVar, flags.ToSortedSlice())))
return err
}

func (g *Generator) GenerateCGOFlags() error {
if g.config.CGOFlagsPath == "" {
return nil
}
PrintErrln("Generating CGO flags:", g.config.CGOFlagsPath)
libDirs := stringSet{}
includes := stringSet{}
for _, rootPath := range g.config.RootPaths {
libDirs.Add(libDirFlag(rootPath))
for _, dep := range rclgoROSIncludes {
includes.Add(includeDirFlag(rootPath, dep))
}
for pkgAndType, imports := range g.cImportsByPkgAndType {
pkg, _, err := parsePkgAndType(pkgAndType)
if err != nil {
PrintErrf("Failed to parse package and type from %s: %v\n", pkgAndType, err)
continue
}
includes.Add(includeDirFlag(rootPath, pkg))
for imp := range imports {
includes.Add(includeDirFlag(rootPath, imp))
}
}
}
var err error
out := os.Stdout
if g.config.CGOFlagsPath != "-" {
out, err = mkdir_p(g.config.CGOFlagsPath)
if err != nil {
return err
}
defer out.Close()
}
if err := writeFlagEnv(out, "CFLAGS", includes); err != nil {
return err
}
return writeFlagEnv(out, "LDFLAGS", libDirs)
}

type rosPkgRef struct {
Interfaces map[Metadata]string
Generated bool
Expand Down Expand Up @@ -443,12 +515,10 @@ func (g *Generator) generateServiceGoFiles(parser *parser, srv *ROS2Service) err
}

func (g *Generator) generateCommonPackageGoFile(pkgAndType string, cImports stringSet) error {
i := strings.LastIndex(pkgAndType, "_")
if i < 0 || i > len(pkgAndType)-1 {
return errors.New("package type suffix is missing or incorrect")
cPkg, pkgType, err := parsePkgAndType(pkgAndType)
if err != nil {
return err
}
cPkg := pkgAndType[:i]
pkgType := pkgAndType[i+1:]
return g.generateGoFile(
filepath.Join(g.config.DestPath, cPkg, pkgType, "common.gen.go"),
ros2PackageCommonTemplate,
Expand All @@ -459,3 +529,11 @@ func (g *Generator) generateCommonPackageGoFile(pkgAndType string, cImports stri
},
)
}

func parsePkgAndType(pkgAndType string) (pkg string, typ string, err error) {
i := strings.LastIndex(pkgAndType, "_")
if i < 0 || i > len(pkgAndType)-1 {
return "", "", errors.New("package type suffix is missing or incorrect")
}
return pkgAndType[:i], pkgAndType[i+1:], nil
}
14 changes: 3 additions & 11 deletions pkg/gogen/templates.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions pkg/gogen/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,13 @@ import (
"io"
"os"
"path/filepath"
"sort"
"strings"
"unicode"
"unicode/utf8"

"github.com/kivilahtio/go-re/v0"
"golang.org/x/exp/maps"
"golang.org/x/exp/slices"
"golang.org/x/tools/go/packages"
)
Expand Down Expand Up @@ -200,6 +202,14 @@ func (s stringSet) AddFrom(s2 stringSet) {
}
}

func (s stringSet) ToSlice() []string { return maps.Keys(s) }

func (s stringSet) ToSortedSlice() []string {
vals := s.ToSlice()
sort.Strings(vals)
return vals
}

func actionHasSuffix(msg *ROS2Message, suffixes ...string) bool {
if msg.Type == "action" {
for _, suffix := range suffixes {
Expand Down

0 comments on commit ef874c0

Please sign in to comment.