Skip to content

Commit d7ab981

Browse files
committed
[flang] Refine output file generation
This patch cleans-up the file generation code in Flang's frontend driver. It improves the layering between `CompilerInstance::CreateDefaultOutputFile`, `CompilerInstance::CreateOutputFile` and their various clients. * Rename `CreateOutputFile` as `CreateOutputFileImpl` and make it private. This method is an implementation detail. * Instead of passing an `std::error_code` out parameter into `CreateOutputFileImpl`, have it return Expected<>. This is a bit shorter and idiomatic LLVM. * Make `CreateDefaultOutputFile` (which calls `CreateOutputFileImpl`) issue an error when file creation fails. The error code from `CreateOutputFileImpl` is used to generate a meaningful diagnostic message. * Remove error reporting from `PrintPreprocessedAction::ExecuteAction`. This is only for cases when output file generation fails. This is handled in `CreateDefaultOutputFile` instead (see the previous point). * Inline `AddOutputFile` into its only caller, `CreateDefaultOutputFile`. * Switch from `lvm::buffer_ostream` to `llvm::buffer_unique_ostream>` for non-seekable output streams. This simplifies the logic in the driver and was introduced for this very reason in [1] * Moke sure that the diagnostics from the prescanner when running `-E` (`PrintPreprocessedAction::ExecuteAction`) are printed before the actual output is generated. * Update comments, add test. NOTE: This patch relands [2]. As suggested by Michael Kruse in the post-commit/post-revert review, I've added the following: ``` config.errc_messages = "@LLVM_LIT_ERRC_MESSAGES@" ``` in Flang's `lit.site.cfg.py.in`. This way, `%errc_ENOENT` in output-paths.f90 gets the correct value on Windows as well as on Linux. [1] https://reviews.llvm.org/D93260 [2] fd21d1e Reviewed By: ashermancinelli Differential Revision: https://reviews.llvm.org/D108390
1 parent 18775ca commit d7ab981

File tree

6 files changed

+61
-54
lines changed

6 files changed

+61
-54
lines changed

flang/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ if (CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
7272
include(AddLLVM)
7373
include(HandleLLVMOptions)
7474
include(VersionFromVCS)
75+
include(GetErrcMessages)
7576

7677
if(FLANG_BUILD_NEW_DRIVER)
7778
include(AddClang)
@@ -136,6 +137,8 @@ if (CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
136137
"Generate build targets for the Flang unit tests."
137138
ON)
138139

140+
get_errc_messages(LLVM_LIT_ERRC_MESSAGES)
141+
139142
#Handle unittests when out-of-tree
140143
#LLVM_BUILD_MAIN_SRC_DIR - Path to llvm source when out-of-tree.
141144
set(FLANG_GTEST_AVAIL 0)

flang/include/flang/Frontend/CompilerInstance.h

Lines changed: 7 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -63,11 +63,6 @@ class CompilerInstance {
6363
: filename_(std::move(inputFilename)) {}
6464
};
6565

66-
/// Output stream that doesn't support seeking (e.g. terminal, pipe).
67-
/// This stream is normally wrapped in buffer_ostream before being passed
68-
/// to users (e.g. via CreateOutputFile).
69-
std::unique_ptr<llvm::raw_fd_ostream> nonSeekStream_;
70-
7166
/// The list of active output files.
7267
std::list<OutputFile> outputFiles_;
7368

@@ -189,38 +184,34 @@ class CompilerInstance {
189184
/// @name Output Files
190185
/// {
191186

192-
/// Add an output file onto the list of tracked output files.
193-
///
194-
/// \param outFile - The output file info.
195-
void AddOutputFile(OutputFile &&outFile);
196-
197187
/// Clear the output file list.
198188
void ClearOutputFiles(bool eraseFiles);
199189

200190
/// Create the default output file (based on the invocation's options) and
201191
/// add it to the list of tracked output files. If the name of the output
202-
/// file is not provided, it is derived from the input file.
192+
/// file is not provided, it will be derived from the input file.
203193
///
204194
/// \param binary The mode to open the file in.
205195
/// \param baseInput If the invocation contains no output file name (i.e.
206196
/// outputFile in FrontendOptions is empty), the input path
207197
/// name to use for deriving the output path.
208198
/// \param extension The extension to use for output names derived from
209199
/// \p baseInput.
210-
/// \return ostream for the output file or nullptr on error.
200+
/// \return Null on error, ostream for the output file otherwise
211201
std::unique_ptr<llvm::raw_pwrite_stream> CreateDefaultOutputFile(
212202
bool binary = true, llvm::StringRef baseInput = "",
213203
llvm::StringRef extension = "");
214204

205+
private:
215206
/// Create a new output file
216207
///
217208
/// \param outputPath The path to the output file.
218-
/// \param error [out] On failure, the error.
219209
/// \param binary The mode to open the file in.
220-
/// \return ostream for the output file or nullptr on error.
221-
std::unique_ptr<llvm::raw_pwrite_stream> CreateOutputFile(
222-
llvm::StringRef outputPath, std::error_code &error, bool binary);
210+
/// \return Null on error, ostream for the output file otherwise
211+
llvm::Expected<std::unique_ptr<llvm::raw_pwrite_stream>> CreateOutputFileImpl(
212+
llvm::StringRef outputPath, bool binary);
223213

214+
public:
224215
/// }
225216
/// @name Construction Utility Methods
226217
/// {

flang/lib/Frontend/CompilerInstance.cpp

Lines changed: 29 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,6 @@ void CompilerInstance::set_semaOutputStream(
5151
semaOutputStream_ = ownedSemaOutputStream_.get();
5252
}
5353

54-
void CompilerInstance::AddOutputFile(OutputFile &&outFile) {
55-
outputFiles_.push_back(std::move(outFile));
56-
}
57-
5854
// Helper method to generate the path of the output file. The following logic
5955
// applies:
6056
// 1. If the user specifies the output file via `-o`, then use that (i.e.
@@ -84,48 +80,52 @@ static std::string GetOutputFilePath(llvm::StringRef outputFilename,
8480
std::unique_ptr<llvm::raw_pwrite_stream>
8581
CompilerInstance::CreateDefaultOutputFile(
8682
bool binary, llvm::StringRef baseName, llvm::StringRef extension) {
87-
std::string outputPathName;
88-
std::error_code ec;
8983

9084
// Get the path of the output file
9185
std::string outputFilePath =
9286
GetOutputFilePath(frontendOpts().outputFile, baseName, extension);
9387

9488
// Create the output file
95-
std::unique_ptr<llvm::raw_pwrite_stream> os =
96-
CreateOutputFile(outputFilePath, ec, binary);
97-
98-
// Add the file to the list of tracked output files (provided it was created
99-
// successfully)
100-
if (os)
101-
AddOutputFile(OutputFile(outputPathName));
89+
llvm::Expected<std::unique_ptr<llvm::raw_pwrite_stream>> os =
90+
CreateOutputFileImpl(outputFilePath, binary);
91+
92+
// If successful, add the file to the list of tracked output files and
93+
// return.
94+
if (os) {
95+
outputFiles_.emplace_back(OutputFile(outputFilePath));
96+
return std::move(*os);
97+
}
10298

103-
return os;
99+
// If unsuccessful, issue an error and return Null
100+
unsigned DiagID = diagnostics().getCustomDiagID(
101+
clang::DiagnosticsEngine::Error, "unable to open output file '%0': '%1'");
102+
diagnostics().Report(DiagID)
103+
<< outputFilePath << llvm::errorToErrorCode(os.takeError()).message();
104+
return nullptr;
104105
}
105106

106-
std::unique_ptr<llvm::raw_pwrite_stream> CompilerInstance::CreateOutputFile(
107-
llvm::StringRef outputFilePath, std::error_code &error, bool binary) {
107+
llvm::Expected<std::unique_ptr<llvm::raw_pwrite_stream>>
108+
CompilerInstance::CreateOutputFileImpl(
109+
llvm::StringRef outputFilePath, bool binary) {
108110

109111
// Creates the file descriptor for the output file
110112
std::unique_ptr<llvm::raw_fd_ostream> os;
111-
std::string osFile;
112-
if (!os) {
113-
osFile = outputFilePath;
114-
os.reset(new llvm::raw_fd_ostream(osFile, error,
115-
(binary ? llvm::sys::fs::OF_None : llvm::sys::fs::OF_TextWithCRLF)));
116-
if (error)
117-
return nullptr;
113+
114+
std::error_code error;
115+
os.reset(new llvm::raw_fd_ostream(outputFilePath, error,
116+
(binary ? llvm::sys::fs::OF_None : llvm::sys::fs::OF_TextWithCRLF)));
117+
if (error) {
118+
return llvm::errorCodeToError(error);
118119
}
119120

120-
// Return the stream corresponding to the output file.
121-
// For non-seekable streams, wrap it in llvm::buffer_ostream first.
121+
// For seekable streams, just return the stream corresponding to the output
122+
// file.
122123
if (!binary || os->supportsSeeking())
123124
return std::move(os);
124125

125-
assert(!nonSeekStream_ && "The non-seek stream has already been set!");
126-
auto b = std::make_unique<llvm::buffer_ostream>(*os);
127-
nonSeekStream_ = std::move(os);
128-
return std::move(b);
126+
// For non-seekable streams, we need to wrap the output stream into something
127+
// that supports 'pwrite' and takes care of the ownership for us.
128+
return std::make_unique<llvm::buffer_unique_ostream>(std::move(os));
129129
}
130130

131131
void CompilerInstance::ClearOutputFiles(bool eraseFiles) {
@@ -134,7 +134,6 @@ void CompilerInstance::ClearOutputFiles(bool eraseFiles) {
134134
llvm::sys::fs::remove(of.filename_);
135135

136136
outputFiles_.clear();
137-
nonSeekStream_.reset();
138137
}
139138

140139
bool CompilerInstance::ExecuteAction(FrontendAction &act) {

flang/lib/Frontend/FrontendActions.cpp

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -90,23 +90,24 @@ void PrintPreprocessedAction::ExecuteAction() {
9090
outForPP, !ci.invocation().preprocessorOpts().noLineDirectives);
9191
}
9292

93+
// Print diagnostics from the prescanner
94+
ci.parsing().messages().Emit(llvm::errs(), ci.allCookedSources());
95+
9396
// If a pre-defined output stream exists, dump the preprocessed content there
9497
if (!ci.IsOutputStreamNull()) {
9598
// Send the output to the pre-defined output buffer.
9699
ci.WriteOutputStream(outForPP.str());
97100
return;
98101
}
99102

100-
// Print diagnostics from the prescanner
101-
ci.parsing().messages().Emit(llvm::errs(), ci.allCookedSources());
102-
103103
// Create a file and save the preprocessed output there
104-
if (auto os{ci.CreateDefaultOutputFile(
105-
/*Binary=*/true, /*InFile=*/GetCurrentFileOrBufferName())}) {
106-
(*os) << outForPP.str();
107-
} else {
108-
llvm::errs() << "Unable to create the output file\n";
104+
std::unique_ptr<llvm::raw_pwrite_stream> os{ci.CreateDefaultOutputFile(
105+
/*Binary=*/true, /*InFile=*/GetCurrentFileOrBufferName())};
106+
if (!os) {
107+
return;
109108
}
109+
110+
(*os) << outForPP.str();
110111
}
111112

112113
void DebugDumpProvenanceAction::ExecuteAction() {

flang/test/Driver/output-paths.f90

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
! Test the diagnostic for cases when the output file cannot be generated
2+
3+
!--------------------------
4+
! RUN lines
5+
!--------------------------
6+
! RUN: not %flang_fc1 -E -o %t.doesnotexist/somename %s 2> %t
7+
! RUN: FileCheck -check-prefix=OUTPUTFAIL -DMSG=%errc_ENOENT -input-file=%t %s
8+
9+
!-----------------------
10+
! EXPECTED OUTPUT
11+
!-----------------------
12+
! OUTPUTFAIL: error: unable to open output file '{{.*}}doesnotexist{{.}}somename': '[[MSG]]'

flang/test/lit.site.cfg.py.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import sys
44

55
config.llvm_tools_dir = "@LLVM_TOOLS_DIR@"
66
config.lit_tools_dir = "@LLVM_LIT_TOOLS_DIR@"
7+
config.errc_messages = "@LLVM_LIT_ERRC_MESSAGES@"
78
config.flang_obj_root = "@FLANG_BINARY_DIR@"
89
config.flang_src_dir = "@FLANG_SOURCE_DIR@"
910
config.flang_tools_dir = "@FLANG_TOOLS_DIR@"

0 commit comments

Comments
 (0)