@@ -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