Unverified Commit 5e17a240 authored by Jenn Magder's avatar Jenn Magder Committed by GitHub

Generate only requested platform directories on create (#68376)

parent a4c2075a
......@@ -546,7 +546,10 @@ To edit platform code in an IDE see https://flutter.dev/developing-packages/#edi
generateSyntheticPackage: false,
);
final FlutterProject project = FlutterProject.fromDirectory(directory);
await project.ensureReadyForPlatformSpecificTooling(checkProjects: false);
await project.ensureReadyForPlatformSpecificTooling(
androidPlatform: true,
iosPlatform: true,
);
}
return generatedCount;
}
......@@ -641,7 +644,6 @@ https://flutter.dev/docs/development/packages-and-plugins/developing-packages#pl
}
}
final FlutterProject project = FlutterProject.fromDirectory(directory);
final bool generateAndroid = templateContext['android'] == true;
if (generateAndroid) {
......@@ -680,7 +682,15 @@ https://flutter.dev/docs/development/packages-and-plugins/developing-packages#pl
offline: boolArg('offline'),
generateSyntheticPackage: false,
);
await project.ensureReadyForPlatformSpecificTooling(checkProjects: pluginExampleApp);
await project.ensureReadyForPlatformSpecificTooling(
androidPlatform: templateContext['android'] as bool ?? false,
iosPlatform: templateContext['ios'] as bool ?? false,
linuxPlatform: templateContext['linux'] as bool ?? false,
macOSPlatform: templateContext['macos'] as bool ?? false,
windowsPlatform: templateContext['windows'] as bool ?? false,
webPlatform: templateContext['web'] as bool ?? false,
);
}
if (templateContext['android'] == true) {
gradle.updateLocalProperties(project: project, requireAndroidSdk: false);
......
......@@ -147,13 +147,13 @@ class PackagesGetCommand extends FlutterCommand {
final FlutterProject rootProject = FlutterProject.fromPath(target);
await _runPubGet(target, rootProject);
await rootProject.ensureReadyForPlatformSpecificTooling(checkProjects: true);
await rootProject.regeneratePlatformSpecificTooling();
// Get/upgrade packages in example app as well
if (rootProject.hasExampleApp) {
final FlutterProject exampleProject = rootProject.example;
await _runPubGet(exampleProject.directory.path, exampleProject);
await exampleProject.ensureReadyForPlatformSpecificTooling(checkProjects: true);
await exampleProject.regeneratePlatformSpecificTooling();
}
return FlutterCommandResult.success();
......
......@@ -657,7 +657,7 @@ class _ResidentWebRunner extends ResidentWebRunner {
final bool hasWebPlugins = (await findPlugins(flutterProject))
.any((Plugin p) => p.platforms.containsKey(WebPlugin.kConfigKey));
await injectPlugins(flutterProject, checkProjects: true);
await injectPlugins(flutterProject, webPlatform: true);
final Uri generatedUri = globals.fs.currentDirectory
.childDirectory('lib')
......
......@@ -18,7 +18,7 @@ Future<void> processPodsIfNeeded(
) async {
final FlutterProject project = xcodeProject.parent;
// Ensure that the plugin list is up to date, since hasPlugins relies on it.
await refreshPluginsList(project);
await refreshPluginsList(project, macOSPlatform: project.macos.existsSync());
if (!(hasPlugins(project) || (project.isModule && xcodeProject.podfile.existsSync()))) {
return;
}
......
......@@ -1145,11 +1145,12 @@ void _createPlatformPluginSymlinks(Directory symlinkDirectory, List<dynamic> pla
/// Rewrites the `.flutter-plugins` file of [project] based on the plugin
/// dependencies declared in `pubspec.yaml`.
///
/// If `checkProjects` is true, then plugins are only injected into directories
/// which already exist.
///
/// Assumes `pub get` has been executed since last change to `pubspec.yaml`.
Future<void> refreshPluginsList(FlutterProject project, {bool checkProjects = false}) async {
Future<void> refreshPluginsList(
FlutterProject project, {
bool iosPlatform = false,
bool macOSPlatform = false,
}) async {
final List<Plugin> plugins = await findPlugins(project);
// TODO(franciscojma): Remove once migration is complete.
......@@ -1159,12 +1160,10 @@ Future<void> refreshPluginsList(FlutterProject project, {bool checkProjects = fa
final bool changed = _writeFlutterPluginsList(project, plugins);
if (changed || legacyChanged) {
createPluginSymlinks(project, force: true);
if (!checkProjects || project.ios.existsSync()) {
if (iosPlatform) {
globals.cocoaPods.invalidatePodInstallOutput(project.ios);
}
// TODO(stuartmorgan): Potentially add checkProjects once a decision has
// made about how to handle macOS in existing projects.
if (project.macos.existsSync()) {
if (macOSPlatform) {
globals.cocoaPods.invalidatePodInstallOutput(project.macos);
}
}
......@@ -1172,34 +1171,40 @@ Future<void> refreshPluginsList(FlutterProject project, {bool checkProjects = fa
/// Injects plugins found in `pubspec.yaml` into the platform-specific projects.
///
/// If `checkProjects` is true, then plugins are only injected into directories
/// which already exist.
///
/// Assumes [refreshPluginsList] has been called since last change to `pubspec.yaml`.
Future<void> injectPlugins(FlutterProject project, {bool checkProjects = false}) async {
Future<void> injectPlugins(
FlutterProject project, {
bool androidPlatform = false,
bool iosPlatform = false,
bool linuxPlatform = false,
bool macOSPlatform = false,
bool windowsPlatform = false,
bool webPlatform = false,
}) async {
final List<Plugin> plugins = await findPlugins(project);
// Sort the plugins by name to keep ordering stable in generated files.
plugins.sort((Plugin left, Plugin right) => left.name.compareTo(right.name));
if ((checkProjects && project.android.existsSync()) || !checkProjects) {
if (androidPlatform) {
await _writeAndroidPluginRegistrant(project, plugins);
}
if ((checkProjects && project.ios.existsSync()) || !checkProjects) {
if (iosPlatform) {
await _writeIOSPluginRegistrant(project, plugins);
}
// TODO(stuartmorgan): Revisit the conditions here once the plans for handling
// desktop in existing projects are in place. For now, ignore checkProjects
// on desktop and always treat it as true.
if (featureFlags.isLinuxEnabled && project.linux.existsSync()) {
if (linuxPlatform) {
await _writeLinuxPluginFiles(project, plugins);
}
if (featureFlags.isMacOSEnabled && project.macos.existsSync()) {
if (macOSPlatform) {
await _writeMacOSPluginRegistrant(project, plugins);
}
if (featureFlags.isWindowsEnabled && project.windows.existsSync()) {
if (windowsPlatform) {
await _writeWindowsPluginFiles(project, plugins);
}
for (final XcodeBasedProject subproject in <XcodeBasedProject>[project.ios, project.macos]) {
if (!project.isModule && (!checkProjects || subproject.existsSync())) {
if (!project.isModule) {
final List<XcodeBasedProject> darwinProjects = <XcodeBasedProject>[
if (iosPlatform) project.ios,
if (macOSPlatform) project.macos,
];
for (final XcodeBasedProject subproject in darwinProjects) {
if (plugins.isNotEmpty) {
await globals.cocoaPods.setupPodfile(subproject);
}
......@@ -1210,7 +1215,7 @@ Future<void> injectPlugins(FlutterProject project, {bool checkProjects = false})
}
}
}
if (featureFlags.isWebEnabled && project.web.existsSync()) {
if (webPlatform) {
await _writeWebPluginRegistrant(project, plugins);
}
}
......
......@@ -229,38 +229,64 @@ class FlutterProject {
return manifest;
}
/// Generates project files necessary to make Gradle builds work on Android
/// and CocoaPods+Xcode work on iOS, for app and module projects only.
// TODO(cyanglaz): The param `checkProjects` is confusing. We should give it a better name
// or add some documentation explaining what it does, or both.
// https://github.com/flutter/flutter/issues/60023
Future<void> ensureReadyForPlatformSpecificTooling({bool checkProjects = false}) async {
/// Reapplies template files and regenerates project files and plugin
/// registrants for app and module projects only.
///
/// Will not create project platform directories if they do not already exist.
Future<void> regeneratePlatformSpecificTooling() async {
return ensureReadyForPlatformSpecificTooling(
androidPlatform: android.existsSync(),
iosPlatform: ios.existsSync(),
// TODO(stuartmorgan): Revisit the conditions here once the plans for handling
// desktop in existing projects are in place.
linuxPlatform: featureFlags.isLinuxEnabled && linux.existsSync(),
macOSPlatform: featureFlags.isMacOSEnabled && macos.existsSync(),
windowsPlatform: featureFlags.isWindowsEnabled && windows.existsSync(),
webPlatform: featureFlags.isWebEnabled && web.existsSync(),
);
}
/// Applies template files and generates project files and plugin
/// registrants for app and module projects only for the specified platforms.
Future<void> ensureReadyForPlatformSpecificTooling({
bool androidPlatform = false,
bool iosPlatform = false,
bool linuxPlatform = false,
bool macOSPlatform = false,
bool windowsPlatform = false,
bool webPlatform = false,
}) async {
if (!directory.existsSync() || hasExampleApp) {
return;
}
await refreshPluginsList(this);
if ((android.existsSync() && checkProjects) || !checkProjects) {
await refreshPluginsList(this, iosPlatform: iosPlatform, macOSPlatform: macOSPlatform);
if (androidPlatform) {
await android.ensureReadyForPlatformSpecificTooling();
}
if ((ios.existsSync() && checkProjects) || !checkProjects) {
if (iosPlatform) {
await ios.ensureReadyForPlatformSpecificTooling();
}
// TODO(stuartmorgan): Revisit conditions once there is a plan for handling
// non-default platform projects. For now, always treat checkProjects as
// true for desktop.
if (featureFlags.isLinuxEnabled && linux.existsSync()) {
if (linuxPlatform) {
await linux.ensureReadyForPlatformSpecificTooling();
}
if (featureFlags.isMacOSEnabled && macos.existsSync()) {
if (macOSPlatform) {
await macos.ensureReadyForPlatformSpecificTooling();
}
if (featureFlags.isWindowsEnabled && windows.existsSync()) {
if (windowsPlatform) {
await windows.ensureReadyForPlatformSpecificTooling();
}
if (featureFlags.isWebEnabled && web.existsSync()) {
if (webPlatform) {
await web.ensureReadyForPlatformSpecificTooling();
}
await injectPlugins(this, checkProjects: checkProjects);
await injectPlugins(
this,
androidPlatform: androidPlatform,
iosPlatform: iosPlatform,
linuxPlatform: linuxPlatform,
macOSPlatform: macOSPlatform,
windowsPlatform: windowsPlatform,
webPlatform: webPlatform,
);
}
/// Returns a json encoded string containing the [appName], [version], and [buildNumber] that is used to generate version.json
......
......@@ -1013,7 +1013,7 @@ abstract class FlutterCommand extends Command<void> {
);
// All done updating dependencies. Release the cache lock.
Cache.releaseLock();
await project.ensureReadyForPlatformSpecificTooling(checkProjects: true);
await project.regeneratePlatformSpecificTooling();
} else {
Cache.releaseLock();
}
......
......@@ -41,7 +41,7 @@ Future<void> buildWeb(
outputDirectory.deleteSync(recursive: true);
outputDirectory.createSync(recursive: true);
}
await injectPlugins(flutterProject, checkProjects: true);
await injectPlugins(flutterProject, webPlatform: true);
final Status status = globals.logger.startProgress('Compiling $target for the Web...');
final Stopwatch sw = Stopwatch()..start();
try {
......
......@@ -301,7 +301,7 @@ void main() {
..writeAsStringSync('Existing release config');
final FlutterProject project = FlutterProject.fromPath('project');
await injectPlugins(project, checkProjects: true);
await injectPlugins(project, iosPlatform: true);
final String debugContents = projectUnderTest.ios.xcodeConfigFor('Debug').readAsStringSync();
expect(debugContents, contains(
......
......@@ -116,12 +116,12 @@ void main() {
FlutterManifest.empty(logger: logger),
FlutterManifest.empty(logger: logger),
);
await project.ensureReadyForPlatformSpecificTooling();
await project.regeneratePlatformSpecificTooling();
expectNotExists(project.directory);
});
_testInMemory('does nothing in plugin or package root project', () async {
final FlutterProject project = await aPluginProject();
await project.ensureReadyForPlatformSpecificTooling();
await project.regeneratePlatformSpecificTooling();
expectNotExists(project.ios.hostAppRoot.childDirectory('Runner').childFile('GeneratedPluginRegistrant.h'));
expectNotExists(androidPluginRegistrant(project.android.hostAppGradleRoot.childDirectory('app')));
expectNotExists(project.ios.hostAppRoot.childDirectory('Flutter').childFile('Generated.xcconfig'));
......@@ -129,22 +129,22 @@ void main() {
});
_testInMemory('injects plugins for iOS', () async {
final FlutterProject project = await someProject();
await project.ensureReadyForPlatformSpecificTooling();
await project.regeneratePlatformSpecificTooling();
expectExists(project.ios.hostAppRoot.childDirectory('Runner').childFile('GeneratedPluginRegistrant.h'));
});
_testInMemory('generates Xcode configuration for iOS', () async {
final FlutterProject project = await someProject();
await project.ensureReadyForPlatformSpecificTooling();
await project.regeneratePlatformSpecificTooling();
expectExists(project.ios.hostAppRoot.childDirectory('Flutter').childFile('Generated.xcconfig'));
});
_testInMemory('injects plugins for Android', () async {
final FlutterProject project = await someProject();
await project.ensureReadyForPlatformSpecificTooling();
await project.regeneratePlatformSpecificTooling();
expectExists(androidPluginRegistrant(project.android.hostAppGradleRoot.childDirectory('app')));
});
_testInMemory('updates local properties for Android', () async {
final FlutterProject project = await someProject();
await project.ensureReadyForPlatformSpecificTooling();
await project.regeneratePlatformSpecificTooling();
expectExists(project.android.hostAppGradleRoot.childFile('local.properties'));
});
_testInMemory('Android project not on v2 embedding shows a warning', () async {
......@@ -153,18 +153,18 @@ void main() {
// v1 embedding, as opposed to having <meta-data
// android:name="flutterEmbedding" android:value="2" />.
await project.ensureReadyForPlatformSpecificTooling();
await project.regeneratePlatformSpecificTooling();
expect(testLogger.statusText, contains('https://flutter.dev/go/android-project-migration'));
});
_testInMemory('updates local properties for Android', () async {
final FlutterProject project = await someProject();
await project.ensureReadyForPlatformSpecificTooling();
await project.regeneratePlatformSpecificTooling();
expectExists(project.android.hostAppGradleRoot.childFile('local.properties'));
});
testUsingContext('injects plugins for macOS', () async {
final FlutterProject project = await someProject();
project.macos.managedDirectory.createSync(recursive: true);
await project.ensureReadyForPlatformSpecificTooling();
await project.regeneratePlatformSpecificTooling();
expectExists(project.macos.managedDirectory.childFile('GeneratedPluginRegistrant.swift'));
}, overrides: <Type, Generator>{
FileSystem: () => MemoryFileSystem.test(),
......@@ -178,7 +178,7 @@ void main() {
testUsingContext('generates Xcode configuration for macOS', () async {
final FlutterProject project = await someProject();
project.macos.managedDirectory.createSync(recursive: true);
await project.ensureReadyForPlatformSpecificTooling();
await project.regeneratePlatformSpecificTooling();
expectExists(project.macos.generatedXcodePropertiesFile);
}, overrides: <Type, Generator>{
FileSystem: () => MemoryFileSystem.test(),
......@@ -192,7 +192,7 @@ void main() {
testUsingContext('injects plugins for Linux', () async {
final FlutterProject project = await someProject();
project.linux.cmakeFile.createSync(recursive: true);
await project.ensureReadyForPlatformSpecificTooling();
await project.regeneratePlatformSpecificTooling();
expectExists(project.linux.managedDirectory.childFile('generated_plugin_registrant.h'));
expectExists(project.linux.managedDirectory.childFile('generated_plugin_registrant.cc'));
}, overrides: <Type, Generator>{
......@@ -207,7 +207,7 @@ void main() {
testUsingContext('injects plugins for Windows', () async {
final FlutterProject project = await someProject();
project.windows.cmakeFile.createSync(recursive: true);
await project.ensureReadyForPlatformSpecificTooling();
await project.regeneratePlatformSpecificTooling();
expectExists(project.windows.managedDirectory.childFile('generated_plugin_registrant.h'));
expectExists(project.windows.managedDirectory.childFile('generated_plugin_registrant.cc'));
}, overrides: <Type, Generator>{
......@@ -221,14 +221,14 @@ void main() {
});
_testInMemory('creates Android library in module', () async {
final FlutterProject project = await aModuleProject();
await project.ensureReadyForPlatformSpecificTooling();
await project.regeneratePlatformSpecificTooling();
expectExists(project.android.hostAppGradleRoot.childFile('settings.gradle'));
expectExists(project.android.hostAppGradleRoot.childFile('local.properties'));
expectExists(androidPluginRegistrant(project.android.hostAppGradleRoot.childDirectory('Flutter')));
});
_testInMemory('creates iOS pod in module', () async {
final FlutterProject project = await aModuleProject();
await project.ensureReadyForPlatformSpecificTooling();
await project.regeneratePlatformSpecificTooling();
final Directory flutter = project.ios.hostAppRoot.childDirectory('Flutter');
expectExists(flutter.childFile('podhelper.rb'));
expectExists(flutter.childFile('flutter_export_environment.sh'));
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment