src/compiler/nvhpc.rs (352 lines of code) (raw):

// Copyright 2016 Mozilla Foundation // SPDX-FileCopyrightText: Copyright (c) 2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #![allow(unused_imports, dead_code, unused_variables)] use crate::compiler::args::*; use crate::compiler::c::{ArtifactDescriptor, CCompilerImpl, CCompilerKind, ParsedArguments}; use crate::compiler::gcc::ArgData::*; use crate::compiler::{ gcc, write_temp_file, CCompileCommand, Cacheable, CompileCommand, CompilerArguments, Language, }; use crate::mock_command::{CommandCreator, CommandCreatorSync, RunCommand}; use crate::util::{run_input_output, OsStrExt}; use crate::{counted_array, dist}; use async_trait::async_trait; use fs::File; use fs_err as fs; use log::Level::Trace; use std::ffi::OsString; use std::future::Future; use std::io::{self, Write}; use std::path::{Path, PathBuf}; use std::process; use crate::errors::*; /// A unit struct on which to implement `CCompilerImpl`. #[derive(Clone, Debug)] pub struct Nvhpc { /// true iff this is nvc++. pub nvcplusplus: bool, pub version: Option<String>, } #[async_trait] impl CCompilerImpl for Nvhpc { fn kind(&self) -> CCompilerKind { CCompilerKind::Nvhpc } fn plusplus(&self) -> bool { self.nvcplusplus } fn version(&self) -> Option<String> { self.version.clone() } fn parse_arguments( &self, arguments: &[OsString], cwd: &Path, _env_vars: &[(OsString, OsString)], ) -> CompilerArguments<ParsedArguments> { gcc::parse_arguments( arguments, cwd, (&gcc::ARGS[..], &ARGS[..]), self.nvcplusplus, 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, _preprocessor_cache_mode: bool, ) -> Result<process::Output> where T: CommandCreatorSync, { let language = match parsed_args.language { Language::C => Ok("c"), Language::Cxx => Ok("c++"), Language::ObjectiveC => Ok("objective-c"), Language::ObjectiveCxx => Ok("objective-c++"), Language::Cuda => Err(anyhow!("CUDA compilation not supported by nvhpc")), _ => Err(anyhow!("PCH not supported by nvhpc")), }?; 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); command.arg("-x").arg(language).arg(&parsed_args.input); command }; let dep_before_preprocessor = || { //nvhpc doesn't support generating both the dependency information //and the preprocessor output at the same time. So if we have //need for both we need separate compiler invocations 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.to_vec()) .current_dir(cwd); if log_enabled!(Trace) { trace!("dep-gen command: {:?}", dep_cmd); } dep_cmd }; trace!("preprocess"); let mut cmd = initialize_cmd_and_args(); //NVHPC doesn't support disabling line info when outputting to console cmd.arg("-E") .env_clear() .envs(env_vars.to_vec()) .current_dir(cwd); if log_enabled!(Trace) { trace!("preprocess: {:?}", cmd); } //Need to chain the dependency generation and the preprocessor //to emulate a `proper` front end if !parsed_args.dependency_args.is_empty() { let first = run_input_output(dep_before_preprocessor(), None); let second = run_input_output(cmd, None); // TODO: If we need to chain these to emulate a frontend, shouldn't // we explicitly wait on the first one before starting the second one? // (rather than via which drives these concurrently) let (_f, s) = futures::future::try_join(first, second).await?; Ok(s) } else { run_input_output(cmd, None).await } } fn generate_compile_commands<T>( &self, path_transformer: &mut dist::PathTransformer, executable: &Path, parsed_args: &ParsedArguments, cwd: &Path, env_vars: &[(OsString, OsString)], rewrite_includes_only: bool, ) -> Result<( Box<dyn CompileCommand<T>>, Option<dist::CompileCommand>, Cacheable, )> where T: CommandCreatorSync, { gcc::generate_compile_commands( path_transformer, executable, parsed_args, cwd, env_vars, self.kind(), rewrite_includes_only, gcc::language_to_gcc_arg, ) .map(|(command, dist_command, cacheable)| { (CCompileCommand::new(command), dist_command, cacheable) }) } } counted_array!(pub static ARGS: [ArgInfo<gcc::ArgData>; _] = [ //todo: refactor show_includes into dependency_args take_arg!("--gcc-toolchain", OsString, CanBeSeparated('='), PassThrough), take_arg!("--include-path", PathBuf, CanBeSeparated, PreprocessorArgumentPath), take_arg!("--linker-options", OsString, CanBeSeparated, PassThrough), take_arg!("--system-include-path", PathBuf, CanBeSeparated, PreprocessorArgumentPath), take_arg!("-Mconcur", OsString, CanBeSeparated('='), PassThrough), flag!("-Mnostdlib", PreprocessorArgumentFlag), take_arg!("-Werror", OsString, CanBeSeparated, PreprocessorArgument), take_arg!("-Xcompiler", OsString, CanBeSeparated('='), PreprocessorArgument), take_arg!("-Xfatbinary", OsString, CanBeSeparated, PassThrough), take_arg!("-Xlinker", OsString, CanBeSeparated('='), PassThrough), take_arg!("-Xnvlink", OsString, CanBeSeparated, PassThrough), take_arg!("-Xptxas", OsString, CanBeSeparated, PassThrough), take_arg!("-acc", OsString, CanBeSeparated('='), PassThrough), flag!("-acclibs", PassThroughFlag), take_arg!("-c++", OsString, Concatenated, Standard), flag!("-c++libs", PassThroughFlag), flag!("-cuda", PreprocessorArgumentFlag), flag!("-cudaforlibs", PassThroughFlag), take_arg!("-cudalib", OsString, CanBeSeparated('='), PassThrough), flag!("-fortranlibs", PassThroughFlag), flag!("-gopt", PassThroughFlag), take_arg!("-gpu", OsString, CanBeSeparated('='), PassThrough), take_arg!("-mcmodel", OsString, CanBeSeparated('='), PassThrough), take_arg!("-mcpu", OsString, CanBeSeparated('='), PassThrough), flag!("-noswitcherror", PassThroughFlag), take_arg!("-ta", OsString, CanBeSeparated('='), PassThrough), take_arg!("-target", OsString, CanBeSeparated('='), PassThrough), take_arg!("-tp", OsString, CanBeSeparated('='), PassThrough), 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<_>>(); Nvhpc { nvcplusplus: false, 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_cxx() { let a = parses!("-c", "foo.cxx", "-o", "foo.o"); assert_eq!(Some("foo.cxx"), a.input.to_str()); assert_eq!(Language::Cxx, 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_values() { let a = parses!( "-c", "foo.cpp", "-fabc", "-I", "include-file", "-o", "foo.o", "--include-path", "include-file", "-isystem", "/system/include/file", "-gpu=ccnative", "-Werror", "an_error" ); 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", "-Werror", "an_error" ], a.preprocessor_args ); assert!(a.dependency_args.is_empty()); assert_eq!(ovec!["-fabc", "-gpu", "ccnative"], a.common_args); } #[test] fn test_parse_md_mt_flags_cxx() { let a = parses!( "-x", "c++", "-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::Cxx, 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", "c++", "-cuda", "-gpu=cc60,cc70", "-c", "foo.c", "-o", "foo.o" ); assert_eq!(Some("foo.c"), 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!["-cuda"], a.preprocessor_args); assert_eq!(ovec!["-gpu", "cc60,cc70"], a.common_args); } #[test] fn test_parse_cant_cache_flags() { assert_eq!( CompilerArguments::CannotCache("-E", None), parse_arguments_(stringvec!["-c", "foo.c", "-o", "foo.o", "-E"]) ); assert_eq!( CompilerArguments::CannotCache("-M", None), parse_arguments_(stringvec!["-c", "foo.c", "-o", "foo.o", "-M"]) ); } }