-
Notifications
You must be signed in to change notification settings - Fork 964
Initial plugin architecture and support for custom targets #655
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
Conversation
There are some security implications with this approach: for example, typoing
|
OK, I've finally sat down to think about this @Diggsey. I'm really sorry it's taken me so I'd like to do some brainstorming about the requirements here to make sure Overriding the syntax of cargo's
|
Could you clarify what you mean by this? This design doesn't alter the existing space of targets at all, it simply extends it with a set of targets including ":" (which would never otherwise be part of a target). The left hand side of this target (split on the ":") maps to a plugin, while the right hand side is an opaque string which the plugin understands (ie. the format of this part is entirely determined by the plugin, and has no relation to "normal" targets)
Each toolchain has it's own "plugins" folder: Within that folder, each plugin has its own directory Given that I was originally going to go with a rustup-wide plugins folder, with plugins all compiled for the host platform. However, I realised there are a lot of advantages to doing it this way:
This is one area I hadn't considered. It's slightly helped by the fact that the NDK toolchains are namespaced by API level, so if you update the NDK and want to use new features, everything will still work (since you would have to up your API level, which would result in a fresh toolchain being generated, using the new NDK) This will not handle bug-fixes to older API versions though, so we'll need to think about that.
The
The API level is not hard-coded, it just defaults to 21 if left unspecified. It can be set as part of the plugin-defined target, and you can also use a custom STL (these are the only two configuration options supported by the NDK when generating a native toolchain), eg. It's foreseeable that for some targets, there could be too many configuration options for them to be contained within a target string to be specified on every build. However, I found that the existing plugin architecture has a solution: We define a new target plugin, eg. That full specification would include a plugin name and arbitrary other options, and those options would be serialised to a string to be passed as the plugins target string, eg. [my-special-target]
plugin = "android"
arch = "armeabi"
args = { "api": 18 } Translates to
Yep, I'm not set on doing everything as part of build, it was more of a gimmick to show that we could do if we wanted. However, we could suggest what command to run to install the target if it's not installed, so that the user doesn't need to look elsewhere to figure out how to cross compile. (Same goes for "normal" targets)
If you want to cross compile today, with rust or any other language, the first thing you'll do is try to find a tutorial online, since generally someone will have at least done something similar before. Now instead/as well as writing a tutorial, that person can actually publish their work on Given how many potential targets there are, I don't see any possible other way this could work, than with a user-extensible system.
I've very much focused on "target" plugins for the moment since they are the obvious case, although they're not necessarily restricted to NDKs. It could be the case that they simply pull in supporting libraries, or change the compilation flags, etc. As for other plugin types, I don't have any particularly compelling use-cases, but here are my thoughts:
In any case, I felt that the cross-target use-case alone was sufficiently compelling to justify this kind of plugin system, and the only real concession it makes to potentially supporting other types in future is through the naming scheme.
True, but I think in general this is preferable: with target plugins we're moving into a very heterogeneous area, where even simple definitions such as "what linker's are available for this target" can be completely meaningless for some targets. This is the fundamental problem with targets: they don't actually give a complete specification! There are many, many ways to compile for any given target triple, (just look at the current RFC about static vs dynamic linking the CRT!) and referring to targets by a plugin name plus a plugin-defined specification string completely solves this issue, is very flexible, and I think actually reduces the cognitive load. eg. It's much easier to write
Basically, there are literally millions of possible targets, but those targets can be split into a fairly small set of groups (for example, android targets), and generally we only actually want to think about what group its in.
Yep, I haven't put to much thought into this, but I imagine we'd do it in a similar way to toolchains. (ie. have a plain |
Sorry it's again taken me so long to come back to this.
I think I clarified this in other comments, but what I was trying to say is there is no way here for
I see. I missed that there was a 'plugins' subfolder here.
In what situations would the specifics of the toolchain need to be encoded in the plugin?
Toolchains aren't per project in general, though one could use them that way. In what situations would one want to install plugins per-project? Is the standalone NDK created under the plugin directory?
Thanks. I see. Here both api level and gnustl are things that have to be configured when creating the standalone ndk?
I've identified 10 cross-compilation scenarios that might be served by this feature (further below). I'm sure there are more, and the configurations are complex; but there are a few crucial ones (like android). I'm not sure there are a great number, and many will be variations, like "find the right gcc for target X on Debian".
The RFC about static vs. dynamic linking though solves this problem upstream by adding a mechanism to toggle a feature with rustc and cargo. I see the android API and it's STL don't have an obvious upstream solution. Are there other cases where the toolchain needs to be configured in a way that doesn't make sense for rustc itself? It probably is useful to be able to talk about targets plus their configurations, and that includes things like CPU features. It's an interesting problem that seems to overlap some other things going on in Rust. To help me clarify my thinking about this I finally got around to sketching out some requirements, below. TTYL. general rustup native toolchain requirements
specific requirements
possible requirements
unknowns
future cross-scenarios
|
Going to close this, as it's quite out of date at this point. |
This is very rough around the edges, but it adds support for plugins to rustup. This commit focuses on "target" plugins, which are designed to make it extremely easy to cross-compile.
As an example, one can now run this one command, after installing rustup with the default settings:
cargo build --target android:armeabi
And it will compile for android!
Caveat: the actual Android NDK download is not yet automated, partly because it's very large and until we have resumable downloads, using the browser is preferable, and partly because automating it will require scraping the download page's html, and also having a mechanism to view and accept the license.
On the other hand, it will handle all the tricky toolchain set up and argument passing!
The above command is very magical, and breaks down into these steps:
--target
parameter (one containing a colon)target-android
rustup-plugin-target-android
target-android
plugin tells rustup to install the normalarm-linux-androideabi
targettarget-android
plugin locates the android NDK, and tells it to create a standalone toolchain within the plugin's directorycargo build
for the "armeabi" target (stripping out the "magic"--target android:armeabi
)CARGO_TARGET_ARM_LINUX_ANDROIDEABI_LINKER="<path_to_ndk_gcc>" cargo build --target arm-linux-androideabi
Magic targets can also be explicitly added via
rustup target add <plugin>:<target_descriptor>
.With this command, additional arguments can also be passed to the
cargo install
used to install the plugin. For example:rustup target add android:armeabi -- --path src/plugins/target-android
This will install the plugin from a location on disk.
Finally, there is a new subcommand for explicitly managing plugins:
On crates.io, plugins are named:
rustup-plugin-<plugin_name>
"target" plugins have a name starting with
target-
, eg.target-android
, or on crates.io,rustup-plugin-target-android
When writing a "smart" target, the
target-
prefix is omitted, eg.--target android:armeabi