1// Copyright 2022 The Android Open Source Project
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//      http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package report
16
17import (
18	"context"
19	"fmt"
20	"sync"
21
22	"tools/treble/build/report/app"
23)
24
25// Channel data structures, include explicit error field to reply to each input
26type buildTargetData struct {
27	input      *app.BuildInput
28	buildSteps int
29	error      bool
30}
31type buildSourceData struct {
32	source string
33	query  *app.BuildQuery
34	error  bool
35}
36type buildPathData struct {
37	filename string
38	path     *app.BuildPath
39	error    bool
40}
41
42//
43// create build target from  using repo data
44//
45func createBuildTarget(ctx context.Context, rtx *Context, buildTarget *buildTargetData) *app.BuildTarget {
46	out := &app.BuildTarget{Name: buildTarget.input.Target,
47		Steps:     buildTarget.buildSteps,
48		Projects:  make(map[string]*app.GitProject),
49		FileCount: len(buildTarget.input.Files),
50	}
51
52	for _, f := range buildTarget.input.Files {
53		proj, buildFile := lookupProjectFile(ctx, rtx, f)
54		if buildFile != nil {
55			if buildProj, exists := out.Projects[proj.Name]; exists {
56				buildProj.Files[buildFile.Filename] = buildFile
57			} else {
58				out.Projects[proj.Name] =
59					&app.GitProject{
60						RepoDir:   proj.GitProj.RepoDir,
61						WorkDir:   proj.GitProj.WorkDir,
62						GitDir:    proj.GitProj.GitDir,
63						Remote:    proj.GitProj.Remote,
64						RemoteUrl: proj.GitProj.RemoteUrl,
65						Revision:  proj.GitProj.Revision,
66						Files:     map[string]*app.GitTreeObj{buildFile.Filename: buildFile}}
67			}
68		}
69	}
70	return (out)
71}
72
73// Setup routines to resolve target names to app.BuildInput objects
74func targetResolvers(ctx context.Context, rtx *Context) (chan string, chan *buildTargetData) {
75	var wg sync.WaitGroup
76	inChan := make(chan string)
77	outChan := make(chan *buildTargetData)
78	for i := 0; i < rtx.BuildWorkerCount; i++ {
79		wg.Add(1)
80		go func() {
81			for targetName := range inChan {
82				var buildSteps int
83				cmds, err := rtx.Build.Command(ctx, targetName)
84				if err == nil {
85					buildSteps = len(cmds.Cmds)
86				}
87				input, err := rtx.Build.Input(ctx, targetName)
88				if input == nil {
89					fmt.Printf("Failed to get input %s (%s)\n", targetName, err)
90				} else {
91					outChan <- &buildTargetData{input: input, buildSteps: buildSteps, error: err != nil}
92				}
93			}
94			wg.Done()
95		}()
96	}
97	go func() {
98		wg.Wait()
99		close(outChan)
100	}()
101
102	return inChan, outChan
103}
104
105//
106// Setup routines to resolve build input targets to BuildTarget
107func resolveBuildInputs(ctx context.Context, rtx *Context, inChan chan *buildTargetData) chan *app.BuildTarget {
108	var wg sync.WaitGroup
109	outChan := make(chan *app.BuildTarget)
110	for i := 0; i < rtx.BuildWorkerCount; i++ {
111		wg.Add(1)
112		go func() {
113			for buildTarget := range inChan {
114				outChan <- createBuildTarget(ctx, rtx, buildTarget)
115			}
116			wg.Done()
117		}()
118	}
119	go func() {
120		wg.Wait()
121		close(outChan)
122	}()
123	return outChan
124}
125
126// Setup routines to resolve source file to query
127func queryResolvers(ctx context.Context, rtx *Context) (chan string, chan *buildSourceData) {
128	var wg sync.WaitGroup
129	inChan := make(chan string)
130	outChan := make(chan *buildSourceData)
131	for i := 0; i < rtx.BuildWorkerCount; i++ {
132		wg.Add(1)
133		go func() {
134			for srcName := range inChan {
135				query, err := rtx.Build.Query(ctx, srcName)
136				outChan <- &buildSourceData{source: srcName, query: query, error: err != nil}
137			}
138			wg.Done()
139		}()
140	}
141	go func() {
142		wg.Wait()
143		close(outChan)
144	}()
145
146	return inChan, outChan
147}
148
149// Setup routines to resolve paths
150func pathsResolvers(ctx context.Context, rtx *Context, target string, singlePath bool) (chan string, chan *buildPathData) {
151	var wg sync.WaitGroup
152	inChan := make(chan string)
153	outChan := make(chan *buildPathData)
154	for i := 0; i < rtx.BuildWorkerCount; i++ {
155		wg.Add(1)
156		go func() {
157			for dep := range inChan {
158				if singlePath {
159					path, err := rtx.Build.Path(ctx, target, dep)
160					outChan <- &buildPathData{filename: dep, path: path, error: err != nil}
161				} else {
162					paths, err := rtx.Build.Paths(ctx, target, dep)
163					if err != nil {
164						outChan <- &buildPathData{filename: dep, path: nil, error: true}
165					} else {
166						for _, path := range paths {
167
168							outChan <- &buildPathData{filename: dep, path: path, error: false}
169						}
170					}
171				}
172			}
173			wg.Done()
174		}()
175	}
176	go func() {
177		wg.Wait()
178		close(outChan)
179	}()
180
181	return inChan, outChan
182}
183