blob: 34b3de406979f7fc6af77a1d0291fed6d91419f6 [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
Colin Cross635c3b02016-05-18 15:37:25 -070015package android
Colin Cross3f40fa42015-01-30 17:27:36 -080016
17import (
18 "fmt"
19 "path/filepath"
20
Colin Cross70b40592015-03-23 12:57:34 -070021 "github.com/google/blueprint"
Colin Cross3f40fa42015-01-30 17:27:36 -080022
23 "android/soong/glob"
24)
25
26// This file supports globbing source files in Blueprints files.
27//
28// The build.ninja file needs to be regenerated any time a file matching the glob is added
29// or removed. The naive solution is to have the build.ninja file depend on all the
30// traversed directories, but this will cause the regeneration step to run every time a
31// non-matching file is added to a traversed directory, including backup files created by
32// editors.
33//
34// The solution implemented here optimizes out regenerations when the directory modifications
35// don't match the glob by having the build.ninja file depend on an intermedate file that
36// is only updated when a file matching the glob is added or removed. The intermediate file
37// depends on the traversed directories via a depfile. The depfile is used to avoid build
38// errors if a directory is deleted - a direct dependency on the deleted directory would result
39// in a build failure with a "missing and no known rule to make it" error.
40
41var (
Dan Willemsenc2aa4a92016-05-26 15:13:03 -070042 globCmd = filepath.Join("${bootstrap.ToolDir}", "soong_glob")
Colin Cross3f40fa42015-01-30 17:27:36 -080043
44 // globRule rule traverses directories to produce a list of files that match $glob
45 // and writes it to $out if it has changed, and writes the directories to $out.d
46 globRule = pctx.StaticRule("globRule",
47 blueprint.RuleParams{
Colin Cross3e8ec072015-03-31 16:40:23 -070048 Command: fmt.Sprintf(`%s -o $out $excludes "$glob"`, globCmd),
Dan Willemsenc94a7682015-11-17 15:27:28 -080049 CommandDeps: []string{globCmd},
Colin Cross3f40fa42015-01-30 17:27:36 -080050 Description: "glob $glob",
51
Colin Cross6a114ca2015-04-24 15:14:48 -070052 Restat: true,
53 Deps: blueprint.DepsGCC,
54 Depfile: "$out.d",
Colin Cross3f40fa42015-01-30 17:27:36 -080055 },
Colin Cross3e8ec072015-03-31 16:40:23 -070056 "glob", "excludes")
Colin Cross3f40fa42015-01-30 17:27:36 -080057)
58
59func hasGlob(in []string) bool {
60 for _, s := range in {
61 if glob.IsGlob(s) {
62 return true
63 }
64 }
65
66 return false
67}
68
Colin Cross8f101b42015-06-17 15:09:06 -070069// The subset of ModuleContext and SingletonContext needed by Glob
70type globContext interface {
Dan Willemsen14e5c2a2015-11-30 13:59:34 -080071 Build(pctx blueprint.PackageContext, params blueprint.BuildParams)
Colin Cross8f101b42015-06-17 15:09:06 -070072 AddNinjaFileDeps(deps ...string)
Colin Cross3f40fa42015-01-30 17:27:36 -080073}
74
Colin Cross8f101b42015-06-17 15:09:06 -070075func Glob(ctx globContext, outDir string, globPattern string, excludes []string) ([]string, error) {
Colin Crossda727cf2016-04-27 13:45:26 -070076 fileListFile := filepath.Join(outDir, "glob", globToString(globPattern)+".glob")
Colin Cross3f40fa42015-01-30 17:27:36 -080077 depFile := fileListFile + ".d"
78
79 // Get a globbed file list, and write out fileListFile and depFile
Colin Cross3e8ec072015-03-31 16:40:23 -070080 files, err := glob.GlobWithDepFile(globPattern, fileListFile, depFile, excludes)
Colin Cross3f40fa42015-01-30 17:27:36 -080081 if err != nil {
Colin Cross8f101b42015-06-17 15:09:06 -070082 return nil, err
Colin Cross3f40fa42015-01-30 17:27:36 -080083 }
84
Colin Cross3e8ec072015-03-31 16:40:23 -070085 GlobRule(ctx, globPattern, excludes, fileListFile, depFile)
86
87 // Make build.ninja depend on the fileListFile
88 ctx.AddNinjaFileDeps(fileListFile)
89
Colin Cross8f101b42015-06-17 15:09:06 -070090 return files, nil
Colin Cross3e8ec072015-03-31 16:40:23 -070091}
92
Colin Cross8f101b42015-06-17 15:09:06 -070093func GlobRule(ctx globContext, globPattern string, excludes []string,
Colin Cross3e8ec072015-03-31 16:40:23 -070094 fileListFile, depFile string) {
Colin Cross3e8ec072015-03-31 16:40:23 -070095
Colin Cross3f40fa42015-01-30 17:27:36 -080096 // Create a rule to rebuild fileListFile if a directory in depFile changes. fileListFile
97 // will only be rewritten if it has changed, preventing unnecesary build.ninja regenerations.
98 ctx.Build(pctx, blueprint.BuildParams{
Dan Willemsenc94a7682015-11-17 15:27:28 -080099 Rule: globRule,
100 Outputs: []string{fileListFile},
Colin Cross3f40fa42015-01-30 17:27:36 -0800101 Args: map[string]string{
Colin Cross3e8ec072015-03-31 16:40:23 -0700102 "glob": globPattern,
Colin Cross9b6826f2015-04-10 15:47:33 -0700103 "excludes": JoinWithPrefixAndQuote(excludes, "-e "),
Colin Cross3f40fa42015-01-30 17:27:36 -0800104 },
105 })
Colin Cross3f40fa42015-01-30 17:27:36 -0800106}
107
108func globToString(glob string) string {
109 ret := ""
110 for _, c := range glob {
111 if c >= 'a' && c <= 'z' ||
112 c >= 'A' && c <= 'Z' ||
113 c >= '0' && c <= '9' ||
114 c == '_' || c == '-' || c == '/' {
115 ret += string(c)
116 }
117 }
118
119 return ret
120}