Signed-off-by: Daniel Nephin <dnephin@docker.com>
| ... | ... |
@@ -62,7 +62,7 @@ type Builder struct {
|
| 62 | 62 |
disableCommit bool |
| 63 | 63 |
cacheBusted bool |
| 64 | 64 |
buildArgs *buildArgs |
| 65 |
- directive parser.Directive |
|
| 65 |
+ directive *parser.Directive |
|
| 66 | 66 |
|
| 67 | 67 |
imageCache builder.ImageCache |
| 68 | 68 |
from builder.Image |
| ... | ... |
@@ -122,14 +122,9 @@ func NewBuilder(clientCtx context.Context, config *types.ImageBuildOptions, back |
| 122 | 122 |
runConfig: new(container.Config), |
| 123 | 123 |
tmpContainers: map[string]struct{}{},
|
| 124 | 124 |
buildArgs: newBuildArgs(config.BuildArgs), |
| 125 |
- directive: parser.Directive{
|
|
| 126 |
- EscapeSeen: false, |
|
| 127 |
- LookingForDirectives: true, |
|
| 128 |
- }, |
|
| 125 |
+ directive: parser.NewDefaultDirective(), |
|
| 129 | 126 |
} |
| 130 | 127 |
b.imageContexts = &imageContexts{b: b}
|
| 131 |
- |
|
| 132 |
- parser.SetEscapeToken(parser.DefaultEscapeToken, &b.directive) // Assume the default token for escape |
|
| 133 | 128 |
return b, nil |
| 134 | 129 |
} |
| 135 | 130 |
|
| ... | ... |
@@ -325,7 +320,7 @@ func BuildFromConfig(config *container.Config, changes []string) (*container.Con |
| 325 | 325 |
return nil, err |
| 326 | 326 |
} |
| 327 | 327 |
|
| 328 |
- ast, err := parser.Parse(bytes.NewBufferString(strings.Join(changes, "\n")), &b.directive) |
|
| 328 |
+ ast, err := parser.Parse(bytes.NewBufferString(strings.Join(changes, "\n")), b.directive) |
|
| 329 | 329 |
if err != nil {
|
| 330 | 330 |
return nil, err |
| 331 | 331 |
} |
| ... | ... |
@@ -10,9 +10,8 @@ import ( |
| 10 | 10 |
|
| 11 | 11 |
func TestAddNodesForLabelOption(t *testing.T) {
|
| 12 | 12 |
dockerfile := "FROM scratch" |
| 13 |
- d := parser.Directive{}
|
|
| 14 |
- parser.SetEscapeToken(parser.DefaultEscapeToken, &d) |
|
| 15 |
- nodes, err := parser.Parse(strings.NewReader(dockerfile), &d) |
|
| 13 |
+ d := parser.NewDefaultDirective() |
|
| 14 |
+ nodes, err := parser.Parse(strings.NewReader(dockerfile), d) |
|
| 16 | 15 |
assert.NilError(t, err) |
| 17 | 16 |
|
| 18 | 17 |
labels := map[string]string{
|
| ... | ... |
@@ -200,7 +200,7 @@ func from(b *Builder, args []string, attributes map[string]bool, original string |
| 200 | 200 |
substituionArgs = append(substituionArgs, key+"="+value) |
| 201 | 201 |
} |
| 202 | 202 |
|
| 203 |
- name, err := ProcessWord(args[0], substituionArgs, b.directive.EscapeToken) |
|
| 203 |
+ name, err := ProcessWord(args[0], substituionArgs, b.directive.EscapeToken()) |
|
| 204 | 204 |
if err != nil {
|
| 205 | 205 |
return err |
| 206 | 206 |
} |
| ... | ... |
@@ -10,6 +10,7 @@ import ( |
| 10 | 10 |
"github.com/docker/docker/api/types/container" |
| 11 | 11 |
"github.com/docker/docker/api/types/strslice" |
| 12 | 12 |
"github.com/docker/docker/builder" |
| 13 |
+ "github.com/docker/docker/builder/dockerfile/parser" |
|
| 13 | 14 |
"github.com/docker/docker/pkg/testutil/assert" |
| 14 | 15 |
"github.com/docker/go-connections/nat" |
| 15 | 16 |
) |
| ... | ... |
@@ -204,6 +205,7 @@ func newBuilderWithMockBackend() *Builder {
|
| 204 | 204 |
options: &types.ImageBuildOptions{},
|
| 205 | 205 |
docker: &MockBackend{},
|
| 206 | 206 |
buildArgs: newBuildArgs(make(map[string]*string)), |
| 207 |
+ directive: parser.NewDefaultDirective(), |
|
| 207 | 208 |
} |
| 208 | 209 |
b.imageContexts = &imageContexts{b: b}
|
| 209 | 210 |
return b |
| ... | ... |
@@ -180,7 +180,7 @@ func (b *Builder) evaluateEnv(cmd string, str string, envs []string) ([]string, |
| 180 | 180 |
return []string{word}, err
|
| 181 | 181 |
} |
| 182 | 182 |
} |
| 183 |
- return processFunc(str, envs, b.directive.EscapeToken) |
|
| 183 |
+ return processFunc(str, envs, b.directive.EscapeToken()) |
|
| 184 | 184 |
} |
| 185 | 185 |
|
| 186 | 186 |
// buildArgsWithoutConfigEnv returns a list of key=value pairs for all the build |
| ... | ... |
@@ -171,9 +171,7 @@ func executeTestCase(t *testing.T, testCase dispatchTestCase) {
|
| 171 | 171 |
}() |
| 172 | 172 |
|
| 173 | 173 |
r := strings.NewReader(testCase.dockerfile) |
| 174 |
- d := parser.Directive{}
|
|
| 175 |
- parser.SetEscapeToken(parser.DefaultEscapeToken, &d) |
|
| 176 |
- n, err := parser.Parse(r, &d) |
|
| 174 |
+ n, err := parser.Parse(r, parser.NewDefaultDirective()) |
|
| 177 | 175 |
|
| 178 | 176 |
if err != nil {
|
| 179 | 177 |
t.Fatalf("Error when parsing Dockerfile: %s", err)
|
| ... | ... |
@@ -190,6 +188,7 @@ func executeTestCase(t *testing.T, testCase dispatchTestCase) {
|
| 190 | 190 |
Stdout: ioutil.Discard, |
| 191 | 191 |
context: context, |
| 192 | 192 |
buildArgs: newBuildArgs(options.BuildArgs), |
| 193 |
+ directive: parser.NewDefaultDirective(), |
|
| 193 | 194 |
} |
| 194 | 195 |
|
| 195 | 196 |
err = b.dispatch(0, len(n.Children), n.Children[0]) |
| ... | ... |
@@ -462,7 +462,7 @@ func (b *Builder) processImageFrom(img builder.Image) error {
|
| 462 | 462 |
|
| 463 | 463 |
// parse the ONBUILD triggers by invoking the parser |
| 464 | 464 |
for _, step := range onBuildTriggers {
|
| 465 |
- ast, err := parser.Parse(strings.NewReader(step), &b.directive) |
|
| 465 |
+ ast, err := parser.Parse(strings.NewReader(step), b.directive) |
|
| 466 | 466 |
if err != nil {
|
| 467 | 467 |
return err |
| 468 | 468 |
} |
| ... | ... |
@@ -702,5 +702,5 @@ func (b *Builder) parseDockerfile() (*parser.Node, error) {
|
| 702 | 702 |
return nil, fmt.Errorf("The Dockerfile (%s) cannot be empty", b.options.Dockerfile)
|
| 703 | 703 |
} |
| 704 | 704 |
} |
| 705 |
- return parser.Parse(f, &b.directive) |
|
| 705 |
+ return parser.Parse(f, b.directive) |
|
| 706 | 706 |
} |
| ... | ... |
@@ -23,10 +23,8 @@ func main() {
|
| 23 | 23 |
} |
| 24 | 24 |
defer f.Close() |
| 25 | 25 |
|
| 26 |
- d := parser.Directive{LookingForDirectives: true}
|
|
| 27 |
- parser.SetEscapeToken(parser.DefaultEscapeToken, &d) |
|
| 28 |
- |
|
| 29 |
- ast, err := parser.Parse(f, &d) |
|
| 26 |
+ d := parser.NewDefaultDirective() |
|
| 27 |
+ ast, err := parser.Parse(f, d) |
|
| 30 | 28 |
if err != nil {
|
| 31 | 29 |
panic(err) |
| 32 | 30 |
} else {
|
| ... | ... |
@@ -29,7 +29,7 @@ var validJSONArraysOfStrings = map[string][]string{
|
| 29 | 29 |
func TestJSONArraysOfStrings(t *testing.T) {
|
| 30 | 30 |
for json, expected := range validJSONArraysOfStrings {
|
| 31 | 31 |
d := Directive{}
|
| 32 |
- SetEscapeToken(DefaultEscapeToken, &d) |
|
| 32 |
+ d.SetEscapeToken(DefaultEscapeToken) |
|
| 33 | 33 |
|
| 34 | 34 |
if node, _, err := parseJSON(json, &d); err != nil {
|
| 35 | 35 |
t.Fatalf("%q should be a valid JSON array of strings, but wasn't! (err: %q)", json, err)
|
| ... | ... |
@@ -52,7 +52,7 @@ func TestJSONArraysOfStrings(t *testing.T) {
|
| 52 | 52 |
} |
| 53 | 53 |
for _, json := range invalidJSONArraysOfStrings {
|
| 54 | 54 |
d := Directive{}
|
| 55 |
- SetEscapeToken(DefaultEscapeToken, &d) |
|
| 55 |
+ d.SetEscapeToken(DefaultEscapeToken) |
|
| 56 | 56 |
|
| 57 | 57 |
if _, _, err := parseJSON(json, &d); err != errDockerfileNotStringArray {
|
| 58 | 58 |
t.Fatalf("%q should be an invalid JSON array of strings, but wasn't!", json)
|
| ... | ... |
@@ -103,7 +103,7 @@ func parseWords(rest string, d *Directive) []string {
|
| 103 | 103 |
blankOK = true |
| 104 | 104 |
phase = inQuote |
| 105 | 105 |
} |
| 106 |
- if ch == d.EscapeToken {
|
|
| 106 |
+ if ch == d.escapeToken {
|
|
| 107 | 107 |
if pos+chWidth == len(rest) {
|
| 108 | 108 |
continue // just skip an escape token at end of line |
| 109 | 109 |
} |
| ... | ... |
@@ -122,7 +122,7 @@ func parseWords(rest string, d *Directive) []string {
|
| 122 | 122 |
phase = inWord |
| 123 | 123 |
} |
| 124 | 124 |
// The escape token is special except for ' quotes - can't escape anything for ' |
| 125 |
- if ch == d.EscapeToken && quote != '\'' {
|
|
| 125 |
+ if ch == d.escapeToken && quote != '\'' {
|
|
| 126 | 126 |
if pos+chWidth == len(rest) {
|
| 127 | 127 |
phase = inWord |
| 128 | 128 |
continue // just skip the escape token at end |
| ... | ... |
@@ -62,15 +62,6 @@ func (node *Node) Dump() string {
|
| 62 | 62 |
return strings.TrimSpace(str) |
| 63 | 63 |
} |
| 64 | 64 |
|
| 65 |
-// Directive is the structure used during a build run to hold the state of |
|
| 66 |
-// parsing directives. |
|
| 67 |
-type Directive struct {
|
|
| 68 |
- EscapeToken rune // Current escape token |
|
| 69 |
- LineContinuationRegex *regexp.Regexp // Current line continuation regex |
|
| 70 |
- LookingForDirectives bool // Whether we are currently looking for directives |
|
| 71 |
- EscapeSeen bool // Whether the escape directive has been seen |
|
| 72 |
-} |
|
| 73 |
- |
|
| 74 | 65 |
var ( |
| 75 | 66 |
dispatch map[string]func(string, *Directive) (*Node, map[string]bool, error) |
| 76 | 67 |
tokenWhitespace = regexp.MustCompile(`[\t\v\f\r ]+`) |
| ... | ... |
@@ -81,16 +72,40 @@ var ( |
| 81 | 81 |
// DefaultEscapeToken is the default escape token |
| 82 | 82 |
const DefaultEscapeToken = "\\" |
| 83 | 83 |
|
| 84 |
+// Directive is the structure used during a build run to hold the state of |
|
| 85 |
+// parsing directives. |
|
| 86 |
+type Directive struct {
|
|
| 87 |
+ escapeToken rune // Current escape token |
|
| 88 |
+ lineContinuationRegex *regexp.Regexp // Current line continuation regex |
|
| 89 |
+ lookingForDirectives bool // Whether we are currently looking for directives |
|
| 90 |
+ escapeSeen bool // Whether the escape directive has been seen |
|
| 91 |
+} |
|
| 92 |
+ |
|
| 84 | 93 |
// SetEscapeToken sets the default token for escaping characters in a Dockerfile. |
| 85 |
-func SetEscapeToken(s string, d *Directive) error {
|
|
| 94 |
+func (d *Directive) SetEscapeToken(s string) error {
|
|
| 86 | 95 |
if s != "`" && s != "\\" {
|
| 87 | 96 |
return fmt.Errorf("invalid ESCAPE '%s'. Must be ` or \\", s)
|
| 88 | 97 |
} |
| 89 |
- d.EscapeToken = rune(s[0]) |
|
| 90 |
- d.LineContinuationRegex = regexp.MustCompile(`\` + s + `[ \t]*$`) |
|
| 98 |
+ d.escapeToken = rune(s[0]) |
|
| 99 |
+ d.lineContinuationRegex = regexp.MustCompile(`\` + s + `[ \t]*$`) |
|
| 91 | 100 |
return nil |
| 92 | 101 |
} |
| 93 | 102 |
|
| 103 |
+// EscapeToken returns the escape token |
|
| 104 |
+func (d *Directive) EscapeToken() rune {
|
|
| 105 |
+ return d.escapeToken |
|
| 106 |
+} |
|
| 107 |
+ |
|
| 108 |
+// NewDefaultDirective returns a new Directive with the default escapeToken token |
|
| 109 |
+func NewDefaultDirective() *Directive {
|
|
| 110 |
+ directive := Directive{
|
|
| 111 |
+ escapeSeen: false, |
|
| 112 |
+ lookingForDirectives: true, |
|
| 113 |
+ } |
|
| 114 |
+ directive.SetEscapeToken(DefaultEscapeToken) |
|
| 115 |
+ return &directive |
|
| 116 |
+} |
|
| 117 |
+ |
|
| 94 | 118 |
func init() {
|
| 95 | 119 |
// Dispatch Table. see line_parsers.go for the parse functions. |
| 96 | 120 |
// The command is parsed and mapped to the line parser. The line parser |
| ... | ... |
@@ -123,18 +138,18 @@ func init() {
|
| 123 | 123 |
// ParseLine parses a line and returns the remainder. |
| 124 | 124 |
func ParseLine(line string, d *Directive, ignoreCont bool) (string, *Node, error) {
|
| 125 | 125 |
if escapeFound, err := handleParserDirective(line, d); err != nil || escapeFound {
|
| 126 |
- d.EscapeSeen = escapeFound |
|
| 126 |
+ d.escapeSeen = escapeFound |
|
| 127 | 127 |
return "", nil, err |
| 128 | 128 |
} |
| 129 | 129 |
|
| 130 |
- d.LookingForDirectives = false |
|
| 130 |
+ d.lookingForDirectives = false |
|
| 131 | 131 |
|
| 132 | 132 |
if line = stripComments(line); line == "" {
|
| 133 | 133 |
return "", nil, nil |
| 134 | 134 |
} |
| 135 | 135 |
|
| 136 |
- if !ignoreCont && d.LineContinuationRegex.MatchString(line) {
|
|
| 137 |
- line = d.LineContinuationRegex.ReplaceAllString(line, "") |
|
| 136 |
+ if !ignoreCont && d.lineContinuationRegex.MatchString(line) {
|
|
| 137 |
+ line = d.lineContinuationRegex.ReplaceAllString(line, "") |
|
| 138 | 138 |
return line, nil, nil |
| 139 | 139 |
} |
| 140 | 140 |
|
| ... | ... |
@@ -170,22 +185,22 @@ func newNodeFromLine(line string, directive *Directive) (*Node, error) {
|
| 170 | 170 |
}, nil |
| 171 | 171 |
} |
| 172 | 172 |
|
| 173 |
-// Handle the parser directive '# escape=<char>. Parser directives must precede |
|
| 173 |
+// Handle the parser directive '# escapeToken=<char>. Parser directives must precede |
|
| 174 | 174 |
// any builder instruction or other comments, and cannot be repeated. |
| 175 | 175 |
func handleParserDirective(line string, d *Directive) (bool, error) {
|
| 176 |
- if !d.LookingForDirectives {
|
|
| 176 |
+ if !d.lookingForDirectives {
|
|
| 177 | 177 |
return false, nil |
| 178 | 178 |
} |
| 179 | 179 |
tecMatch := tokenEscapeCommand.FindStringSubmatch(strings.ToLower(line)) |
| 180 | 180 |
if len(tecMatch) == 0 {
|
| 181 | 181 |
return false, nil |
| 182 | 182 |
} |
| 183 |
- if d.EscapeSeen == true {
|
|
| 183 |
+ if d.escapeSeen == true {
|
|
| 184 | 184 |
return false, fmt.Errorf("only one escape parser directive can be used")
|
| 185 | 185 |
} |
| 186 | 186 |
for i, n := range tokenEscapeCommand.SubexpNames() {
|
| 187 | 187 |
if n == "escapechar" {
|
| 188 |
- if err := SetEscapeToken(tecMatch[i], d); err != nil {
|
|
| 188 |
+ if err := d.SetEscapeToken(tecMatch[i]); err != nil {
|
|
| 189 | 189 |
return false, err |
| 190 | 190 |
} |
| 191 | 191 |
return true, nil |
| ... | ... |
@@ -40,9 +40,7 @@ func TestTestNegative(t *testing.T) {
|
| 40 | 40 |
} |
| 41 | 41 |
defer df.Close() |
| 42 | 42 |
|
| 43 |
- d := Directive{LookingForDirectives: true}
|
|
| 44 |
- SetEscapeToken(DefaultEscapeToken, &d) |
|
| 45 |
- _, err = Parse(df, &d) |
|
| 43 |
+ _, err = Parse(df, NewDefaultDirective()) |
|
| 46 | 44 |
if err == nil {
|
| 47 | 45 |
t.Fatalf("No error parsing broken dockerfile for %s", dir)
|
| 48 | 46 |
} |
| ... | ... |
@@ -60,9 +58,7 @@ func TestTestData(t *testing.T) {
|
| 60 | 60 |
} |
| 61 | 61 |
defer df.Close() |
| 62 | 62 |
|
| 63 |
- d := Directive{LookingForDirectives: true}
|
|
| 64 |
- SetEscapeToken(DefaultEscapeToken, &d) |
|
| 65 |
- ast, err := Parse(df, &d) |
|
| 63 |
+ ast, err := Parse(df, NewDefaultDirective()) |
|
| 66 | 64 |
if err != nil {
|
| 67 | 65 |
t.Fatalf("Error parsing %s's dockerfile: %v", dir, err)
|
| 68 | 66 |
} |
| ... | ... |
@@ -122,9 +118,7 @@ func TestParseWords(t *testing.T) {
|
| 122 | 122 |
} |
| 123 | 123 |
|
| 124 | 124 |
for _, test := range tests {
|
| 125 |
- d := Directive{LookingForDirectives: true}
|
|
| 126 |
- SetEscapeToken(DefaultEscapeToken, &d) |
|
| 127 |
- words := parseWords(test["input"][0], &d) |
|
| 125 |
+ words := parseWords(test["input"][0], NewDefaultDirective()) |
|
| 128 | 126 |
if len(words) != len(test["expect"]) {
|
| 129 | 127 |
t.Fatalf("length check failed. input: %v, expect: %q, output: %q", test["input"][0], test["expect"], words)
|
| 130 | 128 |
} |
| ... | ... |
@@ -143,9 +137,7 @@ func TestLineInformation(t *testing.T) {
|
| 143 | 143 |
} |
| 144 | 144 |
defer df.Close() |
| 145 | 145 |
|
| 146 |
- d := Directive{LookingForDirectives: true}
|
|
| 147 |
- SetEscapeToken(DefaultEscapeToken, &d) |
|
| 148 |
- ast, err := Parse(df, &d) |
|
| 146 |
+ ast, err := Parse(df, NewDefaultDirective()) |
|
| 149 | 147 |
if err != nil {
|
| 150 | 148 |
t.Fatalf("Error parsing dockerfile %s: %v", testFileLineInfo, err)
|
| 151 | 149 |
} |