mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
Hide builder.parser.Directive internals
Signed-off-by: Daniel Nephin <dnephin@docker.com>
This commit is contained in:
parent
d2ab40e5b9
commit
bb429da9a9
12 changed files with 58 additions and 58 deletions
|
@ -62,7 +62,7 @@ type Builder struct {
|
||||||
disableCommit bool
|
disableCommit bool
|
||||||
cacheBusted bool
|
cacheBusted bool
|
||||||
buildArgs *buildArgs
|
buildArgs *buildArgs
|
||||||
directive parser.Directive
|
directive *parser.Directive
|
||||||
|
|
||||||
imageCache builder.ImageCache
|
imageCache builder.ImageCache
|
||||||
from builder.Image
|
from builder.Image
|
||||||
|
@ -122,14 +122,9 @@ func NewBuilder(clientCtx context.Context, config *types.ImageBuildOptions, back
|
||||||
runConfig: new(container.Config),
|
runConfig: new(container.Config),
|
||||||
tmpContainers: map[string]struct{}{},
|
tmpContainers: map[string]struct{}{},
|
||||||
buildArgs: newBuildArgs(config.BuildArgs),
|
buildArgs: newBuildArgs(config.BuildArgs),
|
||||||
directive: parser.Directive{
|
directive: parser.NewDefaultDirective(),
|
||||||
EscapeSeen: false,
|
|
||||||
LookingForDirectives: true,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
b.imageContexts = &imageContexts{b: b}
|
b.imageContexts = &imageContexts{b: b}
|
||||||
|
|
||||||
parser.SetEscapeToken(parser.DefaultEscapeToken, &b.directive) // Assume the default token for escape
|
|
||||||
return b, nil
|
return b, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -325,7 +320,7 @@ func BuildFromConfig(config *container.Config, changes []string) (*container.Con
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
ast, err := parser.Parse(bytes.NewBufferString(strings.Join(changes, "\n")), &b.directive)
|
ast, err := parser.Parse(bytes.NewBufferString(strings.Join(changes, "\n")), b.directive)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,9 +10,8 @@ import (
|
||||||
|
|
||||||
func TestAddNodesForLabelOption(t *testing.T) {
|
func TestAddNodesForLabelOption(t *testing.T) {
|
||||||
dockerfile := "FROM scratch"
|
dockerfile := "FROM scratch"
|
||||||
d := parser.Directive{}
|
d := parser.NewDefaultDirective()
|
||||||
parser.SetEscapeToken(parser.DefaultEscapeToken, &d)
|
nodes, err := parser.Parse(strings.NewReader(dockerfile), d)
|
||||||
nodes, err := parser.Parse(strings.NewReader(dockerfile), &d)
|
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
|
|
||||||
labels := map[string]string{
|
labels := map[string]string{
|
||||||
|
|
|
@ -200,7 +200,7 @@ func from(b *Builder, args []string, attributes map[string]bool, original string
|
||||||
substituionArgs = append(substituionArgs, key+"="+value)
|
substituionArgs = append(substituionArgs, key+"="+value)
|
||||||
}
|
}
|
||||||
|
|
||||||
name, err := ProcessWord(args[0], substituionArgs, b.directive.EscapeToken)
|
name, err := ProcessWord(args[0], substituionArgs, b.directive.EscapeToken())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ import (
|
||||||
"github.com/docker/docker/api/types/container"
|
"github.com/docker/docker/api/types/container"
|
||||||
"github.com/docker/docker/api/types/strslice"
|
"github.com/docker/docker/api/types/strslice"
|
||||||
"github.com/docker/docker/builder"
|
"github.com/docker/docker/builder"
|
||||||
|
"github.com/docker/docker/builder/dockerfile/parser"
|
||||||
"github.com/docker/docker/pkg/testutil/assert"
|
"github.com/docker/docker/pkg/testutil/assert"
|
||||||
"github.com/docker/go-connections/nat"
|
"github.com/docker/go-connections/nat"
|
||||||
)
|
)
|
||||||
|
@ -204,6 +205,7 @@ func newBuilderWithMockBackend() *Builder {
|
||||||
options: &types.ImageBuildOptions{},
|
options: &types.ImageBuildOptions{},
|
||||||
docker: &MockBackend{},
|
docker: &MockBackend{},
|
||||||
buildArgs: newBuildArgs(make(map[string]*string)),
|
buildArgs: newBuildArgs(make(map[string]*string)),
|
||||||
|
directive: parser.NewDefaultDirective(),
|
||||||
}
|
}
|
||||||
b.imageContexts = &imageContexts{b: b}
|
b.imageContexts = &imageContexts{b: b}
|
||||||
return b
|
return b
|
||||||
|
|
|
@ -180,7 +180,7 @@ func (b *Builder) evaluateEnv(cmd string, str string, envs []string) ([]string,
|
||||||
return []string{word}, err
|
return []string{word}, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return processFunc(str, envs, b.directive.EscapeToken)
|
return processFunc(str, envs, b.directive.EscapeToken())
|
||||||
}
|
}
|
||||||
|
|
||||||
// buildArgsWithoutConfigEnv returns a list of key=value pairs for all the build
|
// buildArgsWithoutConfigEnv returns a list of key=value pairs for all the build
|
||||||
|
|
|
@ -171,9 +171,7 @@ func executeTestCase(t *testing.T, testCase dispatchTestCase) {
|
||||||
}()
|
}()
|
||||||
|
|
||||||
r := strings.NewReader(testCase.dockerfile)
|
r := strings.NewReader(testCase.dockerfile)
|
||||||
d := parser.Directive{}
|
n, err := parser.Parse(r, parser.NewDefaultDirective())
|
||||||
parser.SetEscapeToken(parser.DefaultEscapeToken, &d)
|
|
||||||
n, err := parser.Parse(r, &d)
|
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Error when parsing Dockerfile: %s", err)
|
t.Fatalf("Error when parsing Dockerfile: %s", err)
|
||||||
|
@ -190,6 +188,7 @@ func executeTestCase(t *testing.T, testCase dispatchTestCase) {
|
||||||
Stdout: ioutil.Discard,
|
Stdout: ioutil.Discard,
|
||||||
context: context,
|
context: context,
|
||||||
buildArgs: newBuildArgs(options.BuildArgs),
|
buildArgs: newBuildArgs(options.BuildArgs),
|
||||||
|
directive: parser.NewDefaultDirective(),
|
||||||
}
|
}
|
||||||
|
|
||||||
err = b.dispatch(0, len(n.Children), n.Children[0])
|
err = b.dispatch(0, len(n.Children), n.Children[0])
|
||||||
|
|
|
@ -462,7 +462,7 @@ func (b *Builder) processImageFrom(img builder.Image) error {
|
||||||
|
|
||||||
// parse the ONBUILD triggers by invoking the parser
|
// parse the ONBUILD triggers by invoking the parser
|
||||||
for _, step := range onBuildTriggers {
|
for _, step := range onBuildTriggers {
|
||||||
ast, err := parser.Parse(strings.NewReader(step), &b.directive)
|
ast, err := parser.Parse(strings.NewReader(step), b.directive)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -702,5 +702,5 @@ func (b *Builder) parseDockerfile() (*parser.Node, error) {
|
||||||
return nil, fmt.Errorf("The Dockerfile (%s) cannot be empty", b.options.Dockerfile)
|
return nil, fmt.Errorf("The Dockerfile (%s) cannot be empty", b.options.Dockerfile)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return parser.Parse(f, &b.directive)
|
return parser.Parse(f, b.directive)
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,10 +23,8 @@ func main() {
|
||||||
}
|
}
|
||||||
defer f.Close()
|
defer f.Close()
|
||||||
|
|
||||||
d := parser.Directive{LookingForDirectives: true}
|
d := parser.NewDefaultDirective()
|
||||||
parser.SetEscapeToken(parser.DefaultEscapeToken, &d)
|
ast, err := parser.Parse(f, d)
|
||||||
|
|
||||||
ast, err := parser.Parse(f, &d)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -29,7 +29,7 @@ var validJSONArraysOfStrings = map[string][]string{
|
||||||
func TestJSONArraysOfStrings(t *testing.T) {
|
func TestJSONArraysOfStrings(t *testing.T) {
|
||||||
for json, expected := range validJSONArraysOfStrings {
|
for json, expected := range validJSONArraysOfStrings {
|
||||||
d := Directive{}
|
d := Directive{}
|
||||||
SetEscapeToken(DefaultEscapeToken, &d)
|
d.SetEscapeToken(DefaultEscapeToken)
|
||||||
|
|
||||||
if node, _, err := parseJSON(json, &d); err != nil {
|
if node, _, err := parseJSON(json, &d); err != nil {
|
||||||
t.Fatalf("%q should be a valid JSON array of strings, but wasn't! (err: %q)", json, err)
|
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) {
|
||||||
}
|
}
|
||||||
for _, json := range invalidJSONArraysOfStrings {
|
for _, json := range invalidJSONArraysOfStrings {
|
||||||
d := Directive{}
|
d := Directive{}
|
||||||
SetEscapeToken(DefaultEscapeToken, &d)
|
d.SetEscapeToken(DefaultEscapeToken)
|
||||||
|
|
||||||
if _, _, err := parseJSON(json, &d); err != errDockerfileNotStringArray {
|
if _, _, err := parseJSON(json, &d); err != errDockerfileNotStringArray {
|
||||||
t.Fatalf("%q should be an invalid JSON array of strings, but wasn't!", json)
|
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 {
|
||||||
blankOK = true
|
blankOK = true
|
||||||
phase = inQuote
|
phase = inQuote
|
||||||
}
|
}
|
||||||
if ch == d.EscapeToken {
|
if ch == d.escapeToken {
|
||||||
if pos+chWidth == len(rest) {
|
if pos+chWidth == len(rest) {
|
||||||
continue // just skip an escape token at end of line
|
continue // just skip an escape token at end of line
|
||||||
}
|
}
|
||||||
|
@ -122,7 +122,7 @@ func parseWords(rest string, d *Directive) []string {
|
||||||
phase = inWord
|
phase = inWord
|
||||||
}
|
}
|
||||||
// The escape token is special except for ' quotes - can't escape anything for '
|
// The escape token is special except for ' quotes - can't escape anything for '
|
||||||
if ch == d.EscapeToken && quote != '\'' {
|
if ch == d.escapeToken && quote != '\'' {
|
||||||
if pos+chWidth == len(rest) {
|
if pos+chWidth == len(rest) {
|
||||||
phase = inWord
|
phase = inWord
|
||||||
continue // just skip the escape token at end
|
continue // just skip the escape token at end
|
||||||
|
|
|
@ -62,15 +62,6 @@ func (node *Node) Dump() string {
|
||||||
return strings.TrimSpace(str)
|
return strings.TrimSpace(str)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Directive is the structure used during a build run to hold the state of
|
|
||||||
// parsing directives.
|
|
||||||
type Directive struct {
|
|
||||||
EscapeToken rune // Current escape token
|
|
||||||
LineContinuationRegex *regexp.Regexp // Current line continuation regex
|
|
||||||
LookingForDirectives bool // Whether we are currently looking for directives
|
|
||||||
EscapeSeen bool // Whether the escape directive has been seen
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
dispatch map[string]func(string, *Directive) (*Node, map[string]bool, error)
|
dispatch map[string]func(string, *Directive) (*Node, map[string]bool, error)
|
||||||
tokenWhitespace = regexp.MustCompile(`[\t\v\f\r ]+`)
|
tokenWhitespace = regexp.MustCompile(`[\t\v\f\r ]+`)
|
||||||
|
@ -81,16 +72,40 @@ var (
|
||||||
// DefaultEscapeToken is the default escape token
|
// DefaultEscapeToken is the default escape token
|
||||||
const DefaultEscapeToken = "\\"
|
const DefaultEscapeToken = "\\"
|
||||||
|
|
||||||
|
// Directive is the structure used during a build run to hold the state of
|
||||||
|
// parsing directives.
|
||||||
|
type Directive struct {
|
||||||
|
escapeToken rune // Current escape token
|
||||||
|
lineContinuationRegex *regexp.Regexp // Current line continuation regex
|
||||||
|
lookingForDirectives bool // Whether we are currently looking for directives
|
||||||
|
escapeSeen bool // Whether the escape directive has been seen
|
||||||
|
}
|
||||||
|
|
||||||
// SetEscapeToken sets the default token for escaping characters in a Dockerfile.
|
// SetEscapeToken sets the default token for escaping characters in a Dockerfile.
|
||||||
func SetEscapeToken(s string, d *Directive) error {
|
func (d *Directive) SetEscapeToken(s string) error {
|
||||||
if s != "`" && s != "\\" {
|
if s != "`" && s != "\\" {
|
||||||
return fmt.Errorf("invalid ESCAPE '%s'. Must be ` or \\", s)
|
return fmt.Errorf("invalid ESCAPE '%s'. Must be ` or \\", s)
|
||||||
}
|
}
|
||||||
d.EscapeToken = rune(s[0])
|
d.escapeToken = rune(s[0])
|
||||||
d.LineContinuationRegex = regexp.MustCompile(`\` + s + `[ \t]*$`)
|
d.lineContinuationRegex = regexp.MustCompile(`\` + s + `[ \t]*$`)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// EscapeToken returns the escape token
|
||||||
|
func (d *Directive) EscapeToken() rune {
|
||||||
|
return d.escapeToken
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDefaultDirective returns a new Directive with the default escapeToken token
|
||||||
|
func NewDefaultDirective() *Directive {
|
||||||
|
directive := Directive{
|
||||||
|
escapeSeen: false,
|
||||||
|
lookingForDirectives: true,
|
||||||
|
}
|
||||||
|
directive.SetEscapeToken(DefaultEscapeToken)
|
||||||
|
return &directive
|
||||||
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
// Dispatch Table. see line_parsers.go for the parse functions.
|
// Dispatch Table. see line_parsers.go for the parse functions.
|
||||||
// The command is parsed and mapped to the line parser. The line parser
|
// The command is parsed and mapped to the line parser. The line parser
|
||||||
|
@ -123,18 +138,18 @@ func init() {
|
||||||
// ParseLine parses a line and returns the remainder.
|
// ParseLine parses a line and returns the remainder.
|
||||||
func ParseLine(line string, d *Directive, ignoreCont bool) (string, *Node, error) {
|
func ParseLine(line string, d *Directive, ignoreCont bool) (string, *Node, error) {
|
||||||
if escapeFound, err := handleParserDirective(line, d); err != nil || escapeFound {
|
if escapeFound, err := handleParserDirective(line, d); err != nil || escapeFound {
|
||||||
d.EscapeSeen = escapeFound
|
d.escapeSeen = escapeFound
|
||||||
return "", nil, err
|
return "", nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
d.LookingForDirectives = false
|
d.lookingForDirectives = false
|
||||||
|
|
||||||
if line = stripComments(line); line == "" {
|
if line = stripComments(line); line == "" {
|
||||||
return "", nil, nil
|
return "", nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if !ignoreCont && d.LineContinuationRegex.MatchString(line) {
|
if !ignoreCont && d.lineContinuationRegex.MatchString(line) {
|
||||||
line = d.LineContinuationRegex.ReplaceAllString(line, "")
|
line = d.lineContinuationRegex.ReplaceAllString(line, "")
|
||||||
return line, nil, nil
|
return line, nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -170,22 +185,22 @@ func newNodeFromLine(line string, directive *Directive) (*Node, error) {
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle the parser directive '# escape=<char>. Parser directives must precede
|
// Handle the parser directive '# escapeToken=<char>. Parser directives must precede
|
||||||
// any builder instruction or other comments, and cannot be repeated.
|
// any builder instruction or other comments, and cannot be repeated.
|
||||||
func handleParserDirective(line string, d *Directive) (bool, error) {
|
func handleParserDirective(line string, d *Directive) (bool, error) {
|
||||||
if !d.LookingForDirectives {
|
if !d.lookingForDirectives {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
tecMatch := tokenEscapeCommand.FindStringSubmatch(strings.ToLower(line))
|
tecMatch := tokenEscapeCommand.FindStringSubmatch(strings.ToLower(line))
|
||||||
if len(tecMatch) == 0 {
|
if len(tecMatch) == 0 {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
if d.EscapeSeen == true {
|
if d.escapeSeen == true {
|
||||||
return false, fmt.Errorf("only one escape parser directive can be used")
|
return false, fmt.Errorf("only one escape parser directive can be used")
|
||||||
}
|
}
|
||||||
for i, n := range tokenEscapeCommand.SubexpNames() {
|
for i, n := range tokenEscapeCommand.SubexpNames() {
|
||||||
if n == "escapechar" {
|
if n == "escapechar" {
|
||||||
if err := SetEscapeToken(tecMatch[i], d); err != nil {
|
if err := d.SetEscapeToken(tecMatch[i]); err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
return true, nil
|
return true, nil
|
||||||
|
|
|
@ -40,9 +40,7 @@ func TestTestNegative(t *testing.T) {
|
||||||
}
|
}
|
||||||
defer df.Close()
|
defer df.Close()
|
||||||
|
|
||||||
d := Directive{LookingForDirectives: true}
|
_, err = Parse(df, NewDefaultDirective())
|
||||||
SetEscapeToken(DefaultEscapeToken, &d)
|
|
||||||
_, err = Parse(df, &d)
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatalf("No error parsing broken dockerfile for %s", dir)
|
t.Fatalf("No error parsing broken dockerfile for %s", dir)
|
||||||
}
|
}
|
||||||
|
@ -60,9 +58,7 @@ func TestTestData(t *testing.T) {
|
||||||
}
|
}
|
||||||
defer df.Close()
|
defer df.Close()
|
||||||
|
|
||||||
d := Directive{LookingForDirectives: true}
|
ast, err := Parse(df, NewDefaultDirective())
|
||||||
SetEscapeToken(DefaultEscapeToken, &d)
|
|
||||||
ast, err := Parse(df, &d)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Error parsing %s's dockerfile: %v", dir, err)
|
t.Fatalf("Error parsing %s's dockerfile: %v", dir, err)
|
||||||
}
|
}
|
||||||
|
@ -122,9 +118,7 @@ func TestParseWords(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
d := Directive{LookingForDirectives: true}
|
words := parseWords(test["input"][0], NewDefaultDirective())
|
||||||
SetEscapeToken(DefaultEscapeToken, &d)
|
|
||||||
words := parseWords(test["input"][0], &d)
|
|
||||||
if len(words) != len(test["expect"]) {
|
if len(words) != len(test["expect"]) {
|
||||||
t.Fatalf("length check failed. input: %v, expect: %q, output: %q", test["input"][0], test["expect"], words)
|
t.Fatalf("length check failed. input: %v, expect: %q, output: %q", test["input"][0], test["expect"], words)
|
||||||
}
|
}
|
||||||
|
@ -143,9 +137,7 @@ func TestLineInformation(t *testing.T) {
|
||||||
}
|
}
|
||||||
defer df.Close()
|
defer df.Close()
|
||||||
|
|
||||||
d := Directive{LookingForDirectives: true}
|
ast, err := Parse(df, NewDefaultDirective())
|
||||||
SetEscapeToken(DefaultEscapeToken, &d)
|
|
||||||
ast, err := Parse(df, &d)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Error parsing dockerfile %s: %v", testFileLineInfo, err)
|
t.Fatalf("Error parsing dockerfile %s: %v", testFileLineInfo, err)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue