zsup / firmware-rust

Firmware libraries for running Rust on Particle devices (feature/rust branch)
https://www.particle.io/
Other
27 stars 5 forks source link

Add support for Cargo #10

Open dbrgn opened 8 years ago

dbrgn commented 8 years ago

Cargo is the Rust package manager. It handles metadata, compilation and dependencies.

If we want to properly integrate with the Rust library ecosystem, we should support Cargo.

This would replace #7 and #8.

tjpeden commented 8 years ago

I actually took a stab at this the other day. I am really close but ran into an issue. I am happy to dig into it more tonight and this weekend.

tjpeden commented 8 years ago

Here's my progress so far: https://github.com/tjpeden/firmware/commit/38529b100cc9c6e2866968d3c6e6ae1bb2162c71

There are some formatting changes in application.rs that aren't really important, but what is important is that I changed the delay parameters down to 100ms but my photon is still blinking as if it were 500ms (from when I flashed the original code). The DFU shows successful in the output of make PLATFORM=photon all program-dfu in main/ So, that is what I will figure out next.

Let me know if you want/need me to change anything I've done. I'm compiling a few other libraries from rust but I'm not 100% sure if we need/want them?

tjpeden commented 8 years ago

I'm now sure how to tell Cargo to let the makefiles dictate the target dirs. Thoughts?

japaric commented 8 years ago

@tjpeden Did you forget to commit the Cargo.toml file? AFAIK, you can't call cargo without a Cargo.toml file and a project directory. See http://doc.crates.io/guide.html

I'm compiling a few other libraries from rust but I'm not 100% sure if we need/want them?

We only need the core crate in this case.

I'm now sure how to tell Cargo to let the makefiles dictate the target dirs. Thoughts?

If you mean telling cargo to use a custom target directory, then you can use a configuration file with the following key:

# .cargo/config
[build]
target-dir = "custom/target/directory"

Then cargo will output the binary/docs to "custom/target/directory" instead of the default "target" directory.

japaric commented 8 years ago

Other thoughts:

With cargo support we wouldn't need the build_core.sh script. There is cargo crate: rust-libcore that builds the core crate when used as a dependency.

Ideally, I'd like to only use cargo to build particle applications. I'm envisioning something like this:

$ cargo new --bin app
$ cd app

# depend on particle libraries
$ vim Cargo.toml
$ cat Cargo.toml
[package]
name = "app"
version = "0.1.0"
authors = ["me"]

[dependencies]
particle = "0.1.0"

# write app
$ vim src/main.rs

# builds the core crate, particle's C dependencies, and the app
$ cargo build --target=photon

# This installs the cargo-flash and cargo-debug binaries used below
$ cargo install particle-tools

# Custom cargo subcommand that flashes the application
# This subcommand can just shell out to `particle flash ...`
$ cargo flash

# Or, start a remote gdb session
$ cargo debug

I'm sure this is feasible because I have done the cargo build part here. Check the "apps" and the travis output, specially the *-size and *-objdump calls. But, I'm not sure how to expand this to produce a binary or ELF file that's usable by particle's bootloader because I'm not familiar with bootloaders in general as I always flash programs directly at the start of the flash memory (0x08000000).

Can anyone point me to information about particle's bootloader or bootloaders in general. Does the bootloader expect an ELF file that starts at some specific address, other than 0x08000000? Does the particle's bootloader expect the ELF file to contain some specific symbols?

dbrgn commented 8 years ago

@japaric woah, that would be pretty awesome :)

tjpeden commented 8 years ago

@japaric, you're right, I did. Fixed. Also just committed my attempt at using .cargo/config. The problem is I think that the path is based on a couple make variables (if I understand the make spaghetti correctly lol). Either way, it doesn't place the output files where make expects them, even if I specify the directory that the .o is normally placed, cargo will still use it's own directory structure from there. Not to mention I currently have it outputting a .a file.

All of that aside, everything I've seen online has cautioned against using that crate, but maybe that's wrong? I like the idea of using only cargo for building and programming the rust code.

What crate should we use to test being able to use extern crate to make sure it works?

Unfortunately, my fiance won't let me work on it anymore today. lol

tjpeden commented 8 years ago

@japaric, I really like that project you linked. I think that would be an awesome way to model the Particle Rust support

towynlin commented 8 years ago

@japaric This is looking great. Thank you!

Photon firmware consists of several dynamically linked modules. If you run make in the modules folder 3 of the modules will be built if you want to see some examples.

The addresses and number of modules may be different on different platforms. I often reference this file in the CLI: https://github.com/spark/particle-cli/blob/master/lib/deviceSpecs/specifications.js

Additionally, there are several pieces of data prepended and appended to our modules using linker scripts. Here's a tool for reading them: https://github.com/spark/binary-version-reader

Hope that helps!

japaric commented 8 years ago

@tjpeden

@japaric, you're right, I did. Fixed. Also just committed my attempt at using .cargo/config. The problem is I think that the path is based on a couple make variables (if I understand the make spaghetti correctly lol). Either way, it doesn't place the output files where make expects them, even if I specify the directory that the .o is normally placed, cargo will still use it's own directory structure from there. Not to mention I currently have it outputting a .a file.

I guess you can let cargo output its files in the default target directory, and add some cp commands to the Makefile to move the files when the rest of the build system expects them.

About the .a file, you should be able to use something like cargo rustc -- --emit=obj to make cargo output .o files.

All of that aside, everything I've seen online has cautioned against using that crate, but maybe that's wrong? I like the idea of using only cargo for building and programming the rust code.

You mean the rust-libcore crate? What's the argument against it? The zinc project also uses that crate to build the core crate.

What crate should we use to test being able to use extern crate to make sure it works?

You can test with the spin crate, that crate works with just the core crate.


@towynlin That's pretty helpful. I took a quick look and I'm starting to understand how the pieces fall together. Looks like the setup and loop symbols are mandatory, right? During the weekend, I'll take a closer look to the linker scripts and see if I can hack my copper cargo project to generate an ELF file that matches the layout of the user-part example you pointed out. I don't have hardware yet so that's as far I can go right now.

tjpeden commented 8 years ago

Added the spin crate and got the following error running make PLATFORM=photon all program-dfu from main/:

...
cargo rustc --target thumbv7m-none-eabi -- -O -Z no-landing-pads -g -L ../build/arm/rust
    Updating registry `https://github.com/rust-lang/crates.io-index`
   Compiling spin v0.3.5
/home/tjp/.multirust/toolchains/nightly/cargo/registry/src/github.com-88ac128001ac3a9a/spin-0.3.5/src/lib.rs:1:1: 1:1 error: can't find crate for `core` [E0463]

This is how I have the Cargo.toml configured:

[package]
name = "application"
version = "0.1.0"
authors = ["TJ Peden <tj@tjcoding.com>"]

[lib]
crate-type = ["staticlib"]

[dependencies]
rust-libcore = "0.0.3"
spin = "0.3.5"

I'm not sure how to fix that.

japaric commented 8 years ago

@tjpeden Ah right, spin can't be cross compiled like this because it (spin) would need to depend on rust-libcore but it doesn't. One "solution" would be install the libcore.rlib that cargo build --target= generated in your rust installation directory. But I don't like this solution because it pollutes your Rust install. Let me discuss this with other Rust devs to see if there's a cleaner solution.

tjpeden commented 8 years ago

IRC?

japaric commented 8 years ago

IRC?

Yeah, I asked one of the core devs that's working on Rust cross compilation story. Cargo doesn't support this use case out the box, but there's an RFC with a possible solution for this but requires adding new features to cargo.

I also received an idea on how to support this use case without needing new cargo features but hasn't been explored/implemented yet. The idea is that you create a "sysroot" (a directory tree) that contains the cross compiled core crate, and then configure cargo to use rustc --sysroot ... instead of just rustc, that way cargo looks for "standard" dependencies (like libcore) in the sysroot you created rather than in the rust installation directory. I'm going to explore this idea and report back my findings.

tjpeden commented 8 years ago

Awesome! Thanks @japaric

zsup commented 8 years ago

@japaric love your goal of a cargo build process, would love to see the build process work like that.

japaric commented 8 years ago

I created a cargo sysroot subcommand that implements the solution I mentioned above. More details in the README. This subcommand depends on this cargo PR, which hasn't landed yet, so it's not currently usable with the latest cargo nightly.

@tjpeden You may want to test that ^. You'll need to compile cargo checked out at that PR though.

tjpeden commented 8 years ago

I will indeed

tjpeden commented 8 years ago

@japaric I am having trouble building Cargo on that branch. Is there something special one has to do other than the instructions in the readme?

japaric commented 8 years ago

@japaric I am having trouble building Cargo on that branch. Is there something special one has to do other than the instructions in the readme?

Cargo is a Cargo project, so I just did cd cargo && cargo build. The new cargo binary will be in target/debug/cargo. I haven't use the make instructions in the readme in a long time.

tjpeden commented 8 years ago

That works a lot better, thanks!

tjpeden commented 8 years ago

This is what I got:

$ ~/code/cargo/target/debug/cargo sysroot --target ../build/arm/rust/thumbv7m-none-eabi.json target/sysroot
INFO: source up to date
INFO: symlinking host crates
INFO: building the core crate
An unknown error occurred

To learn more, run the command again with --verbose.
thread '<main>' panicked at 'assertion failed: try!(Command:: new ( "cargo" ) . args ( & [ "build" , "--target" ] ) . arg (
     ctx . target ) . arg ( if ctx . release { "--release" } else { "--lib" }
     ) . current_dir ( src_dir ) . env ( "CARGO_TARGET_DIR" , temp_dir ) .
     status (  )).success()', /home/tjp/.cargo/git/checkouts/cargo-sysroot-c5462c8bc31a2590/master/src/main.rs:204
An unknown error occurred

To learn more, run the command again with --verbose.
japaric commented 8 years ago

@tjpeden Ah, yes. cargo-sysroot doesn't support taking a file path as target right now (I'll fix that later). For now, you'll need to have the json file in the same directory where cargo sysroot is called, and you'll have to call cargo sysroot --target thumbv7m-none-eabi (without the json extension).

tjpeden commented 8 years ago

I'm getting the same thing :-/

tjpeden commented 8 years ago

Could you add --verbose and pass it along?

japaric commented 8 years ago

Could you add --verbose and pass it along?

done

tjpeden commented 8 years ago

Okay, I think I have this setup wrong.

$ ~/code/cargo/target/debug/cargo sysroot --target thumb7m-none-eabi target/sysroot --verbose
INFO: source up to date
INFO: symlinking host crates
INFO: building the core crate
Process didn't exit successfully: `rustc - --crate-name _ --crate-type dylib --crate-type staticlib --crate-type bin --print=file-names --target thumb7m-none-eabi` (exit code: 101)
--- stderr
error: Error loading target specification: Could not find specification for target "thumb7m-none-eabi"

thread '<main>' panicked at 'assertion failed: try!(cmd . current_dir ( src_dir ) . env ( "CARGO_TARGET_DIR" , temp_dir ) .
    status (  )).success()', .cargo/git/checkouts/cargo-sysroot-c5462c8bc31a2590/master/src/main.rs:238
An unknown error occurred

To learn more, run the command again with --verbose.
$ ll
total 48K
drwxrwxr-x 11 tjp tjp 4.0K Jan 19 23:45 applications/
-rw-rw-r--  1 tjp tjp 1.6K Jan 19 23:46 build.mk
-rw-r--r--  1 tjp tjp 3.6K Feb  3 23:14 Cargo.lock
-rw-rw-r--  1 tjp tjp  163 Feb  4 08:14 Cargo.toml
-rw-rw-r--  1 tjp tjp  839 Jan 19 23:29 import.mk
drwxrwxr-x  2 tjp tjp 4.0K Jan 19 23:45 inc/
drwxrwxr-x  7 tjp tjp 4.0K Jan 19 23:29 libraries/
-rw-rw-r--  1 tjp tjp  701 Jan 19 23:45 makefile
drwxrwxr-x  3 tjp tjp 4.0K Jan 29 17:43 src/
drwxrwxr-x  3 tjp tjp 4.0K Feb  3 10:31 target/
drwxrwxr-x  8 tjp tjp 4.0K Jan 19 23:45 tests/
-rw-r--r--  1 tjp tjp  381 Feb  3 12:02 thumbv7m-none-eabi.json

Do I have thumb7m-none-eabi.json in the right place? Also, thanks for adding that verbose flag, worked like a charm!

japaric commented 8 years ago

@tjpeden

There's a typo in this command

$ ~/code/cargo/target/debug/cargo sysroot --target thumb7m-none-eabi target/sysroot --verbose

The target is missing a "v": thumbv7m-none-eabi, because that's the name of the spec file: thumbv7m-none-eabi.json.

Once you fix the typo you should see this line in the log:

INFO: copy target specification file

This could have been detected and the tool could have informed you about this, but I haven't implemented nice error reporting (yet).

P.S. If you are using zinc's target specification files then delete the "data-layout" field from the file or you'll hit this issue.

tjpeden commented 8 years ago

I apologize, I should have caught that. I'm using the target file from build/arm/rust so I don't think it's an issue?

However, the fs::copy line failed:

~/code/cargo/target/debug/cargo sysroot --target thumbv7m-none-eabi target/sysroot --verbose
INFO: source up to date
INFO: symlinking host crates
INFO: building the core crate
       Fresh core v0.0.0 (<redacted>)
INFO: copy the core crate to the sysroot
thread '<main>' panicked at 'fs::copy(src, dst)', .cargo/git/checkouts/cargo-sysroot-c5462c8bc31a2590/master/src/main.rs:257
An unknown error occurred

To learn more, run the command again with --verbose.

It's not an access issue (I'm on Ubuntu) so I'm not sure what would cause it to fail. Thoughts?

japaric commented 8 years ago

I'm using the target file from build/arm/rust so I don't think it's an issue?

If you get a LLVM assertion while building the core crate, remove the "data-layout" field from the json file should fix. It's problem that appeared in recent nightlies.

It's not an access issue (I'm on Ubuntu) so I'm not sure what would cause it to fail. Thoughts?

No idea with just that information. But I pushed a debug branch to cargo-sysroot, it'll print the src and dst arguments of the failing fs::copy and will also preserve the temporary directory so you can inspect it. You can install that branch with cargo install --git https://github.com/japaric/cargo-sysroot/ --branch debug.

Also, could we take this discussion somewhere else? Either the cargo-sysroot bug tracker or to IRC (I'm japaric on freenode, I'm on #rust, #cargo, #particle, etc)

dbrgn commented 8 years ago

What's the status here? I'm waiting for advances in Cargo support in order to continue working on library features :)

dbrgn commented 8 years ago

Any news, @tjpeden?

japaric commented 8 years ago

I received my Photon this past Monday and got a LED blinking using the particle-cli tool. So, I'm going to look into this soon. I'm going to try two approaches: (a) have the Makefile build system use Cargo and (b) have Cargo call the Makefile system. I'll report back as soon as I get any of them working.

P.S. I was hoping to use the particle-cli tool as a reference on how the binaries are assembled but the particle compile command uses a web service to compile the program. Is the backend code published somewhere?

bvosk commented 8 years ago

@japaric Instructions on how to compile the firmware locally can be found here. All the firmware and a ton of information can be found in that repo. There's also this write up which contains more details about the build setup.

Thanks for helping out, I'm looking forward to using Cargo with the Photon!

japaric commented 8 years ago

I took the route (b) I mentioned before and I got a working Cargo project that builds photon apps. Check https://github.com/japaric/photon for details. We can use it as a starting point to prototype rustic bindings around the HAL. I'll look into that next; getting bindgen to emit low level bindings to the HAL would be a great start. I'd also like to build a cargo subcommand that flashes the ELF generated by Cargo into the photon because it requires computing a CRC.

zsup commented 8 years ago

@japaric that's awesome, could you contribute that as a PR?

zsup commented 8 years ago

actually, now that I'm looking at your repo, I suppose that doesn't make a ton of sense, since it's a rebuild and not a fork

dbrgn commented 8 years ago

Awesome! This week I'm quite busy but I'll take a closer look next week.