|
|
@ -1,11 +1,13 @@ |
|
|
|
use cargo_metadata::{Message, MetadataCommand}; |
|
|
|
use cargo_metadata::{Message, MetadataCommand}; |
|
|
|
use rustc_version::{Channel, Version}; |
|
|
|
use rustc_version::{Channel, Version}; |
|
|
|
|
|
|
|
use std::io::{BufRead, BufReader}; |
|
|
|
use std::path::{Path, PathBuf}; |
|
|
|
use std::path::{Path, PathBuf}; |
|
|
|
use std::process::ExitStatus; |
|
|
|
use std::process::ExitStatus; |
|
|
|
use std::{ |
|
|
|
use std::{ |
|
|
|
env, fmt, io, |
|
|
|
env, fmt, io, |
|
|
|
process::{self, Command, Stdio}, |
|
|
|
process::{self, Command, Stdio}, |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
use tee::TeeReader; |
|
|
|
|
|
|
|
|
|
|
|
#[derive(serde_derive::Deserialize, Default)] |
|
|
|
#[derive(serde_derive::Deserialize, Default)] |
|
|
|
struct CTRConfig { |
|
|
|
struct CTRConfig { |
|
|
@ -102,9 +104,12 @@ struct CargoCommand { |
|
|
|
command: String, |
|
|
|
command: String, |
|
|
|
should_link: bool, |
|
|
|
should_link: bool, |
|
|
|
args: Vec<String>, |
|
|
|
args: Vec<String>, |
|
|
|
|
|
|
|
message_format: String, |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
impl CargoCommand { |
|
|
|
impl CargoCommand { |
|
|
|
|
|
|
|
const DEFAULT_MESSAGE_FORMAT: &'static str = "json-render-diagnostics"; |
|
|
|
|
|
|
|
|
|
|
|
fn from_args() -> Option<Self> { |
|
|
|
fn from_args() -> Option<Self> { |
|
|
|
// Skip `cargo 3ds`. `cargo-3ds` isn't supported for now
|
|
|
|
// Skip `cargo 3ds`. `cargo-3ds` isn't supported for now
|
|
|
|
let mut args = env::args().skip(2); |
|
|
|
let mut args = env::args().skip(2); |
|
|
@ -127,13 +132,43 @@ impl CargoCommand { |
|
|
|
_ => (command, false), |
|
|
|
_ => (command, false), |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let message_format = match Self::extract_message_format(&mut remaining_args) { |
|
|
|
|
|
|
|
Some(format) => { |
|
|
|
|
|
|
|
if !format.starts_with("json") { |
|
|
|
|
|
|
|
eprintln!("error: non-JSON `message-format` is not supported"); |
|
|
|
|
|
|
|
std::process::exit(1); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
format |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
None => Self::DEFAULT_MESSAGE_FORMAT.to_string(), |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
Some(Self { |
|
|
|
Some(Self { |
|
|
|
command, |
|
|
|
command, |
|
|
|
should_link, |
|
|
|
should_link, |
|
|
|
args: remaining_args, |
|
|
|
args: remaining_args, |
|
|
|
|
|
|
|
message_format, |
|
|
|
}) |
|
|
|
}) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fn extract_message_format(args: &mut Vec<String>) -> Option<String> { |
|
|
|
|
|
|
|
for (i, arg) in args.iter().enumerate() { |
|
|
|
|
|
|
|
if arg.starts_with("--message-format") { |
|
|
|
|
|
|
|
return { |
|
|
|
|
|
|
|
let arg = args.remove(i); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if let Some((_, format)) = arg.split_once('=') { |
|
|
|
|
|
|
|
Some(format.to_string()) |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
Some(args.remove(i)) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
None |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
fn build_elf(&self) -> (ExitStatus, Vec<Message>) { |
|
|
|
fn build_elf(&self) -> (ExitStatus, Vec<Message>) { |
|
|
|
let rustflags = env::var("RUSTFLAGS").unwrap_or_default() |
|
|
|
let rustflags = env::var("RUSTFLAGS").unwrap_or_default() |
|
|
|
+ &format!(" -L{}/libctru/lib -lctru", env::var("DEVKITPRO").unwrap()); |
|
|
|
+ &format!(" -L{}/libctru/lib -lctru", env::var("DEVKITPRO").unwrap()); |
|
|
@ -141,24 +176,39 @@ impl CargoCommand { |
|
|
|
let cargo = env::var("CARGO").unwrap_or_else(|_| "cargo".to_string()); |
|
|
|
let cargo = env::var("CARGO").unwrap_or_else(|_| "cargo".to_string()); |
|
|
|
|
|
|
|
|
|
|
|
let mut command = Command::new(cargo) |
|
|
|
let mut command = Command::new(cargo) |
|
|
|
|
|
|
|
.env("RUSTFLAGS", rustflags) |
|
|
|
.arg(&self.command) |
|
|
|
.arg(&self.command) |
|
|
|
.arg("--message-format=json-render-diagnostics") |
|
|
|
|
|
|
|
.arg("-Z") |
|
|
|
.arg("-Z") |
|
|
|
.arg("build-std") |
|
|
|
.arg("build-std") |
|
|
|
.arg("--target") |
|
|
|
.arg("--target") |
|
|
|
.arg("armv6k-nintendo-3ds") |
|
|
|
.arg("armv6k-nintendo-3ds") |
|
|
|
|
|
|
|
.arg("--message-format") |
|
|
|
|
|
|
|
.arg(&self.message_format) |
|
|
|
.args(&self.args) |
|
|
|
.args(&self.args) |
|
|
|
.env("RUSTFLAGS", rustflags) |
|
|
|
|
|
|
|
.stdin(Stdio::inherit()) |
|
|
|
|
|
|
|
.stdout(Stdio::piped()) |
|
|
|
.stdout(Stdio::piped()) |
|
|
|
|
|
|
|
.stdin(Stdio::inherit()) |
|
|
|
.stderr(Stdio::inherit()) |
|
|
|
.stderr(Stdio::inherit()) |
|
|
|
.spawn() |
|
|
|
.spawn() |
|
|
|
.unwrap(); |
|
|
|
.unwrap(); |
|
|
|
|
|
|
|
|
|
|
|
let stdout_reader = std::io::BufReader::new(command.stdout.take().unwrap()); |
|
|
|
let command_stdout = command.stdout.take().unwrap(); |
|
|
|
|
|
|
|
|
|
|
|
let messages = Message::parse_stream(stdout_reader) |
|
|
|
let mut tee_reader; |
|
|
|
.collect::<io::Result<Vec<Message>>>() |
|
|
|
let mut stdout_reader; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let buf_reader: &mut dyn BufRead = if self.message_format == Self::DEFAULT_MESSAGE_FORMAT { |
|
|
|
|
|
|
|
stdout_reader = BufReader::new(command_stdout); |
|
|
|
|
|
|
|
&mut stdout_reader |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
// The user presumably cares about the message format, so we should
|
|
|
|
|
|
|
|
// copy stuff to stdout like they expect. We can still extract the executable
|
|
|
|
|
|
|
|
// information out of it that we need for 3dsxtool etc.
|
|
|
|
|
|
|
|
tee_reader = BufReader::new(TeeReader::new(command_stdout, io::stdout())); |
|
|
|
|
|
|
|
&mut tee_reader |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let messages = Message::parse_stream(buf_reader) |
|
|
|
|
|
|
|
.collect::<io::Result<_>>() |
|
|
|
.unwrap(); |
|
|
|
.unwrap(); |
|
|
|
|
|
|
|
|
|
|
|
(command.wait().unwrap(), messages) |
|
|
|
(command.wait().unwrap(), messages) |
|
|
@ -220,8 +270,8 @@ fn check_rust_version() { |
|
|
|
let rustc_version = rustc_version::version_meta().unwrap(); |
|
|
|
let rustc_version = rustc_version::version_meta().unwrap(); |
|
|
|
|
|
|
|
|
|
|
|
if rustc_version.channel > Channel::Nightly { |
|
|
|
if rustc_version.channel > Channel::Nightly { |
|
|
|
println!("cargo-3ds requires a nightly rustc version."); |
|
|
|
eprintln!("cargo-3ds requires a nightly rustc version."); |
|
|
|
println!( |
|
|
|
eprintln!( |
|
|
|
"Please run `rustup override set nightly` to use nightly in the \ |
|
|
|
"Please run `rustup override set nightly` to use nightly in the \ |
|
|
|
current directory." |
|
|
|
current directory." |
|
|
|
); |
|
|
|
); |
|
|
@ -239,11 +289,11 @@ fn check_rust_version() { |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
if old_version || old_commit { |
|
|
|
if old_version || old_commit { |
|
|
|
println!( |
|
|
|
eprintln!( |
|
|
|
"cargo-3ds requires rustc nightly version >= {}", |
|
|
|
"cargo-3ds requires rustc nightly version >= {}", |
|
|
|
MINIMUM_COMMIT_DATE, |
|
|
|
MINIMUM_COMMIT_DATE, |
|
|
|
); |
|
|
|
); |
|
|
|
println!("Please run `rustup update nightly` to upgrade your nightly version"); |
|
|
|
eprintln!("Please run `rustup update nightly` to upgrade your nightly version"); |
|
|
|
|
|
|
|
|
|
|
|
process::exit(1); |
|
|
|
process::exit(1); |
|
|
|
} |
|
|
|
} |
|
|
@ -345,7 +395,7 @@ fn build_3dsx(config: &CTRConfig) { |
|
|
|
// If romfs directory exists, automatically include it
|
|
|
|
// If romfs directory exists, automatically include it
|
|
|
|
let (romfs_path, is_default_romfs) = get_romfs_path(config); |
|
|
|
let (romfs_path, is_default_romfs) = get_romfs_path(config); |
|
|
|
if romfs_path.is_dir() { |
|
|
|
if romfs_path.is_dir() { |
|
|
|
println!("Adding RomFS from {}", romfs_path.display()); |
|
|
|
eprintln!("Adding RomFS from {}", romfs_path.display()); |
|
|
|
process = process.arg(format!("--romfs={}", romfs_path.to_string_lossy())); |
|
|
|
process = process.arg(format!("--romfs={}", romfs_path.to_string_lossy())); |
|
|
|
} else if !is_default_romfs { |
|
|
|
} else if !is_default_romfs { |
|
|
|
eprintln!( |
|
|
|
eprintln!( |
|
|
|