Add Windows support and enable CI

This commit is contained in:
Alex Lyon
2018-03-11 05:33:21 -07:00
parent 00bbb7e750
commit 947aa07bbf
15 changed files with 770 additions and 91 deletions
+2
View File
@@ -0,0 +1,2 @@
[target.x86_64-unknown-redox]
linker = "x86_64-unknown-redox-gcc"
+150
View File
@@ -0,0 +1,150 @@
# Based on the "trust" template v0.1.2
# https://github.com/japaric/trust/tree/v0.1.2
dist: trusty
language: rust
services: docker
sudo: required
addons:
apt:
packages:
- libssl-dev
after_success: |
if [ "$TARGET" = x86_64-unknown-linux-gnu -a "$TRAVIS_RUST_VERSION" = stable ]; then
bash <(curl https://raw.githubusercontent.com/xd009642/tarpaulin/master/travis-install.sh)
cargo tarpaulin --out Xml
bash <(curl -s https://codecov.io/bash)
fi
# TODO Rust builds on stable by default, this can be
# overridden on a case by case basis down below.
env:
global:
# TODO Update this to match the name of your project.
- CRATE_NAME=platform-info
matrix:
# TODO These are all the build jobs. Adjust as necessary. Comment out what you
# don't need
include:
# Android
- env: TARGET=aarch64-linux-android DISABLE_TESTS=1
- env: TARGET=arm-linux-androideabi DISABLE_TESTS=1
- env: TARGET=armv7-linux-androideabi DISABLE_TESTS=1
- env: TARGET=i686-linux-android DISABLE_TESTS=1
- env: TARGET=x86_64-linux-android DISABLE_TESTS=1
# iOS
- env: TARGET=aarch64-apple-ios DISABLE_TESTS=1
os: osx
- env: TARGET=armv7-apple-ios DISABLE_TESTS=1
os: osx
- env: TARGET=armv7s-apple-ios DISABLE_TESTS=1
os: osx
- env: TARGET=i386-apple-ios DISABLE_TESTS=1
os: osx
- env: TARGET=x86_64-apple-ios DISABLE_TESTS=1
os: osx
# Linux
- env: TARGET=aarch64-unknown-linux-gnu
- env: TARGET=arm-unknown-linux-gnueabi
- env: TARGET=armv7-unknown-linux-gnueabihf
- env: TARGET=i686-unknown-linux-gnu
- env: TARGET=i686-unknown-linux-musl
- env: TARGET=mips-unknown-linux-gnu
- env: TARGET=mips64-unknown-linux-gnuabi64
- env: TARGET=mips64el-unknown-linux-gnuabi64
- env: TARGET=mipsel-unknown-linux-gnu
- env: TARGET=powerpc-unknown-linux-gnu
- env: TARGET=powerpc64-unknown-linux-gnu
- env: TARGET=powerpc64le-unknown-linux-gnu
- env: TARGET=s390x-unknown-linux-gnu DISABLE_TESTS=1
- env: TARGET=x86_64-unknown-linux-gnu
- env: TARGET=x86_64-unknown-linux-musl
# OSX
- env: TARGET=i686-apple-darwin
os: osx
- env: TARGET=x86_64-apple-darwin
os: osx
# *BSD
- env: TARGET=i686-unknown-freebsd DISABLE_TESTS=1
- env: TARGET=x86_64-unknown-freebsd DISABLE_TESTS=1
- env: TARGET=x86_64-unknown-netbsd DISABLE_TESTS=1
# Windows
- env: TARGET=x86_64-pc-windows-gnu
# Redox
- env: TARGET=x86_64-unknown-redox CC=x86_64-unknown-redox-gcc REDOX=1 DISABLE_TESTS=1
# Bare metal
# These targets don't support std and as such are likely not suitable for
# most crates.
# - env: TARGET=thumbv6m-none-eabi
# - env: TARGET=thumbv7em-none-eabi
# - env: TARGET=thumbv7em-none-eabihf
# - env: TARGET=thumbv7m-none-eabi
# Testing other channels
- env: TARGET=x86_64-unknown-linux-gnu
rust: nightly
- env: TARGET=x86_64-apple-darwin
os: osx
rust: nightly
before_install:
- set -e
- rustup self update
- if [ $REDOX ]; then ./.travis/redox-toolchain.sh; fi
install:
- sh ci/install.sh
- source ~/.cargo/env || true
script:
- bash ci/script.sh
after_script: set +e
before_deploy:
- sh ci/before_deploy.sh
deploy:
# TODO update `api_key.secure`
# - Create a `public_repo` GitHub token. Go to: https://github.com/settings/tokens/new
# - Encrypt it: `travis encrypt 0123456789012345678901234567890123456789
# - Paste the output down here
api_key:
secure: A9v3PIzQQ4U08OHFmDPQzNXbNHEb7YHyLXCvMF+dXFuNSvhUNlmQYykxqUf3dvikhJL2/bsZ14umm7ni7fQh0tGwJ4+lPpNzYAcweGgNXnWvjTpY6ovuRbr3gs4/srkyxp/GBDmSW5L8wFN3hKCB+Lm0YnIPB9IA2afP8a30+8VTXT9nv7pNqGny4ilN41ycr4DZi3sQoXdbruy7ClN7gsWW/GUiudBccHVIjmTapOFKLwZHODaUl/1/RDWQzh+i+17e1ivXuJPktDSrqmHPTZ15OjklnHKd6t179ry6VkGRg4R/R/YukVIqGzeaXGWAwdAQ5gE8cjGZghJLVi2jkDBJ85z8MvT+zLZLyliiuhLc/X8y7mkE1n0FKFtXXzFVt0l7V1LaEKbIbiV6XX3jsir4qgkqWjPHBZqO5mkGNFS16Dmt30/ZtEPAzXiINFXbWuWrpQ/LZ4NSto8IMrRTcoyDbAga/KYxJiNIeVuCe1E9dbytDM7K0GLtxJTul/WnnSeI6r//EFyC4bxYjyHhCXaag4q14KM+ak4rB0QgxsYzyGuh2MqyCoVj8YJLjLdKnL/SV7W7LPD40xlxvI6VCYTVi2ILHwL6vCxpukXYteX0c5IAIWkISDKu6nNBEgmCHXXPSqYSrgE5g7/QoCQHI8++nR8iKe0s7TWxZRydby8=
file_glob: true
file: $CRATE_NAME-$TRAVIS_TAG-$TARGET.*
on:
# TODO Here you can pick which targets will generate binary releases
# In this example, there are some targets that are tested using the stable
# and nightly channels. This condition makes sure there is only one release
# for such targets and that's generated using the stable channel
condition: $TRAVIS_RUST_VERSION = stable
tags: true
provider: releases
skip_cleanup: true
cache: cargo
before_cache:
# Travis can't cache files that are not readable by "others"
- chmod -R a+r $HOME/.cargo
branches:
only:
# release tags
- /^v\d+\.\d+\.\d+.*$/
- master
notifications:
email:
on_success: never
+7
View File
@@ -0,0 +1,7 @@
#!/bin/bash
rustup target add x86_64-unknown-redox
sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys AA12E97F0881517F
sudo add-apt-repository 'deb https://static.redox-os.org/toolchain/apt /'
sudo apt-get update -qq
sudo apt-get install -y x86-64-unknown-redox-gcc
+1 -1
View File
@@ -5,4 +5,4 @@ authors = ["Alex Lyon <arcterus@mail.com>"]
[dependencies]
libc = "0.2"
winapi = { version = "0.3", features = ["sysinfoapi"] }
winapi = { version = "0.3", features = ["libloaderapi", "sysinfoapi", "winbase", "winver"] }
+91
View File
@@ -0,0 +1,91 @@
# Based on the "trust" template v0.1.2
# https://github.com/japaric/trust/tree/v0.1.2
environment:
global:
# TODO This is the Rust channel that build jobs will use by default but can be
# overridden on a case by case basis down below
RUST_VERSION: stable
# TODO Update this to match the name of your project.
CRATE_NAME: platform-info
# TODO These are all the build jobs. Adjust as necessary. Comment out what you
# don't need
matrix:
# MinGW
- TARGET: i686-pc-windows-gnu
- TARGET: x86_64-pc-windows-gnu
# MSVC
- TARGET: i686-pc-windows-msvc
- TARGET: x86_64-pc-windows-msvc
# Testing other channels
- TARGET: x86_64-pc-windows-gnu
RUST_VERSION: nightly
- TARGET: x86_64-pc-windows-msvc
RUST_VERSION: nightly
install:
- ps: >-
If ($Env:TARGET -eq 'x86_64-pc-windows-gnu') {
$Env:PATH += ';C:\msys64\mingw64\bin'
} ElseIf ($Env:TARGET -eq 'i686-pc-windows-gnu') {
$Env:PATH += ';C:\msys64\mingw32\bin'
}
- curl -sSf -o rustup-init.exe https://win.rustup.rs/
- rustup-init.exe -y --default-host %TARGET% --default-toolchain %RUST_VERSION%
- set PATH=%PATH%;C:\Users\appveyor\.cargo\bin
- rustc -Vv
- cargo -V
# TODO This is the "test phase", tweak it as you see fit
test_script:
# we don't run the "test phase" when doing deploys
- if [%APPVEYOR_REPO_TAG%]==[false] (
cargo build --target %TARGET% &&
cargo build --target %TARGET% --release &&
cargo test --target %TARGET% &&
cargo test --target %TARGET% --release
)
before_deploy:
# TODO Update this to build the artifacts that matter to you
- cargo rustc --target %TARGET% --release -- -C lto
- ps: ci\before_deploy.ps1
deploy:
artifact: /.*\.zip/
# TODO update `auth_token.secure`
# - Create a `public_repo` GitHub token. Go to: https://github.com/settings/tokens/new
# - Encrypt it. Go to https://ci.appveyor.com/tools/encrypt
# - Paste the output down here
auth_token:
secure: Xv7lZVmUMWpCKov9dU4t3bl4hhHob31MqgX16HOeJzKiTbHEstWaUc8seGkvwJXT
description: ''
on:
# TODO Here you can pick which targets will generate binary releases
# In this example, there are some targets that are tested using the stable
# and nightly channels. This condition makes sure there is only one release
# for such targets and that's generated using the stable channel
RUST_VERSION: stable
appveyor_repo_tag: true
provider: GitHub
cache:
- C:\Users\appveyor\.cargo\registry
- target
branches:
only:
# Release tags
- /^v\d+\.\d+\.\d+.*$/
- master
notifications:
- provider: Email
on_build_success: false
# Building is done in the test phase, so we disable Appveyor's build phase.
build: false
+23
View File
@@ -0,0 +1,23 @@
# This script takes care of packaging the build artifacts that will go in the
# release zipfile
$SRC_DIR = $PWD.Path
$STAGE = [System.Guid]::NewGuid().ToString()
Set-Location $ENV:Temp
New-Item -Type Directory -Name $STAGE
Set-Location $STAGE
$ZIP = "$SRC_DIR\$($Env:CRATE_NAME)-$($Env:APPVEYOR_REPO_TAG_NAME)-$($Env:TARGET).zip"
# TODO Update this to package the right artifacts
Copy-Item "$SRC_DIR\target\$($Env:TARGET)\release\libplatform_info.rlib" '.\'
7z a "$ZIP" *
Push-AppveyorArtifact "$ZIP"
Remove-Item *.* -Force
Set-Location ..
Remove-Item $STAGE
Set-Location $SRC_DIR
+33
View File
@@ -0,0 +1,33 @@
# This script takes care of building your crate and packaging it for release
set -ex
main() {
local src=$(pwd) \
stage=
case $TRAVIS_OS_NAME in
linux)
stage=$(mktemp -d)
;;
osx)
stage=$(mktemp -d -t tmp)
;;
esac
test -f Cargo.lock || cargo generate-lockfile
# TODO Update this to build the artifacts that matter to you
cross rustc --target $TARGET --release -- -C lto
# TODO Update this to package the right artifacts
cp target/$TARGET/release/libplatform_info.rlib $stage/
cd $stage
tar czf $src/$CRATE_NAME-$TRAVIS_TAG-$TARGET.tar.gz *
cd $src
rm -rf $stage
}
main
+47
View File
@@ -0,0 +1,47 @@
set -ex
main() {
local target=
if [ $TRAVIS_OS_NAME = linux ]; then
target=x86_64-unknown-linux-musl
sort=sort
else
target=x86_64-apple-darwin
sort=gsort # for `sort --sort-version`, from brew's coreutils.
fi
# Builds for iOS are done on OSX, but require the specific target to be
# installed.
case $TARGET in
aarch64-apple-ios)
rustup target install aarch64-apple-ios
;;
armv7-apple-ios)
rustup target install armv7-apple-ios
;;
armv7s-apple-ios)
rustup target install armv7s-apple-ios
;;
i386-apple-ios)
rustup target install i386-apple-ios
;;
x86_64-apple-ios)
rustup target install x86_64-apple-ios
;;
esac
# This fetches latest stable release
local tag=$(git ls-remote --tags --refs --exit-code https://github.com/japaric/cross \
| cut -d/ -f3 \
| grep -E '^v[0.1.0-9.]+$' \
| $sort --version-sort \
| tail -n1)
curl -LSfs https://japaric.github.io/trust/install.sh | \
sh -s -- \
--force \
--git japaric/cross \
--tag $tag \
--target $target
}
main
+21
View File
@@ -0,0 +1,21 @@
# This script takes care of testing your crate
set -ex
# TODO This is the "test phase", tweak it as you see fit
main() {
cross build --target $TARGET
cross build --target $TARGET --release
if [ ! -z $DISABLE_TESTS ]; then
return
fi
cross test --target $TARGET
cross test --target $TARGET --release
}
# we don't run the "test phase" when doing deploys
if [ -z $TRAVIS_TAG ]; then
main
fi
+17 -10
View File
@@ -1,22 +1,29 @@
// This file is part of the uutils coreutils package.
//
// (c) Jian Zeng <anonymousknight96 AT gmail.com>
// (c) Alex Lyon <arcterus@mail.com>
//
// For the full copyright and license information, please view the LICENSE file
// that was distributed with this source code.
//
pub use platform::*;
pub use self::sys::*;
mod platform;
use std::borrow::Cow;
//pub trait Uname
#[cfg(unix)]
#[path = "unix.rs"]
mod sys;
#[cfg(windows)]
#[path = "windows.rs"]
mod sys;
#[cfg(target_os = "redox")]
#[path = "redox.rs"]
mod sys;
#[cfg(test)]
mod tests {
#[test]
fn it_works() {
assert_eq!(2 + 2, 4);
}
pub trait Uname {
fn sysname(&self) -> Cow<str>;
fn nodename(&self) -> Cow<str>;
fn release(&self) -> Cow<str>;
fn version(&self) -> Cow<str>;
fn machine(&self) -> Cow<str>;
}
-21
View File
@@ -1,21 +0,0 @@
// This file is part of the uutils coreutils package.
//
// (c) Alex Lyon <arcterus@mail.com>
//
// For the full copyright and license information, please view the LICENSE file
// that was distributed with this source code.
//
#[cfg(unix)]
pub use self::unix::*;
#[cfg(windows)]
pub use self::windows::*;
#[cfg(target_os = "redox")]
pub use self::redox::*;
#[cfg(unix)]
mod unix;
#[cfg(windows)]
mod windows;
#[cfg(target_os = "redox")]
mod redox;
-41
View File
@@ -1,41 +0,0 @@
// This file is part of the uutils coreutils package.
//
// (c) Alex Lyon <arcterus@mail.com>
//
// For the full copyright and license information, please view the LICENSE file
// that was distributed with this source code.
//
extern crate winapi;
use self::winapi::um::sysinfoapi::{SYSTEM_INFO, GetSystemInfo};
use self::winapi::um::winnt::*;
use std::mem;
use std::borrow::Cow;
use std::io;
pub struct Uname {
inner: SYSTEM_INFO
}
impl Uname {
pub fn new() -> io::Result<Uname> {
unsafe {
let mut info = mem::uninitialized();
GetSystemInfo(&mut info);
Ok(Uname { inner: info })
}
}
// FIXME: need to implement more architectures (e.g. ARM)
pub fn machine(&self) -> Cow<str> {
let arch = unsafe {
match self.inner.u.s().wProcessorArchitecture {
PROCESSOR_ARCHITECTURE_AMD64 => "x86_64",
PROCESSOR_ARCHITECTURE_INTEL => "x86",
_ => unimplemented!()
}
};
Cow::from(arch)
}
}
+13 -10
View File
@@ -6,11 +6,12 @@
// that was distributed with this source code.
//
use super::Uname;
use std::borrow::Cow;
use std::io::{self, Read};
use std::fs::File;
pub struct Uname {
pub struct PlatformInfo {
kernel_name: String,
nodename: String,
kernel_release: String,
@@ -18,8 +19,8 @@ pub struct Uname {
machine: String
}
impl Uname {
pub fn new() -> io::Result<Uname> {
impl PlatformInfo {
pub fn new() -> io::Result<Self> {
let mut inner = Box::new(String::new());
File::open("sys:uname")?.read_to_string(&mut inner)?;
@@ -32,7 +33,7 @@ impl Uname {
let machine = lines.next().unwrap();
// FIXME: don't actually duplicate the data as doing so is wasteful
Ok(Uname {
Ok(Self {
kernel_name: kernel_name.to_owned(),
nodename: nodename.to_owned(),
kernel_release: kernel_release.to_owned(),
@@ -40,24 +41,26 @@ impl Uname {
machine: machine.to_owned()
})
}
}
pub fn sysname(&self) -> Cow<str> {
impl Uname for PlatformInfo {
fn sysname(&self) -> Cow<str> {
Cow::from(self.kernel_name.as_str())
}
pub fn nodename(&self) -> Cow<str> {
fn nodename(&self) -> Cow<str> {
Cow::from(self.nodename.as_str())
}
pub fn release(&self) -> Cow<str> {
fn release(&self) -> Cow<str> {
Cow::from(self.kernel_release.as_str())
}
pub fn version(&self) -> Cow<str> {
fn version(&self) -> Cow<str> {
Cow::from(self.kernel_version.as_str())
}
pub fn machine(&self) -> Cow<str> {
fn machine(&self) -> Cow<str> {
Cow::from(self.machine.as_str())
}
}
}
+11 -8
View File
@@ -10,6 +10,7 @@
extern crate libc;
use self::libc::{uname, utsname};
use super::Uname;
use std::mem;
use std::ffi::CStr;
use std::borrow::Cow;
@@ -21,39 +22,41 @@ macro_rules! cstr2cow {
)
}
pub struct Uname {
pub struct PlatformInfo {
inner: utsname,
}
impl Uname {
impl PlatformInfo {
pub fn new() -> io::Result<Self> {
unsafe {
let mut uts: utsname = mem::uninitialized();
if uname(&mut uts) == 0 {
Ok(Uname { inner: uts })
Ok(Self { inner: uts })
} else {
Err(io::Error::last_os_error())
}
}
}
}
pub fn sysname(&self) -> Cow<str> {
impl Uname for PlatformInfo {
fn sysname(&self) -> Cow<str> {
cstr2cow!(self.inner.sysname)
}
pub fn nodename(&self) -> Cow<str> {
fn nodename(&self) -> Cow<str> {
cstr2cow!(self.inner.nodename)
}
pub fn release(&self) -> Cow<str> {
fn release(&self) -> Cow<str> {
cstr2cow!(self.inner.release)
}
pub fn version(&self) -> Cow<str> {
fn version(&self) -> Cow<str> {
cstr2cow!(self.inner.version)
}
pub fn machine(&self) -> Cow<str> {
fn machine(&self) -> Cow<str> {
cstr2cow!(self.inner.machine)
}
}
+354
View File
@@ -0,0 +1,354 @@
// This file is part of the uutils coreutils package.
//
// (c) Alex Lyon <arcterus@mail.com>
//
// For the full copyright and license information, please view the LICENSE file
// that was distributed with this source code.
//
extern crate winapi;
use self::winapi::um::libloaderapi::*;
use self::winapi::um::sysinfoapi::*;
use self::winapi::um::winbase::*;
use self::winapi::um::winnt::*;
use self::winapi::um::winver::*;
use self::winapi::shared::ntstatus::*;
use self::winapi::shared::ntdef::NTSTATUS;
use self::winapi::shared::minwindef::*;
use super::Uname;
use std::ffi::{CStr, OsStr, OsString};
use std::mem;
use std::borrow::Cow;
use std::io;
use std::iter;
use std::os::windows::ffi::{OsStrExt, OsStringExt};
use std::path::PathBuf;
use std::ptr;
#[allow(unused_variables)]
#[allow(non_snake_case)]
#[repr(C)]
struct VS_FIXEDFILEINFO {
dwSignature: DWORD,
dwStrucVersion: DWORD,
dwFileVersionMS: DWORD,
dwFileVersionLS: DWORD,
dwProductVersionMS: DWORD,
dwProductVersionLS: DWORD,
dwFileFlagsMask: DWORD,
dwFileFlags: DWORD,
dwFileOS: DWORD,
dwFileType: DWORD,
dwFileSubtype: DWORD,
dwFileDateMS: DWORD,
dwFileDateLS: DWORD,
}
pub struct PlatformInfo {
sysinfo: SYSTEM_INFO,
nodename: String,
release: String,
version: String,
}
impl PlatformInfo {
pub fn new() -> io::Result<Self> {
unsafe {
let mut sysinfo = mem::uninitialized();
GetNativeSystemInfo(&mut sysinfo);
let (version, release) = Self::version_info()?;
let nodename = Self::computer_name()?;
Ok(Self {
sysinfo: sysinfo,
nodename: nodename,
version: version,
release: release
})
}
}
fn computer_name() -> io::Result<String> {
let mut size = 0;
unsafe {
// NOTE: shouldn't need to check the error because, on error, the required size will be
// stored in the size variable
// XXX: verify that ComputerNameDnsHostname is the best option
GetComputerNameExW(ComputerNameDnsHostname, ptr::null_mut(), &mut size);
}
let mut data = Vec::with_capacity(size as usize);
unsafe {
// we subtract one from the size because the returned size includes the null terminator
data.set_len(size as usize - 1);
if GetComputerNameExW(ComputerNameDnsHostname, data.as_mut_ptr(), &mut size) != 0 {
Ok(String::from_utf16_lossy(&data))
} else {
// XXX: should this error or just return localhost?
Err(io::Error::last_os_error())
}
}
}
// NOTE: the only reason any of this has to be done is Microsoft deprecated GetVersionEx() and
// it is now basically useless for us on Windows 8.1 and Windows 10
unsafe fn version_info() -> io::Result<(String, String)> {
let dll_wide: Vec<WCHAR> = OsStr::new("ntdll.dll").encode_wide().chain(iter::once(0)).collect();
let module = GetModuleHandleW(dll_wide.as_ptr());
if !module.is_null() {
let funcname = CStr::from_bytes_with_nul_unchecked(b"RtlGetVersion\0");
let func = GetProcAddress(module, funcname.as_ptr());
if !func.is_null() {
let func: extern "stdcall" fn(*mut RTL_OSVERSIONINFOEXW) -> NTSTATUS = mem::transmute(func as *const ());
let mut osinfo: RTL_OSVERSIONINFOEXW = mem::zeroed();
osinfo.dwOSVersionInfoSize = mem::size_of::<RTL_OSVERSIONINFOEXW>() as _;
if func(&mut osinfo) == STATUS_SUCCESS {
let version = String::from_utf16_lossy(&osinfo.szCSDVersion.split(|&v| v == 0).next().unwrap());
let release = Self::determine_release(osinfo.dwMajorVersion, osinfo.dwMinorVersion, osinfo.wProductType, osinfo.wSuiteMask);
return Ok((version, release));
}
}
}
// as a last resort, try to get the relevant info by loading the version info from a system
// file (specifically Kernel32.dll)
Self::version_info_from_file()
}
fn version_info_from_file() -> io::Result<(String, String)> {
use self::winapi::um::sysinfoapi;
let pathbuf = Self::get_kernel32_path()?;
let file_info = Self::get_file_version_info(pathbuf)?;
let (major, minor) = Self::query_version_info(file_info)?;
let mut info: OSVERSIONINFOEXW = unsafe { mem::uninitialized() };
info.wSuiteMask = VER_SUITE_WH_SERVER as WORD;
info.wProductType = VER_NT_WORKSTATION;
let mask = unsafe { sysinfoapi::VerSetConditionMask(0, VER_SUITENAME, VER_EQUAL) };
let suite_mask = if unsafe { VerifyVersionInfoW(&mut info, VER_SUITENAME, mask) } != 0 {
VER_SUITE_WH_SERVER as USHORT
} else {
0
};
let mask = unsafe { sysinfoapi::VerSetConditionMask(0, VER_PRODUCT_TYPE, VER_EQUAL) };
let product_type = if unsafe { VerifyVersionInfoW(&mut info, VER_PRODUCT_TYPE, mask) } != 0 {
VER_NT_WORKSTATION
} else {
0
};
Ok((String::new(), Self::determine_release(major, minor, product_type, suite_mask)))
}
fn get_kernel32_path() -> io::Result<PathBuf> {
let file = OsStr::new("Kernel32.dll");
// the "- 1" is to account for the path separator
let buf_capacity = MAX_PATH - file.len() - 1;
let mut buffer = Vec::with_capacity(buf_capacity);
let buf_size = unsafe { GetSystemDirectoryW(buffer.as_mut_ptr(), buf_capacity as UINT) };
if buf_size >= buf_capacity as UINT || buf_size == 0 {
Err(io::Error::last_os_error())
} else {
unsafe {
buffer.set_len(buf_size as usize);
}
let mut pathbuf = PathBuf::from(OsString::from_wide(&buffer));
pathbuf.push(file);
Ok(pathbuf)
}
}
fn get_file_version_info(path: PathBuf) -> io::Result<Vec<u8>> {
let path_wide: Vec<_> = path.as_os_str().encode_wide().chain(iter::once(0)).collect();
let fver_size = unsafe { GetFileVersionInfoSizeW(path_wide.as_ptr(), ptr::null_mut()) };
if fver_size == 0 {
return Err(io::Error::last_os_error());
}
let mut buffer = Vec::with_capacity(fver_size as usize);
if unsafe { GetFileVersionInfoW(path_wide.as_ptr(), 0, fver_size, buffer.as_mut_ptr() as *mut _) } == 0 {
Err(io::Error::last_os_error())
} else {
unsafe {
buffer.set_len(fver_size as usize);
}
Ok(buffer)
}
}
fn query_version_info(buffer: Vec<u8>) -> io::Result<(ULONG, ULONG)> {
let mut block_size = 0;
let mut block = unsafe { mem::uninitialized() };
let sub_block: Vec<_> = OsStr::new("\\").encode_wide().chain(iter::once(0)).collect();
if unsafe { VerQueryValueW(buffer.as_ptr() as *const _, sub_block.as_ptr(), &mut block, &mut block_size) == 0 } {
if block_size < mem::size_of::<VS_FIXEDFILEINFO>() as UINT {
return Err(io::Error::last_os_error())
}
}
let info = unsafe { &*(block as *const VS_FIXEDFILEINFO) };
Ok((HIWORD(info.dwProductVersionMS) as _, LOWORD(info.dwProductVersionMS) as _))
}
fn determine_release(major: ULONG, minor: ULONG, product_type: UCHAR, suite_mask: USHORT) -> String {
let mut name = match major {
5 => match minor {
0 => "Windows 2000",
1 => "Windows XP",
2 if product_type == VER_NT_WORKSTATION => "Windows XP Professional x64 Edition",
2 if suite_mask as UINT == VER_SUITE_WH_SERVER => "Windows Home Server",
2 => "Windows Server 2003",
_ => ""
}
6 => match minor {
0 if product_type == VER_NT_WORKSTATION => "Windows Vista",
0 => "Windows Server 2008",
1 if product_type != VER_NT_WORKSTATION => "Windows Server 2008 R2",
1 => "Windows 7",
2 if product_type != VER_NT_WORKSTATION => "Windows Server 2012",
2 => "Windows 8",
3 if product_type != VER_NT_WORKSTATION => "Windows Server 2012 R2",
3 => "Windows 8.1",
_ => ""
}
10 => match minor {
0 if product_type != VER_NT_WORKSTATION => "Windows Server 2016",
_ => ""
}
_ => ""
};
// we're doing this down here so we don't have to copy this into multiple branches
if name.len() == 0 {
name = if product_type == VER_NT_WORKSTATION {
"Windows"
} else {
"Windows Server"
};
}
format!("{} {}.{}", name, major, minor)
}
}
impl Uname for PlatformInfo {
fn sysname(&self) -> Cow<str> {
// TODO: report if using MinGW instead of MSVC
// XXX: if Rust ever works on Windows CE and winapi has the VER_PLATFORM_WIN32_CE
// constant, we should probably check for that
Cow::from("WindowsNT")
}
fn nodename(&self) -> Cow<str> {
Cow::from(self.nodename.as_str())
}
// FIXME: definitely wrong
fn release(&self) -> Cow<str> {
Cow::from(self.release.as_str())
}
// FIXME: this is prob wrong
fn version(&self) -> Cow<str> {
Cow::from(self.version.as_str())
}
fn machine(&self) -> Cow<str> {
let arch = unsafe { self.sysinfo.u.s().wProcessorArchitecture };
let arch_str = match arch {
PROCESSOR_ARCHITECTURE_AMD64 => "x86_64",
PROCESSOR_ARCHITECTURE_INTEL => {
match self.sysinfo.wProcessorLevel {
4 => "i486",
5 => "i586",
6 => "i686",
_ => "i386"
}
}
PROCESSOR_ARCHITECTURE_IA64 => "ia64",
// FIXME: not sure if this is wrong because I think uname usually returns stuff like
// armv7l on Linux, but can't find a way to figure that out on Windows
PROCESSOR_ARCHITECTURE_ARM => "arm",
// XXX: I believe this is correct for GNU compat, but differs from LLVM? Like the ARM
// branch above, I'm not really sure about this one either
PROCESSOR_ARCHITECTURE_ARM64 => "aarch64",
PROCESSOR_ARCHITECTURE_MIPS => "mips",
PROCESSOR_ARCHITECTURE_PPC => "powerpc",
PROCESSOR_ARCHITECTURE_ALPHA | PROCESSOR_ARCHITECTURE_ALPHA64 => "alpha",
// FIXME: I don't know anything about this architecture, so this may be incorrect
PROCESSOR_ARCHITECTURE_SHX => "sh",
_ => "unknown"
};
Cow::from(arch_str)
}
}
#[test]
fn test_sysname() {
assert_eq!(PlatformInfo::new().unwrap().sysname(), "WindowsNT");
}
#[test]
fn test_machine() {
let target = if cfg!(target_arch = "x86_64") {
vec!["x86_64"]
} else if cfg!(target_arch = "x86") {
vec!["i386", "i486", "i586", "i686"]
} else if cfg!(target_arch = "arm") {
vec!["arm"]
} else if cfg!(target_arch = "aarch64") {
// NOTE: keeping both of these until the correct behavior is sorted out
vec!["arm64", "aarch64"]
} else if cfg!(target_arch = "powerpc") {
vec!["powerpc"]
} else if cfg!(target_arch = "mips") {
vec!["mips"]
} else {
// NOTE: the other architecture are currently not valid targets for Rust (in fact, I am
// almost certain some of these are not even valid targets for the Windows build)
vec!["unknown"]
};
assert!(target.contains(&&*PlatformInfo::new().unwrap().machine()));
}
// TODO: figure out a way to test these
/*
#[test]
fn test_nodename() {
let info = PlatformInfo::new().unwrap();
panic!("{}", info.nodename());
}
#[test]
fn test_version() {
let info = PlatformInfo::new().unwrap();
panic!("{}", info.version());
}
#[test]
fn test_release() {
let info = PlatformInfo::new().unwrap();
panic!("{}", info.release());
}
*/