blob: 3aa5a8767edefef7424c8273f9fe2efbebdac7cb [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"
Dan Willemsenc2af0be2017-01-20 14:10:01 -080022 "os"
23 "path/filepath"
24 "runtime"
25 "strings"
26 "sync"
Steven Moreland552432e2017-03-29 19:26:09 -070027 "time"
Dan Willemsenc2af0be2017-01-20 14:10:01 -080028
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 == "" {
Steven Moreland552432e2017-03-29 19:26:09 -070087 name := "multiproduct-" + time.Now().Format("20060102150405")
88
89 *outDir = filepath.Join(config.OutDir(), name)
90
91 if err := os.MkdirAll(*outDir, 0777); err != nil {
Dan Willemsenc2af0be2017-01-20 14:10:01 -080092 log.Fatalf("Failed to create tempdir: %v", err)
93 }
94
95 if !*keep {
96 defer func() {
97 if !failed {
98 os.RemoveAll(*outDir)
99 }
100 }()
101 }
102 }
103 config.Environment().Set("OUT_DIR", *outDir)
104 log.Println("Output directory:", *outDir)
105
106 build.SetupOutDir(buildCtx, config)
Dan Willemsen8a073a82017-02-04 17:30:44 -0800107 log.SetOutput(filepath.Join(config.OutDir(), "soong.log"))
Dan Willemsend9f6fa22016-08-21 15:17:17 -0700108 trace.SetOutput(filepath.Join(config.OutDir(), "build.trace"))
Dan Willemsenc2af0be2017-01-20 14:10:01 -0800109
110 vars, err := build.DumpMakeVars(buildCtx, config, nil, nil, []string{"all_named_products"})
111 if err != nil {
112 log.Fatal(err)
113 }
114 products := strings.Fields(vars["all_named_products"])
115 log.Verbose("Got product list:", products)
116
117 var wg sync.WaitGroup
118 errs := make(chan error, len(products))
119 productConfigs := make(chan Product, len(products))
120
121 // Run the product config for every product in parallel
122 for _, product := range products {
123 wg.Add(1)
124 go func(product string) {
125 defer wg.Done()
126 defer logger.Recover(func(err error) {
127 errs <- fmt.Errorf("Error building %s: %v", product, err)
128 })
129
130 productOutDir := filepath.Join(config.OutDir(), product)
131
132 if err := os.MkdirAll(productOutDir, 0777); err != nil {
133 log.Fatalf("Error creating out directory: %v", err)
134 }
135
136 f, err := os.Create(filepath.Join(productOutDir, "std.log"))
137 if err != nil {
138 log.Fatalf("Error creating std.log: %v", err)
139 }
140
141 productLog := logger.New(&bytes.Buffer{})
Dan Willemsen8a073a82017-02-04 17:30:44 -0800142 productLog.SetOutput(filepath.Join(productOutDir, "soong.log"))
Dan Willemsenc2af0be2017-01-20 14:10:01 -0800143
Dan Willemsend9f6fa22016-08-21 15:17:17 -0700144 productCtx := build.Context{&build.ContextImpl{
Dan Willemsenc2af0be2017-01-20 14:10:01 -0800145 Context: ctx,
146 Logger: productLog,
Dan Willemsend9f6fa22016-08-21 15:17:17 -0700147 Tracer: trace,
Dan Willemsenc2af0be2017-01-20 14:10:01 -0800148 StdioInterface: build.NewCustomStdio(nil, f, f),
Dan Willemsend9f6fa22016-08-21 15:17:17 -0700149 Thread: trace.NewThread(product),
150 }}
Dan Willemsenc2af0be2017-01-20 14:10:01 -0800151
152 productConfig := build.NewConfig(productCtx)
153 productConfig.Environment().Set("OUT_DIR", productOutDir)
154 productConfig.Lunch(productCtx, product, "eng")
155
156 build.Build(productCtx, productConfig, build.BuildProductConfig)
157 productConfigs <- Product{productCtx, productConfig}
158 }(product)
159 }
160 go func() {
161 defer close(productConfigs)
162 wg.Wait()
163 }()
164
165 var wg2 sync.WaitGroup
166 // Then run up to numJobs worth of Soong and Kati
167 for i := 0; i < *numJobs; i++ {
168 wg2.Add(1)
169 go func() {
170 defer wg2.Done()
171 for product := range productConfigs {
172 func() {
173 defer logger.Recover(func(err error) {
174 errs <- fmt.Errorf("Error building %s: %v", product.config.TargetProduct(), err)
175 })
176
177 buildWhat := 0
178 if !*onlyConfig {
179 buildWhat |= build.BuildSoong
180 if !*onlySoong {
181 buildWhat |= build.BuildKati
182 }
183 }
184 build.Build(product.ctx, product.config, buildWhat)
185 if !*keep {
Dan Willemsenc38d3662017-02-24 10:53:23 -0800186 os.RemoveAll(product.config.OutDir())
Dan Willemsenc2af0be2017-01-20 14:10:01 -0800187 }
188 log.Println("Finished running for", product.config.TargetProduct())
189 }()
190 }
191 }()
192 }
193 go func() {
194 wg2.Wait()
195 close(errs)
196 }()
197
198 for err := range errs {
199 failed = true
200 log.Print(err)
201 }
202
203 if failed {
204 log.Fatalln("Failed")
205 }
206}