#plist #info #apple #launchd #include

no-std build embed_plist

将属性列表文件(如 Info.plist)直接嵌入到可执行二进制文件中

5 个稳定版本

1.2.2 2022 年 1 月 9 日
1.2.0 2020 年 8 月 30 日
1.1.0 2020 年 8 月 30 日
1.0.0 2020 年 8 月 30 日

#43 in 构建工具

Download history 25194/week @ 2024-03-14 34190/week @ 2024-03-21 31224/week @ 2024-03-28 24249/week @ 2024-04-04 25893/week @ 2024-04-11 30718/week @ 2024-04-18 32116/week @ 2024-04-25 33211/week @ 2024-05-02 28812/week @ 2024-05-09 37757/week @ 2024-05-16 37924/week @ 2024-05-23 43775/week @ 2024-05-30 29895/week @ 2024-06-06 31616/week @ 2024-06-13 34412/week @ 2024-06-20 28675/week @ 2024-06-27

133,692 每月下载量
119 个包中使用(5 个直接使用)

MIT/Apache 许可证

37KB
89


Info.plistlaunchd.plist 文件直接嵌入到您的可执行二进制文件中,由 @NikolaiVazquez 提供!

如果您觉得这个库很有用,请考虑在 GitHub 上 赞助我。❤️

索引

  1. 动机
  2. 用法
  3. 最低支持的 Rust 版本
  4. 多目标考虑因素
  5. 获取嵌入属性列表
  6. 意外重用保护
  7. 实现
  8. 许可证

动机

某些程序需要嵌入的 Info.plistlaunchd.plist 文件才能正确工作。例如

使用 include_bytes! 手动执行此操作很麻烦。要了解原因,请参阅 实现。此库消除了执行此操作的烦恼。

用法

此库可在 crates.io 上找到,并可通过将以下内容添加到项目 Cargo.toml 中来使用

[dependencies]
embed_plist = "1.2"

...并将其添加到您的crate中的任何源文件

embed_plist::embed_info_plist!("Info.plist");

// If making a daemon:
embed_plist::embed_launchd_plist!("launchd.plist");

完成了!就这么简单。😉

有关此魔法的详细信息,请参阅 实现

最低支持的 Rust 版本

此库的目标是支持 1.39 作为其最低支持的 Rust 版本(MSRV)。

要求更高版本的 Rust 被视为破坏性更改,并将导致“主要”库版本更新。换句话说:0.1.z 将变为 0.2.0,或者 1.y.z 将变为 2.0.0

多目标考虑因素

此库仅适用于 Mach-O 二进制文件。当构建跨平台程序时,应在 #[cfg] 下方放置这些宏调用,以防止在其他目标上出现链接器错误。

#[cfg(target_os = "macos")]
embed_plist::embed_info_plist!("Info.plist");

获取嵌入属性列表

使用这些宏之后,您可以通过在程序中的任何地方调用 get_info_plistget_launchd_plist 来获取其内容。

我们可以通过在运行时读取适当的文件来验证结果是否正确

embed_plist::embed_info_plist!("Info.plist");

let embedded_plist = embed_plist::get_info_plist();
let read_plist = std::fs::read("Info.plist")?;

assert_eq!(embedded_plist, read_plist.as_slice());

如果未调用适当的宏,则每个函数都会通过无法引用该宏定义的符号来创建编译时错误

// This fails to compile:
let embedded_plist = embed_plist::get_info_plist();

意外重用保护

在二进制文件中不应存在多个 Info.plistlaunchd.plist 的副本。意外多次嵌入将破坏读取这些部分的工具。

幸运的是,此库将复用视为编译时错误!即使这些宏在不同的模块中重复使用,此保护也有效。

// This fails to compile:
embed_plist::embed_info_plist!("Info.plist");
embed_plist::embed_info_plist!("Info.plist");

此示例产生以下错误

error: symbol `_EMBED_INFO_PLIST` is already defined
 --> src/main.rs:4:1
  |
4 | embed_plist::embed_info_plist!("Info.plist");
  | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  |
  = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)

error: aborting due to previous error

警告:尽管在这里可以看到名称 _EMBED_INFO_PLIST,但您 不应该 使用例如 extern "C" 块来引用此符号。我保留在 SemVer 兼容更新中更改此名称的权利。

实现

文件读取使用 include_bytes!。这通常将数据放置在 __TEXT,__const 中,其中存储不可变数据。然而,属性列表数据应位于 __TEXT,__info_plist__TEXT,__launchd_plist 中。本节将解释我是如何实现这一点的。

我们首先从磁盘读取文件

const BYTES: &[u8] = include_bytes!("Info.plist");

一个简单的方法是执行以下操作

#[used] // Prevent optimizing out
#[link_section = "__TEXT,__info_plist"]
static PLIST: &[u8] = BYTES;

这行不通,因为只有指针和长度被放置在 __TEXT,__info_plist。引用的字节仍然放置在 __TEXT,__const 中。

相反,我们需要达到以下目标

#[used]
#[link_section = "__TEXT,__info_plist"]
static PLIST: [u8; N] = *BYTES;

我们可以通过使用 len 来获取 N。截至 Rust 1.39,可以在 const 中获取切片的长度。

const N: usize = BYTES.len();

下一步是将字节解引用到 [u8; N]

有两种方法

  1. 再次调用 include_bytes!

    这个库没有使用这种方法,因为担心编译性能。有关这个库做了什么的更多信息,请参阅第二种方法

    以下是我们需要的所有内容

    #[used]
    #[link_section = "__TEXT,__info_plist"]
    static PLIST: [u8; N] = *include_bytes!("Info.plist");
    

    这之所以有效,是因为 include_bytes! 实际上返回一个 &[u8; N]。由于我们不知道调用时的大小,它通常用作 &[u8]

  2. 通过指针转换来解引用当前的字节。

    这比 第一种方法(以及有点受诅咒)更复杂。如果你了解我,那么你可以预测我会走这条路。

    我们可以通过 as_ptr 获取字节的一个指针,这在 const 中是可用的。

    const PTR: *const [u8; N] = BYTES.as_ptr() as *const [u8; N];
    

    不幸的是,在 Rust 1.39(最低支持版本)中,这个指针不能直接解引用。

    // This fails to compile:
    #[used]
    #[link_section = "__TEXT,__info_plist"]
    static PLIST: [u8; N] = unsafe { *PTR };
    

    相反,我们必须将指针转换为一个引用。

    你可能想使用 transmute,它在 Rust 1.46 中被稳定用于 const。但是,需要支持更早的版本,所以这不是这个库的选项。

    这个位操作转换可以使用一个 union 来完成

    union Transmute {
        from: *const [u8; N],
        into: &'static [u8; N],
    }
    
    const REF: &[u8; N] = unsafe { Transmute { from: PTR }.into };
    

    最后,我们可以解引用我们的字节

    #[used]
    #[link_section = "__TEXT,__info_plist"]
    static PLIST: [u8; N] = *REF;
    

许可证

本项目可以以以下任一许可证发布

任由您选择。

无运行时依赖