mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00

Recently, the Flutter tool was updated to remove dev dependencies for release builds and add dev dependencies for debug/profile builds. However when building from Xcode directly, dev dependencies are not enabled/disabled. As a result, it was possible for debug builds to not have dev dependencies (or vice versa). Example: 1. `flutter build ios --release` - Release build using Flutter tool. Disables dev dependencies 2. `open ios/Runner.xcworkspace` - Open the iOS project in Xcode 3. In Xcode, **Product** > **Build** - Do a debug build Previously, step 3 would result in debug artifacts that are missing dev dependencies. This PR now makes this an error:  Part of https://github.com/flutter/flutter/issues/163874 ## Implementation The Flutter tool now writes a `FLUTTER_DEV_DEPENDENCIES_ENABLED` in the generated config file. This tracks whether the currently generated project has dev dependencies. In the Xcode build: 1. The Xcode backend script passes the `FLUTTER_DEV_DEPENDENCIES_ENABLED` config to `flutter assemble` using the `DevDependenciesEnabled` define 6. The new `CheckDevDependencies` target verifies dev dependencies: 1. It checks if the dev dependencies status is correct for the current build mode 2. It checks whether the app has dev dependencies by reading the `.flutter-plugins-dependencies` file. If the app has no dev dependencies, the error is suppressed. ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [x] I signed the [CLA]. - [x] I listed at least one issue that this PR fixes in the description above. - [x] I updated/added relevant documentation (doc comments with `///`). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [x] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [x] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. <!-- Links --> [Contributor Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview [Tree Hygiene]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md [test-exempt]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests [Flutter Style Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md [Features we expect every widget to implement]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md [Data Driven Fixes]: https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
250 lines
8.3 KiB
Dart
250 lines
8.3 KiB
Dart
// Copyright 2014 The Flutter Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
import 'dart:convert';
|
|
import 'package:file/file.dart';
|
|
import 'package:file_testing/file_testing.dart';
|
|
import 'package:flutter_tools/src/flutter_plugins.dart';
|
|
|
|
import '../src/common.dart';
|
|
import 'test_utils.dart';
|
|
|
|
void main() {
|
|
test(
|
|
'.flutter-plugins-dependencies correctly denotes project dev dependencies on all default platforms',
|
|
() async {
|
|
final Directory tempDir = createResolvedTempDirectorySync(
|
|
'flutter_plugins_dependencies_test.',
|
|
);
|
|
final Directory tempProjectDir = tempDir.childDirectory('project')..createSync();
|
|
final Directory tempPluginADir = tempDir.childDirectory('plugin_a')..createSync();
|
|
final Directory tempPluginBDir = tempDir.childDirectory('plugin_b')..createSync();
|
|
|
|
addTearDown(() {
|
|
tryToDelete(tempDir);
|
|
});
|
|
|
|
// Create Flutter project.
|
|
await processManager.run(<String>[
|
|
flutterBin,
|
|
'create',
|
|
tempProjectDir.path,
|
|
'--project-name=testapp',
|
|
], workingDirectory: tempProjectDir.path);
|
|
|
|
final File pubspecFile = tempProjectDir.childFile('pubspec.yaml');
|
|
expect(pubspecFile.existsSync(), true);
|
|
|
|
// Create Flutter plugins to add as dependencies to Flutter project.
|
|
final String pluginAPath = '${tempPluginADir.path}/plugin_a_real_dependency';
|
|
final String pluginBPath = '${tempPluginBDir.path}/plugin_b_dev_dependency';
|
|
|
|
await processManager.run(<String>[
|
|
flutterBin,
|
|
'create',
|
|
pluginAPath,
|
|
'--template=plugin',
|
|
'--project-name=plugin_a_real_dependency',
|
|
'--platforms=ios',
|
|
], workingDirectory: tempPluginADir.path);
|
|
|
|
await processManager.run(<String>[
|
|
flutterBin,
|
|
'create',
|
|
pluginBPath,
|
|
'--template=plugin',
|
|
'--project-name=plugin_b_dev_dependency',
|
|
'--platforms=ios',
|
|
], workingDirectory: tempPluginBDir.path);
|
|
|
|
// Add dependency on two plugins: one dependency, one dev dependency.
|
|
await processManager.run(<String>[
|
|
flutterBin,
|
|
'pub',
|
|
'add',
|
|
'plugin_a_real_dependency',
|
|
'--path',
|
|
pluginAPath,
|
|
], workingDirectory: tempProjectDir.path);
|
|
|
|
await processManager.run(<String>[
|
|
flutterBin,
|
|
'pub',
|
|
'add',
|
|
'dev:plugin_b_dev_dependency',
|
|
'--path',
|
|
pluginBPath,
|
|
], workingDirectory: tempProjectDir.path);
|
|
|
|
// Run `flutter pub get` to generate .flutter-plugins-dependencies.
|
|
await processManager.run(<String>[
|
|
flutterBin,
|
|
'--no-implicit-pubspec-resolution',
|
|
'pub',
|
|
'get',
|
|
], workingDirectory: tempProjectDir.path);
|
|
|
|
final File flutterPluginsDependenciesFile = tempProjectDir.childFile(
|
|
'.flutter-plugins-dependencies',
|
|
);
|
|
expect(flutterPluginsDependenciesFile, exists);
|
|
expect(flutterPluginsListHasDevDependencies(flutterPluginsDependenciesFile), isTrue);
|
|
|
|
// Check that .flutter-plugin-dependencies denotes the dependency and
|
|
// dev dependency as expected.
|
|
final String pluginsString = flutterPluginsDependenciesFile.readAsStringSync();
|
|
final Map<String, dynamic> jsonContent = json.decode(pluginsString) as Map<String, dynamic>;
|
|
final Map<String, dynamic> plugins = jsonContent['plugins'] as Map<String, dynamic>;
|
|
|
|
// Loop through all platforms supported by default to verify that the
|
|
// dependency and dev dependency are handled appropriately.
|
|
final List<String> platformsToVerify = <String>[
|
|
'ios',
|
|
'android',
|
|
'windows',
|
|
'linux',
|
|
'macos',
|
|
'web',
|
|
];
|
|
|
|
for (final String platform in platformsToVerify) {
|
|
final List<dynamic> pluginsForPlatform = plugins[platform] as List<dynamic>;
|
|
|
|
for (final dynamic plugin in pluginsForPlatform) {
|
|
final Map<String, dynamic> pluginProperties = plugin as Map<String, dynamic>;
|
|
final String pluginName = pluginProperties['name'] as String;
|
|
final bool pluginIsDevDependency = pluginProperties['dev_dependency'] as bool;
|
|
|
|
// Check camera dependencies are not marked as dev dependencies.
|
|
if (pluginName.startsWith('plugin_a_real_dependency')) {
|
|
expect(pluginIsDevDependency, isFalse);
|
|
}
|
|
|
|
// Check video_player dependencies are marked as dev dependencies.
|
|
if (pluginName.startsWith('plugin_b_dev_dependency')) {
|
|
expect(pluginIsDevDependency, isTrue);
|
|
}
|
|
}
|
|
}
|
|
},
|
|
);
|
|
|
|
test('flutterPluginsListHasDevDependencies returns false if no dev dependencies', () async {
|
|
final Directory tempDir = createResolvedTempDirectorySync(
|
|
'flutter_plugins_list_has_dev_dependencies_test.',
|
|
);
|
|
final Directory tempProjectDir = tempDir.childDirectory('project')..createSync();
|
|
final Directory tempPluginADir = tempDir.childDirectory('plugin_a')..createSync();
|
|
|
|
addTearDown(() {
|
|
tryToDelete(tempDir);
|
|
});
|
|
|
|
// Create Flutter project.
|
|
await processManager.run(<String>[
|
|
flutterBin,
|
|
'create',
|
|
tempProjectDir.path,
|
|
'--project-name=testapp',
|
|
], workingDirectory: tempProjectDir.path);
|
|
|
|
final File pubspecFile = tempProjectDir.childFile('pubspec.yaml');
|
|
expect(pubspecFile.existsSync(), true);
|
|
|
|
// Create a Flutter plugin to add as a dependency to the Flutter project.
|
|
final String pluginAPath = '${tempPluginADir.path}/plugin_a_real_dependency';
|
|
|
|
await processManager.run(<String>[
|
|
flutterBin,
|
|
'create',
|
|
pluginAPath,
|
|
'--template=plugin',
|
|
'--project-name=plugin_a_real_dependency',
|
|
'--platforms=ios',
|
|
], workingDirectory: tempPluginADir.path);
|
|
|
|
// Add dependency on the plugin.
|
|
await processManager.run(<String>[
|
|
flutterBin,
|
|
'pub',
|
|
'add',
|
|
'plugin_a_real_dependency',
|
|
'--path',
|
|
pluginAPath,
|
|
], workingDirectory: tempProjectDir.path);
|
|
|
|
// Run `flutter pub get` to generate .flutter-plugins-dependencies.
|
|
await processManager.run(<String>[
|
|
flutterBin,
|
|
'--no-implicit-pubspec-resolution',
|
|
'pub',
|
|
'get',
|
|
], workingDirectory: tempProjectDir.path);
|
|
|
|
final File flutterPluginsDependenciesFile = tempProjectDir.childFile(
|
|
'.flutter-plugins-dependencies',
|
|
);
|
|
expect(flutterPluginsDependenciesFile, exists);
|
|
expect(flutterPluginsListHasDevDependencies(flutterPluginsDependenciesFile), isFalse);
|
|
});
|
|
|
|
test('flutterPluginsListHasDevDependencies ignores Dart package dev dependency', () async {
|
|
final Directory tempDir = createResolvedTempDirectorySync(
|
|
'flutter_plugins_list_ignores_dart_dev_dependency_test.',
|
|
);
|
|
final Directory tempProjectDir = tempDir.childDirectory('project')..createSync();
|
|
final Directory tempPackageADir = tempDir.childDirectory('package_a')..createSync();
|
|
|
|
addTearDown(() {
|
|
tryToDelete(tempDir);
|
|
});
|
|
|
|
// Create Flutter project.
|
|
await processManager.run(<String>[
|
|
flutterBin,
|
|
'create',
|
|
tempProjectDir.path,
|
|
'--project-name=testapp',
|
|
], workingDirectory: tempProjectDir.path);
|
|
|
|
final File pubspecFile = tempProjectDir.childFile('pubspec.yaml');
|
|
expect(pubspecFile.existsSync(), true);
|
|
|
|
// Create a pure Dart Flutter plugin to add as a dependency to the Flutter project.
|
|
final String packageAPath = '${tempPackageADir.path}/package_a';
|
|
|
|
await processManager.run(<String>[
|
|
flutterBin,
|
|
'create',
|
|
packageAPath,
|
|
'--template=plugin',
|
|
'--project-name=package_a',
|
|
], workingDirectory: tempPackageADir.path);
|
|
|
|
// Add a dev dependency on the plugin.
|
|
await processManager.run(<String>[
|
|
flutterBin,
|
|
'pub',
|
|
'add',
|
|
'dev:package_a',
|
|
'--path',
|
|
packageAPath,
|
|
], workingDirectory: tempProjectDir.path);
|
|
|
|
// Run `flutter pub get` to generate .flutter-plugins-dependencies.
|
|
await processManager.run(<String>[
|
|
flutterBin,
|
|
'--no-implicit-pubspec-resolution',
|
|
'pub',
|
|
'get',
|
|
], workingDirectory: tempProjectDir.path);
|
|
|
|
final File flutterPluginsDependenciesFile = tempProjectDir.childFile(
|
|
'.flutter-plugins-dependencies',
|
|
);
|
|
expect(flutterPluginsDependenciesFile, exists);
|
|
expect(flutterPluginsListHasDevDependencies(flutterPluginsDependenciesFile), isFalse);
|
|
});
|
|
}
|