RustでMac内でLinux用のコードのマクロを展開したコードを取得する方法

[Rustその2 Advent Calendar 2018](https://qiita.com/advent-calendar/2018/rust2)の14日目の記事です。

mioの実装を読むという記事を書いていたのですが、記述量が多くなって来たので、Mac内でLinux用のコードのマクロを展開したコードを取得する方法という内容だけ別記事に切り出しました。

まず、mioはRustの低レイヤーな非同期ioライブラリなのですが、内部はLinuxならepoll、Mac(BSD)ならkqueueを使うという実装になっています。(Windowsは...)


[epoll.rs](https://github.com/carllerche/mio/blob/v0.6.16/src/sys/unix/epoll.rs)に`dlsym!(fn epoll_create1(c_int) -> c_int);`というマクロを使っている所があります。

```rust
impl Selector {
    pub fn new() -> io::Result<Selector> {
        let epfd = unsafe {
            // Emulate `epoll_create` by using `epoll_create1` if it's available
            // and otherwise falling back to `epoll_create` followed by a call to
            // set the CLOEXEC flag.
            dlsym!(fn epoll_create1(c_int) -> c_int);

            match epoll_create1.get() {
                Some(epoll_create1_fn) => {
                    cvt(epoll_create1_fn(libc::EPOLL_CLOEXEC))?
                }
                None => {
                    let fd = cvt(libc::epoll_create(1024))?;
                    drop(set_cloexec(fd));
                    fd
                }
            }
        };
        ...
    }
    ...
}
```

マクロは[dlsym.rs](https://github.com/carllerche/mio/blob/v0.6.16/src/sys/unix/dlsym.rs)内に下記のものがあります。

```rust
macro_rules! dlsym {
    (fn $name:ident($($t:ty),*) -> $ret:ty) => (
        #[allow(bad_style)]
        static $name: ::sys::unix::dlsym::DlSym<unsafe extern fn($($t),*) -> $ret> =
            ::sys::unix::dlsym::DlSym {
                name: concat!(stringify!($name), "\0"),
                addr: ::std::sync::atomic::ATOMIC_USIZE_INIT,
                _marker: ::std::marker::PhantomData,
            };
    )
}
```
`<unsafe extern fn($($t),*) -> $ret>`は文法的に
`<unsafe extern "C" fn ... -> ...>` となると思ったんだけど、マクロ内には `"C"` が無いんだよな...と思い、これを展開したものを見たかったので調べたら[cargo expand](https://github.com/dtolnay/cargo-expand)でマクロを展開出来ることを知りました。
ただ、上のロジックはMac(BSD)では通らないので、 単純に`cargo expand` しただけではみることが出来ません...
なので、　Linux用にコンパイルする必要があります。幸いRustはクロスコンパイル出来きます。
いける!と思ったのですが、rustupのデフォルトを`nightly`にしないと上手く動かず、半日潰しました...

``` sh
# rustupを事前にインストールしdefaultをnightlyにする
# クロスコンパイル用のmuslをインストール
$ brew install FiloSottile/musl-cross/musl-cross
# x86_64-unknown-linux-muslのコンパイル環境をセットアップ
$ rustup target add x86_64-unknown-linux-musl
# linux用のマクロを展開したソースコードを取得
$ cargo expand --release --target=x86_64-unknown-linux-musl
```

展開した結果が下記になります。

```rust
impl Selector {
    pub fn new() -> io::Result<Selector> {
        let epfd = unsafe {
            #[allow(bad_style)]
            static epoll_create1: ::sys::unix::dlsym::DlSym<unsafe extern "C" fn(c_int) -> c_int> =
            ::sys::unix::dlsym::DlSym{
                name:"epoll_create1\u{0}",
                addr: ::std::sync::atomic::ATOMIC_USIZE_INIT,
                _marker: ::std::marker::PhantomData,
            };

            match epoll_create1.get() {
                Some(epoll_create1_fn) => {
                    cvt(epoll_create1_fn(libc::EPOLL_CLOEXEC))?
                }
                None => {
                    let fd = cvt(libc::epoll_create(1024))?;
                    drop(set_cloexec(fd));
                    fd
                }
            }
        };
        ...
    }
    ...
}
```

展開してみると`<unsafe extern "C" fn(c_int) -> c_inｔ>`にちゃんとなっています...:thinking_face:
何でだろう...
そもそも、FFIを調べてみると`extern fn`でも良いけど、expand時に補ってくれているだけかも...?