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 { ...@@ -46,14 +46,17 @@ class GradleUtils {
final Directory androidDir = project.android.hostAppGradleRoot; final Directory androidDir = project.android.hostAppGradleRoot;
// Update the project if needed. // Update the project if needed.
// TODO(egarciad): https://github.com/flutter/flutter/issues/40460 // TODO(egarciad): https://github.com/flutter/flutter/issues/40460
migrateToR8(androidDir); gradleUtils.migrateToR8(androidDir);
injectGradleWrapperIfNeeded(androidDir); gradleUtils.injectGradleWrapperIfNeeded(androidDir);
final File gradle = androidDir.childFile( final File gradle = androidDir.childFile(
platform.isWindows ? 'gradlew.bat' : 'gradlew', platform.isWindows ? 'gradlew.bat' : 'gradlew',
); );
if (gradle.existsSync()) { if (gradle.existsSync()) {
printTrace('Using gradle from ${gradle.absolute.path}.'); 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; return gradle.absolute.path;
} }
throwToolExit( throwToolExit(
...@@ -62,71 +65,69 @@ class GradleUtils { ...@@ -62,71 +65,69 @@ class GradleUtils {
); );
return null; return null;
} }
}
/// Migrates the Android's [directory] to R8. /// Migrates the Android's [directory] to R8.
/// https://developer.android.com/studio/build/shrink-code /// https://developer.android.com/studio/build/shrink-code
@visibleForTesting @visibleForTesting
void migrateToR8(Directory directory) { void migrateToR8(Directory directory) {
final File gradleProperties = directory.childFile('gradle.properties'); final File gradleProperties = directory.childFile('gradle.properties');
if (!gradleProperties.existsSync()) { if (!gradleProperties.existsSync()) {
throwToolExit( throwToolExit(
'Expected file ${gradleProperties.path}. ' 'Expected file ${gradleProperties.path}. '
'Please ensure that this file exists or that ${gradleProperties.dirname} can be read.' 'Please ensure that this file exists or that ${gradleProperties.dirname} can be read.'
); );
} }
final String propertiesContent = gradleProperties.readAsStringSync(); final String propertiesContent = gradleProperties.readAsStringSync();
if (propertiesContent.contains('android.enableR8')) { if (propertiesContent.contains('android.enableR8')) {
printTrace('gradle.properties already sets `android.enableR8`'); printTrace('gradle.properties already sets `android.enableR8`');
return; return;
} }
printTrace('set `android.enableR8=true` in gradle.properties'); printTrace('set `android.enableR8=true` in gradle.properties');
try { try {
if (propertiesContent.isNotEmpty && !propertiesContent.endsWith('\n')) { if (propertiesContent.isNotEmpty && !propertiesContent.endsWith('\n')) {
// Add a new line if the file doesn't end with a new line. // Add a new line if the file doesn't end with a new line.
gradleProperties.writeAsStringSync('\n', mode: FileMode.append); 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]. /// Injects the Gradle wrapper files if any of these files don't exist in [directory].
void injectGradleWrapperIfNeeded(Directory directory) { void injectGradleWrapperIfNeeded(Directory directory) {
copyDirectorySync( copyDirectorySync(
cache.getArtifactDirectory('gradle_wrapper'), cache.getArtifactDirectory('gradle_wrapper'),
directory, directory,
shouldCopyFile: (File sourceFile, File destinationFile) { shouldCopyFile: (File sourceFile, File destinationFile) {
// Don't override the existing files in the project. // Don't override the existing files in the project.
return !destinationFile.existsSync(); return !destinationFile.existsSync();
}, },
onFileCopied: (File sourceFile, File destinationFile) { onFileCopied: (File sourceFile, File destinationFile) {
final String modes = sourceFile.statSync().modeString(); if (_hasExecutePermission(sourceFile)) {
if (modes != null && modes.contains('x')) { _giveExecutePermissionIfNeeded(destinationFile);
os.makeExecutable(destinationFile); }
} },
}, );
); // Add the `gradle-wrapper.properties` file if it doesn't exist.
// Add the `gradle-wrapper.properties` file if it doesn't exist. final File propertiesFile = directory.childFile(
final File propertiesFile = directory.childFile( fs.path.join('gradle', 'wrapper', 'gradle-wrapper.properties'));
fs.path.join('gradle', 'wrapper', 'gradle-wrapper.properties')); if (!propertiesFile.existsSync()) {
if (!propertiesFile.existsSync()) { final String gradleVersion = getGradleVersionForAndroidPlugin(directory);
final String gradleVersion = getGradleVersionForAndroidPlugin(directory); propertiesFile.writeAsStringSync('''
propertiesFile.writeAsStringSync('''
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
distributionUrl=https\\://services.gradle.org/distributions/gradle-$gradleVersion-all.zip distributionUrl=https\\://services.gradle.org/distributions/gradle-$gradleVersion-all.zip
''', flush: true, ''', flush: true,
); );
}
} }
} }
const String _defaultGradleVersion = '5.6.2'; const String _defaultGradleVersion = '5.6.2';
final RegExp _androidPluginRegExp = RegExp('com\.android\.tools\.build\:gradle\:(\\d+\.\\d+\.\\d+\)'); final RegExp _androidPluginRegExp = RegExp('com\.android\.tools\.build\:gradle\:(\\d+\.\\d+\.\\d+\)');
...@@ -150,6 +151,24 @@ String getGradleVersionForAndroidPlugin(Directory directory) { ...@@ -150,6 +151,24 @@ String getGradleVersionForAndroidPlugin(Directory directory) {
return getGradleVersionFor(androidPluginVersion); 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. /// Returns true if [targetVersion] is within the range [min] and [max] inclusive.
bool _isWithinVersionRange( bool _isWithinVersionRange(
String targetVersion, { String targetVersion, {
......
...@@ -904,16 +904,13 @@ class AndroidMavenArtifacts extends ArtifactSet { ...@@ -904,16 +904,13 @@ class AndroidMavenArtifacts extends ArtifactSet {
Future<void> update() async { Future<void> update() async {
final Directory tempDir = final Directory tempDir =
fs.systemTempDirectory.createTempSync('flutter_gradle_wrapper.'); fs.systemTempDirectory.createTempSync('flutter_gradle_wrapper.');
injectGradleWrapperIfNeeded(tempDir); gradleUtils.injectGradleWrapperIfNeeded(tempDir);
final Status status = logger.startProgress('Downloading Android Maven dependencies...', final Status status = logger.startProgress('Downloading Android Maven dependencies...',
timeout: timeoutConfiguration.slowOperation); timeout: timeoutConfiguration.slowOperation);
final File gradle = tempDir.childFile( final File gradle = tempDir.childFile(
platform.isWindows ? 'gradlew.bat' : 'gradlew', platform.isWindows ? 'gradlew.bat' : 'gradlew',
); );
assert(gradle.existsSync());
os.makeExecutable(gradle);
try { try {
final String gradleExecutable = gradle.absolute.path; final String gradleExecutable = gradle.absolute.path;
final String flutterSdk = escapePath(Cache.flutterRoot); final String flutterSdk = escapePath(Cache.flutterRoot);
......
...@@ -630,7 +630,7 @@ class AndroidProject { ...@@ -630,7 +630,7 @@ class AndroidProject {
_overwriteFromTemplate(fs.path.join('module', 'android', 'host_app_common'), _editableHostAppDirectory); _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', 'host_app_editable'), _editableHostAppDirectory);
_overwriteFromTemplate(fs.path.join('module', 'android', 'gradle'), _editableHostAppDirectory); _overwriteFromTemplate(fs.path.join('module', 'android', 'gradle'), _editableHostAppDirectory);
gradle.injectGradleWrapperIfNeeded(_editableHostAppDirectory); gradle.gradleUtils.injectGradleWrapperIfNeeded(_editableHostAppDirectory);
gradle.writeLocalProperties(_editableHostAppDirectory.childFile('local.properties')); gradle.writeLocalProperties(_editableHostAppDirectory.childFile('local.properties'));
await injectPlugins(parent); await injectPlugins(parent);
} }
...@@ -647,7 +647,7 @@ class AndroidProject { ...@@ -647,7 +647,7 @@ class AndroidProject {
featureFlags.isAndroidEmbeddingV2Enabled ? 'library_new_embedding' : 'library', featureFlags.isAndroidEmbeddingV2Enabled ? 'library_new_embedding' : 'library',
), ephemeralDirectory); ), ephemeralDirectory);
_overwriteFromTemplate(fs.path.join('module', 'android', 'gradle'), ephemeralDirectory); _overwriteFromTemplate(fs.path.join('module', 'android', 'gradle'), ephemeralDirectory);
gradle.injectGradleWrapperIfNeeded(ephemeralDirectory); gradle.gradleUtils.injectGradleWrapperIfNeeded(ephemeralDirectory);
} }
void _overwriteFromTemplate(String path, Directory target) { void _overwriteFromTemplate(String path, Directory target) {
......
...@@ -814,107 +814,6 @@ flutter: ...@@ -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', () { group('isAppUsingAndroidX', () {
FileSystem fs; 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