aboutsummaryrefslogtreecommitdiff
path: root/build.rs
blob: 6c0112153c9c5f293f887ad367187e733a9a17bf (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
// rustc-cfg emitted by the build script:
//
// "u128"
//     Include u128 and i128 constructors for proc_macro2::Literal. Enabled on
//     any compiler 1.26+.
//
// "use_proc_macro"
//     Link to extern crate proc_macro. Available on any compiler and any target
//     except wasm32. Requires "proc-macro" Cargo cfg to be enabled (default is
//     enabled). On wasm32 we never link to proc_macro even if "proc-macro" cfg
//     is enabled.
//
// "wrap_proc_macro"
//     Wrap types from libproc_macro rather than polyfilling the whole API.
//     Enabled on rustc 1.29+ as long as procmacro2_semver_exempt is not set,
//     because we can't emulate the unstable API without emulating everything
//     else. Also enabled unconditionally on nightly, in which case the
//     procmacro2_semver_exempt surface area is implemented by using the
//     nightly-only proc_macro API.
//
// "slow_extend"
//     Fallback when `impl Extend for TokenStream` is not available. These impls
//     were added one version later than the rest of the proc_macro token API.
//     Enabled on rustc 1.29 only.
//
// "nightly"
//     Enable the Span::unwrap method. This is to support proc_macro_span and
//     proc_macro_diagnostic use on the nightly channel without requiring the
//     semver exemption opt-in. Enabled when building with nightly.
//
// "super_unstable"
//     Implement the semver exempt API in terms of the nightly-only proc_macro
//     API. Enabled when using procmacro2_semver_exempt on a nightly compiler.
//
// "span_locations"
//     Provide methods Span::start and Span::end which give the line/column
//     location of a token. Enabled by procmacro2_semver_exempt or the
//     "span-locations" Cargo cfg. This is behind a cfg because tracking
//     location inside spans is a performance hit.

use std::env;
use std::process::Command;
use std::str;

fn main() {
    println!("cargo:rerun-if-changed=build.rs");

    let target = env::var("TARGET").unwrap();

    let version = match rustc_version() {
        Some(version) => version,
        None => return,
    };

    if version.minor >= 26 {
        println!("cargo:rustc-cfg=u128");
    }

    let semver_exempt = cfg!(procmacro2_semver_exempt);
    if semver_exempt {
        // https://github.com/alexcrichton/proc-macro2/issues/147
        println!("cargo:rustc-cfg=procmacro2_semver_exempt");
    }

    if semver_exempt || cfg!(feature = "span-locations") {
        println!("cargo:rustc-cfg=span_locations");
    }

    if !enable_use_proc_macro(&target) {
        return;
    }

    println!("cargo:rustc-cfg=use_proc_macro");

    // Rust 1.29 stabilized the necessary APIs in the `proc_macro` crate
    if version.nightly || version.minor >= 29 && !semver_exempt {
        println!("cargo:rustc-cfg=wrap_proc_macro");
    }

    if version.minor == 29 {
        println!("cargo:rustc-cfg=slow_extend");
    }

    if version.nightly {
        println!("cargo:rustc-cfg=nightly");
    }

    if semver_exempt && version.nightly {
        println!("cargo:rustc-cfg=super_unstable");
    }
}

fn enable_use_proc_macro(target: &str) -> bool {
    // wasm targets don't have the `proc_macro` crate, disable this feature.
    if target.contains("wasm32") {
        return false;
    }

    // Otherwise, only enable it if our feature is actually enabled.
    cfg!(feature = "proc-macro")
}

struct RustcVersion {
    minor: u32,
    nightly: bool,
}

fn rustc_version() -> Option<RustcVersion> {
    macro_rules! otry {
        ($e:expr) => {
            match $e {
                Some(e) => e,
                None => return None,
            }
        };
    }

    let rustc = otry!(env::var_os("RUSTC"));
    let output = otry!(Command::new(rustc).arg("--version").output().ok());
    let version = otry!(str::from_utf8(&output.stdout).ok());
    let nightly = version.contains("nightly");
    let mut pieces = version.split('.');
    if pieces.next() != Some("rustc 1") {
        return None;
    }
    let minor = otry!(pieces.next());
    let minor = otry!(minor.parse().ok());

    Some(RustcVersion {
        minor: minor,
        nightly: nightly,
    })
}