Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Detect Swiftpm support #1412

Merged
merged 12 commits into from
Oct 29, 2024
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
## 0.22.13

- Pass-through for `formatter/page_width` in `analysis_options.yaml`.
- Detect support for Swift Package Manager for ios/macos plugins.
- Upgraded `lints` to `^5.0.0`

## 0.22.12
Expand Down
1 change: 1 addition & 0 deletions lib/src/package_context.dart
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,7 @@ class PackageContext {
tagger.flutterPluginTags(tags, explanations);
tagger.nullSafetyTags(tags, explanations);
tagger.wasmReadyTag(tags, explanations);
tagger.swiftPackageManagerPluginTag(tags, explanations);
if (currentSdkVersion.major >= 3) {
tags.add(PanaTags.isDart3Compatible);
}
Expand Down
52 changes: 51 additions & 1 deletion lib/src/report/multi_platform.dart
Original file line number Diff line number Diff line change
Expand Up @@ -118,13 +118,19 @@ Future<ReportSection> multiPlatform(PackageContext context) async {
}

final wasmSubsection = await _createWasmSubsection(context);
final swiftPackageManagerSubsection =
await _createSwiftPackageManagerSubSection(context);

return makeSection(
id: ReportSectionId.platform,
title: 'Platform support',
maxPoints: 20,
basePath: context.packageDir,
subsections: [subsection, wasmSubsection],
subsections: [
subsection,
wasmSubsection,
if (swiftPackageManagerSubsection != null) swiftPackageManagerSubsection
],
maxIssues: 20);
}

Expand Down Expand Up @@ -178,3 +184,47 @@ Future<Subsection> _createWasmSubsection(PackageContext context) async {
);
}
}

/// Create a subsection for ios and macos plugins, to highlight supported
/// for swift package manager (or lack there of).
Future<Subsection?> _createSwiftPackageManagerSubSection(
PackageContext context) async {
final tr = await context.staticAnalysis;
final description = 'Swift Package Manager support';

if (tr.tags.contains(PanaTags.isSwiftPmPlugin)) {
return Subsection(
description,
[
RawParagraph(
'This iOS or macOS plugin supports the Swift Package Manager. '
'It will be rewarded additional points in a future version of the scoring model.'),
RawParagraph('See https://docs.flutter.dev/to/spm for details.'),
],
0,
0,
ReportStatus.passed,
);
}
final explanation = tr.explanations
.where((e) => e.tag == PanaTags.isSwiftPmPlugin)
.firstOrNull;
if (explanation != null) {
return Subsection(
description,
[
explanationToIssue(explanation),
RawParagraph(
'This package for iOS or macOS does not support the Swift Package Manager. '
'It will not receive full points in a future version of the scoring model.',
),
RawParagraph('See https://docs.flutter.dev/to/spm for details.'),
],
0,
0,
ReportStatus.failed,
);
}
// Don't complain if this is not an ios/macos plugin.
return null;
}
1 change: 1 addition & 0 deletions lib/src/tag/pana_tags.dart
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ abstract class PanaTags {
static const hasScreenshot = 'has:screenshot';
static const isPlugin = 'is:plugin';
static const isNullSafe = 'is:null-safe';
static const isSwiftPmPlugin = 'is:swiftpm-plugin';

/// Package version is compatible with Dart 3.
static const isDart3Compatible = 'is:dart3-compatible';
Expand Down
75 changes: 58 additions & 17 deletions lib/src/tag/tagger.dart
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ export '_specs.dart' show Runtime;
/// Calculates the tags for the package residing in a given directory.
class Tagger {
final String packageName;
final String packageDir;
final AnalysisSession _session;
final PubspecCache _pubspecCache;
final bool _isBinaryOnly;
Expand All @@ -115,14 +116,15 @@ class Tagger {
final PackageGraph _packageGraph;

Tagger._(
this.packageName,
this._session,
PubspecCache pubspecCache,
this._isBinaryOnly,
this._usesFlutter,
this._topLibraries,
this._publicLibraries,
) : _pubspecCache = pubspecCache,
this.packageName,
this._session,
PubspecCache pubspecCache,
this._isBinaryOnly,
this._usesFlutter,
this._topLibraries,
this._publicLibraries,
this.packageDir)
: _pubspecCache = pubspecCache,
_packageGraph = PackageGraph(pubspecCache);

/// Assumes that `dart pub get` has been run.
Expand Down Expand Up @@ -170,15 +172,8 @@ class Tagger {
final publicLibraries = nonSrcDartFiles
.map((s) => Uri.parse('package:${pubspec.name}/$s'))
.toList();
return Tagger._(
pubspec.name,
session,
pubspecCache,
isBinaryOnly,
pubspec.usesFlutter,
topLibraries,
publicLibraries,
);
return Tagger._(pubspec.name, session, pubspecCache, isBinaryOnly,
pubspec.usesFlutter, topLibraries, publicLibraries, packageDir);
}

void sdkTags(List<String> tags, List<Explanation> explanations) {
Expand Down Expand Up @@ -355,6 +350,52 @@ class Tagger {
}
}

void swiftPackageManagerPluginTag(
List<String> tags, List<Explanation> explanations) {
if (!_usesFlutter) return;
final pubspec = _pubspecCache.pubspecOfPackage(packageName);

bool pathExists(dynamic m, List<String> path) {
dynamic current = m;
for (final e in path) {
if (current is! Map) return false;
if (!current.containsKey(e)) return false;
current = current[e];
}
return true;
}

var isDarwinPlugin = false;
var swiftPmSupport = true;

for (final darwinOs in ['macos', 'ios']) {
if (pathExists(
pubspec.originalYaml, ['flutter', 'plugin', 'platforms', darwinOs])) {
isDarwinPlugin = true;
final osDir = pubspec.originalYaml['flutter']?['plugin']?['platforms']
?[darwinOs]?['sharedDarwinSource'] ==
true
? 'darwin'
: darwinOs;

final packageSwiftFile = path.join(osDir, packageName, 'Package.swift');
if (!File(path.join(packageDir, packageSwiftFile)).existsSync()) {
swiftPmSupport = false;
final osName = {'macos': 'macOS', 'ios': 'iOS'}[darwinOs];
explanations.add(Explanation(
'Package does not support the Swift Package Manager on $osName',
'''
It does not contain `$packageSwiftFile`.
''',
tag: PanaTags.isSwiftPmPlugin));
}
}
}
if (isDarwinPlugin && swiftPmSupport) {
tags.add(PanaTags.isSwiftPmPlugin);
}
}

/// Adds tags for the Dart runtimes that this package supports to [tags].
///
/// Adds [Explanation]s to [explanations] for runtimes not supported.
Expand Down
2 changes: 1 addition & 1 deletion test/goldens/end2end/audio_service-0.18.10.json
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@
"grantedPoints": 20,
"maxPoints": 20,
"status": "failed",
"summary": "### [*] 20/20 points: Supports 4 of 6 possible platforms (**iOS**, **Android**, **Web**, Windows, **macOS**, Linux)\n\n* ✓ Android\n\n* ✓ iOS\n\n* ✓ macOS\n\n* ✓ Web\n\n\nThese platforms are not supported:\n\n<details>\n<summary>\nPackage does not support platform `Windows`.\n</summary>\n\nBecause:\n* `package:audio_service/audio_service.dart` that declares support for platforms: `Android`, `iOS`, `macOS`, `Web`.\n</details>\n\n<details>\n<summary>\nPackage does not support platform `Linux`.\n</summary>\n\nBecause:\n* `package:audio_service/audio_service.dart` that declares support for platforms: `Android`, `iOS`, `macOS`, `Web`.\n</details>\n\n\nThese issues are present but do not affect the score, because they may not originate in your package:\n\n<details>\n<summary>\nPackage does not support platform `Web`.\n</summary>\n\nBecause:\n* `package:audio_service/audio_service.dart` that imports:\n* `package:flutter_cache_manager/flutter_cache_manager.dart` that imports:\n* `package:flutter_cache_manager/src/storage/cache_info_repositories/cache_info_repositories.dart` that imports:\n* `package:flutter_cache_manager/src/storage/cache_info_repositories/json_cache_info_repository.dart` that imports:\n* `package:path_provider/path_provider.dart` that declares support for platforms: `Android`, `iOS`, `Windows`, `Linux`, `macOS`.\n</details>\n\n### [x] 0/0 points: WASM compatibility\n\n<details>\n<summary>\nPackage not compatible with runtime wasm\n</summary>\n\nBecause:\n* `package:audio_service/audio_service.dart` that imports:\n* `package:flutter_cache_manager/flutter_cache_manager.dart` that imports:\n* `package:flutter_cache_manager/src/web/web_helper.dart` that imports:\n* `package:flutter_cache_manager/src/cache_store.dart` that imports:\n* `package:flutter_cache_manager/src/storage/file_system/file_system.dart` that imports:\n* `package:file/file.dart` that imports:\n* `package:file/src/interface.dart` that imports:\n* `package:file/src/io.dart` that imports:\n* `dart:io`\n</details>\n\nThis package is not compatible with runtime `wasm`, and will not be rewarded full points in a future version of the scoring model.\n\nSee https://dart.dev/web/wasm for details.\n"
"summary": "### [*] 20/20 points: Supports 4 of 6 possible platforms (**iOS**, **Android**, **Web**, Windows, **macOS**, Linux)\n\n* ✓ Android\n\n* ✓ iOS\n\n* ✓ macOS\n\n* ✓ Web\n\n\nThese platforms are not supported:\n\n<details>\n<summary>\nPackage does not support platform `Windows`.\n</summary>\n\nBecause:\n* `package:audio_service/audio_service.dart` that declares support for platforms: `Android`, `iOS`, `macOS`, `Web`.\n</details>\n\n<details>\n<summary>\nPackage does not support platform `Linux`.\n</summary>\n\nBecause:\n* `package:audio_service/audio_service.dart` that declares support for platforms: `Android`, `iOS`, `macOS`, `Web`.\n</details>\n\n\nThese issues are present but do not affect the score, because they may not originate in your package:\n\n<details>\n<summary>\nPackage does not support platform `Web`.\n</summary>\n\nBecause:\n* `package:audio_service/audio_service.dart` that imports:\n* `package:flutter_cache_manager/flutter_cache_manager.dart` that imports:\n* `package:flutter_cache_manager/src/storage/cache_info_repositories/cache_info_repositories.dart` that imports:\n* `package:flutter_cache_manager/src/storage/cache_info_repositories/json_cache_info_repository.dart` that imports:\n* `package:path_provider/path_provider.dart` that declares support for platforms: `Android`, `iOS`, `Windows`, `Linux`, `macOS`.\n</details>\n\n### [x] 0/0 points: WASM compatibility\n\n<details>\n<summary>\nPackage not compatible with runtime wasm\n</summary>\n\nBecause:\n* `package:audio_service/audio_service.dart` that imports:\n* `package:flutter_cache_manager/flutter_cache_manager.dart` that imports:\n* `package:flutter_cache_manager/src/web/web_helper.dart` that imports:\n* `package:flutter_cache_manager/src/cache_store.dart` that imports:\n* `package:flutter_cache_manager/src/storage/file_system/file_system.dart` that imports:\n* `package:file/file.dart` that imports:\n* `package:file/src/interface.dart` that imports:\n* `package:file/src/io.dart` that imports:\n* `dart:io`\n</details>\n\nThis package is not compatible with runtime `wasm`, and will not be rewarded full points in a future version of the scoring model.\n\nSee https://dart.dev/web/wasm for details.\n\n### [x] 0/0 points: Swift Package Manager support\n\n<details>\n<summary>\nPackage does not support the Swift Package Manager on macOS\n</summary>\n\nIt does not contain `macos/audio_service/Package.swift`.\n\n</details>\n\nThis package for iOS or macOS does not support the Swift Package Manager. It will not receive full points in a future version of the scoring model.\n\nSee https://docs.flutter.dev/to/spm for details.\n"
},
{
"id": "analysis",
Expand Down
15 changes: 15 additions & 0 deletions test/goldens/end2end/audio_service-0.18.10.json_report.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,21 @@ This package is not compatible with runtime `wasm`, and will not be rewarded ful

See https://dart.dev/web/wasm for details.

### [x] 0/0 points: Swift Package Manager support

<details>
<summary>
Package does not support the Swift Package Manager on macOS
</summary>

It does not contain `macos/audio_service/Package.swift`.

</details>

This package for iOS or macOS does not support the Swift Package Manager. It will not receive full points in a future version of the scoring model.

See https://docs.flutter.dev/to/spm for details.


## 50/50 Pass static analysis

Expand Down
4 changes: 2 additions & 2 deletions test/goldens/end2end/url_launcher-6.1.12.json
Original file line number Diff line number Diff line change
Expand Up @@ -133,8 +133,8 @@
"title": "Platform support",
"grantedPoints": 20,
"maxPoints": 20,
"status": "passed",
"summary": "### [*] 20/20 points: Supports 6 of 6 possible platforms (**iOS**, **Android**, **Web**, **Windows**, **macOS**, **Linux**)\n\n* ✓ Android\n\n* ✓ iOS\n\n* ✓ Windows\n\n* ✓ Linux\n\n* ✓ macOS\n\n* ✓ Web\n\n### [*] 0/0 points: WASM compatibility\n\nThis package is compatible with runtime `wasm`, and will be rewarded additional points in a future version of the scoring model.\n\nSee https://dart.dev/web/wasm for details.\n"
"status": "failed",
"summary": "### [*] 20/20 points: Supports 6 of 6 possible platforms (**iOS**, **Android**, **Web**, **Windows**, **macOS**, **Linux**)\n\n* ✓ Android\n\n* ✓ iOS\n\n* ✓ Windows\n\n* ✓ Linux\n\n* ✓ macOS\n\n* ✓ Web\n\n### [*] 0/0 points: WASM compatibility\n\nThis package is compatible with runtime `wasm`, and will be rewarded additional points in a future version of the scoring model.\n\nSee https://dart.dev/web/wasm for details.\n\n### [x] 0/0 points: Swift Package Manager support\n\n<details>\n<summary>\nPackage does not support the Swift Package Manager on macOS\n</summary>\n\nIt does not contain `macos/url_launcher/Package.swift`.\n\n</details>\n\nThis package for iOS or macOS does not support the Swift Package Manager. It will not receive full points in a future version of the scoring model.\n\nSee https://docs.flutter.dev/to/spm for details.\n"
},
{
"id": "analysis",
Expand Down
15 changes: 15 additions & 0 deletions test/goldens/end2end/url_launcher-6.1.12.json_report.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,21 @@ This package is compatible with runtime `wasm`, and will be rewarded additional

See https://dart.dev/web/wasm for details.

### [x] 0/0 points: Swift Package Manager support

<details>
<summary>
Package does not support the Swift Package Manager on macOS
</summary>

It does not contain `macos/url_launcher/Package.swift`.

</details>

This package for iOS or macOS does not support the Swift Package Manager. It will not receive full points in a future version of the scoring model.

See https://docs.flutter.dev/to/spm for details.


## 50/50 Pass static analysis

Expand Down
75 changes: 75 additions & 0 deletions test/tag/tag_end2end_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -578,6 +578,81 @@ name: my_package
});
});

group('swift package manager tag', () {
test('Works with ios/<package_name>/Package.swift', () async {
final descriptor = d.dir('cache', [
packageWithPathDeps('my_package', pubspecExtras: {
'flutter': {
'plugin': {
'platforms': {'ios': <String, dynamic>{}}
}
}
}, extraFiles: [
d.dir('ios', [
d.dir('my_package', [d.file('Package.swift')])
]),
])
]);
await descriptor.create();
final tagger = Tagger('${descriptor.io.path}/my_package');
_expectTagging(tagger.swiftPackageManagerPluginTag,
tags: contains('is:swiftpm-plugin'));
});

test('Works with darwin/package_name/Package.swift', () async {
final descriptor = d.dir('cache', [
packageWithPathDeps('my_package', pubspecExtras: {
'flutter': {
'plugin': {
'platforms': {
'ios': <String, dynamic>{'sharedDarwinSource': true},
'macos': <String, dynamic>{'sharedDarwinSource': true}
}
}
}
}, extraFiles: [
d.dir('darwin', [
d.dir('my_package', [d.file('Package.swift')])
]),
])
]);
await descriptor.create();
final tagger = Tagger('${descriptor.io.path}/my_package');
_expectTagging(tagger.swiftPackageManagerPluginTag,
tags: contains('is:swiftpm-plugin'));
});

test('Fails with the wrong os/package_name/Package.swift', () async {
final descriptor = d.dir('cache', [
packageWithPathDeps('my_package', pubspecExtras: {
'flutter': {
'plugin': {
'platforms': {'macos': <String, dynamic>{}}
}
}
}, extraFiles: [
d.dir('ios', [
d.dir('my_package', [d.file('Package.swift')])
]),
])
]);
await descriptor.create();
final tagger = Tagger('${descriptor.io.path}/my_package');
_expectTagging(tagger.swiftPackageManagerPluginTag,
tags: isEmpty,
explanations: contains(isA<Explanation>()
.having((e) => e.tag, 'tag', 'is:swiftpm-plugin')));
});

test('Does not complain about non-plugins', () async {
final descriptor = d.dir('cache', [packageWithPathDeps('my_package')]);
await descriptor.create();
final tagger = Tagger('${descriptor.io.path}/my_package');
_expectTagging(tagger.swiftPackageManagerPluginTag,
tags: isEmpty, explanations: isEmpty);
});
});

group('wasm tag', () {
test('Excluded with dart:js', () async {
final descriptor = d.dir('cache', [
Expand Down