github.com/vektra/tachyon@v0.0.0-20150921164542-0da4f3861aef/runner.go (about)

     1  package tachyon
     2  
     3  import (
     4  	"os"
     5  	"sync"
     6  	"time"
     7  )
     8  
     9  type RunResult struct {
    10  	Task    *Task
    11  	Result  *Result
    12  	Runtime time.Duration
    13  }
    14  
    15  type Runner struct {
    16  	env       *Environment
    17  	plays     []*Play
    18  	wait      sync.WaitGroup
    19  	to_notify map[string]struct{}
    20  	async     chan *AsyncAction
    21  	report    Reporter
    22  
    23  	Results []RunResult
    24  	Start   time.Time
    25  	Runtime time.Duration
    26  }
    27  
    28  func NewRunner(env *Environment, plays []*Play) *Runner {
    29  	r := &Runner{
    30  		env:       env,
    31  		plays:     plays,
    32  		to_notify: make(map[string]struct{}),
    33  		async:     make(chan *AsyncAction),
    34  		report:    env.report,
    35  	}
    36  
    37  	go r.handleAsync()
    38  
    39  	return r
    40  }
    41  
    42  func (r *Runner) SetReport(rep Reporter) {
    43  	r.report = rep
    44  }
    45  
    46  func (r *Runner) AddNotify(n string) {
    47  	r.to_notify[n] = struct{}{}
    48  }
    49  
    50  func (r *Runner) ShouldRunHandler(name string) bool {
    51  	_, ok := r.to_notify[name]
    52  
    53  	return ok
    54  }
    55  
    56  func (r *Runner) AsyncChannel() chan *AsyncAction {
    57  	return r.async
    58  }
    59  
    60  func (r *Runner) Run(env *Environment) error {
    61  	start := time.Now()
    62  	r.Start = start
    63  
    64  	defer func() {
    65  		r.Runtime = time.Since(start)
    66  	}()
    67  
    68  	r.report.StartTasks(r)
    69  
    70  	for _, play := range r.plays {
    71  		fs := NewFutureScope(play.Vars)
    72  
    73  		for _, task := range play.Tasks {
    74  			err := r.runTask(env, play, task, fs, fs)
    75  			if err != nil {
    76  				return err
    77  			}
    78  		}
    79  
    80  		r.Results = append(r.Results, fs.Results()...)
    81  	}
    82  
    83  	r.report.FinishTasks(r)
    84  
    85  	r.wait.Wait()
    86  
    87  	r.report.StartHandlers(r)
    88  
    89  	for _, play := range r.plays {
    90  		fs := NewFutureScope(play.Vars)
    91  
    92  		for _, task := range play.Handlers {
    93  			if r.ShouldRunHandler(task.Name()) {
    94  				err := r.runTask(env, play, task, fs, fs)
    95  
    96  				if err != nil {
    97  					return err
    98  				}
    99  			}
   100  		}
   101  
   102  		fs.Wait()
   103  	}
   104  
   105  	r.report.FinishHandlers(r)
   106  
   107  	return nil
   108  }
   109  
   110  func RunAdhocTask(cmd, args string) (*Result, error) {
   111  	env := NewEnv(NewNestedScope(nil), &Config{})
   112  	defer env.Cleanup()
   113  
   114  	task := AdhocTask(cmd, args)
   115  
   116  	str, err := ExpandVars(env.Vars, task.Args())
   117  	if err != nil {
   118  		return nil, err
   119  	}
   120  
   121  	obj, _, err := MakeCommand(env.Vars, task, str)
   122  	if err != nil {
   123  		return nil, err
   124  	}
   125  
   126  	ar := &AdhocProgress{out: os.Stdout, Start: time.Now()}
   127  
   128  	ce := &CommandEnv{Env: env, Paths: env.Paths, progress: ar}
   129  
   130  	return obj.Run(ce)
   131  }
   132  
   133  func RunAdhocTaskVars(td TaskData) (*Result, error) {
   134  	env := NewEnv(NewNestedScope(nil), &Config{})
   135  	defer env.Cleanup()
   136  
   137  	task := &Task{data: td}
   138  	task.Init(env)
   139  
   140  	obj, _, err := MakeCommand(env.Vars, task, "")
   141  	if err != nil {
   142  		return nil, err
   143  	}
   144  
   145  	ar := &AdhocProgress{out: os.Stdout, Start: time.Now()}
   146  
   147  	ce := &CommandEnv{Env: env, Paths: env.Paths, progress: ar}
   148  
   149  	return obj.Run(ce)
   150  }
   151  
   152  func RunAdhocCommand(cmd Command, args string) (*Result, error) {
   153  	env := NewEnv(NewNestedScope(nil), &Config{})
   154  	defer env.Cleanup()
   155  
   156  	ar := &AdhocProgress{out: os.Stdout, Start: time.Now()}
   157  
   158  	ce := &CommandEnv{Env: env, Paths: env.Paths, progress: ar}
   159  
   160  	return cmd.Run(ce)
   161  }
   162  
   163  type PriorityScope struct {
   164  	task Vars
   165  	rest Scope
   166  }
   167  
   168  func (p *PriorityScope) Get(key string) (Value, bool) {
   169  	if p.task != nil {
   170  		if v, ok := p.task[key]; ok {
   171  			return Any(v), true
   172  		}
   173  	}
   174  
   175  	return p.rest.Get(key)
   176  }
   177  
   178  func (p *PriorityScope) Set(key string, val interface{}) {
   179  	p.rest.Set(key, val)
   180  }
   181  
   182  func boolify(str string) bool {
   183  	switch str {
   184  	case "", "false", "no":
   185  		return false
   186  	default:
   187  		return true
   188  	}
   189  }
   190  
   191  type ModuleRun struct {
   192  	Play        *Play
   193  	Task        *Task
   194  	Module      *Module
   195  	Runner      *Runner
   196  	Scope       Scope
   197  	FutureScope *FutureScope
   198  	Vars        Vars
   199  }
   200  
   201  func (m *ModuleRun) Run(env *CommandEnv) (*Result, error) {
   202  	for _, task := range m.Module.ModTasks {
   203  		ns := NewNestedScope(m.Scope)
   204  
   205  		for k, v := range m.Vars {
   206  			ns.Set(k, v)
   207  		}
   208  
   209  		err := m.Runner.runTask(env.Env, m.Play, task, ns, m.FutureScope)
   210  		if err != nil {
   211  			return nil, err
   212  		}
   213  	}
   214  
   215  	return NewResult(true), nil
   216  }
   217  
   218  func (r *Runner) runTaskItems(env *Environment, play *Play, task *Task, s Scope, fs *FutureScope, start time.Time) error {
   219  
   220  	for _, item := range task.Items() {
   221  		ns := NewNestedScope(s)
   222  		ns.Set("item", item)
   223  
   224  		name, err := ExpandVars(ns, task.Name())
   225  		if err != nil {
   226  			return err
   227  		}
   228  
   229  		str, err := ExpandVars(ns, task.Args())
   230  		if err != nil {
   231  			return err
   232  		}
   233  
   234  		cmd, sm, err := MakeCommand(ns, task, str)
   235  		if err != nil {
   236  			return err
   237  		}
   238  
   239  		r.report.StartTask(task, name, str, sm)
   240  
   241  		ce := NewCommandEnv(env, task)
   242  
   243  		res, err := cmd.Run(ce)
   244  
   245  		if name := task.Register(); name != "" {
   246  			fs.Set(name, res)
   247  		}
   248  
   249  		runtime := time.Since(start)
   250  
   251  		if err != nil {
   252  			res = FailureResult(err)
   253  		}
   254  
   255  		r.Results = append(r.Results, RunResult{task, res, runtime})
   256  
   257  		r.report.FinishTask(task, res)
   258  
   259  		if err == nil {
   260  			for _, x := range task.Notify() {
   261  				r.AddNotify(x)
   262  			}
   263  		} else {
   264  			return err
   265  		}
   266  	}
   267  
   268  	return nil
   269  }
   270  
   271  func (r *Runner) runTask(env *Environment, play *Play, task *Task, s Scope, fs *FutureScope) error {
   272  	ps := &PriorityScope{task.IncludeVars, s}
   273  
   274  	start := time.Now()
   275  
   276  	if when := task.When(); when != "" {
   277  		when, err := ExpandVars(ps, when)
   278  
   279  		if err != nil {
   280  			return err
   281  		}
   282  
   283  		if !boolify(when) {
   284  			return nil
   285  		}
   286  	}
   287  
   288  	if items := task.Items(); items != nil {
   289  		return r.runTaskItems(env, play, task, s, fs, start)
   290  	}
   291  
   292  	name, err := ExpandVars(ps, task.Name())
   293  	if err != nil {
   294  		return err
   295  	}
   296  
   297  	str, err := ExpandVars(ps, task.Args())
   298  	if err != nil {
   299  		return err
   300  	}
   301  
   302  	var cmd Command
   303  
   304  	var argVars Vars
   305  
   306  	if mod, ok := play.Modules[task.Command()]; ok {
   307  		sm, err := ParseSimpleMap(s, str)
   308  		if err != nil {
   309  			return err
   310  		}
   311  
   312  		for ik, iv := range task.Vars {
   313  			if str, ok := iv.Read().(string); ok {
   314  				exp, err := ExpandVars(s, str)
   315  				if err != nil {
   316  					return err
   317  				}
   318  
   319  				sm[ik] = Any(exp)
   320  			} else {
   321  				sm[ik] = iv
   322  			}
   323  		}
   324  
   325  		cmd = &ModuleRun{
   326  			Play:        play,
   327  			Task:        task,
   328  			Module:      mod,
   329  			Runner:      r,
   330  			Scope:       s,
   331  			FutureScope: NewFutureScope(s),
   332  			Vars:        sm,
   333  		}
   334  
   335  		argVars = sm
   336  	} else {
   337  		cmd, argVars, err = MakeCommand(ps, task, str)
   338  
   339  		if err != nil {
   340  			return err
   341  		}
   342  	}
   343  
   344  	r.report.StartTask(task, name, str, argVars)
   345  
   346  	ce := NewCommandEnv(env, task)
   347  
   348  	if name := task.Future(); name != "" {
   349  		future := NewFuture(start, task, func() (*Result, error) {
   350  			return cmd.Run(ce)
   351  		})
   352  
   353  		fs.AddFuture(name, future)
   354  
   355  		return nil
   356  	}
   357  
   358  	if task.Async() {
   359  		asyncAction := &AsyncAction{Task: task}
   360  		asyncAction.Init(r)
   361  
   362  		go func() {
   363  			asyncAction.Finish(cmd.Run(ce))
   364  		}()
   365  	} else {
   366  		res, err := cmd.Run(ce)
   367  
   368  		if name := task.Register(); name != "" {
   369  			fs.Set(name, res)
   370  		}
   371  
   372  		runtime := time.Since(start)
   373  
   374  		if err != nil {
   375  			res = FailureResult(err)
   376  		}
   377  
   378  		r.Results = append(r.Results, RunResult{task, res, runtime})
   379  
   380  		r.report.FinishTask(task, res)
   381  
   382  		if err == nil {
   383  			for _, x := range task.Notify() {
   384  				r.AddNotify(x)
   385  			}
   386  		} else {
   387  			return err
   388  		}
   389  	}
   390  
   391  	return err
   392  }