#![allow(unused_imports, dead_code, unused_variables)]
use crate::compiler::args::*;
use crate::compiler::c::{
ArtifactDescriptor, CCompilerImpl, CCompilerKind, Language, ParsedArguments,
};
use crate::compiler::gcc::ArgData::*;
use crate::compiler::{gcc, write_temp_file, Cacheable, CompileCommand, CompilerArguments};
use crate::mock_command::{CommandCreator, CommandCreatorSync, RunCommand};
use crate::util::{run_input_output, OsStrExt};
use crate::{counted_array, dist};
use log::Level::Trace;
use std::ffi::OsString;
use std::fs::File;
use std::future::Future;
use std::io::{self, Write};
use std::path::{Path, PathBuf};
use std::process;
use crate::errors::*;
#[derive(Clone, Debug)]
pub struct Nvcc {
pub version: Option<String>,
}
#[async_trait]
impl CCompilerImpl for Nvcc {
fn kind(&self) -> CCompilerKind {
CCompilerKind::Nvcc
}
fn plusplus(&self) -> bool {
false
}
fn version(&self) -> Option<String> {
self.version.clone()
}
fn parse_arguments(
&self,
arguments: &[OsString],
cwd: &Path,
) -> CompilerArguments<ParsedArguments> {
gcc::parse_arguments(
arguments,
cwd,
(&gcc::ARGS[..], &ARGS[..]),
false,
self.kind(),
)
}
#[allow(clippy::too_many_arguments)]
async fn preprocess<T>(
&self,
creator: &T,
executable: &Path,
parsed_args: &ParsedArguments,
cwd: &Path,
env_vars: &[(OsString, OsString)],
may_dist: bool,
rewrite_includes_only: bool,
) -> Result<process::Output>
where
T: CommandCreatorSync,
{
let language = match parsed_args.language {
Language::C => "c",
Language::Cxx => "c++",
Language::ObjectiveC => "objective-c",
Language::ObjectiveCxx => "objective-c++",
Language::Cuda => "cu",
};
let initialize_cmd_and_args = || {
let mut command = creator.clone().new_command_sync(executable);
command.args(&parsed_args.preprocessor_args);
command.args(&parsed_args.common_args);
if parsed_args.compilation_flag == "-dc" {
command.arg("-rdc=true");
}
command.arg("-x").arg(language).arg(&parsed_args.input);
command
};
let dep_before_preprocessor = || {
let mut dep_cmd = initialize_cmd_and_args();
let mut transformed_deps = vec![];
for item in parsed_args.dependency_args.iter() {
if item == "-MD" {
transformed_deps.push(OsString::from("-M"));
} else if item == "-MMD" {
transformed_deps.push(OsString::from("-MM"));
} else {
transformed_deps.push(item.clone());
}
}
dep_cmd
.args(&transformed_deps)
.env_clear()
.envs(env_vars.iter().map(|&(ref k, ref v)| (k, v)))
.current_dir(cwd);
if log_enabled!(Trace) {
trace!("dep-gen command: {:?}", dep_cmd);
}
dep_cmd
};
trace!("preprocess");
let mut cmd = initialize_cmd_and_args();
cmd.arg("-E")
.arg("-Xcompiler=-P")
.env_clear()
.envs(env_vars.iter().map(|&(ref k, ref v)| (k, v)))
.current_dir(cwd);
if log_enabled!(Trace) {
trace!("preprocess: {:?}", cmd);
}
if !parsed_args.dependency_args.is_empty() {
let first = run_input_output(dep_before_preprocessor(), None);
let second = run_input_output(cmd, None);
let (_f, s) = futures::future::try_join(first, second).await?;
Ok(s)
} else {
run_input_output(cmd, None).await
}
}
fn generate_compile_commands(
&self,
path_transformer: &mut dist::PathTransformer,
executable: &Path,
parsed_args: &ParsedArguments,
cwd: &Path,
env_vars: &[(OsString, OsString)],
rewrite_includes_only: bool,
) -> Result<(CompileCommand, Option<dist::CompileCommand>, Cacheable)> {
gcc::generate_compile_commands(
path_transformer,
executable,
parsed_args,
cwd,
env_vars,
self.kind(),
rewrite_includes_only,
)
}
}
counted_array!(pub static ARGS: [ArgInfo<gcc::ArgData>; _] = [
take_arg!("--archive-options options", OsString, CanBeSeparated('='), PassThrough),
take_arg!("--compiler-bindir", PathBuf, CanBeSeparated('='), ExtraHashFile),
take_arg!("--compiler-options", OsString, CanBeSeparated('='), PreprocessorArgument),
flag!("--expt-extended-lambda", PreprocessorArgumentFlag),
flag!("--expt-relaxed-constexpr", PreprocessorArgumentFlag),
flag!("--extended-lambda", PreprocessorArgumentFlag),
take_arg!("--generate-code", OsString, CanBeSeparated('='), PassThrough),
take_arg!("--gpu-architecture", OsString, CanBeSeparated('='), PassThrough),
take_arg!("--gpu-code", OsString, CanBeSeparated('='), PassThrough),
take_arg!("--include-path", PathBuf, CanBeSeparated('='), PreprocessorArgumentPath),
take_arg!("--linker-options", OsString, CanBeSeparated('='), PassThrough),
take_arg!("--maxrregcount", OsString, CanBeSeparated('='), PassThrough),
flag!("--no-host-device-initializer-list", PreprocessorArgumentFlag),
take_arg!("--nvlink-options", OsString, CanBeSeparated('='), PassThrough),
take_arg!("--ptxas-options", OsString, CanBeSeparated('='), PassThrough),
take_arg!("--relocatable-device-code", OsString, CanBeSeparated('='), PreprocessorArgument),
take_arg!("--system-include", PathBuf, CanBeSeparated('='), PreprocessorArgumentPath),
take_arg!("-Xarchive", OsString, CanBeSeparated('='), PassThrough),
take_arg!("-Xcompiler", OsString, CanBeSeparated('='), PreprocessorArgument),
take_arg!("-Xlinker", OsString, CanBeSeparated('='), PassThrough),
take_arg!("-Xnvlink", OsString, CanBeSeparated('='), PassThrough),
take_arg!("-Xptxas", OsString, CanBeSeparated('='), PassThrough),
take_arg!("-arch", OsString, CanBeSeparated('='), PassThrough),
take_arg!("-ccbin", PathBuf, CanBeSeparated('='), ExtraHashFile),
take_arg!("-code", OsString, CanBeSeparated('='), PassThrough),
flag!("-dc", DoCompilation),
flag!("-expt-extended-lambda", PreprocessorArgumentFlag),
flag!("-expt-relaxed-constexpr", PreprocessorArgumentFlag),
flag!("-extended-lambda", PreprocessorArgumentFlag),
take_arg!("-gencode", OsString, CanBeSeparated('='), PassThrough),
take_arg!("-isystem", PathBuf, CanBeSeparated('='), PreprocessorArgumentPath),
take_arg!("-maxrregcount", OsString, CanBeSeparated('='), PassThrough),
flag!("-nohdinitlist", PreprocessorArgumentFlag),
flag!("-ptx", DoCompilation),
take_arg!("-rdc", OsString, CanBeSeparated('='), PreprocessorArgument),
take_arg!("-x", OsString, CanBeSeparated('='), Language),
]);
#[cfg(test)]
mod test {
use super::*;
use crate::compiler::gcc;
use crate::compiler::*;
use crate::mock_command::*;
use crate::test::utils::*;
use std::collections::HashMap;
use std::path::PathBuf;
fn parse_arguments_(arguments: Vec<String>) -> CompilerArguments<ParsedArguments> {
let arguments = arguments.iter().map(OsString::from).collect::<Vec<_>>();
Nvcc { version: None }.parse_arguments(&arguments, ".".as_ref())
}
macro_rules! parses {
( $( $s:expr ),* ) => {
match parse_arguments_(vec![ $( $s.to_string(), )* ]) {
CompilerArguments::Ok(a) => a,
o => panic!("Got unexpected parse result: {:?}", o),
}
}
}
#[test]
fn test_parse_arguments_simple_c() {
let a = parses!("-c", "foo.c", "-o", "foo.o");
assert_eq!(Some("foo.c"), a.input.to_str());
assert_eq!(Language::C, a.language);
assert_map_contains!(
a.outputs,
(
"obj",
ArtifactDescriptor {
path: "foo.o".into(),
optional: false
}
)
);
assert!(a.preprocessor_args.is_empty());
assert!(a.common_args.is_empty());
}
#[test]
fn test_parse_arguments_simple_cu() {
let a = parses!("-c", "foo.cu", "-o", "foo.o");
assert_eq!(Some("foo.cu"), a.input.to_str());
assert_eq!(Language::Cuda, a.language);
assert_map_contains!(
a.outputs,
(
"obj",
ArtifactDescriptor {
path: "foo.o".into(),
optional: false
}
)
);
assert!(a.preprocessor_args.is_empty());
assert!(a.common_args.is_empty());
}
#[test]
fn test_parse_arguments_simple_c_as_cu() {
let a = parses!("-x", "cu", "-c", "foo.c", "-o", "foo.o");
assert_eq!(Some("foo.c"), a.input.to_str());
assert_eq!(Language::Cuda, a.language);
assert_map_contains!(
a.outputs,
(
"obj",
ArtifactDescriptor {
path: "foo.o".into(),
optional: false
}
)
);
assert!(a.preprocessor_args.is_empty());
assert!(a.common_args.is_empty());
}
#[test]
fn test_parse_arguments_dc_compile_flag() {
let a = parses!("-x", "cu", "-dc", "foo.c", "-o", "foo.o");
assert_eq!(Some("foo.c"), a.input.to_str());
assert_eq!(Language::Cuda, a.language);
assert_eq!(Some("-dc"), a.compilation_flag.to_str());
assert_map_contains!(
a.outputs,
(
"obj",
ArtifactDescriptor {
path: "foo.o".into(),
optional: false
}
)
);
assert!(a.preprocessor_args.is_empty());
assert!(a.common_args.is_empty());
}
#[test]
fn test_parse_arguments_values() {
let a = parses!(
"-c",
"foo.cpp",
"-fabc",
"-I",
"include-file",
"-o",
"foo.o",
"--include-path",
"include-file",
"-isystem=/system/include/file"
);
assert_eq!(Some("foo.cpp"), a.input.to_str());
assert_eq!(Language::Cxx, a.language);
assert_map_contains!(
a.outputs,
(
"obj",
ArtifactDescriptor {
path: "foo.o".into(),
optional: false
}
)
);
assert_eq!(
ovec![
"-Iinclude-file",
"--include-path",
"include-file",
"-isystem",
"/system/include/file"
],
a.preprocessor_args
);
assert!(a.dependency_args.is_empty());
assert_eq!(ovec!["-fabc"], a.common_args);
}
#[test]
fn test_parse_md_mt_flags_cu() {
let a = parses!(
"-x", "cu", "-c", "foo.c", "-fabc", "-MD", "-MT", "foo.o", "-MF", "foo.o.d", "-o",
"foo.o"
);
assert_eq!(Some("foo.c"), a.input.to_str());
assert_eq!(Language::Cuda, a.language);
assert_eq!(Some("-c"), a.compilation_flag.to_str());
assert_map_contains!(
a.outputs,
(
"obj",
ArtifactDescriptor {
path: "foo.o".into(),
optional: false
}
)
);
assert_eq!(
ovec!["-MD", "-MF", "foo.o.d", "-MT", "foo.o"],
a.dependency_args
);
assert_eq!(ovec!["-fabc"], a.common_args);
}
#[test]
fn test_parse_generate_code_flags() {
let a = parses!(
"-x",
"cu",
"--generate-code=arch=compute_61,code=sm_61",
"-c",
"foo.c",
"-o",
"foo.o"
);
assert_eq!(Some("foo.c"), a.input.to_str());
assert_eq!(Language::Cuda, a.language);
assert_map_contains!(
a.outputs,
(
"obj",
ArtifactDescriptor {
path: "foo.o".into(),
optional: false
}
)
);
assert!(a.preprocessor_args.is_empty());
assert_eq!(
ovec!["--generate-code", "arch=compute_61,code=sm_61"],
a.common_args
);
}
#[test]
fn test_parse_pass_to_host_flags() {
let a = parses!(
"-x=cu",
"--generate-code=arch=compute_60,code=[sm_60,sm_61]",
"-Xnvlink=--suppress-stack-size-warning",
"-Xcompiler",
"-fPIC,-fno-common",
"-Xcompiler=-fvisibility=hidden",
"-Xcompiler=-Wall,-Wno-unknown-pragmas,-Wno-unused-local-typedefs",
"-Xcudafe",
"--display_error_number",
"-c",
"foo.c",
"-o",
"foo.o"
);
assert_eq!(Some("foo.c"), a.input.to_str());
assert_eq!(Language::Cuda, a.language);
assert_map_contains!(
a.outputs,
(
"obj",
ArtifactDescriptor {
path: "foo.o".into(),
optional: false
}
)
);
assert_eq!(
ovec![
"-Xcompiler",
"-fPIC,-fno-common",
"-Xcompiler",
"-fvisibility=hidden",
"-Xcompiler",
"-Wall,-Wno-unknown-pragmas,-Wno-unused-local-typedefs"
],
a.preprocessor_args
);
assert_eq!(
ovec![
"--generate-code",
"arch=compute_60,code=[sm_60,sm_61]",
"-Xnvlink",
"--suppress-stack-size-warning",
"-Xcudafe",
"--display_error_number"
],
a.common_args
);
}
#[test]
fn test_parse_no_capturing_of_xcompiler() {
let a = parses!(
"-x=cu",
"-forward-unknown-to-host-compiler",
"--expt-relaxed-constexpr",
"-Xcompiler",
"-pthread",
"-std=c++14",
"-c",
"foo.c",
"-o",
"foo.o"
);
assert_eq!(Some("foo.c"), a.input.to_str());
assert_eq!(Language::Cuda, a.language);
assert_map_contains!(
a.outputs,
(
"obj",
ArtifactDescriptor {
path: "foo.o".into(),
optional: false
}
)
);
assert_eq!(
ovec!["--expt-relaxed-constexpr", "-Xcompiler", "-pthread"],
a.preprocessor_args
);
assert_eq!(
ovec!["-forward-unknown-to-host-compiler", "-std=c++14"],
a.common_args
);
}
#[test]
fn test_parse_dlink_is_not_compilation() {
assert_eq!(
CompilerArguments::NotCompilation,
parse_arguments_(stringvec![
"-forward-unknown-to-host-compiler",
"--generate-code=arch=compute_50,code=[compute_50,sm_50,sm_52]",
"-dlink",
"main.cu.o",
"-o",
"device_link.o"
])
);
}
#[test]
fn test_parse_cant_cache_flags() {
assert_eq!(
CompilerArguments::CannotCache("-E", None),
parse_arguments_(stringvec!["-x", "cu", "-c", "foo.c", "-o", "foo.o", "-E"])
);
assert_eq!(
CompilerArguments::CannotCache("-M", None),
parse_arguments_(stringvec!["-x", "cu", "-c", "foo.c", "-o", "foo.o", "-M"])
);
}
}