Skip to content

Hook up -emit-interface-path to a simple AST printer #18224

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions include/swift/AST/PrintOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -424,6 +424,14 @@ struct PrintOptions {
return result;
}

/// Retrieve the set of options suitable for stable textual interfaces.
///
/// This is a format that will be parsed again later, so the output must be
/// consistent and well-formed.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice!

///
/// \see swift::emitModuleInterface
static PrintOptions printTextualInterfaceFile();

static PrintOptions printModuleInterface();
static PrintOptions printTypeInterface(Type T);

Expand Down
29 changes: 27 additions & 2 deletions include/swift/Basic/FileSystem.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
Expand All @@ -13,17 +13,41 @@
#ifndef SWIFT_BASIC_FILESYSTEM_H
#define SWIFT_BASIC_FILESYSTEM_H

#include "llvm/ADT/Twine.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/MemoryBuffer.h"
#include <system_error>

namespace llvm {
class raw_pwrite_stream;
class Twine;
}

namespace clang {
namespace vfs {
class FileSystem;
}
}

namespace swift {
/// Invokes \p action with a raw_ostream that refers to a temporary file,
/// which is then renamed into place as \p outputPath when the action
/// completes.
///
/// If a temporary file cannot be created for whatever reason, \p action will
/// be invoked with a stream directly opened at \p outputPath. Otherwise, if
/// there is already a file at \p outputPath, it will not be overwritten if
/// the new contents are identical.
///
/// If the process is interrupted with a signal, any temporary file will be
/// removed.
///
/// As a special case, an output path of "-" is treated as referring to
/// stdout.
std::error_code atomicallyWritingToFile(
llvm::StringRef outputPath, bool binaryMode,
llvm::function_ref<void(llvm::raw_pwrite_stream &)> action);

/// Moves a file from \p source to \p destination, unless there is already
/// a file at \p destination that contains the same data as \p source.
///
Expand All @@ -38,6 +62,7 @@ namespace swift {
const llvm::Twine &Name, int64_t FileSize = -1,
bool RequiresNullTerminator = true, bool IsVolatile = false);
} // end namespace vfs

} // end namespace swift

#endif // SWIFT_BASIC_FILESYSTEM_H
2 changes: 1 addition & 1 deletion include/swift/Basic/SupplementaryOutputPaths.h
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ struct SupplementaryOutputPaths {
///
/// Currently only makes sense when the compiler has whole-module knowledge.
///
/// \sa ModuleOutputPath
/// \sa swift::emitModuleInterface
std::string ModuleInterfaceOutputPath;

SupplementaryOutputPaths() = default;
Expand Down
15 changes: 15 additions & 0 deletions lib/AST/ASTPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,21 @@ void PrintOptions::clearSynthesizedExtension() {
TransformContext.reset();
}

PrintOptions PrintOptions::printTextualInterfaceFile() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The name of this function sounds like it does the printing, but it only computes options. I see that it follows the practice of the other two, but still...

PrintOptions result;
result.PrintLongAttrsOnSeparateLines = true;
result.TypeDefinitions = true;
result.PrintIfConfig = false;
result.FullyQualifiedTypes = true;
result.SkipImports = true;
result.AccessFilter = AccessLevel::Public;

// FIXME: We'll need the actual default parameter expression.
result.PrintDefaultParameterPlaceholder = false;

return result;
}

TypeTransformContext::TypeTransformContext(Type T)
: BaseType(T.getPointer()) {
assert(T->mayHaveMembers());
Expand Down
141 changes: 139 additions & 2 deletions lib/Basic/FileSystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
Expand All @@ -11,9 +11,15 @@
//===----------------------------------------------------------------------===//

#include "swift/Basic/FileSystem.h"

#include "swift/Basic/LLVM.h"
#include "clang/Basic/FileManager.h"
#include "llvm/ADT/Twine.h"
#include "llvm/Support/Errc.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/Process.h"
#include "clang/Basic/FileManager.h"
#include "llvm/Support/Signals.h"

using namespace swift;

Expand All @@ -30,6 +36,137 @@ namespace {
};
} // end anonymous namespace

/// Does some simple checking to see if a temporary file can be written next to
/// \p outputPath and then renamed into place.
///
/// Helper for swift::atomicallyWritingToFile.
///
/// If the result is an error, the write won't succeed at all, and the calling
/// operation should bail out early.
static llvm::ErrorOr<bool>
canUseTemporaryForWrite(const StringRef outputPath) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like that const

namespace fs = llvm::sys::fs;

if (outputPath == "-") {
// Special case: "-" represents stdout, and LLVM's output stream APIs are
// aware of this. It doesn't make sense to use a temporary in this case.
return false;
}

fs::file_status status;
(void)fs::status(outputPath, status);
if (!fs::exists(status)) {
// Assume we'll be able to write to both a temporary file and to the final
// destination if the final destination doesn't exist yet.
return true;
}

// Fail early if we can't write to the final destination.
if (!fs::can_write(outputPath))
return llvm::make_error_code(llvm::errc::operation_not_permitted);

// Only use a temporary if the output is a regular file. This handles
// things like '-o /dev/null'
return fs::is_regular_file(status);
}

/// Attempts to open a temporary file next to \p outputPath, with the intent
/// that once the file has been written it will be renamed into place.
///
/// Helper for swift::atomicallyWritingToFile.
///
/// \param[out] openedStream On success, a stream opened for writing to the
/// temporary file that was just created.
/// \param outputPath The path to the final output file, which is used to decide
/// where to put the temporary.
/// \param openFlags Controls how the output stream will be opened.
///
/// \returns The path to the temporary file that was opened, or \c None if the
/// file couldn't be created.
static Optional<std::string>
tryToOpenTemporaryFile(Optional<llvm::raw_fd_ostream> &openedStream,
const StringRef outputPath,
const llvm::sys::fs::OpenFlags openFlags) {
namespace fs = llvm::sys::fs;

// Create a temporary file path.
// Insert a placeholder for a random suffix before the extension (if any).
// Then because some tools glob for build artifacts (such as clang's own
// GlobalModuleIndex.cpp), also append .tmp.
SmallString<128> tempPath;
const StringRef outputExtension = llvm::sys::path::extension(outputPath);
tempPath = outputPath.drop_back(outputExtension.size());
tempPath += "-%%%%%%%%";
tempPath += outputExtension;
tempPath += ".tmp";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wish we had Swift's string interpolation, sigh.


int fd;
const unsigned perms = fs::all_read | fs::all_write;
std::error_code EC = fs::createUniqueFile(tempPath, fd, tempPath, perms,
openFlags);

if (EC) {
// Ignore the specific error; the caller has to fall back to not using a
// temporary anyway.
return None;
}

openedStream.emplace(fd, /*shouldClose=*/true);
// Make sure the temporary file gets removed if we crash.
llvm::sys::RemoveFileOnSignal(tempPath);
return tempPath.str().str();
}

std::error_code swift::atomicallyWritingToFile(
const StringRef outputPath, const bool binaryMode,
const llvm::function_ref<void(llvm::raw_pwrite_stream &)> action) {
namespace fs = llvm::sys::fs;

// FIXME: This is mostly a simplified version of
// clang::CompilerInstance::createOutputFile. It would be great to share the
// implementation.
assert(!outputPath.empty());

llvm::ErrorOr<bool> canUseTemporary = canUseTemporaryForWrite(outputPath);
if (std::error_code error = canUseTemporary.getError())
return error;

Optional<std::string> temporaryPath;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like how the refactoring is going! When I see a declaration, followed by a block that computes it, I wonder if it works better as a separate function. But I'm not sure I see that factoring here would be comprehensible, even though the code wants it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah. It's almost llvm::ErrorOr<Optional<std::string>> but trying to work with that type (and trying to name the function that would be extracted) seems worse than any of the alternatives. tryToOpenTemporaryFile represents the best compromise I came up with.

{
const fs::OpenFlags openFlags = (binaryMode ? fs::F_None : fs::F_Text);

Optional<llvm::raw_fd_ostream> OS;
if (canUseTemporary.get()) {
temporaryPath = tryToOpenTemporaryFile(OS, outputPath, openFlags);

if (!temporaryPath) {
assert(!OS.hasValue());
// If we failed to create the temporary, fall back to writing to the
// file directly. This handles the corner case where we cannot write to
// the directory, but can write to the file.
}
}

if (!OS.hasValue()) {
std::error_code error;
OS.emplace(outputPath, error, openFlags);
if (error)
return error;
}

action(OS.getValue());
// In addition to scoping the use of 'OS', ending the scope here also
// ensures that it's been flushed (by destroying it).
}

if (!temporaryPath.hasValue()) {
// If we didn't use a temporary, we're done!
return std::error_code();
}

return swift::moveFileIfDifferent(temporaryPath.getValue(), outputPath);
}

std::error_code swift::moveFileIfDifferent(const llvm::Twine &source,
const llvm::Twine &destination) {
namespace fs = llvm::sys::fs;
Expand Down
1 change: 1 addition & 0 deletions lib/FrontendTool/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ add_swift_library(swiftFrontendTool STATIC
ImportedModules.cpp
ReferenceDependencies.cpp
TBD.cpp
TextualInterfaceGeneration.cpp
DEPENDS
swift-syntax-generated-headers SwiftOptions
LINK_LIBRARIES
Expand Down
Loading