Skip to content

Allow using generated assets#5490

Open
skreborn wants to merge 1 commit into
DioxusLabs:mainfrom
skreborn:generated-assets
Open

Allow using generated assets#5490
skreborn wants to merge 1 commit into
DioxusLabs:mainfrom
skreborn:generated-assets

Conversation

@skreborn
Copy link
Copy Markdown

@skreborn skreborn commented Apr 15, 2026

Currently, all assets are resolved either relative to the current file (if the path begins with a .) or the crate root (in all other cases).

Changes

This PR introduces a new option for asset path resolution that enables reading generated assets.

const _: Asset = asset!(concat!(env!("OUT_DIR"), "/generated-asset.txt"));

The path resolution heuristics are as follows:

  • paths beginning with . or .. both resolve relative to the current file;
  • paths beginning with the output directory are used as-is;
  • all other paths are resolved relative to the crate root (with the leading / ignored).

I also updated the documentation of the macros, added some tests for new and existing behavior, and refactored the internal error handling to improve clarity and consistency.

Alternatives considered

Following are a number of alternatives considered before settling on the current solution.

Explicit path resolution strategy

Explicitly declaring the resolution base would make the code more explicit.

asset!(crate "assets/asset.txt"); // Resolved relative to the crate root
asset!(self "../assets/asset.txt"); // Resolved relative to the current file
asset!(out "generated-asset.txt"); // Resolved relative to the output folder

Skipping the explicit strategy would fall back to the original behavior by automatically detecting whether crate or self should be used.

This was ultimately dropped in response to #5490 (comment) to avoid introducing new syntax.

Prefix character

A special character (like "!") at the beginning of the path could denote a generated path.

asset!("!/generated.css");

While the likelihood of anyone having a local folder named "!" is practically non-existent, it's still technically allowed by the operating system and thus this would constitute a breaking change. On Linux particularly, the only character that's not allowed in a file name is /.

Separate macros

A new generated_asset could be introduced so no changes would need to be made to the existing ones. However, this would lead to needless duplication without any practical gains.

generated_asset!("generated.css");

@skreborn skreborn requested a review from a team as a code owner April 15, 2026 13:22
@ealmloff
Copy link
Copy Markdown
Member

ealmloff commented Apr 15, 2026

We do need to expand the path check to allow the out dir path for assets, but we can do this without extra syntax. We support the same macros inside the path as include or include_str macros. You can use those to link to a path from the out dir:

const GENERATED_ASSET: Asset = asset!(concat!(env!("OUT_DIR"), "/file.css"));

Linking this pr with #4426

@ealmloff ealmloff added the manganis Related to the manganis crate label Apr 15, 2026
@skreborn
Copy link
Copy Markdown
Author

skreborn commented Apr 15, 2026

That's what I originally imagined would be possible, but like the linked issue, I was met with resistance.

Making no changes to the macro syntax obviously has an upside—even if the changes are optional.

Checking if an absolute path resolves to an actual file before trying it as a crate-relative one is easy enough, but in case no file is found, it's difficult to provide the user with correct and exact feedback about where the asset is supposed to be, because we can't reasonably tell whether they meant to use an absolute or a crate-relative path.

It's also technically a breaking change because if I happen to have an /assets/asset.txt file in my actual file system root, the resolution fails—unless of course the check for whether the absolute path is inside the allowed folders is repeated between those two.

The explicit resolution strategy also allows using asset!(self "asset.txt") instead of asset!("./asset.txt"), which doesn't seem like a big deal, but it definitely makes the intent clearer and more explicit. Whether that's important enough to the average developer, I don't know; it's mostly a personal preference.

In any case, if you'd like me to head in a different direction, please let me know, and I'll adjust the PR accordingly. I'd be more than happy to implement an alternative solution if that's what you think is best.

@ealmloff
Copy link
Copy Markdown
Member

If we resolve assets like this, it seems unlikely there would be much overlap:

asset!("/assets/asset.txt"); // Resolved relative to the crate root because it starts with slash and isn't point to the dir `OUT_DIR` resolves to in target
asset!("./assets/asset.txt"); // Resolved relative to the current file because it starts with `./`
asset!(concat!(env!("OUT_DIR"), "/generated-asset.txt")); // Resolved relative to the output folder because the file starts with the output folder

You would need to have something like target/{debug|release}/build/<pkg>-<hash>/out both in your root and the current crate. This could be the case if you have a global target dir for caching, but I assume you would want to link to the version you are building, (env!("OUT_DIR")) not the one in the workspace.

/
├─ Users/
│  └─ desktop/
│     └─ code/
│        └─ my-lib/
│            ├─ src/
│            │  └─ lib.rs
│            ├─ assets/
│            │  └─ asset.txt
│            ├─ Cargo.toml
│            └─ target/
│                └─ debug/
│                   └─ build/
│                      └─ my-lib-abc123/
│                         └─ out/
│                            └─ generated-asset.txt # This overlaps if you build with `TARGET_DIR=/target`, but intending to point to the lib local target dir.
└─ target/
   └─ debug/
      └─ build/
         └─ my-lib-abc123/
            └─ out/
               └─ generated-asset.txt

The restriction about which assets you can link to is intended to prevent builds passing locally, but failing elsewhere like when you publish to crates.io. For libraries, only inside the workspace or build directory should be allowed.

@jkelleyrtp
Copy link
Copy Markdown
Member

jkelleyrtp commented Apr 23, 2026

I think I would prefer something like

asset!(concat!(env!("OUT_DIR"), "/generated-asset.txt"));

or

asset!("{OUT_DIR}/generated-assets.txt")

where OUT_DIR and other env vars or hard-coded replacements can be passed through the macro system in a way that the proc macros can see. I lean towards the second approach a bit since it's cleaner and easy to parse. One issue that OUT_DIR for the proc macro might not be the same OUT_DIR for the target crate. I think a dtolnay crate has a solution for that somewhere

@skreborn skreborn force-pushed the generated-assets branch 2 times, most recently from e58061e to 48d85b3 Compare May 11, 2026 10:05
@skreborn
Copy link
Copy Markdown
Author

@ealmloff @jkelleyrtp Thank you both for the feedback! I've rebased and updated to PR to address your comments. The updated version doesn't introduce any new syntax and still avoids breaking changes (except in the most unrealistic conditions).

@ealmloff
Copy link
Copy Markdown
Member

ealmloff commented May 11, 2026

Looks like we may need to canonicalize both paths before checking if it starts with the out dir from the failing tests? Api looks good after that fix

@skreborn skreborn force-pushed the generated-assets branch from 48d85b3 to 0f10459 Compare May 12, 2026 09:17
@skreborn
Copy link
Copy Markdown
Author

I added canonicalization to the output directory path in the test context. Hopefully that'll do it!

@skreborn skreborn force-pushed the generated-assets branch from 0f10459 to fdc8889 Compare May 12, 2026 14:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

manganis Related to the manganis crate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants