New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cmd/go: get go list context function formatted as json #27915

Open
jimmyfrasche opened this Issue Sep 28, 2018 · 10 comments

Comments

Projects
None yet
4 participants
@jimmyfrasche
Member

jimmyfrasche commented Sep 28, 2018

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

@xuruiray

This comment has been minimized.

xuruiray commented Sep 28, 2018

go list should output a json array, but it output each element of array , i need to add , before every element

@jimmyfrasche

This comment has been minimized.

Member

jimmyfrasche commented Sep 28, 2018

@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.

@bcmills bcmills added the GoCommand label Sep 28, 2018

@bcmills

This comment has been minimized.

Member

bcmills commented Sep 28, 2018

@alandonovan

This comment has been minimized.

Contributor

alandonovan commented Oct 1, 2018

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=""}
@bcmills

This comment has been minimized.

Member

bcmills commented Oct 1, 2018

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.

@alandonovan

This comment has been minimized.

Contributor

alandonovan commented Oct 1, 2018

Oh, I forgot that we'd relax the convertibility rules. So then yes, we could add field tags to Context.

@jimmyfrasche

This comment has been minimized.

Member

jimmyfrasche commented Oct 1, 2018

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.

@alandonovan

This comment has been minimized.

Contributor

alandonovan commented Oct 1, 2018

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"
        ]
        }
@jimmyfrasche

This comment has been minimized.

Member

jimmyfrasche commented Oct 1, 2018

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}}'

@jimmyfrasche

This comment has been minimized.

Member

jimmyfrasche commented Oct 10, 2018

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}}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment