Use FlutterProject to locate files (#18913)

This commit is contained in:
Sigurd Meldgaard 2018-07-16 09:39:37 +02:00 committed by GitHub
parent e7832a3897
commit 57d78cc739
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
25 changed files with 438 additions and 392 deletions

View file

@ -21,6 +21,7 @@ import '../base/utils.dart';
import '../build_info.dart';
import '../device.dart';
import '../globals.dart';
import '../project.dart';
import '../protocol_discovery.dart';
import 'adb.dart';
@ -241,7 +242,7 @@ class AndroidDevice extends Device {
String _getSourceSha1(ApplicationPackage app) {
final AndroidApk apk = app;
final File shaFile = fs.file('${apk.apkPath}.sha1');
final File shaFile = fs.file('${apk.file.path}.sha1');
return shaFile.existsSync() ? shaFile.readAsStringSync() : '';
}
@ -269,16 +270,16 @@ class AndroidDevice extends Device {
@override
Future<bool> installApp(ApplicationPackage app) async {
final AndroidApk apk = app;
if (!fs.isFileSync(apk.apkPath)) {
printError('"${apk.apkPath}" does not exist.');
if (!apk.file.existsSync()) {
printError('"${apk.file.path}" does not exist.');
return false;
}
if (!await _checkForSupportedAdbVersion() || !await _checkForSupportedAndroidVersion())
return false;
final Status status = logger.startProgress('Installing ${apk.apkPath}...', expectSlowOperation: true);
final RunResult installResult = await runAsync(adbCommandForDevice(<String>['install', '-t', '-r', apk.apkPath]));
final Status status = logger.startProgress('Installing ${apk.file.path}...', expectSlowOperation: true);
final RunResult installResult = await runAsync(adbCommandForDevice(<String>['install', '-t', '-r', apk.file.path]));
status.stop();
// Some versions of adb exit with exit code 0 even on failure :(
// Parsing the output to check for failures.
@ -374,12 +375,13 @@ class AndroidDevice extends Device {
if (!prebuiltApplication) {
printTrace('Building APK');
await buildApk(
project: new FlutterProject(fs.currentDirectory),
target: mainPath,
buildInfo: buildInfo,
);
// Package has been built, so we can get the updated application ID and
// activity name from the .apk.
package = await AndroidApk.fromCurrentDirectory();
package = await AndroidApk.fromAndroidProject(new FlutterProject(fs.currentDirectory).android);
}
printTrace("Stopping app '${package.name}' on $name.");

View file

@ -4,17 +4,22 @@
import 'dart:async';
import 'package:meta/meta.dart';
import '../base/common.dart';
import '../build_info.dart';
import '../globals.dart';
import '../project.dart';
import 'android_sdk.dart';
import 'gradle.dart';
Future<Null> buildApk({
String target,
@required FlutterProject project,
@required String target,
BuildInfo buildInfo = BuildInfo.debug
}) async {
if (!isProjectUsingGradle()) {
if (!project.android.isUsingGradle()) {
throwToolExit(
'The build process for Android has changed, and the current project configuration\n'
'is no longer valid. Please consult\n\n'
@ -33,5 +38,9 @@ Future<Null> buildApk({
throwToolExit('Try re-installing or updating your Android SDK.');
}
return buildGradleProject(buildInfo, target);
return buildGradleProject(
project: project,
buildInfo: buildInfo,
target: target,
);
}

View file

@ -4,6 +4,8 @@
import 'dart:async';
import 'package:meta/meta.dart';
import '../android/android_sdk.dart';
import '../artifacts.dart';
import '../base/common.dart';
@ -14,16 +16,13 @@ import '../base/platform.dart';
import '../base/process.dart';
import '../base/utils.dart';
import '../build_info.dart';
import '../bundle.dart' as bundle;
import '../cache.dart';
import '../flutter_manifest.dart';
import '../globals.dart';
import '../project.dart';
import 'android_sdk.dart';
import 'android_studio.dart';
const String gradleManifestPath = 'android/app/src/main/AndroidManifest.xml';
const String gradleAppOutV1 = 'android/app/build/outputs/apk/app-debug.apk';
const String gradleAppOutDirV1 = 'android/app/build/outputs/apk';
const String gradleVersion = '4.1';
final RegExp _assembleTaskPattern = new RegExp(r'assemble([^:]+): task ');
@ -45,9 +44,6 @@ final RegExp ndkMessageFilter = new RegExp(r'^(?!NDK is missing a ".*" directory
r'|If you are using NDK, verify the ndk.dir is set to a valid NDK directory. It is currently set to .*)');
bool isProjectUsingGradle() {
return fs.isFileSync('android/build.gradle');
}
FlutterPluginVersion get flutterPluginVersion {
final File plugin = fs.file('android/buildSrc/src/main/groovy/FlutterPlugin.groovy');
@ -69,18 +65,17 @@ FlutterPluginVersion get flutterPluginVersion {
return FlutterPluginVersion.none;
}
/// Returns the path to the apk file created by [buildGradleProject], relative
/// to current directory.
Future<String> getGradleAppOut() async {
/// Returns the apk file created by [buildGradleProject]
Future<File> getGradleAppOut(AndroidProject androidProject) async {
switch (flutterPluginVersion) {
case FlutterPluginVersion.none:
// Fall through. Pretend we're v1, and just go with it.
case FlutterPluginVersion.v1:
return gradleAppOutV1;
return androidProject.gradleAppOutV1File;
case FlutterPluginVersion.managed:
// Fall through. The managed plugin matches plugin v2 for now.
case FlutterPluginVersion.v2:
return fs.path.relative(fs.path.join((await _gradleProject()).apkDirectory, 'app.apk'));
return fs.file((await _gradleProject()).apkDirectory.childFile('app.apk'));
}
return null;
}
@ -94,12 +89,13 @@ Future<GradleProject> _gradleProject() async {
// of calculating the app properties using Gradle. This may take minutes.
Future<GradleProject> _readGradleProject() async {
final String gradle = await _ensureGradle();
await updateLocalProperties();
final FlutterProject flutterProject = new FlutterProject(fs.currentDirectory);
await updateLocalProperties(project: flutterProject);
try {
final Status status = logger.startProgress('Resolving dependencies...', expectSlowOperation: true);
final RunResult runResult = await runCheckedAsync(
<String>[gradle, 'app:properties'],
workingDirectory: 'android',
workingDirectory: flutterProject.android.directory.path,
environment: _gradleEnv,
);
final String properties = runResult.stdout.trim();
@ -117,7 +113,7 @@ Future<GradleProject> _readGradleProject() async {
}
}
// Fall back to the default
return new GradleProject(<String>['debug', 'profile', 'release'], <String>[], gradleAppOutDirV1);
return new GradleProject(<String>['debug', 'profile', 'release'], <String>[], flutterProject.android.gradleAppOutV1Directory);
}
void handleKnownGradleExceptions(String exceptionString) {
@ -199,28 +195,19 @@ distributionUrl=https\\://services.gradle.org/distributions/gradle-$gradleVersio
/// Overwrite android/local.properties in the specified Flutter project, if needed.
///
/// Throws, if `pubspec.yaml` or Android SDK cannot be located.
Future<void> updateLocalProperties({String projectPath, BuildInfo buildInfo}) async {
final Directory android = (projectPath == null)
? fs.directory('android')
: fs.directory(fs.path.join(projectPath, 'android'));
final String flutterManifest = (projectPath == null)
? fs.path.join(bundle.defaultManifestPath)
: fs.path.join(projectPath, bundle.defaultManifestPath);
if (androidSdk == null) {
///
/// If [requireSdk] is `true` this will fail with a tool-exit if no Android Sdk
/// is found.
Future<void> updateLocalProperties({
@required FlutterProject project,
BuildInfo buildInfo,
bool requireAndroidSdk = true,
}) async {
if (requireAndroidSdk && androidSdk == null) {
throwToolExit('Unable to locate Android SDK. Please run `flutter doctor` for more details.');
}
FlutterManifest manifest;
try {
manifest = await FlutterManifest.createFromPath(flutterManifest);
} catch (error) {
throwToolExit('Failed to load pubspec.yaml: $error');
}
updateLocalPropertiesSync(android, manifest, buildInfo);
}
/// Overwrite local.properties in the specified directory, if needed.
void updateLocalPropertiesSync(Directory android, FlutterManifest manifest, [BuildInfo buildInfo]) {
final File localProperties = android.childFile('local.properties');
final File localProperties = await project.androidLocalPropertiesFile;
bool changed = false;
SettingsFile settings;
@ -238,6 +225,8 @@ void updateLocalPropertiesSync(Directory android, FlutterManifest manifest, [Bui
}
}
final FlutterManifest manifest = await project.manifest;
if (androidSdk != null)
changeIfNecessary('sdk.dir', escapePath(androidSdk.directory));
changeIfNecessary('flutter.sdk', escapePath(Cache.flutterRoot));
@ -254,7 +243,11 @@ void updateLocalPropertiesSync(Directory android, FlutterManifest manifest, [Bui
settings.writeContents(localProperties);
}
Future<Null> buildGradleProject(BuildInfo buildInfo, String target) async {
Future<Null> buildGradleProject({
@required FlutterProject project,
@required BuildInfo buildInfo,
@required String target,
}) async {
// Update the local.properties file with the build mode, version name and code.
// FlutterPlugin v1 reads local.properties to determine build mode. Plugin v2
// uses the standard Android way to determine what to build, but we still
@ -263,7 +256,7 @@ Future<Null> buildGradleProject(BuildInfo buildInfo, String target) async {
// and can be overwritten with flutter build command.
// The default Gradle script reads the version name and number
// from the local.properties file.
await updateLocalProperties(buildInfo: buildInfo);
await updateLocalProperties(project: project, buildInfo: buildInfo);
final String gradle = await _ensureGradle();
@ -271,7 +264,7 @@ Future<Null> buildGradleProject(BuildInfo buildInfo, String target) async {
case FlutterPluginVersion.none:
// Fall through. Pretend it's v1, and just go for it.
case FlutterPluginVersion.v1:
return _buildGradleProjectV1(gradle);
return _buildGradleProjectV1(project, gradle);
case FlutterPluginVersion.managed:
// Fall through. Managed plugin builds the same way as plugin v2.
case FlutterPluginVersion.v2:
@ -279,7 +272,7 @@ Future<Null> buildGradleProject(BuildInfo buildInfo, String target) async {
}
}
Future<Null> _buildGradleProjectV1(String gradle) async {
Future<Null> _buildGradleProjectV1(FlutterProject project, String gradle) async {
// Run 'gradlew build'.
final Status status = logger.startProgress('Running \'gradlew build\'...', expectSlowOperation: true);
final int exitCode = await runCommandAndStreamOutput(
@ -293,7 +286,7 @@ Future<Null> _buildGradleProjectV1(String gradle) async {
if (exitCode != 0)
throwToolExit('Gradle build failed: $exitCode', exitCode: exitCode);
printStatus('Built $gradleAppOutV1.');
printStatus('Built ${fs.path.relative(project.android.gradleAppOutV1File.path)}.');
}
Future<Null> _buildGradleProjectV2(String gradle, BuildInfo buildInfo, String target) async {
@ -371,10 +364,10 @@ Future<Null> _buildGradleProjectV2(String gradle, BuildInfo buildInfo, String ta
if (apkFile == null)
throwToolExit('Gradle build failed to produce an Android package.');
// Copy the APK to app.apk, so `flutter run`, `flutter install`, etc. can find it.
apkFile.copySync(fs.path.join(project.apkDirectory, 'app.apk'));
apkFile.copySync(project.apkDirectory.childFile('app.apk').path);
printTrace('calculateSha: ${project.apkDirectory}/app.apk');
final File apkShaFile = fs.file(fs.path.join(project.apkDirectory, 'app.apk.sha1'));
final File apkShaFile = project.apkDirectory.childFile('app.apk.sha1');
apkShaFile.writeAsStringSync(calculateSha(apkFile));
String appSize;
@ -390,15 +383,15 @@ File _findApkFile(GradleProject project, BuildInfo buildInfo) {
final String apkFileName = project.apkFileFor(buildInfo);
if (apkFileName == null)
return null;
File apkFile = fs.file(fs.path.join(project.apkDirectory, apkFileName));
File apkFile = fs.file(fs.path.join(project.apkDirectory.path, apkFileName));
if (apkFile.existsSync())
return apkFile;
apkFile = fs.file(fs.path.join(project.apkDirectory, buildInfo.modeName, apkFileName));
apkFile = fs.file(fs.path.join(project.apkDirectory.path, buildInfo.modeName, apkFileName));
if (apkFile.existsSync())
return apkFile;
if (buildInfo.flavor != null) {
// Android Studio Gradle plugin v3 adds flavor to path.
apkFile = fs.file(fs.path.join(project.apkDirectory, buildInfo.flavor, buildInfo.modeName, apkFileName));
apkFile = fs.file(fs.path.join(project.apkDirectory.path, buildInfo.flavor, buildInfo.modeName, apkFileName));
if (apkFile.existsSync())
return apkFile;
}
@ -453,13 +446,13 @@ class GradleProject {
return new GradleProject(
buildTypes.toList(),
productFlavors.toList(),
fs.path.normalize(fs.path.join(buildDir, 'outputs', 'apk')),
fs.directory(fs.path.join(buildDir, 'outputs', 'apk')),
);
}
final List<String> buildTypes;
final List<String> productFlavors;
final String apkDirectory;
final Directory apkDirectory;
String _buildTypeFor(BuildInfo buildInfo) {
if (buildTypes.contains(buildInfo.modeName))

View file

@ -18,6 +18,7 @@ import 'globals.dart';
import 'ios/ios_workflow.dart';
import 'ios/plist_utils.dart' as plist;
import 'ios/xcodeproj.dart';
import 'project.dart';
import 'tester/flutter_tester.dart';
abstract class ApplicationPackage {
@ -31,7 +32,7 @@ abstract class ApplicationPackage {
String get displayName => name;
String get packagePath => null;
File get packagesFile => null;
@override
String toString() => displayName;
@ -39,21 +40,21 @@ abstract class ApplicationPackage {
class AndroidApk extends ApplicationPackage {
/// Path to the actual apk file.
final String apkPath;
final File file;
/// The path to the activity that should be launched.
final String launchActivity;
AndroidApk({
String id,
@required this.apkPath,
@required this.file,
@required this.launchActivity
}) : assert(apkPath != null),
}) : assert(file != null),
assert(launchActivity != null),
super(id: id);
/// Creates a new AndroidApk from an existing APK.
factory AndroidApk.fromApk(String applicationBinary) {
factory AndroidApk.fromApk(File apk) {
final String aaptPath = androidSdk?.latestVersion?.aaptPath;
if (aaptPath == null) {
printError('Unable to locate the Android SDK; please run \'flutter doctor\'.');
@ -64,7 +65,7 @@ class AndroidApk extends ApplicationPackage {
aaptPath,
'dump',
'xmltree',
applicationBinary,
apk.path,
'AndroidManifest.xml',
];
@ -72,47 +73,46 @@ class AndroidApk extends ApplicationPackage {
.parseFromXmlDump(runCheckedSync(aaptArgs));
if (data == null) {
printError('Unable to read manifest info from $applicationBinary.');
printError('Unable to read manifest info from ${apk.path}.');
return null;
}
if (data.packageName == null || data.launchableActivityName == null) {
printError('Unable to read manifest info from $applicationBinary.');
printError('Unable to read manifest info from ${apk.path}.');
return null;
}
return new AndroidApk(
id: data.packageName,
apkPath: applicationBinary,
file: apk,
launchActivity: '${data.packageName}/${data.launchableActivityName}'
);
}
/// Creates a new AndroidApk based on the information in the Android manifest.
static Future<AndroidApk> fromCurrentDirectory() async {
String manifestPath;
String apkPath;
static Future<AndroidApk> fromAndroidProject(AndroidProject androidProject) async {
File apkFile;
if (isProjectUsingGradle()) {
apkPath = await getGradleAppOut();
if (fs.file(apkPath).existsSync()) {
if (androidProject.isUsingGradle()) {
apkFile = await getGradleAppOut(androidProject);
if (apkFile.existsSync()) {
// Grab information from the .apk. The gradle build script might alter
// the application Id, so we need to look at what was actually built.
return new AndroidApk.fromApk(apkPath);
return new AndroidApk.fromApk(apkFile);
}
// The .apk hasn't been built yet, so we work with what we have. The run
// command will grab a new AndroidApk after building, to get the updated
// IDs.
manifestPath = gradleManifestPath;
} else {
manifestPath = fs.path.join('android', 'AndroidManifest.xml');
apkPath = fs.path.join(getAndroidBuildDirectory(), 'app.apk');
apkFile = fs.file(fs.path.join(getAndroidBuildDirectory(), 'app.apk'));
}
if (!fs.isFileSync(manifestPath))
final File manifest = androidProject.gradleManifestFile;
if (!manifest.existsSync())
return null;
final String manifestString = fs.file(manifestPath).readAsStringSync();
final String manifestString = manifest.readAsStringSync();
final xml.XmlDocument document = xml.parse(manifestString);
final Iterable<xml.XmlElement> manifests = document.findElements('manifest');
@ -138,16 +138,16 @@ class AndroidApk extends ApplicationPackage {
return new AndroidApk(
id: packageId,
apkPath: apkPath,
file: apkFile,
launchActivity: launchActivity
);
}
@override
String get packagePath => apkPath;
File get packagesFile => file;
@override
String get name => fs.path.basename(apkPath);
String get name => file.basename;
}
/// Tests whether a [FileSystemEntity] is an iOS bundle directory
@ -158,18 +158,18 @@ abstract class IOSApp extends ApplicationPackage {
IOSApp({@required String projectBundleId}) : super(id: projectBundleId);
/// Creates a new IOSApp from an existing app bundle or IPA.
factory IOSApp.fromPrebuiltApp(String applicationBinary) {
final FileSystemEntityType entityType = fs.typeSync(applicationBinary);
factory IOSApp.fromPrebuiltApp(File applicationBinary) {
final FileSystemEntityType entityType = fs.typeSync(applicationBinary.path);
if (entityType == FileSystemEntityType.notFound) {
printError(
'File "$applicationBinary" does not exist. Use an app bundle or an ipa.');
'File "${applicationBinary.path}" does not exist. Use an app bundle or an ipa.');
return null;
}
Directory bundleDir;
if (entityType == FileSystemEntityType.directory) {
final Directory directory = fs.directory(applicationBinary);
if (!_isBundleDirectory(directory)) {
printError('Folder "$applicationBinary" is not an app bundle.');
printError('Folder "${applicationBinary.path}" is not an app bundle.');
return null;
}
bundleDir = fs.directory(applicationBinary);
@ -305,16 +305,16 @@ class PrebuiltIOSApp extends IOSApp {
String get _bundlePath => bundleDir.path;
}
Future<ApplicationPackage> getApplicationPackageForPlatform(TargetPlatform platform, {
String applicationBinary
}) async {
Future<ApplicationPackage> getApplicationPackageForPlatform(
TargetPlatform platform,
{File applicationBinary}) async {
switch (platform) {
case TargetPlatform.android_arm:
case TargetPlatform.android_arm64:
case TargetPlatform.android_x64:
case TargetPlatform.android_x86:
return applicationBinary == null
? await AndroidApk.fromCurrentDirectory()
? await AndroidApk.fromAndroidProject(new FlutterProject(fs.currentDirectory).android)
: new AndroidApk.fromApk(applicationBinary);
case TargetPlatform.ios:
return applicationBinary == null
@ -344,7 +344,7 @@ class ApplicationPackageStore {
case TargetPlatform.android_arm64:
case TargetPlatform.android_x64:
case TargetPlatform.android_x86:
android ??= await AndroidApk.fromCurrentDirectory();
android ??= await AndroidApk.fromAndroidProject(new FlutterProject(fs.currentDirectory).android);
return android;
case TargetPlatform.ios:
iOS ??= new IOSApp.fromCurrentDirectory();

View file

@ -5,6 +5,8 @@
import 'dart:async';
import '../android/apk.dart';
import '../base/file_system.dart';
import '../project.dart';
import 'build.dart';
class BuildApkCommand extends BuildSubCommand {
@ -44,6 +46,6 @@ class BuildApkCommand extends BuildSubCommand {
@override
Future<Null> runCommand() async {
await super.runCommand();
await buildApk(buildInfo: getBuildInfo(), target: targetFile);
await buildApk(project: new FlutterProject(fs.currentDirectory),target: targetFile, buildInfo: getBuildInfo());
}
}

View file

@ -168,20 +168,19 @@ class CreateCommand extends FlutterCommand {
printStatus('Creating project ${fs.path.relative(dirPath)}...');
int generatedFileCount = 0;
String appPath = dirPath;
final FlutterProject project = new FlutterProject.fromPath(dirPath);
switch (template) {
case 'app':
generatedFileCount += await _generateApp(dirPath, templateContext);
generatedFileCount += await _generateApp(project, templateContext);
break;
case 'module':
generatedFileCount += await _generateModule(dirPath, templateContext);
generatedFileCount += await _generateModule(project, templateContext);
break;
case 'package':
generatedFileCount += await _generatePackage(dirPath, templateContext);
generatedFileCount += await _generatePackage(project, templateContext);
break;
case 'plugin':
appPath = fs.path.join(dirPath, 'example');
generatedFileCount += await _generatePlugin(dirPath, appPath, templateContext);
generatedFileCount += await _generatePlugin(project, templateContext);
break;
}
printStatus('Wrote $generatedFileCount files.');
@ -194,7 +193,8 @@ class CreateCommand extends FlutterCommand {
printStatus('Your module code is in lib/main.dart in the $relativePath directory.');
} else {
// Run doctor; tell the user the next steps.
final String relativeAppPath = fs.path.relative(appPath);
final FlutterProject app = project.hasExampleApp ? project.example : project;
final String relativeAppPath = fs.path.relative(app.directory.path);
final String relativePluginPath = fs.path.relative(dirPath);
if (doctor.canLaunchAnything) {
// Let them know a summary of the state of their tooling.
@ -233,60 +233,59 @@ To edit platform code in an IDE see https://flutter.io/developing-packages/#edit
}
}
Future<int> _generateModule(String path, Map<String, dynamic> templateContext) async {
Future<int> _generateModule(FlutterProject project, Map<String, dynamic> templateContext) async {
int generatedCount = 0;
final String description = argResults.wasParsed('description')
? argResults['description']
: 'A new flutter module project.';
templateContext['description'] = description;
generatedCount += _renderTemplate(fs.path.join('module', 'common'), path, templateContext);
generatedCount += _renderTemplate(fs.path.join('module', 'common'), project.directory, templateContext);
if (argResults['pub']) {
await pubGet(
context: PubContext.create,
directory: path,
directory: project.directory.path,
offline: argResults['offline'],
);
final FlutterProject project = new FlutterProject.fromPath(path);
await project.ensureReadyForPlatformSpecificTooling();
}
return generatedCount;
}
Future<int> _generatePackage(String dirPath, Map<String, dynamic> templateContext) async {
Future<int> _generatePackage(FlutterProject project, Map<String, dynamic> templateContext) async {
int generatedCount = 0;
final String description = argResults.wasParsed('description')
? argResults['description']
: 'A new flutter package project.';
templateContext['description'] = description;
generatedCount += _renderTemplate('package', dirPath, templateContext);
generatedCount += _renderTemplate('package', project.directory, templateContext);
if (argResults['pub']) {
await pubGet(
context: PubContext.createPackage,
directory: dirPath,
directory: project.directory.path,
offline: argResults['offline'],
);
}
return generatedCount;
}
Future<int> _generatePlugin(String dirPath, String appPath, Map<String, dynamic> templateContext) async {
Future<int> _generatePlugin(FlutterProject project, Map<String, dynamic> templateContext) async {
int generatedCount = 0;
final String description = argResults.wasParsed('description')
? argResults['description']
: 'A new flutter plugin project.';
templateContext['description'] = description;
generatedCount += _renderTemplate('plugin', dirPath, templateContext);
generatedCount += _renderTemplate('plugin', project.directory, templateContext);
if (argResults['pub']) {
await pubGet(
context: PubContext.createPlugin,
directory: dirPath,
directory: project.directory.path,
offline: argResults['offline'],
);
}
if (android_sdk.androidSdk != null)
await gradle.updateLocalProperties(projectPath: dirPath);
await gradle.updateLocalProperties(project: project);
final String projectName = templateContext['projectName'];
final String organization = templateContext['organization'];
@ -299,27 +298,27 @@ To edit platform code in an IDE see https://flutter.io/developing-packages/#edit
templateContext['pluginProjectName'] = projectName;
templateContext['androidPluginIdentifier'] = androidPluginIdentifier;
generatedCount += await _generateApp(appPath, templateContext);
generatedCount += await _generateApp(project.example, templateContext);
return generatedCount;
}
Future<int> _generateApp(String projectPath, Map<String, dynamic> templateContext) async {
Future<int> _generateApp(FlutterProject project, Map<String, dynamic> templateContext) async {
int generatedCount = 0;
generatedCount += _renderTemplate('create', projectPath, templateContext);
generatedCount += _injectGradleWrapper(projectPath);
generatedCount += _renderTemplate('create', project.directory, templateContext);
generatedCount += _injectGradleWrapper(project);
if (argResults['with-driver-test']) {
final String testPath = fs.path.join(projectPath, 'test_driver');
generatedCount += _renderTemplate('driver', testPath, templateContext);
final Directory testDirectory = project.directory.childDirectory('test_driver');
generatedCount += _renderTemplate('driver', testDirectory, templateContext);
}
if (argResults['pub']) {
await pubGet(context: PubContext.create, directory: projectPath, offline: argResults['offline']);
await new FlutterProject.fromPath(projectPath).ensureReadyForPlatformSpecificTooling();
await pubGet(context: PubContext.create, directory: project.directory.path, offline: argResults['offline']);
await project.ensureReadyForPlatformSpecificTooling();
}
if (android_sdk.androidSdk != null)
await gradle.updateLocalProperties(projectPath: projectPath);
await gradle.updateLocalProperties(project: project);
return generatedCount;
}
@ -362,16 +361,16 @@ To edit platform code in an IDE see https://flutter.io/developing-packages/#edit
};
}
int _renderTemplate(String templateName, String dirPath, Map<String, dynamic> context) {
int _renderTemplate(String templateName, Directory directory, Map<String, dynamic> context) {
final Template template = new Template.fromName(templateName);
return template.render(fs.directory(dirPath), context, overwriteExisting: false);
return template.render(directory, context, overwriteExisting: false);
}
int _injectGradleWrapper(String projectDir) {
int _injectGradleWrapper(FlutterProject project) {
int filesCreated = 0;
copyDirectorySync(
cache.getArtifactDirectory('gradle_wrapper'),
fs.directory(fs.path.join(projectDir, 'android')),
project.android.directory,
(File sourceFile, File destinationFile) {
filesCreated++;
final String modes = sourceFile.statSync().modeString();

View file

@ -322,7 +322,7 @@ class AppDomain extends Domain {
Future<AppInstance> startApp(
Device device, String projectDirectory, String target, String route,
DebuggingOptions options, bool enableHotReload, {
String applicationBinary,
File applicationBinary,
@required bool trackWidgetCreation,
String projectRootPath,
String packagesFilePath,

View file

@ -5,9 +5,9 @@
import 'dart:async';
import '../base/file_system.dart';
import '../flutter_manifest.dart';
import '../globals.dart';
import '../plugins.dart';
import '../project.dart';
import '../runner/flutter_command.dart';
class InjectPluginsCommand extends FlutterCommand {
@ -26,10 +26,9 @@ class InjectPluginsCommand extends FlutterCommand {
@override
Future<Null> runCommand() async {
final String projectPath = fs.currentDirectory.path;
final FlutterManifest manifest = await FlutterManifest.createFromPath(projectPath);
injectPlugins(projectPath: projectPath, manifest: manifest);
final bool result = hasPlugins();
final FlutterProject project = new FlutterProject(fs.currentDirectory);
await injectPlugins(project);
final bool result = hasPlugins(project);
if (result) {
printStatus('GeneratedPluginRegistrants successfully written.');
} else {

View file

@ -289,10 +289,13 @@ class RunCommand extends RunCommandBase {
notifyingLogger: new NotifyingLogger(), logToStdout: true);
AppInstance app;
try {
final String applicationBinaryPath = argResults['use-application-binary'];
app = await daemon.appDomain.startApp(
devices.first, fs.currentDirectory.path, targetFile, route,
_createDebuggingOptions(), hotMode,
applicationBinary: argResults['use-application-binary'],
applicationBinary: applicationBinaryPath == null
? null
: fs.file(applicationBinaryPath),
trackWidgetCreation: argResults['track-widget-creation'],
projectRootPath: argResults['project-root'],
packagesFilePath: globalResults['packages'],

View file

@ -15,8 +15,8 @@ import '../base/process.dart';
import '../base/process_manager.dart';
import '../base/version.dart';
import '../cache.dart';
import '../flutter_manifest.dart';
import '../globals.dart';
import '../project.dart';
import 'xcodeproj.dart';
const String noCocoaPodsConsequence = '''
@ -81,18 +81,18 @@ class CocoaPods {
Future<bool> get isCocoaPodsInitialized => fs.isDirectory(fs.path.join(homeDirPath, '.cocoapods', 'repos', 'master'));
Future<bool> processPods({
@required Directory appIosDirectory,
@required IosProject iosProject,
// For backward compatibility with previously created Podfile only.
@required String iosEngineDir,
bool isSwift = false,
bool dependenciesChanged = true,
}) async {
if (!(await appIosDirectory.childFile('Podfile').exists())) {
if (!(await iosProject.podfile.exists())) {
throwToolExit('Podfile missing');
}
if (await _checkPodCondition()) {
if (_shouldRunPodInstall(appIosDirectory, dependenciesChanged)) {
await _runPodInstall(appIosDirectory, iosEngineDir);
if (_shouldRunPodInstall(iosProject, dependenciesChanged)) {
await _runPodInstall(iosProject, iosEngineDir);
return true;
}
}
@ -151,18 +151,18 @@ class CocoaPods {
/// Ensures the `ios` sub-project of the Flutter project at [appDirectory]
/// contains a suitable `Podfile` and that its `Flutter/Xxx.xcconfig` files
/// include pods configuration.
void setupPodfile(String appDirectory, FlutterManifest manifest) {
void setupPodfile(IosProject iosProject) {
if (!xcodeProjectInterpreter.isInstalled) {
// Don't do anything for iOS when host platform doesn't support it.
return;
}
if (!fs.directory(fs.path.join(appDirectory, 'ios')).existsSync()) {
if (!iosProject.directory.existsSync()) {
return;
}
final String podfilePath = fs.path.join(appDirectory, 'ios', 'Podfile');
if (!fs.file(podfilePath).existsSync()) {
final File podfile = iosProject.podfile;
if (!podfile.existsSync()) {
final bool isSwift = xcodeProjectInterpreter.getBuildSettings(
fs.path.join(appDirectory, 'ios', 'Runner.xcodeproj'),
iosProject.directory.childFile('Runner.xcodeproj').path,
'Runner',
).containsKey('SWIFT_VERSION');
final File podfileTemplate = fs.file(fs.path.join(
@ -173,15 +173,14 @@ class CocoaPods {
'cocoapods',
isSwift ? 'Podfile-swift' : 'Podfile-objc',
));
podfileTemplate.copySync(podfilePath);
podfileTemplate.copySync(podfile.path);
}
_addPodsDependencyToFlutterXcconfig(appDirectory, 'Debug');
_addPodsDependencyToFlutterXcconfig(appDirectory, 'Release');
_addPodsDependencyToFlutterXcconfig(iosProject, 'Debug');
_addPodsDependencyToFlutterXcconfig(iosProject, 'Release');
}
void _addPodsDependencyToFlutterXcconfig(String appDirectory, String mode) {
final File file = fs.file(fs.path.join(appDirectory, 'ios', 'Flutter', '$mode.xcconfig'));
void _addPodsDependencyToFlutterXcconfig(IosProject iosProject, String mode) {
final File file = iosProject.xcodeConfigFor(mode);
if (file.existsSync()) {
final String content = file.readAsStringSync();
final String include = '#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.${mode
@ -192,12 +191,11 @@ class CocoaPods {
}
/// Ensures that pod install is deemed needed on next check.
void invalidatePodInstallOutput(String appDirectory) {
final File manifest = fs.file(
fs.path.join(appDirectory, 'ios', 'Pods', 'Manifest.lock'),
);
if (manifest.existsSync())
manifest.deleteSync();
void invalidatePodInstallOutput(IosProject iosProject) {
final File manifestLock = iosProject.podManifestLock;
if (manifestLock.existsSync()) {
manifestLock.deleteSync();
}
}
// Check if you need to run pod install.
@ -206,24 +204,25 @@ class CocoaPods {
// 2. Podfile.lock doesn't exist or is older than Podfile
// 3. Pods/Manifest.lock doesn't exist (It is deleted when plugins change)
// 4. Podfile.lock doesn't match Pods/Manifest.lock.
bool _shouldRunPodInstall(Directory appIosDirectory, bool dependenciesChanged) {
bool _shouldRunPodInstall(IosProject iosProject, bool dependenciesChanged) {
if (dependenciesChanged)
return true;
final File podfileFile = appIosDirectory.childFile('Podfile');
final File podfileLockFile = appIosDirectory.childFile('Podfile.lock');
final File manifestLockFile =
appIosDirectory.childFile(fs.path.join('Pods', 'Manifest.lock'));
final File podfileFile = iosProject.podfile;
final File podfileLockFile = iosProject.podfileLock;
final File manifestLockFile = iosProject.podManifestLock;
return !podfileLockFile.existsSync()
|| !manifestLockFile.existsSync()
|| podfileLockFile.statSync().modified.isBefore(podfileFile.statSync().modified)
|| podfileLockFile.readAsStringSync() != manifestLockFile.readAsStringSync();
}
Future<Null> _runPodInstall(Directory appIosDirectory, String engineDirectory) async {
Future<Null> _runPodInstall(IosProject iosProject, String engineDirectory) async {
final Status status = logger.startProgress('Running pod install...', expectSlowOperation: true);
final ProcessResult result = await processManager.run(
<String>['pod', 'install', '--verbose'],
workingDirectory: appIosDirectory.path,
workingDirectory: iosProject.directory.path,
environment: <String, String>{
// For backward compatibility with previously created Podfile only.
'FLUTTER_FRAMEWORK_DIR': engineDirectory,
@ -244,7 +243,7 @@ class CocoaPods {
}
}
if (result.exitCode != 0) {
invalidatePodInstallOutput(appIosDirectory.parent.path);
invalidatePodInstallOutput(iosProject);
_diagnosePodInstallFailure(result);
throwToolExit('Error running pod install');
}

View file

@ -20,9 +20,9 @@ import '../base/process.dart';
import '../base/process_manager.dart';
import '../base/utils.dart';
import '../build_info.dart';
import '../flutter_manifest.dart';
import '../globals.dart';
import '../plugins.dart';
import '../project.dart';
import '../services.dart';
import 'cocoapods.dart';
import 'code_signing.dart';
@ -221,18 +221,15 @@ Future<XcodeBuildResult> buildXcodeProject({
final Directory appDirectory = fs.directory(app.appDirectory);
await _addServicesToBundle(appDirectory);
final FlutterManifest manifest = await FlutterManifest.createFromPath(
fs.currentDirectory.childFile('pubspec.yaml').path,
);
updateGeneratedXcodeProperties(
projectPath: fs.currentDirectory.path,
buildInfo: buildInfo,
final FlutterProject project = new FlutterProject(fs.currentDirectory);
await updateGeneratedXcodeProperties(
project: project,
targetOverride: targetOverride,
previewDart2: buildInfo.previewDart2,
manifest: manifest,
buildInfo: buildInfo,
);
if (hasPlugins()) {
if (hasPlugins(project)) {
final String iosPath = fs.path.join(fs.currentDirectory.path, app.appDirectory);
// If the Xcode project, Podfile, or Generated.xcconfig have changed since
// last run, pods should be updated.
@ -246,7 +243,7 @@ Future<XcodeBuildResult> buildXcodeProject({
properties: <String, String>{},
);
final bool didPodInstall = await cocoaPods.processPods(
appIosDirectory: appDirectory,
iosProject: project.ios,
iosEngineDir: flutterFrameworkDir(buildInfo.mode),
isSwift: app.isSwift,
dependenciesChanged: !await fingerprinter.doesFingerprintMatch()

View file

@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:async';
import 'package:meta/meta.dart';
import '../artifacts.dart';
@ -17,6 +19,7 @@ import '../bundle.dart' as bundle;
import '../cache.dart';
import '../flutter_manifest.dart';
import '../globals.dart';
import '../project.dart';
final RegExp _settingExpr = new RegExp(r'(\w+)\s*=\s*(.*)$');
final RegExp _varExpr = new RegExp(r'\$\((.*)\)');
@ -25,27 +28,18 @@ String flutterFrameworkDir(BuildMode mode) {
return fs.path.normalize(fs.path.dirname(artifacts.getArtifactPath(Artifact.flutterFramework, TargetPlatform.ios, mode)));
}
String _generatedXcodePropertiesPath({@required String projectPath, @required FlutterManifest manifest}) {
if (manifest.isModule) {
return fs.path.join(projectPath, '.ios', 'Flutter', 'Generated.xcconfig');
} else {
return fs.path.join(projectPath, 'ios', 'Flutter', 'Generated.xcconfig');
}
}
/// Writes default Xcode properties files in the Flutter project at [projectPath],
/// if project is an iOS project and such files are out of date or do not
/// already exist.
void generateXcodeProperties({String projectPath, FlutterManifest manifest}) {
if (manifest.isModule || fs.isDirectorySync(fs.path.join(projectPath, 'ios'))) {
final File propertiesFile = fs.file(_generatedXcodePropertiesPath(projectPath: projectPath, manifest: manifest));
if (!Cache.instance.fileOlderThanToolsStamp(propertiesFile)) {
Future<void> generateXcodeProperties({FlutterProject project}) async {
if ((await project.manifest).isModule ||
project.ios.directory.existsSync()) {
if (!Cache.instance.fileOlderThanToolsStamp(await project.generatedXcodePropertiesFile)) {
return;
}
updateGeneratedXcodeProperties(
projectPath: projectPath,
manifest: manifest,
await updateGeneratedXcodeProperties(
project: project,
buildInfo: BuildInfo.debug,
targetOverride: bundle.defaultMainPath,
previewDart2: true,
@ -57,13 +51,12 @@ void generateXcodeProperties({String projectPath, FlutterManifest manifest}) {
///
/// targetOverride: Optional parameter, if null or unspecified the default value
/// from xcode_backend.sh is used 'lib/main.dart'.
void updateGeneratedXcodeProperties({
@required String projectPath,
@required FlutterManifest manifest,
Future<void> updateGeneratedXcodeProperties({
@required FlutterProject project,
@required BuildInfo buildInfo,
String targetOverride,
@required bool previewDart2,
}) {
}) async {
final StringBuffer localsBuffer = new StringBuffer();
localsBuffer.writeln('// This is a generated file; do not edit or check into version control.');
@ -72,7 +65,7 @@ void updateGeneratedXcodeProperties({
localsBuffer.writeln('FLUTTER_ROOT=$flutterRoot');
// This holds because requiresProjectRoot is true for this command
localsBuffer.writeln('FLUTTER_APPLICATION_PATH=${fs.path.normalize(projectPath)}');
localsBuffer.writeln('FLUTTER_APPLICATION_PATH=${fs.path.normalize(project.directory.path)}');
// Relative to FLUTTER_APPLICATION_PATH, which is [Directory.current].
if (targetOverride != null)
@ -86,6 +79,7 @@ void updateGeneratedXcodeProperties({
localsBuffer.writeln('SYMROOT=\${SOURCE_ROOT}/../${getIosBuildDirectory()}');
final FlutterManifest manifest = await project.manifest;
if (!manifest.isModule) {
// For module projects we do not want to write the FLUTTER_FRAMEWORK_DIR
// explicitly. Rather we rely on the xcode backend script and the Podfile
@ -125,9 +119,9 @@ void updateGeneratedXcodeProperties({
localsBuffer.writeln('TRACK_WIDGET_CREATION=true');
}
final File localsFile = fs.file(_generatedXcodePropertiesPath(projectPath: projectPath, manifest: manifest));
localsFile.createSync(recursive: true);
localsFile.writeAsStringSync(localsBuffer.toString());
final File generatedXcodePropertiesFile = await project.generatedXcodePropertiesFile;
generatedXcodePropertiesFile.createSync(recursive: true);
generatedXcodePropertiesFile.writeAsStringSync(localsBuffer.toString());
}
XcodeProjectInterpreter get xcodeProjectInterpreter => context[XcodeProjectInterpreter];

View file

@ -2,15 +2,16 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:meta/meta.dart';
import 'dart:async';
import 'package:mustache/mustache.dart' as mustache;
import 'package:yaml/yaml.dart';
import 'base/file_system.dart';
import 'dart/package_map.dart';
import 'flutter_manifest.dart';
import 'globals.dart';
import 'ios/cocoapods.dart';
import 'project.dart';
void _renderTemplateToFile(String template, dynamic context, String filePath) {
final String renderedTemplate =
@ -69,11 +70,11 @@ Plugin _pluginFromPubspec(String name, Uri packageRoot) {
return new Plugin.fromYaml(name, packageRootPath, flutterConfig['plugin']);
}
List<Plugin> findPlugins(String directory) {
List<Plugin> findPlugins(FlutterProject project) {
final List<Plugin> plugins = <Plugin>[];
Map<String, Uri> packages;
try {
final String packagesFile = fs.path.join(directory, PackageMap.globalPackagesPath);
final String packagesFile = fs.path.join(project.directory.path, PackageMap.globalPackagesPath);
packages = new PackageMap(packagesFile).map;
} on FormatException catch (e) {
printTrace('Invalid .packages file: $e');
@ -89,9 +90,9 @@ List<Plugin> findPlugins(String directory) {
}
/// Returns true if .flutter-plugins has changed, otherwise returns false.
bool _writeFlutterPluginsList(String directory, List<Plugin> plugins) {
final File pluginsFile = fs.file(fs.path.join(directory, '.flutter-plugins'));
final String oldContents = _readFlutterPluginsList(directory);
bool _writeFlutterPluginsList(FlutterProject project, List<Plugin> plugins) {
final File pluginsFile = project.flutterPluginsFile;
final String oldContents = _readFlutterPluginsList(project);
final String pluginManifest =
plugins.map((Plugin p) => '${p.name}=${escapePath(p.path)}').join('\n');
if (pluginManifest.isNotEmpty) {
@ -100,15 +101,16 @@ bool _writeFlutterPluginsList(String directory, List<Plugin> plugins) {
if (pluginsFile.existsSync())
pluginsFile.deleteSync();
}
final String newContents = _readFlutterPluginsList(directory);
final String newContents = _readFlutterPluginsList(project);
return oldContents != newContents;
}
/// Returns the contents of the `.flutter-plugins` file in [directory], or
/// null if that file does not exist.
String _readFlutterPluginsList(String directory) {
final File pluginsFile = fs.file(fs.path.join(directory, '.flutter-plugins'));
return pluginsFile.existsSync() ? pluginsFile.readAsStringSync() : null;
String _readFlutterPluginsList(FlutterProject project) {
return project.flutterPluginsFile.existsSync()
? project.flutterPluginsFile.readAsStringSync()
: null;
}
const String _androidPluginRegistryTemplate = '''package io.flutter.plugins;
@ -142,7 +144,7 @@ public final class GeneratedPluginRegistrant {
}
''';
void _writeAndroidPluginRegistrant(String directory, List<Plugin> plugins) {
Future<void> _writeAndroidPluginRegistrant(FlutterProject project, List<Plugin> plugins) async {
final List<Map<String, dynamic>> androidPlugins = plugins
.where((Plugin p) => p.androidPackage != null && p.pluginClass != null)
.map((Plugin p) => <String, dynamic>{
@ -155,8 +157,19 @@ void _writeAndroidPluginRegistrant(String directory, List<Plugin> plugins) {
'plugins': androidPlugins,
};
final String javaSourcePath = fs.path.join(directory, 'src', 'main', 'java');
final String registryPath = fs.path.join(javaSourcePath, 'io', 'flutter', 'plugins', 'GeneratedPluginRegistrant.java');
final String javaSourcePath = fs.path.join(
(await project.androidPluginRegistrantHost).path,
'src',
'main',
'java',
);
final String registryPath = fs.path.join(
javaSourcePath,
'io',
'flutter',
'plugins',
'GeneratedPluginRegistrant.java',
);
_renderTemplateToFile(_androidPluginRegistryTemplate, context, registryPath);
}
@ -221,7 +234,7 @@ Depends on all your plugins, and provides a function to register them.
end
''';
void _writeIOSPluginRegistrant(String directory, FlutterManifest manifest, List<Plugin> plugins) {
Future<void> _writeIOSPluginRegistrant(FlutterProject project, List<Plugin> plugins) async {
final List<Map<String, dynamic>> iosPlugins = plugins
.where((Plugin p) => p.pluginClass != null)
.map((Plugin p) => <String, dynamic>{
@ -234,10 +247,8 @@ void _writeIOSPluginRegistrant(String directory, FlutterManifest manifest, List<
'plugins': iosPlugins,
};
if (manifest.isModule) {
// In a module create the GeneratedPluginRegistrant as a pod to be included
// from a hosting app.
final String registryDirectory = fs.path.join(directory, 'Flutter', 'FlutterPluginRegistrant');
final String registryDirectory = (await project.iosPluginRegistrantHost).path;
if ((await project.manifest).isModule) {
final String registryClassesDirectory = fs.path.join(registryDirectory, 'Classes');
_renderTemplateToFile(
_iosPluginRegistrantPodspecTemplate,
@ -255,57 +266,37 @@ void _writeIOSPluginRegistrant(String directory, FlutterManifest manifest, List<
fs.path.join(registryClassesDirectory, 'GeneratedPluginRegistrant.m'),
);
} else {
// For a non-module create the GeneratedPluginRegistrant as source files
// directly in the ios project.
final String runnerDirectory = fs.path.join(directory, 'Runner');
_renderTemplateToFile(
_iosPluginRegistryHeaderTemplate,
context,
fs.path.join(runnerDirectory, 'GeneratedPluginRegistrant.h'),
fs.path.join(registryDirectory, 'GeneratedPluginRegistrant.h'),
);
_renderTemplateToFile(
_iosPluginRegistryImplementationTemplate,
context,
fs.path.join(runnerDirectory, 'GeneratedPluginRegistrant.m'),
fs.path.join(registryDirectory, 'GeneratedPluginRegistrant.m'),
);
}
}
class InjectPluginsResult{
InjectPluginsResult({
@required this.hasPlugin,
@required this.hasChanged,
});
/// True if any flutter plugin exists, otherwise false.
final bool hasPlugin;
/// True if plugins have changed since last build.
final bool hasChanged;
}
/// Injects plugins found in `pubspec.yaml` into the platform-specific projects.
void injectPlugins({@required String projectPath, @required FlutterManifest manifest}) {
final List<Plugin> plugins = findPlugins(projectPath);
final bool changed = _writeFlutterPluginsList(projectPath, plugins);
if (manifest.isModule) {
_writeAndroidPluginRegistrant(fs.path.join(projectPath, '.android', 'Flutter'), plugins);
} else if (fs.isDirectorySync(fs.path.join(projectPath, 'android', 'app'))) {
_writeAndroidPluginRegistrant(fs.path.join(projectPath, 'android', 'app'), plugins);
}
if (manifest.isModule) {
_writeIOSPluginRegistrant(fs.path.join(projectPath, '.ios'), manifest, plugins);
} else if (fs.isDirectorySync(fs.path.join(projectPath, 'ios'))) {
_writeIOSPluginRegistrant(fs.path.join(projectPath, 'ios'), manifest, plugins);
Future<void> injectPlugins(FlutterProject project) async {
final List<Plugin> plugins = findPlugins(project);
final bool changed = _writeFlutterPluginsList(project, plugins);
await _writeAndroidPluginRegistrant(project, plugins);
await _writeIOSPluginRegistrant(project, plugins);
if (project.ios.directory.existsSync()) {
final CocoaPods cocoaPods = new CocoaPods();
if (plugins.isNotEmpty)
cocoaPods.setupPodfile(projectPath, manifest);
cocoaPods.setupPodfile(project.ios);
if (changed)
cocoaPods.invalidatePodInstallOutput(projectPath);
cocoaPods.invalidatePodInstallOutput(project.ios);
}
}
/// Returns whether the Flutter project at the specified [directory]
/// has any plugin dependencies.
bool hasPlugins({String directory}) {
directory ??= fs.currentDirectory.path;
return _readFlutterPluginsList(directory) != null;
bool hasPlugins(FlutterProject project) {
return _readFlutterPluginsList(project) != null;
}

View file

@ -5,7 +5,6 @@
import 'dart:async';
import 'dart:convert';
import 'android/gradle.dart' as gradle;
import 'base/file_system.dart';
import 'bundle.dart' as bundle;
@ -67,6 +66,57 @@ class FlutterProject {
/// The generated IosModule sub project of this module project.
IosModuleProject get iosModule => new IosModuleProject(directory.childDirectory('.ios'));
Future<File> get androidLocalPropertiesFile {
return _androidLocalPropertiesFile ??= manifest.then<File>((FlutterManifest manifest) {
return directory.childDirectory(manifest.isModule ? '.android' : 'android')
.childFile('local.properties');
});
}
Future<File> _androidLocalPropertiesFile;
Future<File> get generatedXcodePropertiesFile {
return _generatedXcodeProperties ??= manifest.then<File>((FlutterManifest manifest) {
return directory.childDirectory(manifest.isModule ? '.ios' : 'ios')
.childDirectory('Flutter')
.childFile('Generated.xcconfig');
});
}
Future<File> _generatedXcodeProperties;
File get flutterPluginsFile {
return _flutterPluginsFile ??= directory.childFile('.flutter-plugins');
}
File _flutterPluginsFile;
Future<Directory> get androidPluginRegistrantHost async {
return _androidPluginRegistrantHost ??= manifest.then((FlutterManifest manifest) {
if (manifest.isModule) {
return directory.childDirectory('.android').childDirectory('Flutter');
} else {
return directory.childDirectory('android').childDirectory('app');
}
});
}
Future<Directory> _androidPluginRegistrantHost;
Future<Directory> get iosPluginRegistrantHost async {
return _iosPluginRegistrantHost ??= manifest.then((FlutterManifest manifest) {
if (manifest.isModule) {
// In a module create the GeneratedPluginRegistrant as a pod to be included
// from a hosting app.
return directory
.childDirectory('.ios')
.childDirectory('Flutter')
.childDirectory('FlutterPluginRegistrant');
} else {
// For a non-module create the GeneratedPluginRegistrant as source files
// directly in the iOS project.
return directory.childDirectory('ios').childDirectory('Runner');
}
});
}
Future<Directory> _iosPluginRegistrantHost;
/// Returns true if this project has an example application
bool get hasExampleApp => _exampleDirectory.childFile('pubspec.yaml').existsSync();
@ -86,11 +136,11 @@ class FlutterProject {
}
final FlutterManifest manifest = await this.manifest;
if (manifest.isModule) {
await androidModule.ensureReadyForPlatformSpecificTooling(manifest);
await iosModule.ensureReadyForPlatformSpecificTooling(manifest);
await androidModule.ensureReadyForPlatformSpecificTooling(this);
await iosModule.ensureReadyForPlatformSpecificTooling();
}
xcode.generateXcodeProperties(projectPath: directory.path, manifest: manifest);
injectPlugins(projectPath: directory.path, manifest: manifest);
await xcode.generateXcodeProperties(project: this);
await injectPlugins(this);
}
}
@ -101,6 +151,20 @@ class IosProject {
final Directory directory;
/// The xcode config file for [mode].
File xcodeConfigFor(String mode) {
return directory.childDirectory('Flutter').childFile('$mode.xcconfig');
}
/// The 'Podfile'.
File get podfile => directory.childFile('Podfile');
/// The 'Podfile.lock'.
File get podfileLock => directory.childFile('Podfile.lock');
/// The 'Manifest.lock'.
File get podManifestLock => directory.childDirectory('Pods').childFile('Manifest.lock');
Future<String> productBundleIdentifier() {
final File projectFile = directory.childDirectory('Runner.xcodeproj').childFile('project.pbxproj');
return _firstMatchInFile(projectFile, _productBundleIdPattern).then((Match match) => match?.group(1));
@ -114,7 +178,7 @@ class IosModuleProject {
final Directory directory;
Future<void> ensureReadyForPlatformSpecificTooling(FlutterManifest manifest) async {
Future<void> ensureReadyForPlatformSpecificTooling() async {
if (_shouldRegenerate()) {
final Template template = new Template.fromName(fs.path.join('module', 'ios'));
template.render(directory, <String, dynamic>{}, printStatusWhenWriting: false);
@ -133,6 +197,29 @@ class AndroidProject {
AndroidProject(this.directory);
File get gradleManifestFile {
return _gradleManifestFile ??= isUsingGradle()
? fs.file(fs.path.join(directory.path, 'app', 'src', 'main', 'AndroidManifest.xml'))
: directory.childFile('AndroidManifest.xml');
}
File _gradleManifestFile;
File get gradleAppOutV1File {
return _gradleAppOutV1File ??= gradleAppOutV1Directory.childFile('app-debug.apk');
}
File _gradleAppOutV1File;
Directory get gradleAppOutV1Directory {
return _gradleAppOutV1Directory ??= fs.directory(fs.path.join(directory.path, 'app', 'build', 'outputs', 'apk'));
}
Directory _gradleAppOutV1Directory;
bool isUsingGradle() {
return directory.childFile('build.gradle').existsSync();
}
final Directory directory;
Future<String> applicationId() {
@ -153,15 +240,15 @@ class AndroidModuleProject {
final Directory directory;
Future<void> ensureReadyForPlatformSpecificTooling(FlutterManifest manifest) async {
Future<void> ensureReadyForPlatformSpecificTooling(FlutterProject project) async {
if (_shouldRegenerate()) {
final Template template = new Template.fromName(fs.path.join('module', 'android'));
template.render(directory, <String, dynamic>{
'androidIdentifier': manifest.moduleDescriptor['androidPackage'],
'androidIdentifier': (await project.manifest).moduleDescriptor['androidPackage'],
}, printStatusWhenWriting: false);
gradle.injectGradleWrapper(directory);
}
gradle.updateLocalPropertiesSync(directory, manifest);
await gradle.updateLocalProperties(project: project, requireAndroidSdk: false);
}
bool _shouldRegenerate() {

View file

@ -6,7 +6,6 @@ import 'dart:async';
import 'package:meta/meta.dart';
import 'android/gradle.dart';
import 'application_package.dart';
import 'artifacts.dart';
import 'asset.dart';
@ -24,6 +23,7 @@ import 'dependency_checker.dart';
import 'devfs.dart';
import 'device.dart';
import 'globals.dart';
import 'project.dart';
import 'run_cold.dart';
import 'run_hot.dart';
import 'vmservice.dart';
@ -814,15 +814,11 @@ abstract class ResidentRunner {
new DartDependencySetBuilder(mainPath, packagesFilePath);
final DependencyChecker dependencyChecker =
new DependencyChecker(dartDependencySetBuilder, assetBundle);
final String path = device.package.packagePath;
if (path == null)
if (device.package.packagesFile == null || !device.package.packagesFile.existsSync()) {
return true;
final FileStat stat = fs.file(path).statSync();
if (stat.type != FileSystemEntityType.FILE) // ignore: deprecated_member_use
return true;
if (!fs.file(path).existsSync())
return true;
final DateTime lastBuildTime = stat.modified;
}
final DateTime lastBuildTime = device.package.packagesFile.statSync().modified;
return dependencyChecker.check(lastBuildTime);
}
@ -906,11 +902,9 @@ String getMissingPackageHintForPlatform(TargetPlatform platform) {
case TargetPlatform.android_arm64:
case TargetPlatform.android_x64:
case TargetPlatform.android_x86:
String manifest = 'android/AndroidManifest.xml';
if (isProjectUsingGradle()) {
manifest = gradleManifestPath;
}
return 'Is your project missing an $manifest?\nConsider running "flutter create ." to create one.';
final FlutterProject project = new FlutterProject(fs.currentDirectory);
final String manifestPath = fs.path.relative(project.android.gradleManifestFile.path);
return 'Is your project missing an $manifestPath?\nConsider running "flutter create ." to create one.';
case TargetPlatform.ios:
return 'Is your project missing an ios/Runner/Info.plist?\nConsider running "flutter create ." to create one.';
default:

View file

@ -30,7 +30,7 @@ class ColdRunner extends ResidentRunner {
ipv6: ipv6);
final bool traceStartup;
final String applicationBinary;
final File applicationBinary;
@override
Future<int> run({

View file

@ -64,7 +64,7 @@ class HotRunner extends ResidentRunner {
ipv6: ipv6);
final bool benchmarkMode;
final String applicationBinary;
final File applicationBinary;
final bool hostIsIde;
Set<String> _dartDependencies;
final String dillOutputPath;

View file

@ -22,21 +22,21 @@ import '../protocol_discovery.dart';
import '../version.dart';
class FlutterTesterApp extends ApplicationPackage {
final String _directory;
final Directory _directory;
factory FlutterTesterApp.fromCurrentDirectory() {
return new FlutterTesterApp._(fs.currentDirectory.path);
return new FlutterTesterApp._(fs.currentDirectory);
}
FlutterTesterApp._(String directory)
FlutterTesterApp._(Directory directory)
: _directory = directory,
super(id: directory);
super(id: directory.path);
@override
String get name => fs.path.basename(_directory);
String get name => _directory.basename;
@override
String get packagePath => fs.path.join(_directory, '.packages');
File get packagesFile => _directory.childFile('.packages');
}
// TODO(scheglov): This device does not currently work with full restarts.

View file

@ -11,6 +11,7 @@ import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/build_info.dart';
import 'package:flutter_tools/src/flutter_manifest.dart';
import 'package:flutter_tools/src/ios/xcodeproj.dart';
import 'package:flutter_tools/src/project.dart';
import 'package:mockito/mockito.dart';
import 'package:platform/platform.dart';
import 'package:process/process.dart';
@ -31,7 +32,7 @@ void main() {
// This test is written to fail if our bots get Android SDKs in the future: shouldBeToolExit
// will be null and our expectation would fail. That would remind us to make these tests
// hermetic before adding Android SDKs to the bots.
await updateLocalProperties();
await updateLocalProperties(project: new FlutterProject(fs.currentDirectory));
} on Exception catch (e) {
shouldBeToolExit = e;
}
@ -69,7 +70,10 @@ someProperty: someValue
buildDir: /Users/some/apps/hello/build/app
someOtherProperty: someOtherValue
''');
expect(project.apkDirectory, fs.path.normalize('/Users/some/apps/hello/build/app/outputs/apk'));
expect(
fs.path.normalize(project.apkDirectory.path),
fs.path.normalize('/Users/some/apps/hello/build/app/outputs/apk'),
);
});
test('should extract default build variants from app properties', () {
final GradleProject project = projectFrom('''
@ -110,27 +114,27 @@ someOtherProperty: someOtherValue
expect(project.productFlavors, <String>['free', 'paid']);
});
test('should provide apk file name for default build types', () {
final GradleProject project = new GradleProject(<String>['debug', 'profile', 'release'], <String>[], '/some/dir');
final GradleProject project = new GradleProject(<String>['debug', 'profile', 'release'], <String>[], fs.directory('/some/dir'));
expect(project.apkFileFor(BuildInfo.debug), 'app-debug.apk');
expect(project.apkFileFor(BuildInfo.profile), 'app-profile.apk');
expect(project.apkFileFor(BuildInfo.release), 'app-release.apk');
expect(project.apkFileFor(const BuildInfo(BuildMode.release, 'unknown')), isNull);
});
test('should provide apk file name for flavored build types', () {
final GradleProject project = new GradleProject(<String>['debug', 'profile', 'release'], <String>['free', 'paid'], '/some/dir');
final GradleProject project = new GradleProject(<String>['debug', 'profile', 'release'], <String>['free', 'paid'], fs.directory('/some/dir'));
expect(project.apkFileFor(const BuildInfo(BuildMode.debug, 'free')), 'app-free-debug.apk');
expect(project.apkFileFor(const BuildInfo(BuildMode.release, 'paid')), 'app-paid-release.apk');
expect(project.apkFileFor(const BuildInfo(BuildMode.release, 'unknown')), isNull);
});
test('should provide assemble task name for default build types', () {
final GradleProject project = new GradleProject(<String>['debug', 'profile', 'release'], <String>[], '/some/dir');
final GradleProject project = new GradleProject(<String>['debug', 'profile', 'release'], <String>[], fs.directory('/some/dir'));
expect(project.assembleTaskFor(BuildInfo.debug), 'assembleDebug');
expect(project.assembleTaskFor(BuildInfo.profile), 'assembleProfile');
expect(project.assembleTaskFor(BuildInfo.release), 'assembleRelease');
expect(project.assembleTaskFor(const BuildInfo(BuildMode.release, 'unknown')), isNull);
});
test('should provide assemble task name for flavored build types', () {
final GradleProject project = new GradleProject(<String>['debug', 'profile', 'release'], <String>['free', 'paid'], '/some/dir');
final GradleProject project = new GradleProject(<String>['debug', 'profile', 'release'], <String>['free', 'paid'], fs.directory('/some/dir'));
expect(project.assembleTaskFor(const BuildInfo(BuildMode.debug, 'free')), 'assembleFreeDebug');
expect(project.assembleTaskFor(const BuildInfo(BuildMode.release, 'paid')), 'assemblePaidRelease');
expect(project.assembleTaskFor(const BuildInfo(BuildMode.release, 'unknown')), isNull);
@ -185,7 +189,10 @@ someOtherProperty: someOtherValue
writeSchemaFile(fs, schemaData);
try {
await updateLocalProperties(projectPath: 'path/to/project', buildInfo: buildInfo);
await updateLocalProperties(
project: new FlutterProject.fromPath('path/to/project'),
buildInfo: buildInfo,
);
final File localPropertiesFile = fs.file('path/to/project/android/local.properties');
expect(propertyFor('flutter.versionName', localPropertiesFile), expectedBuildName);

View file

@ -57,7 +57,7 @@ void main() {
};
testUsingContext('Error on non-existing file', () {
final PrebuiltIOSApp iosApp =
new IOSApp.fromPrebuiltApp('not_existing.ipa');
new IOSApp.fromPrebuiltApp(fs.file('not_existing.ipa'));
expect(iosApp, isNull);
final BufferLogger logger = context[Logger];
expect(
@ -68,7 +68,7 @@ void main() {
testUsingContext('Error on non-app-bundle folder', () {
fs.directory('regular_folder').createSync();
final PrebuiltIOSApp iosApp =
new IOSApp.fromPrebuiltApp('regular_folder');
new IOSApp.fromPrebuiltApp(fs.file('regular_folder'));
expect(iosApp, isNull);
final BufferLogger logger = context[Logger];
expect(
@ -76,7 +76,7 @@ void main() {
}, overrides: overrides);
testUsingContext('Error on no info.plist', () {
fs.directory('bundle.app').createSync();
final PrebuiltIOSApp iosApp = new IOSApp.fromPrebuiltApp('bundle.app');
final PrebuiltIOSApp iosApp = new IOSApp.fromPrebuiltApp(fs.file('bundle.app'));
expect(iosApp, isNull);
final BufferLogger logger = context[Logger];
expect(
@ -87,7 +87,7 @@ void main() {
testUsingContext('Error on bad info.plist', () {
fs.directory('bundle.app').createSync();
fs.file('bundle.app/Info.plist').writeAsStringSync(badPlistData);
final PrebuiltIOSApp iosApp = new IOSApp.fromPrebuiltApp('bundle.app');
final PrebuiltIOSApp iosApp = new IOSApp.fromPrebuiltApp(fs.file('bundle.app'));
expect(iosApp, isNull);
final BufferLogger logger = context[Logger];
expect(
@ -99,7 +99,7 @@ void main() {
testUsingContext('Success with app bundle', () {
fs.directory('bundle.app').createSync();
fs.file('bundle.app/Info.plist').writeAsStringSync(plistData);
final PrebuiltIOSApp iosApp = new IOSApp.fromPrebuiltApp('bundle.app');
final PrebuiltIOSApp iosApp = new IOSApp.fromPrebuiltApp(fs.file('bundle.app'));
final BufferLogger logger = context[Logger];
expect(logger.errorText, isEmpty);
expect(iosApp.bundleDir.path, 'bundle.app');
@ -109,7 +109,7 @@ void main() {
testUsingContext('Bad ipa zip-file, no payload dir', () {
fs.file('app.ipa').createSync();
when(os.unzip(fs.file('app.ipa'), any)).thenAnswer((Invocation _) {});
final PrebuiltIOSApp iosApp = new IOSApp.fromPrebuiltApp('app.ipa');
final PrebuiltIOSApp iosApp = new IOSApp.fromPrebuiltApp(fs.file('app.ipa'));
expect(iosApp, isNull);
final BufferLogger logger = context[Logger];
expect(
@ -132,7 +132,7 @@ void main() {
fs.directory(bundlePath1).createSync(recursive: true);
fs.directory(bundlePath2).createSync(recursive: true);
});
final PrebuiltIOSApp iosApp = new IOSApp.fromPrebuiltApp('app.ipa');
final PrebuiltIOSApp iosApp = new IOSApp.fromPrebuiltApp(fs.file('app.ipa'));
expect(iosApp, isNull);
final BufferLogger logger = context[Logger];
expect(logger.errorText,
@ -153,7 +153,7 @@ void main() {
.file(fs.path.join(bundleAppDir.path, 'Info.plist'))
.writeAsStringSync(plistData);
});
final PrebuiltIOSApp iosApp = new IOSApp.fromPrebuiltApp('app.ipa');
final PrebuiltIOSApp iosApp = new IOSApp.fromPrebuiltApp(fs.file('app.ipa'));
final BufferLogger logger = context[Logger];
expect(logger.errorText, isEmpty);
expect(iosApp.bundleDir.path, endsWith('bundle.app'));

View file

@ -9,7 +9,6 @@ import 'package:file/memory.dart';
import 'package:flutter_tools/src/base/common.dart';
import 'package:flutter_tools/src/base/io.dart';
import 'package:flutter_tools/src/cache.dart';
import 'package:flutter_tools/src/flutter_manifest.dart';
import 'package:flutter_tools/src/project.dart';
import 'package:flutter_tools/src/ios/cocoapods.dart';
import 'package:flutter_tools/src/ios/xcodeproj.dart';
@ -25,7 +24,7 @@ void main() {
FileSystem fs;
ProcessManager mockProcessManager;
MockXcodeProjectInterpreter mockXcodeProjectInterpreter;
Directory projectUnderTest;
FlutterProject projectUnderTest;
CocoaPods cocoaPodsUnderTest;
InvokeProcess resultOfPodVersion;
@ -46,7 +45,8 @@ void main() {
fs = new MemoryFileSystem();
mockProcessManager = new MockProcessManager();
mockXcodeProjectInterpreter = new MockXcodeProjectInterpreter();
projectUnderTest = fs.directory(fs.path.join('project', 'ios'))..createSync(recursive: true);
projectUnderTest = new FlutterProject(fs.directory('project'));
projectUnderTest.ios.directory.createSync(recursive: true);
cocoaPodsUnderTest = new CocoaPods();
pretendPodVersionIs('1.5.0');
fs.file(fs.path.join(
@ -124,22 +124,10 @@ void main() {
});
group('Setup Podfile', () {
File podFile;
File debugConfigFile;
File releaseConfigFile;
setUp(() {
debugConfigFile = fs.file(fs.path.join('project', 'ios', 'Flutter', 'Debug.xcconfig'));
releaseConfigFile = fs.file(fs.path.join('project', 'ios', 'Flutter', 'Release.xcconfig'));
podFile = fs.file(fs.path.join('project', 'ios', 'Podfile'));
});
testUsingContext('creates objective-c Podfile when not present', () async {
final FlutterManifest manifest =
await new FlutterProject.fromPath('project').manifest;
cocoaPodsUnderTest.setupPodfile('project', manifest);
cocoaPodsUnderTest.setupPodfile(projectUnderTest.ios);
expect(podFile.readAsStringSync(), 'Objective-C podfile template');
expect(projectUnderTest.ios.podfile.readAsStringSync(), 'Objective-C podfile template');
}, overrides: <Type, Generator>{
FileSystem: () => fs,
});
@ -150,24 +138,22 @@ void main() {
'SWIFT_VERSION': '4.0',
});
final FlutterManifest manifest =
await new FlutterProject.fromPath('project').manifest;
cocoaPodsUnderTest.setupPodfile('project', manifest);
final FlutterProject project = new FlutterProject.fromPath('project');
cocoaPodsUnderTest.setupPodfile(project.ios);
expect(podFile.readAsStringSync(), 'Swift podfile template');
expect(projectUnderTest.ios.podfile.readAsStringSync(), 'Swift podfile template');
}, overrides: <Type, Generator>{
FileSystem: () => fs,
XcodeProjectInterpreter: () => mockXcodeProjectInterpreter,
});
testUsingContext('does not recreate Podfile when already present', () async {
podFile..createSync()..writeAsStringSync('Existing Podfile');
projectUnderTest.ios.podfile..createSync()..writeAsStringSync('Existing Podfile');
final FlutterManifest manifest =
await new FlutterProject.fromPath('project').manifest;
cocoaPodsUnderTest.setupPodfile('project', manifest);
final FlutterProject project = new FlutterProject.fromPath('project');
cocoaPodsUnderTest.setupPodfile(project.ios);
expect(podFile.readAsStringSync(), 'Existing Podfile');
expect(projectUnderTest.ios.podfile.readAsStringSync(), 'Existing Podfile');
}, overrides: <Type, Generator>{
FileSystem: () => fs,
});
@ -175,30 +161,32 @@ void main() {
testUsingContext('does not create Podfile when we cannot interpret Xcode projects', () async {
when(mockXcodeProjectInterpreter.isInstalled).thenReturn(false);
final FlutterManifest manifest =
await new FlutterProject.fromPath('project').manifest;
cocoaPodsUnderTest.setupPodfile('project', manifest);
final FlutterProject project = new FlutterProject.fromPath('project');
cocoaPodsUnderTest.setupPodfile(project.ios);
expect(podFile.existsSync(), false);
expect(projectUnderTest.ios.podfile.existsSync(), false);
}, overrides: <Type, Generator>{
FileSystem: () => fs,
XcodeProjectInterpreter: () => mockXcodeProjectInterpreter,
});
testUsingContext('includes Pod config in xcconfig files, if not present', () async {
podFile..createSync()..writeAsStringSync('Existing Podfile');
debugConfigFile..createSync(recursive: true)..writeAsStringSync('Existing debug config');
releaseConfigFile..createSync(recursive: true)..writeAsStringSync('Existing release config');
projectUnderTest.ios.podfile..createSync()..writeAsStringSync('Existing Podfile');
projectUnderTest.ios.xcodeConfigFor('Debug')
..createSync(recursive: true)
..writeAsStringSync('Existing debug config');
projectUnderTest.ios.xcodeConfigFor('Release')
..createSync(recursive: true)
..writeAsStringSync('Existing release config');
final FlutterManifest manifest =
await new FlutterProject.fromPath('project').manifest;
cocoaPodsUnderTest.setupPodfile('project', manifest);
final FlutterProject project = new FlutterProject.fromPath('project');
cocoaPodsUnderTest.setupPodfile(project.ios);
final String debugContents = debugConfigFile.readAsStringSync();
final String debugContents = projectUnderTest.ios.xcodeConfigFor('Debug').readAsStringSync();
expect(debugContents, contains(
'#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"\n'));
expect(debugContents, contains('Existing debug config'));
final String releaseContents = releaseConfigFile.readAsStringSync();
final String releaseContents = projectUnderTest.ios.xcodeConfigFor('Release').readAsStringSync();
expect(releaseContents, contains(
'#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"\n'));
expect(releaseContents, contains('Existing release config'));
@ -210,9 +198,9 @@ void main() {
group('Process pods', () {
testUsingContext('prints error, if CocoaPods is not installed', () async {
pretendPodIsNotInstalled();
projectUnderTest.childFile('Podfile').createSync();
projectUnderTest.ios.podfile.createSync();
final bool didInstall = await cocoaPodsUnderTest.processPods(
appIosDirectory: projectUnderTest,
iosProject: projectUnderTest.ios,
iosEngineDir: 'engine/path',
);
verifyNever(mockProcessManager.run(
@ -231,7 +219,7 @@ void main() {
testUsingContext('throws, if Podfile is missing.', () async {
try {
await cocoaPodsUnderTest.processPods(
appIosDirectory: projectUnderTest,
iosProject: projectUnderTest.ios,
iosEngineDir: 'engine/path',
);
fail('ToolExit expected');
@ -278,7 +266,7 @@ Note: as of CocoaPods 1.0, `pod repo update` does not happen on `pod install` by
));
try {
await cocoaPodsUnderTest.processPods(
appIosDirectory: projectUnderTest,
iosProject: projectUnderTest.ios,
iosEngineDir: 'engine/path',
);
fail('ToolExit expected');
@ -295,14 +283,14 @@ Note: as of CocoaPods 1.0, `pod repo update` does not happen on `pod install` by
});
testUsingContext('run pod install, if Podfile.lock is missing', () async {
projectUnderTest.childFile('Podfile')
projectUnderTest.ios.podfile
..createSync()
..writeAsStringSync('Existing Podfile');
projectUnderTest.childFile('Pods/Manifest.lock')
projectUnderTest.ios.podManifestLock
..createSync(recursive: true)
..writeAsStringSync('Existing lock file.');
final bool didInstall = await cocoaPodsUnderTest.processPods(
appIosDirectory: projectUnderTest,
iosProject: projectUnderTest.ios,
iosEngineDir: 'engine/path',
dependenciesChanged: false,
);
@ -318,14 +306,14 @@ Note: as of CocoaPods 1.0, `pod repo update` does not happen on `pod install` by
});
testUsingContext('runs pod install, if Manifest.lock is missing', () async {
projectUnderTest.childFile('Podfile')
projectUnderTest.ios.podfile
..createSync()
..writeAsStringSync('Existing Podfile');
projectUnderTest.childFile('Podfile.lock')
projectUnderTest.ios.podfileLock
..createSync()
..writeAsStringSync('Existing lock file.');
final bool didInstall = await cocoaPodsUnderTest.processPods(
appIosDirectory: projectUnderTest,
iosProject: projectUnderTest.ios,
iosEngineDir: 'engine/path',
dependenciesChanged: false,
);
@ -344,17 +332,17 @@ Note: as of CocoaPods 1.0, `pod repo update` does not happen on `pod install` by
});
testUsingContext('runs pod install, if Manifest.lock different from Podspec.lock', () async {
projectUnderTest.childFile('Podfile')
projectUnderTest.ios.podfile
..createSync()
..writeAsStringSync('Existing Podfile');
projectUnderTest.childFile('Podfile.lock')
projectUnderTest.ios.podfileLock
..createSync()
..writeAsStringSync('Existing lock file.');
projectUnderTest.childFile('Pods/Manifest.lock')
projectUnderTest.ios.podManifestLock
..createSync(recursive: true)
..writeAsStringSync('Different lock file.');
final bool didInstall = await cocoaPodsUnderTest.processPods(
appIosDirectory: projectUnderTest,
iosProject: projectUnderTest.ios,
iosEngineDir: 'engine/path',
dependenciesChanged: false,
);
@ -373,17 +361,17 @@ Note: as of CocoaPods 1.0, `pod repo update` does not happen on `pod install` by
});
testUsingContext('runs pod install, if flutter framework changed', () async {
projectUnderTest.childFile('Podfile')
projectUnderTest.ios.podfile
..createSync()
..writeAsStringSync('Existing Podfile');
projectUnderTest.childFile('Podfile.lock')
projectUnderTest.ios.podfileLock
..createSync()
..writeAsStringSync('Existing lock file.');
projectUnderTest.childFile('Pods/Manifest.lock')
projectUnderTest.ios.podManifestLock
..createSync(recursive: true)
..writeAsStringSync('Existing lock file.');
final bool didInstall = await cocoaPodsUnderTest.processPods(
appIosDirectory: projectUnderTest,
iosProject: projectUnderTest.ios,
iosEngineDir: 'engine/path',
dependenciesChanged: true,
);
@ -402,20 +390,20 @@ Note: as of CocoaPods 1.0, `pod repo update` does not happen on `pod install` by
});
testUsingContext('runs pod install, if Podfile.lock is older than Podfile', () async {
projectUnderTest.childFile('Podfile')
projectUnderTest.ios.podfile
..createSync()
..writeAsStringSync('Existing Podfile');
projectUnderTest.childFile('Podfile.lock')
projectUnderTest.ios.podfileLock
..createSync()
..writeAsStringSync('Existing lock file.');
projectUnderTest.childFile('Pods/Manifest.lock')
projectUnderTest.ios.podManifestLock
..createSync(recursive: true)
..writeAsStringSync('Existing lock file.');
await new Future<void>.delayed(const Duration(milliseconds: 10));
projectUnderTest.childFile('Podfile')
projectUnderTest.ios.podfile
..writeAsStringSync('Updated Podfile');
await cocoaPodsUnderTest.processPods(
appIosDirectory: projectUnderTest,
iosProject: projectUnderTest.ios,
iosEngineDir: 'engine/path',
dependenciesChanged: false,
);
@ -433,17 +421,17 @@ Note: as of CocoaPods 1.0, `pod repo update` does not happen on `pod install` by
});
testUsingContext('skips pod install, if nothing changed', () async {
projectUnderTest.childFile('Podfile')
projectUnderTest.ios.podfile
..createSync()
..writeAsStringSync('Existing Podfile');
projectUnderTest.childFile('Podfile.lock')
projectUnderTest.ios.podfileLock
..createSync()
..writeAsStringSync('Existing lock file.');
projectUnderTest.childFile('Pods/Manifest.lock')
projectUnderTest.ios.podManifestLock
..createSync(recursive: true)
..writeAsStringSync('Existing lock file.');
final bool didInstall = await cocoaPodsUnderTest.processPods(
appIosDirectory: projectUnderTest,
iosProject: projectUnderTest.ios,
iosEngineDir: 'engine/path',
dependenciesChanged: false,
);
@ -459,13 +447,13 @@ Note: as of CocoaPods 1.0, `pod repo update` does not happen on `pod install` by
});
testUsingContext('a failed pod install deletes Pods/Manifest.lock', () async {
projectUnderTest.childFile('Podfile')
projectUnderTest.ios.podfile
..createSync()
..writeAsStringSync('Existing Podfile');
projectUnderTest.childFile('Podfile.lock')
projectUnderTest.ios.podfileLock
..createSync()
..writeAsStringSync('Existing lock file.');
projectUnderTest.childFile('Pods/Manifest.lock')
projectUnderTest.ios.podManifestLock
..createSync(recursive: true)
..writeAsStringSync('Existing lock file.');
@ -482,13 +470,13 @@ Note: as of CocoaPods 1.0, `pod repo update` does not happen on `pod install` by
try {
await cocoaPodsUnderTest.processPods(
appIosDirectory: projectUnderTest,
iosProject: projectUnderTest.ios,
iosEngineDir: 'engine/path',
dependenciesChanged: true,
);
fail('Tool throw expected when pod install fails');
} on ToolExit {
expect(projectUnderTest.childFile('Pods/Manifest.lock').existsSync(), isFalse);
expect(projectUnderTest.ios.podManifestLock.existsSync(), isFalse);
}
}, overrides: <Type, Generator>{
FileSystem: () => fs,

View file

@ -289,11 +289,9 @@ Information about project "Runner":
previewDart2: true,
targetPlatform: TargetPlatform.ios,
);
final FlutterManifest manifest =
await new FlutterProject.fromPath('path/to/project').manifest;
updateGeneratedXcodeProperties(
projectPath: 'path/to/project',
manifest: manifest,
final FlutterProject project = new FlutterProject.fromPath('path/to/project');
await updateGeneratedXcodeProperties(
project: project,
buildInfo: buildInfo,
previewDart2: true,
);
@ -313,11 +311,9 @@ Information about project "Runner":
trackWidgetCreation: true,
targetPlatform: TargetPlatform.ios,
);
final FlutterManifest manifest =
await new FlutterProject.fromPath('path/to/project').manifest;
updateGeneratedXcodeProperties(
projectPath: 'path/to/project',
manifest: manifest,
final FlutterProject project = new FlutterProject.fromPath('path/to/project');
await updateGeneratedXcodeProperties(
project: project,
buildInfo: buildInfo,
previewDart2: true,
);
@ -336,11 +332,9 @@ Information about project "Runner":
previewDart2: true,
targetPlatform: TargetPlatform.ios,
);
final FlutterManifest manifest =
await new FlutterProject.fromPath('path/to/project').manifest;
updateGeneratedXcodeProperties(
projectPath: 'path/to/project',
manifest: manifest,
final FlutterProject project = new FlutterProject.fromPath('path/to/project');
await updateGeneratedXcodeProperties(
project: project,
buildInfo: buildInfo,
previewDart2: true,
);
@ -360,11 +354,9 @@ Information about project "Runner":
targetPlatform: TargetPlatform.ios,
);
final FlutterManifest manifest =
await new FlutterProject.fromPath('path/to/project').manifest;
updateGeneratedXcodeProperties(
projectPath: 'path/to/project',
manifest: manifest,
final FlutterProject project = new FlutterProject.fromPath('path/to/project');
await updateGeneratedXcodeProperties(
project: project,
buildInfo: buildInfo,
previewDart2: true,
);
@ -412,11 +404,8 @@ Information about project "Runner":
const String schemaData = '{}';
writeSchemaFile(fs, schemaData);
final FlutterManifest manifest =
await new FlutterProject.fromPath('path/to/project').manifest;
updateGeneratedXcodeProperties(
projectPath: 'path/to/project',
manifest: manifest,
await updateGeneratedXcodeProperties(
project: new FlutterProject.fromPath('path/to/project'),
buildInfo: buildInfo,
previewDart2: false,
);

View file

@ -129,13 +129,6 @@ void testInMemory(String description, Future<Null> testMethod()) {
);
}
void addPubPackages(Directory directory) {
directory.childFile('pubspec.yaml')
..createSync(recursive: true);
directory.childFile('.packages')
..createSync(recursive: true);
}
void addIosWithBundleId(Directory directory, String id) {
directory
.childDirectory('ios')

View file

@ -25,7 +25,7 @@ class MockApplicationPackageStore extends ApplicationPackageStore {
MockApplicationPackageStore() : super(
android: new AndroidApk(
id: 'io.flutter.android.mock',
apkPath: '/mock/path/to/android/SkyShell.apk',
file: fs.file('/mock/path/to/android/SkyShell.apk'),
launchActivity: 'io.flutter.android.mock.MockActivity'
),
iOS: new BuildableIOSApp(

View file

@ -33,7 +33,7 @@ void main() {
final FlutterTesterApp app = new FlutterTesterApp.fromCurrentDirectory();
expect(app.name, 'my_project');
expect(app.packagePath, fs.path.join(projectPath, '.packages'));
expect(app.packagesFile.path, fs.path.join(projectPath, '.packages'));
}, overrides: <Type, Generator>{
FileSystem: () => fs,
});