mirror of
				https://gitea.com/Lydanne/buildx.git
				synced 2025-11-04 18:13:42 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			228 lines
		
	
	
		
			5.6 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			228 lines
		
	
	
		
			5.6 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package llb
 | 
						|
 | 
						|
import (
 | 
						|
	"context"
 | 
						|
	"sync"
 | 
						|
 | 
						|
	"github.com/moby/buildkit/solver/pb"
 | 
						|
	digest "github.com/opencontainers/go-digest"
 | 
						|
	specs "github.com/opencontainers/image-spec/specs-go/v1"
 | 
						|
	"github.com/pkg/errors"
 | 
						|
)
 | 
						|
 | 
						|
// DefinitionOp implements llb.Vertex using a marshalled definition.
 | 
						|
//
 | 
						|
// For example, after marshalling a LLB state and sending over the wire, the
 | 
						|
// LLB state can be reconstructed from the definition.
 | 
						|
type DefinitionOp struct {
 | 
						|
	MarshalCache
 | 
						|
	mu         sync.Mutex
 | 
						|
	ops        map[digest.Digest]*pb.Op
 | 
						|
	defs       map[digest.Digest][]byte
 | 
						|
	metas      map[digest.Digest]pb.OpMetadata
 | 
						|
	sources    map[digest.Digest][]*SourceLocation
 | 
						|
	platforms  map[digest.Digest]*specs.Platform
 | 
						|
	dgst       digest.Digest
 | 
						|
	index      pb.OutputIndex
 | 
						|
	inputCache map[digest.Digest][]*DefinitionOp
 | 
						|
}
 | 
						|
 | 
						|
// NewDefinitionOp returns a new operation from a marshalled definition.
 | 
						|
func NewDefinitionOp(def *pb.Definition) (*DefinitionOp, error) {
 | 
						|
	ops := make(map[digest.Digest]*pb.Op)
 | 
						|
	defs := make(map[digest.Digest][]byte)
 | 
						|
	platforms := make(map[digest.Digest]*specs.Platform)
 | 
						|
 | 
						|
	var dgst digest.Digest
 | 
						|
	for _, dt := range def.Def {
 | 
						|
		var op pb.Op
 | 
						|
		if err := (&op).Unmarshal(dt); err != nil {
 | 
						|
			return nil, errors.Wrap(err, "failed to parse llb proto op")
 | 
						|
		}
 | 
						|
		dgst = digest.FromBytes(dt)
 | 
						|
		ops[dgst] = &op
 | 
						|
		defs[dgst] = dt
 | 
						|
 | 
						|
		var platform *specs.Platform
 | 
						|
		if op.Platform != nil {
 | 
						|
			spec := op.Platform.Spec()
 | 
						|
			platform = &spec
 | 
						|
		}
 | 
						|
		platforms[dgst] = platform
 | 
						|
	}
 | 
						|
 | 
						|
	srcs := map[digest.Digest][]*SourceLocation{}
 | 
						|
 | 
						|
	if def.Source != nil {
 | 
						|
		sourceMaps := make([]*SourceMap, len(def.Source.Infos))
 | 
						|
		for i, info := range def.Source.Infos {
 | 
						|
			var st *State
 | 
						|
			sdef := info.Definition
 | 
						|
			if sdef != nil {
 | 
						|
				op, err := NewDefinitionOp(sdef)
 | 
						|
				if err != nil {
 | 
						|
					return nil, err
 | 
						|
				}
 | 
						|
				state := NewState(op)
 | 
						|
				st = &state
 | 
						|
			}
 | 
						|
			sourceMaps[i] = NewSourceMap(st, info.Filename, info.Data)
 | 
						|
		}
 | 
						|
 | 
						|
		for dgst, locs := range def.Source.Locations {
 | 
						|
			for _, loc := range locs.Locations {
 | 
						|
				if loc.SourceIndex < 0 || int(loc.SourceIndex) >= len(sourceMaps) {
 | 
						|
					return nil, errors.Errorf("failed to find source map with index %d", loc.SourceIndex)
 | 
						|
				}
 | 
						|
 | 
						|
				srcs[digest.Digest(dgst)] = append(srcs[digest.Digest(dgst)], &SourceLocation{
 | 
						|
					SourceMap: sourceMaps[int(loc.SourceIndex)],
 | 
						|
					Ranges:    loc.Ranges,
 | 
						|
				})
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	var index pb.OutputIndex
 | 
						|
	if dgst != "" {
 | 
						|
		index = ops[dgst].Inputs[0].Index
 | 
						|
		dgst = ops[dgst].Inputs[0].Digest
 | 
						|
	}
 | 
						|
 | 
						|
	return &DefinitionOp{
 | 
						|
		ops:        ops,
 | 
						|
		defs:       defs,
 | 
						|
		metas:      def.Metadata,
 | 
						|
		sources:    srcs,
 | 
						|
		platforms:  platforms,
 | 
						|
		dgst:       dgst,
 | 
						|
		index:      index,
 | 
						|
		inputCache: make(map[digest.Digest][]*DefinitionOp),
 | 
						|
	}, nil
 | 
						|
}
 | 
						|
 | 
						|
func (d *DefinitionOp) ToInput(ctx context.Context, c *Constraints) (*pb.Input, error) {
 | 
						|
	return d.Output().ToInput(ctx, c)
 | 
						|
}
 | 
						|
 | 
						|
func (d *DefinitionOp) Vertex(context.Context) Vertex {
 | 
						|
	return d
 | 
						|
}
 | 
						|
 | 
						|
func (d *DefinitionOp) Validate(context.Context) error {
 | 
						|
	// Scratch state has no digest, ops or metas.
 | 
						|
	if d.dgst == "" {
 | 
						|
		return nil
 | 
						|
	}
 | 
						|
 | 
						|
	d.mu.Lock()
 | 
						|
	defer d.mu.Unlock()
 | 
						|
 | 
						|
	if len(d.ops) == 0 || len(d.defs) == 0 || len(d.metas) == 0 {
 | 
						|
		return errors.Errorf("invalid definition op with no ops %d %d", len(d.ops), len(d.metas))
 | 
						|
	}
 | 
						|
 | 
						|
	_, ok := d.ops[d.dgst]
 | 
						|
	if !ok {
 | 
						|
		return errors.Errorf("invalid definition op with unknown op %q", d.dgst)
 | 
						|
	}
 | 
						|
 | 
						|
	_, ok = d.defs[d.dgst]
 | 
						|
	if !ok {
 | 
						|
		return errors.Errorf("invalid definition op with unknown def %q", d.dgst)
 | 
						|
	}
 | 
						|
 | 
						|
	_, ok = d.metas[d.dgst]
 | 
						|
	if !ok {
 | 
						|
		return errors.Errorf("invalid definition op with unknown metas %q", d.dgst)
 | 
						|
	}
 | 
						|
 | 
						|
	// It is possible for d.index >= len(d.ops[d.dgst]) when depending on scratch
 | 
						|
	// images.
 | 
						|
	if d.index < 0 {
 | 
						|
		return errors.Errorf("invalid definition op with invalid index")
 | 
						|
	}
 | 
						|
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
func (d *DefinitionOp) Marshal(ctx context.Context, c *Constraints) (digest.Digest, []byte, *pb.OpMetadata, []*SourceLocation, error) {
 | 
						|
	if d.dgst == "" {
 | 
						|
		return "", nil, nil, nil, errors.Errorf("cannot marshal empty definition op")
 | 
						|
	}
 | 
						|
 | 
						|
	if err := d.Validate(ctx); err != nil {
 | 
						|
		return "", nil, nil, nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	d.mu.Lock()
 | 
						|
	defer d.mu.Unlock()
 | 
						|
 | 
						|
	meta := d.metas[d.dgst]
 | 
						|
	return d.dgst, d.defs[d.dgst], &meta, d.sources[d.dgst], nil
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
func (d *DefinitionOp) Output() Output {
 | 
						|
	if d.dgst == "" {
 | 
						|
		return nil
 | 
						|
	}
 | 
						|
 | 
						|
	d.mu.Lock()
 | 
						|
	platform := d.platforms[d.dgst]
 | 
						|
	d.mu.Unlock()
 | 
						|
 | 
						|
	return &output{vertex: d, platform: platform, getIndex: func() (pb.OutputIndex, error) {
 | 
						|
		return d.index, nil
 | 
						|
	}}
 | 
						|
}
 | 
						|
 | 
						|
func (d *DefinitionOp) Inputs() []Output {
 | 
						|
	if d.dgst == "" {
 | 
						|
		return nil
 | 
						|
	}
 | 
						|
 | 
						|
	var inputs []Output
 | 
						|
 | 
						|
	d.mu.Lock()
 | 
						|
	op := d.ops[d.dgst]
 | 
						|
	platform := d.platforms[d.dgst]
 | 
						|
	d.mu.Unlock()
 | 
						|
 | 
						|
	for _, input := range op.Inputs {
 | 
						|
		var vtx *DefinitionOp
 | 
						|
		d.mu.Lock()
 | 
						|
		if existingIndexes, ok := d.inputCache[input.Digest]; ok {
 | 
						|
			if int(input.Index) < len(existingIndexes) && existingIndexes[input.Index] != nil {
 | 
						|
				vtx = existingIndexes[input.Index]
 | 
						|
			}
 | 
						|
		}
 | 
						|
		if vtx == nil {
 | 
						|
			vtx = &DefinitionOp{
 | 
						|
				ops:        d.ops,
 | 
						|
				defs:       d.defs,
 | 
						|
				metas:      d.metas,
 | 
						|
				platforms:  d.platforms,
 | 
						|
				dgst:       input.Digest,
 | 
						|
				index:      input.Index,
 | 
						|
				inputCache: d.inputCache,
 | 
						|
			}
 | 
						|
			existingIndexes := d.inputCache[input.Digest]
 | 
						|
			indexDiff := int(input.Index) - len(existingIndexes)
 | 
						|
			if indexDiff >= 0 {
 | 
						|
				// make room in the slice for the new index being set
 | 
						|
				existingIndexes = append(existingIndexes, make([]*DefinitionOp, indexDiff+1)...)
 | 
						|
			}
 | 
						|
			existingIndexes[input.Index] = vtx
 | 
						|
			d.inputCache[input.Digest] = existingIndexes
 | 
						|
		}
 | 
						|
		d.mu.Unlock()
 | 
						|
 | 
						|
		inputs = append(inputs, &output{vertex: vtx, platform: platform, getIndex: func() (pb.OutputIndex, error) {
 | 
						|
			return pb.OutputIndex(vtx.index), nil
 | 
						|
		}})
 | 
						|
	}
 | 
						|
 | 
						|
	return inputs
 | 
						|
}
 |