rust-lang / cc-rs

Rust library for build scripts to compile C/C++ code into a Rust library
https://docs.rs/cc
Apache License 2.0
1.87k stars 452 forks source link

How to get a more friendly error message in cc? #1260

Open mingerfan opened 2 weeks ago

mingerfan commented 2 weeks ago

I'm looking for a way to get more user-friendly error messages to help diagnose issues in my C/C++ code or the bridging code between Rust and C. How can I achieve this?

Here's a simple example of what I'm facing:

Suppose you have a C file intended for use in Rust. You would typically compile it in build.rs using the cc crate. If the C file is correct, everything works fine. However, in the example below, the error message is quite confusing. Image Image

You can see that cc simply reports "error occurred" without providing further details. In contrast, compiling the same file with gcc highlights the specific error line.Is there a way to configure the cc crate to provide detailed gcc or msvc error messages when there are issues in my C/C++ code? How can I set this up?

NobodyXu commented 2 weeks ago

Does you code call Build::cargo_output(false)?

That disables compiler stdout forwarding

mingerfan commented 2 weeks ago

Does you code call Build::cargo_output(false)?

That disables compiler stdout forwarding

No, I explicitly call cargo_output(true) in my build.rs; Image

madsmtm commented 2 weeks ago

Could you try to invoke the command that the commandline says that cc ran (the line with error occurred: Command)? Seems like cc is trying to invoke cl.exe, but you've only showed that gcc works

mingerfan commented 2 weeks ago

Could you try to invoke the command that the commandline says that cc ran (the line with error occurred: Command)? Seems like cc is trying to invoke cl.exe, but you've only showed that gcc works

When I run this code in linux, cargo shows gcc error message. Image In windows(msvc), cargo doesn't. Image

And msvc also reports error when I use it to compile the wrong c code(using command line). But the message is non-ASCII string. Image

I change toolchains to gcc, and it is ok in windows. Image Image

So, is this because cargo warning or windows powershell doesn't support non-ASCII message?

NobodyXu commented 2 weeks ago

Hmm, does cl.exe print error to stderr or stdout?

For stdout we just set it to inherit the stdin of build-script, so it should work fine.

For stderr forward, cc simply just write cargo:warning={msg}\n

https://docs.rs/cc/latest/src/cc/command_helpers.rs.html#239

There's line splitting, but it also flushes the stderr at the end, and I think rust stdlib deal with the encoding stuff, so it should be fine...

mingerfan commented 2 weeks ago

Hmm, does cl.exe print error to stderr or stdout?

For stdout we just set it to inherit the stdin of build-script, so it should work fine.

For stderr forward, cc simply just write cargo:warning={msg}\n

https://docs.rs/cc/latest/src/cc/command_helpers.rs.html#239

There's line splitting, but it also flushes the stderr at the end, and I think rust stdlib deal with the encoding stuff, so it should be fine...

In my experiment, cl.exe prints error to stdout.

# cl.exe
E:\\programme\\vs2019\\software\\VC\\Tools\\MSVC\\14.41.34120\\bin\\HostX64\\x64\\cl.exe src/test.c > stdout.log 2> stderr.log

## stdout.log
test.c
src/test.c(3): error C2143: 语法错误: 缺少“;”(在“}”的前面)

## stderr.log
E:\\programme\\vs2019\\software\\VC\\Tools\\MSVC\\14.41.34120\\bin\\HostX64\\x64\\cl.exe : 用于 x64 的 Microsoft (R) C/C++ 优化编译器 19.41.34123 版
所在位置 行:1 字符: 1
+ E:\\programme\\vs2019\\software\\VC\\Tools\\MSVC\\14.41.34120\\bin\\H ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (用于 x64 的 Micros...器 19.41.34123 版:String) [], RemoteException
    + FullyQualifiedErrorId : NativeCommandError

版权所有(C) Microsoft Corporation。保留所有权利。

while gcc prints error to stderr

# gcc
gcc src/test.c > stdoutgcc.log 2> stderrgcc.log

## stdoutgcc.log          

## stderrgcc.log
gcc : src/test.c: In function 'main':
所在位置 行:1 字符: 1
+ gcc src/test.c > stdoutgcc.log 2> stderrgcc.log
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (src/test.c: In function 'main'::String) [], RemoteException
    + FullyQualifiedErrorId : NativeCommandError

src/test.c:2:13: error: expected ';' before '}' token
    2 |     return 0
      |             ^
      |             ;
    3 | }
      | ~  
NobodyXu commented 2 weeks ago

Hmm maybe we need to forward stdout as well as stderr for the cl.exe

mingerfan commented 2 weeks ago

Hmm maybe we need to forward stdout as well as stderr for the cl.exe

After my research, I found that there are two main difficulties in supporting error capture and output for MSVC.

Firstly, UTF-8 encoding support on Windows is an experimental feature that is not enabled by default (this seems to have been the case since Windows 10 and has persisted for quite some time). As a result, many users in non-English speaking countries use PowerShell or CMD programs that are not UTF-8 encoded. The MSVC output in these terminals is also not in UTF-8 format. This makes handling stdout and stderr using Rust's String very difficult. Here is an example:

let mut s = String::new();
if let Err(e) = child.stdout.take().unwrap().read_to_string(&mut s) {
    eprintln!("Failed to read child stdout: {}", e);
} else {
    eprintln!("child stdout: {}", s);
}

This works well for UTF-8 encoded terminals. However, in non-UTF-8 encoded terminals, it enters the else branch and fails to correctly output MSVC errors.

The second difficulty is that the forward_available function in command_helpers.rs currently only supports capturing and outputting stderr. In the settings, stdout seems to be either forwarded or discarded in most cases (depending on the .cargo_output(bool) setting). To capture the output, these codes need to be modified.

Capturing MSVC's stdout and outputting it in a similar manner to stderr seems to be quite challenging, so I feel that directly forwarding the output is a simpler choice.

However, the problem then becomes, why does forwarding not work effectively on Windows? This is a very strange issue. Here is my experiment:

PS C:\Users\xs\Desktop\test_cc> cargo build
   Compiling test_cc v0.1.0 (C:\Users\xs\Desktop\test_cc)
error: failed to run custom build command for `test_cc v0.1.0 (C:\Users\xs\Desktop\test_cc)`                                                                                                                                                                                         

Caused by:
  process didn't exit successfully: `C:\Users\xs\Desktop\test_cc\target\debug\build\test_cc-fd694f9983ca928f\build-script-build` (exit code: 1)
  --- stderr

  error occurred: Command "E:\\programme\\vs2019\\software\\VC\\Tools\\MSVC\\14.41.34120\\bin\\HostX64\\x64\\cl.exe" "-nologo" "-MD" "-Z7" "-Brepro" "-W4" "-Fo./2e40c9e35e9506f4-test.o" "-c" "src/test.c" with args cl.exe did not execute successfully (status code exit code: 2).

PS C:\Users\xs\Desktop\test_cc> chcp
活动代码页: 936

//  change encoding to utf-8
PS C:\Users\xs\Desktop\test_cc> chcp 65001
Active code page: 65001

PS C:\Users\xs\Desktop\test_cc> cargo build
   Compiling test_cc v0.1.0 (C:\Users\xs\Desktop\test_cc)
error: failed to run custom build command for `test_cc v0.1.0 (C:\Users\xs\Desktop\test_cc)`

Caused by:
  process didn't exit successfully: `C:\Users\xs\Desktop\test_cc\target\debug\build\test_cc-fd694f9983ca928f\build-script-build` (exit code: 1)
  --- stdout
  OPT_LEVEL = Some(0)
  TARGET = Some(x86_64-pc-windows-msvc)
  cargo:rerun-if-env-changed=VCINSTALLDIR
  VCINSTALLDIR = None
  cargo:rerun-if-env-changed=VSTEL_MSBuildProjectFullPath
 .....
  cargo:rerun-if-env-changed=CFLAGS
  CFLAGS = None
  cargo:rerun-if-env-changed=CC_ENABLE_DEBUG_OUTPUT

  // print error here
  **_test.c
  src/test.c(3): error C2143: syntax error: missing ';' before '}'_**

  --- stderr

  error occurred: Command "E:\\programme\\vs2019\\software\\VC\\Tools\\MSVC\\14.41.34120\\bin\\HostX64\\x64\\cl.exe" "-nologo" "-MD" "-Z7" "-Brepro" "-W4" "-Fo./2e40c9e35e9506f4-test.o" "-c" "src/test.c" with args cl.exe did not execute successfully (status code exit code: 2).

So, what happened?

NobodyXu commented 2 weeks ago

So it works if it is under utf-8?

😨 cc @ChrisDenton can you take a look please?

I have no idea why forwarding fail on Windows when not using utf-8?

ChrisDenton commented 2 weeks ago

I've not looked into it yet but this is almost certainly a Cargo issue. Or at least has nothing to do with cc directly. I suspect you can reproduce the issue just by having a build script that prints non UTF-8 to stdout. It might not even be a Windows specific issue if it's only being encountered because cl.exe produces non-unicode output.

ChrisDenton commented 2 weeks ago

I've opened a Cargo issue about this. Let's see what they say.

As I mentioned in that issue, it'd be good if cc-rs could convert the output of cl.exe so that it is output as UTF-8. I believe I wrote something similar for rustc but that was link.exe. I'm not sure if cl.exe uses the same code page (old tools with a long history have some weird inconsistencies, iirc).

ChrisDenton commented 2 weeks ago

The quick workaround would be to use String::from_utf8_lossy for non-unicode output.

NobodyXu commented 2 weeks ago

Yes, perhaps forwarding stdout as well is the right thing to do in cc.

It would take some time for it to happen though, quite a few places need to be updated to have it supported in cc

mingerfan commented 1 day ago

@ChrisDenton I saw the issue in Cargo. So the current best solution is to use cargo run -vv or cargo build -vv, right? Not outputting non-UTF-8 characters seems to be a feature 🤔.