blob: 21d8383475538d472921ad0684e55ac3b8d645f6 [file] [log] [blame]
Dan Willemsenc2af0be2017-01-20 14:10:01 -08001// Copyright 2017 Google Inc. All rights reserved.
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 main
16
17import (
18 "bytes"
19 "context"
20 "flag"
21 "fmt"
22 "io/ioutil"
23 "os"
24 "path/filepath"
25 "runtime"
26 "strings"
27 "sync"
28
29 "android/soong/ui/build"
30 "android/soong/ui/logger"
Dan Willemsend9f6fa22016-08-21 15:17:17 -070031 "android/soong/ui/tracer"
Dan Willemsenc2af0be2017-01-20 14:10:01 -080032)
33
34// We default to number of cpus / 4, which seems to be the sweet spot for my
35// system. I suspect this is mostly due to memory or disk bandwidth though, and
36// may depend on the size ofthe source tree, so this probably isn't a great
37// default.
38func detectNumJobs() int {
39 if runtime.NumCPU() < 4 {
40 return 1
41 }
42 return runtime.NumCPU() / 4
43}
44
45var numJobs = flag.Int("j", detectNumJobs(), "number of parallel kati jobs")
46
47var keep = flag.Bool("keep", false, "keep successful output files")
48
49var outDir = flag.String("out", "", "path to store output directories (defaults to tmpdir under $OUT when empty)")
50
51var onlyConfig = flag.Bool("only-config", false, "Only run product config (not Soong or Kati)")
52var onlySoong = flag.Bool("only-soong", false, "Only run product config and Soong (not Kati)")
53
54type Product struct {
55 ctx build.Context
56 config build.Config
57}
58
59func main() {
60 log := logger.New(os.Stderr)
61 defer log.Cleanup()
62
63 flag.Parse()
64
65 ctx, cancel := context.WithCancel(context.Background())
66 defer cancel()
67
Dan Willemsend9f6fa22016-08-21 15:17:17 -070068 trace := tracer.New(log)
69 defer trace.Close()
Dan Willemsenc2af0be2017-01-20 14:10:01 -080070
Dan Willemsend9f6fa22016-08-21 15:17:17 -070071 build.SetupSignals(log, cancel, func() {
72 trace.Close()
73 log.Cleanup()
74 })
75
76 buildCtx := build.Context{&build.ContextImpl{
Dan Willemsenc2af0be2017-01-20 14:10:01 -080077 Context: ctx,
78 Logger: log,
Dan Willemsend9f6fa22016-08-21 15:17:17 -070079 Tracer: trace,
Dan Willemsenc2af0be2017-01-20 14:10:01 -080080 StdioInterface: build.StdioImpl{},
Dan Willemsend9f6fa22016-08-21 15:17:17 -070081 }}
Dan Willemsenc2af0be2017-01-20 14:10:01 -080082
83 failed := false
84
85 config := build.NewConfig(buildCtx)
86 if *outDir == "" {
87 var err error
88 *outDir, err = ioutil.TempDir(config.OutDir(), "multiproduct")
89 if err != nil {
90 log.Fatalf("Failed to create tempdir: %v", err)
91 }
92
93 if !*keep {
94 defer func() {
95 if !failed {
96 os.RemoveAll(*outDir)
97 }
98 }()
99 }
100 }
101 config.Environment().Set("OUT_DIR", *outDir)
102 log.Println("Output directory:", *outDir)
103
104 build.SetupOutDir(buildCtx, config)
Dan Willemsen8a073a82017-02-04 17:30:44 -0800105 log.SetOutput(filepath.Join(config.OutDir(), "soong.log"))
Dan Willemsend9f6fa22016-08-21 15:17:17 -0700106 trace.SetOutput(filepath.Join(config.OutDir(), "build.trace"))
Dan Willemsenc2af0be2017-01-20 14:10:01 -0800107
108 vars, err := build.DumpMakeVars(buildCtx, config, nil, nil, []string{"all_named_products"})
109 if err != nil {
110 log.Fatal(err)
111 }
112 products := strings.Fields(vars["all_named_products"])
113 log.Verbose("Got product list:", products)
114
115 var wg sync.WaitGroup
116 errs := make(chan error, len(products))
117 productConfigs := make(chan Product, len(products))
118
119 // Run the product config for every product in parallel
120 for _, product := range products {
121 wg.Add(1)
122 go func(product string) {
123 defer wg.Done()
124 defer logger.Recover(func(err error) {
125 errs <- fmt.Errorf("Error building %s: %v", product, err)
126 })
127
128 productOutDir := filepath.Join(config.OutDir(), product)
129
130 if err := os.MkdirAll(productOutDir, 0777); err != nil {
131 log.Fatalf("Error creating out directory: %v", err)
132 }
133
134 f, err := os.Create(filepath.Join(productOutDir, "std.log"))
135 if err != nil {
136 log.Fatalf("Error creating std.log: %v", err)
137 }
138
139 productLog := logger.New(&bytes.Buffer{})
Dan Willemsen8a073a82017-02-04 17:30:44 -0800140 productLog.SetOutput(filepath.Join(productOutDir, "soong.log"))
Dan Willemsenc2af0be2017-01-20 14:10:01 -0800141
Dan Willemsend9f6fa22016-08-21 15:17:17 -0700142 productCtx := build.Context{&build.ContextImpl{
Dan Willemsenc2af0be2017-01-20 14:10:01 -0800143 Context: ctx,
144 Logger: productLog,
Dan Willemsend9f6fa22016-08-21 15:17:17 -0700145 Tracer: trace,
Dan Willemsenc2af0be2017-01-20 14:10:01 -0800146 StdioInterface: build.NewCustomStdio(nil, f, f),
Dan Willemsend9f6fa22016-08-21 15:17:17 -0700147 Thread: trace.NewThread(product),
148 }}
Dan Willemsenc2af0be2017-01-20 14:10:01 -0800149
150 productConfig := build.NewConfig(productCtx)
151 productConfig.Environment().Set("OUT_DIR", productOutDir)
152 productConfig.Lunch(productCtx, product, "eng")
153
154 build.Build(productCtx, productConfig, build.BuildProductConfig)
155 productConfigs <- Product{productCtx, productConfig}
156 }(product)
157 }
158 go func() {
159 defer close(productConfigs)
160 wg.Wait()
161 }()
162
163 var wg2 sync.WaitGroup
164 // Then run up to numJobs worth of Soong and Kati
165 for i := 0; i < *numJobs; i++ {
166 wg2.Add(1)
167 go func() {
168 defer wg2.Done()
169 for product := range productConfigs {
170 func() {
171 defer logger.Recover(func(err error) {
172 errs <- fmt.Errorf("Error building %s: %v", product.config.TargetProduct(), err)
173 })
174
175 buildWhat := 0
176 if !*onlyConfig {
177 buildWhat |= build.BuildSoong
178 if !*onlySoong {
179 buildWhat |= build.BuildKati
180 }
181 }
182 build.Build(product.ctx, product.config, buildWhat)
183 if !*keep {
184 // TODO: kati aborts from opendir while setting up the find emulator
185 //os.RemoveAll(product.config.OutDir())
186 }
187 log.Println("Finished running for", product.config.TargetProduct())
188 }()
189 }
190 }()
191 }
192 go func() {
193 wg2.Wait()
194 close(errs)
195 }()
196
197 for err := range errs {
198 failed = true
199 log.Print(err)
200 }
201
202 if failed {
203 log.Fatalln("Failed")
204 }
205}