Skip to content

Commit ae04c67

Browse files
authored
Merge pull request #18224 from jrose-apple/a-journey-of-a-thousand-miles
Hook up -emit-interface-path to a simple AST printer
2 parents 335e913 + 0204561 commit ae04c67

File tree

13 files changed

+348
-87
lines changed

13 files changed

+348
-87
lines changed

include/swift/AST/PrintOptions.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -424,6 +424,14 @@ struct PrintOptions {
424424
return result;
425425
}
426426

427+
/// Retrieve the set of options suitable for stable textual interfaces.
428+
///
429+
/// This is a format that will be parsed again later, so the output must be
430+
/// consistent and well-formed.
431+
///
432+
/// \see swift::emitModuleInterface
433+
static PrintOptions printTextualInterfaceFile();
434+
427435
static PrintOptions printModuleInterface();
428436
static PrintOptions printTypeInterface(Type T);
429437

include/swift/Basic/FileSystem.h

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
//
33
// This source file is part of the Swift.org open source project
44
//
5-
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
5+
// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors
66
// Licensed under Apache License v2.0 with Runtime Library Exception
77
//
88
// See https://swift.org/LICENSE.txt for license information
@@ -13,17 +13,41 @@
1313
#ifndef SWIFT_BASIC_FILESYSTEM_H
1414
#define SWIFT_BASIC_FILESYSTEM_H
1515

16-
#include "llvm/ADT/Twine.h"
16+
#include "llvm/ADT/STLExtras.h"
17+
#include "llvm/ADT/StringRef.h"
1718
#include "llvm/Support/MemoryBuffer.h"
1819
#include <system_error>
1920

21+
namespace llvm {
22+
class raw_pwrite_stream;
23+
class Twine;
24+
}
25+
2026
namespace clang {
2127
namespace vfs {
2228
class FileSystem;
2329
}
2430
}
2531

2632
namespace swift {
33+
/// Invokes \p action with a raw_ostream that refers to a temporary file,
34+
/// which is then renamed into place as \p outputPath when the action
35+
/// completes.
36+
///
37+
/// If a temporary file cannot be created for whatever reason, \p action will
38+
/// be invoked with a stream directly opened at \p outputPath. Otherwise, if
39+
/// there is already a file at \p outputPath, it will not be overwritten if
40+
/// the new contents are identical.
41+
///
42+
/// If the process is interrupted with a signal, any temporary file will be
43+
/// removed.
44+
///
45+
/// As a special case, an output path of "-" is treated as referring to
46+
/// stdout.
47+
std::error_code atomicallyWritingToFile(
48+
llvm::StringRef outputPath, bool binaryMode,
49+
llvm::function_ref<void(llvm::raw_pwrite_stream &)> action);
50+
2751
/// Moves a file from \p source to \p destination, unless there is already
2852
/// a file at \p destination that contains the same data as \p source.
2953
///
@@ -38,6 +62,7 @@ namespace swift {
3862
const llvm::Twine &Name, int64_t FileSize = -1,
3963
bool RequiresNullTerminator = true, bool IsVolatile = false);
4064
} // end namespace vfs
65+
4166
} // end namespace swift
4267

4368
#endif // SWIFT_BASIC_FILESYSTEM_H

include/swift/Basic/SupplementaryOutputPaths.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ struct SupplementaryOutputPaths {
121121
///
122122
/// Currently only makes sense when the compiler has whole-module knowledge.
123123
///
124-
/// \sa ModuleOutputPath
124+
/// \sa swift::emitModuleInterface
125125
std::string ModuleInterfaceOutputPath;
126126

127127
SupplementaryOutputPaths() = default;

lib/AST/ASTPrinter.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,21 @@ void PrintOptions::clearSynthesizedExtension() {
6464
TransformContext.reset();
6565
}
6666

67+
PrintOptions PrintOptions::printTextualInterfaceFile() {
68+
PrintOptions result;
69+
result.PrintLongAttrsOnSeparateLines = true;
70+
result.TypeDefinitions = true;
71+
result.PrintIfConfig = false;
72+
result.FullyQualifiedTypes = true;
73+
result.SkipImports = true;
74+
result.AccessFilter = AccessLevel::Public;
75+
76+
// FIXME: We'll need the actual default parameter expression.
77+
result.PrintDefaultParameterPlaceholder = false;
78+
79+
return result;
80+
}
81+
6782
TypeTransformContext::TypeTransformContext(Type T)
6883
: BaseType(T.getPointer()) {
6984
assert(T->mayHaveMembers());

lib/Basic/FileSystem.cpp

Lines changed: 139 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
//
33
// This source file is part of the Swift.org open source project
44
//
5-
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
5+
// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors
66
// Licensed under Apache License v2.0 with Runtime Library Exception
77
//
88
// See https://swift.org/LICENSE.txt for license information
@@ -11,9 +11,15 @@
1111
//===----------------------------------------------------------------------===//
1212

1313
#include "swift/Basic/FileSystem.h"
14+
15+
#include "swift/Basic/LLVM.h"
16+
#include "clang/Basic/FileManager.h"
17+
#include "llvm/ADT/Twine.h"
18+
#include "llvm/Support/Errc.h"
1419
#include "llvm/Support/FileSystem.h"
20+
#include "llvm/Support/Path.h"
1521
#include "llvm/Support/Process.h"
16-
#include "clang/Basic/FileManager.h"
22+
#include "llvm/Support/Signals.h"
1723

1824
using namespace swift;
1925

@@ -30,6 +36,137 @@ namespace {
3036
};
3137
} // end anonymous namespace
3238

39+
/// Does some simple checking to see if a temporary file can be written next to
40+
/// \p outputPath and then renamed into place.
41+
///
42+
/// Helper for swift::atomicallyWritingToFile.
43+
///
44+
/// If the result is an error, the write won't succeed at all, and the calling
45+
/// operation should bail out early.
46+
static llvm::ErrorOr<bool>
47+
canUseTemporaryForWrite(const StringRef outputPath) {
48+
namespace fs = llvm::sys::fs;
49+
50+
if (outputPath == "-") {
51+
// Special case: "-" represents stdout, and LLVM's output stream APIs are
52+
// aware of this. It doesn't make sense to use a temporary in this case.
53+
return false;
54+
}
55+
56+
fs::file_status status;
57+
(void)fs::status(outputPath, status);
58+
if (!fs::exists(status)) {
59+
// Assume we'll be able to write to both a temporary file and to the final
60+
// destination if the final destination doesn't exist yet.
61+
return true;
62+
}
63+
64+
// Fail early if we can't write to the final destination.
65+
if (!fs::can_write(outputPath))
66+
return llvm::make_error_code(llvm::errc::operation_not_permitted);
67+
68+
// Only use a temporary if the output is a regular file. This handles
69+
// things like '-o /dev/null'
70+
return fs::is_regular_file(status);
71+
}
72+
73+
/// Attempts to open a temporary file next to \p outputPath, with the intent
74+
/// that once the file has been written it will be renamed into place.
75+
///
76+
/// Helper for swift::atomicallyWritingToFile.
77+
///
78+
/// \param[out] openedStream On success, a stream opened for writing to the
79+
/// temporary file that was just created.
80+
/// \param outputPath The path to the final output file, which is used to decide
81+
/// where to put the temporary.
82+
/// \param openFlags Controls how the output stream will be opened.
83+
///
84+
/// \returns The path to the temporary file that was opened, or \c None if the
85+
/// file couldn't be created.
86+
static Optional<std::string>
87+
tryToOpenTemporaryFile(Optional<llvm::raw_fd_ostream> &openedStream,
88+
const StringRef outputPath,
89+
const llvm::sys::fs::OpenFlags openFlags) {
90+
namespace fs = llvm::sys::fs;
91+
92+
// Create a temporary file path.
93+
// Insert a placeholder for a random suffix before the extension (if any).
94+
// Then because some tools glob for build artifacts (such as clang's own
95+
// GlobalModuleIndex.cpp), also append .tmp.
96+
SmallString<128> tempPath;
97+
const StringRef outputExtension = llvm::sys::path::extension(outputPath);
98+
tempPath = outputPath.drop_back(outputExtension.size());
99+
tempPath += "-%%%%%%%%";
100+
tempPath += outputExtension;
101+
tempPath += ".tmp";
102+
103+
int fd;
104+
const unsigned perms = fs::all_read | fs::all_write;
105+
std::error_code EC = fs::createUniqueFile(tempPath, fd, tempPath, perms,
106+
openFlags);
107+
108+
if (EC) {
109+
// Ignore the specific error; the caller has to fall back to not using a
110+
// temporary anyway.
111+
return None;
112+
}
113+
114+
openedStream.emplace(fd, /*shouldClose=*/true);
115+
// Make sure the temporary file gets removed if we crash.
116+
llvm::sys::RemoveFileOnSignal(tempPath);
117+
return tempPath.str().str();
118+
}
119+
120+
std::error_code swift::atomicallyWritingToFile(
121+
const StringRef outputPath, const bool binaryMode,
122+
const llvm::function_ref<void(llvm::raw_pwrite_stream &)> action) {
123+
namespace fs = llvm::sys::fs;
124+
125+
// FIXME: This is mostly a simplified version of
126+
// clang::CompilerInstance::createOutputFile. It would be great to share the
127+
// implementation.
128+
assert(!outputPath.empty());
129+
130+
llvm::ErrorOr<bool> canUseTemporary = canUseTemporaryForWrite(outputPath);
131+
if (std::error_code error = canUseTemporary.getError())
132+
return error;
133+
134+
Optional<std::string> temporaryPath;
135+
{
136+
const fs::OpenFlags openFlags = (binaryMode ? fs::F_None : fs::F_Text);
137+
138+
Optional<llvm::raw_fd_ostream> OS;
139+
if (canUseTemporary.get()) {
140+
temporaryPath = tryToOpenTemporaryFile(OS, outputPath, openFlags);
141+
142+
if (!temporaryPath) {
143+
assert(!OS.hasValue());
144+
// If we failed to create the temporary, fall back to writing to the
145+
// file directly. This handles the corner case where we cannot write to
146+
// the directory, but can write to the file.
147+
}
148+
}
149+
150+
if (!OS.hasValue()) {
151+
std::error_code error;
152+
OS.emplace(outputPath, error, openFlags);
153+
if (error)
154+
return error;
155+
}
156+
157+
action(OS.getValue());
158+
// In addition to scoping the use of 'OS', ending the scope here also
159+
// ensures that it's been flushed (by destroying it).
160+
}
161+
162+
if (!temporaryPath.hasValue()) {
163+
// If we didn't use a temporary, we're done!
164+
return std::error_code();
165+
}
166+
167+
return swift::moveFileIfDifferent(temporaryPath.getValue(), outputPath);
168+
}
169+
33170
std::error_code swift::moveFileIfDifferent(const llvm::Twine &source,
34171
const llvm::Twine &destination) {
35172
namespace fs = llvm::sys::fs;

lib/FrontendTool/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ add_swift_library(swiftFrontendTool STATIC
33
ImportedModules.cpp
44
ReferenceDependencies.cpp
55
TBD.cpp
6+
TextualInterfaceGeneration.cpp
67
DEPENDS
78
swift-syntax-generated-headers SwiftOptions
89
LINK_LIBRARIES

0 commit comments

Comments
 (0)