Ensure directories in the Soong output dir have appropriate permissions, so that 'm clean' works.

In particular, Bazel sometimes creates directories whose files cannot be deleted.

Test: m clean used to fail after b build //system/timezone/apex:com.android.tzdata, but now it works
Test: Added integration test which fails without this change

Change-Id: I08c8feed21c31ec187fe40be513f7eb4865c8ac3
This commit is contained in:
Rupert Shuttleworth 2021-08-11 09:20:27 -04:00
parent c0a671fc80
commit 755ceb01e3
3 changed files with 64 additions and 0 deletions

View file

@ -6,3 +6,4 @@ TOP="$(readlink -f "$(dirname "$0")"/../../..)"
"$TOP/build/soong/tests/bootstrap_test.sh"
"$TOP/build/soong/tests/mixed_mode_test.sh"
"$TOP/build/soong/tests/bp2build_bazel_test.sh"
"$TOP/build/soong/tests/soong_test.sh"

22
tests/soong_test.sh Executable file
View file

@ -0,0 +1,22 @@
#!/bin/bash -eu
set -o pipefail
# Tests of Soong functionality
source "$(dirname "$0")/lib.sh"
function test_m_clean_works {
setup
# Create a directory with files that cannot be removed
mkdir -p out/bad_directory_permissions
touch out/bad_directory_permissions/unremovable_file
# File permissions are fine but directory permissions are bad
chmod a+rwx out/bad_directory_permissions/unremovable_file
chmod a-rwx out/bad_directory_permissions
run_soong clean
}
test_m_clean_works

View file

@ -17,6 +17,7 @@ package build
import (
"bytes"
"fmt"
"io/fs"
"io/ioutil"
"os"
"path/filepath"
@ -46,9 +47,49 @@ func removeGlobs(ctx Context, globs ...string) {
}
}
// Based on https://stackoverflow.com/questions/28969455/how-to-properly-instantiate-os-filemode
// Because Go doesn't provide a nice way to set bits on a filemode
const (
FILEMODE_READ = 04
FILEMODE_WRITE = 02
FILEMODE_EXECUTE = 01
FILEMODE_USER_SHIFT = 6
FILEMODE_USER_READ = FILEMODE_READ << FILEMODE_USER_SHIFT
FILEMODE_USER_WRITE = FILEMODE_WRITE << FILEMODE_USER_SHIFT
FILEMODE_USER_EXECUTE = FILEMODE_EXECUTE << FILEMODE_USER_SHIFT
)
// Ensures that files and directories in the out dir can be deleted.
// For example, Bazen can generate output directories where the write bit isn't set, causing 'm' clean' to fail.
func ensureOutDirRemovable(ctx Context, config Config) {
err := filepath.WalkDir(config.OutDir(), func(path string, d fs.DirEntry, err error) error {
if err != nil {
return err
}
if d.IsDir() {
info, err := d.Info()
if err != nil {
return err
}
// Equivalent to running chmod u+rwx on each directory
newMode := info.Mode() | FILEMODE_USER_READ | FILEMODE_USER_WRITE | FILEMODE_USER_EXECUTE
if err := os.Chmod(path, newMode); err != nil {
return err
}
}
// Continue walking the out dir...
return nil
})
if err != nil && !os.IsNotExist(err) {
// Display the error, but don't crash.
ctx.Println(err.Error())
}
}
// Remove everything under the out directory. Don't remove the out directory
// itself in case it's a symlink.
func clean(ctx Context, config Config) {
ensureOutDirRemovable(ctx, config)
removeGlobs(ctx, filepath.Join(config.OutDir(), "*"))
ctx.Println("Entire build directory removed.")
}