|  | // Copyright 2015 Google Inc. All rights reserved. | 
|  | // | 
|  | // Licensed under the Apache License, Version 2.0 (the "License"); | 
|  | // you may not use this file except in compliance with the License. | 
|  | // You may obtain a copy of the License at | 
|  | // | 
|  | //     http://www.apache.org/licenses/LICENSE-2.0 | 
|  | // | 
|  | // Unless required by applicable law or agreed to in writing, software | 
|  | // distributed under the License is distributed on an "AS IS" BASIS, | 
|  | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  | // See the License for the specific language governing permissions and | 
|  | // limitations under the License. | 
|  |  | 
|  | package android | 
|  |  | 
|  | import ( | 
|  | "fmt" | 
|  | "strings" | 
|  | "testing" | 
|  |  | 
|  | "github.com/google/blueprint" | 
|  | "github.com/google/blueprint/bootstrap" | 
|  | "github.com/google/blueprint/proptools" | 
|  | ) | 
|  |  | 
|  | var ( | 
|  | pctx         = NewPackageContext("android/soong/android") | 
|  | exportedVars = NewExportedVariables(pctx) | 
|  |  | 
|  | cpPreserveSymlinks = pctx.VariableConfigMethod("cpPreserveSymlinks", | 
|  | Config.CpPreserveSymlinksFlags) | 
|  |  | 
|  | // A phony rule that is not the built-in Ninja phony rule.  The built-in | 
|  | // phony rule has special behavior that is sometimes not desired.  See the | 
|  | // Ninja docs for more details. | 
|  | Phony = pctx.AndroidStaticRule("Phony", | 
|  | blueprint.RuleParams{ | 
|  | Command:     "# phony $out", | 
|  | Description: "phony $out", | 
|  | }) | 
|  |  | 
|  | // GeneratedFile is a rule for indicating that a given file was generated | 
|  | // while running soong.  This allows the file to be cleaned up if it ever | 
|  | // stops being generated by soong. | 
|  | GeneratedFile = pctx.AndroidStaticRule("GeneratedFile", | 
|  | blueprint.RuleParams{ | 
|  | Command:     "# generated $out", | 
|  | Description: "generated $out", | 
|  | Generator:   true, | 
|  | }) | 
|  |  | 
|  | // A copy rule. | 
|  | Cp = pctx.AndroidStaticRule("Cp", | 
|  | blueprint.RuleParams{ | 
|  | Command:     "rm -f $out && cp $cpPreserveSymlinks $cpFlags $in $out$extraCmds", | 
|  | Description: "cp $out", | 
|  | }, | 
|  | "cpFlags", "extraCmds") | 
|  |  | 
|  | // A copy rule that doesn't preserve symlinks. | 
|  | CpNoPreserveSymlink = pctx.AndroidStaticRule("CpNoPreserveSymlink", | 
|  | blueprint.RuleParams{ | 
|  | Command:     "rm -f $out && cp $cpFlags $in $out$extraCmds", | 
|  | Description: "cp $out", | 
|  | }, | 
|  | "cpFlags", "extraCmds") | 
|  |  | 
|  | // A copy rule that only updates the output if it changed. | 
|  | CpIfChanged = pctx.AndroidStaticRule("CpIfChanged", | 
|  | blueprint.RuleParams{ | 
|  | Command:     "if ! cmp -s $in $out; then cp $in $out; fi", | 
|  | Description: "cp if changed $out", | 
|  | Restat:      true, | 
|  | }, | 
|  | "cpFlags") | 
|  |  | 
|  | CpExecutable = pctx.AndroidStaticRule("CpExecutable", | 
|  | blueprint.RuleParams{ | 
|  | Command:     "rm -f $out && cp $cpFlags $in $out && chmod +x $out$extraCmds", | 
|  | Description: "cp $out", | 
|  | }, | 
|  | "cpFlags", "extraCmds") | 
|  |  | 
|  | // A timestamp touch rule. | 
|  | Touch = pctx.AndroidStaticRule("Touch", | 
|  | blueprint.RuleParams{ | 
|  | Command:     "touch $out", | 
|  | Description: "touch $out", | 
|  | }) | 
|  |  | 
|  | // A symlink rule. | 
|  | Symlink = pctx.AndroidStaticRule("Symlink", | 
|  | blueprint.RuleParams{ | 
|  | Command:        "rm -f $out && ln -f -s $fromPath $out", | 
|  | Description:    "symlink $out", | 
|  | SymlinkOutputs: []string{"$out"}, | 
|  | }, | 
|  | "fromPath") | 
|  |  | 
|  | ErrorRule = pctx.AndroidStaticRule("Error", | 
|  | blueprint.RuleParams{ | 
|  | Command:     `echo "$error" && false`, | 
|  | Description: "error building $out", | 
|  | }, | 
|  | "error") | 
|  |  | 
|  | Cat = pctx.AndroidStaticRule("Cat", | 
|  | blueprint.RuleParams{ | 
|  | Command:     "rm -f $out && cat $in > $out", | 
|  | Description: "concatenate files to $out", | 
|  | }) | 
|  |  | 
|  | // ubuntu 14.04 offcially use dash for /bin/sh, and its builtin echo command | 
|  | // doesn't support -e option. Therefore we force to use /bin/bash when writing out | 
|  | // content to file. | 
|  | writeFile = pctx.AndroidStaticRule("writeFile", | 
|  | blueprint.RuleParams{ | 
|  | Command:     `rm -f $out && /bin/bash -c 'echo -e -n "$$0" > $out' $content`, | 
|  | Description: "writing file $out", | 
|  | }, | 
|  | "content") | 
|  |  | 
|  | // Used only when USE_GOMA=true is set, to restrict non-goma jobs to the local parallelism value | 
|  | localPool = blueprint.NewBuiltinPool("local_pool") | 
|  |  | 
|  | // Used only by RuleBuilder to identify remoteable rules. Does not actually get created in ninja. | 
|  | remotePool = blueprint.NewBuiltinPool("remote_pool") | 
|  |  | 
|  | // Used for processes that need significant RAM to ensure there are not too many running in parallel. | 
|  | highmemPool = blueprint.NewBuiltinPool("highmem_pool") | 
|  | ) | 
|  |  | 
|  | func init() { | 
|  | pctx.Import("github.com/google/blueprint/bootstrap") | 
|  |  | 
|  | pctx.VariableFunc("RBEWrapper", func(ctx PackageVarContext) string { | 
|  | return ctx.Config().RBEWrapper() | 
|  | }) | 
|  |  | 
|  | exportedVars.ExportStringList("NeverAllowNotInIncludeDir", neverallowNotInIncludeDir) | 
|  | exportedVars.ExportStringList("NeverAllowNoUseIncludeDir", neverallowNoUseIncludeDir) | 
|  | } | 
|  |  | 
|  | func BazelCcToolchainVars(config Config) string { | 
|  | return BazelToolchainVars(config, exportedVars) | 
|  | } | 
|  |  | 
|  | var ( | 
|  | // echoEscaper escapes a string such that passing it to "echo -e" will produce the input value. | 
|  | echoEscaper = strings.NewReplacer( | 
|  | `\`, `\\`, // First escape existing backslashes so they aren't interpreted by `echo -e`. | 
|  | "\n", `\n`, // Then replace newlines with \n | 
|  | ) | 
|  |  | 
|  | // echoEscaper reverses echoEscaper. | 
|  | echoUnescaper = strings.NewReplacer( | 
|  | `\n`, "\n", | 
|  | `\\`, `\`, | 
|  | ) | 
|  |  | 
|  | // shellUnescaper reverses the replacer in proptools.ShellEscape | 
|  | shellUnescaper = strings.NewReplacer(`'\''`, `'`) | 
|  | ) | 
|  |  | 
|  | func buildWriteFileRule(ctx BuilderContext, outputFile WritablePath, content string) { | 
|  | content = echoEscaper.Replace(content) | 
|  | content = proptools.NinjaEscape(proptools.ShellEscapeIncludingSpaces(content)) | 
|  | if content == "" { | 
|  | content = "''" | 
|  | } | 
|  | ctx.Build(pctx, BuildParams{ | 
|  | Rule:        writeFile, | 
|  | Output:      outputFile, | 
|  | Description: "write " + outputFile.Base(), | 
|  | Args: map[string]string{ | 
|  | "content": content, | 
|  | }, | 
|  | }) | 
|  | } | 
|  |  | 
|  | // WriteFileRule creates a ninja rule to write contents to a file.  The contents will be escaped | 
|  | // so that the file contains exactly the contents passed to the function, plus a trailing newline. | 
|  | func WriteFileRule(ctx BuilderContext, outputFile WritablePath, content string) { | 
|  | WriteFileRuleVerbatim(ctx, outputFile, content+"\n") | 
|  | } | 
|  |  | 
|  | // WriteFileRuleVerbatim creates a ninja rule to write contents to a file.  The contents will be | 
|  | // escaped so that the file contains exactly the contents passed to the function. | 
|  | func WriteFileRuleVerbatim(ctx BuilderContext, outputFile WritablePath, content string) { | 
|  | // This is MAX_ARG_STRLEN subtracted with some safety to account for shell escapes | 
|  | const SHARD_SIZE = 131072 - 10000 | 
|  |  | 
|  | if len(content) > SHARD_SIZE { | 
|  | var chunks WritablePaths | 
|  | for i, c := range ShardString(content, SHARD_SIZE) { | 
|  | tempPath := outputFile.ReplaceExtension(ctx, fmt.Sprintf("%s.%d", outputFile.Ext(), i)) | 
|  | buildWriteFileRule(ctx, tempPath, c) | 
|  | chunks = append(chunks, tempPath) | 
|  | } | 
|  | ctx.Build(pctx, BuildParams{ | 
|  | Rule:        Cat, | 
|  | Inputs:      chunks.Paths(), | 
|  | Output:      outputFile, | 
|  | Description: "Merging to " + outputFile.Base(), | 
|  | }) | 
|  | return | 
|  | } | 
|  | buildWriteFileRule(ctx, outputFile, content) | 
|  | } | 
|  |  | 
|  | // WriteExecutableFileRuleVerbatim is the same as WriteFileRuleVerbatim, but runs chmod +x on the result | 
|  | func WriteExecutableFileRuleVerbatim(ctx BuilderContext, outputFile WritablePath, content string) { | 
|  | intermediate := PathForIntermediates(ctx, "write_executable_file_intermediates").Join(ctx, outputFile.String()) | 
|  | WriteFileRuleVerbatim(ctx, intermediate, content) | 
|  | ctx.Build(pctx, BuildParams{ | 
|  | Rule:   CpExecutable, | 
|  | Output: outputFile, | 
|  | Input:  intermediate, | 
|  | }) | 
|  | } | 
|  |  | 
|  | // shellUnescape reverses proptools.ShellEscape | 
|  | func shellUnescape(s string) string { | 
|  | // Remove leading and trailing quotes if present | 
|  | if len(s) >= 2 && s[0] == '\'' { | 
|  | s = s[1 : len(s)-1] | 
|  | } | 
|  | s = shellUnescaper.Replace(s) | 
|  | return s | 
|  | } | 
|  |  | 
|  | // ContentFromFileRuleForTests returns the content that was passed to a WriteFileRule for use | 
|  | // in tests. | 
|  | func ContentFromFileRuleForTests(t *testing.T, params TestingBuildParams) string { | 
|  | t.Helper() | 
|  | if g, w := params.Rule, writeFile; g != w { | 
|  | t.Errorf("expected params.Rule to be %q, was %q", w, g) | 
|  | return "" | 
|  | } | 
|  |  | 
|  | content := params.Args["content"] | 
|  | content = shellUnescape(content) | 
|  | content = echoUnescaper.Replace(content) | 
|  |  | 
|  | return content | 
|  | } | 
|  |  | 
|  | // GlobToListFileRule creates a rule that writes a list of files matching a pattern to a file. | 
|  | func GlobToListFileRule(ctx ModuleContext, pattern string, excludes []string, file WritablePath) { | 
|  | bootstrap.GlobFile(ctx.blueprintModuleContext(), pattern, excludes, file.String()) | 
|  | } |