Git Product home page Git Product logo

watt's Introduction

Watt

github crates.io docs.rs

Watt is a runtime for executing Rust procedural macros compiled as WebAssembly.

[dependencies]
watt = "0.5"

Compiler support: requires rustc 1.42+


Rationale

  • Faster compilation. By compiling macros ahead-of-time to Wasm, we save all downstream users of the macro from having to compile the macro logic or its dependencies themselves.

    Instead, what they compile is a small self-contained Wasm runtime (~3 seconds, shared by all macros) and a tiny proc macro shim for each macro crate to hand off Wasm bytecode into the Watt runtime (~0.3 seconds per proc-macro crate you depend on). This is much less than the 20+ seconds it can take to compile complex procedural macros and their dependencies.

  • Isolation. The Watt runtime is 100% safe code with zero dependencies. While running in this environment, a macro's only possible interaction with the world is limited to consuming tokens and producing tokens. This is true regardless of how much unsafe code the macro itself might contain! Modulo bugs in the Rust compiler or standard library, it is impossible for a macro to do anything other than shuffle tokens around.

  • Determinism. From a build system point of view, a macro backed by Wasm has the advantage that it can be treated as a purely deterministic function from input to output. There is no possibility of implicit dependencies, such as via the filesystem, which aren't visible to or taken into account by the build system.


Getting started

Start by implementing and testing your proc macro as you normally would, using whatever dependencies you want (syn, quote, etc). You will end up with something that looks like:

use proc_macro::TokenStream;

#[proc_macro]
pub fn the_macro(input: TokenStream) -> TokenStream {
    /* ... */
}

#[proc_macro_derive] and #[proc_macro_attribute] are supported as well; everything is analogous to what will be shown here for #[proc_macro].

When your macro is ready, there are just a few changes we need to make to the signature and the Cargo.toml. In your lib.rs, change each of your macro entry points to a no_mangle extern "C" function, and change the TokenStream in the signature from proc_macro to proc_macro2.

It will look like:

use proc_macro2::TokenStream;

#[no_mangle]
pub extern "C" fn the_macro(input: TokenStream) -> TokenStream {
    /* same as before */
}

Now in your macro's Cargo.toml which used to contain this:

# my_macros/Cargo.toml

[lib]
proc-macro = true

change it instead to say:

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

[patch.crates-io]
proc-macro2 = { git = "https://github.com/dtolnay/watt" }

This crate will be the binary that we compile to Wasm. Compile it by running:

$ cargo build --release --target wasm32-unknown-unknown

Next we need to make a small proc-macro shim crate to hand off the compiled Wasm bytes into the Watt runtime. It's fine to give this the same crate name as the previous crate, since the other one won't be getting published to crates.io. In a new Cargo.toml, put:

[lib]
proc-macro = true

[dependencies]
watt = "0.5"

And in its src/lib.rs, define real proc macros corresponding to each of the ones previously defined as no_mangle extern "C" functions in the other crate:

use proc_macro::TokenStream;
use watt::WasmMacro;

static MACRO: WasmMacro = WasmMacro::new(WASM);
static WASM: &[u8] = include_bytes!("my_macros.wasm");

#[proc_macro]
pub fn the_macro(input: TokenStream) -> TokenStream {
    MACRO.proc_macro("the_macro", input)
}

Finally, copy the compiled Wasm binary from target/wasm32-unknown-unknown/release/my_macros.wasm under your implementation crate, to the src directory of your shim crate, and it's ready to publish!


Remaining work

  • Performance. Watt compiles pretty fast, but so far I have not put any effort toward optimizing the runtime. That means macro expansion can potentially take longer than with a natively compiled proc macro.

    Note that the performance overhead of the Wasm environment is partially offset by the fact that our proc macros are compiled to Wasm in release mode, so downstream cargo build will be running a release-mode macro when it would have been running debug-mode for a traditional proc macro.

    A neat approach would be to provide some kind of cargo install watt-runtime which installs an optimized Wasm runtime locally, which the Watt crate can detect and hand off code to if available. That way we avoid running things in a debug-mode runtime altogether. The experimental beginnings of this can be found under the jit/ directory.

  • Tooling. The getting started section shows there are a lot of steps to building a macro for Watt, and a pretty hacky patching in of proc-macro2. Ideally this would all be more straightforward, including easy tooling for doing reproducible builds of the Wasm artifact for confirming that it was indeed compiled from the publicly available sources.

  • RFCs. The advantages of fast compile time, isolation, and determinism may make it worthwhile to build first-class support for Wasm proc macros into rustc and Cargo. The toolchain could ship its own high performance Wasm runtime, which is an even better outcome than Watt because that runtime can be heavily optimized and consumers of macros don't need to compile it.


This can't be real

To assist in convincing you that this is real, here is serde_derive compiled to Wasm. It was compiled from the commit serde-rs/serde@1afae183. Feel free to try it out as:

// [dependencies]
// serde = "1.0"
// serde_json = "1.0"
// wa-serde-derive = "0.1"

use wa_serde_derive::Serialize;

#[derive(Serialize)]
struct Watt {
    msg: &'static str,
}

fn main() {
    let w = Watt { msg: "hello from wasm!" };
    println!("{}", serde_json::to_string(&w).unwrap());
}

Acknowledgements

The current underlying Wasm runtime is a fork of the Rust-WASM project by Yoann Blein and Hugo Guiroux, a simple and spec-compliant WebAssembly interpreter.


License

Everything outside of the `runtime` directory is licensed under either of Apache License, Version 2.0 or MIT license at your option. The `runtime` directory is licensed under the ISC license.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this crate by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

watt's People

Contributors

alexcrichton avatar cad97 avatar dtolnay avatar jasperdesutter avatar msizanoen1 avatar mystor avatar seeekr avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

watt's Issues

Move token_stream_parse implementation to Wasm

As noted in #2 (comment),

watt::sym::token_stream_parse is me being too lazy to implement a Rust syntax tokenizer in wasm (aka copy it from proc-macro2), but we could likely optimize that slightly by running that in wasm as well.

We'll need to import something like https://github.com/alexcrichton/proc-macro2/blob/1.0.6/src/strnom.rs and use that to implement token stream parsing rather than calling out to a host func.

It's possible we can simplify the implementation from proc-macro2 by omitting the parts that deal with parsing comments, as those won't appear in our use case.

Improve access to a precompiled wasm runtime

Right now there's rudimentary support for executing code in a JIT loaded through a dynamic library which exposes a wasm C API. Unfortunately though there's not really a great way to download this runtime for your platform or configure this. It'd be good to have a "run this command" style thing to accelerate macro invocations locally.

Consider printing warning in non-JIT mode if any single invocation takes >100ms

In the non-JIT runtime let's time how long each macro call takes and print a message if any one takes too long, pointing to the JIT installation instructions and explaining that it would likely improve your compile time.

We'll want the warning to somehow appear only once per compilation if possible. Also we'll want some way to silence it in case the user can't install JIT for some reason.

Tooling for verifying wasm was compiled from the public source

Hello

Maybe this is just a paranoia on my side. However, currently if I want to do an audit of my dependencies, I can download and extract the crate source downloaded from crates.io and read it. There are even tools promoting such approach, like cargo-crev.

But if the crate contains the binary wasm code, reading it and understanding what it does gets much harder. I understand that the compiled code can't do arbitrary stuff to the system itself, but it could still produce malicious code into its output, or simply be buggy and produce wrong code.

So I wonder, would it be possible to somehow make it possible (by some tooling) to automatically bundle both the original source code and the wasm and have a way to opt into the longer compile time, but making sure it is indeed from the given sources?

Cache wasm module instantiations across macro invocations

I'm extracting this as an actionable item from #2 where one thing for speed optimization that we'll need to do is to cache the instantiation of a wasm module across macro invocations. This'll shave about 50ms off each macro invocation in the case of using the JIT.

I'm not really sure how best to key this cache though in an efficient manner. We may be able to get by with a simple hash table and comparing sizes of wasm blobs, and then memcmp should be a well optimized rountine located in libstd, even for debug builds, so that may be fast enough to just key off wasm blobs directly. (they're all pretty unlikely to be the same length anyway)

Addressing crate local state for procedural macros problem

Hi! Thank you for this cool work!

I think that this approach could also address this issue. More precisely, I mean persistent data storage for all macros. This could be obtained by Wasm multimodules approach and sharing the same memory for one modules. Moreover, SQLite or Redis could be used to save state. All of them compiled in such a way to work like in-memory db without any imports. With our proc macro they could be used in the following way:

#[invocation_handler(side_modules = redis)]
fn run(arg: String) -> Vec<u8> {
    redis::call("SET A 1")
}

#[invocation_handler(side_modules = sqlite)]
fn run(arg: String) -> Vec<u8> {
    sqlite::call("SELECT * FROM Table")
}

And I can adapt them to any convention.

What do you think about that? I haven't look at the internals of watt, so can't estimate how many should be add to support this idea.

Hand off code to a preinstalled optimized runtime if available

From some rough tests, Watt macro expansion when compiling the runtime in release mode is about 15x faster than when the runtime is compiled in debug mode.

Maybe we can set it up such that users can run something like cargo install watt-runtime and then our debug-mode runtime can detect whether that optimized runtime is installed; if it is, then handing off the program to it.

Forwarding cargo features into the wasm

I have a macro I want to run through watt (in fact, I need to in order to break a self-dependency bootstrap cycle). However, I additionally want to have the macro emit slightly more or less depending on enabled features of my facade crate.

(Specifically, because I'm writing a proc macro that targets working with syn, I want to support disabling the bits covered by syn features. In the future, I can maybe emit #[cfg(accessible(::my_runtime::syn::path)], but for now, this is covered by my runtime having equivalent feature flags.)

I have it... theoretically working, but the hack to call one of 24 different symbols is... not pretty at best.

image

image

It'd be really nice if watt would support passing through a second FFI-safe argument so we can inform the proc macro about some more state. The author would be responsible for making sure that cargo still understands all of the inputs to the proc macro for when it needs to be rerun.

Mention that the Isolation property listed in the readme is not a security property

Hello,

Following discussions on IRC, I've been pointed to this crate. It looks great!

That said, there is a thing that is listed in the readme, and, I think, has been misinterpreted by some: the Isolation property does not protect against a malicious proc macro, only against a buggy proc macro.

It doesn't protect against an actively malicious proc macro, because just wrapping the malicious code in quote!{} and returning it alongside the token streams would be enough to bypass any isolation properties of wasm -- except if the built code is never run locally, even just for cargo test.

As such, I think it may be helpful to just add a sentence to the readme stating that this isolation property does not protect against actively malicious proc macros?

Panic info is compiled in

It seems that panic information is still compiled into the wasm file.

The crate file can be downloaded from this link. It contains the wasm file (2.7 MB uncompressed). If you run strings on the wasm file, you see some paths included. This is caused by panic information still being compiled in. Mostly, for proc macros such info is irrelevant. With a custom panic handler, the linker should be able to optimize it out.

Although I'm not sure, it might even be wanted:

//! panics get printed to the console; this is optional but helpful!

Demo build failing + Example module decode error

Would love to write macros using watt, it's very exciting. Can't get anything to work unfortunately, following the demo or the example pattern in the readme.

Demo broken

watt/demo/impl on  master is 📦 v0.0.0 via 🦀 v1.75.0 
❯ cargo build --release --target wasm32-unknown-unknown
    Updating crates.io index
   Compiling proc-macro2 v1.0.75 (/Users/wbrickner/Downloads/watt/proc-macro)
   Compiling unicode-ident v1.0.12
   Compiling quote v1.0.35
   Compiling syn v2.0.48
error[E0277]: `proc_macro2::LexError` doesn't implement `std::fmt::Display`
   --> /Users/wbrickner/.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.48/src/error.rs:407:32
    |
407 |         Error::new(err.span(), err)
    |         ----------             ^^^ `proc_macro2::LexError` cannot be formatted with the default formatter
    |         |
    |         required by a bound introduced by this call
    |
    = help: the trait `std::fmt::Display` is not implemented for `proc_macro2::LexError`
    = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
note: required by a bound in `error::Error::new`
   --> /Users/wbrickner/.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.48/src/error.rs:158:19
    |
158 |     pub fn new<T: Display>(span: Span, message: T) -> Self {
    |                   ^^^^^^^ required by this bound in `Error::new`

For more information about this error, try `rustc --explain E0277`.
error: could not compile `syn` (lib) due to previous error

Example Code

Following the guide in the readme exactly, I get the wasm to build, but then at the invocation site of the shim (my_macro!()) I get a panic with a module decode error. This originates in the shim, on WasmMacro::new.

ts_test on  master [?] is 📦 v0.1.0 via 🦀 v1.75.0 
❯ cargo check
   Compiling type_set v0.1.0 (/Users/wbrickner/Documents/Projects/type_set/new/type_set)
    Checking ts_test v0.1.0 (/Users/wbrickner/Documents/Projects/type_set/new/ts_test)
error: proc macro panicked
 --> src/main.rs:3:1
  |
3 | the_macro!();
  | ^^^^^^^^^^^^
  |
  = help: message: called `Result::unwrap()` on an `Err` value: DecodeModuleFailed

error: could not compile `ts_test` (bin "ts_test") due to previous error

Thank for all the work you guys are doing for the ecosystem, it's amazing!

Consider adding a separate WASI runtime for build.rs isolation

This project is brilliant! I've wanted some way to pre-compile build tools like this for a while. I've got a fairly hefty feature request, though.

Currently, Watt is designed to completely sandbox the input. However, I've worked on several non-proc-macro build tools that need to access the filesystem (e.g. to locate non-Rust input files). These projects would still be amenable to pre-compilation; the output binaries don't need to link with them. Unfortunately, Watt currently can't address this use case, since it can only read and write TokenStreams.

It would be neat if there was a way to invoke Watt from a build.rs file. and allow it to access more of the external environment.

One route to implement this would be through WASI. WASI is a wasm "syscall" ABI -- a set of functions implemented in the wasm runtime, which access the external system. (It's defined here.) All that would be needed to support this would be to implement these functions and link them into the Watt runtime.

Rust code can then be compiled with the wasm32-wasi target, and standard library operations will then be routed through these "syscalls" in the compiled wasm module. So build tools could be written using standard Rust APIs, and transparently run through Watt.

You could still retain some sandboxing, since WASI is designed to be explicitly initialized with a set of capabilities -- for example, you could explicitly pass in the paths the build tool is allowed to access in your build.rs file.

You could also allow using WASI syscalls in the proc-macro runtime. I'm not sure if non-deterministic proc-macros are permitted by Rust, though.

Downsides of this approach: it would add some compile time to the runtime, and it adds some complexity.

proc_macro instead of proc_macro2

I'm currently working on a pr that allows proc_macro to run inside webassembly by implementing a proc_macro::bridge::Server that forwards all the calls over ffi to the real macro. (I already got the demo working 🥳 ).

Pros:

  • closer to normal proc-macros
    • more compatiblity with other things like proc-macro-error or synstructure?
  • the proc_macro2 patch can be removed

Cons:

  • proc_macro::bridge::Server is unstable
  • the webassembly part of the macro has to be compiled on nightly

So my question is:
Is there a reason why you're not already doing this? I don't want to waste my time on this if that's not even something you might want

`cargo watt` subcommand

I made a cargo subcommand called cargo watt, which aims to improve the tooling listed in the README.

Currently, it does two things:

  1. Compile a proc-macro crate (locally, from git or from crates.io) and generate a crate which exposes the compiled wasm
  2. Verify that that wasm file was compiled from some source

Initially I wanted to replace

#[proc_macro]
pub fn the_macro(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
    /* ... */
}

with

#[no_mangle]
pub extern "C" fn the_macro(input: proc_macro2::TokenStream) -> proc_macro2::TokenStream {
    the_macro_inner(input.into().into()
}
pub fn the_macro_inner(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
    /* ... */
}

but that doesn't work because Into<proc_macro::TokenStream> does not exist in wasm.

So my solution was to just replace the TokenStreams and hope for the best, while also using a patched version of syn which basically just has all instances of proc_macro replaced with proc_macro2.

This actually works for quite a few crates (e.g. serde-derive, typed-builder, tracing-attributes) but it doesn't for some other ones (everything depending on synstructure and some others).

Now I am wondering:
Is it fundamentally impossible to provide the From-impls because proc-macro just doesn't exist in wasm, or would it be possible to do this transformation in some other way?

Pure Macros

Hello,

I hope you having a wonderful day, First off let me thank you for your awesome projects; I'm a fan of your work.

I was wondering have you ever tried to cache the TokenStream based on the input? So maybe we can mark a procedural macro as #[pure] and get the results cached based on the input?

I have recently forked proc-macro2 and serde (to remove its dependency on proc-macro2) and bootstrapped a simple serializable TokenStream, But It means that I would still spend a lot of time compiling and running a noop macro that just reads the cache and returns it.

Then I came around to this article a few days ago https://www.coderemote.dev/blog/faster-rust-compiler-macro-expansion-caching/

It sounded promising to me, So that's when I started thinking about using Watt as both the runtime and cache server to prevent the redundant time spent on the macro execution by short-circuiting from the cache auto-magically.

So what I want to ask you is, Have you ever tried this? What are your thoughts? Do you think Watt can be a good place to do this kind of caching? Or do you prefer me to not submit such a PR and instead work on a standalone solution?

I also would like to know about the current situation with the Compiler and Fallback variants of the token stream and whether are they going to both be kept in or one is a temporary transition period solution. But I think that's another question for another repo.

Note: while this text is written with @dtolnay as the recipient the question remains open for everyone to discuss. Sorry if it felt more like an email than an issue, I just noticed the tone as I'm about to submit😅

With watt's proc-macro, parsing raw identifiers causes a panic

Without patching proc-macro2, syn::parse_str::<syn::Ident> accepts raw identifiers:

[src\main.rs:2] syn::parse_str::<syn::Ident>("r#raw") = Ok(Ident(r#raw))
The simplest possible test:
pub fn main() {
    dbg!(syn::parse_str::<syn::Ident>("r#raw"));
}

Hooking the same test up to run in watt, however:

error: proc-macro derive panicked
 --> demo\caller\src\main.rs:3:10
  |
3 | #[derive(Demo)]
  |          ^^^^
  |
  = help: message: panicked at '"r#raw" is not a valid Ident', D:\repos\dtolnay\watt\proc-macro\src\lib.rs:445:13  
The demo derive, patched:
#[no_mangle]
pub extern "C" fn demo(input: TokenStream) -> TokenStream {
    let input: DeriveInput = match syn::parse2(input) {
        Ok(input) => input,
        Err(err) => return err.to_compile_error(),
    };

    let ident = input.ident;
    let message = format!("{:?}", syn::parse_str::<syn::Ident>("r#raw"));

    quote! {
        impl #ident {
            pub const MESSAGE: &'static str = #message;
        }
    }
}

100x slower performance on serde_derive (with recent serde and syn?)

I just tried to use watt with serde_derive to improve performance in a project of mine, only to find it got much slower running the derive macros. Just compiling the shim serde_derive crate is of course much faster.

For this snippet:

use serde_derive::{Deserialize, Serialize};

#[derive(Serialize, Deserialize)]
pub struct Information {
    pub a: Option<f32>,
    pub b: Option<f32>,
    pub c: Option<f32>,
    pub d: Option<f32>,
    pub e: Option<f32>,
    pub f: Option<f32>,
    pub g: Option<f32>,
}

fn main() {}

cargo +nightly rustc -- -Z time-passes gives me:
time: 2.753; rss: 53MB -> 113MB ( +60MB) macro_expand_crate

When using the normal serde_derive I get:
time: 0.027; rss: 53MB -> 75MB ( +21MB) macro_expand_crate

I am using a patched syn (rebased on master from jakobhellermann's): https://github.com/acshi/syn-watt/tree/watt-feature
And my patched serde is here: https://github.com/acshi/serde/tree/watt
The final shim crate I have is: https://github.com/acshi/serde_derive_wa

I am guessing this might have something to do with using the latest versions of syn and serde? For some reason I found I also had to patch serde_derive to output r#try! or I would get compilation errors because try is a reserved keyword.

Investigate more efficient implementation of `proc-macro2`

Right now the [patch] implementation of proc-macro2 hasn't really been optimized at all, it's basically the fallback implementation in proc-macro2 itself. There's a number of aspects to it that may cause macros to be slower than they otherwise should be:

  • TokenStream is simply a Vec, so Clone is expensive
  • Both Ident and Literal store an internal String, so cloning is expensive

That may actually be it now that I think about it... I might be worthwhile investigating interning and/or Rc<Vec<TokenTree>> for wasm.

Debug info is compiled in

More than half of the (uncompressed) .wasm file of wa-serde-derive is made up of the debug info section.

Warnings on unnecessary trailing semicolon in fnimpl macros

$ cargo check
warning: unnecessary trailing semicolon
   --> src/../runtime/src/func.rs:73:11
    |
73  |         $a;
    |           ^ help: remove this semicolon
...
102 | fnimpl!(WasmFunc1 A);
    | --------------------- in this macro invocation
    |
    = note: `#[warn(redundant_semicolon)]` on by default

JIT runtime failed to initiate panic

Panics seem to get printed decently by the interpreted runtime, but not the JIT runtime.

To reproduce, cherry-pick 149ca1a and then:

$ cargo build --release --target wasm32-unknown-unknown --manifest-path demo/impl/Cargo.toml && cargo run --manifest-path demo/caller/Cargo.toml

error: proc-macro derive panicked
 --> demo/caller/src/main.rs:3:10
  |
3 | #[derive(Demo)]
  |          ^^^^
  |
  = help: message: panicked at 'oh no!', src/lib.rs:5:5


$ cargo build --release --target wasm32-unknown-unknown --manifest-path demo/impl/Cargo.toml && WATT_JIT=/git/wasmtime/target/release/libwasmtime_api.so cargo run --manifest-path demo/caller/Cargo.toml

fatal runtime error: failed to initiate panic, error 5
error: could not compile `watt-demo-caller`.

Set up a benchmark of the runtime

We'll want to take some representative proc macro(s) to compile to wasm, and set up a benchmark that times how long it takes to expand some representative inputs.

This would be a necessary first step toward beginning to optimize the Watt runtime.

Cannot depend on crates that depends on procedural macro?

In my procedural macro, I need several crates that further depends on procedural macro. For example:

#[derive(serde::Deserialize)]
struct Config {
  ...
}

use proc_macro2::TokenStream;

#[no_mangle]
pub extern "C" fn the_macro(input: TokenStream) -> TokenStream {
    /* use struct Config to help manipulating TokenStream */
}

and in my Cargo.toml I have:

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

[patch.crates-io]
proc-macro2 = { git = "https://github.com/dtolnay/watt" }

[dependencies]
serde = { version = "*", features = ["derive"] }

When I compile, I get:

$ cargo build --release --target wasm32-unknown-unknown
   Compiling serde v1.0.116
error: /home/mashplant/Code/CLionProjects/lalr1-wasm/target/release/deps/libserde_derive-cbe50235eb5bda1c.so: undefined symbol: print_panic
   --> /home/mashplant/.cargo/registry/src/mirrors.tuna.tsinghua.edu.cn-df7c3c540f42cdbd/serde-1.0.116/src/lib.rs:285:1
    |
285 | extern crate serde_derive;
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^

error: aborting due to previous error

error: could not compile `serde`

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.