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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
use failure::{bail, format_err, Error};
use platform_info::*;
use platforms;
use platforms::target::Arch;
use serde::{Deserialize, Serialize};
use std::env;
use std::path::Path;
use std::path::PathBuf;
use std::str::FromStr;
use target_info;

/// All supported operating system
#[derive(Debug, Clone, Eq, PartialEq)]
enum OS {
    Linux,
    Windows,
    Macos,
    FreeBSD,
}

/// Decides how to handle manylinux compliance
#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)]
pub enum Manylinux {
    /// Use the manylinux1 tag and check for compliance
    Manylinux1,
    /// Use the manylinux1 tag but don't check for compliance
    Manylinux1Unchecked,
    /// Use manylinux2010 tag and check for compliance
    Manylinux2010,
    /// Use the manylinux2010 tag but don't check for compliance
    Manylinux2010Unchecked,
    /// Use the native linux tag
    Off,
}

impl FromStr for Manylinux {
    type Err = &'static str;

    fn from_str(value: &str) -> Result<Self, Self::Err> {
        match value {
            "1" => Ok(Manylinux::Manylinux1),
            "1-unchecked" => Ok(Manylinux::Manylinux1Unchecked),
            "2010" => Ok(Manylinux::Manylinux2010),
            "2010-unchecked" => Ok(Manylinux::Manylinux2010Unchecked),
            "off" => Ok(Manylinux::Off),
            _ => Err("Invalid value for the manylinux option"),
        }
    }
}

/// The part of the current platform that is relevant when building wheels and is supported
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct Target {
    os: OS,
    is_64_bit: bool,
}

impl Target {
    /// Returns the target maturin was compiled for
    pub fn current() -> Self {
        let os = match target_info::Target::os() {
            "linux" => OS::Linux,
            "windows" => OS::Windows,
            "macos" => OS::Macos,
            "freebsd" => OS::FreeBSD,
            unsupported => panic!("The platform {} is not supported", unsupported),
        };

        let is_64_bit = match target_info::Target::pointer_width() {
            "64" => true,
            "32" => false,
            unsupported => panic!("The pointer width {} is not supported ಠ_ಠ", unsupported),
        };

        Target { os, is_64_bit }
    }

    /// Uses the given target triple or tries the guess the current target by using the one used
    /// for compilation
    ///
    /// Fails if the target triple isn't supported
    pub fn from_target_triple(target_triple: Option<String>) -> Result<Self, Error> {
        let platform = if let Some(ref target_triple) = target_triple {
            platforms::find(target_triple)
                .ok_or_else(|| format_err!("Unknown target triple {}", target_triple))?
        } else {
            platforms::guess_current()
                .ok_or_else(|| format_err!("Could guess the current platform"))?
        };

        let os = match platform.target_os {
            platforms::target::OS::Linux => OS::Linux,
            platforms::target::OS::Windows => OS::Windows,
            platforms::target::OS::MacOS => OS::Macos,
            platforms::target::OS::FreeBSD => OS::FreeBSD,
            unsupported => bail!("The operating system {:?} is not supported", unsupported),
        };

        let is_64_bit = match platform.target_arch {
            Arch::X86_64 => true,
            Arch::X86 => false,
            unsupported => bail!("The architecture {:?} is not supported", unsupported),
        };

        Ok(Target { os, is_64_bit })
    }

    /// Returns whether the platform is 64 bit or 32 bit
    pub fn pointer_width(&self) -> usize {
        if self.is_64_bit {
            64
        } else {
            32
        }
    }

    /// Returns true if the current platform is linux or mac os
    pub fn is_unix(&self) -> bool {
        self.os != OS::Windows
    }

    /// Returns true if the current platform is linux
    pub fn is_linux(&self) -> bool {
        self.os == OS::Linux
    }

    /// Returns true if the current platform is freebsd
    pub fn is_freebsd(&self) -> bool {
        self.os == OS::FreeBSD
    }

    /// Returns true if the current platform is mac os
    pub fn is_macos(&self) -> bool {
        self.os == OS::Macos
    }

    /// Returns true if the current platform is windows
    pub fn is_windows(&self) -> bool {
        self.os == OS::Windows
    }

    /// Returns the platform part of the tag for the wheel name for cffi wheels
    pub fn get_platform_tag(&self, manylinux: &Manylinux) -> String {
        match (&self.os, self.is_64_bit, manylinux) {
            (&OS::Linux, true, Manylinux::Off) => "linux_x86_64".to_string(),
            (&OS::Linux, false, Manylinux::Off) => "linux_i686".to_string(),
            (&OS::Linux, true, Manylinux::Manylinux1) => "manylinux1_x86_64".to_string(),
            (&OS::Linux, true, Manylinux::Manylinux1Unchecked) => "manylinux1_x86_64".to_string(),
            (&OS::Linux, true, Manylinux::Manylinux2010) => "manylinux2010_x86_64".to_string(),
            (&OS::Linux, true, Manylinux::Manylinux2010Unchecked) => {
                "manylinux2010_x86_64".to_string()
            }
            (&OS::Linux, false, Manylinux::Manylinux1) => "manylinux1_i686".to_string(),
            (&OS::Linux, false, Manylinux::Manylinux1Unchecked) => "manylinux1_i686".to_string(),
            (&OS::Linux, false, Manylinux::Manylinux2010) => "manylinux2010_i686".to_string(),
            (&OS::Linux, false, Manylinux::Manylinux2010Unchecked) => {
                "manylinux2010_i686".to_string()
            }
            (&OS::Windows, true, _) => "win_amd64".to_string(),
            (&OS::Windows, false, _) => "win32".to_string(),
            (&OS::Macos, true, _) => "macosx_10_7_x86_64".to_string(),
            (&OS::Macos, false, _) => panic!("32-bit wheels are not supported for mac os"),
            (&OS::FreeBSD, true, _) => {
                let info = match PlatformInfo::new() {
                    Ok(info) => info,
                    Err(error) => panic!(error),
                };
                let release = info.release().replace(".", "_").replace("-", "_");
                format!("freebsd_{}_amd64", release)
            }
            (&OS::FreeBSD, false, _) => panic!("32-bit wheels are not supported for FreeBSD"),
        }
    }

    /// Returns the tags for the WHEEL file for cffi wheels
    pub fn get_py2_and_py3_tags(&self, manylinux: &Manylinux) -> Vec<String> {
        vec![
            format!("py2-none-{}", self.get_platform_tag(&manylinux)),
            format!("py3-none-{}", self.get_platform_tag(&manylinux)),
        ]
    }

    /// Returns the platform for the tag in the shared libaries file name
    pub fn get_shared_platform_tag(&self) -> &'static str {
        match self.os {
            OS::Linux => {
                if self.is_64_bit {
                    "x86_64-linux-gnu"
                } else {
                    "x86-linux-gnu"
                }
            }
            OS::Macos => "darwin",
            OS::FreeBSD => "", // according imp.get_suffixes(), there are no such
            OS::Windows => {
                if self.is_64_bit {
                    "win_amd64"
                } else {
                    "win32"
                }
            }
        }
    }

    /// Returns the path to the python executable inside a venv
    pub fn get_venv_python(&self, venv_base: impl AsRef<Path>) -> PathBuf {
        if self.is_windows() {
            venv_base.as_ref().join("Scripts").join("python.exe")
        } else {
            venv_base.as_ref().join("bin").join("python")
        }
    }

    /// Returns the directory where the binaries are stored inside a venv
    pub fn get_venv_bin_dir(&self, venv_base: impl AsRef<Path>) -> PathBuf {
        if self.is_windows() {
            venv_base.as_ref().join("Scripts")
        } else {
            venv_base.as_ref().join("bin")
        }
    }

    /// Returns the path to the python executable
    ///
    /// For windows it's always python.exe for unix it's first the venv's `python`
    /// and then `python3`
    pub fn get_python(&self) -> PathBuf {
        if self.is_windows() {
            PathBuf::from("python.exe")
        } else if env::var_os("VIRTUAL_ENV").is_some() {
            PathBuf::from("python")
        } else {
            PathBuf::from("python3")
        }
    }

    /// Returns the tags for the platform without python version
    pub fn get_universal_tags(&self, manylinux: &Manylinux) -> (String, Vec<String>) {
        let tag = format!(
            "py2.py3-none-{platform}",
            platform = self.get_platform_tag(&manylinux)
        );
        let tags = self.get_py2_and_py3_tags(&manylinux);
        (tag, tags)
    }
}