| // Copyright (C) 2016 The Android Open Source Project |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| package build |
| |
| import ( |
| "reflect" |
| "sync" |
| |
| "android.googlesource.com/platform/tools/gpu/framework/device" |
| "android.googlesource.com/platform/tools/gpu/framework/event" |
| "android.googlesource.com/platform/tools/gpu/framework/id" |
| "android.googlesource.com/platform/tools/gpu/framework/log" |
| "android.googlesource.com/platform/tools/gpu/gapid/record" |
| "android.googlesource.com/platform/tools/gpu/gapid/search" |
| "android.googlesource.com/platform/tools/gpu/gapid/search/eval" |
| ) |
| |
| var packageClass = reflect.TypeOf(&Package{}) |
| |
| type packages struct { |
| mu sync.Mutex |
| ledger record.Ledger |
| entries []*Package |
| byID map[string]*Package |
| byName map[string]*Package |
| onChange event.Broadcast |
| } |
| |
| func (p *packages) init(ctx log.Context, library record.Library) error { |
| ledger, err := library.Open(ctx, "packages", &Package{}) |
| if err != nil { |
| return err |
| } |
| p.ledger = ledger |
| p.byID = map[string]*Package{} |
| apply := event.AsHandler(ctx, p.apply) |
| if err := ledger.Read(ctx, apply); err != nil { |
| return err |
| } |
| ledger.Watch(ctx, apply) |
| return nil |
| } |
| |
| // apply is called with items coming out of the ledger |
| // it should be called with the mutation lock already held. |
| func (p *packages) apply(ctx log.Context, pkg *Package) error { |
| old := p.byID[pkg.Id] |
| if old == nil { |
| p.entries = append(p.entries, pkg) |
| p.byID[pkg.Id] = pkg |
| p.onChange.Send(ctx, pkg) |
| return nil |
| } |
| if pkg.Parent != "" { |
| old.Parent = pkg.Parent |
| } |
| if pkg.Information != nil { |
| // description is the only thing we allow to be edited |
| if pkg.Information.Description != "" { |
| old.Information.Description = pkg.Information.Description |
| } |
| } |
| if len(pkg.Artifact) > 0 { |
| old.Artifact = append(old.Artifact, pkg.Artifact...) |
| } |
| for _, t := range pkg.Tool { |
| old.mergeTool(ctx, t) |
| } |
| p.onChange.Send(ctx, pkg) |
| return nil |
| } |
| |
| func (p *packages) search(ctx log.Context, query *search.Query, handler PackageHandler) error { |
| filter := eval.Filter(ctx, query, packageClass, event.AsHandler(ctx, handler)) |
| initial := event.AsProducer(ctx, p.entries) |
| if query.Monitor { |
| return event.Monitor(ctx, &p.mu, p.onChange.Listen, initial, filter) |
| } |
| return event.Feed(ctx, filter, initial) |
| } |
| |
| func (p *packages) update(ctx log.Context, pkg *Package) error { |
| p.mu.Lock() |
| defer p.mu.Unlock() |
| return p.ledger.Add(ctx, pkg) |
| } |
| |
| func (p *packages) addArtifact(ctx log.Context, a *Artifact, info *Information) (*Package, bool, error) { |
| p.mu.Lock() |
| defer p.mu.Unlock() |
| |
| old := p.findArtifactPackage(ctx, a, info) |
| pkg := &Package{ |
| Information: info, |
| Artifact: []string{a.Id}, |
| Tool: a.Tool, |
| } |
| merged := false |
| if old != nil { |
| pkg.Id = old.Id |
| merged = true |
| } else { |
| pkg.Id = id.Unique().String() |
| } |
| if err := p.ledger.Add(ctx, pkg); err != nil { |
| return nil, false, err |
| } |
| return p.byID[pkg.Id], merged, nil |
| } |
| |
| func (p *packages) findArtifactPackage(ctx log.Context, a *Artifact, info *Information) *Package { |
| // Search for a set to merge into |
| if info.Tag != "" { |
| // if we have a tag, that's the only thing that matters |
| for _, pkg := range p.entries { |
| if pkg.Information.Tag == info.Tag { |
| return pkg |
| } |
| } |
| } |
| // if we don't have a tag, local builds never merge |
| if info.Type == Local { |
| return nil |
| } |
| if info.Cl != "" { |
| // non local builds with a matching cl can be merged |
| for _, pkg := range p.entries { |
| if pkg.Information.Cl == info.Cl { |
| return pkg |
| } |
| } |
| } |
| // No match, cannot merge |
| return nil |
| } |
| |
| func (pkg *Package) mergeTool(ctx log.Context, tool *ToolSet) { |
| for _, t := range pkg.Tool { |
| if device.SameABI(*t.Abi, *tool.Abi) { |
| // merge into existing tool |
| if tool.Interceptor != "" { |
| t.Interceptor = tool.Interceptor |
| } |
| if tool.Gapii != "" { |
| t.Gapii = tool.Gapii |
| } |
| if tool.Gapir != "" { |
| t.Gapir = tool.Gapir |
| } |
| if tool.Gapis != "" { |
| t.Gapis = tool.Gapis |
| } |
| if tool.Gapit != "" { |
| t.Gapit = tool.Gapit |
| } |
| return |
| } |
| } |
| // no matching tool, so just append it |
| pkg.Tool = append(pkg.Tool, tool) |
| } |
| |
| // GetTools will return the toolset that match the abi, if there is one. |
| func (p *Package) GetTools(abi *device.ABI) *ToolSet { |
| for _, t := range p.Tool { |
| if device.SameABI(*t.Abi, *abi) { |
| return t |
| } |
| } |
| return nil |
| } |