Merge remote-tracking branch 'origin/master' into master-next

This commit is contained in:
swift-ci
2018-08-01 18:49:02 -07:00
13 changed files with 349 additions and 88 deletions

View File

@@ -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.
///
/// \see swift::emitModuleInterface
static PrintOptions printTextualInterfaceFile();
static PrintOptions printModuleInterface();
static PrintOptions printTypeInterface(Type T);

View File

@@ -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
@@ -13,10 +13,16 @@
#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;
@@ -24,6 +30,24 @@ namespace clang {
}
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.
///
@@ -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

View File

@@ -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;

View File

@@ -64,6 +64,21 @@ void PrintOptions::clearSynthesizedExtension() {
TransformContext.reset();
}
PrintOptions PrintOptions::printTextualInterfaceFile() {
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());

View File

@@ -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
@@ -11,9 +11,15 @@
//===----------------------------------------------------------------------===//
#include "swift/Basic/FileSystem.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Process.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 "llvm/Support/Signals.h"
using namespace swift;
@@ -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) {
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";
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;
{
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;

View File

@@ -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

View File

@@ -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
@@ -24,6 +24,7 @@
#include "ImportedModules.h"
#include "ReferenceDependencies.h"
#include "TBD.h"
#include "TextualInterfaceGeneration.h"
#include "swift/Subsystems.h"
#include "swift/AST/ASTScope.h"
@@ -62,11 +63,7 @@
#include "swift/Syntax/SyntaxNodes.h"
#include "swift/TBDGen/TBDGen.h"
// FIXME: We're just using CompilerInstance::createOutputFile.
// This API should be sunk down to LLVM.
#include "clang/Frontend/CompilerInstance.h"
#include "clang/AST/ASTContext.h"
#include "clang/APINotes/Types.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/IR/LLVMContext.h"
@@ -314,47 +311,64 @@ static bool writeSIL(SILModule &SM, const PrimarySpecificPaths &PSPs,
PSPs.OutputFilename, opts.EmitSortedSIL);
}
static bool printAsObjCIfNeeded(StringRef outputPath, ModuleDecl *M,
StringRef bridgingHeader, bool moduleIsPublic) {
using namespace llvm::sys;
/// A wrapper around swift::atomicallyWritingToFile that handles diagnosing any
/// filesystem errors and ignores empty output paths.
///
/// \returns true if there were any errors, either from the filesystem
/// operations or from \p action returning true.
static bool atomicallyWritingToTextFile(
StringRef outputPath, DiagnosticEngine &diags,
llvm::function_ref<bool(llvm::raw_pwrite_stream &)> action) {
assert(!outputPath.empty());
if (outputPath.empty())
return false;
clang::CompilerInstance Clang;
std::string tmpFilePath;
std::error_code EC;
std::unique_ptr<llvm::raw_pwrite_stream> out =
Clang.createOutputFile(outputPath, EC,
/*Binary=*/false,
/*RemoveFileOnSignal=*/true,
/*BaseInput=*/"",
path::extension(outputPath),
/*UseTemporary=*/true,
/*CreateMissingDirectories=*/false,
/*ResultPathName=*/nullptr,
&tmpFilePath);
if (!out) {
M->getASTContext().Diags.diagnose(SourceLoc(), diag::error_opening_output,
tmpFilePath, EC.message());
return true;
}
auto requiredAccess = moduleIsPublic ? AccessLevel::Public
: AccessLevel::Internal;
bool hadError = printAsObjC(*out, M, bridgingHeader, requiredAccess);
out->flush();
EC = swift::moveFileIfDifferent(tmpFilePath, outputPath);
bool actionFailed = false;
std::error_code EC =
swift::atomicallyWritingToFile(outputPath, /*binary*/false,
[&](llvm::raw_pwrite_stream &out) {
actionFailed = action(out);
});
if (EC) {
M->getASTContext().Diags.diagnose(SourceLoc(), diag::error_opening_output,
diags.diagnose(SourceLoc(), diag::error_opening_output,
outputPath, EC.message());
return true;
}
return actionFailed;
}
return hadError;
/// Prints the Objective-C "generated header" interface for \p M to \p
/// outputPath.
///
/// ...unless \p outputPath is empty, in which case it does nothing.
///
/// \returns true if there were any errors
///
/// \see swift::printAsObjC
static bool printAsObjCIfNeeded(StringRef outputPath, ModuleDecl *M,
StringRef bridgingHeader, bool moduleIsPublic) {
if (outputPath.empty())
return false;
return atomicallyWritingToTextFile(outputPath, M->getDiags(),
[&](raw_ostream &out) -> bool {
auto requiredAccess = moduleIsPublic ? AccessLevel::Public
: AccessLevel::Internal;
return printAsObjC(out, M, bridgingHeader, requiredAccess);
});
}
/// Prints the stable textual interface for \p M to \p outputPath.
///
/// ...unless \p outputPath is empty, in which case it does nothing.
///
/// \returns true if there were any errors
///
/// \see swift::emitModuleInterface
static bool printModuleInterfaceIfNeeded(StringRef outputPath, ModuleDecl *M) {
if (outputPath.empty())
return false;
return atomicallyWritingToTextFile(outputPath, M->getDiags(),
[M](raw_ostream &out) -> bool {
return swift::emitModuleInterface(out, M);
});
}
/// Returns the OutputKind for the given Action.
@@ -1322,6 +1336,10 @@ static bool performCompileStepsPostSILGen(
Instance.getMainModule(),
opts.ImplicitObjCHeaderPath, moduleIsPublic);
(void)printModuleInterfaceIfNeeded(
PSPs.SupplementaryOutputs.ModuleInterfaceOutputPath,
Instance.getMainModule());
if (Action == FrontendOptions::ActionType::EmitSIB)
return serializeSIB(SM.get(), PSPs, Instance.getASTContext(), MSF);

View File

@@ -0,0 +1,31 @@
//===--- TextualInterfaceGeneration.cpp - swiftinterface files ------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 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
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
#include "TextualInterfaceGeneration.h"
#include "swift/AST/Decl.h"
#include "swift/AST/Module.h"
using namespace swift;
bool swift::emitModuleInterface(raw_ostream &out, ModuleDecl *M) {
const PrintOptions printOptions = PrintOptions::printTextualInterfaceFile();
SmallVector<Decl *, 16> topLevelDecls;
M->getTopLevelDecls(topLevelDecls);
for (const Decl *D : topLevelDecls) {
if (!D->shouldPrintInContext(printOptions))
continue;
D->print(out, printOptions);
out << "\n";
}
return false;
}

View File

@@ -0,0 +1,39 @@
//===--- TextualInterfaceGeneration.h - swiftinterface files ----*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 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
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
#ifndef SWIFT_FRONTENDTOOL_TEXTUALINTERFACEGENERATION_H
#define SWIFT_FRONTENDTOOL_TEXTUALINTERFACEGENERATION_H
#include "swift/Basic/LLVM.h"
namespace swift {
class ModuleDecl;
/// Emit a stable, textual interface for \p M, which can be used by a client
/// source file to import this module.
///
/// Unlike a serialized module, the textual format generated by
/// emitModuleInterface is intended to be stable across compiler versions while
/// still describing the full ABI of the module in question.
///
/// The initial plan for this format can be found at
/// https://forums.swift.org/t/plan-for-module-stability/14551/
///
/// \return true if an error occurred
///
/// \sa swift::serialize
bool emitModuleInterface(raw_ostream &out, ModuleDecl *M);
} // end namespace swift
#endif

View File

@@ -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
@@ -38,9 +38,7 @@
#include "swift/Serialization/SerializationOptions.h"
#include "swift/Strings.h"
// FIXME: We're just using CompilerInstance::createOutputFile.
// This API should be sunk down to LLVM.
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Basic/Module.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringExtras.h"
@@ -5093,44 +5091,15 @@ void Serializer::writeDocToStream(raw_ostream &os, ModuleOrSourceFile DC,
static inline bool
withOutputFile(ASTContext &ctx, StringRef outputPath,
llvm::function_ref<void(raw_ostream &)> action){
namespace path = llvm::sys::path;
clang::CompilerInstance Clang;
std::error_code EC = swift::atomicallyWritingToFile(outputPath,
/*binary*/true,
action);
if (!EC)
return false;
std::string tmpFilePath;
{
std::error_code EC;
std::unique_ptr<llvm::raw_pwrite_stream> out =
Clang.createOutputFile(outputPath, EC,
/*Binary=*/true,
/*RemoveFileOnSignal=*/true,
/*BaseInput=*/"",
path::extension(outputPath),
/*UseTemporary=*/true,
/*CreateMissingDirectories=*/false,
/*ResultPathName=*/nullptr,
&tmpFilePath);
if (!out) {
StringRef problematicPath =
tmpFilePath.empty() ? outputPath : StringRef(tmpFilePath);
ctx.Diags.diagnose(SourceLoc(), diag::error_opening_output,
problematicPath, EC.message());
return true;
}
action(*out);
}
if (!tmpFilePath.empty()) {
std::error_code EC = swift::moveFileIfDifferent(tmpFilePath, outputPath);
if (EC) {
ctx.Diags.diagnose(SourceLoc(), diag::error_opening_output,
outputPath, EC.message());
return true;
}
}
return false;
}
void swift::serialize(ModuleOrSourceFile DC,

View File

@@ -0,0 +1 @@
public func otherFileFunction() {}

View File

@@ -0,0 +1,9 @@
// RUN: %empty-directory(%t)
// RUN: %target-swift-frontend -emit-module -o %t/main~partial.swiftmodule -primary-file %s %S/Inputs/other.swift -module-name main
// RUN: %target-swift-frontend -emit-module -o %t/other~partial.swiftmodule %s -primary-file %S/Inputs/other.swift -module-name main
// RUN: %target-swift-frontend -merge-modules -emit-module -o /dev/null -emit-interface-path - %t/main~partial.swiftmodule -module-name main %t/other~partial.swiftmodule | %FileCheck %s
// CHECK: {{^}}func verySimpleFunction(){{$}}
public func verySimpleFunction() {}
// CHECK: {{^}}func otherFileFunction(){{$}}

View File

@@ -0,0 +1,8 @@
// RUN: %target-swift-frontend -emit-interface-path - -emit-module -o /dev/null %s | %FileCheck %s
// RUN: %target-swift-frontend -emit-interface-path - -emit-module -o /dev/null %s %S/Inputs/other.swift | %FileCheck -check-prefix CHECK-MULTI-FILE %s
// CHECK: public func verySimpleFunction(){{$}}
// CHECK-MULTI-FILE: public func verySimpleFunction(){{$}}
public func verySimpleFunction() {}
// CHECK-MULTI-FILE: public func otherFileFunction(){{$}}