-
Notifications
You must be signed in to change notification settings - Fork 13
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
Build scripts can distinguish between solve nodes for different targets. #3531
Changes from 2 commits
27d4e9d
aed5512
683a457
bdf9fd5
b17383d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,6 +12,7 @@ import ( | |
const ( | ||
solveFuncName = "solve" | ||
solveLegacyFuncName = "solve_legacy" | ||
srcKey = "src" | ||
requirementsKey = "requirements" | ||
platformsKey = "platforms" | ||
) | ||
|
@@ -43,8 +44,10 @@ type UnknownRequirement struct { | |
|
||
func (r UnknownRequirement) IsRequirement() {} | ||
|
||
func (b *BuildScript) Requirements() ([]Requirement, error) { | ||
requirementsNode, err := b.getRequirementsNode() | ||
// Returns the requirements for the given target. | ||
// If the given target is the empty string, uses the default target (i.e. the name assigned to 'main'). | ||
func (b *BuildScript) Requirements(target string) ([]Requirement, error) { | ||
requirementsNode, err := b.getRequirementsNode(target) | ||
if err != nil { | ||
return nil, errs.Wrap(err, "Could not get requirements node") | ||
} | ||
|
@@ -95,8 +98,8 @@ func (b *BuildScript) Requirements() ([]Requirement, error) { | |
// DependencyRequirements is identical to Requirements except that it only considers dependency type requirements, | ||
// which are the most common. | ||
// ONLY use this when you know you only need to care about dependencies. | ||
func (b *BuildScript) DependencyRequirements() ([]types.Requirement, error) { | ||
reqs, err := b.Requirements() | ||
func (b *BuildScript) DependencyRequirements(target string) ([]types.Requirement, error) { | ||
reqs, err := b.Requirements(target) | ||
if err != nil { | ||
return nil, errs.Wrap(err, "Could not get requirements") | ||
} | ||
|
@@ -109,8 +112,8 @@ func (b *BuildScript) DependencyRequirements() ([]types.Requirement, error) { | |
return deps, nil | ||
} | ||
|
||
func (b *BuildScript) getRequirementsNode() (*Value, error) { | ||
node, err := b.getSolveNode() | ||
func (b *BuildScript) getRequirementsNode(target string) (*Value, error) { | ||
node, err := b.getSolveNode(target) | ||
if err != nil { | ||
return nil, errs.Wrap(err, "Could not get solve node") | ||
} | ||
|
@@ -147,7 +150,23 @@ func getVersionRequirements(v *Value) []types.VersionRequirement { | |
return reqs | ||
} | ||
|
||
func (b *BuildScript) getSolveNode() (*Value, error) { | ||
func isSolveFuncName(name string) bool { | ||
return name == solveFuncName || name == solveLegacyFuncName | ||
} | ||
|
||
func (b *BuildScript) getTargetNode(target string) (*Value, error) { | ||
if target == "" { | ||
for _, assignment := range b.raw.Assignments { | ||
if assignment.Key != mainKey { | ||
continue | ||
} | ||
if assignment.Value.Ident != nil { | ||
target = *assignment.Value.Ident | ||
break | ||
} | ||
} | ||
} | ||
|
||
var search func([]*Assignment) *Value | ||
search = func(assignments []*Assignment) *Value { | ||
var nextLet []*Assignment | ||
|
@@ -157,7 +176,13 @@ func (b *BuildScript) getSolveNode() (*Value, error) { | |
continue | ||
} | ||
|
||
if f := a.Value.FuncCall; f != nil && (f.Name == solveFuncName || f.Name == solveLegacyFuncName) { | ||
if a.Key == target && a.Value.FuncCall != nil { | ||
return a.Value | ||
} | ||
|
||
if f := a.Value.FuncCall; target == "" && f != nil && isSolveFuncName(f.Name) { | ||
// This is coming from a complex build expression with no straightforward way to determine | ||
// a default target. Fall back on a top-level solve node. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
return a.Value | ||
} | ||
} | ||
|
@@ -169,15 +194,45 @@ func (b *BuildScript) getSolveNode() (*Value, error) { | |
|
||
return nil | ||
} | ||
|
||
if node := search(b.raw.Assignments); node != nil { | ||
return node, nil | ||
} | ||
return nil, errNodeNotFound | ||
} | ||
|
||
func (b *BuildScript) getSolveNode(target string) (*Value, error) { | ||
node, err := b.getTargetNode(target) | ||
if err != nil { | ||
return nil, errs.Wrap(err, "Could not get target node") | ||
} | ||
|
||
// If the target is the solve function, we're done. | ||
if isSolveFuncName(node.FuncCall.Name) { | ||
return node, nil | ||
} | ||
|
||
// Otherwise, the "src" key contains a reference to the solve node. Look over the build expression | ||
// again for that referenced node. | ||
for _, arg := range node.FuncCall.Arguments { | ||
if arg.Assignment == nil { | ||
continue | ||
} | ||
a := arg.Assignment | ||
if a.Key == srcKey && a.Value.Ident != nil { | ||
node, err := b.getSolveNode(*a.Value.Ident) | ||
if err != nil { | ||
return nil, errs.Wrap(err, "Could not get solve node from target") | ||
} | ||
return node, nil | ||
} | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why can't this solve node be identified by the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider this build script snippet:
I've added a brief comment in the code. |
||
|
||
return nil, errNodeNotFound | ||
} | ||
|
||
func (b *BuildScript) getSolveAtTimeValue() (*Value, error) { | ||
node, err := b.getSolveNode() | ||
func (b *BuildScript) getSolveAtTimeValue(target string) (*Value, error) { | ||
node, err := b.getSolveNode(target) | ||
if err != nil { | ||
return nil, errs.Wrap(err, "Could not get solve node") | ||
} | ||
|
@@ -191,8 +246,8 @@ func (b *BuildScript) getSolveAtTimeValue() (*Value, error) { | |
return nil, errValueNotFound | ||
} | ||
|
||
func (b *BuildScript) Platforms() ([]strfmt.UUID, error) { | ||
node, err := b.getPlatformsNode() | ||
func (b *BuildScript) Platforms(target string) ([]strfmt.UUID, error) { | ||
node, err := b.getPlatformsNode(target) | ||
if err != nil { | ||
return nil, errs.Wrap(err, "Could not get platform node") | ||
} | ||
|
@@ -204,8 +259,8 @@ func (b *BuildScript) Platforms() ([]strfmt.UUID, error) { | |
return list, nil | ||
} | ||
|
||
func (b *BuildScript) getPlatformsNode() (*Value, error) { | ||
node, err := b.getSolveNode() | ||
func (b *BuildScript) getPlatformsNode(target string) (*Value, error) { | ||
node, err := b.getSolveNode(target) | ||
if err != nil { | ||
return nil, errs.Wrap(err, "Could not get solve node") | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -50,5 +50,15 @@ func Unmarshal(data []byte) (*BuildScript, error) { | |
break | ||
} | ||
|
||
// Verify there are no duplicate key assignments. | ||
// This is primarily to catch duplicate solve nodes for a given target. | ||
seen := make(map[string]bool) | ||
for _, assignment := range raw.Assignments { | ||
if _, exists := seen[assignment.Key]; exists { | ||
return nil, locale.NewInputError(locale.Tl("err_buildscript_duplicate_keys", "Build script has duplicate '{{.V0}}' assignments", assignment.Key)) | ||
} | ||
seen[assignment.Key] = true | ||
} | ||
Comment on lines
+55
to
+61
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does this still allow multiple solve nodes for different targets? The ACs in the story don't stipulate that it has to be multiple solve nodes for a single target that raise an error. Maybe the story needs to be updated? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, you can have more than one solve node in a buildscript. There's only an issue if you have a buildscript whose target has two different solve nodes. For example:
This error would identify |
||
|
||
return &BuildScript{raw}, nil | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Any taste for a variadic argument here? Would allow us to forgo the passing of
""
and be ready for multiple targets when the time comes.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes! I was looking for an excuse to make this an optional argument, and multiple target support justifies it!