Can I pass to build command flag and read inside code? - go

I want in init function to execute one of those two lines
func init() {
log.SetPrefix(">>>>>>>>>>>>> ")
log.SetOutput(ioutil.Discard)
}
How to achieve something similiar to C and macro definition in make file ?
I build go program like go build my_project and I don't have any custom make file. Can I pass to build command flag and read inside code ?

Create a string variable that you can test. Then set that string variable using -X passed to the linker. For example:
package main
var MODE string
func main() {
if MODE == "PROD" {
println("I'm in prod mode")
} else {
println("I'm NOT in prod mode")
}
}
Now, if you build this with just go build, MODE will be "". But you can also build it this way:
go build -ldflags "-X main.MODE PROD"
And now MODE will be "PROD". You can use that to modify your logic based on build settings. I generally use this technique to set version numbers in the binary; but it can be used for all kinds of build-time configuration. Note that it can only set strings. So you couldn't make MODE a bool or integer.
You can also achieve this with tags, which are slightly more complicated to set up, but much more powerful. Create three files:
main.go
package main
func main() {
doThing()
}
main_prod.go
// +build prod
package main
func doThing() {
println("I'm in prod mode")
}
main_devel.go
// +build !prod
package main
func doThing() {
println("I'm NOT in prod mode")
}
Now when you build with go build, you'll get your main_devel.go version (the filename doesn't matter; just the // +build !prod line at the top). But you can get a production build by building with go build -tags prod.
See the section on Build Constraints in the build documentation.

Related

Get Name of Current Module in Go

I am attempting to create named loggers automatically for HTTP handlers that I'm writing, where I am passed a function (pointer).
I'm using the code mentioned in this question to get the name of a function:
package utils
import (
"reflect"
"runtime"
)
func GetFunctionName(fn interface{}) string {
value := reflect.ValueOf(fn)
ptr := value.Pointer()
ffp := runtime.FuncForPC(ptr)
return ffp.Name()
}
I'm using this in my main function to try it out like so:
package main
import (
"github.com/naftulikay/golang-webapp/experiments/functionname/long"
"github.com/naftulikay/golang-webapp/experiments/functionname/long/nested/path"
"github.com/naftulikay/golang-webapp/experiments/functionname/utils"
"log"
)
type Empty struct{}
func main() {
a := long.HandlerA
b := path.HandlerB
c := path.HandlerC
log.Printf("long.HandlerA: %s", utils.GetFunctionName(a))
log.Printf("long.nested.path.HandlerB: %s", utils.GetFunctionName(b))
log.Printf("long.nested.path.HandlerC: %s", utils.GetFunctionName(c))
}
I see output like this:
github.com/naftulikay/golang-webapp/experiments/functionname/long.HandlerA
This is okay but I'd like an output such as long.HandlerA, long.nested.path.HandlerB, etc.
If I could get the Go module name (github.com/naftulikay/golang-webapp/experiments/functionname), I can then use strings.Replace to remove the module name to arrive at long/nested/path.HandlerB, then strings.Replace to replace / with . to finally get to my desired value, which is long.nested.path.HandlerB.
The first question is: can I do better than runtime.FuncForPC(reflect.ValueOf(fn).Pointer()) for getting the qualified path to a function?
If the answer is no, is there a way to get the current Go module name using runtime or reflect so that I can transform the output of runtime.FuncForPC into what I need?
Once again, I'm getting values like:
github.com/naftulikay/golang-webapp/experiments/functionname/long.HandlerA
github.com/naftulikay/golang-webapp/experiments/functionname/long/nested/path.HandlerB
github.com/naftulikay/golang-webapp/experiments/functionname/long/nested/path.HandlerC
And I'd like to get values like:
long.HandlerA
long.nested.path.HandlerB
long.nested.path.HandlerC
EDIT: It appears that Go does not have a runtime representation of modules, and that's okay, if I can do it at compile time that would be fine too. I've seen the codegen documentation and I'm having a hard time figuring out how to write my own custom codegen that can be used from go generate.
The module info is included in the executable binary, and can be acquired using the debug.ReadBuildInfo() function (the only requirement is that the executable must be built using module support, but this is the default in the current version, and likely the only in future versions).
BuildInfo.Path is the current module's path.
Let's say you have the following go.mod file:
module example.com/foo
Example reading the build info:
bi, ok := debug.ReadBuildInfo()
if !ok {
log.Printf("Failed to read build info")
return
}
fmt.Println(bi.Main.Path)
// or
fmt.Println(bi.Path)
This will output (try it on the Go Playground):
example.com/foo
example.com/foo
See related: Golang - How to display modules version from inside of code
If your goal is to just have the name of the module available in your program, and if you are okay with setting this value at link time, then you may use the -ldflags build option.
You can get the name of the module with go list -m from within the module directory.
You can place everything in a Makefile or in a shell script:
MOD_NAME=$(go list -m)
go build -ldflags="-X 'main.MODNAME=$MOD_NAME'" -o main ./...
With main.go looking like:
package main
import "fmt"
var MODNAME string
func main() {
fmt.Println(MODNAME) // example.com
}
With the mentioned "golang.org/x/mod/modfile" package, an example might look like:
package main
import (
"fmt"
"golang.org/x/mod/modfile"
_ "embed"
)
//go:embed go.mod
var gomod []byte
func main() {
f, err := modfile.Parse("go.mod", gomod, nil)
if err != nil {
panic(err)
}
fmt.Println(f.Module.Mod.Path) // example.com
}
However embedding the entire go.mod file in your use case seems overkill. Of course you could also open the file at runtime, but that means you have to deploy go.mod along with your executable. Setting the module name with -ldflags is more straightforward IMO.

Is there a way with Go to build more than one executable with one “go build” command? (as with Rust)? [duplicate]

This question already has answers here:
How to build multiple package binaries at once
(4 answers)
Closed 1 year ago.
With Rust,it is possible to generate a plurality of binary targets by simply designating them in the cargo.toml file associated with the target:
[[bin]]
name = "daemon"
path = "src/daemon/bin/main.rs"
[[bin]]
name = "client"
path = "src/client/bin/main.rs"
Is there a way to achieve the same with Go modules, i.e. build more than one executable with one “go build” command?
Example of the problem
//main1.o
//package main
func main() {
println("From main1")
}
// - -
//main2.o
//package main
func main() {
println("From main2")
}
// ---
go build:
// ---
//Result:
./main2.go:3:6: main redeclared in this block
/Users/sergehulne/Documents/code/Go/tst/main1.go:3:6: previous declaration
Apparently, the solution is to ignore the warnings associated with having more than one main() function in one Go module and to compile the files separately as if they were kind of sub-modules.
go mod init tst
package main
// file main1.go
func main() {
println("From main1")
}
package main
// file main2.go
func main() {
println("From main2")
}
and then sequentially:
go build main1.go
go build main2.go
Which yield respectively main1 and main2 (two executables)
It's awkward, but it works.

"flag provided but not defined" error in Go test despite the flag being defined in init()?

This question is similar to go test flag: flag provided but not defined, but since that question does not contain a minimal example and the answer is quite high-level, I'm asking it again. In a Go module with a main.go and a main_test.go,
.
├── go.mod
├── go.sum
├── main.go
└── main_test.go
The main.go defines a sayHi flag:
package main
import (
"flag"
"fmt"
)
var sayHi bool
func init() {
flag.BoolVar(&sayHi, "sayHi", false, "Say hi or not")
flag.Parse()
}
func main() {
if sayHi {
fmt.Println("Hi!")
}
}
and the main_test.go is just a placeholder test:
package main
import "testing"
func TestSayHi(t *testing.T) {
}
The problem is that if I try to run the tests, I get a "flag provided but not defined" error:
> go test ./...
flag provided but not defined: -test.testlogfile
Usage of /var/folders/tp/s7nwwdws1wj0z8s0vftppnnc0000gn/T/go-build952058535/b001/my-module.test:
-sayHi
Say hi or not
FAIL github.com/kurtpeek/my-module 0.192s
FAIL
From the answer of the aforementioned question,
You have to make sure that all flag definitions happen before calling flag.Parse(), usually by defining all flags inside init() functions.
I don't see how that is not being done here?
Create test binary with following command and then execute it with your argument.
> go test -c -o test
./test --sayHi=true
PASS
go test command does not pass along the arguments to underlying tests.
Update as of May 2022 - NOT fixed
So far the recommended approach is to move flag initalisation outside of init() (so stupid, I know!)
So I did that:
moving the code for flag handling to its own function,
and then calling that function at the top of main()
and my tests stopped complaining.
func initParseCommandLineFlags() {
// do your flag thing here
}
func main() {
initParseCommandLineFlags()
...
}
NOTE: A huge down side was that I had to move all of my initialisation out of init() into other setup functions, because my program startup can be modified by command line switches.
So I don't have init anymore :(
Reference: https://github.com/golang/go/issues/46869#issuecomment-865695953

Building multiple binaries using different packages and build tags

I have an application that needs to use different packages depending on the target operating system and then generate an executable. The core package has an interface that needs to be populated depending on the package that is used.
I've recently found out the best way to achieve this is by using build tags. But what I'm struggling with is getting the interface populated by the loaded package with the correct build tag(s). Or perhaps there is a better alternative approach.
Here is a visual of how I imagined this to look like:
Whichever Build Constraints you chose, you can achieve this with interfaces and implementing the interface with New() constructors. And each of those special files will have the special packages you seek, on a per file basis. This approach also enforces good decoupling by forcing you to break off only the raw parts you need to implement specific to each architecture.
I am a personal fan of file suffixes, instead of build tags, as it makes it extremely easy to know which file binds to what architecture - just by looking at the filename. A big plus is you don't have to mess with any build tags and it will JustWork™. So my examples below will use file file suffixes. Specifically, the format is:
*_GOOS
*_GOARCH
*_GOOS_GOARCH
For example, renderer_windows_amd64.go, renderer_windows_amd64_test.go, renderer_linux.go, renderer_linux_test.go, etc. You can find all the GOOS and GOARCH that Go supports here.
EDIT: Validated code on kiddo's laptop (tweaking a build error). ;) Note though, you can't call go run main.go as the architecture isn't known. You'll have to go build && ./mybinary to execute it locally to test.
main.go
package main
import (
"fmt"
"os"
)
func main() {
r, err := NewRenderer()
if err != nil {
fmt.Println(err)
os.Exit(1)
}
// call the Render() method for the specific goarch-goos.
if err := r.Render(); err != nil {
fmt.Println(err)
}
}
renderer.go
This is a simple file that only defines the interface. And maybe some common enums.
package main
// Renderer renders performs the platform-specific rendering.
type Renderer interface {
Render() error
}
// alternatively, you could define a global renderer struct
// here to use in each of hte files below if they are always
// the same. often not though, as you want to keep states of
// of specific architectures within each struct.
// type renderer struct {
// ...
// }
//
// func (r *renderer) Render() error {
// ...
// }
renderer_windows.go
Includes 32 and 64 bit builds. If you want to target, say 64 bit only for specific 64bit compiled DLLs, then you can target more specifically with renderer_windows_amd64.go.
package main
import (
"fmt"
// "WindowsDLLPackage" specific package to import for Windows
)
// renderer implements Renderer interface.
type renderer struct {
// you can include some stateful info here for Windows versions,
// to keep it out of the global heap.
GOOS string
WindowsRules bool
}
// NewRenderer instantiates a Windows version.
func NewRenderer() (Renderer, error) {
return &renderer{
GOOS: "Windows",
WindowsRules: true,
}, nil
}
// Render renders the Windows version.
func (r *renderer) Render() error {
// use WindowsDLLPackage.NewSomething()
fmt.Println(r.GOOS, r.WindowsRules)
return nil
}
renderer_linux.go
Linux does not include Android (nor darwin, aka macOS) builds.
package main
import (
"fmt"
// "LinuxPackage" specific package to import for Linux
)
// renderer implements Renderer interface.
type renderer struct {
// you can include some stateful info here for Linux versions,
// to keep it out of the global heap.
GOOS string
LinuxRules bool
}
// NewRenderer instantiates a Linux version.
func NewRenderer() (Renderer, error) {
return &renderer{
GOOS: "Linux",
LinuxRules: true,
}, nil
}
// Render renders the Linux version.
func (r *renderer) Render() error {
// use LinuxPackage.NewSomething()
fmt.Println(r.GOOS, r.LinuxRules)
return nil
}
renderer_android.go
Android only specific version.
package main
import (
"fmt"
// "AndroidPackage" specific package to import for Android
)
// renderer implements Renderer interface.
type renderer struct {
// you can include some stateful info here for Android versions,
// to keep it out of the global heap.
GOOS string
AndroidRules bool
}
// NewRenderer instantiates a Android version.
func NewRenderer() (Renderer, error) {
return &renderer{
GOOS: "Linux",
AndroidRules: true,
}, nil
}
// Render renders the Android version.
func (r *renderer) Render() error {
// use AndroidPackage.NewSomething()
fmt.Println(r.GOOS, r.AndroidRules)
return nil
}
generate different binaries
All that's left is to cross-compile:
$ GOOS=windows GOARCH=amd64 go build -o mybinary.exe
$ GOOS=linux GOARCH=amd64 go build -o mybinary_linux
$ GOOS=darwin GOARCH=amd64 go build -o mybinary_macos
# and whatever u do to get ios/android builds...
Notice how all of the files above are part of the single package main and they exist all in the same directory? This works because the compiler only picks the one file suffix for the GOOS (windows, linux or android - you can do darwin, freebsd and a lot more). During compilation, the compiler only implements NewRenderer() once by using that one file. This is also how you can use specific packages per file.
Also notice how func NewRenderer() (Renderer, error) returns the Renderer interface, not the renderer struct type.
The type renderer struct is completely agnostic to the rest of the package, and can be used for any architecture's means by holding any state you need.
Also note that there aren't any global variables here. I've often use this pattern with goroutines and channels for highly concurrent applications - with no mutex locking bottlenecks. Keeping things off the heap is critical to avoid mutex locking. You could easily do go r.Render() and let it spawn a goroutine. Or, call it a few million times.
Finally, notice how all of the filenames above are easily distinguishable of what platform they target?
Don't fight the tooling with build tags, let the tools work for you.
Coding tips above:
I exported the interface, Renderer, as all of this could be moved to a package outside of main quite easily. You don't want to export the struct versions. But, you may want to export the NewRenderer() init method.
Renderer follows GoLang Effective Go guidelines in using simple interfaces with a single function: Render. And those functions become the name of the interface with the suffix of "er" - yes, even if the name ends in 'er', we add 'er' to the end and it becomes type Renderer interface. IOW: it shouldn't be called RenderEngine and instead it should be called Renderer with a single method you are manipulating: Render(). This clearly defines a single focus for the tooling and code. Aka, "the Go Way."
Create two files somewhat like these:
// +build myBuildFlag
package mypackage
import package1
var important = package1.Foo
Other one:
// +build !myBuildFlag
package mypackage
import package2
var important = package2.Foo
Now whenever you use important it something different depending on your build flag.
Take a look at Dave Chaney's post which explains how to deal with building for specific platforms/architectures quite clearly: https://dave.cheney.net/2013/10/12/how-to-use-conditional-compilation-with-the-go-build-tool

How to call function from another file in Go

I want to call function from another file in Go. Can any one help?
test1.go
package main
func main() {
demo()
}
test2.go
package main
import "fmt"
func main() {
}
func demo() {
fmt.Println("HI")
}
How to call demo in test2 from test1?
You can't have more than one main in your package.
More generally, you can't have more than one function with a given name in a package.
Remove the main in test2.go and compile the application. The demo function will be visible from test1.go.
Go Lang by default builds/runs only the mentioned file. To Link all files you need to specify the name of all files while running.
Run either of below two commands:
$go run test1.go test2.go. //order of file doesn't matter
$go run *.go
You should do similar thing, if you want to build them.
I was looking for the same thing. To answer your question "How to call demo in test2 from test1?", here is the way I did it. Run this code with go run test1.go command. Change the current_folder to folder where test1.go is.
test1.go
package main
import (
L "./lib"
)
func main() {
L.Demo()
}
lib\test2.go
Put test2.go file in subfolder lib
package lib
import "fmt"
// This func must be Exported, Capitalized, and comment added.
func Demo() {
fmt.Println("HI")
}
A functional, objective, simple quick example:
main.go
package main
import "pathToProject/controllers"
func main() {
controllers.Test()
}
control.go
package controllers
func Test() {
// Do Something
}
Don't ever forget: Visible External Functions, Variables and Methods starts with Capital Letter.
i.e:
func test() {
// I am not Visible outside the file
}
func Test() {
// I am VISIBLE OUTSIDE the FILE
}
If you just run go run test1.go and that file has a reference to a function in another file within the same package, it will error because you didn't tell Go to run the whole package, you told it to only run that one file.
You can tell go to run as a whole package by grouping the files as a package in the run commaned in several ways. Here are some examples (if your terminal is in the directory of your package):
go run ./
OR
go run test1.go test2.go
OR
go run *.go
You can expect the same behavior using the build command, and after running the executable created will run as a grouped package, where the files know about eachothers functions, etc. Example:
go build ./
OR
go build test1.go test2.go
OR
go build *.go
And then afterward simply calling the executable from the command line will give you a similar output to using the run command when you ran all the files together as a whole package. Ex:
./test1
Or whatever your executable filename happens to be called when it was created.
Folder Structure
duplicate
|
|--duplicate_main.go
|
|--countLines.go
|
|--abc.txt
duplicate_main.go
package main
import (
"fmt"
"os"
)
func main() {
counts := make(map[string]int)
files := os.Args[1:]
if len(files) == 0 {
countLines(os.Stdin, counts)
} else {
for _, arg := range files {
f, err := os.Open(arg)
if err != nil {
fmt.Fprintf(os.Stderr, "dup2: %v\n", err)
continue
}
countLines(f, counts)
f.Close()
}
}
for line, n := range counts {
if n > 1 {
fmt.Printf("%d\t%s\n", n, line)
}
}
}
countLines.go
package main
import (
"bufio"
"os"
)
func countLines(f *os.File, counts map[string]int) {
input := bufio.NewScanner(f)
for input.Scan() {
counts[input.Text()]++
}
}
go run ch1_dup2.go countLines.go abc.txt
go run *.go abc.txt
go build ./
go build ch1_dup2.go countLines.go
go build *.go
You can import functions from another file by declaring the other file as a module. Keep both the files in the same project folder.
The first file test1.go should look like this:
package main
func main() {
demo()
}
From the second file remove the main function because only one main function can exist in a package. The second file, test2.go should look like below:
package main
import "fmt"
func demo() {
fmt.Println("HI")
}
Now from any terminal with the project directory set as the working directory run the command:
go mod init myproject.
This would create a file called go.mod in the project directory. The contents of this mod file might look like the below:
module myproject
go 1.16
Now from the terminal simply run the command go run .! The demo function would be executed from the first file as desired !!
as a stupid person who didn't find out what is going on with go module
should say :
create your main.go
in the same directory write this in your terminal
go mod init "your module name"
create a new directory and go inside it
create a new .go file and write the directory's name as package name
write any function you want ; just notice your function must starts with capital letter
back to main.go and
import "your module name / the name of your new directory"
finally what you need is writing the name of package and your function name after it
"the name of your new dirctory" + . + YourFunction()
and write this in terminal
go run .
you can write go run main.go instead.
sometimes you don't want to create a directory and want to create new .go file in the same directory, in this situation you need to be aware of, it doesn't matter to start your function with capital letter or not and you should run all .go files
go run *.go
because
go run main.go
doesn't work.
Let me try.
Firstly
at the root directory, you can run go mod init mymodule (note: mymodule is just an example name, changes it to what you use)
and maybe you need to run go mod tidy after that.
Folder structure will be like this
.
├── go.mod
├── calculator
│ └── calculator.go
└── main.go
for ./calculator/calculator.go
package calculator
func Sum(a, b int) int {
return a + b
}
Secondly
you can import calculator package and used function Sum (note that function will have Capitalize naming) in main.go like this
for ./main.go
package main
import (
"fmt"
"mymodule/calculator"
)
func main() {
result := calculator.Sum(1, 2)
fmt.Println(result)
}
After that
you can run this command at root directory.
go run main.go
Result will return 3 at console.
Bonus: for ./go.mod
module mymodule
go 1.19
ps. This is my first answer ever. I hope this help.

Resources