Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 755ceb01 authored by Rupert Shuttleworth's avatar Rupert Shuttleworth
Browse files

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
parent c0a671fc
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -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"

tests/soong_test.sh

0 → 100755
+22 −0
Original line number Diff line number Diff line
#!/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
+41 −0
Original line number Diff line number Diff line
@@ -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.")
}