Skip to content

amenzhinsky/dbus-codegen-go

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

84 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

dbus-codegen-go

D-Bus Introspection Data Format Go code generator.

The project depends only on github.com/godbus/dbus module and cannot be used separately because it operates its data types.

CLI and generated code API is a subject to change until v1.0.0.

Overview

The tool generates two types of code-bases: server and client (or can be limited to just one of them by using -client-only or -server-only options), so if we take use the following XML:

<node>
	<interface name="my.awesome.interface">
		<method name="IToA">
			<arg name="In" type="x" direction="in" />
			<arg name="Out" type="s" direction="out" />
		</method>
		<property name="Powered" type="b" access="readwrite" />
		<signal name="SomethingHappened">
			<arg name="object_path" type="o" />
			<arg name="what" type="s" />
		</signal>
	</interface>
</node>

Client

The program generates statically compiled bindings that are used to create objects that wrap around dbus.BusObject:

obj := NewMy_Awesome_Interface(conn.Object("my.awesome.service", "/my/awesome/service"))

Now we can call its methods:

s, err := obj.IToA(context.Background(), 666)
if err != nil {
    return err
}
fmt.Printf("itoa(%d) = %s", 666, s)

Retrieve or set property values:

powered, err := obj.GetPowered(context.Background())
if err != nil {
    return err
}
fmt.Printf("powered = %t", powered)

if err := o.SetPowered(context.Background(), true); err != nil {
    return err
}

Handling signals requires type assertions:

sigt := (*My_Awesome_Interface_SomethingHappenedSignal)(nil)
if err := AddMatchSignal(conn, sigt); err != nil {
    return err
}
defer RemoveMatchSignal(conn, sigt)

sigc := make(chan *dbus.Signal, 1)
conn.Signal(sigc)
for sig := range sigc {
    s, err := LookupSignal(sig)
    if err != nil {
        if err == ErrUnknownSignal {
            continue
        }
        return err
    }

    switch typed := s.(type) {
    case *My_Awesome_Interface_SomethingHappenedSignal:
        fmt.Printf("%s happened at %s", typed.Body.What, typed.Body.ObjectPath)
    }
}

Server

Now we can implement the server interface, all interfaces are postfixed with er, in this example it's My_Awesome_Interfaceer.

It's recommended to embed the corresponding generated unimplemented structure for forward compatible implementations:

type server struct {
	*UnimplementedMy_Awesome_Interface
}

func (s *server) IToA(in int64) (string, *dbus.Error) {
	return strconv.Itoa(int(in)), nil
}

And now we can export the implementation:

if err := ExportMy_Awesome_Interface(conn, "/my/awesome/service", &srv{}); err != nil {
	return err
}

Emitting signals is done reusing the same structures generated for the client side:

if err := Emit(conn, &My_Awesome_Interface_SomethingHappenedSignal{
    Path: "/org/my/iface",
    Body: &My_Awesome_Interface_SomethingHappenedSignalBody{
        ObjectPath: "/org/obj",
        What:       "something terrible",
    },
}); err != nil {
	return err
}

Installation

You can install it with go get:

GO111MODULE=on go get -u github.com/amenzhinsky/dbus-codegen-go

Or clone the repo and build it manually:

git clone https://github.com/amenzhinsky/dbus-codegen-go.git .
go install

Make sure $(go env GOPATH)/bin is in your $PATH.

Usage

The program treats command-line arguments as paths to XML files or reads out stdin if none given:

dbus-send --system \
	--type=method_call \
	--print-reply=literal \
	--dest=org.freedesktop.systemd1 \
	/org/freedesktop/systemd1 \
	org.freedesktop.DBus.Introspectable.Introspect \
	> org.freedesktop.systemd1.xml

dbus-codegen-go org.freedesktop.systemd1.xml
dbus-codegen-go < org.freedesktop.systemd1.xml

Apart of reading existing files it can introspect real D-Bus destinations recursively:

dbus-codegen-go -dest=org.freedesktop.systemd1

You may also want to safe the introspection file that combines all interfaces in the tree on some system for further reuse. For that simply add -xml flag:

dbus-codegen-go -xml -dest=org.freedesktop.systemd1

Here's an example of a bit more advanced usage, where we're changing the generated code's package name and narrow down the introspected interfaces to just two we need, plus we're trimming org.freedesktop prefix to shorten generated structure names:

dbus-codegen-go \
	-dest=org.freedesktop.systemd1 \
	-package=systemd \
	-camelize \
	-only=org.freedesktop.systemd1.Manager \
	-only=org.freedesktop.systemd1.Service \
	-prefix=org.freedesktop.systemd1

Annotations

  • org.freedesktop.DBus.Method.NoReply = true

    dbus.FlagNoReplyExpected flag is used for method calling.

  • org.freedesktop.DBus.Deprecated = true

    // Deprecated comment is added for better IDE integration.

Testing

To test the package simply run:

go test ./...

tests directory contains integration tests that technically compile the package's binary and use it for code generation and further compilation with test file scenarios, so make sure that go is in your $PATH.

Troubleshooting

parse error: ...

The generated output by printer package cannot be parsed by gofmt and that is the package issue, disable it with -gofmt=false and inspect the result or create an issue with input xml files and the generated code.

TODO

  • name conflicts resolver
  • sophisticated tests

Contributing

All contributions are welcome, just create an issue or issue a pull request.

About

D-Bus code-generation for golang

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages