blob: a3cfab267a8decc11fa3e14aeda89e6e247a0aac [file] [log] [blame]
Colin Cross3f40fa42015-01-30 17:27:36 -08001// Copyright 2015 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 common
16
17import (
18 "fmt"
19 "path/filepath"
Colin Cross3e8ec072015-03-31 16:40:23 -070020 "strings"
Colin Cross3f40fa42015-01-30 17:27:36 -080021
Colin Cross70b40592015-03-23 12:57:34 -070022 "github.com/google/blueprint"
23 "github.com/google/blueprint/bootstrap"
Colin Cross3f40fa42015-01-30 17:27:36 -080024
25 "android/soong/glob"
26)
27
28// This file supports globbing source files in Blueprints files.
29//
30// The build.ninja file needs to be regenerated any time a file matching the glob is added
31// or removed. The naive solution is to have the build.ninja file depend on all the
32// traversed directories, but this will cause the regeneration step to run every time a
33// non-matching file is added to a traversed directory, including backup files created by
34// editors.
35//
36// The solution implemented here optimizes out regenerations when the directory modifications
37// don't match the glob by having the build.ninja file depend on an intermedate file that
38// is only updated when a file matching the glob is added or removed. The intermediate file
39// depends on the traversed directories via a depfile. The depfile is used to avoid build
40// errors if a directory is deleted - a direct dependency on the deleted directory would result
41// in a build failure with a "missing and no known rule to make it" error.
42
43var (
44 globCmd = filepath.Join(bootstrap.BinDir, "soong_glob")
45
46 // globRule rule traverses directories to produce a list of files that match $glob
47 // and writes it to $out if it has changed, and writes the directories to $out.d
48 globRule = pctx.StaticRule("globRule",
49 blueprint.RuleParams{
Colin Cross3e8ec072015-03-31 16:40:23 -070050 Command: fmt.Sprintf(`%s -o $out $excludes "$glob"`, globCmd),
Colin Cross3f40fa42015-01-30 17:27:36 -080051 Description: "glob $glob",
52
53 Restat: true,
54 Generator: true,
55 Deps: blueprint.DepsGCC,
56 Depfile: "$out.d",
57 },
Colin Cross3e8ec072015-03-31 16:40:23 -070058 "glob", "excludes")
Colin Cross3f40fa42015-01-30 17:27:36 -080059)
60
61func hasGlob(in []string) bool {
62 for _, s := range in {
63 if glob.IsGlob(s) {
64 return true
65 }
66 }
67
68 return false
69}
70
71func ExpandGlobs(ctx AndroidModuleContext, in []string) []string {
72 if !hasGlob(in) {
73 return in
74 }
75
76 out := make([]string, 0, len(in))
77 for _, s := range in {
78 if glob.IsGlob(s) {
79 out = append(out, Glob(ctx, s)...)
80 } else {
81 out = append(out, s)
82 }
83 }
84
85 return out
86}
87
88func Glob(ctx AndroidModuleContext, globPattern string) []string {
89 fileListFile := filepath.Join(ModuleOutDir(ctx), "glob", globToString(globPattern))
90 depFile := fileListFile + ".d"
91
Colin Cross3e8ec072015-03-31 16:40:23 -070092 var excludes []string
93
Colin Cross3f40fa42015-01-30 17:27:36 -080094 // Get a globbed file list, and write out fileListFile and depFile
Colin Cross3e8ec072015-03-31 16:40:23 -070095 files, err := glob.GlobWithDepFile(globPattern, fileListFile, depFile, excludes)
Colin Cross3f40fa42015-01-30 17:27:36 -080096 if err != nil {
97 ctx.ModuleErrorf("glob: %s", err.Error())
98 return []string{globPattern}
99 }
100
Colin Cross3e8ec072015-03-31 16:40:23 -0700101 GlobRule(ctx, globPattern, excludes, fileListFile, depFile)
102
103 // Make build.ninja depend on the fileListFile
104 ctx.AddNinjaFileDeps(fileListFile)
105
106 return files
107}
108
109func GlobRule(ctx AndroidModuleContext, globPattern string, excludes []string,
110 fileListFile, depFile string) {
111 var excludeArgs []string
112 for _, e := range excludes {
113 excludeArgs = append(excludeArgs, "-e "+e)
114 }
115
Colin Cross3f40fa42015-01-30 17:27:36 -0800116 // Create a rule to rebuild fileListFile if a directory in depFile changes. fileListFile
117 // will only be rewritten if it has changed, preventing unnecesary build.ninja regenerations.
118 ctx.Build(pctx, blueprint.BuildParams{
119 Rule: globRule,
120 Outputs: []string{fileListFile},
121 Implicits: []string{globCmd},
122 Args: map[string]string{
Colin Cross3e8ec072015-03-31 16:40:23 -0700123 "glob": globPattern,
124 "excludes": strings.Join(excludeArgs, " "),
Colin Cross3f40fa42015-01-30 17:27:36 -0800125 },
126 })
127
128 // Phony rule so the cleanup phase doesn't delete the depFile
129 ctx.Build(pctx, blueprint.BuildParams{
130 Rule: blueprint.Phony,
131 Outputs: []string{depFile},
132 })
Colin Cross3f40fa42015-01-30 17:27:36 -0800133}
134
135func globToString(glob string) string {
136 ret := ""
137 for _, c := range glob {
138 if c >= 'a' && c <= 'z' ||
139 c >= 'A' && c <= 'Z' ||
140 c >= '0' && c <= '9' ||
141 c == '_' || c == '-' || c == '/' {
142 ret += string(c)
143 }
144 }
145
146 return ret
147}