FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

Restore dependency-depth build scheduling by jakebailey · Pull Request #64615 · microsoft/TypeScript · GitHub

Repository navigation

Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension .go  (2) All 1 file type selected
Viewed files
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Unified
Split
Hide whitespace
Diff view
Unified
Split
Hide whitespace
121 changes: 121 additions & 0 deletions tsc/internal/execute/build/graph_test.go
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,19 @@ package build_test

import (
"fmt"
"io"
"slices"
"strings"
"testing"
"time"

"github.com/microsoft/TypeScript/tsc/internal/core"
"github.com/microsoft/TypeScript/tsc/internal/execute/build"
"github.com/microsoft/TypeScript/tsc/internal/execute/tsc"
"github.com/microsoft/TypeScript/tsc/internal/execute/tsctests"
"github.com/microsoft/TypeScript/tsc/internal/tsoptions"
"github.com/microsoft/TypeScript/tsc/internal/tspath"
"github.com/microsoft/TypeScript/tsc/internal/vfs"
"gotest.tools/v3/assert"
)

Expand All @@ -32,6 +36,123 @@ func TestBuildOrderGenerator(t *testing.T) {
}
}

func TestBuildScheduling(t *testing.T) {
t.Parallel()
for _, operation := range []struct {
name string
project string
refsOnly bool
order []string
}{
{"full build", "", false, []string{"Leaf", "Middle", "Independent", "Root", "Other"}},
{"selected project", "Root", false, []string{"Leaf", "Middle", "Independent", "Root"}},
{"selected references", "Root", true, []string{"Leaf", "Middle", "Independent"}},
} {
t.Run(operation.name, func(t *testing.T) {
t.Parallel()
sys := newSchedulingTestSystem()
independentStarted := make(chan struct{})
sys.fs.onRead = func(path tspath.RootedFilePath) {
if path.BaseName() != "index.ts" {
return
}
switch path.Directory().BaseName() {
case "Leaf":
// Independent must start while Leaf is still occupying a builder.
select {
case <-independentStarted:
return
case <-time.After(30 * time.Second):
t.Error("Independent was queued behind a builder waiting on Leaf")
}
case "Independent":
close(independentStarted)
}
}
command := tsoptions.ParseBuildCommandLine([]string{"--build", "--verbose", "--builders", "2", "Root", "Other"}, sys.FS(), sys.GetCurrentDirectory())
orchestrator := build.NewOrchestrator(build.Options{Sys: sys, Command: command})
var result *build.OrchestratorResult
if operation.refsOnly {
result = orchestrator.BuildReferences(t.Context(), operation.project)
} else {
result = orchestrator.Build(t.Context(), operation.project)
}
assert.Equal(t, result.Result.Status, tsc.ExitStatusSuccess)
assert.Equal(t, result.Statistics.Projects, len(operation.order))
assert.Equal(t, result.Statistics.ProjectsBuilt, len(operation.order))
reported := []string{}
for line := range strings.SplitSeq(sys.output.String(), "\n") {
if strings.Contains(line, "Building project") {
for _, project := range operation.order {
if strings.Contains(line, "'"+project+"/tsconfig.json'") {
reported = append(reported, project)
}
}
}
}
assert.DeepEqual(t, reported, operation.order)
for _, project := range []string{"Leaf", "Middle", "Independent", "Root", "Other"} {
assert.Equal(t, sys.FS().FileExists(sys.GetCurrentDirectory().ResolveFile(project+"/dist/index.js")), slices.Contains(operation.order, project))
}
})
}
}

type schedulingTestFS struct {
vfs.FS
onRead func(tspath.RootedFilePath)
}

func (f *schedulingTestFS) ReadFile(path tspath.RootedFilePath) (string, bool) {
f.onRead(path)
return f.FS.ReadFile(path)
}

type schedulingTestSystem struct {
*tsctests.TestSys
fs *schedulingTestFS
output strings.Builder
}

func (s *schedulingTestSystem) FS() vfs.FS { return s.fs }
func (s *schedulingTestSystem) Writer() io.Writer { return &s.output }

func newSchedulingTestSystem() *schedulingTestSystem {
files := tsctests.FileMap{
"/project/lib.d.ts": `
interface Array<T> {}
interface Boolean {}
interface CallableFunction {}
interface Function {}
interface IArguments {}
interface NewableFunction {}
interface Number {}
interface Object {}
interface RegExp {}
interface String {}
`,
}
deps := map[string][]string{
"Root": {"Middle", "Independent"},
"Middle": {"Leaf"},
"Leaf": {},
"Independent": {},
"Other": {},
}
for project, references := range deps {
files["/project/"+project+"/index.ts"] = "export const value = 1;"
files["/project/"+project+"/tsconfig.json"] = fmt.Sprintf(`{
"compilerOptions": { "composite": true, "noLib": true, "outDir": "dist" },
"files": ["../lib.d.ts", "index.ts"],
"references": [%s]
}`, strings.Join(core.Map(references, func(ref string) string {
return fmt.Sprintf(`{ "path": "../%s" }`, ref)
}), ","))
}
sys := tsctests.NewTscSystem(files, tspath.CaseSensitive, "/project")
return &schedulingTestSystem{TestSys: sys, fs: &schedulingTestFS{FS: sys.FS()}}
}

type buildOrderTestCase struct {
name string
projects []string
Expand Down
18 changes: 11 additions & 7 deletions tsc/internal/execute/build/orchestrator.go
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -124,14 +124,14 @@ func (o *Orchestrator) ScheduleOrder() []string {
// picked up may not be done yet, so a builder can take a dependent of a slow project and
// wait on that project while a later project's upstream has already finished. The stable
// sort preserves the original order within a depth, and reporting still follows Order().
func (o *Orchestrator) computeScheduleOrder() []*BuildTask {
func (o *Orchestrator) computeScheduleOrder(order []*BuildTask) []*BuildTask {
type scheduleEntry struct {
task *BuildTask
depth int
}
entries := make([]scheduleEntry, len(o.order))
depths := make(map[*BuildTask]int, len(o.order))
for i, task := range o.order {
entries := make([]scheduleEntry, len(order))
depths := make(map[*BuildTask]int, len(order))
for i, task := range order {
depth := 0
for _, upstream := range task.upStream {
depth = max(depth, depths[upstream.task]+1)
Expand Down Expand Up @@ -271,7 +271,7 @@ func (o *Orchestrator) GenerateGraph(oldTasks *collections.SyncMap[tspath.PathKe
for _, project := range projects {
o.setupBuildTask(project, nil, false, &completed, &analyzing, circularityStack)
}
o.scheduleOrder = o.computeScheduleOrder()
o.scheduleOrder = o.computeScheduleOrder(o.order)
if oldTasks != nil {
oldTasks.Range(func(path tspath.PathKey, oldTask *BuildTask) bool {
if task, ok := o.tasks.Load(path); ok && task == oldTask {
Expand Down Expand Up @@ -884,7 +884,11 @@ func (o *Orchestrator) buildOrCleanOrder(order []*BuildTask) *OrchestratorResult
// prevReporter = task
// }
buildResult.Statistics.Projects = len(order)
// Builders pick up projects in scheduleOrder; results are reported in Order(), waiting for each project to finish
scheduleOrder := o.scheduleOrder
if len(order) != len(o.order) {
scheduleOrder = o.computeScheduleOrder(order)
}
// Builders pick up selected projects in dependency-depth order; reporting preserves the selected build order.
reported := make(chan struct{})
go func() {
defer close(reported)
Expand All @@ -893,7 +897,7 @@ func (o *Orchestrator) buildOrCleanOrder(order []*BuildTask) *OrchestratorResult
task.report(o, task.path, buildResult)
}
}()
o.rangeTasks(order, func(path tspath.PathKey, task *BuildTask) {
o.rangeTasks(scheduleOrder, func(path tspath.PathKey, task *BuildTask) {
o.buildOrCleanProject(task, path)
})
<-reported
Expand Down
Loading

Back | FazBrowse Home | New Git URL