The module system

digraph {
  node [fontsize=12 fontname=monospace shape=box]
  nodesep=0.3
  ranksep=0.5
  subgraph cluster_module {
    label="Module"
    style=dashed
    gomod [width="2.6"
      label="go.mod file\n(marks the module root)"]
    modpkg [shape=box3d width="2.4"
      label="module packages"]
    note_modpkg [shape=note width="3.2"
      label="Contains libraries/exe/tests\n(only 1 main per package)"]
  }
  subgraph cluster_env {
    label="Environment"
    style=dashed
    gopath [width="2.0"
      label="GOPATH\n(User level)"]
    goroot [width="2.0"
      label="GOROOT\n(System level)"]
    pathsrc [width="1.8"
      label="src/\n(source libs)"]
    pathpkg [width="1.8"
      label="pkg/\n(binary libs)"]
    rootsrc [width="1.8"
      label="src/\n(source libs)"]
    rootpkg [width="1.8"
      label="pkg/\n(binary libs)"]
  }

  gopath -> { pathsrc pathpkg }
  goroot -> { rootsrc rootpkg }
  gomod -> modpkg [style=invis]
  modpkg -> note_modpkg [arrowhead=none color=grey]
}

Examples : run/build/install a file/pkg/module

alias go="GOENV=/tmp/testenv go"
go env -w GOPATH=/tmp/gopath GOBIN=/tmp/gobin GOCACHE=/tmp/gocache
mkdir -p /tmp/gopath /tmp/gobin /tmp/gocache \
      /tmp/standalone_pkg /tmp/gopath/src/dependent_pkg \
      /tmp/mymodule/ext_dep /tmp/mymodule/another_bin /tmp/mymodule/dependent_pkg

echo '
package main
import "fmt"
func main() { fmt.Println("helloworld") }
' > /tmp/helloworld.go
echo '
package main
import ( "fmt" ; "dependent_pkg" )
func main() { fmt.Println("helloworld", dependent_pkg.Doit()) }
' > /tmp/hello_with_dep.go
echo '
package dependent_pkg
func Doit() string { return "dependent_pkg" }
' > /tmp/some_lib.go

# All these work : `run` builds into a temp file and execs, `build` creates exe in curdir, `install` builds into GOBIN
go run helloworld.go
go build helloworld.go
go install helloworld.go

sed 's/helloworld/standalone_pkg/' /tmp/helloworld.go > /tmp/standalone_pkg/standalone.go
# FAILS since standalone_pkg is looked for in GOPATH
go run standalone_pkg
# OK looks for pkg in curdir
go run ./standalone_pkg
# Needs `-o` otherwise exe name will conflict
go build -o a.out ./standalone_pkg
go install ./standalone_pkg

mv some_lib.go /tmp/gopath/src/dependent_pkg
# Alternatively it works by importing `./dependent_pkg` and `dependent_pkg/some_lib.go` in the curdir.
go run hello_with_dep.go
go build hello_with_dep.go
go install hello_with_dep.go

# Create go module : anywhere within it 'mymodule' refers to the root of all pkg contained within
pushd /tmp/mymodule
go mod init mymodule

# FAIL inside a module you can only dependent on system packages in `GOROOT` or packages in the module itself
# (or binary packages installed in GOPATH/pkg)
go run .
# Be careful it is a trap ! `GOROOT` can only have a single path ?!
#go env -w GOROOT='/usr/lib/go;/tmp/goroot'
sed 's/"dependent_pkg"/"mymodule\/dependent_pkg"/' /tmp/hello_with_dep.go > /tmp/mymodule/hello_with_dep.go
mv /tmp/gopath/src/dependent_pkg /tmp/mymodule
# run/build/install will work as expected (exe name is the module name)
go run .

sed 's/helloworld/another_hello_with_dep/' /tmp/mymodule/hello_with_dep.go > /tmp/mymodule/another_hello_with_dep.go
# FAIL (`main` redefinition) module can only have 1 executable
go run .

mv /tmp/mymodule/another_hello_with_dep.go /tmp/mymodule/another_bin
go run ./another_bin
# NOTE the `...` it will install all exe under the module
go install ./...
# Be careful it is a trap ! `...` (instead of `./...`) will run tests for the whole system ?!
go test ./...

sed 's/"fmt"/( "fmt" ; "golang.org/x/blog/content/cover" )/' /tmp/helloworld.go > /tmp/mymodule/ext_dep/ext_dep.go
# Looks for external dependencies on other modules and installs then into GOPATH/pkg
go mod tidy
# Upgrades all module dependencies to its latest version
go get ./...

unalias go

Interop with c : cgo

Static or dynamic linking

# Suppose you want the go program to link against mylibs/libthingy.so (or libthingy.a)
go env -w CGO_CFLAGS="-Ithingy_include" \
          CGO_LDFLAGS="-Lmylibs -lthingy.so"
# To link statically use CGO_LDFLAGS="mylibs/libthingy.a"

Does the go program need to be built with the same compiler/linker options as the c library it links with ?

Channel behavior

  Open & Empty Open & Full Closed Nil
Read Blocks Reads (value, true) Reads (zero-val, false) Blocks forever
Write Ok Blocks Panic ! Blocks forever
Close Ok (fail for receive-only channel) Blocks (unless buffered channel) Panic ! Panic !