Skip to content

Commit a87b84d

Browse files
authored
Allow invoking getExecutableForCommand everywhere in workspace (#4257)
1 parent a284c9b commit a87b84d

File tree

6 files changed

+397
-94
lines changed

6 files changed

+397
-94
lines changed

lib/src/entrypoint.dart

Lines changed: 159 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -740,22 +740,29 @@ To update `$lockFilePath` run `$topLevelProgram pub get`$suffix without
740740
///
741741
/// If [onlyOutputWhenTerminal] is `true` (the default) there will be no
742742
/// output if no terminal is attached.
743-
static Future<PackageConfig> ensureUpToDate(
743+
///
744+
/// When succesfull returns the found/created `PackageConfig` and the
745+
/// directory containing it.
746+
static Future<({PackageConfig packageConfig, String rootDir})> ensureUpToDate(
744747
String dir, {
745748
required SystemCache cache,
746749
bool summaryOnly = true,
747750
bool onlyOutputWhenTerminal = true,
748751
}) async {
749-
final lockFilePath = p.normalize(p.join(dir, 'pubspec.lock'));
750-
final packageConfigPath =
751-
p.normalize(p.join(dir, '.dart_tool', 'package_config.json'));
752+
late final wasRelative = p.isRelative(dir);
753+
String relativeIfNeeded(String path) =>
754+
wasRelative ? p.relative(path) : path;
752755

753756
/// Whether the lockfile is out of date with respect to the dependencies'
754757
/// pubspecs.
755758
///
756759
/// If any mutable pubspec contains dependencies that are not in the lockfile
757760
/// or that don't match what's in there, this will return `false`.
758-
bool isLockFileUpToDate(LockFile lockFile, Package root) {
761+
bool isLockFileUpToDate(
762+
LockFile lockFile,
763+
Package root, {
764+
required String lockFilePath,
765+
}) {
759766
/// Returns whether the locked version of [dep] matches the dependency.
760767
bool isDependencyUpToDate(PackageRange dep) {
761768
if (dep.name == root.name) return true;
@@ -827,16 +834,20 @@ To update `$lockFilePath` run `$topLevelProgram pub get`$suffix without
827834
bool isPackageConfigUpToDate(
828835
PackageConfig packageConfig,
829836
LockFile lockFile,
830-
Package root,
831-
) {
837+
Package root, {
838+
required String packageConfigPath,
839+
required String lockFilePath,
840+
}) {
832841
/// Determines if [lockFile] agrees with the given [packagePathsMapping].
833842
///
834843
/// The [packagePathsMapping] is a mapping from package names to paths where
835844
/// the packages are located. (The library is located under
836845
/// `lib/` relative to the path given).
837846
bool isPackagePathsMappingUpToDateWithLockfile(
838-
Map<String, String> packagePathsMapping,
839-
) {
847+
Map<String, String> packagePathsMapping, {
848+
required String lockFilePath,
849+
required String packageConfigPath,
850+
}) {
840851
// Check that [packagePathsMapping] does not contain more packages than what
841852
// is required. This could lead to import statements working, when they are
842853
// not supposed to work.
@@ -899,7 +910,11 @@ To update `$lockFilePath` run `$topLevelProgram pub get`$suffix without
899910
packagePathsMapping[pkg.name] =
900911
root.path('.dart_tool', p.fromUri(pkg.rootUri));
901912
}
902-
if (!isPackagePathsMappingUpToDateWithLockfile(packagePathsMapping)) {
913+
if (!isPackagePathsMappingUpToDateWithLockfile(
914+
packagePathsMapping,
915+
packageConfigPath: packageConfigPath,
916+
lockFilePath: lockFilePath,
917+
)) {
903918
log.fine('The $lockFilePath file has changed since the '
904919
'$packageConfigPath file '
905920
'was generated, please run "$topLevelProgram pub get" again.');
@@ -950,8 +965,9 @@ To update `$lockFilePath` run `$topLevelProgram pub get`$suffix without
950965
}
951966

952967
/// The [PackageConfig] object representing `.dart_tool/package_config.json`
953-
/// if it and `pubspec.lock` exist and are up to date with respect to
954-
/// pubspec.yaml and its dependencies. Or `null` if it is outdate
968+
/// along with the dir where it resides, if it and `pubspec.lock` exist and
969+
/// are up to date with respect to pubspec.yaml and its dependencies. Or
970+
/// `null` if it is outdated.
955971
///
956972
/// Always returns `null` if `.dart_tool/package_config.json` was generated
957973
/// with a different PUB_CACHE location, a different $FLUTTER_ROOT or a
@@ -969,7 +985,8 @@ To update `$lockFilePath` run `$topLevelProgram pub get`$suffix without
969985
/// where version control or other processes mess up the timestamp order.
970986
///
971987
/// If the resolution is still valid, the timestamps are updated and this
972-
/// returns `true`. Otherwise this returns `false`.
988+
/// returns the package configuration and the root dir. Otherwise this
989+
/// returns `null`.
973990
///
974991
/// This check is on the fast-path of `dart run` and should do as little
975992
/// work as possible. Specifically we avoid parsing any yaml when the
@@ -984,11 +1001,100 @@ To update `$lockFilePath` run `$topLevelProgram pub get`$suffix without
9841001
/// `touch pubspec.lock; touch .dart_tool/package_config.json`) - that is
9851002
/// hard to avoid, but also unlikely to happen by accident because
9861003
/// `.dart_tool/package_config.json` is not checked into version control.
987-
PackageConfig? isResolutionUpToDate() {
1004+
(PackageConfig, String)? isResolutionUpToDate() {
1005+
FileStat? packageConfigStat;
1006+
late final String packageConfigPath;
1007+
late final String rootDir;
1008+
for (final parent in parentDirs(dir)) {
1009+
final potentialPackageConfigPath =
1010+
p.normalize(p.join(parent, '.dart_tool', 'package_config.json'));
1011+
packageConfigStat = tryStatFile(potentialPackageConfigPath);
1012+
1013+
if (packageConfigStat != null) {
1014+
packageConfigPath = potentialPackageConfigPath;
1015+
rootDir = parent;
1016+
break;
1017+
}
1018+
final potentialPubspacPath = p.join(parent, 'pubspec.yaml');
1019+
if (tryStatFile(potentialPubspacPath) == null) {
1020+
// No package at [parent] continue to next dir.
1021+
continue;
1022+
}
1023+
1024+
final potentialWorkspaceRefPath = p.normalize(
1025+
p.join(parent, '.dart_tool', 'pub', 'workspace_ref.json'),
1026+
);
1027+
1028+
final workspaceRefText = tryReadTextFile(potentialWorkspaceRefPath);
1029+
if (workspaceRefText == null) {
1030+
log.fine(
1031+
'`$potentialPubspacPath` exists without corresponding `$potentialPubspacPath` or `$potentialWorkspaceRefPath`.',
1032+
);
1033+
return null;
1034+
} else {
1035+
try {
1036+
if (jsonDecode(workspaceRefText)
1037+
case {'workspaceRoot': final String path}) {
1038+
final potentialPackageConfigPath2 = relativeIfNeeded(
1039+
p.normalize(
1040+
p.absolute(
1041+
p.join(
1042+
p.dirname(potentialWorkspaceRefPath),
1043+
workspaceRefText,
1044+
path,
1045+
'.dart_tool',
1046+
'package_config.json',
1047+
),
1048+
),
1049+
),
1050+
);
1051+
packageConfigStat = tryStatFile(potentialPackageConfigPath2);
1052+
if (packageConfigStat == null) {
1053+
log.fine(
1054+
'`$potentialWorkspaceRefPath` points to non-existing `$potentialPackageConfigPath2`',
1055+
);
1056+
return null;
1057+
} else {
1058+
packageConfigPath = potentialPackageConfigPath2;
1059+
rootDir = relativeIfNeeded(
1060+
p.normalize(
1061+
p.absolute(
1062+
p.join(
1063+
p.dirname(potentialWorkspaceRefPath),
1064+
workspaceRefText,
1065+
path,
1066+
),
1067+
),
1068+
),
1069+
);
1070+
1071+
break;
1072+
}
1073+
} else {
1074+
log.fine(
1075+
'`$potentialWorkspaceRefPath` is missing "workspaceRoot" property',
1076+
);
1077+
return null;
1078+
}
1079+
} on FormatException catch (e) {
1080+
log.fine(
1081+
'`$potentialWorkspaceRefPath` not valid json: $e.',
1082+
);
1083+
return null;
1084+
}
1085+
}
1086+
}
1087+
if (packageConfigStat == null) {
1088+
log.fine(
1089+
'Found no .dart_tool/package_config.json - no existing resolution.',
1090+
);
1091+
return null;
1092+
}
1093+
final lockFilePath = p.normalize(p.join(rootDir, 'pubspec.lock'));
9881094
late final packageConfig = _loadPackageConfig(packageConfigPath);
9891095
if (p.isWithin(cache.rootDir, packageConfigPath)) {
9901096
// We always consider a global package (inside the cache) up-to-date.
991-
return packageConfig;
1097+
return (packageConfig, rootDir);
9921098
}
9931099

9941100
/// Whether or not the `.dart_tool/package_config.json` file is was
@@ -1004,11 +1110,6 @@ To update `$lockFilePath` run `$topLevelProgram pub get`$suffix without
10041110
return true;
10051111
}
10061112

1007-
final packageConfigStat = tryStatFile(packageConfigPath);
1008-
if (packageConfigStat == null) {
1009-
log.fine('No $packageConfigPath file found".\n');
1010-
return null;
1011-
}
10121113
final flutter = FlutterSdk();
10131114
// If Flutter has moved since last invocation, we want to have new
10141115
// sdk-packages, and therefore do a new resolution.
@@ -1095,7 +1196,7 @@ To update `$lockFilePath` run `$topLevelProgram pub get`$suffix without
10951196
);
10961197

10971198
if (!lockfileNewerThanPubspecs) {
1098-
if (isLockFileUpToDate(lockFile, root)) {
1199+
if (isLockFileUpToDate(lockFile, root, lockFilePath: lockFilePath)) {
10991200
touch(lockFilePath);
11001201
touchedLockFile = true;
11011202
} else {
@@ -1106,40 +1207,52 @@ To update `$lockFilePath` run `$topLevelProgram pub get`$suffix without
11061207
if (touchedLockFile ||
11071208
lockFileModified.isAfter(packageConfigStat.modified)) {
11081209
log.fine('`$lockFilePath` is newer than `$packageConfigPath`');
1109-
if (isPackageConfigUpToDate(packageConfig, lockFile, root)) {
1210+
if (isPackageConfigUpToDate(
1211+
packageConfig,
1212+
lockFile,
1213+
root,
1214+
packageConfigPath: packageConfigPath,
1215+
lockFilePath: lockFilePath,
1216+
)) {
11101217
touch(packageConfigPath);
11111218
} else {
11121219
return null;
11131220
}
11141221
}
1115-
return packageConfig;
1222+
return (packageConfig, rootDir);
11161223
}
11171224

1118-
switch (isResolutionUpToDate()) {
1119-
case null:
1120-
final entrypoint = Entrypoint(
1121-
dir, cache,
1122-
// [ensureUpToDate] is also used for entries in 'global_packages/'
1123-
checkInCache: false,
1225+
if (isResolutionUpToDate()
1226+
case (final PackageConfig packageConfig, final String rootDir)) {
1227+
log.fine('Package Config up to date.');
1228+
return (packageConfig: packageConfig, rootDir: rootDir);
1229+
}
1230+
final entrypoint = Entrypoint(
1231+
dir, cache,
1232+
// [ensureUpToDate] is also used for entries in 'global_packages/'
1233+
checkInCache: false,
1234+
);
1235+
if (onlyOutputWhenTerminal) {
1236+
await log.errorsOnlyUnlessTerminal(() async {
1237+
await entrypoint.acquireDependencies(
1238+
SolveType.get,
1239+
summaryOnly: summaryOnly,
11241240
);
1125-
if (onlyOutputWhenTerminal) {
1126-
await log.errorsOnlyUnlessTerminal(() async {
1127-
await entrypoint.acquireDependencies(
1128-
SolveType.get,
1129-
summaryOnly: summaryOnly,
1130-
);
1131-
});
1132-
} else {
1133-
await entrypoint.acquireDependencies(
1134-
SolveType.get,
1135-
summaryOnly: summaryOnly,
1136-
);
1137-
}
1138-
return entrypoint.packageConfig;
1139-
case final PackageConfig packageConfig:
1140-
log.fine('Package Config up to date.');
1141-
return packageConfig;
1241+
});
1242+
} else {
1243+
await entrypoint.acquireDependencies(
1244+
SolveType.get,
1245+
summaryOnly: summaryOnly,
1246+
);
11421247
}
1248+
return (
1249+
packageConfig: entrypoint.packageConfig,
1250+
rootDir: relativeIfNeeded(
1251+
p.normalize(
1252+
p.absolute(entrypoint.workspaceRoot.dir),
1253+
),
1254+
)
1255+
);
11431256
}
11441257

11451258
/// We require an SDK constraint lower-bound as of Dart 2.12.0

0 commit comments

Comments
 (0)