| // Copyright (c) 2025, 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:convert'; |
| import 'dart:io'; |
| import 'dart:typed_data'; |
| |
| import 'package:kernel/ast.dart'; |
| import 'package:kernel/binary/ast_from_binary.dart'; |
| |
| void main(List<String> args) { |
| if (args.length != 2) { |
| throw "Need 2 arguments: The input dill and the output dir."; |
| } |
| |
| File dill = new File(args[0]); |
| if (!dill.existsSync()) { |
| throw "${args[0]} is not an existing file."; |
| } |
| |
| Directory dir = new Directory(args[1]); |
| if (dir.existsSync()) { |
| print("Warning: ${args[1]} is an existing dir."); |
| } else if (!dir.parent.existsSync()) { |
| print( |
| "Warning: ${dir.parent} is not an existing dir, but will be created."); |
| dir.createSync(recursive: true); |
| } else { |
| dir.createSync(); |
| } |
| |
| Component component = new Component(); |
| try { |
| Uint8List bytes = dill.readAsBytesSync(); |
| new BinaryBuilder(bytes).readComponent(component); |
| } catch (e, st) { |
| print("Error when loading $dill:"); |
| print(e); |
| print("Error thrown via:"); |
| print(st); |
| exit(1); |
| } |
| |
| Map<String, Uri> packagesToRoot = {}; |
| Map<Uri, String> rootsToPackage = {}; |
| Set<MapEntry<Uri, Source>> nonPackageUris = {}; |
| int writes = 0; |
| for (MapEntry<Uri, Source> sourceEntry in component.uriToSource.entries) { |
| Uri uri = sourceEntry.value.importUri ?? |
| sourceEntry.value.fileUri ?? |
| sourceEntry.key; |
| Uri fileUri = sourceEntry.key; |
| if (uri.isScheme("dart")) continue; |
| if (!uri.isScheme("package")) { |
| nonPackageUris.add(sourceEntry); |
| } else { |
| // Package uri. Calculate root. |
| String package = uri.pathSegments[0]; |
| String relativeFromPackageUri = uri.pathSegments.skip(1).join("/"); |
| String relativeFromFileUri = fileUri.pathSegments |
| .skip(fileUri.pathSegments.length - uri.pathSegments.length + 1) |
| .join("/"); |
| if (relativeFromPackageUri != relativeFromFileUri) { |
| throw "Expected $relativeFromPackageUri and $relativeFromFileUri to be " |
| "the same (from $uri and $fileUri)"; |
| } |
| List<String> rootPathSegments = fileUri.pathSegments |
| .take(fileUri.pathSegments.length - uri.pathSegments.length + 1) |
| .toList(); |
| if (rootPathSegments[rootPathSegments.length - 1] == "lib") { |
| rootPathSegments[rootPathSegments.length - 1] = ""; |
| } else { |
| rootPathSegments.add(""); |
| } |
| Uri root = fileUri.replace(pathSegments: rootPathSegments); |
| Uri? existingRoot = packagesToRoot[package]; |
| if (existingRoot != null && existingRoot != root) { |
| throw "Previously found root for $package to be $existingRoot " |
| "but now found $root"; |
| } else if (existingRoot == null) { |
| packagesToRoot[package] = root; |
| rootsToPackage[root] = package; |
| } |
| |
| Uri newPlacement = dir.uri.resolve("$package/lib/$relativeFromFileUri"); |
| File.fromUri(newPlacement) |
| ..createSync(recursive: true) |
| ..writeAsBytesSync(sourceEntry.value.source); |
| writes++; |
| } |
| } |
| |
| for (MapEntry<Uri, Source> sourceEntry in nonPackageUris) { |
| Uri fileUri = sourceEntry.key; |
| Uri possibleRoot = fileUri.resolve("."); |
| while (!rootsToPackage.containsKey(possibleRoot)) { |
| Uri newPossibleRoot = possibleRoot.resolve(".."); |
| if (newPossibleRoot != possibleRoot) { |
| possibleRoot = newPossibleRoot; |
| } else { |
| throw "Failure on $fileUri"; |
| } |
| } |
| String package = rootsToPackage[possibleRoot]!; |
| |
| String relativeFromFileUri = fileUri.pathSegments |
| .skip(possibleRoot.pathSegments.length - 1) |
| .join("/"); |
| Uri newPlacement = dir.uri.resolve("$package/$relativeFromFileUri"); |
| File.fromUri(newPlacement) |
| ..createSync(recursive: true) |
| ..writeAsBytesSync(sourceEntry.value.source); |
| writes++; |
| } |
| |
| Map<String, Version> packagesToLowestLanguageVersion = {}; |
| for (Library lib in component.libraries) { |
| if (lib.importUri.isScheme("package")) { |
| String package = lib.importUri.pathSegments[0]; |
| Version? existing = packagesToLowestLanguageVersion[package]; |
| Version libLanguageVersion = lib.languageVersion; |
| if (existing == null) { |
| packagesToLowestLanguageVersion[package] = libLanguageVersion; |
| } else if (existing > libLanguageVersion) { |
| packagesToLowestLanguageVersion[package] = libLanguageVersion; |
| } |
| } |
| } |
| List<Map<String, dynamic>> packages = []; |
| Map<String, dynamic> packageConfig = { |
| "configVersion": 2, |
| "packages": packages, |
| }; |
| for (MapEntry<String, Uri> package in packagesToRoot.entries) { |
| Version version = packagesToLowestLanguageVersion[package.key]!; |
| packages.add({ |
| "name": package.key, |
| "rootUri": "../${package.key}", |
| "packageUri": "lib/", |
| "languageVersion": version.toText() |
| }); |
| } |
| File.fromUri(dir.uri.resolve(".dart_tool/package_config.json")) |
| ..createSync(recursive: true) |
| ..writeAsStringSync(json.encode(packageConfig)); |
| |
| print("Done. Wrote $writes source files."); |
| } |