The context template function available to go list has some useful data, even after ignoring the overlap with go env -json. There's no way to output the context as json, making it hard to use the results programatically.
cc: @bcmills @rsc @ianlancetaylor @alandonovan
go list should output a json array, but it output each element of array , i need to add , before every element
@xuruiray that's unrelated to this issue. When go list -json returns a sequence of json documents. It's not returning an array in a single json document. This is extremely unlikely to change as much code relies on the current format.
@xuruiray See https://github.com/golang/go/issues/27655#issuecomment-420993215.
We can't change the declaration of go/build.Context to have json: tags on its fields, as that is a backward incompatible change. I suppose it could have a MarshalJSON method.
As a workaround, can you extract the fields you need in a convenient format based on a command such as this one?
$ go list -f '{{printf "{GOROOT=%q, GOOS=%q, GOPATH=%q, CgoEnabled=%t, UseAllFiles=%t, Compiler=%q, BuildTags=%q, InstallSuffix=%q}" context.GOROOT context.GOOS context.GOPATH context.CgoEnabled context.UseAllFiles context.Compiler context.BuildTags context.InstallSuffix}}' errors
{GOROOT="/usr/local/google/home/adonovan/goroot", GOOS="linux", GOPATH="/usr/local/google/home/adonovan/go", CgoEnabled=true, UseAllFiles=false, Compiler="gc", BuildTags=[], InstallSuffix=""}
We can't change the declaration of go/build.Context to have
json:tags on its fields, as that is a backward incompatible change.
Could you give some more detail on that? We're allowed to add fields, so nobody should be reyling on convertibility anyway (especially given #16085), and we're allowed to add methods, so nobody should be relying on the fact that go/build.Context currently cannot be marshalled.
Oh, I forgot that we'd relax the convertibility rules. So then yes, we could add field tags to Context.
The go list command actually copies the build.Context's contents into a locally defined struct for printing. It's the same as build.Context but without all the function pointers. It already has json tags, even.
So it does. One possible solution would be to add a "json" function to the set of functions available to the template, that would call json.Marshal on its operand. For example:
$ git diff
diff --git a/src/cmd/go/internal/list/list.go b/src/cmd/go/internal/list/list.go
index f3cb4e47ec..31512763df 100644
--- a/src/cmd/go/internal/list/list.go
+++ b/src/cmd/go/internal/list/list.go
@@ -344,7 +344,14 @@ func runList(cmd *base.Command, args []string) {
fm := template.FuncMap{
"join": strings.Join,
"context": context,
- "module": modload.ModuleInfo,
+ "json": func(x interface{}) (string, error) {
+ y, err := json.MarshalIndent(x, "\t", "")
+ if err != nil {
+ return "", err
+ }
+ return string(y), nil
+ },
+ "module": modload.ModuleInfo,
}
tmpl, err := template.New("main").Funcs(fm).Parse(*listFmt)
if err != nil {
$ go list -f '{{context | json}}' errors
{
"GOARCH": "amd64",
"GOOS": "linux",
"GOROOT": "/usr/local/google/home/adonovan/goroot",
"GOPATH": "/usr/local/google/home/adonovan/go",
"CgoEnabled": true,
"Compiler": "gc",
"ReleaseTags": [
"go1.1",
"go1.2",
"go1.3",
"go1.4",
"go1.5",
"go1.6",
"go1.7",
"go1.8",
"go1.9",
"go1.10",
"go1.11"
]
}
Since this is the only thing you can't already get as json, maybe it should be a method on that local context type? Like `go list -f '{{context.Json}}'
In the meantime, here's a workaround that formats BuildTags/ReleaseTags as json as well:
{{define "List"}}
[
{{range $i, $_ := .}}
{{if $i}},{{end}}
{{.|printf "%q"}}
{{end}}
]
{{end}}
{{with context}}
{
"GOARCH": {{.GOARCH|printf "%q"}},
"GOOS": {{.GOOS|printf "%q"}},
"GOROOT": {{.GOROOT|printf "%q"}},
"GOPATH": {{.GOPATH|printf "%q"}},
"CgoEnabled": {{.CgoEnabled}},
"UseAllFiles": {{.UseAllFiles}},
"Compiler": {{.Compiler|printf "%q"}},
"BuildTags": {{template "List" .BuildTags}},
"ReleaseTags": {{template "List" .ReleaseTags}},
"InstallSuffix": {{.InstallSuffix|printf "%q"}}
}
{{end}}
Most helpful comment
So it does. One possible solution would be to add a "json" function to the set of functions available to the template, that would call json.Marshal on its operand. For example: