blob: 00c2d421c1757ad8b8852b6fa6faa1ebad59e8cb [file] [log] [blame]
// Copyright (C) 2015 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 maker
import (
"bufio"
"fmt"
"log"
"os"
"reflect"
"sync"
"time"
)
// Step represents an action that creates it's outputs from it's inputs.
// It is the linking entity in the build graph.
// Each entity can be built by only one step, but a step may build many entites,
// and may depend on many entities.
// It is an error to have a cycle in the build graph.
type Step struct {
inputs []Entity
outputs []Entity
always bool
action func(*Step) error
once sync.Once
done chan struct{}
err error
accesses []string
}
var (
steps = map[Entity]*Step{}
stepHooks = []func(s *Step){}
)
// StepHook adds a function that is invoked each time a step is added or
// modified.
func StepHook(f func(s *Step)) {
stepHooks = append(stepHooks, f)
}
// Creator looks up the step that builds an entity.
// The entity will be looked up using EntityOf.
func Creator(of interface{}) *Step {
e := EntityOf(of)
s, _ := steps[e]
return s
}
// NewStep creates and returns a new step that runs the supplied action.
func NewStep(a func(*Step) error) *Step {
s := &Step{
action: a,
done: make(chan struct{}),
}
if Config.DisableParallel {
s.Access("single_thread")
}
for _, h := range stepHooks {
h(s)
}
return s
}
// Creates adds new outputs to the Step.
// It will complain if any of the outputs already have a Creator.
func (s *Step) Creates(out ...Entity) *Step {
for _, e := range out {
if Creator(e) != nil {
log.Fatalf("%s created by more than one step", e.Name())
}
steps[e] = s
}
s.outputs = append(s.outputs, out...)
for _, h := range stepHooks {
h(s)
}
return s
}
// DependsOn adds a new input dependancy to a Step.
// If the Step already depends on an input, it will not be added again.
func (s *Step) DependsOn(in ...interface{}) *Step {
for _, v := range in {
e := EntityOf(v)
if !s.HasInput(e) {
s.inputs = append(s.inputs, e)
}
}
for _, h := range stepHooks {
h(s)
}
return s
}
// Access adds the supplied shared resource names to the list of resources
// accessed by this step.
func (s *Step) Access(v ...string) *Step {
s.accesses = append(s.accesses, v...)
for _, name := range v {
addLock(name)
}
return s
}
// HasInput returns true if the step already has the supplied entity in it's inputs.
func (s *Step) HasInput(e Entity) bool {
for _, in := range s.inputs {
if in == e {
return true
}
}
return false
}
// HasOutput returns true if the step already has the supplied entity in it's outputs.
func (s *Step) HasOutput(e Entity) bool {
for _, out := range s.outputs {
if out == e {
return true
}
}
return false
}
// AlwaysRun marks a step as always needing to run, rather than running only
// when it's outputs need updating.
func (s *Step) AlwaysRun() *Step {
s.always = true
return s
}
// String returns the name of the first output if present, for debugging.
func (s *Step) String() string {
if len(s.outputs) > 0 {
return s.outputs[0].Name()
}
return ""
}
// UseDepsFile reads in a deps file and uses it to populuate the inputs and
// outputs of the Step.
func (s *Step) UseDepsFile(deps Entity) {
s.Creates(deps)
depsfile, err := os.Open(deps.Name())
if err != nil {
return
}
defer depsfile.Close()
scanner := bufio.NewScanner(depsfile)
inputs := true
for scanner.Scan() {
line := scanner.Text()
switch {
case line == "==Inputs==":
inputs = true
case line == "==Outputs==":
inputs = false
case inputs:
s.DependsOn(File(line))
default:
s.Creates(File(line))
}
}
}
// DependsStruct adds all the fields of the supplied struct as input dependacies
// of the step.
// It is an error if the struct has any fields that are not public fields of
// types that implement Entity.
func (s *Step) DependsStruct(v interface{}) {
val := reflect.ValueOf(v)
for i := 0; i < val.NumField(); i++ {
f := val.Field(i)
s.DependsOn(f.Interface().(Entity))
}
}
func (s *Step) updateInputs() {
deps := make([]*Step, 0, len(s.inputs))
// Bring all inputs up to date in parallel
for _, e := range s.inputs {
dep := Creator(e)
if dep != nil {
deps = append(deps, dep)
go dep.start()
}
}
// Wait for all inputs to be ready
for _, dep := range deps {
<-dep.done
if dep.err != nil && s.err == nil {
s.err = fmt.Errorf("failed in %s", dep)
}
}
}
func (s *Step) shouldRun() bool {
if s.always {
return true
}
// Find the newest input
t := time.Time{}
for _, e := range s.inputs {
t = Newest(t, e.Timestamp())
}
if t.IsZero() {
// No timestamped inputs, so always run
return true
}
// Ask the outputs if they want an update
for _, e := range s.outputs {
if e.NeedsUpdate(t) {
return true
}
}
return false
}
func (s *Step) run() {
if s.action == nil {
return
}
if err := s.action(s); err != nil {
Errors.Add(s, err)
}
// Mark all our ouptuts as potentially updated
for _, e := range s.outputs {
e.Updated()
}
}
func (s *Step) start() {
s.once.Do(func() {
s.updateInputs()
if s.err == nil && s.shouldRun() {
withLocks(s.accesses, s.run)
}
// Signal we are complete
close(s.done)
})
}