2019-01-31 09:32:39 +08:00
// Copyright 2019 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 (
2019-02-02 08:42:32 +08:00
"fmt"
2019-01-31 09:32:39 +08:00
"path/filepath"
"reflect"
2020-11-14 08:23:53 +08:00
"regexp"
2019-02-02 08:42:32 +08:00
"strings"
2019-01-31 09:32:39 +08:00
"testing"
2019-04-13 02:11:38 +08:00
"github.com/google/blueprint"
"android/soong/shared"
2019-01-31 09:32:39 +08:00
)
2019-02-16 02:39:37 +08:00
func pathContext ( ) PathContext {
2019-12-14 12:41:13 +08:00
return PathContextForTesting ( TestConfig ( "out" , nil , "" , map [ string ] [ ] byte {
"ld" : nil ,
"a.o" : nil ,
"b.o" : nil ,
"cp" : nil ,
"a" : nil ,
"b" : nil ,
"ls" : nil ,
2020-09-23 12:30:02 +08:00
"ln" : nil ,
2019-12-14 12:41:13 +08:00
"turbine" : nil ,
"java" : nil ,
"javac" : nil ,
} ) )
2019-02-16 02:39:37 +08:00
}
2019-02-02 08:42:32 +08:00
func ExampleRuleBuilder ( ) {
rule := NewRuleBuilder ( )
2019-02-16 02:39:37 +08:00
ctx := pathContext ( )
rule . Command ( ) .
Tool ( PathForSource ( ctx , "ld" ) ) .
Inputs ( PathsForTesting ( "a.o" , "b.o" ) ) .
FlagWithOutput ( "-o " , PathForOutput ( ctx , "linked" ) )
2019-02-02 08:42:32 +08:00
rule . Command ( ) . Text ( "echo success" )
// To add the command to the build graph:
// rule.Build(pctx, ctx, "link", "link")
fmt . Printf ( "commands: %q\n" , strings . Join ( rule . Commands ( ) , " && " ) )
fmt . Printf ( "tools: %q\n" , rule . Tools ( ) )
fmt . Printf ( "inputs: %q\n" , rule . Inputs ( ) )
fmt . Printf ( "outputs: %q\n" , rule . Outputs ( ) )
// Output:
2019-02-16 02:39:37 +08:00
// commands: "ld a.o b.o -o out/linked && echo success"
2019-02-02 08:42:32 +08:00
// tools: ["ld"]
// inputs: ["a.o" "b.o"]
2019-02-16 02:39:37 +08:00
// outputs: ["out/linked"]
2019-02-02 08:42:32 +08:00
}
2020-09-23 12:30:02 +08:00
func ExampleRuleBuilder_SymlinkOutputs ( ) {
rule := NewRuleBuilder ( )
ctx := pathContext ( )
rule . Command ( ) .
Tool ( PathForSource ( ctx , "ln" ) ) .
FlagWithInput ( "-s " , PathForTesting ( "a.o" ) ) .
SymlinkOutput ( PathForOutput ( ctx , "a" ) )
rule . Command ( ) . Text ( "cp out/a out/b" ) .
ImplicitSymlinkOutput ( PathForOutput ( ctx , "b" ) )
fmt . Printf ( "commands: %q\n" , strings . Join ( rule . Commands ( ) , " && " ) )
fmt . Printf ( "tools: %q\n" , rule . Tools ( ) )
fmt . Printf ( "inputs: %q\n" , rule . Inputs ( ) )
fmt . Printf ( "outputs: %q\n" , rule . Outputs ( ) )
fmt . Printf ( "symlink_outputs: %q\n" , rule . SymlinkOutputs ( ) )
// Output:
// commands: "ln -s a.o out/a && cp out/a out/b"
// tools: ["ln"]
// inputs: ["a.o"]
// outputs: ["out/a" "out/b"]
// symlink_outputs: ["out/a" "out/b"]
}
2019-02-03 13:25:18 +08:00
func ExampleRuleBuilder_Temporary ( ) {
rule := NewRuleBuilder ( )
2019-02-16 02:39:37 +08:00
ctx := pathContext ( )
rule . Command ( ) .
Tool ( PathForSource ( ctx , "cp" ) ) .
Input ( PathForSource ( ctx , "a" ) ) .
Output ( PathForOutput ( ctx , "b" ) )
rule . Command ( ) .
Tool ( PathForSource ( ctx , "cp" ) ) .
Input ( PathForOutput ( ctx , "b" ) ) .
Output ( PathForOutput ( ctx , "c" ) )
rule . Temporary ( PathForOutput ( ctx , "b" ) )
2019-02-03 13:25:18 +08:00
fmt . Printf ( "commands: %q\n" , strings . Join ( rule . Commands ( ) , " && " ) )
fmt . Printf ( "tools: %q\n" , rule . Tools ( ) )
fmt . Printf ( "inputs: %q\n" , rule . Inputs ( ) )
fmt . Printf ( "outputs: %q\n" , rule . Outputs ( ) )
// Output:
2019-02-16 02:39:37 +08:00
// commands: "cp a out/b && cp out/b out/c"
2019-02-03 13:25:18 +08:00
// tools: ["cp"]
// inputs: ["a"]
2019-02-16 02:39:37 +08:00
// outputs: ["out/c"]
2019-02-03 13:25:18 +08:00
}
func ExampleRuleBuilder_DeleteTemporaryFiles ( ) {
rule := NewRuleBuilder ( )
2019-02-16 02:39:37 +08:00
ctx := pathContext ( )
rule . Command ( ) .
Tool ( PathForSource ( ctx , "cp" ) ) .
Input ( PathForSource ( ctx , "a" ) ) .
Output ( PathForOutput ( ctx , "b" ) )
rule . Command ( ) .
Tool ( PathForSource ( ctx , "cp" ) ) .
Input ( PathForOutput ( ctx , "b" ) ) .
Output ( PathForOutput ( ctx , "c" ) )
rule . Temporary ( PathForOutput ( ctx , "b" ) )
2019-02-03 13:25:18 +08:00
rule . DeleteTemporaryFiles ( )
fmt . Printf ( "commands: %q\n" , strings . Join ( rule . Commands ( ) , " && " ) )
fmt . Printf ( "tools: %q\n" , rule . Tools ( ) )
fmt . Printf ( "inputs: %q\n" , rule . Inputs ( ) )
fmt . Printf ( "outputs: %q\n" , rule . Outputs ( ) )
// Output:
2019-02-16 02:39:37 +08:00
// commands: "cp a out/b && cp out/b out/c && rm -f out/b"
2019-02-03 13:25:18 +08:00
// tools: ["cp"]
// inputs: ["a"]
2019-02-16 02:39:37 +08:00
// outputs: ["out/c"]
2019-02-03 13:25:18 +08:00
}
2019-02-12 06:11:09 +08:00
func ExampleRuleBuilder_Installs ( ) {
rule := NewRuleBuilder ( )
2019-02-16 02:39:37 +08:00
ctx := pathContext ( )
out := PathForOutput ( ctx , "linked" )
rule . Command ( ) .
Tool ( PathForSource ( ctx , "ld" ) ) .
Inputs ( PathsForTesting ( "a.o" , "b.o" ) ) .
FlagWithOutput ( "-o " , out )
rule . Install ( out , "/bin/linked" )
rule . Install ( out , "/sbin/linked" )
2019-02-12 06:11:09 +08:00
fmt . Printf ( "rule.Installs().String() = %q\n" , rule . Installs ( ) . String ( ) )
// Output:
2019-02-16 02:39:37 +08:00
// rule.Installs().String() = "out/linked:/bin/linked out/linked:/sbin/linked"
2019-02-12 06:11:09 +08:00
}
2019-02-02 08:42:32 +08:00
func ExampleRuleBuilderCommand ( ) {
rule := NewRuleBuilder ( )
2019-02-16 02:39:37 +08:00
ctx := pathContext ( )
2019-02-02 08:42:32 +08:00
// chained
2019-02-16 02:39:37 +08:00
rule . Command ( ) .
Tool ( PathForSource ( ctx , "ld" ) ) .
Inputs ( PathsForTesting ( "a.o" , "b.o" ) ) .
FlagWithOutput ( "-o " , PathForOutput ( ctx , "linked" ) )
2019-02-02 08:42:32 +08:00
// unchained
cmd := rule . Command ( )
2019-02-16 02:39:37 +08:00
cmd . Tool ( PathForSource ( ctx , "ld" ) )
cmd . Inputs ( PathsForTesting ( "a.o" , "b.o" ) )
cmd . FlagWithOutput ( "-o " , PathForOutput ( ctx , "linked" ) )
2019-02-02 08:42:32 +08:00
// mixed:
2019-02-16 02:39:37 +08:00
cmd = rule . Command ( ) . Tool ( PathForSource ( ctx , "ld" ) )
cmd . Inputs ( PathsForTesting ( "a.o" , "b.o" ) )
cmd . FlagWithOutput ( "-o " , PathForOutput ( ctx , "linked" ) )
2019-02-02 08:42:32 +08:00
}
func ExampleRuleBuilderCommand_Flag ( ) {
2019-02-16 02:39:37 +08:00
ctx := pathContext ( )
2019-02-02 08:42:32 +08:00
fmt . Println ( NewRuleBuilder ( ) . Command ( ) .
2019-02-16 02:39:37 +08:00
Tool ( PathForSource ( ctx , "ls" ) ) . Flag ( "-l" ) )
2019-02-02 08:42:32 +08:00
// Output:
// ls -l
}
2019-03-30 06:32:51 +08:00
func ExampleRuleBuilderCommand_Flags ( ) {
ctx := pathContext ( )
fmt . Println ( NewRuleBuilder ( ) . Command ( ) .
Tool ( PathForSource ( ctx , "ls" ) ) . Flags ( [ ] string { "-l" , "-a" } ) )
// Output:
// ls -l -a
}
2019-02-02 08:42:32 +08:00
func ExampleRuleBuilderCommand_FlagWithArg ( ) {
2019-02-16 02:39:37 +08:00
ctx := pathContext ( )
2019-02-02 08:42:32 +08:00
fmt . Println ( NewRuleBuilder ( ) . Command ( ) .
2019-02-16 02:39:37 +08:00
Tool ( PathForSource ( ctx , "ls" ) ) .
2019-02-02 08:42:32 +08:00
FlagWithArg ( "--sort=" , "time" ) )
// Output:
// ls --sort=time
}
2019-02-12 06:11:09 +08:00
func ExampleRuleBuilderCommand_FlagForEachArg ( ) {
2019-02-16 02:39:37 +08:00
ctx := pathContext ( )
2019-02-12 06:11:09 +08:00
fmt . Println ( NewRuleBuilder ( ) . Command ( ) .
2019-02-16 02:39:37 +08:00
Tool ( PathForSource ( ctx , "ls" ) ) .
2019-02-12 06:11:09 +08:00
FlagForEachArg ( "--sort=" , [ ] string { "time" , "size" } ) )
// Output:
// ls --sort=time --sort=size
}
2019-02-02 08:42:32 +08:00
func ExampleRuleBuilderCommand_FlagForEachInput ( ) {
2019-02-16 02:39:37 +08:00
ctx := pathContext ( )
2019-02-02 08:42:32 +08:00
fmt . Println ( NewRuleBuilder ( ) . Command ( ) .
2019-02-16 02:39:37 +08:00
Tool ( PathForSource ( ctx , "turbine" ) ) .
FlagForEachInput ( "--classpath " , PathsForTesting ( "a.jar" , "b.jar" ) ) )
2019-02-02 08:42:32 +08:00
// Output:
// turbine --classpath a.jar --classpath b.jar
}
func ExampleRuleBuilderCommand_FlagWithInputList ( ) {
2019-02-16 02:39:37 +08:00
ctx := pathContext ( )
2019-02-02 08:42:32 +08:00
fmt . Println ( NewRuleBuilder ( ) . Command ( ) .
2019-02-16 02:39:37 +08:00
Tool ( PathForSource ( ctx , "java" ) ) .
FlagWithInputList ( "-classpath=" , PathsForTesting ( "a.jar" , "b.jar" ) , ":" ) )
2019-02-02 08:42:32 +08:00
// Output:
// java -classpath=a.jar:b.jar
}
func ExampleRuleBuilderCommand_FlagWithInput ( ) {
2019-02-16 02:39:37 +08:00
ctx := pathContext ( )
2019-02-02 08:42:32 +08:00
fmt . Println ( NewRuleBuilder ( ) . Command ( ) .
2019-02-16 02:39:37 +08:00
Tool ( PathForSource ( ctx , "java" ) ) .
FlagWithInput ( "-classpath=" , PathForSource ( ctx , "a" ) ) )
2019-02-02 08:42:32 +08:00
// Output:
// java -classpath=a
}
func ExampleRuleBuilderCommand_FlagWithList ( ) {
2019-02-16 02:39:37 +08:00
ctx := pathContext ( )
2019-02-02 08:42:32 +08:00
fmt . Println ( NewRuleBuilder ( ) . Command ( ) .
2019-02-16 02:39:37 +08:00
Tool ( PathForSource ( ctx , "ls" ) ) .
2019-02-02 08:42:32 +08:00
FlagWithList ( "--sort=" , [ ] string { "time" , "size" } , "," ) )
// Output:
// ls --sort=time,size
}
2019-07-12 01:59:15 +08:00
func ExampleRuleBuilderCommand_FlagWithRspFileInputList ( ) {
ctx := pathContext ( )
fmt . Println ( NewRuleBuilder ( ) . Command ( ) .
Tool ( PathForSource ( ctx , "javac" ) ) .
FlagWithRspFileInputList ( "@" , PathsForTesting ( "a.java" , "b.java" ) ) .
NinjaEscapedString ( ) )
// Output:
// javac @$out.rsp
}
func ExampleRuleBuilderCommand_String ( ) {
fmt . Println ( NewRuleBuilder ( ) . Command ( ) .
Text ( "FOO=foo" ) .
Text ( "echo $FOO" ) .
String ( ) )
// Output:
// FOO=foo echo $FOO
}
func ExampleRuleBuilderCommand_NinjaEscapedString ( ) {
fmt . Println ( NewRuleBuilder ( ) . Command ( ) .
Text ( "FOO=foo" ) .
Text ( "echo $FOO" ) .
NinjaEscapedString ( ) )
// Output:
// FOO=foo echo $$FOO
}
2019-01-31 09:32:39 +08:00
func TestRuleBuilder ( t * testing . T ) {
2019-02-16 02:39:37 +08:00
fs := map [ string ] [ ] byte {
2020-02-22 08:55:19 +08:00
"dep_fixer" : nil ,
"input" : nil ,
"Implicit" : nil ,
"Input" : nil ,
"OrderOnly" : nil ,
"OrderOnlys" : nil ,
"Tool" : nil ,
"input2" : nil ,
"tool2" : nil ,
"input3" : nil ,
2019-02-16 02:39:37 +08:00
}
2019-12-14 12:41:13 +08:00
ctx := PathContextForTesting ( TestConfig ( "out" , nil , "" , fs ) )
2019-02-16 02:39:37 +08:00
2019-04-13 02:11:38 +08:00
addCommands := func ( rule * RuleBuilder ) {
cmd := rule . Command ( ) .
DepFile ( PathForOutput ( ctx , "DepFile" ) ) .
Flag ( "Flag" ) .
FlagWithArg ( "FlagWithArg=" , "arg" ) .
FlagWithDepFile ( "FlagWithDepFile=" , PathForOutput ( ctx , "depfile" ) ) .
FlagWithInput ( "FlagWithInput=" , PathForSource ( ctx , "input" ) ) .
FlagWithOutput ( "FlagWithOutput=" , PathForOutput ( ctx , "output" ) ) .
Implicit ( PathForSource ( ctx , "Implicit" ) ) .
ImplicitDepFile ( PathForOutput ( ctx , "ImplicitDepFile" ) ) .
ImplicitOutput ( PathForOutput ( ctx , "ImplicitOutput" ) ) .
Input ( PathForSource ( ctx , "Input" ) ) .
Output ( PathForOutput ( ctx , "Output" ) ) .
2020-02-22 08:55:19 +08:00
OrderOnly ( PathForSource ( ctx , "OrderOnly" ) ) .
2020-09-23 12:30:02 +08:00
SymlinkOutput ( PathForOutput ( ctx , "SymlinkOutput" ) ) .
ImplicitSymlinkOutput ( PathForOutput ( ctx , "ImplicitSymlinkOutput" ) ) .
2019-04-13 02:11:38 +08:00
Text ( "Text" ) .
Tool ( PathForSource ( ctx , "Tool" ) )
rule . Command ( ) .
Text ( "command2" ) .
DepFile ( PathForOutput ( ctx , "depfile2" ) ) .
Input ( PathForSource ( ctx , "input2" ) ) .
Output ( PathForOutput ( ctx , "output2" ) ) .
2020-02-22 08:55:19 +08:00
OrderOnlys ( PathsForSource ( ctx , [ ] string { "OrderOnlys" } ) ) .
2019-04-13 02:11:38 +08:00
Tool ( PathForSource ( ctx , "tool2" ) )
// Test updates to the first command after the second command has been started
cmd . Text ( "after command2" )
// Test updating a command when the previous update did not replace the cmd variable
cmd . Text ( "old cmd" )
// Test a command that uses the output of a previous command as an input
rule . Command ( ) .
Text ( "command3" ) .
Input ( PathForSource ( ctx , "input3" ) ) .
Input ( PathForOutput ( ctx , "output2" ) ) .
Output ( PathForOutput ( ctx , "output3" ) )
2019-01-31 09:32:39 +08:00
}
2019-03-30 06:33:06 +08:00
2019-02-16 02:39:37 +08:00
wantInputs := PathsForSource ( ctx , [ ] string { "Implicit" , "Input" , "input" , "input2" , "input3" } )
2020-09-23 12:30:02 +08:00
wantOutputs := PathsForOutput ( ctx , [ ] string { "ImplicitOutput" , "ImplicitSymlinkOutput" , "Output" , "SymlinkOutput" , "output" , "output2" , "output3" } )
2019-03-30 06:33:06 +08:00
wantDepFiles := PathsForOutput ( ctx , [ ] string { "DepFile" , "depfile" , "ImplicitDepFile" , "depfile2" } )
2019-02-16 02:39:37 +08:00
wantTools := PathsForSource ( ctx , [ ] string { "Tool" , "tool2" } )
2020-02-22 08:55:19 +08:00
wantOrderOnlys := PathsForSource ( ctx , [ ] string { "OrderOnly" , "OrderOnlys" } )
2020-09-23 12:30:02 +08:00
wantSymlinkOutputs := PathsForOutput ( ctx , [ ] string { "ImplicitSymlinkOutput" , "SymlinkOutput" } )
2019-01-31 09:32:39 +08:00
2019-04-13 02:11:38 +08:00
t . Run ( "normal" , func ( t * testing . T ) {
rule := NewRuleBuilder ( )
addCommands ( rule )
2019-03-30 06:33:06 +08:00
2019-04-13 02:11:38 +08:00
wantCommands := [ ] string {
2020-09-23 12:30:02 +08:00
"out/DepFile Flag FlagWithArg=arg FlagWithDepFile=out/depfile FlagWithInput=input FlagWithOutput=out/output Input out/Output out/SymlinkOutput Text Tool after command2 old cmd" ,
2019-04-13 02:11:38 +08:00
"command2 out/depfile2 input2 out/output2 tool2" ,
"command3 input3 out/output2 out/output3" ,
}
2019-03-30 06:33:06 +08:00
2019-04-13 02:11:38 +08:00
wantDepMergerCommand := "out/host/" + ctx . Config ( ) . PrebuiltOS ( ) + "/bin/dep_fixer out/DepFile out/depfile out/ImplicitDepFile out/depfile2"
if g , w := rule . Commands ( ) , wantCommands ; ! reflect . DeepEqual ( g , w ) {
t . Errorf ( "\nwant rule.Commands() = %#v\n got %#v" , w , g )
}
if g , w := rule . Inputs ( ) , wantInputs ; ! reflect . DeepEqual ( w , g ) {
t . Errorf ( "\nwant rule.Inputs() = %#v\n got %#v" , w , g )
}
if g , w := rule . Outputs ( ) , wantOutputs ; ! reflect . DeepEqual ( w , g ) {
t . Errorf ( "\nwant rule.Outputs() = %#v\n got %#v" , w , g )
}
2020-09-23 12:30:02 +08:00
if g , w := rule . SymlinkOutputs ( ) , wantSymlinkOutputs ; ! reflect . DeepEqual ( w , g ) {
t . Errorf ( "\nwant rule.SymlinkOutputs() = %#v\n got %#v" , w , g )
}
2019-04-13 02:11:38 +08:00
if g , w := rule . DepFiles ( ) , wantDepFiles ; ! reflect . DeepEqual ( w , g ) {
t . Errorf ( "\nwant rule.DepFiles() = %#v\n got %#v" , w , g )
}
if g , w := rule . Tools ( ) , wantTools ; ! reflect . DeepEqual ( w , g ) {
t . Errorf ( "\nwant rule.Tools() = %#v\n got %#v" , w , g )
}
2020-02-22 08:55:19 +08:00
if g , w := rule . OrderOnlys ( ) , wantOrderOnlys ; ! reflect . DeepEqual ( w , g ) {
t . Errorf ( "\nwant rule.OrderOnlys() = %#v\n got %#v" , w , g )
}
2019-04-13 02:11:38 +08:00
if g , w := rule . depFileMergerCmd ( ctx , rule . DepFiles ( ) ) . String ( ) , wantDepMergerCommand ; g != w {
t . Errorf ( "\nwant rule.depFileMergerCmd() = %#v\n got %#v" , w , g )
}
} )
t . Run ( "sbox" , func ( t * testing . T ) {
rule := NewRuleBuilder ( ) . Sbox ( PathForOutput ( ctx ) )
addCommands ( rule )
wantCommands := [ ] string {
2020-09-23 12:30:02 +08:00
"__SBOX_OUT_DIR__/DepFile Flag FlagWithArg=arg FlagWithDepFile=__SBOX_OUT_DIR__/depfile FlagWithInput=input FlagWithOutput=__SBOX_OUT_DIR__/output Input __SBOX_OUT_DIR__/Output __SBOX_OUT_DIR__/SymlinkOutput Text Tool after command2 old cmd" ,
2019-04-13 02:11:38 +08:00
"command2 __SBOX_OUT_DIR__/depfile2 input2 __SBOX_OUT_DIR__/output2 tool2" ,
"command3 input3 __SBOX_OUT_DIR__/output2 __SBOX_OUT_DIR__/output3" ,
}
wantDepMergerCommand := "out/host/" + ctx . Config ( ) . PrebuiltOS ( ) + "/bin/dep_fixer __SBOX_OUT_DIR__/DepFile __SBOX_OUT_DIR__/depfile __SBOX_OUT_DIR__/ImplicitDepFile __SBOX_OUT_DIR__/depfile2"
if g , w := rule . Commands ( ) , wantCommands ; ! reflect . DeepEqual ( g , w ) {
t . Errorf ( "\nwant rule.Commands() = %#v\n got %#v" , w , g )
}
if g , w := rule . Inputs ( ) , wantInputs ; ! reflect . DeepEqual ( w , g ) {
t . Errorf ( "\nwant rule.Inputs() = %#v\n got %#v" , w , g )
}
if g , w := rule . Outputs ( ) , wantOutputs ; ! reflect . DeepEqual ( w , g ) {
t . Errorf ( "\nwant rule.Outputs() = %#v\n got %#v" , w , g )
}
if g , w := rule . DepFiles ( ) , wantDepFiles ; ! reflect . DeepEqual ( w , g ) {
t . Errorf ( "\nwant rule.DepFiles() = %#v\n got %#v" , w , g )
}
if g , w := rule . Tools ( ) , wantTools ; ! reflect . DeepEqual ( w , g ) {
t . Errorf ( "\nwant rule.Tools() = %#v\n got %#v" , w , g )
}
2020-02-22 08:55:19 +08:00
if g , w := rule . OrderOnlys ( ) , wantOrderOnlys ; ! reflect . DeepEqual ( w , g ) {
t . Errorf ( "\nwant rule.OrderOnlys() = %#v\n got %#v" , w , g )
}
2019-04-13 02:11:38 +08:00
if g , w := rule . depFileMergerCmd ( ctx , rule . DepFiles ( ) ) . String ( ) , wantDepMergerCommand ; g != w {
t . Errorf ( "\nwant rule.depFileMergerCmd() = %#v\n got %#v" , w , g )
}
} )
2019-01-31 09:32:39 +08:00
}
func testRuleBuilderFactory ( ) Module {
module := & testRuleBuilderModule { }
module . AddProperties ( & module . properties )
InitAndroidModule ( module )
return module
}
type testRuleBuilderModule struct {
ModuleBase
properties struct {
2020-11-14 08:23:53 +08:00
Srcs [ ] string
2019-04-13 02:11:38 +08:00
Restat bool
Sbox bool
2019-01-31 09:32:39 +08:00
}
}
func ( t * testRuleBuilderModule ) GenerateAndroidBuildActions ( ctx ModuleContext ) {
2020-11-14 08:23:53 +08:00
in := PathsForSource ( ctx , t . properties . Srcs )
2019-01-31 09:32:39 +08:00
out := PathForModuleOut ( ctx , ctx . ModuleName ( ) )
2019-04-13 02:11:38 +08:00
outDep := PathForModuleOut ( ctx , ctx . ModuleName ( ) + ".d" )
outDir := PathForModuleOut ( ctx )
2019-01-31 09:32:39 +08:00
2019-04-13 02:11:38 +08:00
testRuleBuilder_Build ( ctx , in , out , outDep , outDir , t . properties . Restat , t . properties . Sbox )
2019-02-02 08:41:11 +08:00
}
type testRuleBuilderSingleton struct { }
func testRuleBuilderSingletonFactory ( ) Singleton {
return & testRuleBuilderSingleton { }
}
func ( t * testRuleBuilderSingleton ) GenerateBuildActions ( ctx SingletonContext ) {
in := PathForSource ( ctx , "bar" )
out := PathForOutput ( ctx , "baz" )
2019-04-13 02:11:38 +08:00
outDep := PathForOutput ( ctx , "baz.d" )
outDir := PathForOutput ( ctx )
2020-11-14 08:23:53 +08:00
testRuleBuilder_Build ( ctx , Paths { in } , out , outDep , outDir , true , false )
2019-02-02 08:41:11 +08:00
}
2020-11-14 08:23:53 +08:00
func testRuleBuilder_Build ( ctx BuilderContext , in Paths , out , outDep , outDir WritablePath , restat , sbox bool ) {
2019-02-02 08:42:32 +08:00
rule := NewRuleBuilder ( )
2019-02-02 08:41:11 +08:00
2019-04-13 02:11:38 +08:00
if sbox {
rule . Sbox ( outDir )
}
2020-11-14 08:23:53 +08:00
rule . Command ( ) . Tool ( PathForSource ( ctx , "cp" ) ) . Inputs ( in ) . Output ( out ) . ImplicitDepFile ( outDep )
2019-01-31 09:32:39 +08:00
2019-04-13 02:11:38 +08:00
if restat {
rule . Restat ( )
}
2019-02-26 06:56:01 +08:00
2019-01-31 09:32:39 +08:00
rule . Build ( pctx , ctx , "rule" , "desc" )
}
func TestRuleBuilder_Build ( t * testing . T ) {
2019-12-14 12:41:13 +08:00
fs := map [ string ] [ ] byte {
"bar" : nil ,
"cp" : nil ,
}
2019-01-31 09:32:39 +08:00
bp := `
rule_builder_test {
name : "foo" ,
2020-11-14 08:23:53 +08:00
srcs : [ "bar" ] ,
2019-04-13 02:11:38 +08:00
restat : true ,
}
rule_builder_test {
name : "foo_sbox" ,
2020-11-14 08:23:53 +08:00
srcs : [ "bar" ] ,
2019-04-13 02:11:38 +08:00
sbox : true ,
2019-01-31 09:32:39 +08:00
}
`
2019-12-14 12:41:13 +08:00
config := TestConfig ( buildDir , nil , bp , fs )
2020-10-30 08:09:13 +08:00
ctx := NewTestContext ( config )
2019-11-23 07:25:03 +08:00
ctx . RegisterModuleType ( "rule_builder_test" , testRuleBuilderFactory )
ctx . RegisterSingletonType ( "rule_builder_test" , testRuleBuilderSingletonFactory )
2020-10-30 08:09:13 +08:00
ctx . Register ( )
2019-01-31 09:32:39 +08:00
_ , errs := ctx . ParseFileList ( "." , [ ] string { "Android.bp" } )
FailIfErrored ( t , errs )
_ , errs = ctx . PrepareBuildActions ( config )
FailIfErrored ( t , errs )
2019-04-13 02:11:38 +08:00
check := func ( t * testing . T , params TestingBuildParams , wantCommand , wantOutput , wantDepfile string , wantRestat bool , extraCmdDeps [ ] string ) {
2019-08-30 05:47:40 +08:00
t . Helper ( )
2020-11-14 08:23:53 +08:00
command := params . RuleParams . Command
re := regexp . MustCompile ( " (# hash of input list:|--input-hash) [a-z0-9]*" )
command = re . ReplaceAllLiteralString ( command , "" )
if command != wantCommand {
2019-04-13 02:11:38 +08:00
t . Errorf ( "\nwant RuleParams.Command = %q\n got %q" , wantCommand , params . RuleParams . Command )
}
wantDeps := append ( [ ] string { "cp" } , extraCmdDeps ... )
if ! reflect . DeepEqual ( params . RuleParams . CommandDeps , wantDeps ) {
t . Errorf ( "\nwant RuleParams.CommandDeps = %q\n got %q" , wantDeps , params . RuleParams . CommandDeps )
}
if params . RuleParams . Restat != wantRestat {
t . Errorf ( "want RuleParams.Restat = %v, got %v" , wantRestat , params . RuleParams . Restat )
2019-02-26 06:54:28 +08:00
}
2019-01-31 09:32:39 +08:00
2019-02-26 06:54:28 +08:00
if len ( params . Implicits ) != 1 || params . Implicits [ 0 ] . String ( ) != "bar" {
t . Errorf ( "want Implicits = [%q], got %q" , "bar" , params . Implicits . Strings ( ) )
}
2019-01-31 09:32:39 +08:00
2019-03-30 06:33:06 +08:00
if params . Output . String ( ) != wantOutput {
t . Errorf ( "want Output = %q, got %q" , wantOutput , params . Output )
2019-02-26 06:54:28 +08:00
}
2019-02-26 06:56:01 +08:00
2019-04-13 02:11:38 +08:00
if len ( params . ImplicitOutputs ) != 0 {
t . Errorf ( "want ImplicitOutputs = [], got %q" , params . ImplicitOutputs . Strings ( ) )
}
if params . Depfile . String ( ) != wantDepfile {
t . Errorf ( "want Depfile = %q, got %q" , wantDepfile , params . Depfile )
}
if params . Deps != blueprint . DepsGCC {
t . Errorf ( "want Deps = %q, got %q" , blueprint . DepsGCC , params . Deps )
2019-02-26 06:56:01 +08:00
}
2019-01-31 09:32:39 +08:00
}
2019-02-26 06:54:28 +08:00
t . Run ( "module" , func ( t * testing . T ) {
2019-04-13 02:11:38 +08:00
outFile := filepath . Join ( buildDir , ".intermediates" , "foo" , "foo" )
2019-02-26 06:54:28 +08:00
check ( t , ctx . ModuleForTests ( "foo" , "" ) . Rule ( "rule" ) ,
2019-04-13 02:11:38 +08:00
"cp bar " + outFile ,
outFile , outFile + ".d" , true , nil )
} )
t . Run ( "sbox" , func ( t * testing . T ) {
outDir := filepath . Join ( buildDir , ".intermediates" , "foo_sbox" )
outFile := filepath . Join ( outDir , "foo_sbox" )
2019-08-30 05:47:40 +08:00
depFile := filepath . Join ( outDir , "foo_sbox.d" )
2019-04-13 02:11:38 +08:00
sbox := filepath . Join ( buildDir , "host" , config . PrebuiltOS ( ) , "bin/sbox" )
sandboxPath := shared . TempDirForOutDir ( buildDir )
2019-08-30 05:47:40 +08:00
cmd := sbox + ` -c 'cp bar __SBOX_OUT_DIR__/foo_sbox' --sandbox-path ` + sandboxPath + " --output-root " + outDir + " --depfile-out " + depFile + " __SBOX_OUT_DIR__/foo_sbox"
2019-04-13 02:11:38 +08:00
check ( t , ctx . ModuleForTests ( "foo_sbox" , "" ) . Rule ( "rule" ) ,
2019-08-30 05:47:40 +08:00
cmd , outFile , depFile , false , [ ] string { sbox } )
2019-02-26 06:54:28 +08:00
} )
t . Run ( "singleton" , func ( t * testing . T ) {
2019-04-13 02:11:38 +08:00
outFile := filepath . Join ( buildDir , "baz" )
2019-02-26 06:54:28 +08:00
check ( t , ctx . SingletonForTests ( "rule_builder_test" ) . Rule ( "rule" ) ,
2019-04-13 02:11:38 +08:00
"cp bar " + outFile , outFile , outFile + ".d" , true , nil )
2019-02-26 06:54:28 +08:00
} )
2019-01-31 09:32:39 +08:00
}
2019-07-12 01:59:15 +08:00
func Test_ninjaEscapeExceptForSpans ( t * testing . T ) {
type args struct {
s string
spans [ ] [ 2 ] int
}
tests := [ ] struct {
name string
args args
want string
} {
{
name : "empty" ,
args : args {
s : "" ,
} ,
want : "" ,
} ,
{
name : "unescape none" ,
args : args {
s : "$abc" ,
} ,
want : "$$abc" ,
} ,
{
name : "unescape all" ,
args : args {
s : "$abc" ,
spans : [ ] [ 2 ] int { { 0 , 4 } } ,
} ,
want : "$abc" ,
} ,
{
name : "unescape first" ,
args : args {
s : "$abc$" ,
spans : [ ] [ 2 ] int { { 0 , 1 } } ,
} ,
want : "$abc$$" ,
} ,
{
name : "unescape last" ,
args : args {
s : "$abc$" ,
spans : [ ] [ 2 ] int { { 4 , 5 } } ,
} ,
want : "$$abc$" ,
} ,
{
name : "unescape middle" ,
args : args {
s : "$a$b$c$" ,
spans : [ ] [ 2 ] int { { 2 , 5 } } ,
} ,
want : "$$a$b$c$$" ,
} ,
{
name : "unescape multiple" ,
args : args {
s : "$a$b$c$" ,
spans : [ ] [ 2 ] int { { 2 , 3 } , { 4 , 5 } } ,
} ,
want : "$$a$b$c$$" ,
} ,
}
for _ , tt := range tests {
t . Run ( tt . name , func ( t * testing . T ) {
if got := ninjaEscapeExceptForSpans ( tt . args . s , tt . args . spans ) ; got != tt . want {
t . Errorf ( "ninjaEscapeExceptForSpans() = %v, want %v" , got , tt . want )
}
} )
}
}
2020-11-14 08:23:53 +08:00
func TestRuleBuilderHashInputs ( t * testing . T ) {
// The basic idea here is to verify that the command (in the case of a
// non-sbox rule) or the sbox textproto manifest contain a hash of the
// inputs.
// By including a hash of the inputs, we cause the rule to re-run if
// the list of inputs changes because the command line or a dependency
// changes.
bp := `
rule_builder_test {
name : "hash0" ,
srcs : [ "in1.txt" , "in2.txt" ] ,
}
rule_builder_test {
name : "hash0_sbox" ,
srcs : [ "in1.txt" , "in2.txt" ] ,
sbox : true ,
}
rule_builder_test {
name : "hash1" ,
srcs : [ "in1.txt" , "in2.txt" , "in3.txt" ] ,
}
rule_builder_test {
name : "hash1_sbox" ,
srcs : [ "in1.txt" , "in2.txt" , "in3.txt" ] ,
sbox : true ,
}
`
testcases := [ ] struct {
name string
expectedHash string
} {
{
name : "hash0" ,
// sha256 value obtained from: echo -en 'in1.txt\nin2.txt' | sha256sum
expectedHash : "18da75b9b1cc74b09e365b4ca2e321b5d618f438cc632b387ad9dc2ab4b20e9d" ,
} ,
{
name : "hash1" ,
// sha256 value obtained from: echo -en 'in1.txt\nin2.txt\nin3.txt' | sha256sum
expectedHash : "a38d432a4b19df93140e1f1fe26c97ff0387dae01fe506412b47208f0595fb45" ,
} ,
}
config := TestConfig ( buildDir , nil , bp , nil )
ctx := NewTestContext ( config )
ctx . RegisterModuleType ( "rule_builder_test" , testRuleBuilderFactory )
ctx . Register ( )
_ , errs := ctx . ParseFileList ( "." , [ ] string { "Android.bp" } )
FailIfErrored ( t , errs )
_ , errs = ctx . PrepareBuildActions ( config )
FailIfErrored ( t , errs )
for _ , test := range testcases {
t . Run ( test . name , func ( t * testing . T ) {
t . Run ( "sbox" , func ( t * testing . T ) {
gen := ctx . ModuleForTests ( test . name + "_sbox" , "" )
command := gen . Output ( test . name + "_sbox" ) . RuleParams . Command
if g , w := command , " --input-hash " + test . expectedHash ; ! strings . Contains ( g , w ) {
t . Errorf ( "Expected command line to end with %q, got %q" , w , g )
}
} )
t . Run ( "" , func ( t * testing . T ) {
gen := ctx . ModuleForTests ( test . name + "" , "" )
command := gen . Output ( test . name ) . RuleParams . Command
if g , w := command , " # hash of input list: " + test . expectedHash ; ! strings . HasSuffix ( g , w ) {
t . Errorf ( "Expected command line to end with %q, got %q" , w , g )
}
} )
} )
}
}