From ab104abcf7feaa954d84be432c0ffee8c927270b Mon Sep 17 00:00:00 2001 From: Sam Rawlins Date: Tue, 22 Oct 2024 09:33:59 -0700 Subject: [PATCH] Move ToolConfiguration wholesale to it's own file --- lib/src/dartdoc_options.dart | 129 +----------------------------- lib/src/tool_configuration.dart | 136 ++++++++++++++++++++++++++++++++ lib/src/tool_runner.dart | 2 +- test/tool_runner_test.dart | 2 +- 4 files changed, 140 insertions(+), 129 deletions(-) create mode 100644 lib/src/tool_configuration.dart diff --git a/lib/src/dartdoc_options.dart b/lib/src/dartdoc_options.dart index fafd0c09d4..93605a531b 100644 --- a/lib/src/dartdoc_options.dart +++ b/lib/src/dartdoc_options.dart @@ -12,7 +12,7 @@ /// line arguments. library; -import 'dart:io' show Platform, exitCode, stderr, stdout; +import 'dart:io' show exitCode, stderr, stdout; import 'package:analyzer/dart/element/element.dart'; import 'package:analyzer/file_system/file_system.dart'; @@ -26,8 +26,7 @@ import 'package:dartdoc/src/logging.dart'; import 'package:dartdoc/src/model/model.dart'; import 'package:dartdoc/src/package_meta.dart'; import 'package:dartdoc/src/source_linker.dart'; -import 'package:dartdoc/src/tool_definition.dart'; -import 'package:dartdoc/src/tool_runner.dart'; +import 'package:dartdoc/src/tool_configuration.dart'; import 'package:dartdoc/src/warnings.dart'; import 'package:path/path.dart' as p show Context, canonicalize; import 'package:yaml/yaml.dart'; @@ -152,130 +151,6 @@ class CategoryConfiguration { } } -/// A configuration class that can interpret [ToolDefinition]s from a YAML map. -class ToolConfiguration { - final Map tools; - - final ResourceProvider resourceProvider; - - late ToolRunner runner = ToolRunner(this); - - ToolConfiguration._(this.tools, this.resourceProvider); - - static ToolConfiguration empty(ResourceProvider resourceProvider) { - return ToolConfiguration._(const {}, resourceProvider); - } - - // TODO(jcollins-g): consider caching these. - static ToolConfiguration fromYamlMap(YamlMap yamlMap, - String canonicalYamlPath, ResourceProvider resourceProvider) { - var newToolDefinitions = {}; - var pathContext = resourceProvider.pathContext; - for (var MapEntry(:key, value: toolMap) in yamlMap.entries) { - var name = key.toString(); - if (toolMap is! Map) { - throw DartdocOptionError( - 'Tools must be defined as a map of tool names to definitions. Tool ' - '$name is not a map.'); - } - - var description = toolMap['description'].toString(); - - List? findCommand([String prefix = '']) { - // If the command key is given, then it applies to all platforms. - var commandFromKey = toolMap.containsKey('${prefix}command') - ? '${prefix}command' - : '$prefix${Platform.operatingSystem}'; - if (!toolMap.containsKey(commandFromKey)) { - return null; - } - var commandFrom = toolMap[commandFromKey] as YamlNode; - List command; - if (commandFrom.value is String) { - command = [commandFrom.toString()]; - } else if (commandFrom is YamlList) { - command = commandFrom.map((node) => node.toString()).toList(); - } else { - throw DartdocOptionError( - 'Tool commands must be a path to an executable, or a list of ' - 'strings that starts with a path to an executable. The tool ' - "'$name' has a '$commandFromKey' entry that is a " - '${commandFrom.runtimeType}'); - } - if (command.isEmpty || command[0].isEmpty) { - throw DartdocOptionError( - 'Tool commands must not be empty. Tool $name command entry ' - '"$commandFromKey" must contain at least one path.'); - } - return command; - } - - var command = findCommand(); - if (command == null) { - throw DartdocOptionError( - 'At least one of "command" or "${Platform.operatingSystem}" must ' - 'be defined for the tool $name.'); - } - var setupCommand = findCommand('setup_'); - - var rawCompileArgs = toolMap[compileArgsTagName]; - var compileArgs = switch (rawCompileArgs) { - null => const [], - String() => [toolMap[compileArgsTagName].toString()], - YamlList() => - rawCompileArgs.map((node) => node.toString()).toList(growable: false), - _ => throw DartdocOptionError( - 'Tool compile arguments must be a list of strings. The tool ' - "'$name' has a '$compileArgsTagName' entry that is a " - '${rawCompileArgs.runtimeType}', - ), - }; - - /// Validates [executable] and returns whether it is a Dart script. - bool isDartScript(String executable) { - var executableFile = resourceProvider.getFile(executable); - if (resourceProvider.isNotFound(executableFile)) { - throw DartdocOptionError('Command executables must exist. ' - 'The file "$executable" does not exist for tool $name.'); - } - - var isDartCommand = ToolDefinition.isDartExecutable(executable); - // Dart scripts don't need to be executable, because they'll be - // executed with the Dart binary. - if (!isDartCommand && !resourceProvider.isExecutable(executableFile)) { - throw DartdocOptionError('Non-Dart commands must be ' - 'executable. The file "$executable" for tool $name does not have ' - 'execute permission.'); - } - return isDartCommand; - } - - var executableRelativePath = command.removeAt(0); - var executable = pathContext.canonicalize( - pathContext.join(canonicalYamlPath, executableRelativePath)); - var isDartSetupScript = isDartScript(executable); - if (setupCommand != null) { - var setupExecutableRelativePath = setupCommand.removeAt(0); - var setupExecutable = pathContext.canonicalize( - pathContext.join(canonicalYamlPath, setupExecutableRelativePath)); - // Setup commands aren't snapshotted, since they're only run once. - setupCommand = [ - if (isDartSetupScript) Platform.resolvedExecutable, - setupExecutable, - ...setupCommand, - ]; - } - newToolDefinitions[name] = ToolDefinition.fromCommand( - [executable, ...command], - setupCommand ?? const [], - description, - resourceProvider, - compileArgs: compileArgs); - } - return ToolConfiguration._(newToolDefinitions, resourceProvider); - } -} - /// A container class to keep track of where our yaml data came from. class _YamlFileData { /// The map from the yaml file. diff --git a/lib/src/tool_configuration.dart b/lib/src/tool_configuration.dart new file mode 100644 index 0000000000..94cf9753e1 --- /dev/null +++ b/lib/src/tool_configuration.dart @@ -0,0 +1,136 @@ +// Copyright (c) 2024, 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' show Platform; + +import 'package:analyzer/file_system/file_system.dart'; +import 'package:dartdoc/src/dartdoc_options.dart'; +import 'package:dartdoc/src/io_utils.dart'; +import 'package:dartdoc/src/tool_definition.dart'; +import 'package:dartdoc/src/tool_runner.dart'; +import 'package:yaml/yaml.dart'; + +/// A configuration class that can interpret [ToolDefinition]s from a YAML map. +class ToolConfiguration { + final Map tools; + + final ResourceProvider resourceProvider; + + late final ToolRunner runner = ToolRunner(this); + + ToolConfiguration._(this.tools, this.resourceProvider); + + static ToolConfiguration empty(ResourceProvider resourceProvider) { + return ToolConfiguration._(const {}, resourceProvider); + } + + // TODO(jcollins-g): consider caching these. + static ToolConfiguration fromYamlMap(YamlMap yamlMap, + String canonicalYamlPath, ResourceProvider resourceProvider) { + var newToolDefinitions = {}; + var pathContext = resourceProvider.pathContext; + for (var MapEntry(:key, value: toolMap) in yamlMap.entries) { + var name = key.toString(); + if (toolMap is! Map) { + throw DartdocOptionError( + 'Tools must be defined as a map of tool names to definitions. Tool ' + '$name is not a map.'); + } + + var description = toolMap['description'].toString(); + + List? findCommand([String prefix = '']) { + // If the command key is given, then it applies to all platforms. + var commandFromKey = toolMap.containsKey('${prefix}command') + ? '${prefix}command' + : '$prefix${Platform.operatingSystem}'; + if (!toolMap.containsKey(commandFromKey)) { + return null; + } + var commandFrom = toolMap[commandFromKey] as YamlNode; + List command; + if (commandFrom.value is String) { + command = [commandFrom.toString()]; + } else if (commandFrom is YamlList) { + command = commandFrom.map((node) => node.toString()).toList(); + } else { + throw DartdocOptionError( + 'Tool commands must be a path to an executable, or a list of ' + 'strings that starts with a path to an executable. The tool ' + "'$name' has a '$commandFromKey' entry that is a " + '${commandFrom.runtimeType}'); + } + if (command.isEmpty || command[0].isEmpty) { + throw DartdocOptionError( + 'Tool commands must not be empty. Tool $name command entry ' + '"$commandFromKey" must contain at least one path.'); + } + return command; + } + + var command = findCommand(); + if (command == null) { + throw DartdocOptionError( + 'At least one of "command" or "${Platform.operatingSystem}" must ' + 'be defined for the tool $name.'); + } + var setupCommand = findCommand('setup_'); + + var rawCompileArgs = toolMap[compileArgsTagName]; + var compileArgs = switch (rawCompileArgs) { + null => const [], + String() => [toolMap[compileArgsTagName].toString()], + YamlList() => + rawCompileArgs.map((node) => node.toString()).toList(growable: false), + _ => throw DartdocOptionError( + 'Tool compile arguments must be a list of strings. The tool ' + "'$name' has a '$compileArgsTagName' entry that is a " + '${rawCompileArgs.runtimeType}', + ), + }; + + /// Validates [executable] and returns whether it is a Dart script. + bool isDartScript(String executable) { + var executableFile = resourceProvider.getFile(executable); + if (resourceProvider.isNotFound(executableFile)) { + throw DartdocOptionError('Command executables must exist. ' + 'The file "$executable" does not exist for tool $name.'); + } + + var isDartCommand = ToolDefinition.isDartExecutable(executable); + // Dart scripts don't need to be executable, because they'll be + // executed with the Dart binary. + if (!isDartCommand && !resourceProvider.isExecutable(executableFile)) { + throw DartdocOptionError('Non-Dart commands must be ' + 'executable. The file "$executable" for tool $name does not have ' + 'execute permission.'); + } + return isDartCommand; + } + + var executableRelativePath = command.removeAt(0); + var executable = pathContext.canonicalize( + pathContext.join(canonicalYamlPath, executableRelativePath)); + var isDartSetupScript = isDartScript(executable); + if (setupCommand != null) { + var setupExecutableRelativePath = setupCommand.removeAt(0); + var setupExecutable = pathContext.canonicalize( + pathContext.join(canonicalYamlPath, setupExecutableRelativePath)); + // Setup commands aren't snapshotted, since they're only run once. + setupCommand = [ + if (isDartSetupScript) Platform.resolvedExecutable, + setupExecutable, + ...setupCommand, + ]; + } + newToolDefinitions[name] = ToolDefinition.fromCommand( + [executable, ...command], + setupCommand ?? const [], + description, + resourceProvider, + compileArgs: compileArgs); + } + return ToolConfiguration._(newToolDefinitions, resourceProvider); + } +} diff --git a/lib/src/tool_runner.dart b/lib/src/tool_runner.dart index c8d473d363..59380d651b 100644 --- a/lib/src/tool_runner.dart +++ b/lib/src/tool_runner.dart @@ -5,8 +5,8 @@ import 'dart:io' show Process, ProcessException; import 'package:analyzer/file_system/file_system.dart'; -import 'package:dartdoc/src/dartdoc_options.dart'; import 'package:dartdoc/src/io_utils.dart'; +import 'package:dartdoc/src/tool_configuration.dart'; import 'package:dartdoc/src/tool_definition.dart'; import 'package:path/path.dart' as path; diff --git a/test/tool_runner_test.dart b/test/tool_runner_test.dart index 0dccc655fb..e096b7fc7b 100644 --- a/test/tool_runner_test.dart +++ b/test/tool_runner_test.dart @@ -4,8 +4,8 @@ import 'dart:io'; -import 'package:dartdoc/src/dartdoc_options.dart'; import 'package:dartdoc/src/package_meta.dart'; +import 'package:dartdoc/src/tool_configuration.dart'; import 'package:dartdoc/src/tool_definition.dart'; import 'package:dartdoc/src/tool_runner.dart'; import 'package:path/path.dart' as path;