Skip to content

Add an ExtensionEvaluator for evaluating extensions in a package graph #3286

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Feb 18, 2021

Conversation

abertelrud
Copy link
Contributor

Add a first version of an ExtensionEvaluator that allows all use of extensions in targets to be evaluated and that returns the commands to run and other information. Some TODOs and FIXMEs in the code for things left to be filled in, such as product dependencies and separate extension usage arrays. This will be added in follow-on PRs.

Motivation:

This abstracts out the logic around invoking extensions and passing them inputs and receiving outputs, while leaving the details of how that is done (e.g. compiling and then running in a sandbox, or some other way) to concrete implementations. This also allows the facility to be at a lower level than the logic providing the extension runner.

There is no caching yet and but that will be added in future PRs (although most is likely to live in the concrete implementations of ExtensionRunner).

There is some similarity here with the manifest loader, and the intent is to evolve both so that there can be a shared implementation.

Modifications:

Add an ExtensionEvaluator plus an ExtensionRunner protocol.
Define a ExtensionEvaluationResult struct for capturing distilled results of invoking extensions. This includes diagnostics and plain-text output from running the extension (suitable for debugging of the extension, as with the manifest).
Define the three types of generated commands that are planned for the initial implementation: prebuild, built-tool, and postbuild.

This builds on #3285 and it is best to review that PR first and then just the diff 37b2915 of this PR.

@abertelrud
Copy link
Contributor Author

@swift-ci please smoke test

@tomerd tomerd added the ready Author believes the PR is ready to be merged & any feedback has been addressed label Feb 17, 2021

// If this target does use any extensions, create the input context to pass to them.
// FIXME: We'll want to decide on what directories to provide to the extenion
let package = self.packages.first{ $0.targets.contains(target) }!
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

first! safe? replace with guard to make safer long term?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this should be an internal error. A leftover from before cleaning up the code. Thanks for catching!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, one would think that mapping a target back to its package should be common enough to have a method of some sort (which could then do caching etc) but I didn't spot one. I can create one but wanted to keep the code minimal here. But this should definitely throw an error (I try to avoid ! completely).

)

// Generate emittable Diagnostics from the extension output.
let diagnostics: [Diagnostic] = extensionOutput.diagnostics.map { diag in
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe this can be extracted to an extension that will make this easier to read

}

// Generate commands from the extension output.
let commands: [ExtensionEvaluationResult.Command] = extensionOutput.commands.map { cmd in
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this can also be extracted to an extension imo

/// FIXME: This should be asynchronous, taking a queue and a completion closure.
fileprivate func runExtension(sources: Sources, input: ExtensionEvaluationInput, extensionRunner: ExtensionRunner, diagnostics: DiagnosticsEngine, fileSystem: FileSystem) throws -> (output: ExtensionEvaluationOutput, stdoutText: Data) {
// Serialize the ExtensionEvaluationInput to JSON.
let encoder = JSONEncoder()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could the be made static at top level to avoid creating new instance every time?

Copy link
Contributor

@tomerd tomerd left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

very cool. some some nits / ideas inline

// Deserialize the JSON to an ExtensionEvaluationOutput.
let output: ExtensionEvaluationOutput
do {
let decoder = JSONDecoder()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto

fileprivate func runExtension(sources: Sources, input: ExtensionEvaluationInput, extensionRunner: ExtensionRunner, diagnostics: DiagnosticsEngine, fileSystem: FileSystem) throws -> (output: ExtensionEvaluationOutput, stdoutText: Data) {
// Serialize the ExtensionEvaluationInput to JSON.
let encoder = JSONEncoder()
encoder.outputFormatting = [.prettyPrinted, .withoutEscapingSlashes]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

iirc we have the helpers for this in Basics

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, that's a good point. Also, this doesn't need to be pretty-printed, so I should probably just take this out.

public let diagnostics: [Diagnostic]

// A location representing a file name or path and an optional line number.
// FIXME: This should be part of the Diagnostics APIs.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe add as an extension to Basics until we move to TSC

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That makes sense. I think maybe that could be a separate follow-on PR?

var outputDir: String
var cacheDir: String
var execsDir: String
var options: [String: String]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should this be optional?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, or probably removed since we're not doing options initially.

@abertelrud
Copy link
Contributor Author

The test failure is:

11:59:31     dyld: Library not loaded: @rpath/XCTest.framework/Versions/A/XCTest
11:59:31       Referenced from: /Users/buildnode/jenkins/workspace/swift-package-manager-with-xcode-self-hosted-PR-osx/branch-main/usr/libexec/swift/pm/swiftpm-xctest-helper
11:59:31       Reason: image not found

which is a toolchain problem.

@abertelrud
Copy link
Contributor Author

@swift-ci please smoke test

…by input parameters to the package graph in addition to the SWIFTPM_ENABLE_EXTENSION_TARGETS environment variable, so that unit tests can selectively enable it for synthetic-package tests.
…xtensions in targets to be evaluated and that returns the commands to run and other information. Some TODOs and FIXMEs in the code for things left to be filled in, such as product dependencies and separate extension usage arrays.
@abertelrud abertelrud force-pushed the extensibility-part-three branch from e9c5954 to 6aea3c9 Compare February 17, 2021 20:48
@abertelrud
Copy link
Contributor Author

@swift-ci please smoke test

@abertelrud
Copy link
Contributor Author

Not sure what's going on here:

12:52:05 
/home/buildnode/jenkins/workspace/swift-package-manager-self-hosted-Linux-smoke-test/branch-main/swiftpm/Tests/SourceControlTests/RepositoryManagerTests.swift:249: error: RepositoryManagerTests.testBasics : Asynchronous wait failed - Exceeded timeout of 1.0 seconds, with unfulfilled expectations: Repository lookup expectation

@abertelrud
Copy link
Contributor Author

@swift-ci please self test linux

@abertelrud
Copy link
Contributor Author

@swift-ci please smoke test linux

@abertelrud
Copy link
Contributor Author

This Linux smoke test seems to be a failure while building libClang...

@tomerd
Copy link
Contributor

tomerd commented Feb 18, 2021

@swift-ci please self test linux

@tomerd
Copy link
Contributor

tomerd commented Feb 18, 2021

@swift-ci please smoke test linux

@abertelrud
Copy link
Contributor Author

17:38:43 
/home/buildnode/jenkins/workspace/swift-package-manager-Linux-smoke-test/branch-main/swiftpm/Sources/PackageLoading/PackageDescription4Loader.swift:16:8: error: no such module 'SourceControl'
17:38:43 import SourceControl
17:38:43        ^
17:38:43 [10/22][ 45%][5.014s] Linking Swift shared library lib/libSourceControl.so

???

@abertelrud
Copy link
Contributor Author

Looking into the failure.

@tomerd
Copy link
Contributor

tomerd commented Feb 18, 2021

@swift-ci please smoke test linux

@tomerd
Copy link
Contributor

tomerd commented Feb 18, 2021

Looking into the failure.

should be fixed with #3288

@abertelrud abertelrud merged commit df1e396 into swiftlang:main Feb 18, 2021
@abertelrud abertelrud deleted the extensibility-part-three branch February 18, 2021 06:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
ready Author believes the PR is ready to be merged & any feedback has been addressed
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants