| Colin Cross | 3719349 | 2017-11-16 17:55:00 -0800 | [diff] [blame] | 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 ( | 
| Colin Cross | 8d411ff | 2023-12-07 10:31:24 -0800 | [diff] [blame] | 18 | "android/soong/ui/metrics" | 
|  | 19 | "android/soong/ui/status" | 
| Colin Cross | 3719349 | 2017-11-16 17:55:00 -0800 | [diff] [blame] | 20 | "bufio" | 
| Dan Willemsen | d2e231a | 2018-08-02 12:06:24 -0700 | [diff] [blame] | 21 | "fmt" | 
| Colin Cross | 3719349 | 2017-11-16 17:55:00 -0800 | [diff] [blame] | 22 | "path/filepath" | 
|  | 23 | "runtime" | 
| Colin Cross | 63b4e0f | 2018-06-26 23:48:52 -0700 | [diff] [blame] | 24 | "sort" | 
| Colin Cross | 3719349 | 2017-11-16 17:55:00 -0800 | [diff] [blame] | 25 | "strings" | 
|  | 26 | ) | 
|  | 27 |  | 
|  | 28 | // Checks for files in the out directory that have a rule that depends on them but no rule to | 
|  | 29 | // create them. This catches a common set of build failures where a rule to generate a file is | 
|  | 30 | // deleted (either by deleting a module in an Android.mk file, or by modifying the build system | 
|  | 31 | // incorrectly).  These failures are often not caught by a local incremental build because the | 
|  | 32 | // previously built files are still present in the output directory. | 
|  | 33 | func testForDanglingRules(ctx Context, config Config) { | 
|  | 34 | // Many modules are disabled on mac.  Checking for dangling rules would cause lots of build | 
|  | 35 | // breakages, and presubmit wouldn't catch them, so just disable the check. | 
|  | 36 | if runtime.GOOS != "linux" { | 
|  | 37 | return | 
|  | 38 | } | 
|  | 39 |  | 
| Nan Zhang | 17f2767 | 2018-12-12 16:01:49 -0800 | [diff] [blame] | 40 | ctx.BeginTrace(metrics.TestRun, "test for dangling rules") | 
| Colin Cross | 3719349 | 2017-11-16 17:55:00 -0800 | [diff] [blame] | 41 | defer ctx.EndTrace() | 
|  | 42 |  | 
| Dan Willemsen | d2e231a | 2018-08-02 12:06:24 -0700 | [diff] [blame] | 43 | ts := ctx.Status.StartTool() | 
|  | 44 | action := &status.Action{ | 
|  | 45 | Description: "Test for dangling rules", | 
|  | 46 | } | 
|  | 47 | ts.StartAction(action) | 
|  | 48 |  | 
| Colin Cross | 3719349 | 2017-11-16 17:55:00 -0800 | [diff] [blame] | 49 | // Get a list of leaf nodes in the dependency graph from ninja | 
|  | 50 | executable := config.PrebuiltBuildTool("ninja") | 
|  | 51 |  | 
| Jaewoong Jung | 18aefc1 | 2020-12-21 09:11:10 -0800 | [diff] [blame] | 52 | commonArgs := []string{} | 
| Jaewoong Jung | 18aefc1 | 2020-12-21 09:11:10 -0800 | [diff] [blame] | 53 | commonArgs = append(commonArgs, "-f", config.CombinedNinjaFile()) | 
|  | 54 | args := append(commonArgs, "-t", "targets", "rule") | 
| Colin Cross | 3719349 | 2017-11-16 17:55:00 -0800 | [diff] [blame] | 55 |  | 
|  | 56 | cmd := Command(ctx, config, "ninja", executable, args...) | 
|  | 57 | stdout, err := cmd.StdoutPipe() | 
|  | 58 | if err != nil { | 
|  | 59 | ctx.Fatal(err) | 
|  | 60 | } | 
|  | 61 |  | 
|  | 62 | cmd.StartOrFatal() | 
|  | 63 |  | 
|  | 64 | outDir := config.OutDir() | 
| Chris Parsons | 8f232a2 | 2020-06-23 17:37:05 -0400 | [diff] [blame] | 65 | modulePathsDir := filepath.Join(outDir, ".module_paths") | 
|  | 66 | variablesFilePath := filepath.Join(outDir, "soong", "soong.variables") | 
| Rupert Shuttleworth | 0bc9a9a | 2020-12-08 13:28:38 +0000 | [diff] [blame] | 67 |  | 
| Jingwen Chen | ebb0b57 | 2020-11-02 00:24:57 -0500 | [diff] [blame] | 68 | // dexpreopt.config is an input to the soong_docs action, which runs the | 
|  | 69 | // soong_build primary builder. However, this file is created from $(shell) | 
|  | 70 | // invocation at Kati parse time, so it's not an explicit output of any | 
|  | 71 | // Ninja action, but it is present during the build itself and can be | 
|  | 72 | // treated as an source file. | 
|  | 73 | dexpreoptConfigFilePath := filepath.Join(outDir, "soong", "dexpreopt.config") | 
| Colin Cross | 3719349 | 2017-11-16 17:55:00 -0800 | [diff] [blame] | 74 |  | 
| Cole Faust | e842a6c | 2023-10-13 02:51:08 +0000 | [diff] [blame] | 75 | // out/build_date.txt is considered a "source file" | 
| Rupert Shuttleworth | 0bc9a9a | 2020-12-08 13:28:38 +0000 | [diff] [blame] | 76 | buildDatetimeFilePath := filepath.Join(outDir, "build_date.txt") | 
|  | 77 |  | 
| Lukacs T. Berki | 5a67da7 | 2021-11-10 12:32:22 +0100 | [diff] [blame] | 78 | // bpglob is built explicitly using Microfactory | 
|  | 79 | bpglob := filepath.Join(config.SoongOutDir(), "bpglob") | 
|  | 80 |  | 
| Colin Cross | 63b4e0f | 2018-06-26 23:48:52 -0700 | [diff] [blame] | 81 | danglingRules := make(map[string]bool) | 
| Colin Cross | 3719349 | 2017-11-16 17:55:00 -0800 | [diff] [blame] | 82 |  | 
|  | 83 | scanner := bufio.NewScanner(stdout) | 
|  | 84 | for scanner.Scan() { | 
|  | 85 | line := scanner.Text() | 
|  | 86 | if !strings.HasPrefix(line, outDir) { | 
|  | 87 | // Leaf node is not in the out directory. | 
|  | 88 | continue | 
|  | 89 | } | 
| Lukacs T. Berki | 90b4334 | 2021-11-02 14:42:04 +0100 | [diff] [blame] | 90 | if strings.HasPrefix(line, modulePathsDir) || | 
| Jingwen Chen | ebb0b57 | 2020-11-02 00:24:57 -0500 | [diff] [blame] | 91 | line == variablesFilePath || | 
| Rupert Shuttleworth | 0bc9a9a | 2020-12-08 13:28:38 +0000 | [diff] [blame] | 92 | line == dexpreoptConfigFilePath || | 
| Lukacs T. Berki | 5a67da7 | 2021-11-10 12:32:22 +0100 | [diff] [blame] | 93 | line == buildDatetimeFilePath || | 
|  | 94 | line == bpglob { | 
| Colin Cross | 3719349 | 2017-11-16 17:55:00 -0800 | [diff] [blame] | 95 | // Leaf node is in one of Soong's bootstrap directories, which do not have | 
|  | 96 | // full build rules in the primary build.ninja file. | 
|  | 97 | continue | 
|  | 98 | } | 
| Liz Kammer | 2af5ea8 | 2022-11-11 14:21:03 -0500 | [diff] [blame] | 99 |  | 
| Colin Cross | 63b4e0f | 2018-06-26 23:48:52 -0700 | [diff] [blame] | 100 | danglingRules[line] = true | 
| Colin Cross | 3719349 | 2017-11-16 17:55:00 -0800 | [diff] [blame] | 101 | } | 
|  | 102 |  | 
|  | 103 | cmd.WaitOrFatal() | 
|  | 104 |  | 
| Colin Cross | 63b4e0f | 2018-06-26 23:48:52 -0700 | [diff] [blame] | 105 | var danglingRulesList []string | 
|  | 106 | for rule := range danglingRules { | 
|  | 107 | danglingRulesList = append(danglingRulesList, rule) | 
|  | 108 | } | 
|  | 109 | sort.Strings(danglingRulesList) | 
|  | 110 |  | 
|  | 111 | if len(danglingRulesList) > 0 { | 
| Dan Willemsen | d2e231a | 2018-08-02 12:06:24 -0700 | [diff] [blame] | 112 | sb := &strings.Builder{} | 
|  | 113 | title := "Dependencies in out found with no rule to create them:" | 
|  | 114 | fmt.Fprintln(sb, title) | 
| Martin Stjernholm | 946fb67 | 2020-04-15 23:23:34 +0100 | [diff] [blame] | 115 |  | 
| Jaewoong Jung | 18aefc1 | 2020-12-21 09:11:10 -0800 | [diff] [blame] | 116 | reportLines := 1 | 
| Martin Stjernholm | 946fb67 | 2020-04-15 23:23:34 +0100 | [diff] [blame] | 117 | for i, dep := range danglingRulesList { | 
| Jaewoong Jung | 18aefc1 | 2020-12-21 09:11:10 -0800 | [diff] [blame] | 118 | if reportLines > 20 { | 
| Martin Stjernholm | 946fb67 | 2020-04-15 23:23:34 +0100 | [diff] [blame] | 119 | fmt.Fprintf(sb, "  ... and %d more\n", len(danglingRulesList)-i) | 
|  | 120 | break | 
|  | 121 | } | 
|  | 122 | // It's helpful to see the reverse dependencies. ninja -t query is the | 
|  | 123 | // best tool we got for that. Its output starts with the dependency | 
|  | 124 | // itself. | 
| Jaewoong Jung | 18aefc1 | 2020-12-21 09:11:10 -0800 | [diff] [blame] | 125 | queryCmd := Command(ctx, config, "ninja", executable, | 
|  | 126 | append(commonArgs, "-t", "query", dep)...) | 
|  | 127 | queryStdout, err := queryCmd.StdoutPipe() | 
| Martin Stjernholm | 946fb67 | 2020-04-15 23:23:34 +0100 | [diff] [blame] | 128 | if err != nil { | 
|  | 129 | ctx.Fatal(err) | 
|  | 130 | } | 
| Jaewoong Jung | 18aefc1 | 2020-12-21 09:11:10 -0800 | [diff] [blame] | 131 | queryCmd.StartOrFatal() | 
|  | 132 | scanner := bufio.NewScanner(queryStdout) | 
| Martin Stjernholm | 946fb67 | 2020-04-15 23:23:34 +0100 | [diff] [blame] | 133 | for scanner.Scan() { | 
| Jaewoong Jung | 18aefc1 | 2020-12-21 09:11:10 -0800 | [diff] [blame] | 134 | reportLines++ | 
| Martin Stjernholm | 946fb67 | 2020-04-15 23:23:34 +0100 | [diff] [blame] | 135 | fmt.Fprintln(sb, " ", scanner.Text()) | 
|  | 136 | } | 
| Jaewoong Jung | 18aefc1 | 2020-12-21 09:11:10 -0800 | [diff] [blame] | 137 | queryCmd.WaitOrFatal() | 
| Colin Cross | 3719349 | 2017-11-16 17:55:00 -0800 | [diff] [blame] | 138 | } | 
| Martin Stjernholm | 946fb67 | 2020-04-15 23:23:34 +0100 | [diff] [blame] | 139 |  | 
| Dan Willemsen | d2e231a | 2018-08-02 12:06:24 -0700 | [diff] [blame] | 140 | ts.FinishAction(status.ActionResult{ | 
|  | 141 | Action: action, | 
|  | 142 | Error:  fmt.Errorf(title), | 
|  | 143 | Output: sb.String(), | 
|  | 144 | }) | 
| Colin Cross | 63b4e0f | 2018-06-26 23:48:52 -0700 | [diff] [blame] | 145 | ctx.Fatal("stopping") | 
| Colin Cross | 3719349 | 2017-11-16 17:55:00 -0800 | [diff] [blame] | 146 | } | 
| Dan Willemsen | d2e231a | 2018-08-02 12:06:24 -0700 | [diff] [blame] | 147 | ts.FinishAction(status.ActionResult{Action: action}) | 
| Colin Cross | 3719349 | 2017-11-16 17:55:00 -0800 | [diff] [blame] | 148 | } |