School me on Go packages, imports and protos - go

I'm mostly familiar with Python and a bazel build environment. I'm trying to rewrite a portion of code in Go and I'm struggling to get the proto imports to align properly.
In github.com/djhedges/exit_speed/gps.proto I have set go_package like so
option go_package = "github.com/djhedges/exit_speed/gps_go_proto";
I've added a reflector.proto which imports gps.proto and reuses some of the messages defined in there.
import "gps.proto";
I compile gps.proto with
protoc -I ./ --go_out=./ --go_opt=paths=source_relative --python_out ./ gps.proto
And compile reflector.proto with
LD_PRELOAD=/usr/lib/arm-linux-gnueabihf/libatomic.so.1 python3 -m grpc_tools.protoc -I ./ --go_out=./ --go_opt=paths=source_relative --python_out ./ --grpc_python_out ./ reflector.proto
And finally in exit_speed/reflector.go I try to import gps_go_proto with
import gpspb "github.com/djhedges/exit_speed/gps_go_proto"
Which errors with
go: finding github.com/djhedges/exit_speed/gps_go_proto latest
reflector.go:21:2: unknown import path "github.com/djhedges/exit_speed/gps_go_proto": cannot find module providing package github.com/djhedges/exit_speed/gps_go_proto
I would prefer to keep gps.proto in the root directory if possible because I have protos logged into data files and I believe moving this breaks the proto parsing. I suppose I could write a migration script.
I'm also confused as to how setup multiple protos with different go packages and imports. Do go packages have to have a dedicated folder?

If I'm understanding go packages correctly the package name must map to a directory. By changing --go_out=./ to --go_out=./gps_go_proto seems to solve my issue without having to move the proto files. It does mean I have some additional directories but that seems okay.

As the Go Specification import section says:
Import declarations
An import declaration states that the source file containing the declaration depends on functionality of the imported package (§Program initialization and execution) and enables access to exported identifiers of that package. The import names an identifier (PackageName) to be used for access and an ImportPath that specifies the package to be imported.
ImportDecl = "import" ( ImportSpec | "(" { ImportSpec ";" } ")" ) .
ImportSpec = [ "." | PackageName ] ImportPath .
ImportPath = string_lit .
(snipped here, and I broke some links; use the first link to get the text with the links). The string literal is the familiar:
import "github.com/..."
part; the PackageName, or a dot, are optional. Returning to the text:
The interpretation of the ImportPath is implementation-dependent but it is typically a substring of the full file name of the compiled package and may be relative to a repository of installed packages.
In your own answer, you add:
If I'm understanding go packages correctly the package name must map to a directory.
That's not necessarily so. It's up to the implementation to decide what the string literal means. In general, though, the Go build system will find a directory full of files, using this path. It may also use a version control system, such as Git, to fill a directory full of files, so that the compiler can find them.
Something that I found a bit surprising, but in the end, makes perfect sense (and is described in the spec), is this: the package name found in the package clause:
package somename
statement at the top of some imported file, is sometimes irrelevant. A directory full of files that implement package x will have them all say package x, of course. But it's the y identifier in the line:
import y "path/to/x"
in your package that supplies the name you use to access exported identifiers. The name you use here, with the import having y as the package name, is y, even if the package itself says package x. The package clause in package x is, in effect, just recommending the name x, so that you can write:
import "path/to/x"
if you'd like to refer to these as x.Thing.
There's no need for the string literal to mention x either: perhaps all the files are in something found via path/to/z, and you write:
import "path/to/z"
and then (still) write x.Thing because files in path/to/z/*.go recommend x as the package name.
How protoc uses this
Regardless of the above, the protobuf Go generators will print, in the generated name*.pb.go* Go code, an import directive. That import directive will read:
import "some/path/here"
You can specify the string literal using option go_package. The protobuf compiler doesn't need to interpret this itself. But, as the documentation notes, these name.pb.go files themselves will be dropped into a directory somewhere. They will also contain a package pkg line, of course.
The location of each output file depends on whether you use --go_opt=paths=source_relative. If you do, the go_package option string becomes irrelevant here.
The .pb.go file generator takes the go_package option string, strips off everything up to and including the last slash, and uses the remaining string for the package line.
The second point means that regardless of what the Go system itself does, how it finds these files, and how you write your own imports, the recommended package name in those files will be the last component of whatever was in the go_package option. So in a sense, all you really need is the final component. But if your Go implementation needs more than that, you can supply more.
The first point means that if you don't use --go_opt=paths=source_relative, the rest of the text in the go_package option matters: the Go generator will try to use that string to produce output files. So if your Go implementation needs more, and you do supply more, and it doesn't match what you want for output file names, you'll definitely need the --go-opt.

Related

Is there a way to define import alias for go generated code from proto files

I am import two different proto files in my current proto file as below
import "author/message/name.proto"
import "reader/message/details.proto"
in name.proto I have go_package = "author/message" and in details.proto I have go_package = "reader/message" because of which when pb.go is generated import alias is showing as below
message1 "author/message"
message2 "reader/message"
I am trying to find a way where i can name alias like authormsg and readermsg respectively during pb.go generation. I have checked documentation but couldnt find anyway to do that.
You can add the explicit Go package name like this:
name.proto: option go_package="author/message;authormsg"
details.proto: option go_package="reader/message;readermsg"
Means you add the package name separated with a semicolon (;) after the import path.
Note:
This usage is discouraged since the package name will be derived by default from the import path in a reasonable manner.
Protocol Buffers documentation

Invalid import path - Go + windows

When attempting to import to files from the windows file system:
import (
pb "github.com\\sewelol\\sgx-decryption-service\\decryptionservice"
dev "github.com\\sewelol\\sgx-decryption-service\\device"
"google.golang.org\\rpc"`
)
I get this error
server\main.go:10:5: invalid import path: "github.com\\sewelol\\sgx-decryption-service\\decryptionservice"
I have checked $PATH enviroment variable includes the directory that has github.com, and $GOROOT also is set up to point to the Go installation.
I assume it is something to do with the file paths themselves, but I can't find any information on how to do filepaths in the windows environment.
Thanks
You have to use forward slashes / in import paths (of import declarations), even if you're on Windows.
Spec: Import declarations:
Implementation restriction: A compiler may restrict ImportPaths to non-empty strings using only characters belonging to Unicode's L, M, N, P, and S general categories (the Graphic characters without spaces) and may also exclude the characters !"#$%&'()*,:;<=>?[\]^{|}` and the Unicode replacement character U+FFFD.
Any compiler may exclude the backslash \ character among others. Even if you would use one that doesn't, your code would not be portable.
So instead try:
import (
pb "github.com/sewelol/sgx-decryption-service/decryptionservice"
dev "github.com/sewelol/sgx-decryption-service/device"
"google.golang.org/rpc"
)

protoc generate golang code from two proto files which belong to different package

I have two proto src file, in same folder, let's say:
the 1st one is foo.proto
syntax = "proto3";
package foo;
the second is bar.proto, which need import foo.proto
syntax = "proto3";
import "foo.proto";
package bar;
you can see they have different package name, when I use protoc to generate them one by one, (foo.proto as the 1st of cause), I have two golang file generated, but I have to put them into 2 directory (you can't put different package name file inside same directory, golang )
foo/foo.pb.go
bar/bar.pb.go
but inside bar.pb.go the import are using local import path, which is like
import foo "."
I'm tweeking several options which protoc provided, but didn't make this work, any suggestion?
I believe add option go_package should be the correct solution

How to handle case sensitive import collisions

I am using a third party library in GoLang that has previously had import paths in different case. Initially a letter was lower case then the author changed it to uppercase.
Some plugin authors updated their libraries and others didn't. In the meantime the original library author reverted the case change.
Now I find myself in a state where my application won't build due to to case import collisions.
How can one go about fixing this?
Many thanks
You could vendor the dependencies, and then go into the vendor/ directory and manually change (try greping or seding the dependency), the dependencies.
For an introduction to vendoring, try here, https://blog.gopheracademy.com/advent-2015/vendor-folder/
The original repo can still live in your GOPATH, but the 'corrected' version can go in the vendor folder, in which the compiler will look first when linking dependencies.
There are many tools for vendoring, I use govendor
Edit
As mkopriva mentions in the comments, you can refactor import names using the gofmt tool:
gofmt -w -r '"path/to/PackageName" -> "path/to/packagename"' ./
gofmt -w -r 'PackageName.x -> packagename.x' ./
The lowercase single character identifier is a wildcard.
from the docs
The rewrite rule specified with the -r flag must be a string of the form:
pattern -> replacement
Both pattern and replacement must be valid Go expressions. In the pattern, single-character lowercase identifiers serve as wildcards matching arbitrary sub-expressions; those expressions will be substituted for the same identifiers in the replacement.
Just if anyone wonders why this error might occur in your project: Make sure all imports use either lowercase or uppercase path, but not mixed.
So like this:
one.go -> "github.com/name/app/login"
another.go -> "github.com/name/app/login"
And not like this:
one.go -> "github.com/name/app/Login"
another.go -> "github.com/name/app/login"

GO 'split' package configuration setup

Go newbie alert:
I've created a package that I want to split up into several individual files, but I wanted to have the layout like so:
\--src
|---myLib.go
|---myLib
|--- file1.go
|--- file2.go
| ...
|--- fileN.go
Each .go file has "package myLib" at the top, but I'm not sure how to get the pathing to work.
The main file "myLib.go" contains the "package globals" and high level function calls (exported), while the file1, ..., fileN files essentially contain the "private" non-exported functions used internally by the exported functions.
Not sure how to setup the imports, every combination I've tried I get the message:
"no such file or directory" when I try to include the files in the myLib folder. If I put all the files at the same level, then it works fine. From an organizational standpoint, I prefer to have the one file with the exported functions separate and all the non-exported functions/files consolidated in a sub-folder. Any suggestions / examples?
Thx
You cannot do this. You must do it like it is intended:
The file myLib.go must go in the myLib folder. (But you may call the folder whatever you like.)
Have a look at how the standard lib is organized.
The key piece is: all the .go and _test.go files in the same directory should start with the same package <packagename> statement. The .go files in the directory that contains a file with the main function should all have package main statement - they are used to create an executable (named with the directory name).
the file1, ..., fileN files essentially contain the "private" non-exported functions used internally by the exported functions.
Only the upper-cased function and type names can be used in other packages (including in package main). The setup you have should work as long as all the .go files in the same directory share the same package <pkgname> statement - for example, if you put package mylib in file1.go, file2.go... fileN.go (I would also recommend to avoid uppercase directory names to avoid confusion).
It is also worth to review the "How to Write Go Code" document https://golang.org/doc/code.html or the screencast linked from it, should help with the Go codebase layout conventions.

Resources