{"id":18406963,"url":"https://github.com/nvzqz/embed-plist-rs","last_synced_at":"2025-07-26T09:36:01.694Z","repository":{"id":41475113,"uuid":"291219997","full_name":"nvzqz/embed-plist-rs","owner":"nvzqz","description":"Embed property list files like Info.plist directly in your Rust executable binary","archived":false,"fork":false,"pushed_at":"2022-01-10T00:09:30.000Z","size":195,"stargazers_count":41,"open_issues_count":0,"forks_count":0,"subscribers_count":4,"default_branch":"main","last_synced_at":"2025-06-27T00:57:14.915Z","etag":null,"topics":["apple","file","include","info","launchd","plist","rust"],"latest_commit_sha":null,"homepage":"https://docs.rs/embed_plist","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/nvzqz.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE-APACHE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null},"funding":{"github":["nvzqz"],"custom":["https://www.paypal.me/nvzqz"]}},"created_at":"2020-08-29T07:04:42.000Z","updated_at":"2024-12-17T19:56:07.000Z","dependencies_parsed_at":"2022-08-19T01:21:18.738Z","dependency_job_id":null,"html_url":"https://github.com/nvzqz/embed-plist-rs","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/nvzqz/embed-plist-rs","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nvzqz%2Fembed-plist-rs","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nvzqz%2Fembed-plist-rs/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nvzqz%2Fembed-plist-rs/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nvzqz%2Fembed-plist-rs/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nvzqz","download_url":"https://codeload.github.com/nvzqz/embed-plist-rs/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nvzqz%2Fembed-plist-rs/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":267145778,"owners_count":24042655,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-07-26T02:00:08.937Z","response_time":62,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"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":["apple","file","include","info","launchd","plist","rust"],"created_at":"2024-11-06T03:11:34.558Z","updated_at":"2025-07-26T09:36:01.641Z","avatar_url":"https://github.com/nvzqz.png","language":"Rust","funding_links":["https://github.com/sponsors/nvzqz","https://www.paypal.me/nvzqz"],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n    \u003ca href=\"https://github.com/nvzqz/embed-plist-rs\"\u003e\n        \u003cimg src=\"https://raw.githubusercontent.com/nvzqz/embed-plist-rs/main/img/icon.svg?sanitize=true\"\n             height=\"200px\"\u003e\n        \u003ch1 style=\"font-size:2rem;margin-top:0;\"\u003eembed_plist\u003c/h1\u003e\n    \u003c/a\u003e\n    \u003ca href=\"https://crates.io/crates/embed_plist\"\u003e\n        \u003cimg src=\"https://img.shields.io/crates/v/embed_plist.svg\" alt=\"crates.io\"\u003e\n        \u003cimg src=\"https://img.shields.io/crates/d/embed_plist.svg\" alt=\"downloads\"\u003e\n    \u003c/a\u003e\n    \u003ca href=\"https://docs.rs/embed_plist\"\u003e\n        \u003cimg src=\"https://docs.rs/embed_plist/badge.svg\" alt=\"docs.rs\"\u003e\n    \u003c/a\u003e\n    \u003ca href=\"https://github.com/nvzqz/embed-plist-rs/actions?query=workflow%3Aci\"\u003e\n        \u003cimg src=\"https://github.com/nvzqz/embed-plist-rs/workflows/ci/badge.svg\" alt=\"build status\"\u003e\n    \u003c/a\u003e\n    \u003cimg src=\"https://img.shields.io/badge/rustc-^1.39.0-blue.svg\" alt=\"rustc ^1.39.0\"\u003e\n\u003c/div\u003e\n\u003cbr\u003e\n\nEmbed an [`Info.plist`] or [`launchd.plist`] file directly in your\nexecutable binary, brought to you by [@NikolaiVazquez]!\n\nIf you found this library useful, please consider\n[sponsoring me on GitHub](https://github.com/sponsors/nvzqz). ❤️\n\n## Index\n\n1. [Motivation](#motivation)\n2. [Usage](#usage)\n3. [Minimum Supported Rust Version](#minimum-supported-rust-version)\n4. [Multi-Target Considerations](#multi-target-considerations)\n5. [Get Embedded Property Lists](#get-embedded-property-lists)\n6. [Accidental Reuse Protection](#accidental-reuse-protection)\n7. [Implementation](#implementation)\n8. [License](#license)\n\n## Motivation\n\nCertain programs require an embedded `Info.plist` or `launchd.plist` file to\nwork correctly. For example:\n\n- `Info.plist` is needed to obtain certain permissions on macOS 10.15 and\n  later.\n\n- `launchd.plist` is needed to make\n  [`launchd` daemons and user agents](https://developer.apple.com/library/archive/documentation/MacOSX/Conceptual/BPSystemStartup/Chapters/CreatingLaunchdJobs.html).\n\nDoing this manually with [`include_bytes!`] is cumbersome. To understand\nwhy, see the [implementation](#implementation). This library removes the\nheadache of doing this.\n\n## Usage\n\nThis library is available [on crates.io][crate] and can be used by adding\nthe following to your project's [`Cargo.toml`]:\n\n```toml\n[dependencies]\nembed_plist = \"1.2\"\n```\n\n...and this to any source file in your crate:\n\n```rust\nembed_plist::embed_info_plist!(\"Info.plist\");\n\n// If making a daemon:\nembed_plist::embed_launchd_plist!(\"launchd.plist\");\n```\n\nDone! It's that simple. 🙂\n\nSee [implementation](#implementation) for details on this sorcery.\n\n## Minimum Supported Rust Version\n\nThis library targets \u003cb\u003e1.39\u003c/b\u003e as its minimum supported Rust version\n(MSRV).\n\nRequiring a newer Rust version is considered a breaking change and will\nresult in a \"major\" library version update. In other words: `0.1.z` would\nbecome `0.2.0`, or `1.y.z` would become `2.0.0`.\n\n## Multi-Target Considerations\n\nThis library only works for [Mach-O](https://en.wikipedia.org/wiki/Mach-O)\nbinaries. When building a cross-platform program, these macro calls should\nbe placed behind a `#[cfg]` to prevent linker errors on other targets.\n\n```rust\n#[cfg(target_os = \"macos\")]\nembed_plist::embed_info_plist!(\"Info.plist\");\n```\n\n## Get Embedded Property Lists\n\nAfter using these macros, you can get their contents by calling\n[`get_info_plist`] or [`get_launchd_plist`] from anywhere in your program.\n\nWe can verify that the result is correct by checking it against reading the\nappropriate file at runtime:\n\n```rust\nembed_plist::embed_info_plist!(\"Info.plist\");\n\nlet embedded_plist = embed_plist::get_info_plist();\nlet read_plist = std::fs::read(\"Info.plist\")?;\n\nassert_eq!(embedded_plist, read_plist.as_slice());\n```\n\nIf the appropriate macro has not been called, each function creates a\ncompile-time error by failing to reference the symbol defined by that macro:\n\n```rust\n// This fails to compile:\nlet embedded_plist = embed_plist::get_info_plist();\n```\n\n## Accidental Reuse Protection\n\nOnly one copy of `Info.plist` or `launchd.plist` should exist in a binary.\nAccidentally embedding them multiple times would break tools that read these\nsections.\n\nFortunately, this library makes reuse a compile-time error! This protection\nworks even if these macros are reused in different modules.\n\n```rust\n// This fails to compile:\nembed_plist::embed_info_plist!(\"Info.plist\");\nembed_plist::embed_info_plist!(\"Info.plist\");\n```\n\nThis example produces the following error:\n\n```txt\nerror: symbol `_EMBED_INFO_PLIST` is already defined\n --\u003e src/main.rs:4:1\n  |\n4 | embed_plist::embed_info_plist!(\"Info.plist\");\n  | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n  |\n  = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)\n\nerror: aborting due to previous error\n```\n\n\u003e \u003cb\u003eWarning:\u003c/b\u003e Although the name `_EMBED_INFO_PLIST` can be seen here, you\n\u003e **should not** reference this symbol with e.g. an `extern \"C\"` block. I\n\u003e reserve the right to change this name in a SemVer-compatible update.\n\n## Implementation\n\nFiles are read using [`include_bytes!`]. This normally places data in\n`__TEXT,__const`, where immutable data is kept. However, property list data\nis expected to be in `__TEXT,__info_plist` or `__TEXT,__launchd_plist`. This\nsection will explain how I accomplish that.\n\nWe begin by reading the file from disk:\n\n```rust\nconst BYTES: \u0026[u8] = include_bytes!(\"Info.plist\");\n```\n\nA naïve approach is to do the following:\n\n```rust\n#[used] // Prevent optimizing out\n#[link_section = \"__TEXT,__info_plist\"]\nstatic PLIST: \u0026[u8] = BYTES;\n```\n\nThis doesn't work because only the slice's pointer and length that are\nplaced in `__TEXT,__info_plist`. The referenced bytes are still placed in\n`__TEXT,__const`.\n\nInstead, we need to arrive at the following:\n\n```rust\n#[used]\n#[link_section = \"__TEXT,__info_plist\"]\nstatic PLIST: [u8; N] = *BYTES;\n```\n\nWe can get `N` by using [`len`]. As of Rust 1.39, it is possible to get the\nlength of a slice within a `const`.\n\n```rust\nconst N: usize = BYTES.len();\n```\n\nThe next step is to dereference the bytes into a `[u8; N]`.\n\nThere are two approaches:\n\n1. \u003cspan id=\"approach-1\"\u003e\u003c/span\u003eCall [`include_bytes!`] again.\n\n   This is not the approach used by this library because of concerns about\n   compile performance. See the [second approach](#approach-2) for what this\n   library does.\n\n   The following is all we need:\n\n   ```rust\n   #[used]\n   #[link_section = \"__TEXT,__info_plist\"]\n   static PLIST: [u8; N] = *include_bytes!(\"Info.plist\");\n   ```\n\n   This works because `include_bytes!` actually returns a `\u0026[u8; N]`. It's\n   often used as a `\u0026[u8]` because we don't know the size when calling it.\n\n2. \u003cspan id=\"approach-2\"\u003e\u003c/span\u003eDereference our current bytes through\n   pointer casting.\n\n   This is tricker than the [first approach](#approach-1) (and somewhat\n   cursed). If you know me, then it's predictable I'd go this route.\n\n   We can get a pointer to the bytes via [`as_ptr`], which is usable in\n   `const`:\n\n   ```rust\n   const PTR: *const [u8; N] = BYTES.as_ptr() as *const [u8; N];\n   ```\n\n   Unfortunately, this pointer can't be directly dereferenced in Rust 1.39\n   (minimum supported version).\n\n   ```rust\n   // This fails to compile:\n   #[used]\n   #[link_section = \"__TEXT,__info_plist\"]\n   static PLIST: [u8; N] = unsafe { *PTR };\n   ```\n\n   Instead, we must cast the pointer to a reference.\n\n   You may want to reach for [`transmute`], which was stabilized for use in\n   `const` in Rust 1.46. However, earlier versions need to be supported, so\n   that is not an option for this library.\n\n   This bitwise cast can be accomplished with a `union`:\n\n   ```rust\n   union Transmute {\n       from: *const [u8; N],\n       into: \u0026'static [u8; N],\n   }\n\n   const REF: \u0026[u8; N] = unsafe { Transmute { from: PTR }.into };\n   ```\n\n   Finally, we can dereference our bytes:\n\n   ```rust\n   #[used]\n   #[link_section = \"__TEXT,__info_plist\"]\n   static PLIST: [u8; N] = *REF;\n   ```\n\n## License\n\nThis project is released under either:\n\n- [MIT License](https://github.com/nvzqz/embed-plist-rs/blob/master/LICENSE-MIT)\n- [Apache License (Version 2.0)](https://github.com/nvzqz/embed-plist-rs/blob/master/LICENSE-APACHE)\n\nat your choosing.\n\n[`get_info_plist`]:    https://docs.rs/embed_plist/1.2.2/embed_plist/fn.get_info_plist.html\n[`get_launchd_plist`]: https://docs.rs/embed_plist/1.2.2/embed_plist/fn.get_launchd_plist.html\n\n[@NikolaiVazquez]: https://twitter.com/NikolaiVazquez\n\n[`Cargo.toml`]: https://doc.rust-lang.org/cargo/reference/manifest.html\n[crate]:        https://crates.io/crates/embed_plist\n\n[`Info.plist`]:    https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Introduction/Introduction.html\n[`launchd.plist`]: https://developer.apple.com/library/archive/documentation/MacOSX/Conceptual/BPSystemStartup/Chapters/CreatingLaunchdJobs.html#//apple_ref/doc/uid/TP40001762-104142\n\n[`include_bytes!`]: https://doc.rust-lang.org/std/macro.include_bytes.html\n[`as_ptr`]:         https://doc.rust-lang.org/std/primitive.slice.html#method.as_ptr\n[`len`]:            https://doc.rust-lang.org/std/primitive.slice.html#method.len\n[`transmute`]:      https://doc.rust-lang.org/std/mem/fn.transmute.html\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnvzqz%2Fembed-plist-rs","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnvzqz%2Fembed-plist-rs","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnvzqz%2Fembed-plist-rs/lists"}