go generate stdout piped to gofmt to file - go

What is the syntax so go generate can pipe stdout from go run to gofmt and ultimately to a file? Below is simple example of what I have tried. Its in the file main.go. I can't find any examples of this after searching. Thank you.
Edit: ultimately I would like to use go generate and have it write a formatted file.
//go:generate go run main.go | go fmt > foo.go
package main
import "fmt"
const content = `
package main
func foo() string {return "Foo"}
`
func main() {
fmt.Print(content)
}

Use the format package directly instead of running a shell:
//go:generate go run main.go
package main
import (
"go/format"
"io/ioutil"
"log"
)
const content = `
package main
func foo() string {return "Foo"}
`
func main() {
formattedContent, err := format.Source([]byte(content))
if err != nil {
log.Fatal(err)
}
err = ioutil.WriteFile("foo.go", formattedContent, 0666)
if err != nil {
log.Fatal(err)
}
}
Avoid using a shell like bash because the shell may not be available on all systems where the Go tools run.

Related

go run command doesn't pick up non-go files in the same path

Folder structure:
- dev.env
- go.mod
- main.go
dev.env:
ENV="DEV"
PASSWORD="DEV!##$%"
main.go:
package main
import (
"fmt"
"log"
"os"
"path/filepath"
"github.com/joho/godotenv"
)
var environment string
func init() {
fmt.Println("ENV: ", environment)
dir, err := filepath.Abs(filepath.Dir(os.Args[0]))
if err != nil {
log.Fatal(err)
}
devEnvPath := filepath.Join(dir, "dev.env")
_ = godotenv.Load(devEnvPath)
}
func main() {
fmt.Println("PASSWORD", os.Getenv("PASSWORD"))
}
Command:
go run -ldflags="-X 'main.environment=DEV'" .
Output:
ENV: DEV
PASSWORD
It works with go build but curious why it doesn't work with go run
As a general rule, don't use go run unless in the most trivial of use cases - it is the most common footgun in the Go community.
As #Marc pointed out the error stems from the go run binary is built in a temporary directory. To keep things simple, just use a relative path in your directory. This will work for both go build & go run:
dir := "."
devEnvPath := filepath.Join(dir, "dev.env")
err := godotenv.Load(devEnvPath)
if err != nil {
log.Fatal(err)
}
As of Go 1.16, the simplest way to access files from the directory containing a package's source code is to use //go:embed and the embed package to embed those files into the compiled binary:
//go:embed dev.env
var devEnv string
func init() {
m, err := godotenv.Unmarshal(devEnv)
…
}

How can I run/call a Makefile from golang code?

I need to compile proto files and generate pb files dynamically. I have my protoc commands in a Makefile. I am planning to trigger this from golang init() function.
How can I call this Makefile from my golang code? Tried finding solution, but all of them suggest how I can achieve this the other way round.
Thanks.
As a continuation to what Muffin Top mentioned, I achieved this using the below code:
import (
"bytes"
"fmt"
"log"
"os/exec"
)
func init() {
e := exec.Command("make", "all")
var out bytes.Buffer
e.Stdout = &out
err := e.Run()
if err != nil {
log.Fatal(err)
}
fmt.Printf("Output: %q\n", out.String())
}
You should use the go:generate syntax:
package main
//go:generate protoc bla bla bla
When you run your "go build" command, it will then run everything in your code prefixed with //go:generate
https://golang.org/pkg/cmd/go/internal/generate/

How to run variable as shell script file

I need to run a variable as shell script file in golang. I tried like below code
package main
import (
"fmt"
"os/exec"
)
func main() {
var namespaceYaml string = `#!/bin/bash
docker verson`
out, err := exec.Command(namespaceYaml).Output()
fmt.Println(err, string(out))
}
But I cannot get any result. I cannot find where is the mistake.
Please anyone to fix this issue. Thanks in advance.
From official doc:
func Command(name string, arg ...string) *Cmd
Command returns the Cmd struct to execute the named program with the given arguments.
Try this:
package main
import (
"fmt"
"os/exec"
)
func main() {
out, err := exec.Command("docker", "version").Output()
if err != nil {
log.Fatal(err)
}
fmt.Printf("Docker version is: %s\n", out)
}
Useful links for further details:
exec
examples
Note: make sure docker is installed on your machine.

FilePaths in Go

So this is the example from Programming in Go by Mark Summerfield.
package main
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
)
var britishAmerican = "british-american.txt"
func init() {
dir, _ := filepath.Split(os.Args[0])
britishAmerican = filepath.Join(dir, britishAmerican)
}
func main() {
rawBytes, err := ioutil.ReadFile(britishAmerican)
if err != nil {
fmt.Println(err)
}
text := string(rawBytes)
usForBritish := make(map[string]string)
lines := strings.Split(text, "\n")
fmt.Println(lines)
for _, line := range lines {
fields := strings.Fields(line)
if len(fields) == 2 {
usForBritish[fields[0]] = fields[1]
}
}
fmt.Println(usForBritish)
}
When I run this code with the init() func commented out, it works perfectly fine. If I leave it in I get this error:
open /var/folders/l6/rdqtyrfd303dw1cz8qvlfcvc0000gn/T/go- build652175567/command-line-arguments/_obj/exe/british-american.txt: no such file or directory exit status 1
My question is, why does the init() func not grab the file from the appropriate directory?
You change the variable britishAmerican in the init function. Without init(), the program looks in the current directory (no path given, only the file name). With init(), it looks in the path where the executable is (os.Args[0]). And with go run main.go, the directory with the executable is not the current working directory.
You should use go build to build the binary and then run it, or you should tell us what you want to achieve (as written by #RoninDev).
The MCVE I've mentioned could look like this:
package main
import (
"io/ioutil"
"log"
"os"
"path/filepath"
)
var filename = "foo.txt"
func init() {
// change to true and things break
if false {
dir, _ := filepath.Split(os.Args[0])
filename = filepath.Join(dir, filename)
}
}
func main() {
// requires a file 'foo.txt' in the current directory
_, err := ioutil.ReadFile(filename)
if err != nil {
log.Fatal(err)
}
}
It can (of course) be even shorter, but this should be enough for the others in the community to see what is going on.
It looks to me like the program is expecting a file called british-american.txt in the directory that the executable is in.
That is what the code in init() does - it finds the path the the executable and constructs a path to the dictionary relative to that.
I can see from your error message that you are using go run to run the code. This makes a temporary executable in /tmp and runs that. If you leave the init() code in then it will look for the dictionary in the /tmp directory and it won't find it. If you take the init() code out it will look for the dictionary in the current directory and it will succeed.
If you want to use it as the author intended then use go build to build a binary and then run it - that will work.

Communication with other Go process

I have a program that reads a filename from the console and executes go run filename.go.
// main.go
package main
import (
"bufio"
"fmt"
"log"
"os"
"os/exec"
)
func main() {
console := bufio.NewReader(os.Stdin)
fmt.Print("Enter a filename: ")
input, err := console.ReadString('\n')
if err != nil {
log.Fatalln(err)
}
input = input[:len(input)-1]
gorun := exec.Command("go", "run", input)
result, err := gorun.Output()
if err != nil {
log.Println(err)
}
fmt.Println("---", input, "Result ---")
fmt.Println(string(result))
}
In the same directory, I have another file like this.
// hello.go
package main
import "fmt"
func main() {
fmt.Println("Hello, World!")
}
When I input "hello.go" in the console, that file is run, and its output gets returned to the parent Go process. However, I have another program like this.
// count.go
package main
import (
"fmt"
"time"
)
func main() {
i := 0
for {
time.Sleep(time.Second)
i++
fmt.Println(i)
}
}
Except, because this program never returns, my parent process is left hanging forever. Is there a way to communicate with different Go processes? I'm thinking something like channels for goroutines, but for processes. I need to be able to receive live stdout from the child process.
The problem I'm trying to solve is dynamically executing Go programs from a directory. Go files will be added, removed, and modified daily. I'm kind of trying to make something like Go Playgrounds. The main process is a webserver serving webpages, so I can't shut it down all the time to modify code.
Don't use go run, you need to do what go run is doing yourself to have the go program be a direct child of your server process.
Using go build -o path_to/binary source_file.go will give you more control. Then you can can directly execute and communicate with the resulting binary.

Resources