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 	"bufio"
     19 	"path/filepath"
     20 	"runtime"
     21 	"strings"
     22 )
     23 
     24 // Checks for files in the out directory that have a rule that depends on them but no rule to
     25 // create them. This catches a common set of build failures where a rule to generate a file is
     26 // deleted (either by deleting a module in an Android.mk file, or by modifying the build system
     27 // incorrectly).  These failures are often not caught by a local incremental build because the
     28 // previously built files are still present in the output directory.
     29 func testForDanglingRules(ctx Context, config Config) {
     30 	// Many modules are disabled on mac.  Checking for dangling rules would cause lots of build
     31 	// breakages, and presubmit wouldn't catch them, so just disable the check.
     32 	if runtime.GOOS != "linux" {
     33 		return
     34 	}
     35 
     36 	ctx.BeginTrace("test for dangling rules")
     37 	defer ctx.EndTrace()
     38 
     39 	// Get a list of leaf nodes in the dependency graph from ninja
     40 	executable := config.PrebuiltBuildTool("ninja")
     41 
     42 	args := []string{}
     43 	args = append(args, config.NinjaArgs()...)
     44 	args = append(args, "-f", config.CombinedNinjaFile())
     45 	args = append(args, "-t", "targets", "rule")
     46 
     47 	cmd := Command(ctx, config, "ninja", executable, args...)
     48 	stdout, err := cmd.StdoutPipe()
     49 	if err != nil {
     50 		ctx.Fatal(err)
     51 	}
     52 
     53 	cmd.StartOrFatal()
     54 
     55 	outDir := config.OutDir()
     56 	bootstrapDir := filepath.Join(outDir, "soong", ".bootstrap")
     57 	miniBootstrapDir := filepath.Join(outDir, "soong", ".minibootstrap")
     58 
     59 	var danglingRules []string
     60 
     61 	scanner := bufio.NewScanner(stdout)
     62 	for scanner.Scan() {
     63 		line := scanner.Text()
     64 		if !strings.HasPrefix(line, outDir) {
     65 			// Leaf node is not in the out directory.
     66 			continue
     67 		}
     68 		if strings.HasPrefix(line, bootstrapDir) || strings.HasPrefix(line, miniBootstrapDir) {
     69 			// Leaf node is in one of Soong's bootstrap directories, which do not have
     70 			// full build rules in the primary build.ninja file.
     71 			continue
     72 		}
     73 		danglingRules = append(danglingRules, line)
     74 	}
     75 
     76 	cmd.WaitOrFatal()
     77 
     78 	if len(danglingRules) > 0 {
     79 		ctx.Println("Dependencies in out found with no rule to create them:")
     80 		for _, dep := range danglingRules {
     81 			ctx.Println(dep)
     82 		}
     83 		ctx.Fatal("")
     84 	}
     85 }
     86