{"id":28851499,"url":"https://github.com/timnn/chaud","last_synced_at":"2026-03-05T06:31:28.994Z","repository":{"id":295191058,"uuid":"967658594","full_name":"TimNN/chaud","owner":"TimNN","description":"A hot-reloading library for Cargo workspaces designed for ease of use. Unix only.","archived":false,"fork":false,"pushed_at":"2025-05-24T10:00:14.000Z","size":188,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-02-08T11:49:16.387Z","etag":null,"topics":["cargo-workspaces","hot-reload","rust"],"latest_commit_sha":null,"homepage":"https://docs.rs/chaud","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/TimNN.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE-APACHE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-04-16T19:48:56.000Z","updated_at":"2025-12-08T07:38:18.000Z","dependencies_parsed_at":"2025-05-24T05:55:37.426Z","dependency_job_id":"767468ca-55ec-41ea-9c53-711168721a33","html_url":"https://github.com/TimNN/chaud","commit_stats":null,"previous_names":["timnn/chaud"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/TimNN/chaud","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TimNN%2Fchaud","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TimNN%2Fchaud/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TimNN%2Fchaud/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TimNN%2Fchaud/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/TimNN","download_url":"https://codeload.github.com/TimNN/chaud/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TimNN%2Fchaud/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30112226,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-05T03:40:26.266Z","status":"ssl_error","status_checked_at":"2026-03-05T03:39:15.902Z","response_time":93,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cargo-workspaces","hot-reload","rust"],"created_at":"2025-06-19T21:05:16.920Z","updated_at":"2026-03-05T06:31:28.958Z","avatar_url":"https://github.com/TimNN.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Chaud 🔥\n\n\u003c!-- Parts of this README are based on https://github.com/dtolnay's setup. --\u003e\n\u003c!-- Badge colors were picked from https://uchu.style/. --\u003e\n\n[\u003cimg alt=\"github\" src=\"https://img.shields.io/badge/github-timnn/chaud-afecb6?style=for-the-badge\u0026logo=github\" height=\"20\"\u003e](https://github.com/TimNN/chaud)\n[\u003cimg alt=\"crates.io\" src=\"https://img.shields.io/crates/v/chaud?style=for-the-badge\u0026logo=rust\u0026color=3984f2\" height=\"20\"\u003e](https://crates.io/crates/chaud)\n[\u003cimg alt=\"docs.rs\" src=\"https://img.shields.io/badge/docs.rs-chaud-c7abe9?style=for-the-badge\u0026logo=docs.rs\" height=\"20\"\u003e](https://docs.rs/chaud)\n\u003cimg alt=\"license\" src=\"https://img.shields.io/crates/l/chaud?style=for-the-badge\u0026color=e3e5e5\" height=\"20\"\u003e\n[\u003cimg alt=\"CI\" src=\"https://img.shields.io/github/actions/workflow/status/TimNN/chaud/ci.yml?style=for-the-badge\" height=\"20\"\u003e](https://github.com/TimNN/chaud/actions/workflows/ci.yml)\n\n_Chaud_ (French for \"hot\") is a hot-reloading library for Cargo workspaces\ndesigned for ease of use. [Unix only](#platform-support).\n\n```rust \u003c!--no_run--\u003e\nuse std::sync::atomic::{AtomicU32, Ordering};\n\n// Statics annotated with `persist` will keep their value, even if the crate\n// is hot-reloaded.\n#[chaud::persist]\nstatic STATE: AtomicU32 = AtomicU32::new(0);\n\n// Functions annotated with `hot` will be hot-reloaded, and use the latest\n// available version every time they are called.\n#[chaud::hot]\nfn do_something() -\u003e u32 {\n    STATE.fetch_add(1, Ordering::Relaxed)\n}\n\nfn main() {\n    chaud::init!();\n\n    loop {\n        println!(\"Something: {}\", do_something());\n        std::thread::sleep(std::time::Duration::from_secs(2));\n    }\n}\n```\n\nUnless the relevant Cargo feature is enabled, the `#[chaud::*]` macros are\nessentially no-ops (and can thus be present even in production code). Check the\n[documentation](https://docs.rs/chaud) for details on the syntax supported by\nthe macros.\n\nEnabling the `unsafe-hot-reload` feature will rewrite the items annotated with\n`#[chaud::*]` so that they can be hot-reloaded. Then, once you call\n`chaud::init!()`, Chaud does everything necessary to hot-reload your code:\n\n- It determines which crates in your workspace need to be watched.\n- It watches the filesystem for changes to those crates.\n- It rebuilds the affected crates when changes are detected.\n- It reloads any modified libraries, updating all `#[chaud::hot]` functions to\n  their latest version.\n\nThis requires some specific linker features to work, which need to be\n[configured](#setup) and are [not supported](#platform-support) on Windows.\n\nSee [How It Works](#how-it-works) if you are curios about the details.\n\n\u003c!-- prettier-ignore --\u003e\n\u003e [!CAUTION]\n\u003e Hot-reloading is fundamentally unsafe. Use at your own risk.\n\u003e\n\u003e Care was taken when writing the unsafe code in Chaud itself, but at this point\n\u003e it has not been audited by any (community) experts.\n\u003e\n\u003e Chaud is still experimental and needs more extensive testing, especially in\n\u003e non-standard linking scenarios, larger projects, and on platforms other than\n\u003e macOS.\n\n## Platform Support\n\nChaud's hot-reloading implementation is tested on `aarch64-apple-darwin` and\n`x86_64-unknown-linux-gnu` via CI. Other Unix platforms are expected to work as\nwell, unless their linkers differ significantly from the Linux linker, in which\ncase Chaud may require platform-specific support.\n\nHot-reloading is not supported on Windows, because as far as I could tell it is\nnot (easily) possible to create DLLs with undefined symbols.\n\nIf hot-reloading is not enabled (i.e., the `unsafe-hot-reload` feature is not\nenabled), then Chaud should compile on all platforms.\n\n\u003c!-- FIXME: Test this on CI for Windows --\u003e\n\nChaud is tested on `stable`, `beta` and `nightly`. However, it requires some\nunstable `rustc` flags to operate properly, and generally depends on `rustc`\nimplementation details that could change at any time.\n\nFor now Chaud targets the latest stable Rust version. In the future older Rust\nversions will likely be supported as well, probably with a policy along the\nlines of \"if hot-reloading is enabled, the current or previous stable release is\nrequired; otherwise a stable release from the past 18 months is required\".\n\n## Setup\n\nAdd `chaud` as a dependency to your application and call `chaud::init!()` during\nyour startup process (after you have configured [logging](#logging)).\n\nThe `init!()` **macro** can only be used in the package that contains your\n`fn main`. The `init()` function can be used from any package, but requires more\nmanual setup.\n\n`chaud` must be a dependency of the package that contains your `fn main`, so\nthat its features can be enabled with the `--features chaud/\u003cfeature name\u003e`\nflag.\n\nThe easiest way to actually enabled hot-reloading is via\n`cargo install chaud-cli`. This enabled the `cargo chaud` and `chaud-rustc`\nintegrations.\n\n### `cargo chaud`\n\n`cargo chaud` takes the same arguments as `cargo run`, but automatically does\neverything necessary to enable hot-reloading.\n\n### `chaud-rustc`\n\nIf you cannot use `cargo chaud` (e.g. because `cargo` is invoked by some other\nbuild tool), you can instead set `RUSTC_WRAPPER=chaud-rustc` to get most of the\nsame benefits.\n\n`chaud-rustc` will automatically add the necessary `rustc` flags when it detects\ncompilation of a binary that has hot-reloading enabled.\n\nIf used with the `init!()` **macro** it can also detect enabled features\nautomatically. In case that does not work, you must manually specify\n`CHAUD_FEATURE_FLAGS` as described in the next section.\n\nTo actually enable hot-reloading you must enable Chaud's `unsafe-hot-reload`\nfeature.\n\n### Manual Setup\n\n- As when using `chaud-rustc`, you must enable the `unsafe-hot-reload` feature\n  to actually enabled hot-reloading.\n\n- To ensure that everything is linked correcty, you must pass additional flags\n  to `rustc` when it links your application. This is often accomplished via the\n  `RUSTFLAGS` environment variable. The `\u003cplatform specific\u003e` part is:\n\n  - On all platforms:\n    - `-Clink-dead-code`: Disable dead-code stripping.\n  - On macOS:\n    - `-Zpre-link-args=-Wl,-all_load`: Include all symbols from static\n      libraries.\n      - Without this, hot-reloaded code would be unable to use any function from\n        a dependency that wasn't already being used by the original code.\n  - On Linux:\n    - `-Zpre-link-args=-Wl,--whole-archive`: Include all symbols from static\n      libraries.\n    - `-Clink-args=-Wl,--allow-multiple-definition`: Ignore duplicate symbols\n      from Rust's `compiler_builtins` and `libgcc`.\n    - `-Clink-args=-Wl,--export-dynamic`: Make all symbols in the executable\n      available to hot-reloaded libraries.\n\n- If you are not using `nightly`, you must set `RUSTC_BOOTSTRAP=1` to use the\n  `-Z` flag.\n\n- If you are not using your crate's default features, you set\n  `CHAUD_FEATURE_FLAGS` to inform Chaud about the enabled features. For example,\n  `CHAUD_FEATURE_FLAGS=\"--no-default-features --features=alpha,beta\"`.\n\n## Safety\n\nHot-reloading is fundamentally unsafe. By enabling the `unsafe-hot-reload`\nfeature, you acknowledge and accept the associated risks.\n\nThe following is an incomplete list of things to keep in mind:\n\n- The following crates will be hot-reloaded:\n  - Any crate in the workspace that you edit (and all crates that depend on it).\n- Do not change the definition of any types that persist across hot-reloads.\n- Do not apply Chaud's macros to items with the same name in the same module.\n  - Items with the same name in different modules / crates are fine.\n- `static`s defined in hot-reloaded crates will be duplicated, unless they are\n  annotated with `#[chaud::persist]`.\n- Thread local variables in crates that are hot-reloaded are not supported. It's\n  possible that they work (in which case they would still be duplicated), but\n  that's not guaranteed.\n- Hot-reloaded code only becomes active once a function annotated with\n  `#[chaud::hot]` is called. If such a function is never called, old code will\n  keep running indefinitely.\n- Function pointers and trait objects are some ways in which old code can\n  continue to run even after a hot-reload.\n\n## Logging\n\nThe vast majority of Chaud's work happens in the background, which leaves\nlogging as the only real option for reporting any errors. The\n[`log`](https://docs.rs/log) crate is used for that purpose.\n\nMany things can go wrong while hot-reloading, so to avoid any confusion it is\nimportant that you enable logging for Chaud at least at the **`warn`** level to\nbe informed about any issues.\n\nEnabling the **`info`** level is recommended to track what Chaud is doing, so\nyou know when e.g. a Cargo build is running, or a hot-reload completes.\n\nIf you do not configure any logger, Chaud will install a simple one (which\nprints to stderr) for you.\n\nIf you do configure your own logger, but do not enable at least the **`warn`**\nlevel for Chaud, Chaud will print a single message to stderr complaining about\nthat fact. (You can disable this behavior with the `silence-log-level-warning`\nfeature).\n\n### Log Levels\n\n- **`error`**: Unrecoverable errors, hot-reloading will not work\n- **`warn`**: Potentially recoverable errors, hot-reloading likely won't work\n  correctly\n- **`info`**: High-level messages about what Chaud is doing\n  - Enable this to know approximately what Chaud is doing.\n- **`debug`**: Detailed messages about what Chaud is doing\n  - Mostly irrelevant, unless you are interested in Chaud's internal operations.\n  - Log volume should be low enough to leave this permanently enabled.\n- **`trace`**: Verbose messages to aid in debugging\n  - Log volume can be quite high.\n  - I recommend only enabling this when Chaud isn't working as expected.\n\n## Troubleshooting\n\nCarefully read all `warn` messages logged by Chaud, they may be able to point\nout what the problem is.\n\nIf that doesn't help, then feel free to open an issue and I'll do my best to\nhelp. Please include `trace` logs for `chaud`.\n\nTo debug issues with undefined symbols, compiling with\n`-Csymbol-mangling-version=v0` can be useful because it includes more\ninformation in the symbol name.\n\nUndefined `__CHAUD::` symbols:\n\n- If you added the `#[chaud::*]` attributes to a new item, you need to do a full\n  restart of the application.\n- Remove `crate-type` overrides from `Cargo.toml` files. Compling non-`rlib`\n  dependencies is currently not supported.\n\n## How It Works\n\n- During the initial compilation with the `unsafe-hot-reload` feature enabled,\n  Chaud generates code similar to the following:\n\n  ```rust \u003c!--ignore--\u003e\n  // For `#[chaud::hot]`:\n  fn do_something(args) {\n    #[chaud::persist]\n    static __chaud_FUNC: AtomicFnPtr = AtomicFnPtr::new(actual_fn);\n\n    __chaud_FUNC.get()(args)\n  }\n\n  // For `#[chaud::persist]`:\n  #[export_name = \"_CHAUD::module::path::STATE\"]\n  static STATE: Whatever = Whatever::new();\n  ```\n\n  The `hot` macro stores a function pointer to the actual implementation in an\n  atomic `static`, and just calls the latest value of the atomic every time.\n\n  The `persist` macro gives that `static` a non-mangled name that never changes.\n\n  To see the full expansion, check out the `expanded_*.rs` files in the `/demo/`\n  directory.\n\n- `chaud::init()` isn't particulary intersting. It spawns a background thread\n  that:\n\n  - Runs `cargo metadata` to understand the structure of the workspace.\n  - Figures out the root crate and binary and all its workspace dependencies.\n  - Watches all those crates for changes.\n  - Rebuilds and reloads when changes are detected.\n\n- A reload build sets the `__CHAUD_RELOAD` environment variable in addition to\n  enabling the `unsafe-hot-reload` feature. This changes the code generated by\n  the macros:\n\n  ```rust \u003c!--ignore--\u003e\n  // For `#[chaud::hot]`:\n  fn do_something(args) {\n      #[chaud::persist]\n      static __chaud_FUNC: AtomicFnPtr = AtomicFnPtr::new(actual_fn);\n\n      // NEW:\n      ctor! { __chaud_FUNC.update(actual_fn) }\n\n      __chaud_FUNC.get()(args)\n  }\n\n  // For `#[chaud::persist]`:\n  unsafe extern \"Rust\" {\n      #[link_name = \"_CHAUD::module::path::STATE\"]\n      static STATE: Whatever;\n  }\n  ```\n\n  The `persist` macro changes the `static` to reference the one defined by the\n  initial compilation.\n\n  The `hot` macro now defines a [`ctor!`](https://docs.rs/ctor) that\n  automatically updates the function pointer stored in the atomic to the latest\n  version once the dynamic library containing it is loaded.\n\n- Since the `extern` `static`s would produce linker errors, Chaud performs\n  reload builds with `-Clinker=true` and records the linker invocation via\n  `--print=link-args`.\n\n  From the linker invocation, it extracts the `rlib` and object files that have\n  changed since the initial build, manually links them together into a dynamic\n  library, and then loads that library.\n\n\u003c!-- readme-license-begin --\u003e\n\n\u003cbr\u003e\n\n#### License\n\n\u003csup\u003e\nLicensed under either of \u003ca href=\"LICENSE-APACHE\"\u003eApache License, Version\n2.0\u003c/a\u003e or \u003ca href=\"LICENSE-MIT\"\u003eMIT license\u003c/a\u003e at your option.\n\u003c/sup\u003e\n\n\u003cbr\u003e\n\n\u003csub\u003e\nUnless you explicitly state otherwise, any contribution intentionally submitted\nfor inclusion in the work by you, as defined in the Apache-2.0 license, shall\nbe dual licensed as above, without any additional terms or conditions.\n\u003c/sub\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftimnn%2Fchaud","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftimnn%2Fchaud","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftimnn%2Fchaud/lists"}