Suppose I have a Go project which act as shared library in another Go project. How do I markup Go symbols (consts, structs, vars) that they are used outside this project?
I guess the underlying problem is that I have a hard time knowing which code uses said symbols.
Please note: This is not about semantic versioning, of which I am very well aware and which I use. I know Semver can help to identify breaking changes.
Instead, this is about finding out if I actually break one of my own projects (compared to: This symbol should be unexported or used outside the package). I am thinking of some sort of annotation which don't exist in Go.
As an aside, IntelliJ doesn't know either and marks those symbols as "Unnecessarily exported". Maybe an IntelliJ-centric solution could suffice.
To illustrate my problem:
package sharedlib
import "time"
// MyFavoriteTimeFormat is a blablabla...
const MyFavoriteTimeFormat = Time.RFC3339
package dependingproject
import "github.com/thething/sharedlib"
import "time"
func convertToString(timestamp time.Time) string {
return timestamp.Format(sharedlib.MyFavoriteTimeFormat)
}
When I happily rename MyFavoriteTimeFormate and release it, the code will break in the depending project when it updates the dependency.
Don't export anything until some other package needs it. If another package needs something, then do export, and then you'll know that if something is exported, it is because it is used outside of the package. And do not do breaking changes on exported identifiers. If you really must, then increment major version. Using go modules, that won't break existing other packages, they will continue to use the old version.
If your module is broken down into multiple packages (because it is "big"), and you wish to export something solely for the other packages of your module, then use the internal package concept, so it will still be "unexported" (unimportable) to other modules. For details, see Can I develop a go package in multiple source directories?
Related
I'm working on a library with multiple layers of functionality. I want developers to be able to import only the parts they need, ie mylib-core, mylib-feature1, mylib-feature2, etc. Each lives in its own git repo. I'd also like to provide a simple mylib package which exposes a default set of functionality which is useful for developers new to the library. See d3.js version 4+ for something very similar to what I'm trying to accomplish.
The problems I've run into are
Apparently you can't share a package name between packages. This is a problem because it would be nice to import all the desired repos and then simply have everything available under the mylib name.
I don't see an obvious way to re-export functionality, in order to build the default mylib package.
Are there good solutions or a more idomatic go way to accomplish what I'm shooting for?
Answering your question, there is no idiomatic way of doing what you want. It is common in JavaScript to import a library and export its members without interference. This is not the case in Golang.
I advise you to host your whole library under a single repo, and split the functionality to packages. The Go compiler will only compile and use the imported packages.
And a tip for the future, Go is very different than almost any other language you previously know 😅
I'm working with ory-am / hydra and it makes me bug in the Golang library gopkg.in/gorethink/gorethink.v3, the error is as follows:
../../../gopkg.in/gorethink/gorethink.v3/cluster.go:10:2: case-insensitive import collision: "github.com/sirupsen/logrus" and "github.com/Sirupsen/ logrus "
this after adding go app.go
If you have any suggestions with which you can guide me to solve this incident
That package went through a very painful, and regretful, rename some time back. It was previously Sirupsen/logrus, and was renamed to sirupsen/logrus. The latter is erroneously considered more idiomatic by some, but Go handles mixed case imports with no problem, so the rename was unnecessary, and the author regrets it. But renaming back would just add to the confusion, so the decision was made to stick with the unnecessary lowercase version.
But that's all in the past. At this point, the lowercase version is the only working one.
The behavior you have observed is usually the result of importing two (or more) packages which depend on the logrus package, one depending on the older name, the other depending on the newer name. Therefore, if you have a dependency that still has the upper-case version as a dependency, that library needs to be updated.
From the project readme:
Case-sensitivity
The organization's name was changed to lower-case--and this will not be changed back. If you are getting import conflicts due to case sensitivity, please use the lower-case import: github.com/sirupsen/logrus.
Alternately, you could vendor an old version of the library with the capitalized import name and update all of your imports to use that version.
I want to make some changes to the Go crypto/tls standard library.
Is making a copy of crypto/tls in the vendor folder a good way to do this?
It almost works, it seems the vendored is copy used when I compile the application (Caddy webserver). Apart from one error I get:
go/src/github.com/user/caddy/caddytls/httphandler.go:40: cannot use "vendor/crypto/tls".Config literal (type *"vendor/crypto/tls".Config) as type *"crypto/tls".Config in field value
Is there a way of casting to get around this one error? Doesn't sound like good practice to me though.
I would have thought that the vendored copy would always be used, but it seems something is still using the standard crypto/tls library? (I think "net/http" is. Do I have to vendor this too?)
I actually had to do that. The most practical way is to copy and modify the package (as well as its internal dependencies) - this includes some import paths. And it is not really vendored (vendoring is basically using unmodified packages, otherwise vendoring tools will not work), it is a fork, under a different name. I would guess that caddy does not need the modified version - if it does, you need to fork and modify caddy as well.
It goes without saying, but be extremely careful when modifying crypto/tls package - I for example has to make a minimal change that does not really modify TLS operation (I needed to be able to derive key material from the session master secret and randoms).
Also, you have to fully realize that this has significant cost - when a new version of go is out, one that potentially has updates for the crypto/tls package or its dependencies, you will have to apply your changes once again, manually. Committing a diff between the vanilla version and your version helps. I don't think this is at all practical for non-trivial changes (mine were quite limited - a new public field in Config, few lines of code in handshakes, a new interface).
I need the same functionality. It seems like the crypto/tls package does not allow the reading of custom TLS extensions added from the client side in the ClientHello payload. It would be great to be able to check for any custom extensions and then marshal them out accordingly.
It is a pity that this is not a separate package as we could then in our go.mod file be able to use replace to specify a custom TLS package.
e.g
replace golang.org/x/crypto/tls => ./tls
Then running go mod vendor.
Where the ./tls is my local version with the changes applied.
I'm developing a tiny project that has a single package main. AFAIK, best-practice for small Golang binary projects is to have all code in a single (main) namespace, so that's what I've done.
Just curious, within a package main, is it best practice to keep functions/constants/variables exported (MyFunction) or unexported (myFunction)?
Really it doesn't matter. main packages can't be imported so whether you export them or not doesn't matter in that regard.
However if you do export them then tools like golint will encourage you to document them so maybe that's a good reason to go ahead an export them.
The most important thing is to be consistent. I prefer to name un-exported items in package main starting with lowercase even though they can't be exported. This is because it's an additional hint that these entities aren't used outside their containing package. You don't need to know that the declaration is in main to know that the thing being declared is not used externally.
I'm writing a library in Go. I'm planning to distribute it, and with a main requirement of 'without source codes'.
For testing, I have created two workspaces like following,
WS1
bin/
pkg/linux_amd64/lib.a
src/lib/src.go
WS2
bin/
pkg/
src/main/main.go
My first workspace (WS1) is the actual dummy library, which has some utility functions. Second workspace (WS2) has main function which uses the package (lib.a) from WS1.
Everything was working good until I remove the sources from WS1. If I remove the directory /lib/src.go in WS1, I'm getting the following error during go build,
main.go:5:2: cannot find package "lib" in any of:
/usr/local/go/src/pkg/lib (from $GOROOT) ../Testing/ws1/src/lib
(from $GOPATH)
The above message indicates us that we should keep the source files as well. Precompiled binary packages alone cannot be used directly.
Based on few suggestions online, we may keep some dummy sources with timestamp value lesser than binary packages' timestamp. But, this doesn't seems to be a feasible solution for us. What happens if timestamp of the dummy sources got updated unfortunately?
I have seen similar issue discussed here,
https://github.com/golang/go/issues/2775
My Questions:
Distributing the sources is the only possibility in Golang?
Why Go is not providing a provision for using '.a' files directly?
If keeping the source is mandatory for Go, why this small thing is
not mentioned anywhere in Go? (or) Am I missing something here?
Thanks in advance for your help guys!
The Go compiler just needs the .a files. If you ship them anybody will be able to use your package without the source code.
BUT your users will have to invoke the compiler (e.g. 6g, not the go tool) manually. If you ship a myfoo.a file and a dummy source myfoo.go containing just package myfoo and the timestamp of myfoo.a is newer than that of myfoo.go (and you put everything in place) you may use the go tool.
Update: Newer version of the go tool detect deleted files and require all files (possibly empty) with the proper filenames and older timestamps in the src folder.
Managing a timestamp should not be a dealbreaker.
Don't get fooled that the go tool is Go: It is a dead convenient tool to build, test, get, whatever your Go code, but it is neither the language nor the compiler nor the linker.
BTW: There is really no point in not distributing the sources.
The binary-only packages will be available in go1.7 (August 2016) - https://tip.golang.org/doc/go1.7
This release adds experimental, minimal support for building programs using binary-only packages, packages distributed in binary form without the corresponding source code. This feature is needed in some commercial settings but is not intended to be fully integrated into the rest of the toolchain. For example, tools that assume access to complete source code will not work with such packages, and there are no plans to support such packages in the “go get” command.
The proposal is at https://github.com/golang/proposal/blob/master/design/2775-binary-only-packages.md , https://tip.golang.org/pkg/go/build/#hdr-Binary_Only_Packages has more information about the new feature.
The binary-only packages is supported in go 1.7 now.
You can only provide .a files and fake go files without source code to distribute it now.
Here is a detailed example and a script of Go1.7 binary package generator.
myframework/frameImplement.go
package myframework
import "fmt"
func Hello(name string) string {
return fmt.Sprintf("Hello, %s!", name)
}
main/main.go
package main
import (
"fmt"
"golang-binary-package-generator/myframework"
)
func main() {
fmt.Println(" start program ")
fmt.Println(" print program :", myframework.Hello("print something now"))
}
If I want to hide my framework's source code, just build it with go build -i -o $GOPATH/pkg/framework.a, then modify your source code to
//go:binary-only-package
package framework
//you can add function prototype here for go doc etc, but no necessary.
, which you can use my binary package generator(script) to help you.
The decision to drop the feature:
cmd/go: drop support for binary-only packages