Add missing files in the Gradle wrapper directory (#39145)

This commit is contained in:
Emmanuel Garcia 2019-08-28 14:52:08 -07:00 committed by GitHub
parent ddd31bcee7
commit 4a1c62c28b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 227 additions and 23 deletions

View file

@ -268,13 +268,10 @@ String _locateGradlewExecutable(Directory directory) {
final File gradle = directory.childFile(
platform.isWindows ? 'gradlew.bat' : 'gradlew',
);
if (gradle.existsSync()) {
os.makeExecutable(gradle);
return gradle.absolute.path;
} else {
return null;
}
return null;
}
Future<String> _ensureGradle(FlutterProject project) async {
@ -286,12 +283,12 @@ Future<String> _ensureGradle(FlutterProject project) async {
// of validating the Gradle executable. This may take several seconds.
Future<String> _initializeGradle(FlutterProject project) async {
final Directory android = project.android.hostAppGradleRoot;
final Status status = logger.startProgress('Initializing gradle...', timeout: timeoutConfiguration.slowOperation);
String gradle = _locateGradlewExecutable(android);
if (gradle == null) {
injectGradleWrapper(android);
gradle = _locateGradlewExecutable(android);
}
final Status status = logger.startProgress('Initializing gradle...',
timeout: timeoutConfiguration.slowOperation);
injectGradleWrapperIfNeeded(android);
final String gradle = _locateGradlewExecutable(android);
if (gradle == null)
throwToolExit('Unable to locate gradlew script');
printTrace('Using gradle from $gradle.');
@ -302,11 +299,25 @@ Future<String> _initializeGradle(FlutterProject project) async {
return gradle;
}
/// Injects the Gradle wrapper into the specified directory.
void injectGradleWrapper(Directory directory) {
copyDirectorySync(cache.getArtifactDirectory('gradle_wrapper'), directory);
_locateGradlewExecutable(directory);
final File propertiesFile = directory.childFile(fs.path.join('gradle', 'wrapper', 'gradle-wrapper.properties'));
/// 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('''

View file

@ -64,11 +64,18 @@ void ensureDirectoryExists(String filePath) {
}
}
/// Recursively copies `srcDir` to `destDir`, invoking [onFileCopied] if
/// specified for each source/destination file pair.
/// Creates `destDir` if needed, then recursively copies `srcDir` to `destDir`,
/// invoking [onFileCopied], if specified, for each source/destination file pair.
///
/// Creates `destDir` if needed.
void copyDirectorySync(Directory srcDir, Directory destDir, [ void onFileCopied(File srcFile, File destFile) ]) {
/// Skips files if [shouldCopyFile] returns `false`.
void copyDirectorySync(
Directory srcDir,
Directory destDir,
{
bool shouldCopyFile(File srcFile, File destFile),
void onFileCopied(File srcFile, File destFile),
}
) {
if (!srcDir.existsSync())
throw Exception('Source directory "${srcDir.path}" does not exist, nothing to copy');
@ -79,11 +86,18 @@ void copyDirectorySync(Directory srcDir, Directory destDir, [ void onFileCopied(
final String newPath = destDir.fileSystem.path.join(destDir.path, entity.basename);
if (entity is File) {
final File newFile = destDir.fileSystem.file(newPath);
if (shouldCopyFile != null && !shouldCopyFile(entity, newFile)) {
continue;
}
newFile.writeAsBytesSync(entity.readAsBytesSync());
onFileCopied?.call(entity, newFile);
} else if (entity is Directory) {
copyDirectorySync(
entity, destDir.fileSystem.directory(newPath));
entity,
destDir.fileSystem.directory(newPath),
shouldCopyFile: shouldCopyFile,
onFileCopied: onFileCopied,
);
} else {
throw Exception('${entity.path} is neither File nor Directory');
}

View file

@ -628,7 +628,7 @@ To edit platform code in an IDE see https://flutter.dev/developing-packages/#edi
copyDirectorySync(
cache.getArtifactDirectory('gradle_wrapper'),
project.android.hostAppGradleRoot,
(File sourceFile, File destinationFile) {
onFileCopied: (File sourceFile, File destinationFile) {
filesCreated++;
final String modes = sourceFile.statSync().modeString();
if (modes != null && modes.contains('x')) {

View file

@ -580,7 +580,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.injectGradleWrapper(_editableHostAppDirectory);
gradle.injectGradleWrapperIfNeeded(_editableHostAppDirectory);
gradle.writeLocalProperties(_editableHostAppDirectory.childFile('local.properties'));
await injectPlugins(parent);
}
@ -593,7 +593,7 @@ class AndroidProject {
_deleteIfExistsSync(ephemeralDirectory);
_overwriteFromTemplate(fs.path.join('module', 'android', 'library'), ephemeralDirectory);
_overwriteFromTemplate(fs.path.join('module', 'android', 'gradle'), ephemeralDirectory);
gradle.injectGradleWrapper(ephemeralDirectory);
gradle.injectGradleWrapperIfNeeded(ephemeralDirectory);
}
void _overwriteFromTemplate(String path, Directory target) {

View file

@ -13,6 +13,7 @@ import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/artifacts.dart';
import 'package:flutter_tools/src/base/common.dart';
import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/base/os.dart';
import 'package:flutter_tools/src/build_info.dart';
import 'package:flutter_tools/src/cache.dart';
import 'package:flutter_tools/src/ios/xcodeproj.dart';
@ -848,6 +849,125 @@ flutter:
});
});
group('injectGradleWrapperIfNeeded', () {
MemoryFileSystem memoryFileSystem;
Directory tempDir;
Directory gradleWrapperDirectory;
setUp(() {
memoryFileSystem = MemoryFileSystem();
tempDir = memoryFileSystem.systemTempDirectory.createTempSync('artifacts_test.');
gradleWrapperDirectory = memoryFileSystem.directory(
memoryFileSystem.path.join(tempDir.path, 'bin', 'cache', 'artifacts', 'gradle_wrapper'));
gradleWrapperDirectory.createSync(recursive: true);
gradleWrapperDirectory
.childFile('gradlew')
.writeAsStringSync('irrelevant');
gradleWrapperDirectory
.childDirectory('gradle')
.childDirectory('wrapper')
.createSync(recursive: true);
gradleWrapperDirectory
.childDirectory('gradle')
.childDirectory('wrapper')
.childFile('gradle-wrapper.jar')
.writeAsStringSync('irrelevant');
});
testUsingContext('Inject the wrapper when all files are missing', () {
final Directory sampleAppAndroid = fs.directory('/sample-app/android');
sampleAppAndroid.createSync(recursive: true);
injectGradleWrapperIfNeeded(sampleAppAndroid);
expect(sampleAppAndroid.childFile('gradlew').existsSync(), isTrue);
expect(sampleAppAndroid
.childDirectory('gradle')
.childDirectory('wrapper')
.childFile('gradle-wrapper.jar')
.existsSync(), isTrue);
expect(sampleAppAndroid
.childDirectory('gradle')
.childDirectory('wrapper')
.childFile('gradle-wrapper.properties')
.existsSync(), isTrue);
expect(sampleAppAndroid
.childDirectory('gradle')
.childDirectory('wrapper')
.childFile('gradle-wrapper.properties')
.readAsStringSync(),
'distributionBase=GRADLE_USER_HOME\n'
'distributionPath=wrapper/dists\n'
'zipStoreBase=GRADLE_USER_HOME\n'
'zipStorePath=wrapper/dists\n'
'distributionUrl=https\\://services.gradle.org/distributions/gradle-4.10.2-all.zip\n');
}, overrides: <Type, Generator>{
Cache: () => Cache(rootOverride: tempDir),
FileSystem: () => memoryFileSystem,
});
testUsingContext('Inject the wrapper when some files are missing', () {
final Directory sampleAppAndroid = fs.directory('/sample-app/android');
sampleAppAndroid.createSync(recursive: true);
// There's an existing gradlew
sampleAppAndroid.childFile('gradlew').writeAsStringSync('existing gradlew');
injectGradleWrapperIfNeeded(sampleAppAndroid);
expect(sampleAppAndroid.childFile('gradlew').existsSync(), isTrue);
expect(sampleAppAndroid.childFile('gradlew').readAsStringSync(),
equals('existing gradlew'));
expect(sampleAppAndroid
.childDirectory('gradle')
.childDirectory('wrapper')
.childFile('gradle-wrapper.jar')
.existsSync(), isTrue);
expect(sampleAppAndroid
.childDirectory('gradle')
.childDirectory('wrapper')
.childFile('gradle-wrapper.properties')
.existsSync(), isTrue);
expect(sampleAppAndroid
.childDirectory('gradle')
.childDirectory('wrapper')
.childFile('gradle-wrapper.properties')
.readAsStringSync(),
'distributionBase=GRADLE_USER_HOME\n'
'distributionPath=wrapper/dists\n'
'zipStoreBase=GRADLE_USER_HOME\n'
'zipStorePath=wrapper/dists\n'
'distributionUrl=https\\://services.gradle.org/distributions/gradle-4.10.2-all.zip\n');
}, overrides: <Type, Generator>{
Cache: () => Cache(rootOverride: tempDir),
FileSystem: () => memoryFileSystem,
});
testUsingContext('Gives executable permission to gradle', () {
final Directory sampleAppAndroid = fs.directory('/sample-app/android');
sampleAppAndroid.createSync(recursive: true);
// Make gradlew in the wrapper executable.
os.makeExecutable(gradleWrapperDirectory.childFile('gradlew'));
injectGradleWrapperIfNeeded(sampleAppAndroid);
final File gradlew = sampleAppAndroid.childFile('gradlew');
expect(gradlew.existsSync(), isTrue);
expect(gradlew.statSync().modeString().contains('x'), isTrue);
}, overrides: <Type, Generator>{
Cache: () => Cache(rootOverride: tempDir),
FileSystem: () => memoryFileSystem,
OperatingSystemUtils: () => OperatingSystemUtils(),
});
});
group('gradle build', () {
MockAndroidSdk mockAndroidSdk;
MockAndroidStudio mockAndroidStudio;
@ -855,6 +975,7 @@ flutter:
MockProcessManager mockProcessManager;
FakePlatform android;
FileSystem fs;
Cache cache;
setUp(() {
fs = MemoryFileSystem();
@ -863,6 +984,28 @@ flutter:
mockArtifacts = MockLocalEngineArtifacts();
mockProcessManager = MockProcessManager();
android = fakePlatform('android');
final Directory tempDir = fs.systemTempDirectory.createTempSync('artifacts_test.');
cache = Cache(rootOverride: tempDir);
final Directory gradleWrapperDirectory = tempDir
.childDirectory('bin')
.childDirectory('cache')
.childDirectory('artifacts')
.childDirectory('gradle_wrapper');
gradleWrapperDirectory.createSync(recursive: true);
gradleWrapperDirectory
.childFile('gradlew')
.writeAsStringSync('irrelevant');
gradleWrapperDirectory
.childDirectory('gradle')
.childDirectory('wrapper')
.createSync(recursive: true);
gradleWrapperDirectory
.childDirectory('gradle')
.childDirectory('wrapper')
.childFile('gradle-wrapper.jar')
.writeAsStringSync('irrelevant');
});
testUsingContext('build aar uses selected local engine', () async {
@ -928,6 +1071,7 @@ flutter:
AndroidSdk: () => mockAndroidSdk,
AndroidStudio: () => mockAndroidStudio,
Artifacts: () => mockArtifacts,
Cache: () => cache,
ProcessManager: () => mockProcessManager,
Platform: () => android,
FileSystem: () => fs,

View file

@ -39,17 +39,20 @@ void main() {
AndroidSdk sdk;
ProcessManager mockProcessManager;
MemoryFileSystem fs;
Cache mockCache;
File gradle;
final Map<Type, Generator> overrides = <Type, Generator>{
AndroidSdk: () => sdk,
ProcessManager: () => mockProcessManager,
FileSystem: () => fs,
Cache: () => mockCache,
};
setUp(() async {
sdk = MockitoAndroidSdk();
mockProcessManager = MockitoProcessManager();
fs = MemoryFileSystem();
mockCache = MockCache();
Cache.flutterRoot = '../..';
when(sdk.licensesAvailable).thenReturn(true);
when(mockProcessManager.canRun(any)).thenReturn(true);
@ -100,6 +103,14 @@ void main() {
platform.isWindows ? 'gradlew.bat' : 'gradlew',
)..createSync(recursive: true);
final Directory gradleWrapperDir = fs.systemTempDirectory.createTempSync('gradle_wrapper.');
when(mockCache.getArtifactDirectory('gradle_wrapper')).thenReturn(gradleWrapperDir);
fs.directory(gradleWrapperDir.childDirectory('gradle').childDirectory('wrapper'))
.createSync(recursive: true);
fs.file(fs.path.join(gradleWrapperDir.path, 'gradlew')).writeAsStringSync('irrelevant');
fs.file(fs.path.join(gradleWrapperDir.path, 'gradlew.bat')).writeAsStringSync('irrelevant');
await ApplicationPackageFactory.instance.getPackageForPlatform(
TargetPlatform.android_arm,
applicationBinary: fs.file('app.apk'),
@ -606,4 +617,5 @@ const String plistData = '''
{"CFBundleIdentifier": "fooBundleId"}
''';
class MockCache extends Mock implements Cache {}
class MockOperatingSystemUtils extends Mock implements OperatingSystemUtils { }

View file

@ -58,6 +58,29 @@ void main() {
// There's still 3 things in the original directory as there were initially.
expect(sourceMemoryFs.directory(sourcePath).listSync().length, 3);
});
testUsingContext('Skip files if shouldCopyFile returns false', () {
final Directory origin = fs.directory('/origin');
origin.createSync();
fs.file(fs.path.join('origin', 'a.txt')).writeAsStringSync('irrelevant');
fs.directory('/origin/nested').createSync();
fs.file(fs.path.join('origin', 'nested', 'a.txt')).writeAsStringSync('irrelevant');
fs.file(fs.path.join('origin', 'nested', 'b.txt')).writeAsStringSync('irrelevant');
final Directory destination = fs.directory('/destination');
copyDirectorySync(origin, destination, shouldCopyFile: (File origin, File dest) {
return origin.basename == 'b.txt';
});
expect(destination.existsSync(), isTrue);
expect(destination.childDirectory('nested').existsSync(), isTrue);
expect(destination.childDirectory('nested').childFile('b.txt').existsSync(), isTrue);
expect(destination.childFile('a.txt').existsSync(), isFalse);
expect(destination.childDirectory('nested').childFile('a.txt').existsSync(), isFalse);
}, overrides: <Type, Generator>{
FileSystem: () => MemoryFileSystem(),
});
});
group('canonicalizePath', () {