Skip to content
This repository has been archived by the owner on Dec 13, 2018. It is now read-only.

Add support for Rust #1606

Open
wants to merge 18 commits into
base: master
Choose a base branch
from
Open

Conversation

Xanewok
Copy link

@Xanewok Xanewok commented Aug 31, 2018

This sets up preliminary support for Rust in Buck.
It uses the Rust Language Server (RLS) LSP server to provide necessary IDE features.

Since RLS and Cargo (the Rust package manager) don't support custom build systems out of box yet, this uses a hacky workaround for the moment. The user has to explicitly build a Rust target in Buck using the Buck toolbar - this in turn executes the #save-analysis flavor build and passes location of the resulting JSON save-analysis (index) files to the RLS. For this, the files should be saved first, before executing the build.

Until rust-lang/rust#53870 lands and the updated RLS will ship with a Rust toolchain, it has to be built manually using recent Rust nightly compiler and to have its executable pointed at by the user.

Currently the workflow is fragile - if the users starts editing the files without the explicit build first, the RLS can crash because how much it's currently tied to Cargo. This should be fixed when rust-lang/rls#1026 is implemented (at least stop it from crashing).

cc @jsgf @mostafaeweda @sunshowers

@facebook-github-bot
Copy link

Thank you for your pull request and welcome to our community. We require contributors to sign our Contributor License Agreement, and we don't seem to have you on file. In order for us to review and merge your code, please sign up at https://code.facebook.com/cla. If you are contributing on behalf of someone else (eg your employer), the individual CLA may not be sufficient and your employer may need the corporate CLA signed.

If you have received this in error or have any questions, please contact us at cla@fb.com. Thanks!

const buildTarget = normalizeNameForBuckQuery(task.buildTarget);

// TODO: Filter by known Rust build targets
const files = await getRustInputs(task.buckRoot, buildTarget);
Copy link
Author

Choose a reason for hiding this comment

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

This still requires explicit filtering for rust_* target kinds.

Copy link
Contributor

Choose a reason for hiding this comment

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

please do this filtering to avoid calling the expensive buck query unless we're sure this is a rust target & this call will have value.

Copy link
Author

Choose a reason for hiding this comment

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

Done in 8d4772f. Forgot to remove the comment, sorry!

@Xanewok
Copy link
Author

Xanewok commented Sep 3, 2018

@jsgf I added a disclaimer (b948e1f) that warns the user to run the Buck build in a Rust project first. Is that desirable/okay for now?

@Xanewok Xanewok changed the title WIP: Add support for Rust Add support for Rust Sep 3, 2018
@jsgf
Copy link
Contributor

jsgf commented Sep 4, 2018

@Xanewok I think its OK as a placeholder. I hope we can get something more elegant in the longer term though.

@Xanewok
Copy link
Author

Xanewok commented Sep 5, 2018

I hope we can get something more elegant in the longer term though.

Definitely, I am conviced this will not be an issue anymore once the RLS natively supports custom build systems.

Update: rust-lang/rust#53870 has been merged and the newest Rust nightly (2018-09-04) contains the RLS binary supporting external simple build systems (needed for this) out of box, as well as external Rustfmt.

const buildTarget = normalizeNameForBuckQuery(task.buildTarget);

// TODO: Filter by known Rust build targets
const files = await getRustInputs(task.buckRoot, buildTarget);
Copy link
Contributor

Choose a reason for hiding this comment

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

please do this filtering to avoid calling the expensive buck query unless we're sure this is a rust target & this call will have value.

);
}

export function getSaveAnalysisTargets(
Copy link
Contributor

Choose a reason for hiding this comment

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

change to use async / await :

export async function getSaveAnalysisTargets(
...
  const deps = await BuckService.query(buckRoot, query, []);
  return deps.map(dep => dep + '#save-analysis');
}

logger.debug(`analysisTargets: ${analysisTargets.join('\n')}`);
const artifacts: Array<string> = [];

const buildReport = await BuckService.build(task.buckRoot, analysisTargets);
Copy link
Contributor

Choose a reason for hiding this comment

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

how long does this typically take for large rust targets? do we need to report its progress?

Copy link
Author

Choose a reason for hiding this comment

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

It depends; using cargo check as example, complete check build can even take a minute or two; rechecking the leaf only shouldn't take longer than a couple of seconds. Third party deps are vendored and pre-compiled from what I understand, so this should also further cut down compilation time. @jsgf would know more about how long exactly these #save-analysis builds take in the Buck environment.

It would certainly be good to report the progress - do you know if it's possible to pass a callback or report the progress gracefully using the Buck API here?

Copy link
Author

Choose a reason for hiding this comment

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

Changed this to display a busy spinner indicator while the save-analysis build executes.

const fileUri = task.buckRoot + '/' + files[0];
logger.debug(`fileUri: ${fileUri}`);

const langService = await service.getLanguageServiceForUri(fileUri);
Copy link
Contributor

Choose a reason for hiding this comment

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

There's an invalid assumption here that the multi-project language service will only have one LSP server that we need to update its build command (while in reality there can be multiple buck roots, each having a different LSP server -- according to the project files config below).

Do you need to getAllLanguageServices or observeLanguageServices?
Can you test with multiple buck roots to see if things work properly?

Copy link
Author

Choose a reason for hiding this comment

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

I understood that MultiLspLanguageService spawn separate LSP server per appropriate 'top-level' project config files like .hhconfig and that calling this seems like a good way to retrieve a handle to appropriate LSP server that's responsible for the fileUri we're building.

However, I did assume there's going to be a single Buck root; I'll see if it works with multiple ones.

Copy link
Author

Choose a reason for hiding this comment

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

@mostafaeweda sorry it took so long! Just tested this with multiple buck roots, using https://github.com/Xanewok/rust-buck-skeleton and building both //server:server and nested//:nested. This correctly retrieved the LSP server at root dir and nested/, respectively, so this looks good so far =)


class Activation {
_rustLanguageService: AtomLanguageService<LanguageService>;
_buckTaskRunnerService: ?BuckTaskRunnerService;
Copy link
Contributor

Choose a reason for hiding this comment

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

we don't seem to need this to be an instance variable, right?

}

consumeBuckTaskRunner(service: BuckTaskRunnerService): IDisposable {
service.onDidCompleteTask(task =>
Copy link
Contributor

Choose a reason for hiding this comment

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

that's a non disposed registration, maybe this function should just:

return service.onDidCompleteTask...

Also consume Buck task service to later rebuild index on save
After hitting Buck build button, the relevant save-analysis Buck build
is performed and RLS is supplied with a dummy build command to retrieve
necessary save-analysis indexing files.

Things left to do/worth noting:
* We should detect if Buck is an appropriate build system for a given
LSP service and if not, not override RLS' external build command
* Until path remapping lands in rustc
(rust-lang/rust#53110) the index-based features
(find refs, goto def etc.) only work for source files in the
buck-out/**/$TARGET#save-analysis-container source files.
* RLS needs some bugfixin' since currently external build command does
not properly refresh file dirty state, leading to an endless build loop.
For example, running `buck build cell//:target` will create output with
paths relative to the cell, and not the current Buck root.
I could've sworn it worked with relative directories - it boiled
down to

```js
const buildFile = ...; // relative
return fsPromise.findNearestFile('.buckconfig', buildFile);
```

which *should* work for relative files, I believe?
@Xanewok
Copy link
Author

Xanewok commented Sep 14, 2018

@mostafaeweda @jsgf Rebased and pushed two changes: one being displaying busy indicator while the Buck builds the save-analysis build and the other one working around a Buck bug (9f64faf), where outputs are relative to the cell, even when these are executed from the outer Buck root.

Does this still need more work or can we land it as the 1st increment?

Copy link
Contributor

@mostafaeweda mostafaeweda left a comment

Choose a reason for hiding this comment

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

This seems ready to roll 🥇(the only comment I have here is trying to avoid unnecessary buck queries if the build task completed isn't actually a rust target).

@jsgf to land this diff, you'd need to click 'Import to Phabricator' and once landed internally (source of truth), in 5 minutes, it'll sync the changes to github.

pkg/nuclide-rust/lib/BuckIntegration.js Show resolved Hide resolved
@facebook-github-bot
Copy link

Thank you for signing our Contributor License Agreement. We can now accept your code for this (and any) Facebook open source project. Thanks!

@Xanewok
Copy link
Author

Xanewok commented Oct 2, 2018

@jsgf signed the CLA here, just in case.

Copy link

@facebook-github-bot facebook-github-bot left a comment

Choose a reason for hiding this comment

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

mostafaeweda has imported this pull request. If you are a Facebook employee, you can view this diff on Phabricator.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants