This is an archive of the discontinued LLVM Phabricator instance.

[Support] Add getFileOrSTDIN which calls a callback if stdin is a tty
AbandonedPublic

Authored by abrachet on Jun 26 2019, 11:03 PM.

Details

Summary

Adds an overload of MemoryBuffer::getFileOrSTDIN which takes a callback ( void(*)(void) ) which is called if Filename is "-" and stdin has not had a file or pipe redirected to it, and exiting with return code 1. This is useful for programs which wish to read from stdin, but where it doesn't make sense to read from a character device. These programs can pass a callback to getFileOrSTDIN to do something like print help message.

Notably, although cl::printHelpMessage has two optional booleans, it cannot be converted to void(void). It's pretty awkward considering having inline void PrintHelpMessage() { PrintHelpMessage(); } will upset the compiler, but it cannot convert to void(void). I'm not sure what the easiest way to work around this is. Perhaps getFileOrSTDIN should not call exit on its own but let the callback do it and a PrintHelpAndExit function could be added to CommandLine.h. cl::printHelpMessage is an obvious candidate for this use case.

This patch depends on D63858 and uses sys::fs::is_tty in it currently.

Diff Detail

Event Timeline

abrachet created this revision.Jun 26 2019, 11:03 PM
Herald added a project: Restricted Project. · View Herald TranscriptJun 26 2019, 11:03 PM
abrachet marked an inline comment as done.Jun 26 2019, 11:23 PM
abrachet added inline comments.
llvm/unittests/Support/MemoryBufferTest.cpp
304

This test is actually failing, it doesn't change the int, but it does print "message" according to EXPECT_EXIT, I also tried other things like calling exit from inside the lamda with different exit status, and and it picks up that it wasn't exit code 1. So the ChangeFunc's operator() is being called, but TestForChange is still equal to 0 gtest says. I hope its just a silly mistake with the lambda, but surely the compiler would have complained. I'm really at a loss for this one. Any ideas?

jhenderson added inline comments.Jul 2 2019, 2:12 AM
llvm/include/llvm/Support/MemoryBuffer.h
130

ie -> i.e.

134

Do you actually need an overload? Can this just be added as a default parameter for the other getFileOrSTDIN?

llvm/lib/Support/MemoryBuffer.cpp
164

This shoudn't be calling std::exit. Either it should always be an error if the code gets here (in which case TtyStdin return an error code), or you need to have TtyStdin return a valid MemoryBuffer. Either way, the lambda signature is wrong.

If TtyStdin is intended to handle errors, then it needs renaming to clearly indicate this. Perhaps giving it a name indicating when it is used would make it clearer.

llvm/unittests/Support/MemoryBufferTest.cpp
19

Have you tried this on Windows. I suspect you'll find it won't work as is.

abrachet updated this revision to Diff 207982.Jul 4 2019, 1:23 AM

Changed lambdas return type.

abrachet marked 4 inline comments as done.Jul 4 2019, 1:28 AM
abrachet added inline comments.
llvm/include/llvm/Support/MemoryBuffer.h
134

I think so. The alternative is to put it before FileSize, which isn't great because a caller might actually be specifying this somewhere, and at the end is worse if they need to specify FileSize and RequiresNullTerminator.

llvm/unittests/Support/MemoryBufferTest.cpp
19

I'm guessing there is more to this than dup2 being called _dup2 and defined in io.h. I'll look into testing this on Windows. Also probably I need to #define dup2 not declare it. dup2 should be defined in io.h, but Microsoft say it is deprecated. I'll change this once I have a better understanding of how Windows file redirection works.

abrachet updated this revision to Diff 207993.Jul 4 2019, 2:08 AM
abrachet marked an inline comment as done.

Use #define dup2 _dup2 instead of auto dup2 = _dup2, which would not have worked. Also temporarily comment out testing if the lambda changes a local variable.

I've made more comments, but in researching dup2 options, I stumbled on StandardInIsUserInput which might make this whole function redundant. Does it satisfy your use-case?

llvm/include/llvm/Support/MemoryBuffer.h
131

"return its result." (It could be a nullptr for example).

134

But can't the people who want to call this new overload just explicitly specify the FileSize and RequiresNullTerminator fields, and then provide the callback as the final argument? Other call sites would be completely unaffected.

llvm/lib/Support/MemoryBuffer.cpp
144

If you really feel there is a need for a second overload, this version should call the new version, with a do-nothing function of some kind.

156

How about OnUnredirectedInput?

llvm/unittests/Support/MemoryBufferTest.cpp
304

Does this need to be EXPECT_EXIT with your updated version?

I suspect the problem previously was the call of std::exit - I think EXPECT_EXIT spawns a separate process to run the test, which means that the state used in the lambda will be unrelated to that used in the code under test.

I stumbled on StandardInIsUserInput which might make this whole function redundant

Ah thanks James, can't believe I didn't find that before. Do you mean this function getFileOrSTDIN with the callback, or sys::fs::is_tty? For is_tty, yes, but I'm not sure for getFileOrSTDIN. I will ask on the list I originally started this discussion on if the tools calling if (StandardInIsUserInput) is better than doing this through getFileOrSTDIN, it might be, actually cause not all such tools will use this. Good suggestion.

Also, I found this function RedirectIO here for Windows and Unix they are both static and actually have different signatures, actually. But I bring it up because the Windows version uses DuplicateHandle. I'm guessing this test worked on Windows though or you would have said something. But I could use this if it is preferable to dup2.

I stumbled on StandardInIsUserInput which might make this whole function redundant

Ah thanks James, can't believe I didn't find that before. Do you mean this function getFileOrSTDIN with the callback, or sys::fs::is_tty? For is_tty, yes, but I'm not sure for getFileOrSTDIN. I will ask on the list I originally started this discussion on if the tools calling if (StandardInIsUserInput) is better than doing this through getFileOrSTDIN, it might be, actually cause not all such tools will use this. Good suggestion.

It really depends on your exact use-case, and it's somewhat hard to say which is the right thing to do based on that. Personally, I think you could do if (StandardInIsUserInput) in the caller, and then call getFileorSTDIN as appropriate afterwards.

Also, I found this function RedirectIO here for Windows and Unix they are both static and actually have different signatures, actually. But I bring it up because the Windows version uses DuplicateHandle. I'm guessing this test worked on Windows though or you would have said something. But I could use this if it is preferable to dup2.

It's probably preferable to use existing things rather than try to use the low-level APIs like dup2 that are not particularly portable.

I am a bit dubious about the change. Why is a callback needed? Doesn't performing operations in the call sites meet your needs?

abrachet abandoned this revision.Jul 4 2019, 11:57 PM

I am a bit dubious about the change. Why is a callback needed? Doesn't performing operations in the call sites meet your needs?

Yeah I think you two are right. It is better perform that check outside of this function when its needed.