Repository: rust-lang/cc-rs (Rust)

Issue:
Feature request: support setting asm-specific flags
Background: clang fails with an error if you pass asm options when compiling a non-asm input. For example:
```console
$ clang -c foo.S -Wa,-defsym,abc=0
(no error!)

$ clang -c foo.c -Wa,-defsym,abc=0
error: unknown argument: '-defsym'
```

It would be helpful to have a `Build::asm_flag` method for adding flags specific to compiling assembly. The flags passed in this way would only be added to the command for `.S`/`.asm` files.


Discussion:
Patch welcome. I can try to get to it this after thanksgiving if no movement has happened though.
Formally speaking, one could have used `flag_if_supported("-no-integrated-as")` in this case. And one can wonder if it would be generally more appropriate. Indeed, since the suggestion is to pass the option to multiple files(*), it implies that the symbol definition is intended to be global. In which case C is formally entitled to refer to it too :-)

(*) And if not, then it would be more appropriate to split the compilation into two and generate pair of libraries for rustc to link with.



Patch:
diff --git a/src/lib.rs b/src/lib.rs
index 469b0f893..486d67e0b 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -97,6 +97,7 @@ pub struct Build {
     flags_supported: Vec<String>,
     known_flag_support_status: Arc<Mutex<HashMap<String, bool>>>,
     ar_flags: Vec<String>,
+    asm_flags: Vec<String>,
     no_default_flags: bool,
     files: Vec<PathBuf>,
     cpp: bool,
@@ -299,6 +300,7 @@ impl Build {
             flags_supported: Vec::new(),
             known_flag_support_status: Arc::new(Mutex::new(HashMap::new())),
             ar_flags: Vec::new(),
+            asm_flags: Vec::new(),
             no_default_flags: false,
             files: Vec::new(),
             shared_flag: None,
@@ -434,6 +436,25 @@ impl Build {
         self
     }
 
+    /// Add a flag that will only be used with assembly files.
+    ///
+    /// The flag will be applied to input files with either a `.s` or
+    /// `.asm` extension (case insensitive).
+    ///
+    /// # Example
+    ///
+    /// ```no_run
+    /// cc::Build::new()
+    ///     .asm_flag("-Wa,-defsym,abc=1")
+    ///     .file("src/foo.S")  // The asm flag will be applied here
+    ///     .file("src/bar.c")  // The asm flag will not be applied here
+    ///     .compile("foo");
+    /// ```
+    pub fn asm_flag(&mut self, flag: &str) -> &mut Build {
+        self.asm_flags.push(flag.to_string());
+        self
+    }
+
     fn ensure_check_file(&self) -> Result<PathBuf, Error> {
         let out_dir = self.get_out_dir()?;
         let src = if self.cuda {
@@ -1318,7 +1339,7 @@ impl Build {
     }
 
     fn compile_object(&self, obj: &Object) -> Result<(), Error> {
-        let is_asm = obj.src.extension().and_then(|s| s.to_str()) == Some("asm");
+        let is_asm = is_asm(&obj.src);
         let target = self.get_target()?;
         let msvc = target.contains("msvc");
         let compiler = self.try_get_compiler()?;
@@ -1349,6 +1370,9 @@ impl Build {
         if self.cuda && self.files.len() > 1 {
             cmd.arg("--device-c");
         }
+        if is_asm {
+            cmd.args(&self.asm_flags);
+        }
         if compiler.family == (ToolFamily::Msvc { clang_cl: true }) && !is_asm {
             // #513: For `clang-cl`, separate flags/options from the input file.
             // When cross-compiling macOS -> Windows, this avoids interpreting
@@ -3471,3 +3495,15 @@ fn which(tool: &Path) -> Option<PathBuf> {
         return if check_exe(&mut exe) { Some(exe) } else { None };
     })
 }
+
+/// Check if the file's extension is either "asm" or "s", case insensitive.
+fn is_asm(file: &Path) -> bool {
+    if let Some(ext) = file.extension() {
+        if let Some(ext) = ext.to_str() {
+            let ext = ext.to_lowercase();
+            return ext == "asm" || ext == "s";
+        }
+    }
+
+    false
+}


Test patch:
diff --git a/tests/test.rs b/tests/test.rs
index 98a03e851..161abd8ab 100644
--- a/tests/test.rs
+++ b/tests/test.rs
@@ -442,3 +442,20 @@ fn msvc_no_dash_dash() {
 
     test.cmd(0).must_not_have("--");
 }
+
+// Disable this test with the parallel feature because the execution
+// order is not deterministic.
+#[cfg(not(feature = "parallel"))]
+#[test]
+fn asm_flags() {
+    let test = Test::gnu();
+    test.gcc()
+        .file("foo.c")
+        .file("x86_64.asm")
+        .file("x86_64.S")
+        .asm_flag("--abc")
+        .compile("foo");
+    test.cmd(0).must_not_have("--abc");
+    test.cmd(1).must_have("--abc");
+    test.cmd(2).must_have("--abc");
+}