Skip to content

Commit 2e346e5

Browse files
varunbpatilsbarzowski
authored andcommitted
jsonnet-deps: Jsonnet static dependency parser
Fixes #833
1 parent a631234 commit 2e346e5

File tree

6 files changed

+365
-18
lines changed

6 files changed

+365
-18
lines changed

BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ go_library(
2828
"//ast:go_default_library",
2929
"//astgen:go_default_library",
3030
"//internal/errors:go_default_library",
31+
"//internal/parser:go_default_library",
3132
"//internal/program:go_default_library",
3233
],
3334
)

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ git clone [email protected]:google/go-jsonnet.git
3232
cd go-jsonnet
3333
go build ./cmd/jsonnet
3434
go build ./cmd/jsonnetfmt
35+
go build ./cmd/jsonnet-deps
3536
```
3637
To build with [Bazel](https://bazel.build/) instead:
3738
```bash
@@ -41,6 +42,7 @@ git submodule init
4142
git submodule update
4243
bazel build //cmd/jsonnet
4344
bazel build //cmd/jsonnetfmt
45+
bazel build //cmd/jsonnet-deps
4446
```
4547
The resulting _jsonnet_ program will then be available at a platform-specific path, such as _bazel-bin/cmd/jsonnet/darwin_amd64_stripped/jsonnet_ for macOS.
4648

cmd/jsonnet-deps/BUILD.bazel

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library")
2+
3+
go_library(
4+
name = "go_default_library",
5+
srcs = ["cmd.go"],
6+
importpath = "github.com/google/go-jsonnet/cmd/jsonnet-deps",
7+
visibility = ["//visibility:private"],
8+
deps = [
9+
"//:go_default_library",
10+
"//cmd/internal/cmd:go_default_library",
11+
"@com_github_fatih_color//:go_default_library",
12+
],
13+
)
14+
15+
go_binary(
16+
name = "jsonnet-deps",
17+
embed = [":go_default_library"],
18+
visibility = ["//visibility:public"],
19+
)

cmd/jsonnet-deps/cmd.go

Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
/*
2+
Copyright 2020 Google Inc. All rights reserved.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package main
18+
19+
import (
20+
"fmt"
21+
"io"
22+
"os"
23+
"path/filepath"
24+
"strings"
25+
26+
"github.com/fatih/color"
27+
28+
"github.com/google/go-jsonnet"
29+
"github.com/google/go-jsonnet/cmd/internal/cmd"
30+
)
31+
32+
func version(o io.Writer) {
33+
fmt.Fprintf(o, "Jsonnet static dependency parser %s\n", jsonnet.Version())
34+
}
35+
36+
func usage(o io.Writer) {
37+
version(o)
38+
fmt.Fprintln(o)
39+
fmt.Fprintln(o, "jsonnet-deps {<option>} <filename>...")
40+
fmt.Fprintln(o)
41+
fmt.Fprintln(o, "Available options:")
42+
fmt.Fprintln(o, " -h / --help This message")
43+
fmt.Fprintln(o, " -J / --jpath <dir> Specify an additional library search dir")
44+
fmt.Fprintln(o, " (right-most wins)")
45+
fmt.Fprintln(o, " -o / --output-file <file> Write to the output file rather than stdout")
46+
fmt.Fprintln(o, " --version Print version")
47+
fmt.Fprintln(o)
48+
fmt.Fprintln(o, "Environment variables:")
49+
fmt.Fprintln(o, " JSONNET_PATH is a colon (semicolon on Windows) separated list of directories")
50+
fmt.Fprintln(o, " added in reverse order before the paths specified by --jpath (i.e. left-most")
51+
fmt.Fprintln(o, " wins). E.g. these are equivalent:")
52+
fmt.Fprintln(o, " JSONNET_PATH=a:b jsonnet -J c -J d")
53+
fmt.Fprintln(o, " JSONNET_PATH=d:c:a:b jsonnet")
54+
fmt.Fprintln(o, " jsonnet -J b -J a -J c -J d")
55+
fmt.Fprintln(o)
56+
fmt.Fprintln(o, "In all cases:")
57+
fmt.Fprintln(o, " Multichar options are expanded e.g. -abc becomes -a -b -c.")
58+
fmt.Fprintln(o, " The -- option suppresses option processing for subsequent arguments.")
59+
fmt.Fprintln(o, " Note that since filenames and jsonnet programs can begin with -, it is")
60+
fmt.Fprintln(o, " advised to use -- if the argument is unknown, e.g. jsonnet-deps -- \"$FILENAME\".")
61+
}
62+
63+
type config struct {
64+
inputFiles []string
65+
outputFile string
66+
jPaths []string
67+
}
68+
69+
type processArgsStatus int
70+
71+
const (
72+
processArgsStatusContinue = iota
73+
processArgsStatusSuccessUsage = iota
74+
processArgsStatusFailureUsage = iota
75+
processArgsStatusSuccess = iota
76+
processArgsStatusFailure = iota
77+
)
78+
79+
func processArgs(givenArgs []string, conf *config, vm *jsonnet.VM) (processArgsStatus, error) {
80+
args := cmd.SimplifyArgs(givenArgs)
81+
remainingArgs := make([]string, 0, len(args))
82+
83+
for i := 0; i < len(args); i++ {
84+
arg := args[i]
85+
if arg == "-h" || arg == "--help" {
86+
return processArgsStatusSuccessUsage, nil
87+
} else if arg == "-v" || arg == "--version" {
88+
version(os.Stdout)
89+
return processArgsStatusSuccess, nil
90+
} else if arg == "-o" || arg == "--output-file" {
91+
outputFile := cmd.NextArg(&i, args)
92+
if len(outputFile) == 0 {
93+
return processArgsStatusFailure, fmt.Errorf("-o argument was empty string")
94+
}
95+
conf.outputFile = outputFile
96+
} else if arg == "-J" || arg == "--jpath" {
97+
dir := cmd.NextArg(&i, args)
98+
if len(dir) == 0 {
99+
return processArgsStatusFailure, fmt.Errorf("-J argument was empty string")
100+
}
101+
conf.jPaths = append(conf.jPaths, dir)
102+
} else if arg == "--" {
103+
// All subsequent args are not options.
104+
i++
105+
for ; i < len(args); i++ {
106+
remainingArgs = append(remainingArgs, args[i])
107+
}
108+
break
109+
} else if len(arg) > 1 && arg[0] == '-' {
110+
return processArgsStatusFailure, fmt.Errorf("unrecognized argument: %s", arg)
111+
} else {
112+
remainingArgs = append(remainingArgs, arg)
113+
}
114+
}
115+
116+
if len(remainingArgs) == 0 {
117+
return processArgsStatusFailureUsage, fmt.Errorf("must give filename")
118+
}
119+
conf.inputFiles = remainingArgs
120+
121+
return processArgsStatusContinue, nil
122+
}
123+
124+
func writeDependencies(dependencies []string, outputFile string) (err error) {
125+
var f *os.File
126+
127+
if outputFile == "" {
128+
f = os.Stdout
129+
} else {
130+
f, err = os.Create(outputFile)
131+
if err != nil {
132+
return err
133+
}
134+
defer func() {
135+
if ferr := f.Close(); ferr != nil {
136+
err = ferr
137+
}
138+
}()
139+
}
140+
141+
if len(dependencies) != 0 {
142+
output := strings.Join(dependencies, "\n") + "\n"
143+
_, err = f.WriteString(output)
144+
if err != nil {
145+
return err
146+
}
147+
}
148+
149+
return
150+
}
151+
152+
func main() {
153+
cmd.StartCPUProfile()
154+
defer cmd.StopCPUProfile()
155+
156+
vm := jsonnet.MakeVM()
157+
vm.ErrorFormatter.SetColorFormatter(color.New(color.FgRed).Fprintf)
158+
159+
conf := config{}
160+
jsonnetPath := filepath.SplitList(os.Getenv("JSONNET_PATH"))
161+
for i := len(jsonnetPath) - 1; i >= 0; i-- {
162+
conf.jPaths = append(conf.jPaths, jsonnetPath[i])
163+
}
164+
165+
status, err := processArgs(os.Args[1:], &conf, vm)
166+
if err != nil {
167+
fmt.Fprintln(os.Stderr, "ERROR: "+err.Error())
168+
}
169+
switch status {
170+
case processArgsStatusContinue:
171+
break
172+
case processArgsStatusSuccessUsage:
173+
usage(os.Stdout)
174+
os.Exit(0)
175+
case processArgsStatusFailureUsage:
176+
if err != nil {
177+
fmt.Fprintln(os.Stderr, "")
178+
}
179+
usage(os.Stderr)
180+
os.Exit(1)
181+
case processArgsStatusSuccess:
182+
os.Exit(0)
183+
case processArgsStatusFailure:
184+
os.Exit(1)
185+
}
186+
187+
vm.Importer(&jsonnet.FileImporter{JPaths: conf.jPaths})
188+
189+
for _, file := range conf.inputFiles {
190+
if _, err := os.Stat(file); err != nil {
191+
fmt.Fprintln(os.Stderr, err.Error())
192+
os.Exit(1)
193+
}
194+
}
195+
196+
dependencies, err := vm.FindDependencies("", conf.inputFiles)
197+
if err != nil {
198+
fmt.Fprintln(os.Stderr, err.Error())
199+
os.Exit(1)
200+
}
201+
cmd.MemProfile()
202+
203+
err = writeDependencies(dependencies, conf.outputFile)
204+
if err != nil {
205+
fmt.Fprintln(os.Stderr, err.Error())
206+
os.Exit(1)
207+
}
208+
}

cmd/jsonnet/cmd.go

Lines changed: 5 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ import (
2323
"os"
2424
"path/filepath"
2525
"sort"
26-
"strconv"
2726
"strings"
2827

2928
"github.com/fatih/color"
@@ -93,15 +92,6 @@ func usage(o io.Writer) {
9392
fmt.Fprintln(o, " advised to use -- if the argument is unknown, e.g. jsonnet -- \"$FILENAME\".")
9493
}
9594

96-
func safeStrToInt(str string) (i int) {
97-
i, err := strconv.Atoi(str)
98-
if err != nil {
99-
fmt.Fprintf(os.Stderr, "Invalid integer \"%s\"\n", str)
100-
os.Exit(1)
101-
}
102-
return
103-
}
104-
10595
type config struct {
10696
inputFiles []string
10797
outputFile string
@@ -203,7 +193,7 @@ func processArgs(givenArgs []string, config *config, vm *jsonnet.VM) (processArg
203193
}
204194
break
205195
} else if arg == "-s" || arg == "--max-stack" {
206-
l := safeStrToInt(cmd.NextArg(&i, args))
196+
l := cmd.SafeStrToInt(cmd.NextArg(&i, args))
207197
if l < 1 {
208198
return processArgsStatusFailure, fmt.Errorf("invalid --max-stack value: %d", l)
209199
}
@@ -213,9 +203,6 @@ func processArgs(givenArgs []string, config *config, vm *jsonnet.VM) (processArg
213203
if len(dir) == 0 {
214204
return processArgsStatusFailure, fmt.Errorf("-J argument was empty string")
215205
}
216-
if dir[len(dir)-1] != '/' {
217-
dir += "/"
218-
}
219206
config.evalJpath = append(config.evalJpath, dir)
220207
} else if arg == "-V" || arg == "--ext-str" {
221208
if err := handleVarVal(vm.ExtVar); err != nil {
@@ -250,7 +237,7 @@ func processArgs(givenArgs []string, config *config, vm *jsonnet.VM) (processArg
250237
return processArgsStatusFailure, err
251238
}
252239
} else if arg == "-t" || arg == "--max-trace" {
253-
l := safeStrToInt(cmd.NextArg(&i, args))
240+
l := cmd.SafeStrToInt(cmd.NextArg(&i, args))
254241
if l < 0 {
255242
return processArgsStatusFailure, fmt.Errorf("invalid --max-trace value: %d", l)
256243
}
@@ -314,7 +301,7 @@ func writeMultiOutputFiles(output map[string]string, outputDir, outputFile strin
314301
return err
315302
}
316303
defer func() {
317-
if ferr := manifest.Close(); err != nil {
304+
if ferr := manifest.Close(); ferr != nil {
318305
err = ferr
319306
}
320307
}()
@@ -363,7 +350,7 @@ func writeMultiOutputFiles(output map[string]string, outputDir, outputFile strin
363350
return err
364351
}
365352
defer func() {
366-
if ferr := f.Close(); err != nil {
353+
if ferr := f.Close(); ferr != nil {
367354
err = ferr
368355
}
369356
}()
@@ -389,7 +376,7 @@ func writeOutputStream(output []string, outputFile string) (err error) {
389376
return err
390377
}
391378
defer func() {
392-
if ferr := f.Close(); err != nil {
379+
if ferr := f.Close(); ferr != nil {
393380
err = ferr
394381
}
395382
}()

0 commit comments

Comments
 (0)