diff --git a/builder/dockerfile/builder.go b/builder/dockerfile/builder.go index 06c5d69355..d13fe184f5 100644 --- a/builder/dockerfile/builder.go +++ b/builder/dockerfile/builder.go @@ -238,7 +238,7 @@ func (b *Builder) build(stdout io.Writer, stderr io.Writer, out io.Writer) (stri for k, v := range b.options.Labels { line += fmt.Sprintf("%q='%s' ", k, v) } - _, node, err := parser.ParseLine(line, &b.directive) + _, node, err := parser.ParseLine(line, &b.directive, false) if err != nil { return "", err } diff --git a/builder/dockerfile/parser/line_parsers.go b/builder/dockerfile/parser/line_parsers.go index d82b19a081..d2bf2b01b1 100644 --- a/builder/dockerfile/parser/line_parsers.go +++ b/builder/dockerfile/parser/line_parsers.go @@ -35,7 +35,7 @@ func parseSubCommand(rest string, d *Directive) (*Node, map[string]bool, error) return nil, nil, nil } - _, child, err := ParseLine(rest, d) + _, child, err := ParseLine(rest, d, false) if err != nil { return nil, nil, err } diff --git a/builder/dockerfile/parser/parser.go b/builder/dockerfile/parser/parser.go index 71c2c0d811..b4ce605f46 100644 --- a/builder/dockerfile/parser/parser.go +++ b/builder/dockerfile/parser/parser.go @@ -95,7 +95,7 @@ func init() { } // ParseLine parses a line and returns the remainder. -func ParseLine(line string, d *Directive) (string, *Node, error) { +func ParseLine(line string, d *Directive, ignoreCont bool) (string, *Node, error) { // Handle the parser directive '# escape=. Parser directives must precede // any builder instruction or other comments, and cannot be repeated. if d.LookingForDirectives { @@ -122,7 +122,7 @@ func ParseLine(line string, d *Directive) (string, *Node, error) { return "", nil, nil } - if d.LineContinuationRegex.MatchString(line) { + if !ignoreCont && d.LineContinuationRegex.MatchString(line) { line = d.LineContinuationRegex.ReplaceAllString(line, "") return line, nil, nil } @@ -165,7 +165,7 @@ func Parse(rwc io.Reader, d *Directive) (*Node, error) { } scannedLine := strings.TrimLeftFunc(string(scannedBytes), unicode.IsSpace) currentLine++ - line, child, err := ParseLine(scannedLine, d) + line, child, err := ParseLine(scannedLine, d, false) if err != nil { return nil, err } @@ -187,7 +187,7 @@ func Parse(rwc io.Reader, d *Directive) (*Node, error) { if strings.TrimSpace(newline) == "" { break } - line, child, err = ParseLine(line+newline, d) + line, child, err = ParseLine(line+newline, d, false) if err != nil { return nil, err } @@ -197,7 +197,13 @@ func Parse(rwc io.Reader, d *Directive) (*Node, error) { } } if child == nil && line != "" { - _, child, err = ParseLine(line, d) + // When we call ParseLine we'll pass in 'true' for + // the ignoreCont param if we're at the EOF. This will + // prevent the func from returning immediately w/o + // parsing the line thinking that there's more input + // to come. + + _, child, err = ParseLine(line, d, scanner.Err() == nil) if err != nil { return nil, err } diff --git a/integration-cli/docker_cli_build_test.go b/integration-cli/docker_cli_build_test.go index e5d1882a7a..f890db1b2f 100644 --- a/integration-cli/docker_cli_build_test.go +++ b/integration-cli/docker_cli_build_test.go @@ -7237,3 +7237,33 @@ func (s *DockerSuite) TestBuildSquashParent(c *check.C) { c.Assert(err, checker.IsNil) c.Assert(strings.TrimSpace(out), checker.Equals, "3") } + +func (s *DockerSuite) TestBuildContChar(c *check.C) { + name := "testbuildcontchar" + + _, out, err := buildImageWithOut(name, + `FROM busybox\`, true) + c.Assert(err, checker.IsNil) + c.Assert(out, checker.Contains, "Step 1/1 : FROM busybox") + + _, out, err = buildImageWithOut(name, + `FROM busybox + RUN echo hi \`, true) + c.Assert(err, checker.IsNil) + c.Assert(out, checker.Contains, "Step 1/2 : FROM busybox") + c.Assert(out, checker.Contains, "Step 2/2 : RUN echo hi\n") + + _, out, err = buildImageWithOut(name, + `FROM busybox + RUN echo hi \\`, true) + c.Assert(err, checker.IsNil) + c.Assert(out, checker.Contains, "Step 1/2 : FROM busybox") + c.Assert(out, checker.Contains, "Step 2/2 : RUN echo hi \\\n") + + _, out, err = buildImageWithOut(name, + `FROM busybox + RUN echo hi \\\`, true) + c.Assert(err, checker.IsNil) + c.Assert(out, checker.Contains, "Step 1/2 : FROM busybox") + c.Assert(out, checker.Contains, "Step 2/2 : RUN echo hi \\\\\n") +}