blob: f3f690d7da9dcb5aac8852cf0cb3352fb90dbe38 [file] [log] [blame]
// 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
}