Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion lib/src/command.dart
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,12 @@ abstract class PubCommand extends Command<int> {
///
/// This will load the pubspec and fail with an error if the current directory
/// is not a package.
late final Entrypoint entrypoint = Entrypoint(directory, cache);
late final Entrypoint entrypoint =
Entrypoint(directory, cache, allowThirdPartyTool: allowThirdPartyTool);

/// Whether to skip up-to-date checks of dependency files, when generated by a
/// third party tool.
bool get allowThirdPartyTool => false;

/// The URL for web documentation for this command.
String? get docUrl => null;
Expand Down
2 changes: 2 additions & 0 deletions lib/src/command/run.dart
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ class RunCommand extends PubCommand {
bool get allowTrailingOptions => false;
@override
bool get hidden => deprecated;
@override
bool get allowThirdPartyTool => true;

final bool deprecated;
final bool alwaysUseSubprocess;
Expand Down
32 changes: 30 additions & 2 deletions lib/src/entrypoint.dart
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,13 @@ class Entrypoint {
/// Whether this is an entrypoint for a globally-activated package.
final bool isGlobal;

/// Whether to skip up-to-date checks of dependency files, when generated by a
/// third party tool.
///
/// If `package_config.json` has as a `generator` other than `pub`, checks of
/// it, `.packages` and `pubspec.lock` are skipped.
final bool allowThirdPartyTool;

/// The lockfile for the entrypoint.
///
/// If not provided to the entrypoint, it will be loaded lazily from disk.
Expand Down Expand Up @@ -173,15 +180,16 @@ class Entrypoint {
String get _incrementalDillsPath => p.join(cachePath, 'incremental');

/// Loads the entrypoint from a package at [rootDir].
Entrypoint(String rootDir, this.cache)
Entrypoint(String rootDir, this.cache, {this.allowThirdPartyTool = false})
: root = Package.load(null, rootDir, cache.sources),
isGlobal = false;

/// Creates an entrypoint given package and lockfile objects.
/// If a SolveResult is already created it can be passed as an optimization.
Entrypoint.global(this.root, this._lockFile, this.cache,
{SolveResult? solveResult})
: isGlobal = true {
: isGlobal = true,
allowThirdPartyTool = false {
if (solveResult != null) {
_packageGraph = PackageGraph.fromSolveResult(this, solveResult);
}
Expand Down Expand Up @@ -506,6 +514,26 @@ class Entrypoint {
);
}

if (allowThirdPartyTool) {
// Check if a third party tool generated `package_config.json`.
var packageConfigText = readTextFile(packageConfigFile);
var packageConfigIsFromThirdParty =
!packageConfigText.contains('\n "generator": "pub",\n');

if (packageConfigIsFromThirdParty) {
// Skip all checks of dependency files. This allows third party tools to
// resolve dependencies.
final generator = RegExp(r'"generator":\s*"([^"]+)",')
.firstMatch(packageConfigText)
?.group(1) ??
'<unknown>';
log.message(
'Skipping dependency up-to-date checks: resolved by $generator tool.',
);
return;
}
}

// Manually parse the lockfile because a full YAML parse is relatively slow
// and this is on the hot path for "pub run".
var lockFileText = readTextFile(lockFilePath);
Expand Down
10 changes: 5 additions & 5 deletions lib/src/executable.dart
Original file line number Diff line number Diff line change
Expand Up @@ -92,10 +92,6 @@ Future<int> runExecutable(
}

if (useSnapshot) {
// Since we don't access the package graph, this doesn't happen
// automatically.
entrypoint.assertUpToDate();

if (!fileExists(snapshotPath) ||
entrypoint.packageGraph.isPackageMutable(package)) {
await recompile(executable);
Expand Down Expand Up @@ -298,7 +294,11 @@ Future<DartExecutableWithPackageConfig> getExecutableForCommand(
'Could not find file `$descriptor`',
CommandResolutionIssue.fileNotFound);
}
final entrypoint = Entrypoint(root, SystemCache(rootDir: pubCacheDir));
final entrypoint = Entrypoint(
root,
SystemCache(rootDir: pubCacheDir),
allowThirdPartyTool: true,
);
try {
// TODO(sigurdm): it would be nicer with a 'isUpToDate' function.
entrypoint.assertUpToDate();
Expand Down
3 changes: 2 additions & 1 deletion test/descriptor.dart
Original file line number Diff line number Diff line change
Expand Up @@ -279,8 +279,9 @@ Descriptor packagesFile([Map<String, String> dependencies]) =>
Descriptor packageConfigFile(
List<PackageConfigEntry> packages, {
String generatorVersion = '0.1.2+3',
String generator = 'pub',
}) =>
PackageConfigFileDescriptor(packages, generatorVersion);
PackageConfigFileDescriptor(packages, generatorVersion, generator);

/// Create a [PackageConfigEntry] which assumes package with [name] is either
/// a cached package with given [version] or a path dependency at given [path].
Expand Down
6 changes: 4 additions & 2 deletions test/descriptor/packages.dart
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ class PackagesFileDescriptor extends Descriptor {
/// Describes a `.dart_tools/package_config.json` file and its contents.
class PackageConfigFileDescriptor extends Descriptor {
final String _generatorVersion;
final String _generator;

/// A map describing the packages in this `package_config.json` file.
final List<PackageConfigEntry> _packages;
Expand All @@ -115,7 +116,7 @@ class PackageConfigFileDescriptor extends Descriptor {
configVersion: 2,
packages: _packages,
generatorVersion: Version.parse(_generatorVersion),
generator: 'pub',
generator: _generator,
generated: DateTime.now().toUtc(),
);
}
Expand All @@ -124,7 +125,8 @@ class PackageConfigFileDescriptor extends Descriptor {
///
/// [dependencies] maps package names to strings describing where the packages
/// are located on disk.
PackageConfigFileDescriptor(this._packages, this._generatorVersion)
PackageConfigFileDescriptor(
this._packages, this._generatorVersion, this._generator)
: super('.dart_tool/package_config.json');

@override
Expand Down
52 changes: 52 additions & 0 deletions test/run/allows_third_part_tool_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// Copyright (c) 2014, 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.

// @dart=2.10

import 'package:test/test.dart';

import '../descriptor.dart' as d;
import '../test_pub.dart';

void main() {
test('allows a third party tool to resolve dependencies', () async {
await d.dir('local_baz', [
d.pubspec({'name': 'baz'}),
]).create();

await d.dir(appPath, [
d.pubspec({
'name': 'myapp',
'dependencies': {
// Path dependencies trigger checks of all dependency files and
// is what third party tools, such as melos, use to link up packages
// for local development.
'baz': {'path': '../local_baz'},
},
}),
d.libDir('myapp'),
]).create();

// Run pub.get to generate the dependency files.
await pubGet();

// Simulate a third party tool, modifying `package_config.json`.
var thirdPartyToolFile = d.dir(appPath, [
d.packageConfigFile([], generator: 'not-pub'),
]);
await thirdPartyToolFile.create();
await thirdPartyToolFile.validate();

// Run the app with pub.run.
final pub = await pubRun(args: ['lib/myapp.dart']);
expect(
pub.stdoutStream(),
emits('Skipping dependency up-to-date checks: resolved by not-pub tool.'),
);
await pub.shouldExit(0);

// Validate that the file was not overwritten.
await thirdPartyToolFile.validate();
});
}