Unverified Commit 354f80b8 authored by Emmanuel Garcia's avatar Emmanuel Garcia Committed by GitHub

Check and give execute permission to Gradle if needed (#46748)

parent 8b88c829
......@@ -46,14 +46,17 @@ class GradleUtils {
final Directory androidDir = project.android.hostAppGradleRoot;
// Update the project if needed.
// TODO(egarciad): https://github.com/flutter/flutter/issues/40460
migrateToR8(androidDir);
injectGradleWrapperIfNeeded(androidDir);
gradleUtils.migrateToR8(androidDir);
gradleUtils.injectGradleWrapperIfNeeded(androidDir);
final File gradle = androidDir.childFile(
platform.isWindows ? 'gradlew.bat' : 'gradlew',
);
if (gradle.existsSync()) {
printTrace('Using gradle from ${gradle.absolute.path}.');
// If the Gradle executable doesn't have execute permission,
// then attempt to set it.
_giveExecutePermissionIfNeeded(gradle);
return gradle.absolute.path;
}
throwToolExit(
......@@ -62,71 +65,69 @@ class GradleUtils {
);
return null;
}
}
/// Migrates the Android's [directory] to R8.
/// https://developer.android.com/studio/build/shrink-code
@visibleForTesting
void migrateToR8(Directory directory) {
final File gradleProperties = directory.childFile('gradle.properties');
if (!gradleProperties.existsSync()) {
throwToolExit(
'Expected file ${gradleProperties.path}. '
'Please ensure that this file exists or that ${gradleProperties.dirname} can be read.'
);
}
final String propertiesContent = gradleProperties.readAsStringSync();
if (propertiesContent.contains('android.enableR8')) {
printTrace('gradle.properties already sets `android.enableR8`');
return;
}
printTrace('set `android.enableR8=true` in gradle.properties');
try {
if (propertiesContent.isNotEmpty && !propertiesContent.endsWith('\n')) {
// Add a new line if the file doesn't end with a new line.
gradleProperties.writeAsStringSync('\n', mode: FileMode.append);
/// Migrates the Android's [directory] to R8.
/// https://developer.android.com/studio/build/shrink-code
@visibleForTesting
void migrateToR8(Directory directory) {
final File gradleProperties = directory.childFile('gradle.properties');
if (!gradleProperties.existsSync()) {
throwToolExit(
'Expected file ${gradleProperties.path}. '
'Please ensure that this file exists or that ${gradleProperties.dirname} can be read.'
);
}
final String propertiesContent = gradleProperties.readAsStringSync();
if (propertiesContent.contains('android.enableR8')) {
printTrace('gradle.properties already sets `android.enableR8`');
return;
}
printTrace('set `android.enableR8=true` in gradle.properties');
try {
if (propertiesContent.isNotEmpty && !propertiesContent.endsWith('\n')) {
// Add a new line if the file doesn't end with a new line.
gradleProperties.writeAsStringSync('\n', mode: FileMode.append);
}
gradleProperties.writeAsStringSync('android.enableR8=true\n', mode: FileMode.append);
} on FileSystemException {
throwToolExit(
'The tool failed to add `android.enableR8=true` to ${gradleProperties.path}. '
'Please update the file manually and try this command again.'
);
}
gradleProperties.writeAsStringSync('android.enableR8=true\n', mode: FileMode.append);
} on FileSystemException {
throwToolExit(
'The tool failed to add `android.enableR8=true` to ${gradleProperties.path}. '
'Please update the file manually and try this command again.'
);
}
}
/// Injects the Gradle wrapper files if any of these files don't exist in [directory].
void injectGradleWrapperIfNeeded(Directory directory) {
copyDirectorySync(
cache.getArtifactDirectory('gradle_wrapper'),
directory,
shouldCopyFile: (File sourceFile, File destinationFile) {
// Don't override the existing files in the project.
return !destinationFile.existsSync();
},
onFileCopied: (File sourceFile, File destinationFile) {
final String modes = sourceFile.statSync().modeString();
if (modes != null && modes.contains('x')) {
os.makeExecutable(destinationFile);
}
},
);
// Add the `gradle-wrapper.properties` file if it doesn't exist.
final File propertiesFile = directory.childFile(
fs.path.join('gradle', 'wrapper', 'gradle-wrapper.properties'));
if (!propertiesFile.existsSync()) {
final String gradleVersion = getGradleVersionForAndroidPlugin(directory);
propertiesFile.writeAsStringSync('''
/// Injects the Gradle wrapper files if any of these files don't exist in [directory].
void injectGradleWrapperIfNeeded(Directory directory) {
copyDirectorySync(
cache.getArtifactDirectory('gradle_wrapper'),
directory,
shouldCopyFile: (File sourceFile, File destinationFile) {
// Don't override the existing files in the project.
return !destinationFile.existsSync();
},
onFileCopied: (File sourceFile, File destinationFile) {
if (_hasExecutePermission(sourceFile)) {
_giveExecutePermissionIfNeeded(destinationFile);
}
},
);
// Add the `gradle-wrapper.properties` file if it doesn't exist.
final File propertiesFile = directory.childFile(
fs.path.join('gradle', 'wrapper', 'gradle-wrapper.properties'));
if (!propertiesFile.existsSync()) {
final String gradleVersion = getGradleVersionForAndroidPlugin(directory);
propertiesFile.writeAsStringSync('''
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\\://services.gradle.org/distributions/gradle-$gradleVersion-all.zip
''', flush: true,
);
);
}
}
}
const String _defaultGradleVersion = '5.6.2';
final RegExp _androidPluginRegExp = RegExp('com\.android\.tools\.build\:gradle\:(\\d+\.\\d+\.\\d+\)');
......@@ -150,6 +151,24 @@ String getGradleVersionForAndroidPlugin(Directory directory) {
return getGradleVersionFor(androidPluginVersion);
}
const int _kExecPermissionMask = 0x49; // a+x
/// Returns [true] if [executable] has execute permission.
bool _hasExecutePermission(File executable) {
final FileStat stat = executable.statSync();
assert(stat.type != FileSystemEntityType.notFound);
printTrace('${executable.path} mode: ${stat.mode} ${stat.modeString()}.');
return stat.mode & _kExecPermissionMask == _kExecPermissionMask;
}
/// Gives execute permission to [executable] if it doesn't have it already.
void _giveExecutePermissionIfNeeded(File executable) {
if (!_hasExecutePermission(executable)) {
printTrace('Trying to give execute permission to ${executable.path}.');
os.makeExecutable(executable);
}
}
/// Returns true if [targetVersion] is within the range [min] and [max] inclusive.
bool _isWithinVersionRange(
String targetVersion, {
......
......@@ -904,16 +904,13 @@ class AndroidMavenArtifacts extends ArtifactSet {
Future<void> update() async {
final Directory tempDir =
fs.systemTempDirectory.createTempSync('flutter_gradle_wrapper.');
injectGradleWrapperIfNeeded(tempDir);
gradleUtils.injectGradleWrapperIfNeeded(tempDir);
final Status status = logger.startProgress('Downloading Android Maven dependencies...',
timeout: timeoutConfiguration.slowOperation);
final File gradle = tempDir.childFile(
platform.isWindows ? 'gradlew.bat' : 'gradlew',
);
assert(gradle.existsSync());
os.makeExecutable(gradle);
try {
final String gradleExecutable = gradle.absolute.path;
final String flutterSdk = escapePath(Cache.flutterRoot);
......
......@@ -630,7 +630,7 @@ class AndroidProject {
_overwriteFromTemplate(fs.path.join('module', 'android', 'host_app_common'), _editableHostAppDirectory);
_overwriteFromTemplate(fs.path.join('module', 'android', 'host_app_editable'), _editableHostAppDirectory);
_overwriteFromTemplate(fs.path.join('module', 'android', 'gradle'), _editableHostAppDirectory);
gradle.injectGradleWrapperIfNeeded(_editableHostAppDirectory);
gradle.gradleUtils.injectGradleWrapperIfNeeded(_editableHostAppDirectory);
gradle.writeLocalProperties(_editableHostAppDirectory.childFile('local.properties'));
await injectPlugins(parent);
}
......@@ -647,7 +647,7 @@ class AndroidProject {
featureFlags.isAndroidEmbeddingV2Enabled ? 'library_new_embedding' : 'library',
), ephemeralDirectory);
_overwriteFromTemplate(fs.path.join('module', 'android', 'gradle'), ephemeralDirectory);
gradle.injectGradleWrapperIfNeeded(ephemeralDirectory);
gradle.gradleUtils.injectGradleWrapperIfNeeded(ephemeralDirectory);
}
void _overwriteFromTemplate(String path, Directory target) {
......
......@@ -814,107 +814,6 @@ flutter:
});
});
group('migrateToR8', () {
MemoryFileSystem memoryFileSystem;
setUp(() {
memoryFileSystem = MemoryFileSystem();
});
testUsingContext('throws ToolExit if gradle.properties doesn\'t exist', () {
final Directory sampleAppAndroid = fs.directory('/sample-app/android');
sampleAppAndroid.createSync(recursive: true);
expect(() {
migrateToR8(sampleAppAndroid);
}, throwsToolExit(message: 'Expected file ${sampleAppAndroid.path}'));
}, overrides: <Type, Generator>{
FileSystem: () => memoryFileSystem,
ProcessManager: () => FakeProcessManager.any(),
});
testUsingContext('throws ToolExit if it cannot write gradle.properties', () {
final MockDirectory sampleAppAndroid = MockDirectory();
final MockFile gradleProperties = MockFile();
when(gradleProperties.path).thenReturn('foo/gradle.properties');
when(gradleProperties.existsSync()).thenReturn(true);
when(gradleProperties.readAsStringSync()).thenReturn('');
when(gradleProperties.writeAsStringSync('android.enableR8=true\n', mode: FileMode.append))
.thenThrow(const FileSystemException());
when(sampleAppAndroid.childFile('gradle.properties'))
.thenReturn(gradleProperties);
expect(() {
migrateToR8(sampleAppAndroid);
},
throwsToolExit(message:
'The tool failed to add `android.enableR8=true` to foo/gradle.properties. '
'Please update the file manually and try this command again.'));
});
testUsingContext('does not update gradle.properties if it already uses R8', () {
final Directory sampleAppAndroid = fs.directory('/sample-app/android');
sampleAppAndroid.createSync(recursive: true);
sampleAppAndroid.childFile('gradle.properties')
.writeAsStringSync('android.enableR8=true');
migrateToR8(sampleAppAndroid);
expect(testLogger.traceText,
contains('gradle.properties already sets `android.enableR8`'));
expect(sampleAppAndroid.childFile('gradle.properties').readAsStringSync(),
equals('android.enableR8=true'));
}, overrides: <Type, Generator>{
FileSystem: () => memoryFileSystem,
ProcessManager: () => FakeProcessManager.any(),
});
testUsingContext('sets android.enableR8=true', () {
final Directory sampleAppAndroid = fs.directory('/sample-app/android');
sampleAppAndroid.createSync(recursive: true);
sampleAppAndroid.childFile('gradle.properties')
.writeAsStringSync('org.gradle.jvmargs=-Xmx1536M\n');
migrateToR8(sampleAppAndroid);
expect(testLogger.traceText, contains('set `android.enableR8=true` in gradle.properties'));
expect(
sampleAppAndroid.childFile('gradle.properties').readAsStringSync(),
equals(
'org.gradle.jvmargs=-Xmx1536M\n'
'android.enableR8=true\n'
),
);
}, overrides: <Type, Generator>{
FileSystem: () => memoryFileSystem,
ProcessManager: () => FakeProcessManager.any(),
});
testUsingContext('appends android.enableR8=true to the new line', () {
final Directory sampleAppAndroid = fs.directory('/sample-app/android');
sampleAppAndroid.createSync(recursive: true);
sampleAppAndroid.childFile('gradle.properties')
.writeAsStringSync('org.gradle.jvmargs=-Xmx1536M');
migrateToR8(sampleAppAndroid);
expect(testLogger.traceText, contains('set `android.enableR8=true` in gradle.properties'));
expect(
sampleAppAndroid.childFile('gradle.properties').readAsStringSync(),
equals(
'org.gradle.jvmargs=-Xmx1536M\n'
'android.enableR8=true\n'
),
);
}, overrides: <Type, Generator>{
FileSystem: () => memoryFileSystem,
ProcessManager: () => FakeProcessManager.any()
});
});
group('isAppUsingAndroidX', () {
FileSystem fs;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment