Skip to content

Commit

Permalink
Add go_protobuf_library
Browse files Browse the repository at this point in the history
Following the per-language `*_protobuf_library` pattern, here we add `go_protobuf_library` support and as simple example showing how to use it.
  • Loading branch information
traviscrawford committed May 24, 2018
1 parent 06aa372 commit ce19e86
Show file tree
Hide file tree
Showing 17 changed files with 389 additions and 1 deletion.
5 changes: 5 additions & 0 deletions contrib/go/3rdparty/go/github.com/golang/protobuf/proto/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Copyright 2018 Pants project contributors (see CONTRIBUTORS.md).
# Licensed under the Apache License, Version 2.0 (see LICENSE).

go_remote_library(rev='v1.1.0')

25 changes: 25 additions & 0 deletions contrib/go/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,31 @@ You can run your Go tests with `./pants test [go targets]`. Any
[standard Go tests](https://golang.org/pkg/testing/) found amongst the targets will be compiled and
run with output sent to the console.

## Protocol Buffers

Pants integrates [Go support for protocol buffers](https://github.com/golang/protobuf) with the
`go_protobuf_library` target.

Go targets may depend on a `go_protobuf_library` targets as if it were a `go_library` target.
Behind the scenes, Pants will generate Go code from the protobuf sources and exposes it as if it
were a regular Go library.

For example,
[`contrib/go/examples/src/protobuf/org/pantsbuild/example/route/BUILD`](https://github.com/pantsbuild/pants/blob/master/contrib/go/examples/src/protobuf/org/pantsbuild/example/route/BUILD)
defines a `go_protobuf_library` target. Notice how it depends on another `go_protobuf_library`
to satisfy imports in the IDL file.

!inc[start-at=go_protobuf_library](examples/src/protobuf/org/pantsbuild/example/route/BUILD)

[`contrib/go/examples/src/go/distance/BUILD`](https://github.com/pantsbuild/pants/blob/master/contrib/go/examples/src/go/distance/BUILD)
depends on the `go_protobuf_library`, which transitively depends on another protobuf library.

!inc[start-at=go_binary](examples/src/go/distance/BUILD)

In Go, we can import and use the protobuf generated code as it were regular Go code.

!inc[start-at=main](examples/src/go/distance/main.go)

## Working with other Go ecosystem tools

Go and the Go ecosystem provide rich tool support. From native Go tools like `go list` and `go vet`
Expand Down
11 changes: 11 additions & 0 deletions contrib/go/examples/src/go/distance/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Copyright 2018 Pants project contributors (see CONTRIBUTORS.md).
# Licensed under the Apache License, Version 2.0 (see LICENSE).

# This target must be build with protoc that recognizes `option go_package`
# ./pants run contrib/go/examples/src/go/distance/ --protoc-version=3.5.1
go_binary(
dependencies=[
'contrib/go/examples/src/protobuf/org/pantsbuild/example/route:route-go',
]
)

27 changes: 27 additions & 0 deletions contrib/go/examples/src/go/distance/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Copyright 2018 Pants project contributors (see CONTRIBUTORS.md).
// Licensed under the Apache License, Version 2.0 (see LICENSE).

package main

import (
"github.com/golang/protobuf/proto"
"pantsbuild/example/distance"
"pantsbuild/example/route"
)

func main() {
r := &route.Route{
Name: proto.String("example_route"),
Distances: []*distance.Distance{
{
Unit: proto.String("parsecs"),
Number: proto.Int64(27),
},
{
Unit: proto.String("mm"),
Number: proto.Int64(2),
},
},
}
println(r.String())
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Copyright 2018 Pants project contributors (see CONTRIBUTORS.md).
# Licensed under the Apache License, Version 2.0 (see LICENSE).

go_protobuf_library(name='distance-go')

Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Copyright 2018 Pants project contributors (see CONTRIBUTORS.md).
// Licensed under the Apache License, Version 2.0 (see LICENSE).

package org.pantsbuild.example.distance;

option go_package = "pantsbuild/example/distance";

/**
* Structure for expressing distance measures: 8mm, 12 parsecs, etc.
* Not so useful on its own.
*/
message Distance {
optional string unit = 1;
required int64 number = 2;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Copyright 2018 Pants project contributors (see CONTRIBUTORS.md).
# Licensed under the Apache License, Version 2.0 (see LICENSE).

go_protobuf_library(
name='route-go',
dependencies=[
'contrib/go/examples/src/protobuf/org/pantsbuild/example/distance:distance-go',
],
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Copyright 2018 Pants project contributors (see CONTRIBUTORS.md).
// Licensed under the Apache License, Version 2.0 (see LICENSE).

package org.pantsbuild.example.route;

import "org/pantsbuild/example/distance/distances.proto";

option go_package = "pantsbuild/example/route";


/**
* Structure for expressing a simple route with distances
* Not so useful on its own.
*/
message Route {
optional string name = 1;
repeated org.pantsbuild.example.distance.Distance distances = 2;
}
4 changes: 4 additions & 0 deletions contrib/go/src/python/pants/contrib/go/register.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

from pants.contrib.go.targets.go_binary import GoBinary
from pants.contrib.go.targets.go_library import GoLibrary
from pants.contrib.go.targets.go_protobuf_library import GoProtobufLibrary
from pants.contrib.go.targets.go_remote_library import GoRemoteLibrary
from pants.contrib.go.targets.go_thrift_library import GoThriftLibrary
from pants.contrib.go.tasks.go_binary_create import GoBinaryCreate
Expand All @@ -19,6 +20,7 @@
from pants.contrib.go.tasks.go_fetch import GoFetch
from pants.contrib.go.tasks.go_fmt import GoFmt
from pants.contrib.go.tasks.go_go import GoEnv, GoGo
from pants.contrib.go.tasks.go_protobuf_gen import GoProtobufGen
from pants.contrib.go.tasks.go_run import GoRun
from pants.contrib.go.tasks.go_test import GoTest
from pants.contrib.go.tasks.go_thrift_gen import GoThriftGen
Expand All @@ -29,6 +31,7 @@ def build_file_aliases():
targets={
GoBinary.alias(): TargetMacro.Factory.wrap(GoBinary.create, GoBinary),
GoLibrary.alias(): TargetMacro.Factory.wrap(GoLibrary.create, GoLibrary),
GoProtobufLibrary.alias(): GoProtobufLibrary,
GoThriftLibrary.alias(): GoThriftLibrary,
'go_remote_libraries': TargetMacro.Factory.wrap(GoRemoteLibrary.from_packages,
GoRemoteLibrary),
Expand All @@ -39,6 +42,7 @@ def build_file_aliases():

def register_goals():
task(name='go-thrift', action=GoThriftGen).install('gen')
task(name='go-protobuf', action=GoProtobufGen).install('gen')
task(name='go', action=GoBuildgen).install('buildgen')
task(name='go', action=GoGo).install('go')
task(name='go-env', action=GoEnv).install()
Expand Down
69 changes: 69 additions & 0 deletions contrib/go/src/python/pants/contrib/go/subsystems/protoc_gen_go.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# coding=utf-8
# Copyright 2018 Pants project contributors (see CONTRIBUTORS.md).
# Licensed under the Apache License, Version 2.0 (see LICENSE).

from __future__ import (absolute_import, division, generators, nested_scopes, print_function,
unicode_literals, with_statement)

import logging
import os

from pants.backend.codegen.protobuf.subsystems.protoc import Protoc
from pants.base.workunit import WorkUnitLabel
from pants.scm.git import Git
from pants.subsystem.subsystem import Subsystem, SubsystemError
from pants.util.dirutil import safe_mkdir
from pants.util.memo import memoized_method

from pants.contrib.go.subsystems.go_distribution import GoDistribution


logger = logging.getLogger(__name__)


class ProtocGenGo(Subsystem):
"""A compiled protobuf plugin that generates Go code.
For details, see https://github.com/golang/protobuf
"""
options_scope = 'protoc-gen-go'

@classmethod
def register_options(cls, register):
super(ProtocGenGo, cls).register_options(register)
register('--version', default='v1.1.0',
help='Version of protoc-gen-go plugin to use when generating code')

@classmethod
def subsystem_dependencies(cls):
return super(ProtocGenGo, cls).subsystem_dependencies() + (Protoc.scoped(cls), GoDistribution,)

@memoized_method
def select(self, context):
self.get_options()
workdir = os.path.join(self.get_options().pants_workdir, self.options_scope,
'versions', self.get_options().version)
tool_path = os.path.join(workdir, 'bin/protoc-gen-go')

if not os.path.exists(tool_path):
safe_mkdir(workdir, clean=True)

# Checkout the git repo at a given version. `go get` always gets master.
repo = Git.clone('https://github.com/golang/protobuf.git',
os.path.join(workdir, 'src/github.com/golang/protobuf'))
repo.set_state(self.get_options().version)

go = GoDistribution.global_instance()
result, go_cmd = go.execute_go_cmd(
cmd='install',
gopath=workdir,
args=['github.com/golang/protobuf/protoc-gen-go'],
workunit_factory=context.new_workunit,
workunit_labels=[WorkUnitLabel.BOOTSTRAP],
)

if result != 0:
raise SubsystemError('{} failed with exit code {}'.format(go_cmd, result))

logger.info('Selected {} binary bootstrapped to: {}'.format(self.options_scope, tool_path))
return tool_path
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# coding=utf-8
# Copyright 2018 Pants project contributors (see CONTRIBUTORS.md).
# Licensed under the Apache License, Version 2.0 (see LICENSE).

from __future__ import (absolute_import, division, generators, nested_scopes, print_function,
unicode_literals, with_statement)

from pants.base.payload import Payload
from pants.build_graph.target import Target

from pants.contrib.go.targets.go_local_source import GoLocalSource
from pants.contrib.go.targets.go_target import GoTarget


class GoProtobufLibrary(Target):
"""A Go library generated from Protobuf IDL files."""

default_sources_globs = '*.proto'

def __init__(self,
address=None,
payload=None,
sources=None,
**kwargs):
"""
:param sources: protobuf source files
:type sources: :class:`pants.source.wrapped_globs.FilesetWithSpec` or list of strings. Paths
are relative to the BUILD file's directory.
"""
payload = payload or Payload()
payload.add_field('sources',
self.create_sources_field(sources, address.spec_path, key_arg='sources'))

super(GoProtobufLibrary, self).__init__(payload=payload, address=address, **kwargs)

@classmethod
def alias(cls):
return "go_protobuf_library"


class GoProtobufGenLibrary(GoTarget):

def __init__(self, sources=None, address=None, payload=None, **kwargs):
payload = payload or Payload()
payload.add_fields({
'sources': self.create_sources_field(sources=sources,
sources_rel_path=address.spec_path,
key_arg='sources'),
})
super(GoProtobufGenLibrary, self).__init__(address=address, payload=payload, **kwargs)

@property
def import_path(self):
"""The import path as used in import statements in `.go` source files."""
return GoLocalSource.local_import_path(self.target_base, self.address)
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import hashlib

from pants.base.fingerprint_strategy import FingerprintStrategy

from pants.contrib.go.targets.go_binary import GoBinary


Expand Down
Loading

0 comments on commit ce19e86

Please sign in to comment.