Project structure and importing packages - go

I'm just starting to learn Go and I'm wondering how to best organize my project and import packages. I created a project at $GOPATH/src/my_project. It contains the file main.go and the folder foo containing the file bar.go.
$GOPATH/src/my_project
main.go
foo/
bar.go
My main.go looks as follows
package main
import (
"./foo"
"fmt"
)
func main() {
fmt.Println("Main")
foo.Baz()
}
Here is the foo.go
package foo
import "fmt"
func Baz() {
fmt.Println("Baz")
}
It works the way I expect it to, but I wonder if this is really the right way to structure a project and import packages, because most tutorials only import packages from github and never from local.
Thanks for your answers..

Relative imports are possible in Go but they are not encouraged to be used.
Instead you always use the full path of the package. In your case that would be my_project/foo. This is is relative to the GOPATH/src folder.
However, since we have Go modules now which make things a tiny bit more complicated to start with but have a lot of advantages in the long run.
Just earlier today I gave a step by step guide on how to set up a new project with Go modules: After go install the import doesn't recognize the package
The module name in this guide is the basis for your project then and all paths in the module build on that path. Let me give an example:
Let's say you name your module github.com/yourName/myProject. Then you can import the subpackage foo with github.com/yourName/myProject/foo.
Note: The module path should be the same as the git URL, that way you can just get the module with go get. If you don't plan on ever uploading the project to github or another git URL, you should still choose a unique name. Starting with a domain you own is a good idea -- for uniqueness.

Related

Conflict in repo name and module name

I created the github.com project qjson/qjson-go that contains the package name qjson that you can see here. I named the github project this way because github.com/qjson/ contains other projects for different languages (e.g. qjson-c).
Unfortunately, I get the following error when I try to import the project as github.com/qjson/qjson-go:
$ go mod tidy
go: finding module for package github.com/qjson/qjson-go
go: downloading github.com/qjson/qjson-go v0.0.0-20210128102242-170c47e2db46
github.com/xxx/xxx imports
github.com/qjson/qjson-go: module github.com/qjson/qjson-go#latest found (v0.0.0-20210128102242-170c47e2db46), but does not contain package github.com/qjson/qjson-go
I’m apparently doing it wrong. I understand that due to the import statement we are then expected to use gjson-go as package identifier.
What must I do so that the git project can be named qjson-go and the package qjson ?
I assume that one solution is to create a sub-directory named qjson inside qjson-go and move all package files in it. The user would then import "github.com/qson/qson-go/qjson". Is that correct ? Is there another solution avoiding the stutter ?
This program works as expected:
package main
import (
"fmt"
"github.com/qjson/qjson-go/qjson"
)
func main() {
fmt.Println(qjson.ErrDivisionByZero)
}
The issue is that you are using this file structure:
qjson/engine.go
qjson/errors.go
When you should just be putting them at the top level, like this:
engine.go
errors.go
So you can either fix the directory, and tag a new version, or just leave the
files as is, and change your imports to match what I have above.

multiple Go modules in a monorepo and custom dir structure

I have 2 possibly related issues.
I have a test monorepo setup, with 2 subdirectories (mod1 and mod2).
Each one of them has a go.mod file in them, and each of the modules has a single .go file with basic printing code. in mod2 there is a subdirectory mod2_lib (that holds one of those simple .go files with basic printing code), since I read that Go modules are basically their own little GOPATH's.
Id like to call function Run() thats in a package mod2/mod2_lib from mod1, but all Im getting is build github.com/account_name/test/mod1: cannot find module for path github.com/account_name/test/mod2/mod2_lib.
here are the files Im using to figure this out:
mod1/t.go
package main
import (
"fmt"
"github.com/account_name/test/mod2/mod2_lib"
)
func main() {
fmt.Println("mod1")
mod2_lib.Run()
}
mod2/mod2_lib/t_lib.go
package mod2_lib
import "fmt"
func Run() {
fmt.Println("RUNS")
}
the second issue is that in this monorepo I'd like to have related Python and Rust code in top-level dirs py and rust. So I'd like to place all my Go packages in the go/src dir. How would other people import this go/src path into their project (possibly still having the "github.com/account_name/test/mod2/mod2_lib" as the import path, and not "github.com/account_name/test/go/src/mod2/mod2_lib")?
can anyone give me some pointers on these issues? I want to move to using Golang modules, and abandon the GOPATH.
the issue was that it was a private github.com repo. making it public fixed it! :) have to figure out authentication now for the module system.

Accessing local packages within a go module (go 1.11)

I'm trying out Go's new modules system and am having trouble accessing local packages. The following project is in a folder on my desktop outside my gopath.
My project structure looks like:
/
- /platform
- platform.go
- main.go
- go.mod
// platform.go
package platform
import "fmt"
func Print() {
fmt.Println("Hi")
}
// main.go
package main
import "platform"
func main() {
platform.Print()
}
go build main.go tells me
cannot find module for path platform
Let me define this first modules are collections of packages. In Go 11, I use go modules like the following:
If both packages are in the same project, you could just do the following:
In go.mod:
module github.com/userName/moduleName
and inside your main.go
import "github.com/userName/moduleName/platform"
However, if they are separate modules, i.e different physical paths and you still want to import local packages without publishing this remotely to github for example, you could achieve this by using replace directive.
Given the module name github.com/otherModule and platform, as you've called it, is the only package inside there. In your main module's go.mod add the following lines:
module github.com/userName/mainModule
require "github.com/userName/otherModule" v0.0.0
replace "github.com/userName/otherModule" v0.0.0 => "local physical path to the otherModule"
Note: The path should point to the root directory of the module, and can be absolute or relative.
Inside main.go, to import a specific package like platform from otherModule:
import "github.com/userName/otherModule/platform"
Here's a gentle introduction to Golang Modules
I would strongly suggest you to use go toolchain which takes care of these issues out of the box. Visual Studio Code with vscode-go plugin is really useful.
Problem here is that Go requires relative paths with respect to your $GOPATH/src or module in import statement. Depending on where you are in your GOPATH, import path should include that as well. In this case, import statement must include go module path in go.mod
GOPATH
Assume your project resides here:
$GOPATH/src/github.com/myuser/myproject
Your import path should be:
import "github.com/myuser/myproject/platform"
VGO
Assume your go.mod file is:
module example.com/myuser/myproject
Your import path should be:
import "example.com/myuser/myproject/platform"
As someone new to go I didn't immediately understand the accepted answer – which is great, by the way. Here's a shorter answer for those very new people!
In go modules/packages are expressed as urls that you import in your code:
import your.org/internal/fancy_module
But wait! My code isn't at a url, what's happening??
This is the cleverness of go. You pretend there's a url even when there isn't one. Because:
This makes including easier as no matter where your file is located the import uses the same url (the code stays the same even if the files move!)
You can have packages that having naming conflicts. So google.com/go-api/user doesn't conflict with the definitions at your.org/internal/user
Someday you might publish a url on GitHub and all the code will just work
That's all great Evan, but how do I import a relative path?
Good question! You can import by changing your go.mod file to have this line:
module fancy_module
go 1.16
replace your.org/fancy_module => ../path/to/fancy_module
Given the Golang Project structure
/
- /platform
- platform.go
- main.go
- go.mod
To access the methods or structs...etc (which are public) from local packages of /platform is simple, shown below
// main.go
package main
import (
p "./platform"
)
func main() {
p.Print()
}
this should work

How to import project specific go packages while maintaining a separate location for go packages that are common to totally different projects?

So I was developing a go application for the very first time. I came to know that there are two variables GOROOT and GOPATH which are used to maintain go packages. What I understand till now, is that GOROOT is for the go binary files and GOPATH is mainly for storing library and helper packages that is needed for projects.
Here is my current project structure -
/Users/john/work/project-mars
/Users/john/work/project-mars/main.go
/Users/john/work/project-mars/helper
/Users/john/work/project-mars/helper/helper.go
Content of main.go
package main
import (
"fmt"
"helper"
)
func main(){
fmt.Println("Hello")
}
Content of helper.go
package helper
import (
"fmt"
)
func SayWorld(){
fmt.Println("World")
}
And the go variables are -
GOROOT = /Users/john/apps/go
GOPATH = /Users/john/apps/go-packages
Problem:
Now when I perform the following command, I get this error -
mac-machine:project-mars john$ go build main.go
main.go:5:5: cannot find package "helper" in any of:
/Users/john/apps/go/src/helper (from $GOROOT)
/Users/john/apps/go-packages/src/helper (from $GOPATH)
I understand that GOPATH should be the project directory that I am working on. But I am concerned with keeping my projects and library packages in a modular way, so that when later I have a totally different project (i.e. project-aurora) which might use same github helper packages, that they are not downloaded two times, both in project-mars and project-aurora .
How can I avoid this redundancy while working on different projects ?
Update: It's not that I can not compile them. I can use the GOPATH as my project directory and use src,pkg,bin project layouts and also reorganize the files and finally get to compile the project. yeeeeppi. But my question is about resolving the redundancy of common package problem that appears in this single GOPATH way.
Please read How to Write Go Code carefully. It explains everything you need to know.
You don't use GOPATH as your project directory. Assuming you want to work with the standard Go tooling, your package source needs to be in the directory corresponding to its import path, just like any other package.
Your project should be located at $GOPATH/src/project-mars, which can be built via go install project-mars. The helper package should be located at $GOPATH/src/project-mars/helper, and imported via "project-mars/helper".
Rename your helper-lib folder to helper
Then move this folder from project-mars to the upper folder work
This should make your
import "helper"
statement in main.go work.

How to import local packages in go?

I am new to go and working on an example code that I want to localize.
In the original main.go import statement it was:
import (
"log"
"net/http"
"github.com/foo/bar/myapp/common"
"github.com/foo/bar/myapp/routers"
)
Now I have common and routers package in /home/me/go/src/myapp
So I converted the import statement to:
import (
"log"
"net/http"
"./common"
"./routers"
)
But when I run go install myapp I get these errors:
can't load package: /home/me/go/src/myapp/main.go:7:3: local import "./common" in non-local package
Also, when I use common and routers instead of ./common and ./routers in the import statement, I get:
myapp/main.go:7:3: cannot find package "common" in any of:
/usr/local/go/src/common (from $GOROOT)
/home/me/go/src/common (from $GOPATH)
myapp/main.go:8:2: cannot find package "routers" in any of:
/usr/local/go/src/routers (from $GOROOT)
/home/me/go/src/routers (from $GOPATH)
How can I fix this?
Well, I figured out the problem.
Basically Go starting path for import is $HOME/go/src
So I just needed to add myapp in front of the package names, that is, the import should be:
import (
"log"
"net/http"
"myapp/common"
"myapp/routers"
)
If you are using Go 1.5 above, you can try to use vendoring feature.
It allows you to put your local package under vendor folder and import it with shorter path.
In your case, you can put your common and routers folder inside vendor folder
so it would be like
myapp/
--vendor/
----common/
----routers/
------middleware/
--main.go
and import it like this
import (
"common"
"routers"
"routers/middleware"
)
This will work because Go will try to lookup your package starting at your project’s vendor directory (if it has at least one .go file) instead of $GOPATH/src.
FYI: You can do more with vendor, because this feature allows you to put "all your dependency’s code" for a package inside your own project's directory so it will be able to always get the same dependencies versions for all builds. It's like npm or pip in python, but you need to manually copy your dependencies to you project, or if you want to make it easy, try to look govendor by Daniel Theophanes
For more learning about this feature, try to look up here
Understanding and Using Vendor Folder by Daniel Theophanes
Understanding Go Dependency Management by Lucas Fernandes da Costa
I hope you or someone else find it helpfully
You should have created your package with go mod init e.g. go mod init github.com/my-org/my-package
Now in my-package you have a sub module called utils for example.
main.go
utils
|- randstr.go
And your randstr.go looks like this:
package utils
func RandStr(n int) string {
// TODO: Generate random string....
return "I am a random string"
}
And then anywhere in your project you would use exported functions from the utils package like this, for example in main.go:
package main
import (
"fmt"
// "github.com/my-org/my-package" is the module name at the
// top of your `go.mod`
"github.com/my-org/my-package/utils"
)
func main() {
fmt.Printf("Random string: %s\n", utils.RandStr(20))
}
Import paths are relative to your $GOPATH and $GOROOT environment variables. For example, with the following $GOPATH:
GOPATH=/home/me/go
Packages located in /home/me/go/src/lib/common and /home/me/go/src/lib/routers are imported respectively as:
import (
"lib/common"
"lib/routers"
)
an example:
in ./greetings, do go mod init example.com/greetings
from another module, do go mod edit -replace=example.com/greetings=../greetings
go get example.com/greetings
from the go tutorial
Local package is a annoying problem in go.
For some projects in our company we decide not use sub packages at all.
$ glide install
$ go get
$ go install
All work.
For some projects we use sub packages, and import local packages with full path:
import "xxxx.gitlab.xx/xxgroup/xxproject/xxsubpackage
But if we fork this project, then the subpackages still refer the original one.
Follow instructions here https://go.dev/doc/tutorial/call-module-code
Mainly you need the replace call in your go.mod file.
module example.com/hello
go 1.16
replace example.com/greetings => ../greetings
As in the question, the folder structure is:
/home/me/go/src/myapp
└─ common
└─ routers
So go to myapp dir
cd /home/me/go/src/myapp
Do
go mod init myapp
This will create a go.mod file which lets Go know the name of the module myapp so that when it’s looking at import paths in any package, it knows not to look elsewhere for myapp
Then you can do the following in the code:
import (
"log"
"net/http"
"myapp/common"
"myapp/routers"
)
Now package common and routers gets imported.
Another approach, available since go1.18, is to use a go.work file.
First, the local common package has to be a module, so provide a go.mod file inside the common folder:
module common
go 1.18
You can now create a go.work file in the root of your directory manually or call go work init, then go work use . and finally go work use ./common. It will look like this:
go 1.18
use (
.
./common
)
Finally you can import the package in your code by name
package main
import "common"
Just remember to not commit your go.work files :)
The key is how you name your module in the following command
go mod init <TheNameGiven>
Then refer the modules in the inner folder with,
TheNameGiven/folder
I have found the best solution here... Read More
Try to change the package name with the go mod init command.
So, I have go 1.17, and I have the same import problem. My project directory is $GOPATH/src/myswagger/app-swagger-test. I ran this command into app-swagger-test dir:
go mod init app-swagger-test
go mod tidy
In my new go.mod file the package name is app-swagger-test. For example, this import was wrong:
import (
...
"myswagger/app-swagger-test/internal/generated/restapi"
"myswagger/app-swagger-test/internal/generated/restapi/operations"
)
So I removed go.mod and go.sum. And I ran next commands into app-swagger-test dir:
go mod init myswagger/app-swagger-test
go mod tidy
After that all imports in the project were imported successfully. In the new go.mod file the first line is:
module myswagger/app-swagger-test
Maybe this information is common, but I did not find it. Thanks!

Resources