| // Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file | |
| // for details. 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:io'; | |
| import 'package:package_config/package_config.dart'; | |
| import 'find_sdk_root.dart'; | |
| import 'suite.dart'; | |
| /// Writes a package config under [root] and returns the [Uri] pointing to it. | |
| Future<Uri> writePackageConfig( | |
| Module module, | |
| Set<Module> transitiveDependencies, | |
| Uri root, { | |
| bool useRealPaths = false, | |
| }) async { | |
| const packageConfigJsonPath = ".dart_tool/package_config.json"; | |
| var sdkRoot = await findRoot(); | |
| Uri packageConfigUri = sdkRoot.resolve(packageConfigJsonPath); | |
| var packageConfig = await loadPackageConfigUri(packageConfigUri); | |
| // We create a package_config.json file to support the CFE in (a) determine | |
| // the default nullability if the current module is a package, and (b) | |
| // resolve `package:` imports. Technically the latter shouldn't be necessary, | |
| // but the CFE requires that if a `package:` URI of a dependency is used in an | |
| // import, then we need that package entry in the associated file. In fact, | |
| // after it checks that the definition exists, the CFE will not actually use | |
| // the resolved URI if a library for the import URI is already found in one of | |
| // the provide .dill files of the dependencies. For that reason, and to ensure | |
| // that a step only has access to the files provided in a module, we generate | |
| // a config file with invalid folders for other packages. | |
| // TODO(sigmund): follow up with the CFE to see if we can remove the need | |
| // for these dummy entries. | |
| var packagesJson = []; | |
| if (module.isPackage) { | |
| packagesJson.add( | |
| _packageConfigEntry(module.name, Uri.parse('../${module.packageBase}')), | |
| ); | |
| } | |
| int unusedNum = 0; | |
| for (Module dependency in transitiveDependencies) { | |
| if (dependency.isPackage) { | |
| // rootUri should be ignored for dependent modules, so we pass in a | |
| // bogus value. | |
| var rootUri = useRealPaths | |
| ? dependency.rootUri | |
| : Uri.parse('unused${unusedNum++}'); | |
| var dependentPackage = packageConfig[dependency.name]; | |
| var packageJson = dependentPackage == null | |
| ? _packageConfigEntry( | |
| dependency.name, | |
| rootUri, | |
| packageRoot: dependency.packageBase, | |
| ) | |
| : _packageConfigEntry( | |
| dependentPackage.name, | |
| rootUri, | |
| packageRoot: dependency.packageBase, | |
| version: dependentPackage.languageVersion, | |
| ); | |
| packagesJson.add(packageJson); | |
| } | |
| } | |
| var packageConfigFile = File.fromUri(root.resolve(packageConfigJsonPath)); | |
| await packageConfigFile.create(recursive: true); | |
| await packageConfigFile.writeAsString( | |
| '{' | |
| ' "configVersion": ${packageConfig.version},' | |
| ' "packages": [ ${packagesJson.join(',')} ]' | |
| '}', | |
| ); | |
| return packageConfigFile.uri; | |
| } | |
| String _packageConfigEntry( | |
| String name, | |
| Uri root, { | |
| Uri? packageRoot, | |
| LanguageVersion? version, | |
| }) { | |
| var fields = [ | |
| '"name": "$name"', | |
| '"rootUri": "$root"', | |
| if (packageRoot != null) '"packageUri": "$packageRoot"', | |
| if (version != null) '"languageVersion": "$version"', | |
| ]; | |
| return '{${fields.join(',')}}'; | |
| } |