Home | History | Annotate | Download | only in build
      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