1// 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 android 16 17import ( 18 "fmt" 19 "strings" 20 "testing" 21 22 "github.com/google/blueprint" 23 "github.com/google/blueprint/bootstrap" 24 "github.com/google/blueprint/proptools" 25) 26 27var ( 28 pctx = NewPackageContext("android/soong/android") 29 exportedVars = NewExportedVariables(pctx) 30 31 cpPreserveSymlinks = pctx.VariableConfigMethod("cpPreserveSymlinks", 32 Config.CpPreserveSymlinksFlags) 33 34 // A phony rule that is not the built-in Ninja phony rule. The built-in 35 // phony rule has special behavior that is sometimes not desired. See the 36 // Ninja docs for more details. 37 Phony = pctx.AndroidStaticRule("Phony", 38 blueprint.RuleParams{ 39 Command: "# phony $out", 40 Description: "phony $out", 41 }) 42 43 // GeneratedFile is a rule for indicating that a given file was generated 44 // while running soong. This allows the file to be cleaned up if it ever 45 // stops being generated by soong. 46 GeneratedFile = pctx.AndroidStaticRule("GeneratedFile", 47 blueprint.RuleParams{ 48 Command: "# generated $out", 49 Description: "generated $out", 50 Generator: true, 51 }) 52 53 // A copy rule. 54 Cp = pctx.AndroidStaticRule("Cp", 55 blueprint.RuleParams{ 56 Command: "rm -f $out && cp $cpPreserveSymlinks $cpFlags $in $out$extraCmds", 57 Description: "cp $out", 58 }, 59 "cpFlags", "extraCmds") 60 61 // A copy rule that doesn't preserve symlinks. 62 CpNoPreserveSymlink = pctx.AndroidStaticRule("CpNoPreserveSymlink", 63 blueprint.RuleParams{ 64 Command: "rm -f $out && cp $cpFlags $in $out$extraCmds", 65 Description: "cp $out", 66 }, 67 "cpFlags", "extraCmds") 68 69 // A copy rule that only updates the output if it changed. 70 CpIfChanged = pctx.AndroidStaticRule("CpIfChanged", 71 blueprint.RuleParams{ 72 Command: "if ! cmp -s $in $out; then cp $in $out; fi", 73 Description: "cp if changed $out", 74 Restat: true, 75 }, 76 "cpFlags") 77 78 CpExecutable = pctx.AndroidStaticRule("CpExecutable", 79 blueprint.RuleParams{ 80 Command: "rm -f $out && cp $cpFlags $in $out && chmod +x $out$extraCmds", 81 Description: "cp $out", 82 }, 83 "cpFlags", "extraCmds") 84 85 // A timestamp touch rule. 86 Touch = pctx.AndroidStaticRule("Touch", 87 blueprint.RuleParams{ 88 Command: "touch $out", 89 Description: "touch $out", 90 }) 91 92 // A symlink rule. 93 Symlink = pctx.AndroidStaticRule("Symlink", 94 blueprint.RuleParams{ 95 Command: "rm -f $out && ln -f -s $fromPath $out", 96 Description: "symlink $out", 97 SymlinkOutputs: []string{"$out"}, 98 }, 99 "fromPath") 100 101 ErrorRule = pctx.AndroidStaticRule("Error", 102 blueprint.RuleParams{ 103 Command: `echo "$error" && false`, 104 Description: "error building $out", 105 }, 106 "error") 107 108 Cat = pctx.AndroidStaticRule("Cat", 109 blueprint.RuleParams{ 110 Command: "cat $in > $out", 111 Description: "concatenate licenses $out", 112 }) 113 114 // ubuntu 14.04 offcially use dash for /bin/sh, and its builtin echo command 115 // doesn't support -e option. Therefore we force to use /bin/bash when writing out 116 // content to file. 117 writeFile = pctx.AndroidStaticRule("writeFile", 118 blueprint.RuleParams{ 119 Command: `/bin/bash -c 'echo -e -n "$$0" > $out' $content`, 120 Description: "writing file $out", 121 }, 122 "content") 123 124 // Used only when USE_GOMA=true is set, to restrict non-goma jobs to the local parallelism value 125 localPool = blueprint.NewBuiltinPool("local_pool") 126 127 // Used only by RuleBuilder to identify remoteable rules. Does not actually get created in ninja. 128 remotePool = blueprint.NewBuiltinPool("remote_pool") 129 130 // Used for processes that need significant RAM to ensure there are not too many running in parallel. 131 highmemPool = blueprint.NewBuiltinPool("highmem_pool") 132) 133 134func init() { 135 pctx.Import("github.com/google/blueprint/bootstrap") 136 137 pctx.VariableFunc("RBEWrapper", func(ctx PackageVarContext) string { 138 return ctx.Config().RBEWrapper() 139 }) 140 141 exportedVars.ExportStringList("NeverAllowNotInIncludeDir", neverallowNotInIncludeDir) 142 exportedVars.ExportStringList("NeverAllowNoUseIncludeDir", neverallowNoUseIncludeDir) 143} 144 145func BazelCcToolchainVars(config Config) string { 146 return BazelToolchainVars(config, exportedVars) 147} 148 149var ( 150 // echoEscaper escapes a string such that passing it to "echo -e" will produce the input value. 151 echoEscaper = strings.NewReplacer( 152 `\`, `\\`, // First escape existing backslashes so they aren't interpreted by `echo -e`. 153 "\n", `\n`, // Then replace newlines with \n 154 ) 155 156 // echoEscaper reverses echoEscaper. 157 echoUnescaper = strings.NewReplacer( 158 `\n`, "\n", 159 `\\`, `\`, 160 ) 161 162 // shellUnescaper reverses the replacer in proptools.ShellEscape 163 shellUnescaper = strings.NewReplacer(`'\''`, `'`) 164) 165 166func buildWriteFileRule(ctx BuilderContext, outputFile WritablePath, content string) { 167 content = echoEscaper.Replace(content) 168 content = proptools.NinjaEscape(proptools.ShellEscapeIncludingSpaces(content)) 169 if content == "" { 170 content = "''" 171 } 172 ctx.Build(pctx, BuildParams{ 173 Rule: writeFile, 174 Output: outputFile, 175 Description: "write " + outputFile.Base(), 176 Args: map[string]string{ 177 "content": content, 178 }, 179 }) 180} 181 182// WriteFileRule creates a ninja rule to write contents to a file. The contents will be escaped 183// so that the file contains exactly the contents passed to the function, plus a trailing newline. 184func WriteFileRule(ctx BuilderContext, outputFile WritablePath, content string) { 185 WriteFileRuleVerbatim(ctx, outputFile, content+"\n") 186} 187 188// WriteFileRuleVerbatim creates a ninja rule to write contents to a file. The contents will be 189// escaped so that the file contains exactly the contents passed to the function. 190func WriteFileRuleVerbatim(ctx BuilderContext, outputFile WritablePath, content string) { 191 // This is MAX_ARG_STRLEN subtracted with some safety to account for shell escapes 192 const SHARD_SIZE = 131072 - 10000 193 194 if len(content) > SHARD_SIZE { 195 var chunks WritablePaths 196 for i, c := range ShardString(content, SHARD_SIZE) { 197 tempPath := outputFile.ReplaceExtension(ctx, fmt.Sprintf("%s.%d", outputFile.Ext(), i)) 198 buildWriteFileRule(ctx, tempPath, c) 199 chunks = append(chunks, tempPath) 200 } 201 ctx.Build(pctx, BuildParams{ 202 Rule: Cat, 203 Inputs: chunks.Paths(), 204 Output: outputFile, 205 Description: "Merging to " + outputFile.Base(), 206 }) 207 return 208 } 209 buildWriteFileRule(ctx, outputFile, content) 210} 211 212func CatFileRule(ctx BuilderContext, paths Paths, outputFile WritablePath) { 213 ctx.Build(pctx, BuildParams{ 214 Rule: Cat, 215 Inputs: paths, 216 Output: outputFile, 217 Description: "combine files to " + outputFile.Base(), 218 }) 219} 220 221// shellUnescape reverses proptools.ShellEscape 222func shellUnescape(s string) string { 223 // Remove leading and trailing quotes if present 224 if len(s) >= 2 && s[0] == '\'' { 225 s = s[1 : len(s)-1] 226 } 227 s = shellUnescaper.Replace(s) 228 return s 229} 230 231// ContentFromFileRuleForTests returns the content that was passed to a WriteFileRule for use 232// in tests. 233func ContentFromFileRuleForTests(t *testing.T, params TestingBuildParams) string { 234 t.Helper() 235 if g, w := params.Rule, writeFile; g != w { 236 t.Errorf("expected params.Rule to be %q, was %q", w, g) 237 return "" 238 } 239 240 content := params.Args["content"] 241 content = shellUnescape(content) 242 content = echoUnescaper.Replace(content) 243 244 return content 245} 246 247// GlobToListFileRule creates a rule that writes a list of files matching a pattern to a file. 248func GlobToListFileRule(ctx ModuleContext, pattern string, excludes []string, file WritablePath) { 249 bootstrap.GlobFile(ctx.blueprintModuleContext(), pattern, excludes, file.String()) 250} 251