Handle WriteFile and SourceSymlinkManifest actions. am: 1da064c1e6
Original change: https://android-review.googlesource.com/c/platform/build/soong/+/2121035 Change-Id: I696b7b3836f2b8a7b68facd0bd75083fcdc5a59e Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
@@ -34,6 +34,14 @@ import (
|
|||||||
"android/soong/bazel"
|
"android/soong/bazel"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
writeBazelFile = pctx.AndroidStaticRule("bazelWriteFileRule", blueprint.RuleParams{
|
||||||
|
Command: `sed "s/\\\\n/\n/g" ${out}.rsp >${out}`,
|
||||||
|
Rspfile: "${out}.rsp",
|
||||||
|
RspfileContent: "${content}",
|
||||||
|
}, "content")
|
||||||
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
RegisterMixedBuildsMutator(InitRegistrationContext)
|
RegisterMixedBuildsMutator(InitRegistrationContext)
|
||||||
}
|
}
|
||||||
@@ -778,7 +786,7 @@ func (context *bazelContext) InvokeBazel(config Config) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extraFlags := append([]string{"--output=jsonproto"}, coverageFlags...)
|
extraFlags := append([]string{"--output=jsonproto", "--include_file_write_contents"}, coverageFlags...)
|
||||||
|
|
||||||
aqueryOutput, _, err = context.issueBazelCommand(
|
aqueryOutput, _, err = context.issueBazelCommand(
|
||||||
context.paths,
|
context.paths,
|
||||||
@@ -874,13 +882,37 @@ func (c *bazelSingleton) GenerateBuildActions(ctx SingletonContext) {
|
|||||||
executionRoot := path.Join(ctx.Config().BazelContext.OutputBase(), "execroot", "__main__")
|
executionRoot := path.Join(ctx.Config().BazelContext.OutputBase(), "execroot", "__main__")
|
||||||
bazelOutDir := path.Join(executionRoot, "bazel-out")
|
bazelOutDir := path.Join(executionRoot, "bazel-out")
|
||||||
for index, buildStatement := range ctx.Config().BazelContext.BuildStatementsToRegister() {
|
for index, buildStatement := range ctx.Config().BazelContext.BuildStatementsToRegister() {
|
||||||
if len(buildStatement.Command) < 1 {
|
if len(buildStatement.Command) > 0 {
|
||||||
panic(fmt.Sprintf("unhandled build statement: %v", buildStatement))
|
|
||||||
}
|
|
||||||
rule := NewRuleBuilder(pctx, ctx)
|
rule := NewRuleBuilder(pctx, ctx)
|
||||||
createCommand(rule.Command(), buildStatement, executionRoot, bazelOutDir, ctx)
|
createCommand(rule.Command(), buildStatement, executionRoot, bazelOutDir, ctx)
|
||||||
desc := fmt.Sprintf("%s: %s", buildStatement.Mnemonic, buildStatement.OutputPaths)
|
desc := fmt.Sprintf("%s: %s", buildStatement.Mnemonic, buildStatement.OutputPaths)
|
||||||
rule.Build(fmt.Sprintf("bazel %d", index), desc)
|
rule.Build(fmt.Sprintf("bazel %d", index), desc)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// Certain actions returned by aquery (for instance FileWrite) do not contain a command
|
||||||
|
// and thus require special treatment. If BuildStatement were an interface implementing
|
||||||
|
// buildRule(ctx) function, the code here would just call it.
|
||||||
|
// Unfortunately, the BuildStatement is defined in
|
||||||
|
// the 'bazel' package, which cannot depend on 'android' package where ctx is defined,
|
||||||
|
// because this would cause circular dependency. So, until we move aquery processing
|
||||||
|
// to the 'android' package, we need to handle special cases here.
|
||||||
|
if buildStatement.Mnemonic == "FileWrite" || buildStatement.Mnemonic == "SourceSymlinkManifest" {
|
||||||
|
// Pass file contents as the value of the rule's "content" argument.
|
||||||
|
// Escape newlines and $ in the contents (the action "writeBazelFile" restores "\\n"
|
||||||
|
// back to the newline, and Ninja reads $$ as $.
|
||||||
|
escaped := strings.ReplaceAll(strings.ReplaceAll(buildStatement.FileContents, "\n", "\\n"),
|
||||||
|
"$", "$$")
|
||||||
|
ctx.Build(pctx, BuildParams{
|
||||||
|
Rule: writeBazelFile,
|
||||||
|
Output: PathForBazelOut(ctx, buildStatement.OutputPaths[0]),
|
||||||
|
Description: fmt.Sprintf("%s %s", buildStatement.Mnemonic, buildStatement.OutputPaths[0]),
|
||||||
|
Args: map[string]string{
|
||||||
|
"content": escaped,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
panic(fmt.Sprintf("unhandled build statement: %v", buildStatement))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -83,6 +83,7 @@ type action struct {
|
|||||||
OutputIds []artifactId
|
OutputIds []artifactId
|
||||||
TemplateContent string
|
TemplateContent string
|
||||||
Substitutions []KeyValuePair
|
Substitutions []KeyValuePair
|
||||||
|
FileContents string
|
||||||
}
|
}
|
||||||
|
|
||||||
// actionGraphContainer contains relevant portions of Bazel's aquery proto, ActionGraphContainer.
|
// actionGraphContainer contains relevant portions of Bazel's aquery proto, ActionGraphContainer.
|
||||||
@@ -110,6 +111,7 @@ type BuildStatement struct {
|
|||||||
// input path string, but not both.
|
// input path string, but not both.
|
||||||
InputDepsetHashes []string
|
InputDepsetHashes []string
|
||||||
InputPaths []string
|
InputPaths []string
|
||||||
|
FileContents string
|
||||||
}
|
}
|
||||||
|
|
||||||
// A helper type for aquery processing which facilitates retrieval of path IDs from their
|
// A helper type for aquery processing which facilitates retrieval of path IDs from their
|
||||||
@@ -346,12 +348,14 @@ func AqueryBuildStatements(aqueryJsonProto []byte) ([]BuildStatement, []AqueryDe
|
|||||||
}
|
}
|
||||||
|
|
||||||
var buildStatement BuildStatement
|
var buildStatement BuildStatement
|
||||||
if isSymlinkAction(actionEntry) {
|
if actionEntry.isSymlinkAction() {
|
||||||
buildStatement, err = aqueryHandler.symlinkActionBuildStatement(actionEntry)
|
buildStatement, err = aqueryHandler.symlinkActionBuildStatement(actionEntry)
|
||||||
} else if isTemplateExpandAction(actionEntry) && len(actionEntry.Arguments) < 1 {
|
} else if actionEntry.isTemplateExpandAction() && len(actionEntry.Arguments) < 1 {
|
||||||
buildStatement, err = aqueryHandler.templateExpandActionBuildStatement(actionEntry)
|
buildStatement, err = aqueryHandler.templateExpandActionBuildStatement(actionEntry)
|
||||||
} else if isPythonZipperAction(actionEntry) {
|
} else if actionEntry.isPythonZipperAction() {
|
||||||
buildStatement, err = aqueryHandler.pythonZipperActionBuildStatement(actionEntry, buildStatements)
|
buildStatement, err = aqueryHandler.pythonZipperActionBuildStatement(actionEntry, buildStatements)
|
||||||
|
} else if actionEntry.isFileWriteAction() {
|
||||||
|
buildStatement, err = aqueryHandler.fileWriteActionBuildStatement(actionEntry)
|
||||||
} else if len(actionEntry.Arguments) < 1 {
|
} else if len(actionEntry.Arguments) < 1 {
|
||||||
return nil, nil, fmt.Errorf("received action with no command: [%s]", actionEntry.Mnemonic)
|
return nil, nil, fmt.Errorf("received action with no command: [%s]", actionEntry.Mnemonic)
|
||||||
} else {
|
} else {
|
||||||
@@ -532,6 +536,25 @@ func (a *aqueryArtifactHandler) templateExpandActionBuildStatement(actionEntry a
|
|||||||
return buildStatement, nil
|
return buildStatement, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *aqueryArtifactHandler) fileWriteActionBuildStatement(actionEntry action) (BuildStatement, error) {
|
||||||
|
outputPaths, _, err := a.getOutputPaths(actionEntry)
|
||||||
|
var depsetHashes []string
|
||||||
|
if err == nil {
|
||||||
|
depsetHashes, err = a.depsetContentHashes(actionEntry.InputDepSetIds)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return BuildStatement{}, err
|
||||||
|
}
|
||||||
|
return BuildStatement{
|
||||||
|
Depfile: nil,
|
||||||
|
OutputPaths: outputPaths,
|
||||||
|
Env: actionEntry.EnvironmentVariables,
|
||||||
|
Mnemonic: actionEntry.Mnemonic,
|
||||||
|
InputDepsetHashes: depsetHashes,
|
||||||
|
FileContents: actionEntry.FileContents,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (a *aqueryArtifactHandler) symlinkActionBuildStatement(actionEntry action) (BuildStatement, error) {
|
func (a *aqueryArtifactHandler) symlinkActionBuildStatement(actionEntry action) (BuildStatement, error) {
|
||||||
outputPaths, depfile, err := a.getOutputPaths(actionEntry)
|
outputPaths, depfile, err := a.getOutputPaths(actionEntry)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -654,21 +677,25 @@ func addCommandForPyBinaryRunfilesDir(oldCommand string, zipFilePath string) str
|
|||||||
return oldCommand + " && " + command
|
return oldCommand + " && " + command
|
||||||
}
|
}
|
||||||
|
|
||||||
func isSymlinkAction(a action) bool {
|
func (a action) isSymlinkAction() bool {
|
||||||
return a.Mnemonic == "Symlink" || a.Mnemonic == "SolibSymlink" || a.Mnemonic == "ExecutableSymlink"
|
return a.Mnemonic == "Symlink" || a.Mnemonic == "SolibSymlink" || a.Mnemonic == "ExecutableSymlink"
|
||||||
}
|
}
|
||||||
|
|
||||||
func isTemplateExpandAction(a action) bool {
|
func (a action) isTemplateExpandAction() bool {
|
||||||
return a.Mnemonic == "TemplateExpand"
|
return a.Mnemonic == "TemplateExpand"
|
||||||
}
|
}
|
||||||
|
|
||||||
func isPythonZipperAction(a action) bool {
|
func (a action) isPythonZipperAction() bool {
|
||||||
return a.Mnemonic == "PythonZipper"
|
return a.Mnemonic == "PythonZipper"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a action) isFileWriteAction() bool {
|
||||||
|
return a.Mnemonic == "FileWrite" || a.Mnemonic == "SourceSymlinkManifest"
|
||||||
|
}
|
||||||
|
|
||||||
func shouldSkipAction(a action) bool {
|
func shouldSkipAction(a action) bool {
|
||||||
// TODO(b/180945121): Handle complex symlink actions.
|
// TODO(b/180945121): Handle complex symlink actions.
|
||||||
if a.Mnemonic == "SymlinkTree" || a.Mnemonic == "SourceSymlinkManifest" {
|
if a.Mnemonic == "SymlinkTree" {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
// Middleman actions are not handled like other actions; they are handled separately as a
|
// Middleman actions are not handled like other actions; they are handled separately as a
|
||||||
@@ -681,11 +708,6 @@ func shouldSkipAction(a action) bool {
|
|||||||
if a.Mnemonic == "Fail" {
|
if a.Mnemonic == "Fail" {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
// TODO(b/180946980): Handle FileWrite. The aquery proto currently contains no information
|
|
||||||
// about the contents that are written.
|
|
||||||
if a.Mnemonic == "FileWrite" {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if a.Mnemonic == "BaselineCoverage" {
|
if a.Mnemonic == "BaselineCoverage" {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@@ -1008,6 +1008,69 @@ func TestPythonZipperActionNoOutput(t *testing.T) {
|
|||||||
assertError(t, err, `Expect 1+ input and 1 output to python zipper action, got: input ["python_binary.py"], output []`)
|
assertError(t, err, `Expect 1+ input and 1 output to python zipper action, got: input ["python_binary.py"], output []`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestFileWrite(t *testing.T) {
|
||||||
|
const inputString = `
|
||||||
|
{
|
||||||
|
"artifacts": [
|
||||||
|
{ "id": 1, "pathFragmentId": 1 }],
|
||||||
|
"actions": [{
|
||||||
|
"targetId": 1,
|
||||||
|
"actionKey": "x",
|
||||||
|
"mnemonic": "FileWrite",
|
||||||
|
"configurationId": 1,
|
||||||
|
"outputIds": [1],
|
||||||
|
"primaryOutputId": 1,
|
||||||
|
"executionPlatform": "//build/bazel/platforms:linux_x86_64",
|
||||||
|
"fileContents": "file data\n"
|
||||||
|
}],
|
||||||
|
"pathFragments": [
|
||||||
|
{ "id": 1, "label": "foo.manifest" }]
|
||||||
|
}
|
||||||
|
`
|
||||||
|
actual, _, err := AqueryBuildStatements([]byte(inputString))
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Unexpected error %q", err)
|
||||||
|
}
|
||||||
|
assertBuildStatements(t, []BuildStatement{
|
||||||
|
{
|
||||||
|
OutputPaths: []string{"foo.manifest"},
|
||||||
|
Mnemonic: "FileWrite",
|
||||||
|
FileContents: "file data\n",
|
||||||
|
},
|
||||||
|
}, actual)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSourceSymlinkManifest(t *testing.T) {
|
||||||
|
const inputString = `
|
||||||
|
{
|
||||||
|
"artifacts": [
|
||||||
|
{ "id": 1, "pathFragmentId": 1 }],
|
||||||
|
"actions": [{
|
||||||
|
"targetId": 1,
|
||||||
|
"actionKey": "x",
|
||||||
|
"mnemonic": "SourceSymlinkManifest",
|
||||||
|
"configurationId": 1,
|
||||||
|
"outputIds": [1],
|
||||||
|
"primaryOutputId": 1,
|
||||||
|
"executionPlatform": "//build/bazel/platforms:linux_x86_64",
|
||||||
|
"fileContents": "symlink target\n"
|
||||||
|
}],
|
||||||
|
"pathFragments": [
|
||||||
|
{ "id": 1, "label": "foo.manifest" }]
|
||||||
|
}
|
||||||
|
`
|
||||||
|
actual, _, err := AqueryBuildStatements([]byte(inputString))
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Unexpected error %q", err)
|
||||||
|
}
|
||||||
|
assertBuildStatements(t, []BuildStatement{
|
||||||
|
{
|
||||||
|
OutputPaths: []string{"foo.manifest"},
|
||||||
|
Mnemonic: "SourceSymlinkManifest",
|
||||||
|
},
|
||||||
|
}, actual)
|
||||||
|
}
|
||||||
|
|
||||||
func assertError(t *testing.T, err error, expected string) {
|
func assertError(t *testing.T, err error, expected string) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
Reference in New Issue
Block a user