mirror of
				https://gitea.com/Lydanne/buildx.git
				synced 2025-11-04 18:13:42 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			122 lines
		
	
	
		
			3.2 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			122 lines
		
	
	
		
			3.2 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package llb
 | 
						|
 | 
						|
import (
 | 
						|
	"context"
 | 
						|
 | 
						|
	"github.com/moby/buildkit/solver/pb"
 | 
						|
	digest "github.com/opencontainers/go-digest"
 | 
						|
	"github.com/pkg/errors"
 | 
						|
)
 | 
						|
 | 
						|
type MergeOp struct {
 | 
						|
	MarshalCache
 | 
						|
	inputs      []Output
 | 
						|
	output      Output
 | 
						|
	constraints Constraints
 | 
						|
}
 | 
						|
 | 
						|
func NewMerge(inputs []State, c Constraints) *MergeOp {
 | 
						|
	op := &MergeOp{constraints: c}
 | 
						|
	for _, input := range inputs {
 | 
						|
		op.inputs = append(op.inputs, input.Output())
 | 
						|
	}
 | 
						|
	op.output = &output{vertex: op}
 | 
						|
	return op
 | 
						|
}
 | 
						|
 | 
						|
func (m *MergeOp) Validate(ctx context.Context, constraints *Constraints) error {
 | 
						|
	if len(m.inputs) < 2 {
 | 
						|
		return errors.Errorf("merge must have at least 2 inputs")
 | 
						|
	}
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
func (m *MergeOp) Marshal(ctx context.Context, constraints *Constraints) (digest.Digest, []byte, *pb.OpMetadata, []*SourceLocation, error) {
 | 
						|
	if m.Cached(constraints) {
 | 
						|
		return m.Load()
 | 
						|
	}
 | 
						|
	if err := m.Validate(ctx, constraints); err != nil {
 | 
						|
		return "", nil, nil, nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	pop, md := MarshalConstraints(constraints, &m.constraints)
 | 
						|
	pop.Platform = nil // merge op is not platform specific
 | 
						|
 | 
						|
	op := &pb.MergeOp{}
 | 
						|
	for _, input := range m.inputs {
 | 
						|
		op.Inputs = append(op.Inputs, &pb.MergeInput{Input: pb.InputIndex(len(pop.Inputs))})
 | 
						|
		pbInput, err := input.ToInput(ctx, constraints)
 | 
						|
		if err != nil {
 | 
						|
			return "", nil, nil, nil, err
 | 
						|
		}
 | 
						|
		pop.Inputs = append(pop.Inputs, pbInput)
 | 
						|
	}
 | 
						|
	pop.Op = &pb.Op_Merge{Merge: op}
 | 
						|
 | 
						|
	dt, err := pop.Marshal()
 | 
						|
	if err != nil {
 | 
						|
		return "", nil, nil, nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	m.Store(dt, md, m.constraints.SourceLocations, constraints)
 | 
						|
	return m.Load()
 | 
						|
}
 | 
						|
 | 
						|
func (m *MergeOp) Output() Output {
 | 
						|
	return m.output
 | 
						|
}
 | 
						|
 | 
						|
func (m *MergeOp) Inputs() []Output {
 | 
						|
	return m.inputs
 | 
						|
}
 | 
						|
 | 
						|
// Merge merges multiple states into a single state. This is useful in
 | 
						|
// conjunction with [Diff] to create set of patches which are independent of
 | 
						|
// each other to a base state without affecting the cache of other merged
 | 
						|
// states.
 | 
						|
// As an example, lets say you have a rootfs with the following directories:
 | 
						|
//
 | 
						|
//	/ /bin /etc /opt /tmp
 | 
						|
//
 | 
						|
// Now lets say you want to copy a directory /etc/foo from one state and a
 | 
						|
// binary /bin/bar from another state.
 | 
						|
// [Copy] makes a duplicate of file on top of another directory.
 | 
						|
// Merge creates a directory whose contents is an overlay of 2 states on top of each other.
 | 
						|
//
 | 
						|
// With "Merge" you can do this:
 | 
						|
//
 | 
						|
//	fooState := Diff(rootfs, fooState)
 | 
						|
//	barState := Diff(rootfs, barState)
 | 
						|
//
 | 
						|
// Then merge the results with:
 | 
						|
//
 | 
						|
//	Merge(rootfs, fooDiff, barDiff)
 | 
						|
//
 | 
						|
// The resulting state will have both /etc/foo and /bin/bar, but because Merge
 | 
						|
// was used, changing the contents of "fooDiff" does not require copying
 | 
						|
// "barDiff" again.
 | 
						|
func Merge(inputs []State, opts ...ConstraintsOpt) State {
 | 
						|
	// filter out any scratch inputs, which have no effect when merged
 | 
						|
	var filteredInputs []State
 | 
						|
	for _, input := range inputs {
 | 
						|
		if input.Output() != nil {
 | 
						|
			filteredInputs = append(filteredInputs, input)
 | 
						|
		}
 | 
						|
	}
 | 
						|
	if len(filteredInputs) == 0 {
 | 
						|
		// a merge of only scratch results in scratch
 | 
						|
		return Scratch()
 | 
						|
	}
 | 
						|
	if len(filteredInputs) == 1 {
 | 
						|
		// a merge of a single non-empty input results in that non-empty input
 | 
						|
		return filteredInputs[0]
 | 
						|
	}
 | 
						|
 | 
						|
	var c Constraints
 | 
						|
	for _, o := range opts {
 | 
						|
		o.SetConstraintsOption(&c)
 | 
						|
	}
 | 
						|
	addCap(&c, pb.CapMergeOp)
 | 
						|
	return filteredInputs[0].WithOutput(NewMerge(filteredInputs, c).Output())
 | 
						|
}
 |