diff --git a/packages/flutter_tools/lib/src/android/android_device.dart b/packages/flutter_tools/lib/src/android/android_device.dart index e805f14ad94..65fb6ab124b 100644 --- a/packages/flutter_tools/lib/src/android/android_device.dart +++ b/packages/flutter_tools/lib/src/android/android_device.dart @@ -21,7 +21,6 @@ import '../base/utils.dart'; import '../build_info.dart'; import '../device.dart'; import '../globals.dart'; -import '../project.dart'; import '../protocol_discovery.dart'; import 'adb.dart'; @@ -242,7 +241,7 @@ class AndroidDevice extends Device { String _getSourceSha1(ApplicationPackage app) { final AndroidApk apk = app; - final File shaFile = fs.file('${apk.file.path}.sha1'); + final File shaFile = fs.file('${apk.apkPath}.sha1'); return shaFile.existsSync() ? shaFile.readAsStringSync() : ''; } @@ -270,16 +269,16 @@ class AndroidDevice extends Device { @override Future installApp(ApplicationPackage app) async { final AndroidApk apk = app; - if (!apk.file.existsSync()) { - printError('"${apk.file.path}" does not exist.'); + if (!fs.isFileSync(apk.apkPath)) { + printError('"${apk.apkPath}" does not exist.'); return false; } if (!await _checkForSupportedAdbVersion() || !await _checkForSupportedAndroidVersion()) return false; - final Status status = logger.startProgress('Installing ${apk.file.path}...', expectSlowOperation: true); - final RunResult installResult = await runAsync(adbCommandForDevice(['install', '-t', '-r', apk.file.path])); + final Status status = logger.startProgress('Installing ${apk.apkPath}...', expectSlowOperation: true); + final RunResult installResult = await runAsync(adbCommandForDevice(['install', '-t', '-r', apk.apkPath])); status.stop(); // Some versions of adb exit with exit code 0 even on failure :( // Parsing the output to check for failures. @@ -375,13 +374,12 @@ 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.fromAndroidProject(new FlutterProject(fs.currentDirectory).android); + package = await AndroidApk.fromCurrentDirectory(); } printTrace("Stopping app '${package.name}' on $name."); diff --git a/packages/flutter_tools/lib/src/android/apk.dart b/packages/flutter_tools/lib/src/android/apk.dart index 57e7f2952b4..4a2af8e102f 100644 --- a/packages/flutter_tools/lib/src/android/apk.dart +++ b/packages/flutter_tools/lib/src/android/apk.dart @@ -4,22 +4,17 @@ 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 buildApk({ - @required FlutterProject project, - @required String target, + String target, BuildInfo buildInfo = BuildInfo.debug }) async { - if (!project.android.isUsingGradle()) { + if (!isProjectUsingGradle()) { throwToolExit( 'The build process for Android has changed, and the current project configuration\n' 'is no longer valid. Please consult\n\n' @@ -38,9 +33,5 @@ Future buildApk({ throwToolExit('Try re-installing or updating your Android SDK.'); } - return buildGradleProject( - project: project, - buildInfo: buildInfo, - target: target, - ); + return buildGradleProject(buildInfo, target); } diff --git a/packages/flutter_tools/lib/src/android/gradle.dart b/packages/flutter_tools/lib/src/android/gradle.dart index c7b1e253fd3..833ae588aba 100644 --- a/packages/flutter_tools/lib/src/android/gradle.dart +++ b/packages/flutter_tools/lib/src/android/gradle.dart @@ -4,8 +4,6 @@ import 'dart:async'; -import 'package:meta/meta.dart'; - import '../android/android_sdk.dart'; import '../artifacts.dart'; import '../base/common.dart'; @@ -16,13 +14,16 @@ 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 '); @@ -44,6 +45,9 @@ 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'); @@ -65,17 +69,18 @@ FlutterPluginVersion get flutterPluginVersion { return FlutterPluginVersion.none; } -/// Returns the apk file created by [buildGradleProject] -Future getGradleAppOut(AndroidProject androidProject) async { +/// Returns the path to the apk file created by [buildGradleProject], relative +/// to current directory. +Future getGradleAppOut() async { switch (flutterPluginVersion) { case FlutterPluginVersion.none: // Fall through. Pretend we're v1, and just go with it. case FlutterPluginVersion.v1: - return androidProject.gradleAppOutV1File; + return gradleAppOutV1; case FlutterPluginVersion.managed: // Fall through. The managed plugin matches plugin v2 for now. case FlutterPluginVersion.v2: - return fs.file((await _gradleProject()).apkDirectory.childFile('app.apk')); + return fs.path.relative(fs.path.join((await _gradleProject()).apkDirectory, 'app.apk')); } return null; } @@ -89,13 +94,12 @@ Future _gradleProject() async { // of calculating the app properties using Gradle. This may take minutes. Future _readGradleProject() async { final String gradle = await _ensureGradle(); - final FlutterProject flutterProject = new FlutterProject(fs.currentDirectory); - await updateLocalProperties(project: flutterProject); + await updateLocalProperties(); try { final Status status = logger.startProgress('Resolving dependencies...', expectSlowOperation: true); final RunResult runResult = await runCheckedAsync( [gradle, 'app:properties'], - workingDirectory: flutterProject.android.directory.path, + workingDirectory: 'android', environment: _gradleEnv, ); final String properties = runResult.stdout.trim(); @@ -113,7 +117,7 @@ Future _readGradleProject() async { } } // Fall back to the default - return new GradleProject(['debug', 'profile', 'release'], [], flutterProject.android.gradleAppOutV1Directory); + return new GradleProject(['debug', 'profile', 'release'], [], gradleAppOutDirV1); } void handleKnownGradleExceptions(String exceptionString) { @@ -195,19 +199,28 @@ 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. -/// -/// If [requireSdk] is `true` this will fail with a tool-exit if no Android Sdk -/// is found. -Future updateLocalProperties({ - @required FlutterProject project, - BuildInfo buildInfo, - bool requireAndroidSdk = true, -}) async { - if (requireAndroidSdk && androidSdk == null) { +Future 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) { 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); +} - final File localProperties = await project.androidLocalPropertiesFile; +/// Overwrite local.properties in the specified directory, if needed. +void updateLocalPropertiesSync(Directory android, FlutterManifest manifest, [BuildInfo buildInfo]) { + final File localProperties = android.childFile('local.properties'); bool changed = false; SettingsFile settings; @@ -225,8 +238,6 @@ Future updateLocalProperties({ } } - final FlutterManifest manifest = await project.manifest; - if (androidSdk != null) changeIfNecessary('sdk.dir', escapePath(androidSdk.directory)); changeIfNecessary('flutter.sdk', escapePath(Cache.flutterRoot)); @@ -243,11 +254,7 @@ Future updateLocalProperties({ settings.writeContents(localProperties); } -Future buildGradleProject({ - @required FlutterProject project, - @required BuildInfo buildInfo, - @required String target, -}) async { +Future buildGradleProject(BuildInfo buildInfo, 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 @@ -256,7 +263,7 @@ Future buildGradleProject({ // 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(project: project, buildInfo: buildInfo); + await updateLocalProperties(buildInfo: buildInfo); final String gradle = await _ensureGradle(); @@ -264,7 +271,7 @@ Future buildGradleProject({ case FlutterPluginVersion.none: // Fall through. Pretend it's v1, and just go for it. case FlutterPluginVersion.v1: - return _buildGradleProjectV1(project, gradle); + return _buildGradleProjectV1(gradle); case FlutterPluginVersion.managed: // Fall through. Managed plugin builds the same way as plugin v2. case FlutterPluginVersion.v2: @@ -272,7 +279,7 @@ Future buildGradleProject({ } } -Future _buildGradleProjectV1(FlutterProject project, String gradle) async { +Future _buildGradleProjectV1(String gradle) async { // Run 'gradlew build'. final Status status = logger.startProgress('Running \'gradlew build\'...', expectSlowOperation: true); final int exitCode = await runCommandAndStreamOutput( @@ -286,7 +293,7 @@ Future _buildGradleProjectV1(FlutterProject project, String gradle) async if (exitCode != 0) throwToolExit('Gradle build failed: $exitCode', exitCode: exitCode); - printStatus('Built ${fs.path.relative(project.android.gradleAppOutV1File.path)}.'); + printStatus('Built $gradleAppOutV1.'); } Future _buildGradleProjectV2(String gradle, BuildInfo buildInfo, String target) async { @@ -364,10 +371,10 @@ Future _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(project.apkDirectory.childFile('app.apk').path); + apkFile.copySync(fs.path.join(project.apkDirectory, 'app.apk')); printTrace('calculateSha: ${project.apkDirectory}/app.apk'); - final File apkShaFile = project.apkDirectory.childFile('app.apk.sha1'); + final File apkShaFile = fs.file(fs.path.join(project.apkDirectory, 'app.apk.sha1')); apkShaFile.writeAsStringSync(calculateSha(apkFile)); String appSize; @@ -383,15 +390,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.path, apkFileName)); + File apkFile = fs.file(fs.path.join(project.apkDirectory, apkFileName)); if (apkFile.existsSync()) return apkFile; - apkFile = fs.file(fs.path.join(project.apkDirectory.path, buildInfo.modeName, apkFileName)); + apkFile = fs.file(fs.path.join(project.apkDirectory, 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.path, buildInfo.flavor, buildInfo.modeName, apkFileName)); + apkFile = fs.file(fs.path.join(project.apkDirectory, buildInfo.flavor, buildInfo.modeName, apkFileName)); if (apkFile.existsSync()) return apkFile; } @@ -446,13 +453,13 @@ class GradleProject { return new GradleProject( buildTypes.toList(), productFlavors.toList(), - fs.directory(fs.path.join(buildDir, 'outputs', 'apk')), + fs.path.normalize(fs.path.join(buildDir, 'outputs', 'apk')), ); } final List buildTypes; final List productFlavors; - final Directory apkDirectory; + final String apkDirectory; String _buildTypeFor(BuildInfo buildInfo) { if (buildTypes.contains(buildInfo.modeName)) diff --git a/packages/flutter_tools/lib/src/application_package.dart b/packages/flutter_tools/lib/src/application_package.dart index 7861239e8b0..ff52975964a 100644 --- a/packages/flutter_tools/lib/src/application_package.dart +++ b/packages/flutter_tools/lib/src/application_package.dart @@ -18,7 +18,6 @@ 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 { @@ -32,7 +31,7 @@ abstract class ApplicationPackage { String get displayName => name; - File get packagesFile => null; + String get packagePath => null; @override String toString() => displayName; @@ -40,21 +39,21 @@ abstract class ApplicationPackage { class AndroidApk extends ApplicationPackage { /// Path to the actual apk file. - final File file; + final String apkPath; /// The path to the activity that should be launched. final String launchActivity; AndroidApk({ String id, - @required this.file, + @required this.apkPath, @required this.launchActivity - }) : assert(file != null), + }) : assert(apkPath != null), assert(launchActivity != null), super(id: id); /// Creates a new AndroidApk from an existing APK. - factory AndroidApk.fromApk(File apk) { + factory AndroidApk.fromApk(String applicationBinary) { final String aaptPath = androidSdk?.latestVersion?.aaptPath; if (aaptPath == null) { printError('Unable to locate the Android SDK; please run \'flutter doctor\'.'); @@ -65,7 +64,7 @@ class AndroidApk extends ApplicationPackage { aaptPath, 'dump', 'xmltree', - apk.path, + applicationBinary, 'AndroidManifest.xml', ]; @@ -73,46 +72,47 @@ class AndroidApk extends ApplicationPackage { .parseFromXmlDump(runCheckedSync(aaptArgs)); if (data == null) { - printError('Unable to read manifest info from ${apk.path}.'); + printError('Unable to read manifest info from $applicationBinary.'); return null; } if (data.packageName == null || data.launchableActivityName == null) { - printError('Unable to read manifest info from ${apk.path}.'); + printError('Unable to read manifest info from $applicationBinary.'); return null; } return new AndroidApk( id: data.packageName, - file: apk, + apkPath: applicationBinary, launchActivity: '${data.packageName}/${data.launchableActivityName}' ); } /// Creates a new AndroidApk based on the information in the Android manifest. - static Future fromAndroidProject(AndroidProject androidProject) async { - File apkFile; + static Future fromCurrentDirectory() async { + String manifestPath; + String apkPath; - if (androidProject.isUsingGradle()) { - apkFile = await getGradleAppOut(androidProject); - if (apkFile.existsSync()) { + if (isProjectUsingGradle()) { + apkPath = await getGradleAppOut(); + if (fs.file(apkPath).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(apkFile); + return new AndroidApk.fromApk(apkPath); } // 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 { - apkFile = fs.file(fs.path.join(getAndroidBuildDirectory(), 'app.apk')); + manifestPath = fs.path.join('android', 'AndroidManifest.xml'); + apkPath = fs.path.join(getAndroidBuildDirectory(), 'app.apk'); } - final File manifest = androidProject.gradleManifestFile; - - if (!manifest.existsSync()) + if (!fs.isFileSync(manifestPath)) return null; - final String manifestString = manifest.readAsStringSync(); + final String manifestString = fs.file(manifestPath).readAsStringSync(); final xml.XmlDocument document = xml.parse(manifestString); final Iterable manifests = document.findElements('manifest'); @@ -138,16 +138,16 @@ class AndroidApk extends ApplicationPackage { return new AndroidApk( id: packageId, - file: apkFile, + apkPath: apkPath, launchActivity: launchActivity ); } @override - File get packagesFile => file; + String get packagePath => apkPath; @override - String get name => file.basename; + String get name => fs.path.basename(apkPath); } /// 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(File applicationBinary) { - final FileSystemEntityType entityType = fs.typeSync(applicationBinary.path); + factory IOSApp.fromPrebuiltApp(String applicationBinary) { + final FileSystemEntityType entityType = fs.typeSync(applicationBinary); if (entityType == FileSystemEntityType.notFound) { printError( - 'File "${applicationBinary.path}" does not exist. Use an app bundle or an ipa.'); + 'File "$applicationBinary" 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.path}" is not an app bundle.'); + printError('Folder "$applicationBinary" 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 getApplicationPackageForPlatform( - TargetPlatform platform, - {File applicationBinary}) async { +Future getApplicationPackageForPlatform(TargetPlatform platform, { + String 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.fromAndroidProject(new FlutterProject(fs.currentDirectory).android) + ? await AndroidApk.fromCurrentDirectory() : 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.fromAndroidProject(new FlutterProject(fs.currentDirectory).android); + android ??= await AndroidApk.fromCurrentDirectory(); return android; case TargetPlatform.ios: iOS ??= new IOSApp.fromCurrentDirectory(); diff --git a/packages/flutter_tools/lib/src/commands/build_apk.dart b/packages/flutter_tools/lib/src/commands/build_apk.dart index 3dda93a63ba..e6e2cc54f31 100644 --- a/packages/flutter_tools/lib/src/commands/build_apk.dart +++ b/packages/flutter_tools/lib/src/commands/build_apk.dart @@ -5,8 +5,6 @@ import 'dart:async'; import '../android/apk.dart'; -import '../base/file_system.dart'; -import '../project.dart'; import 'build.dart'; class BuildApkCommand extends BuildSubCommand { @@ -46,6 +44,6 @@ class BuildApkCommand extends BuildSubCommand { @override Future runCommand() async { await super.runCommand(); - await buildApk(project: new FlutterProject(fs.currentDirectory),target: targetFile, buildInfo: getBuildInfo()); + await buildApk(buildInfo: getBuildInfo(), target: targetFile); } } diff --git a/packages/flutter_tools/lib/src/commands/create.dart b/packages/flutter_tools/lib/src/commands/create.dart index 7c97cf30c0b..0c69f1891cb 100644 --- a/packages/flutter_tools/lib/src/commands/create.dart +++ b/packages/flutter_tools/lib/src/commands/create.dart @@ -168,19 +168,20 @@ class CreateCommand extends FlutterCommand { printStatus('Creating project ${fs.path.relative(dirPath)}...'); int generatedFileCount = 0; - final FlutterProject project = new FlutterProject.fromPath(dirPath); + String appPath = dirPath; switch (template) { case 'app': - generatedFileCount += await _generateApp(project, templateContext); + generatedFileCount += await _generateApp(dirPath, templateContext); break; case 'module': - generatedFileCount += await _generateModule(project, templateContext); + generatedFileCount += await _generateModule(dirPath, templateContext); break; case 'package': - generatedFileCount += await _generatePackage(project, templateContext); + generatedFileCount += await _generatePackage(dirPath, templateContext); break; case 'plugin': - generatedFileCount += await _generatePlugin(project, templateContext); + appPath = fs.path.join(dirPath, 'example'); + generatedFileCount += await _generatePlugin(dirPath, appPath, templateContext); break; } printStatus('Wrote $generatedFileCount files.'); @@ -193,8 +194,7 @@ 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 FlutterProject app = project.hasExampleApp ? project.example : project; - final String relativeAppPath = fs.path.relative(app.directory.path); + final String relativeAppPath = fs.path.relative(appPath); final String relativePluginPath = fs.path.relative(dirPath); if (doctor.canLaunchAnything) { // Let them know a summary of the state of their tooling. @@ -233,59 +233,60 @@ To edit platform code in an IDE see https://flutter.io/developing-packages/#edit } } - Future _generateModule(FlutterProject project, Map templateContext) async { + Future _generateModule(String path, Map 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'), project.directory, templateContext); + generatedCount += _renderTemplate(fs.path.join('module', 'common'), path, templateContext); if (argResults['pub']) { await pubGet( context: PubContext.create, - directory: project.directory.path, + directory: path, offline: argResults['offline'], ); + final FlutterProject project = new FlutterProject.fromPath(path); await project.ensureReadyForPlatformSpecificTooling(); } return generatedCount; } - Future _generatePackage(FlutterProject project, Map templateContext) async { + Future _generatePackage(String dirPath, Map templateContext) async { int generatedCount = 0; final String description = argResults.wasParsed('description') ? argResults['description'] : 'A new flutter package project.'; templateContext['description'] = description; - generatedCount += _renderTemplate('package', project.directory, templateContext); + generatedCount += _renderTemplate('package', dirPath, templateContext); if (argResults['pub']) { await pubGet( context: PubContext.createPackage, - directory: project.directory.path, + directory: dirPath, offline: argResults['offline'], ); } return generatedCount; } - Future _generatePlugin(FlutterProject project, Map templateContext) async { + Future _generatePlugin(String dirPath, String appPath, Map templateContext) async { int generatedCount = 0; final String description = argResults.wasParsed('description') ? argResults['description'] : 'A new flutter plugin project.'; templateContext['description'] = description; - generatedCount += _renderTemplate('plugin', project.directory, templateContext); + generatedCount += _renderTemplate('plugin', dirPath, templateContext); if (argResults['pub']) { await pubGet( context: PubContext.createPlugin, - directory: project.directory.path, + directory: dirPath, offline: argResults['offline'], ); } if (android_sdk.androidSdk != null) - await gradle.updateLocalProperties(project: project); + await gradle.updateLocalProperties(projectPath: dirPath); final String projectName = templateContext['projectName']; final String organization = templateContext['organization']; @@ -298,27 +299,27 @@ To edit platform code in an IDE see https://flutter.io/developing-packages/#edit templateContext['pluginProjectName'] = projectName; templateContext['androidPluginIdentifier'] = androidPluginIdentifier; - generatedCount += await _generateApp(project.example, templateContext); + generatedCount += await _generateApp(appPath, templateContext); return generatedCount; } - Future _generateApp(FlutterProject project, Map templateContext) async { + Future _generateApp(String projectPath, Map templateContext) async { int generatedCount = 0; - generatedCount += _renderTemplate('create', project.directory, templateContext); - generatedCount += _injectGradleWrapper(project); + generatedCount += _renderTemplate('create', projectPath, templateContext); + generatedCount += _injectGradleWrapper(projectPath); if (argResults['with-driver-test']) { - final Directory testDirectory = project.directory.childDirectory('test_driver'); - generatedCount += _renderTemplate('driver', testDirectory, templateContext); + final String testPath = fs.path.join(projectPath, 'test_driver'); + generatedCount += _renderTemplate('driver', testPath, templateContext); } if (argResults['pub']) { - await pubGet(context: PubContext.create, directory: project.directory.path, offline: argResults['offline']); - await project.ensureReadyForPlatformSpecificTooling(); + await pubGet(context: PubContext.create, directory: projectPath, offline: argResults['offline']); + await new FlutterProject.fromPath(projectPath).ensureReadyForPlatformSpecificTooling(); } if (android_sdk.androidSdk != null) - await gradle.updateLocalProperties(project: project); + await gradle.updateLocalProperties(projectPath: projectPath); return generatedCount; } @@ -361,16 +362,16 @@ To edit platform code in an IDE see https://flutter.io/developing-packages/#edit }; } - int _renderTemplate(String templateName, Directory directory, Map context) { + int _renderTemplate(String templateName, String dirPath, Map context) { final Template template = new Template.fromName(templateName); - return template.render(directory, context, overwriteExisting: false); + return template.render(fs.directory(dirPath), context, overwriteExisting: false); } - int _injectGradleWrapper(FlutterProject project) { + int _injectGradleWrapper(String projectDir) { int filesCreated = 0; copyDirectorySync( cache.getArtifactDirectory('gradle_wrapper'), - project.android.directory, + fs.directory(fs.path.join(projectDir, 'android')), (File sourceFile, File destinationFile) { filesCreated++; final String modes = sourceFile.statSync().modeString(); diff --git a/packages/flutter_tools/lib/src/commands/daemon.dart b/packages/flutter_tools/lib/src/commands/daemon.dart index 87efc31f59d..bb0f4b7b2d4 100644 --- a/packages/flutter_tools/lib/src/commands/daemon.dart +++ b/packages/flutter_tools/lib/src/commands/daemon.dart @@ -322,7 +322,7 @@ class AppDomain extends Domain { Future startApp( Device device, String projectDirectory, String target, String route, DebuggingOptions options, bool enableHotReload, { - File applicationBinary, + String applicationBinary, @required bool trackWidgetCreation, String projectRootPath, String packagesFilePath, diff --git a/packages/flutter_tools/lib/src/commands/inject_plugins.dart b/packages/flutter_tools/lib/src/commands/inject_plugins.dart index 6a1bb9403b4..832b64256dd 100644 --- a/packages/flutter_tools/lib/src/commands/inject_plugins.dart +++ b/packages/flutter_tools/lib/src/commands/inject_plugins.dart @@ -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,9 +26,10 @@ class InjectPluginsCommand extends FlutterCommand { @override Future runCommand() async { - final FlutterProject project = new FlutterProject(fs.currentDirectory); - await injectPlugins(project); - final bool result = hasPlugins(project); + final String projectPath = fs.currentDirectory.path; + final FlutterManifest manifest = await FlutterManifest.createFromPath(projectPath); + injectPlugins(projectPath: projectPath, manifest: manifest); + final bool result = hasPlugins(); if (result) { printStatus('GeneratedPluginRegistrants successfully written.'); } else { diff --git a/packages/flutter_tools/lib/src/commands/run.dart b/packages/flutter_tools/lib/src/commands/run.dart index 93acfc95cf3..47a30364dba 100644 --- a/packages/flutter_tools/lib/src/commands/run.dart +++ b/packages/flutter_tools/lib/src/commands/run.dart @@ -289,13 +289,10 @@ 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: applicationBinaryPath == null - ? null - : fs.file(applicationBinaryPath), + applicationBinary: argResults['use-application-binary'], trackWidgetCreation: argResults['track-widget-creation'], projectRootPath: argResults['project-root'], packagesFilePath: globalResults['packages'], diff --git a/packages/flutter_tools/lib/src/ios/cocoapods.dart b/packages/flutter_tools/lib/src/ios/cocoapods.dart index d6dc48c63f3..99048129e35 100644 --- a/packages/flutter_tools/lib/src/ios/cocoapods.dart +++ b/packages/flutter_tools/lib/src/ios/cocoapods.dart @@ -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 get isCocoaPodsInitialized => fs.isDirectory(fs.path.join(homeDirPath, '.cocoapods', 'repos', 'master')); Future processPods({ - @required IosProject iosProject, + @required Directory appIosDirectory, // For backward compatibility with previously created Podfile only. @required String iosEngineDir, bool isSwift = false, bool dependenciesChanged = true, }) async { - if (!(await iosProject.podfile.exists())) { + if (!(await appIosDirectory.childFile('Podfile').exists())) { throwToolExit('Podfile missing'); } if (await _checkPodCondition()) { - if (_shouldRunPodInstall(iosProject, dependenciesChanged)) { - await _runPodInstall(iosProject, iosEngineDir); + if (_shouldRunPodInstall(appIosDirectory, dependenciesChanged)) { + await _runPodInstall(appIosDirectory, 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(IosProject iosProject) { + void setupPodfile(String appDirectory, FlutterManifest manifest) { if (!xcodeProjectInterpreter.isInstalled) { // Don't do anything for iOS when host platform doesn't support it. return; } - if (!iosProject.directory.existsSync()) { + if (!fs.directory(fs.path.join(appDirectory, 'ios')).existsSync()) { return; } - final File podfile = iosProject.podfile; - if (!podfile.existsSync()) { + final String podfilePath = fs.path.join(appDirectory, 'ios', 'Podfile'); + if (!fs.file(podfilePath).existsSync()) { final bool isSwift = xcodeProjectInterpreter.getBuildSettings( - iosProject.directory.childFile('Runner.xcodeproj').path, + fs.path.join(appDirectory, 'ios', 'Runner.xcodeproj'), 'Runner', ).containsKey('SWIFT_VERSION'); final File podfileTemplate = fs.file(fs.path.join( @@ -173,14 +173,15 @@ class CocoaPods { 'cocoapods', isSwift ? 'Podfile-swift' : 'Podfile-objc', )); - podfileTemplate.copySync(podfile.path); + podfileTemplate.copySync(podfilePath); } - _addPodsDependencyToFlutterXcconfig(iosProject, 'Debug'); - _addPodsDependencyToFlutterXcconfig(iosProject, 'Release'); + + _addPodsDependencyToFlutterXcconfig(appDirectory, 'Debug'); + _addPodsDependencyToFlutterXcconfig(appDirectory, 'Release'); } - void _addPodsDependencyToFlutterXcconfig(IosProject iosProject, String mode) { - final File file = iosProject.xcodeConfigFor(mode); + void _addPodsDependencyToFlutterXcconfig(String appDirectory, String mode) { + final File file = fs.file(fs.path.join(appDirectory, 'ios', 'Flutter', '$mode.xcconfig')); if (file.existsSync()) { final String content = file.readAsStringSync(); final String include = '#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.${mode @@ -191,11 +192,12 @@ class CocoaPods { } /// Ensures that pod install is deemed needed on next check. - void invalidatePodInstallOutput(IosProject iosProject) { - final File manifestLock = iosProject.podManifestLock; - if (manifestLock.existsSync()) { - manifestLock.deleteSync(); - } + void invalidatePodInstallOutput(String appDirectory) { + final File manifest = fs.file( + fs.path.join(appDirectory, 'ios', 'Pods', 'Manifest.lock'), + ); + if (manifest.existsSync()) + manifest.deleteSync(); } // Check if you need to run pod install. @@ -204,25 +206,24 @@ 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(IosProject iosProject, bool dependenciesChanged) { + bool _shouldRunPodInstall(Directory appIosDirectory, bool dependenciesChanged) { if (dependenciesChanged) return true; - - final File podfileFile = iosProject.podfile; - final File podfileLockFile = iosProject.podfileLock; - final File manifestLockFile = iosProject.podManifestLock; - + final File podfileFile = appIosDirectory.childFile('Podfile'); + final File podfileLockFile = appIosDirectory.childFile('Podfile.lock'); + final File manifestLockFile = + appIosDirectory.childFile(fs.path.join('Pods', 'Manifest.lock')); return !podfileLockFile.existsSync() || !manifestLockFile.existsSync() || podfileLockFile.statSync().modified.isBefore(podfileFile.statSync().modified) || podfileLockFile.readAsStringSync() != manifestLockFile.readAsStringSync(); } - Future _runPodInstall(IosProject iosProject, String engineDirectory) async { + Future _runPodInstall(Directory appIosDirectory, String engineDirectory) async { final Status status = logger.startProgress('Running pod install...', expectSlowOperation: true); final ProcessResult result = await processManager.run( ['pod', 'install', '--verbose'], - workingDirectory: iosProject.directory.path, + workingDirectory: appIosDirectory.path, environment: { // For backward compatibility with previously created Podfile only. 'FLUTTER_FRAMEWORK_DIR': engineDirectory, @@ -243,7 +244,7 @@ class CocoaPods { } } if (result.exitCode != 0) { - invalidatePodInstallOutput(iosProject); + invalidatePodInstallOutput(appIosDirectory.parent.path); _diagnosePodInstallFailure(result); throwToolExit('Error running pod install'); } diff --git a/packages/flutter_tools/lib/src/ios/mac.dart b/packages/flutter_tools/lib/src/ios/mac.dart index 97bd65245f4..d5a2c6b44a1 100644 --- a/packages/flutter_tools/lib/src/ios/mac.dart +++ b/packages/flutter_tools/lib/src/ios/mac.dart @@ -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,15 +221,18 @@ Future buildXcodeProject({ final Directory appDirectory = fs.directory(app.appDirectory); await _addServicesToBundle(appDirectory); - final FlutterProject project = new FlutterProject(fs.currentDirectory); - await updateGeneratedXcodeProperties( - project: project, + final FlutterManifest manifest = await FlutterManifest.createFromPath( + fs.currentDirectory.childFile('pubspec.yaml').path, + ); + updateGeneratedXcodeProperties( + projectPath: fs.currentDirectory.path, + buildInfo: buildInfo, targetOverride: targetOverride, previewDart2: buildInfo.previewDart2, - buildInfo: buildInfo, + manifest: manifest, ); - if (hasPlugins(project)) { + if (hasPlugins()) { 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. @@ -243,7 +246,7 @@ Future buildXcodeProject({ properties: {}, ); final bool didPodInstall = await cocoaPods.processPods( - iosProject: project.ios, + appIosDirectory: appDirectory, iosEngineDir: flutterFrameworkDir(buildInfo.mode), isSwift: app.isSwift, dependenciesChanged: !await fingerprinter.doesFingerprintMatch() diff --git a/packages/flutter_tools/lib/src/ios/xcodeproj.dart b/packages/flutter_tools/lib/src/ios/xcodeproj.dart index 4f7f6b5bb43..b9bf73d0c7a 100644 --- a/packages/flutter_tools/lib/src/ios/xcodeproj.dart +++ b/packages/flutter_tools/lib/src/ios/xcodeproj.dart @@ -2,8 +2,6 @@ // 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'; @@ -19,7 +17,6 @@ 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'\$\((.*)\)'); @@ -28,18 +25,27 @@ 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. -Future generateXcodeProperties({FlutterProject project}) async { - if ((await project.manifest).isModule || - project.ios.directory.existsSync()) { - if (!Cache.instance.fileOlderThanToolsStamp(await project.generatedXcodePropertiesFile)) { +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)) { return; } - await updateGeneratedXcodeProperties( - project: project, + updateGeneratedXcodeProperties( + projectPath: projectPath, + manifest: manifest, buildInfo: BuildInfo.debug, targetOverride: bundle.defaultMainPath, previewDart2: true, @@ -51,12 +57,13 @@ Future generateXcodeProperties({FlutterProject project}) async { /// /// targetOverride: Optional parameter, if null or unspecified the default value /// from xcode_backend.sh is used 'lib/main.dart'. -Future updateGeneratedXcodeProperties({ - @required FlutterProject project, +void updateGeneratedXcodeProperties({ + @required String projectPath, + @required FlutterManifest manifest, @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.'); @@ -65,7 +72,7 @@ Future updateGeneratedXcodeProperties({ localsBuffer.writeln('FLUTTER_ROOT=$flutterRoot'); // This holds because requiresProjectRoot is true for this command - localsBuffer.writeln('FLUTTER_APPLICATION_PATH=${fs.path.normalize(project.directory.path)}'); + localsBuffer.writeln('FLUTTER_APPLICATION_PATH=${fs.path.normalize(projectPath)}'); // Relative to FLUTTER_APPLICATION_PATH, which is [Directory.current]. if (targetOverride != null) @@ -79,7 +86,6 @@ Future 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 @@ -119,9 +125,9 @@ Future updateGeneratedXcodeProperties({ localsBuffer.writeln('TRACK_WIDGET_CREATION=true'); } - final File generatedXcodePropertiesFile = await project.generatedXcodePropertiesFile; - generatedXcodePropertiesFile.createSync(recursive: true); - generatedXcodePropertiesFile.writeAsStringSync(localsBuffer.toString()); + final File localsFile = fs.file(_generatedXcodePropertiesPath(projectPath: projectPath, manifest: manifest)); + localsFile.createSync(recursive: true); + localsFile.writeAsStringSync(localsBuffer.toString()); } XcodeProjectInterpreter get xcodeProjectInterpreter => context[XcodeProjectInterpreter]; diff --git a/packages/flutter_tools/lib/src/plugins.dart b/packages/flutter_tools/lib/src/plugins.dart index 484d89748ad..d9e29465ae8 100644 --- a/packages/flutter_tools/lib/src/plugins.dart +++ b/packages/flutter_tools/lib/src/plugins.dart @@ -2,16 +2,15 @@ // 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 '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 = @@ -70,11 +69,11 @@ Plugin _pluginFromPubspec(String name, Uri packageRoot) { return new Plugin.fromYaml(name, packageRootPath, flutterConfig['plugin']); } -List findPlugins(FlutterProject project) { +List findPlugins(String directory) { final List plugins = []; Map packages; try { - final String packagesFile = fs.path.join(project.directory.path, PackageMap.globalPackagesPath); + final String packagesFile = fs.path.join(directory, PackageMap.globalPackagesPath); packages = new PackageMap(packagesFile).map; } on FormatException catch (e) { printTrace('Invalid .packages file: $e'); @@ -90,9 +89,9 @@ List findPlugins(FlutterProject project) { } /// Returns true if .flutter-plugins has changed, otherwise returns false. -bool _writeFlutterPluginsList(FlutterProject project, List plugins) { - final File pluginsFile = project.flutterPluginsFile; - final String oldContents = _readFlutterPluginsList(project); +bool _writeFlutterPluginsList(String directory, List plugins) { + final File pluginsFile = fs.file(fs.path.join(directory, '.flutter-plugins')); + final String oldContents = _readFlutterPluginsList(directory); final String pluginManifest = plugins.map((Plugin p) => '${p.name}=${escapePath(p.path)}').join('\n'); if (pluginManifest.isNotEmpty) { @@ -101,16 +100,15 @@ bool _writeFlutterPluginsList(FlutterProject project, List plugins) { if (pluginsFile.existsSync()) pluginsFile.deleteSync(); } - final String newContents = _readFlutterPluginsList(project); + final String newContents = _readFlutterPluginsList(directory); return oldContents != newContents; } /// Returns the contents of the `.flutter-plugins` file in [directory], or /// null if that file does not exist. -String _readFlutterPluginsList(FlutterProject project) { - return project.flutterPluginsFile.existsSync() - ? project.flutterPluginsFile.readAsStringSync() - : null; +String _readFlutterPluginsList(String directory) { + final File pluginsFile = fs.file(fs.path.join(directory, '.flutter-plugins')); + return pluginsFile.existsSync() ? pluginsFile.readAsStringSync() : null; } const String _androidPluginRegistryTemplate = '''package io.flutter.plugins; @@ -144,7 +142,7 @@ public final class GeneratedPluginRegistrant { } '''; -Future _writeAndroidPluginRegistrant(FlutterProject project, List plugins) async { +void _writeAndroidPluginRegistrant(String directory, List plugins) { final List> androidPlugins = plugins .where((Plugin p) => p.androidPackage != null && p.pluginClass != null) .map((Plugin p) => { @@ -157,19 +155,8 @@ Future _writeAndroidPluginRegistrant(FlutterProject project, List 'plugins': androidPlugins, }; - 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', - ); + final String javaSourcePath = fs.path.join(directory, 'src', 'main', 'java'); + final String registryPath = fs.path.join(javaSourcePath, 'io', 'flutter', 'plugins', 'GeneratedPluginRegistrant.java'); _renderTemplateToFile(_androidPluginRegistryTemplate, context, registryPath); } @@ -234,7 +221,7 @@ Depends on all your plugins, and provides a function to register them. end '''; -Future _writeIOSPluginRegistrant(FlutterProject project, List plugins) async { +void _writeIOSPluginRegistrant(String directory, FlutterManifest manifest, List plugins) { final List> iosPlugins = plugins .where((Plugin p) => p.pluginClass != null) .map((Plugin p) => { @@ -247,8 +234,10 @@ Future _writeIOSPluginRegistrant(FlutterProject project, List plug 'plugins': iosPlugins, }; - final String registryDirectory = (await project.iosPluginRegistrantHost).path; - if ((await project.manifest).isModule) { + 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 registryClassesDirectory = fs.path.join(registryDirectory, 'Classes'); _renderTemplateToFile( _iosPluginRegistrantPodspecTemplate, @@ -266,37 +255,57 @@ Future _writeIOSPluginRegistrant(FlutterProject project, List plug 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(registryDirectory, 'GeneratedPluginRegistrant.h'), + fs.path.join(runnerDirectory, 'GeneratedPluginRegistrant.h'), ); _renderTemplateToFile( _iosPluginRegistryImplementationTemplate, context, - fs.path.join(registryDirectory, 'GeneratedPluginRegistrant.m'), + fs.path.join(runnerDirectory, 'GeneratedPluginRegistrant.m'), ); } } -/// Injects plugins found in `pubspec.yaml` into the platform-specific projects. -Future injectPlugins(FlutterProject project) async { - final List plugins = findPlugins(project); - final bool changed = _writeFlutterPluginsList(project, plugins); - await _writeAndroidPluginRegistrant(project, plugins); - await _writeIOSPluginRegistrant(project, plugins); +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; +} - if (project.ios.directory.existsSync()) { +/// Injects plugins found in `pubspec.yaml` into the platform-specific projects. +void injectPlugins({@required String projectPath, @required FlutterManifest manifest}) { + final List 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); final CocoaPods cocoaPods = new CocoaPods(); if (plugins.isNotEmpty) - cocoaPods.setupPodfile(project.ios); + cocoaPods.setupPodfile(projectPath, manifest); if (changed) - cocoaPods.invalidatePodInstallOutput(project.ios); + cocoaPods.invalidatePodInstallOutput(projectPath); } } /// Returns whether the Flutter project at the specified [directory] /// has any plugin dependencies. -bool hasPlugins(FlutterProject project) { - return _readFlutterPluginsList(project) != null; +bool hasPlugins({String directory}) { + directory ??= fs.currentDirectory.path; + return _readFlutterPluginsList(directory) != null; } diff --git a/packages/flutter_tools/lib/src/project.dart b/packages/flutter_tools/lib/src/project.dart index ad506d2a2ef..d3f2e66a914 100644 --- a/packages/flutter_tools/lib/src/project.dart +++ b/packages/flutter_tools/lib/src/project.dart @@ -5,6 +5,7 @@ import 'dart:async'; import 'dart:convert'; + import 'android/gradle.dart' as gradle; import 'base/file_system.dart'; import 'bundle.dart' as bundle; @@ -66,57 +67,6 @@ class FlutterProject { /// The generated IosModule sub project of this module project. IosModuleProject get iosModule => new IosModuleProject(directory.childDirectory('.ios')); - Future get androidLocalPropertiesFile { - return _androidLocalPropertiesFile ??= manifest.then((FlutterManifest manifest) { - return directory.childDirectory(manifest.isModule ? '.android' : 'android') - .childFile('local.properties'); - }); - } - Future _androidLocalPropertiesFile; - - Future get generatedXcodePropertiesFile { - return _generatedXcodeProperties ??= manifest.then((FlutterManifest manifest) { - return directory.childDirectory(manifest.isModule ? '.ios' : 'ios') - .childDirectory('Flutter') - .childFile('Generated.xcconfig'); - }); - } - Future _generatedXcodeProperties; - - File get flutterPluginsFile { - return _flutterPluginsFile ??= directory.childFile('.flutter-plugins'); - } - File _flutterPluginsFile; - - Future 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 _androidPluginRegistrantHost; - - Future 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 _iosPluginRegistrantHost; - /// Returns true if this project has an example application bool get hasExampleApp => _exampleDirectory.childFile('pubspec.yaml').existsSync(); @@ -136,11 +86,11 @@ class FlutterProject { } final FlutterManifest manifest = await this.manifest; if (manifest.isModule) { - await androidModule.ensureReadyForPlatformSpecificTooling(this); - await iosModule.ensureReadyForPlatformSpecificTooling(); + await androidModule.ensureReadyForPlatformSpecificTooling(manifest); + await iosModule.ensureReadyForPlatformSpecificTooling(manifest); } - await xcode.generateXcodeProperties(project: this); - await injectPlugins(this); + xcode.generateXcodeProperties(projectPath: directory.path, manifest: manifest); + injectPlugins(projectPath: directory.path, manifest: manifest); } } @@ -151,20 +101,6 @@ 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 productBundleIdentifier() { final File projectFile = directory.childDirectory('Runner.xcodeproj').childFile('project.pbxproj'); return _firstMatchInFile(projectFile, _productBundleIdPattern).then((Match match) => match?.group(1)); @@ -178,7 +114,7 @@ class IosModuleProject { final Directory directory; - Future ensureReadyForPlatformSpecificTooling() async { + Future ensureReadyForPlatformSpecificTooling(FlutterManifest manifest) async { if (_shouldRegenerate()) { final Template template = new Template.fromName(fs.path.join('module', 'ios')); template.render(directory, {}, printStatusWhenWriting: false); @@ -197,29 +133,6 @@ 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 applicationId() { @@ -240,15 +153,15 @@ class AndroidModuleProject { final Directory directory; - Future ensureReadyForPlatformSpecificTooling(FlutterProject project) async { + Future ensureReadyForPlatformSpecificTooling(FlutterManifest manifest) async { if (_shouldRegenerate()) { final Template template = new Template.fromName(fs.path.join('module', 'android')); template.render(directory, { - 'androidIdentifier': (await project.manifest).moduleDescriptor['androidPackage'], + 'androidIdentifier': manifest.moduleDescriptor['androidPackage'], }, printStatusWhenWriting: false); gradle.injectGradleWrapper(directory); } - await gradle.updateLocalProperties(project: project, requireAndroidSdk: false); + gradle.updateLocalPropertiesSync(directory, manifest); } bool _shouldRegenerate() { diff --git a/packages/flutter_tools/lib/src/resident_runner.dart b/packages/flutter_tools/lib/src/resident_runner.dart index 66c805635b5..771d83dc604 100644 --- a/packages/flutter_tools/lib/src/resident_runner.dart +++ b/packages/flutter_tools/lib/src/resident_runner.dart @@ -6,6 +6,7 @@ import 'dart:async'; import 'package:meta/meta.dart'; +import 'android/gradle.dart'; import 'application_package.dart'; import 'artifacts.dart'; import 'asset.dart'; @@ -23,7 +24,6 @@ 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,11 +814,15 @@ abstract class ResidentRunner { new DartDependencySetBuilder(mainPath, packagesFilePath); final DependencyChecker dependencyChecker = new DependencyChecker(dartDependencySetBuilder, assetBundle); - if (device.package.packagesFile == null || !device.package.packagesFile.existsSync()) { + final String path = device.package.packagePath; + if (path == null) return true; - } - final DateTime lastBuildTime = device.package.packagesFile.statSync().modified; - + 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; return dependencyChecker.check(lastBuildTime); } @@ -902,9 +906,11 @@ String getMissingPackageHintForPlatform(TargetPlatform platform) { case TargetPlatform.android_arm64: case TargetPlatform.android_x64: case TargetPlatform.android_x86: - 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.'; + String manifest = 'android/AndroidManifest.xml'; + if (isProjectUsingGradle()) { + manifest = gradleManifestPath; + } + return 'Is your project missing an $manifest?\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: diff --git a/packages/flutter_tools/lib/src/run_cold.dart b/packages/flutter_tools/lib/src/run_cold.dart index a0a40a254a0..aafa113b536 100644 --- a/packages/flutter_tools/lib/src/run_cold.dart +++ b/packages/flutter_tools/lib/src/run_cold.dart @@ -30,7 +30,7 @@ class ColdRunner extends ResidentRunner { ipv6: ipv6); final bool traceStartup; - final File applicationBinary; + final String applicationBinary; @override Future run({ diff --git a/packages/flutter_tools/lib/src/run_hot.dart b/packages/flutter_tools/lib/src/run_hot.dart index d7226098746..a1ec78bb780 100644 --- a/packages/flutter_tools/lib/src/run_hot.dart +++ b/packages/flutter_tools/lib/src/run_hot.dart @@ -64,7 +64,7 @@ class HotRunner extends ResidentRunner { ipv6: ipv6); final bool benchmarkMode; - final File applicationBinary; + final String applicationBinary; final bool hostIsIde; Set _dartDependencies; final String dillOutputPath; diff --git a/packages/flutter_tools/lib/src/tester/flutter_tester.dart b/packages/flutter_tools/lib/src/tester/flutter_tester.dart index 88637c8e059..ad65f7b12fe 100644 --- a/packages/flutter_tools/lib/src/tester/flutter_tester.dart +++ b/packages/flutter_tools/lib/src/tester/flutter_tester.dart @@ -22,21 +22,21 @@ import '../protocol_discovery.dart'; import '../version.dart'; class FlutterTesterApp extends ApplicationPackage { - final Directory _directory; + final String _directory; factory FlutterTesterApp.fromCurrentDirectory() { - return new FlutterTesterApp._(fs.currentDirectory); + return new FlutterTesterApp._(fs.currentDirectory.path); } - FlutterTesterApp._(Directory directory) + FlutterTesterApp._(String directory) : _directory = directory, - super(id: directory.path); + super(id: directory); @override - String get name => _directory.basename; + String get name => fs.path.basename(_directory); @override - File get packagesFile => _directory.childFile('.packages'); + String get packagePath => fs.path.join(_directory, '.packages'); } // TODO(scheglov): This device does not currently work with full restarts. diff --git a/packages/flutter_tools/test/android/gradle_test.dart b/packages/flutter_tools/test/android/gradle_test.dart index 74603d22aa7..750651cd686 100644 --- a/packages/flutter_tools/test/android/gradle_test.dart +++ b/packages/flutter_tools/test/android/gradle_test.dart @@ -11,7 +11,6 @@ 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'; @@ -32,7 +31,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(project: new FlutterProject(fs.currentDirectory)); + await updateLocalProperties(); } on Exception catch (e) { shouldBeToolExit = e; } @@ -70,10 +69,7 @@ someProperty: someValue buildDir: /Users/some/apps/hello/build/app someOtherProperty: someOtherValue '''); - expect( - fs.path.normalize(project.apkDirectory.path), - fs.path.normalize('/Users/some/apps/hello/build/app/outputs/apk'), - ); + expect(project.apkDirectory, fs.path.normalize('/Users/some/apps/hello/build/app/outputs/apk')); }); test('should extract default build variants from app properties', () { final GradleProject project = projectFrom(''' @@ -114,27 +110,27 @@ someOtherProperty: someOtherValue expect(project.productFlavors, ['free', 'paid']); }); test('should provide apk file name for default build types', () { - final GradleProject project = new GradleProject(['debug', 'profile', 'release'], [], fs.directory('/some/dir')); + final GradleProject project = new GradleProject(['debug', 'profile', 'release'], [], '/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(['debug', 'profile', 'release'], ['free', 'paid'], fs.directory('/some/dir')); + final GradleProject project = new GradleProject(['debug', 'profile', 'release'], ['free', 'paid'], '/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(['debug', 'profile', 'release'], [], fs.directory('/some/dir')); + final GradleProject project = new GradleProject(['debug', 'profile', 'release'], [], '/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(['debug', 'profile', 'release'], ['free', 'paid'], fs.directory('/some/dir')); + final GradleProject project = new GradleProject(['debug', 'profile', 'release'], ['free', 'paid'], '/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); @@ -189,10 +185,7 @@ someOtherProperty: someOtherValue writeSchemaFile(fs, schemaData); try { - await updateLocalProperties( - project: new FlutterProject.fromPath('path/to/project'), - buildInfo: buildInfo, - ); + await updateLocalProperties(projectPath: 'path/to/project', buildInfo: buildInfo); final File localPropertiesFile = fs.file('path/to/project/android/local.properties'); expect(propertyFor('flutter.versionName', localPropertiesFile), expectedBuildName); diff --git a/packages/flutter_tools/test/application_package_test.dart b/packages/flutter_tools/test/application_package_test.dart index a57e72b2f40..da5b17a98bd 100644 --- a/packages/flutter_tools/test/application_package_test.dart +++ b/packages/flutter_tools/test/application_package_test.dart @@ -57,7 +57,7 @@ void main() { }; testUsingContext('Error on non-existing file', () { final PrebuiltIOSApp iosApp = - new IOSApp.fromPrebuiltApp(fs.file('not_existing.ipa')); + new IOSApp.fromPrebuiltApp('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(fs.file('regular_folder')); + new IOSApp.fromPrebuiltApp('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(fs.file('bundle.app')); + final PrebuiltIOSApp iosApp = new IOSApp.fromPrebuiltApp('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(fs.file('bundle.app')); + final PrebuiltIOSApp iosApp = new IOSApp.fromPrebuiltApp('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(fs.file('bundle.app')); + final PrebuiltIOSApp iosApp = new IOSApp.fromPrebuiltApp('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(fs.file('app.ipa')); + final PrebuiltIOSApp iosApp = new IOSApp.fromPrebuiltApp('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(fs.file('app.ipa')); + final PrebuiltIOSApp iosApp = new IOSApp.fromPrebuiltApp('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(fs.file('app.ipa')); + final PrebuiltIOSApp iosApp = new IOSApp.fromPrebuiltApp('app.ipa'); final BufferLogger logger = context[Logger]; expect(logger.errorText, isEmpty); expect(iosApp.bundleDir.path, endsWith('bundle.app')); diff --git a/packages/flutter_tools/test/ios/cocoapods_test.dart b/packages/flutter_tools/test/ios/cocoapods_test.dart index fe43de429dd..b65e75763b5 100644 --- a/packages/flutter_tools/test/ios/cocoapods_test.dart +++ b/packages/flutter_tools/test/ios/cocoapods_test.dart @@ -9,6 +9,7 @@ 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'; @@ -24,7 +25,7 @@ void main() { FileSystem fs; ProcessManager mockProcessManager; MockXcodeProjectInterpreter mockXcodeProjectInterpreter; - FlutterProject projectUnderTest; + Directory projectUnderTest; CocoaPods cocoaPodsUnderTest; InvokeProcess resultOfPodVersion; @@ -45,8 +46,7 @@ void main() { fs = new MemoryFileSystem(); mockProcessManager = new MockProcessManager(); mockXcodeProjectInterpreter = new MockXcodeProjectInterpreter(); - projectUnderTest = new FlutterProject(fs.directory('project')); - projectUnderTest.ios.directory.createSync(recursive: true); + projectUnderTest = fs.directory(fs.path.join('project', 'ios'))..createSync(recursive: true); cocoaPodsUnderTest = new CocoaPods(); pretendPodVersionIs('1.5.0'); fs.file(fs.path.join( @@ -124,10 +124,22 @@ void main() { }); group('Setup Podfile', () { - testUsingContext('creates objective-c Podfile when not present', () async { - cocoaPodsUnderTest.setupPodfile(projectUnderTest.ios); + File podFile; + File debugConfigFile; + File releaseConfigFile; - expect(projectUnderTest.ios.podfile.readAsStringSync(), 'Objective-C podfile template'); + 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); + + expect(podFile.readAsStringSync(), 'Objective-C podfile template'); }, overrides: { FileSystem: () => fs, }); @@ -138,22 +150,24 @@ void main() { 'SWIFT_VERSION': '4.0', }); - final FlutterProject project = new FlutterProject.fromPath('project'); - cocoaPodsUnderTest.setupPodfile(project.ios); + final FlutterManifest manifest = + await new FlutterProject.fromPath('project').manifest; + cocoaPodsUnderTest.setupPodfile('project', manifest); - expect(projectUnderTest.ios.podfile.readAsStringSync(), 'Swift podfile template'); + expect(podFile.readAsStringSync(), 'Swift podfile template'); }, overrides: { FileSystem: () => fs, XcodeProjectInterpreter: () => mockXcodeProjectInterpreter, }); testUsingContext('does not recreate Podfile when already present', () async { - projectUnderTest.ios.podfile..createSync()..writeAsStringSync('Existing Podfile'); + podFile..createSync()..writeAsStringSync('Existing Podfile'); - final FlutterProject project = new FlutterProject.fromPath('project'); - cocoaPodsUnderTest.setupPodfile(project.ios); + final FlutterManifest manifest = + await new FlutterProject.fromPath('project').manifest; + cocoaPodsUnderTest.setupPodfile('project', manifest); - expect(projectUnderTest.ios.podfile.readAsStringSync(), 'Existing Podfile'); + expect(podFile.readAsStringSync(), 'Existing Podfile'); }, overrides: { FileSystem: () => fs, }); @@ -161,32 +175,30 @@ void main() { testUsingContext('does not create Podfile when we cannot interpret Xcode projects', () async { when(mockXcodeProjectInterpreter.isInstalled).thenReturn(false); - final FlutterProject project = new FlutterProject.fromPath('project'); - cocoaPodsUnderTest.setupPodfile(project.ios); + final FlutterManifest manifest = + await new FlutterProject.fromPath('project').manifest; + cocoaPodsUnderTest.setupPodfile('project', manifest); - expect(projectUnderTest.ios.podfile.existsSync(), false); + expect(podFile.existsSync(), false); }, overrides: { FileSystem: () => fs, XcodeProjectInterpreter: () => mockXcodeProjectInterpreter, }); testUsingContext('includes Pod config in xcconfig files, if not present', () async { - 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'); + podFile..createSync()..writeAsStringSync('Existing Podfile'); + debugConfigFile..createSync(recursive: true)..writeAsStringSync('Existing debug config'); + releaseConfigFile..createSync(recursive: true)..writeAsStringSync('Existing release config'); - final FlutterProject project = new FlutterProject.fromPath('project'); - cocoaPodsUnderTest.setupPodfile(project.ios); + final FlutterManifest manifest = + await new FlutterProject.fromPath('project').manifest; + cocoaPodsUnderTest.setupPodfile('project', manifest); - final String debugContents = projectUnderTest.ios.xcodeConfigFor('Debug').readAsStringSync(); + final String debugContents = debugConfigFile.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 = projectUnderTest.ios.xcodeConfigFor('Release').readAsStringSync(); + final String releaseContents = releaseConfigFile.readAsStringSync(); expect(releaseContents, contains( '#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"\n')); expect(releaseContents, contains('Existing release config')); @@ -198,9 +210,9 @@ void main() { group('Process pods', () { testUsingContext('prints error, if CocoaPods is not installed', () async { pretendPodIsNotInstalled(); - projectUnderTest.ios.podfile.createSync(); + projectUnderTest.childFile('Podfile').createSync(); final bool didInstall = await cocoaPodsUnderTest.processPods( - iosProject: projectUnderTest.ios, + appIosDirectory: projectUnderTest, iosEngineDir: 'engine/path', ); verifyNever(mockProcessManager.run( @@ -219,7 +231,7 @@ void main() { testUsingContext('throws, if Podfile is missing.', () async { try { await cocoaPodsUnderTest.processPods( - iosProject: projectUnderTest.ios, + appIosDirectory: projectUnderTest, iosEngineDir: 'engine/path', ); fail('ToolExit expected'); @@ -266,7 +278,7 @@ Note: as of CocoaPods 1.0, `pod repo update` does not happen on `pod install` by )); try { await cocoaPodsUnderTest.processPods( - iosProject: projectUnderTest.ios, + appIosDirectory: projectUnderTest, iosEngineDir: 'engine/path', ); fail('ToolExit expected'); @@ -283,14 +295,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.ios.podfile + projectUnderTest.childFile('Podfile') ..createSync() ..writeAsStringSync('Existing Podfile'); - projectUnderTest.ios.podManifestLock + projectUnderTest.childFile('Pods/Manifest.lock') ..createSync(recursive: true) ..writeAsStringSync('Existing lock file.'); final bool didInstall = await cocoaPodsUnderTest.processPods( - iosProject: projectUnderTest.ios, + appIosDirectory: projectUnderTest, iosEngineDir: 'engine/path', dependenciesChanged: false, ); @@ -306,14 +318,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.ios.podfile + projectUnderTest.childFile('Podfile') ..createSync() ..writeAsStringSync('Existing Podfile'); - projectUnderTest.ios.podfileLock + projectUnderTest.childFile('Podfile.lock') ..createSync() ..writeAsStringSync('Existing lock file.'); final bool didInstall = await cocoaPodsUnderTest.processPods( - iosProject: projectUnderTest.ios, + appIosDirectory: projectUnderTest, iosEngineDir: 'engine/path', dependenciesChanged: false, ); @@ -332,17 +344,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.ios.podfile + projectUnderTest.childFile('Podfile') ..createSync() ..writeAsStringSync('Existing Podfile'); - projectUnderTest.ios.podfileLock + projectUnderTest.childFile('Podfile.lock') ..createSync() ..writeAsStringSync('Existing lock file.'); - projectUnderTest.ios.podManifestLock + projectUnderTest.childFile('Pods/Manifest.lock') ..createSync(recursive: true) ..writeAsStringSync('Different lock file.'); final bool didInstall = await cocoaPodsUnderTest.processPods( - iosProject: projectUnderTest.ios, + appIosDirectory: projectUnderTest, iosEngineDir: 'engine/path', dependenciesChanged: false, ); @@ -361,17 +373,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.ios.podfile + projectUnderTest.childFile('Podfile') ..createSync() ..writeAsStringSync('Existing Podfile'); - projectUnderTest.ios.podfileLock + projectUnderTest.childFile('Podfile.lock') ..createSync() ..writeAsStringSync('Existing lock file.'); - projectUnderTest.ios.podManifestLock + projectUnderTest.childFile('Pods/Manifest.lock') ..createSync(recursive: true) ..writeAsStringSync('Existing lock file.'); final bool didInstall = await cocoaPodsUnderTest.processPods( - iosProject: projectUnderTest.ios, + appIosDirectory: projectUnderTest, iosEngineDir: 'engine/path', dependenciesChanged: true, ); @@ -390,20 +402,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.ios.podfile + projectUnderTest.childFile('Podfile') ..createSync() ..writeAsStringSync('Existing Podfile'); - projectUnderTest.ios.podfileLock + projectUnderTest.childFile('Podfile.lock') ..createSync() ..writeAsStringSync('Existing lock file.'); - projectUnderTest.ios.podManifestLock + projectUnderTest.childFile('Pods/Manifest.lock') ..createSync(recursive: true) ..writeAsStringSync('Existing lock file.'); await new Future.delayed(const Duration(milliseconds: 10)); - projectUnderTest.ios.podfile + projectUnderTest.childFile('Podfile') ..writeAsStringSync('Updated Podfile'); await cocoaPodsUnderTest.processPods( - iosProject: projectUnderTest.ios, + appIosDirectory: projectUnderTest, iosEngineDir: 'engine/path', dependenciesChanged: false, ); @@ -421,17 +433,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.ios.podfile + projectUnderTest.childFile('Podfile') ..createSync() ..writeAsStringSync('Existing Podfile'); - projectUnderTest.ios.podfileLock + projectUnderTest.childFile('Podfile.lock') ..createSync() ..writeAsStringSync('Existing lock file.'); - projectUnderTest.ios.podManifestLock + projectUnderTest.childFile('Pods/Manifest.lock') ..createSync(recursive: true) ..writeAsStringSync('Existing lock file.'); final bool didInstall = await cocoaPodsUnderTest.processPods( - iosProject: projectUnderTest.ios, + appIosDirectory: projectUnderTest, iosEngineDir: 'engine/path', dependenciesChanged: false, ); @@ -447,13 +459,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.ios.podfile + projectUnderTest.childFile('Podfile') ..createSync() ..writeAsStringSync('Existing Podfile'); - projectUnderTest.ios.podfileLock + projectUnderTest.childFile('Podfile.lock') ..createSync() ..writeAsStringSync('Existing lock file.'); - projectUnderTest.ios.podManifestLock + projectUnderTest.childFile('Pods/Manifest.lock') ..createSync(recursive: true) ..writeAsStringSync('Existing lock file.'); @@ -470,13 +482,13 @@ Note: as of CocoaPods 1.0, `pod repo update` does not happen on `pod install` by try { await cocoaPodsUnderTest.processPods( - iosProject: projectUnderTest.ios, + appIosDirectory: projectUnderTest, iosEngineDir: 'engine/path', dependenciesChanged: true, ); fail('Tool throw expected when pod install fails'); } on ToolExit { - expect(projectUnderTest.ios.podManifestLock.existsSync(), isFalse); + expect(projectUnderTest.childFile('Pods/Manifest.lock').existsSync(), isFalse); } }, overrides: { FileSystem: () => fs, diff --git a/packages/flutter_tools/test/ios/xcodeproj_test.dart b/packages/flutter_tools/test/ios/xcodeproj_test.dart index d1c44f95c93..6fb0f61fccc 100644 --- a/packages/flutter_tools/test/ios/xcodeproj_test.dart +++ b/packages/flutter_tools/test/ios/xcodeproj_test.dart @@ -289,9 +289,11 @@ Information about project "Runner": previewDart2: true, targetPlatform: TargetPlatform.ios, ); - final FlutterProject project = new FlutterProject.fromPath('path/to/project'); - await updateGeneratedXcodeProperties( - project: project, + final FlutterManifest manifest = + await new FlutterProject.fromPath('path/to/project').manifest; + updateGeneratedXcodeProperties( + projectPath: 'path/to/project', + manifest: manifest, buildInfo: buildInfo, previewDart2: true, ); @@ -311,9 +313,11 @@ Information about project "Runner": trackWidgetCreation: true, targetPlatform: TargetPlatform.ios, ); - final FlutterProject project = new FlutterProject.fromPath('path/to/project'); - await updateGeneratedXcodeProperties( - project: project, + final FlutterManifest manifest = + await new FlutterProject.fromPath('path/to/project').manifest; + updateGeneratedXcodeProperties( + projectPath: 'path/to/project', + manifest: manifest, buildInfo: buildInfo, previewDart2: true, ); @@ -332,9 +336,11 @@ Information about project "Runner": previewDart2: true, targetPlatform: TargetPlatform.ios, ); - final FlutterProject project = new FlutterProject.fromPath('path/to/project'); - await updateGeneratedXcodeProperties( - project: project, + final FlutterManifest manifest = + await new FlutterProject.fromPath('path/to/project').manifest; + updateGeneratedXcodeProperties( + projectPath: 'path/to/project', + manifest: manifest, buildInfo: buildInfo, previewDart2: true, ); @@ -354,9 +360,11 @@ Information about project "Runner": targetPlatform: TargetPlatform.ios, ); - final FlutterProject project = new FlutterProject.fromPath('path/to/project'); - await updateGeneratedXcodeProperties( - project: project, + final FlutterManifest manifest = + await new FlutterProject.fromPath('path/to/project').manifest; + updateGeneratedXcodeProperties( + projectPath: 'path/to/project', + manifest: manifest, buildInfo: buildInfo, previewDart2: true, ); @@ -404,8 +412,11 @@ Information about project "Runner": const String schemaData = '{}'; writeSchemaFile(fs, schemaData); - await updateGeneratedXcodeProperties( - project: new FlutterProject.fromPath('path/to/project'), + final FlutterManifest manifest = + await new FlutterProject.fromPath('path/to/project').manifest; + updateGeneratedXcodeProperties( + projectPath: 'path/to/project', + manifest: manifest, buildInfo: buildInfo, previewDart2: false, ); diff --git a/packages/flutter_tools/test/project_test.dart b/packages/flutter_tools/test/project_test.dart index 7eabcdbecc2..ee5f20ead6b 100644 --- a/packages/flutter_tools/test/project_test.dart +++ b/packages/flutter_tools/test/project_test.dart @@ -129,6 +129,13 @@ void testInMemory(String description, Future 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') diff --git a/packages/flutter_tools/test/src/mocks.dart b/packages/flutter_tools/test/src/mocks.dart index e89fb67bbcc..fe732a98708 100644 --- a/packages/flutter_tools/test/src/mocks.dart +++ b/packages/flutter_tools/test/src/mocks.dart @@ -25,7 +25,7 @@ class MockApplicationPackageStore extends ApplicationPackageStore { MockApplicationPackageStore() : super( android: new AndroidApk( id: 'io.flutter.android.mock', - file: fs.file('/mock/path/to/android/SkyShell.apk'), + apkPath: '/mock/path/to/android/SkyShell.apk', launchActivity: 'io.flutter.android.mock.MockActivity' ), iOS: new BuildableIOSApp( diff --git a/packages/flutter_tools/test/tester/flutter_tester_test.dart b/packages/flutter_tools/test/tester/flutter_tester_test.dart index 7e510d0a730..af38a1ba9a1 100644 --- a/packages/flutter_tools/test/tester/flutter_tester_test.dart +++ b/packages/flutter_tools/test/tester/flutter_tester_test.dart @@ -33,7 +33,7 @@ void main() { final FlutterTesterApp app = new FlutterTesterApp.fromCurrentDirectory(); expect(app.name, 'my_project'); - expect(app.packagesFile.path, fs.path.join(projectPath, '.packages')); + expect(app.packagePath, fs.path.join(projectPath, '.packages')); }, overrides: { FileSystem: () => fs, });