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 15 package build 16 17 import ( 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. 26 func 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 38 var combinedBuildNinjaTemplate = template.Must(template.New("combined").Parse(` 39 builddir = {{.OutDir}} 40 {{if .HasKatiSuffix}}include {{.KatiNinjaFile}} 41 {{end -}} 42 include {{.SoongNinjaFile}} 43 build {{.CombinedNinjaFile}}: phony {{.SoongNinjaFile}} 44 `)) 45 46 func createCombinedBuildNinjaFile(ctx Context, config Config) { 47 // If we're in SkipMake mode, skip creating this file if it already exists 48 if config.SkipMake() { 49 if _, err := os.Stat(config.CombinedNinjaFile()); err == nil || !os.IsNotExist(err) { 50 return 51 } 52 } 53 54 file, err := os.Create(config.CombinedNinjaFile()) 55 if err != nil { 56 ctx.Fatalln("Failed to create combined ninja file:", err) 57 } 58 defer file.Close() 59 60 if err := combinedBuildNinjaTemplate.Execute(file, config); err != nil { 61 ctx.Fatalln("Failed to write combined ninja file:", err) 62 } 63 } 64 65 const ( 66 BuildNone = iota 67 BuildProductConfig = 1 << iota 68 BuildSoong = 1 << iota 69 BuildKati = 1 << iota 70 BuildNinja = 1 << iota 71 RunBuildTests = 1 << iota 72 BuildAll = BuildProductConfig | BuildSoong | BuildKati | BuildNinja 73 ) 74 75 func checkCaseSensitivity(ctx Context, config Config) { 76 outDir := config.OutDir() 77 lowerCase := filepath.Join(outDir, "casecheck.txt") 78 upperCase := filepath.Join(outDir, "CaseCheck.txt") 79 lowerData := "a" 80 upperData := "B" 81 82 err := ioutil.WriteFile(lowerCase, []byte(lowerData), 0777) 83 if err != nil { 84 ctx.Fatalln("Failed to check case sensitivity:", err) 85 } 86 87 err = ioutil.WriteFile(upperCase, []byte(upperData), 0777) 88 if err != nil { 89 ctx.Fatalln("Failed to check case sensitivity:", err) 90 } 91 92 res, err := ioutil.ReadFile(lowerCase) 93 if err != nil { 94 ctx.Fatalln("Failed to check case sensitivity:", err) 95 } 96 97 if string(res) != lowerData { 98 ctx.Println("************************************************************") 99 ctx.Println("You are building on a case-insensitive filesystem.") 100 ctx.Println("Please move your source tree to a case-sensitive filesystem.") 101 ctx.Println("************************************************************") 102 ctx.Fatalln("Case-insensitive filesystems not supported") 103 } 104 } 105 106 func help(ctx Context, config Config, what int) { 107 cmd := Command(ctx, config, "help.sh", "build/make/help.sh") 108 cmd.Sandbox = dumpvarsSandbox 109 cmd.Stdout = ctx.Stdout() 110 cmd.Stderr = ctx.Stderr() 111 cmd.RunOrFatal() 112 } 113 114 // Build the tree. The 'what' argument can be used to chose which components of 115 // the build to run. 116 func Build(ctx Context, config Config, what int) { 117 ctx.Verboseln("Starting build with args:", config.Arguments()) 118 ctx.Verboseln("Environment:", config.Environment().Environ()) 119 120 if config.SkipMake() { 121 ctx.Verboseln("Skipping Make/Kati as requested") 122 what = what & (BuildSoong | BuildNinja) 123 } 124 125 if inList("help", config.Arguments()) { 126 help(ctx, config, what) 127 return 128 } else if inList("clean", config.Arguments()) || inList("clobber", config.Arguments()) { 129 clean(ctx, config, what) 130 return 131 } 132 133 // Make sure that no other Soong process is running with the same output directory 134 buildLock := BecomeSingletonOrFail(ctx, config) 135 defer buildLock.Unlock() 136 137 SetupOutDir(ctx, config) 138 139 checkCaseSensitivity(ctx, config) 140 141 ensureEmptyDirectoriesExist(ctx, config.TempDir()) 142 143 if what&BuildProductConfig != 0 { 144 // Run make for product config 145 runMakeProductConfig(ctx, config) 146 } 147 148 if inList("installclean", config.Arguments()) { 149 installClean(ctx, config, what) 150 ctx.Println("Deleted images and staging directories.") 151 return 152 } else if inList("dataclean", config.Arguments()) { 153 dataClean(ctx, config, what) 154 ctx.Println("Deleted data files.") 155 return 156 } 157 158 if what&BuildSoong != 0 { 159 // Run Soong 160 runSoong(ctx, config) 161 } 162 163 if what&BuildKati != 0 { 164 // Run ckati 165 runKati(ctx, config) 166 167 ioutil.WriteFile(config.LastKatiSuffixFile(), []byte(config.KatiSuffix()), 0777) 168 } else { 169 // Load last Kati Suffix if it exists 170 if katiSuffix, err := ioutil.ReadFile(config.LastKatiSuffixFile()); err == nil { 171 ctx.Verboseln("Loaded previous kati config:", string(katiSuffix)) 172 config.SetKatiSuffix(string(katiSuffix)) 173 } 174 } 175 176 // Write combined ninja file 177 createCombinedBuildNinjaFile(ctx, config) 178 179 if what&RunBuildTests != 0 { 180 testForDanglingRules(ctx, config) 181 } 182 183 if what&BuildNinja != 0 { 184 if !config.SkipMake() { 185 installCleanIfNecessary(ctx, config) 186 } 187 188 // Run ninja 189 runNinja(ctx, config) 190 } 191 } 192