• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// 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 build
16
17import (
18	"io/ioutil"
19	"os"
20	"path/filepath"
21	"text/template"
22)
23
24// Ensures the out directory exists, and has the proper files to prevent kati
25// from recursing into it.
26func SetupOutDir(ctx Context, config Config) {
27	ensureEmptyFileExists(ctx, filepath.Join(config.OutDir(), "Android.mk"))
28	ensureEmptyFileExists(ctx, filepath.Join(config.OutDir(), "CleanSpec.mk"))
29	if !config.SkipMake() {
30		ensureEmptyFileExists(ctx, filepath.Join(config.SoongOutDir(), ".soong.in_make"))
31	}
32	// The ninja_build file is used by our buildbots to understand that the output
33	// can be parsed as ninja output.
34	ensureEmptyFileExists(ctx, filepath.Join(config.OutDir(), "ninja_build"))
35	ensureEmptyFileExists(ctx, filepath.Join(config.OutDir(), ".out-dir"))
36}
37
38var combinedBuildNinjaTemplate = template.Must(template.New("combined").Parse(`
39builddir = {{.OutDir}}
40pool local_pool
41 depth = {{.Parallel}}
42build _kati_always_build_: phony
43{{if .HasKatiSuffix}}subninja {{.KatiBuildNinjaFile}}
44subninja {{.KatiPackageNinjaFile}}
45{{end -}}
46subninja {{.SoongNinjaFile}}
47`))
48
49func createCombinedBuildNinjaFile(ctx Context, config Config) {
50	// If we're in SkipMake mode, skip creating this file if it already exists
51	if config.SkipMake() {
52		if _, err := os.Stat(config.CombinedNinjaFile()); err == nil || !os.IsNotExist(err) {
53			return
54		}
55	}
56
57	file, err := os.Create(config.CombinedNinjaFile())
58	if err != nil {
59		ctx.Fatalln("Failed to create combined ninja file:", err)
60	}
61	defer file.Close()
62
63	if err := combinedBuildNinjaTemplate.Execute(file, config); err != nil {
64		ctx.Fatalln("Failed to write combined ninja file:", err)
65	}
66}
67
68const (
69	BuildNone          = iota
70	BuildProductConfig = 1 << iota
71	BuildSoong         = 1 << iota
72	BuildKati          = 1 << iota
73	BuildNinja         = 1 << iota
74	RunBuildTests      = 1 << iota
75	BuildAll           = BuildProductConfig | BuildSoong | BuildKati | BuildNinja
76)
77
78func checkProblematicFiles(ctx Context) {
79	files := []string{"Android.mk", "CleanSpec.mk"}
80	for _, file := range files {
81		if _, err := os.Stat(file); !os.IsNotExist(err) {
82			absolute := absPath(ctx, file)
83			ctx.Printf("Found %s in tree root. This file needs to be removed to build.\n", file)
84			ctx.Fatalf("    rm %s\n", absolute)
85		}
86	}
87}
88
89func checkCaseSensitivity(ctx Context, config Config) {
90	outDir := config.OutDir()
91	lowerCase := filepath.Join(outDir, "casecheck.txt")
92	upperCase := filepath.Join(outDir, "CaseCheck.txt")
93	lowerData := "a"
94	upperData := "B"
95
96	err := ioutil.WriteFile(lowerCase, []byte(lowerData), 0777)
97	if err != nil {
98		ctx.Fatalln("Failed to check case sensitivity:", err)
99	}
100
101	err = ioutil.WriteFile(upperCase, []byte(upperData), 0777)
102	if err != nil {
103		ctx.Fatalln("Failed to check case sensitivity:", err)
104	}
105
106	res, err := ioutil.ReadFile(lowerCase)
107	if err != nil {
108		ctx.Fatalln("Failed to check case sensitivity:", err)
109	}
110
111	if string(res) != lowerData {
112		ctx.Println("************************************************************")
113		ctx.Println("You are building on a case-insensitive filesystem.")
114		ctx.Println("Please move your source tree to a case-sensitive filesystem.")
115		ctx.Println("************************************************************")
116		ctx.Fatalln("Case-insensitive filesystems not supported")
117	}
118}
119
120func help(ctx Context, config Config, what int) {
121	cmd := Command(ctx, config, "help.sh", "build/make/help.sh")
122	cmd.Sandbox = dumpvarsSandbox
123	cmd.RunAndPrintOrFatal()
124}
125
126// Build the tree. The 'what' argument can be used to chose which components of
127// the build to run.
128func Build(ctx Context, config Config, what int) {
129	ctx.Verboseln("Starting build with args:", config.Arguments())
130	ctx.Verboseln("Environment:", config.Environment().Environ())
131
132	if config.SkipMake() {
133		ctx.Verboseln("Skipping Make/Kati as requested")
134		what = what & (BuildSoong | BuildNinja)
135	}
136
137	if inList("help", config.Arguments()) {
138		help(ctx, config, what)
139		return
140	} else if inList("clean", config.Arguments()) || inList("clobber", config.Arguments()) {
141		clean(ctx, config, what)
142		return
143	}
144
145	// Make sure that no other Soong process is running with the same output directory
146	buildLock := BecomeSingletonOrFail(ctx, config)
147	defer buildLock.Unlock()
148
149	checkProblematicFiles(ctx)
150
151	SetupOutDir(ctx, config)
152
153	checkCaseSensitivity(ctx, config)
154
155	ensureEmptyDirectoriesExist(ctx, config.TempDir())
156
157	SetupPath(ctx, config)
158
159	if config.StartGoma() {
160		// Ensure start Goma compiler_proxy
161		startGoma(ctx, config)
162	}
163
164	if what&BuildProductConfig != 0 {
165		// Run make for product config
166		runMakeProductConfig(ctx, config)
167	}
168
169	if inList("installclean", config.Arguments()) {
170		installClean(ctx, config, what)
171		ctx.Println("Deleted images and staging directories.")
172		return
173	} else if inList("dataclean", config.Arguments()) {
174		dataClean(ctx, config, what)
175		ctx.Println("Deleted data files.")
176		return
177	}
178
179	if what&BuildSoong != 0 {
180		// Run Soong
181		runSoong(ctx, config)
182	}
183
184	if what&BuildKati != 0 {
185		// Run ckati
186		genKatiSuffix(ctx, config)
187		runKatiCleanSpec(ctx, config)
188		runKatiBuild(ctx, config)
189		runKatiPackage(ctx, config)
190
191		ioutil.WriteFile(config.LastKatiSuffixFile(), []byte(config.KatiSuffix()), 0777)
192	} else {
193		// Load last Kati Suffix if it exists
194		if katiSuffix, err := ioutil.ReadFile(config.LastKatiSuffixFile()); err == nil {
195			ctx.Verboseln("Loaded previous kati config:", string(katiSuffix))
196			config.SetKatiSuffix(string(katiSuffix))
197		}
198	}
199
200	// Write combined ninja file
201	createCombinedBuildNinjaFile(ctx, config)
202
203	if what&RunBuildTests != 0 {
204		testForDanglingRules(ctx, config)
205	}
206
207	if what&BuildNinja != 0 {
208		if !config.SkipMake() {
209			installCleanIfNecessary(ctx, config)
210		}
211
212		// Run ninja
213		runNinja(ctx, config)
214	}
215}
216