add parallelism with sections | todo: add [pre] section executed before all
This commit is contained in:
parent
7e8b45d750
commit
5fd41cae67
|
@ -7,6 +7,7 @@ import (
|
|||
"github.com/xdrm-brackets/nix-amer/internal/clifmt"
|
||||
"github.com/xdrm-brackets/nix-amer/internal/instruction"
|
||||
"io"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
@ -14,14 +15,31 @@ import (
|
|||
// ErrNullContext is raised when the given context is nil
|
||||
var ErrNullContext = errors.New("null context")
|
||||
|
||||
// ErrNoParent is raised when there is an instruction but has no parent section
|
||||
var ErrNoParent = errors.New("missing parent section")
|
||||
|
||||
// Reader is the buildfile reader
|
||||
type Reader struct {
|
||||
// Context is the linux distribution-specified execution context (package manager, service manager, etc)
|
||||
Context *instruction.ExecutionContext
|
||||
// Content is the instruction list
|
||||
Content []instruction.T
|
||||
Content map[string]*[]instruction.T
|
||||
}
|
||||
|
||||
type execStatus struct {
|
||||
name string
|
||||
start time.Time
|
||||
stop time.Time
|
||||
stopped bool
|
||||
err error
|
||||
}
|
||||
type tableSection struct {
|
||||
name string
|
||||
instructions []execStatus
|
||||
}
|
||||
|
||||
var reSection = regexp.MustCompile(`(?m)^\[\s*([a-z0-9_-]+)\s*\]$`)
|
||||
|
||||
// NewReader creates a new reader for the specified build file and linux distribution
|
||||
func NewReader(ctx *instruction.ExecutionContext, buildfile io.Reader) (*Reader, error) {
|
||||
|
||||
|
@ -32,20 +50,20 @@ func NewReader(ctx *instruction.ExecutionContext, buildfile io.Reader) (*Reader,
|
|||
|
||||
r := &Reader{
|
||||
Context: ctx,
|
||||
Content: make([]instruction.T, 0),
|
||||
Content: make(map[string]*[]instruction.T),
|
||||
}
|
||||
|
||||
// add each line as instruction
|
||||
l, reader := 0, bufio.NewReader(buildfile)
|
||||
eof := false
|
||||
var section *[]instruction.T = nil // current section
|
||||
for {
|
||||
l++
|
||||
if eof {
|
||||
break
|
||||
}
|
||||
|
||||
l++
|
||||
|
||||
// read line until end
|
||||
// 1. read line until end
|
||||
line, err := reader.ReadString('\n')
|
||||
if err == io.EOF {
|
||||
if len(line) > 0 {
|
||||
|
@ -58,19 +76,34 @@ func NewReader(ctx *instruction.ExecutionContext, buildfile io.Reader) (*Reader,
|
|||
}
|
||||
line = strings.Trim(line, " \t\r\n")
|
||||
|
||||
// ignore newline & comments
|
||||
if len(line) < 1 || strings.ContainsAny(line[0:1], "[#;") {
|
||||
// 2. ignore newline & comments
|
||||
if len(line) < 1 || strings.ContainsAny(line[0:1], "#;") {
|
||||
continue
|
||||
}
|
||||
|
||||
// turn into instruction
|
||||
// 3. section
|
||||
if match := reSection.FindStringSubmatch(line); len(match) > 1 {
|
||||
// already in section
|
||||
sec := make([]instruction.T, 0)
|
||||
section = &sec
|
||||
r.Content[match[1]] = section
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
// 4. fail if no parent section
|
||||
if section == nil {
|
||||
return nil, ErrNoParent
|
||||
}
|
||||
|
||||
// 5. create instruction
|
||||
inst, err := instruction.Parse(line)
|
||||
if err != nil {
|
||||
return nil, LineError{l, err}
|
||||
}
|
||||
|
||||
// add to list
|
||||
r.Content = append(r.Content, inst)
|
||||
*section = append(*section, inst)
|
||||
|
||||
}
|
||||
|
||||
|
@ -91,26 +124,88 @@ func (r *Reader) Execute() error {
|
|||
// if err != nil {
|
||||
// return fmt.Errorf("cannot upgrade | %s", err)
|
||||
// }
|
||||
refresh := make(chan bool, 1)
|
||||
|
||||
// 3. exec each instruction
|
||||
for i, inst := range r.Content {
|
||||
clifmt.Align(fmt.Sprintf("(%d) %s", i, clifmt.Color(0, inst.Raw())))
|
||||
fmt.Printf("%s", clifmt.Color(33, "processing"))
|
||||
|
||||
start := time.Now()
|
||||
|
||||
_, err := inst.Exec(*r.Context)
|
||||
if err != nil {
|
||||
fmt.Printf("\r")
|
||||
clifmt.Align(fmt.Sprintf("(%d) %s", i, clifmt.Color(0, inst.Raw())))
|
||||
fmt.Printf("%s \n", clifmt.Color(31, err.Error()))
|
||||
continue
|
||||
} else {
|
||||
fmt.Printf("\r")
|
||||
clifmt.Align(fmt.Sprintf("(%d) %s", i, clifmt.Color(34, inst.Raw())))
|
||||
fmt.Printf("%ss \n", clifmt.Color(32, fmt.Sprintf("%.2f", time.Now().Sub(start).Seconds())))
|
||||
// 1. create status table
|
||||
table := make([]tableSection, 0)
|
||||
for secname, sec := range r.Content {
|
||||
tableSec := tableSection{
|
||||
name: secname,
|
||||
instructions: make([]execStatus, len(*sec), len(*sec)+1),
|
||||
}
|
||||
|
||||
// for each instruction
|
||||
for i, inst := range *sec {
|
||||
tableSec.instructions[i].name = inst.Raw()
|
||||
tableSec.instructions[i].start = time.Now()
|
||||
}
|
||||
|
||||
table = append(table, tableSec)
|
||||
go execSection(sec, *r.Context, refresh, &table[len(table)-1])
|
||||
|
||||
}
|
||||
|
||||
// 2. create status updater
|
||||
go status(refresh, table)
|
||||
time.Sleep(time.Second * 10)
|
||||
close(refresh)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func execSection(section *[]instruction.T, ctx instruction.ExecutionContext, refresher chan<- bool, tsec *tableSection) {
|
||||
|
||||
for i, inst := range *section {
|
||||
_, err := inst.Exec(ctx)
|
||||
tsec.instructions[i].stop = time.Now()
|
||||
tsec.instructions[i].stopped = true
|
||||
tsec.instructions[i].err = err
|
||||
refresher <- true
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func status(refresher chan bool, table []tableSection) {
|
||||
fmt.Printf("status\n")
|
||||
remain := false
|
||||
|
||||
for <-refresher {
|
||||
|
||||
// 1. clean screen
|
||||
fmt.Printf("\033[H\033[2J")
|
||||
|
||||
// 2. for each section
|
||||
for _, sec := range table {
|
||||
|
||||
fmt.Printf("\n[ %s ]\n", sec.name)
|
||||
|
||||
// 3. for each instruction
|
||||
for i, inst := range sec.instructions {
|
||||
|
||||
if !inst.stopped {
|
||||
clifmt.Align(fmt.Sprintf("(%d) %s", i, clifmt.Color(0, inst.name)))
|
||||
fmt.Printf("%s\n", clifmt.Color(33, "processing"))
|
||||
remain = true
|
||||
continue
|
||||
}
|
||||
|
||||
if inst.err != nil {
|
||||
clifmt.Align(fmt.Sprintf("(%d) %s", i, clifmt.Color(0, inst.name)))
|
||||
fmt.Printf("%s\n", clifmt.Color(31, inst.err.Error()))
|
||||
continue
|
||||
} else {
|
||||
clifmt.Align(fmt.Sprintf("(%d) %s", i, clifmt.Color(34, inst.name)))
|
||||
fmt.Printf("%ss\n", clifmt.Color(32, fmt.Sprintf("%.2f", inst.stop.Sub(inst.start).Seconds())))
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// 4. close refresher if no remaining task
|
||||
if !remain {
|
||||
break
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
7
main.go
7
main.go
|
@ -30,7 +30,12 @@ func main() {
|
|||
// 2. parse buildfile
|
||||
instructions, err := buildfile.NewReader(ctx, bfreader)
|
||||
if err != nil {
|
||||
fmt.Printf("%s%s\n", bf, err)
|
||||
if _, ok := err.(buildfile.LineError); ok {
|
||||
fmt.Printf("line error\n")
|
||||
fmt.Printf("%s%s\n", bf, err.Error())
|
||||
} else {
|
||||
fmt.Printf("%s\n", clifmt.Warn(err.Error()))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue