blob: 273c681ffed050e2f08af03e82cd6f660614088f [file] [log] [blame]
//===--- OutputFileMap.cpp - Map of inputs to multiple outputs ------------===//
//
// This source file is part of the Swift.org open source project
//
// 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
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
#include "swift/Basic/OutputFileMap.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/raw_ostream.h"
#include <system_error>
using namespace swift;
llvm::Expected<OutputFileMap>
OutputFileMap::loadFromPath(StringRef Path, StringRef workingDirectory) {
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> FileBufOrErr =
llvm::MemoryBuffer::getFile(Path);
if (!FileBufOrErr) {
return llvm::errorCodeToError(FileBufOrErr.getError());
}
return loadFromBuffer(std::move(FileBufOrErr.get()), workingDirectory);
}
llvm::Expected<OutputFileMap>
OutputFileMap::loadFromBuffer(StringRef Data, StringRef workingDirectory) {
std::unique_ptr<llvm::MemoryBuffer> Buffer{
llvm::MemoryBuffer::getMemBuffer(Data)};
return loadFromBuffer(std::move(Buffer), workingDirectory);
}
llvm::Expected<OutputFileMap>
OutputFileMap::loadFromBuffer(std::unique_ptr<llvm::MemoryBuffer> Buffer,
StringRef workingDirectory) {
return parse(std::move(Buffer), workingDirectory);
}
const TypeToPathMap *OutputFileMap::getOutputMapForInput(StringRef Input) const{
auto iter = InputToOutputsMap.find(Input);
if (iter == InputToOutputsMap.end())
return nullptr;
else
return &iter->second;
}
TypeToPathMap &
OutputFileMap::getOrCreateOutputMapForInput(StringRef Input) {
return InputToOutputsMap[Input];
}
const TypeToPathMap *OutputFileMap::getOutputMapForSingleOutput() const {
return getOutputMapForInput(StringRef());
}
TypeToPathMap &
OutputFileMap::getOrCreateOutputMapForSingleOutput() {
return InputToOutputsMap[StringRef()];
}
void OutputFileMap::dump(llvm::raw_ostream &os, bool Sort) const {
using TypePathPair = std::pair<file_types::ID, std::string>;
auto printOutputPair = [&os](StringRef InputPath,
const TypePathPair &OutputPair) -> void {
os << InputPath << " -> " << file_types::getTypeName(OutputPair.first)
<< ": \"" << OutputPair.second << "\"\n";
};
if (Sort) {
using PathMapPair = std::pair<StringRef, TypeToPathMap>;
std::vector<PathMapPair> Maps;
for (auto &InputPair : InputToOutputsMap) {
Maps.emplace_back(InputPair.first(), InputPair.second);
}
std::sort(Maps.begin(), Maps.end(), [] (const PathMapPair &LHS,
const PathMapPair &RHS) -> bool {
return LHS.first < RHS.first;
});
for (auto &InputPair : Maps) {
const TypeToPathMap &Map = InputPair.second;
std::vector<TypePathPair> Pairs;
Pairs.insert(Pairs.end(), Map.begin(), Map.end());
std::sort(Pairs.begin(), Pairs.end());
for (auto &OutputPair : Pairs) {
printOutputPair(InputPair.first, OutputPair);
}
}
} else {
for (auto &InputPair : InputToOutputsMap) {
const TypeToPathMap &Map = InputPair.second;
for (const TypePathPair &OutputPair : Map) {
printOutputPair(InputPair.first(), OutputPair);
}
}
}
}
static void writeQuotedEscaped(llvm::raw_ostream &os,
const StringRef fileName) {
os << "\"" << llvm::yaml::escape(fileName) << "\"";
}
void OutputFileMap::write(llvm::raw_ostream &os,
ArrayRef<StringRef> inputs) const {
for (const auto input : inputs) {
writeQuotedEscaped(os, input);
os << ":";
const TypeToPathMap *outputMap = getOutputMapForInput(input);
if (!outputMap) {
// The map doesn't have an entry for this input. (Perhaps there were no
// outputs and thus the entry was never created.) Put an empty sub-map
// into the output and move on.
os << " {}\n";
continue;
}
os << "\n";
for (auto &typeAndOutputPath : *outputMap) {
file_types::ID type = typeAndOutputPath.getFirst();
StringRef output = typeAndOutputPath.getSecond();
os << " " << file_types::getTypeName(type) << ": ";
writeQuotedEscaped(os, output);
os << "\n";
}
}
}
llvm::Expected<OutputFileMap>
OutputFileMap::parse(std::unique_ptr<llvm::MemoryBuffer> Buffer,
StringRef workingDirectory) {
auto constructError =
[](const char *errorString) -> llvm::Expected<OutputFileMap> {
return llvm::make_error<llvm::StringError>(errorString,
llvm::inconvertibleErrorCode());
};
/// FIXME: Make the returned error strings more specific by including some of
/// the source.
llvm::SourceMgr SM;
llvm::yaml::Stream YAMLStream(Buffer->getMemBufferRef(), SM);
auto I = YAMLStream.begin();
if (I == YAMLStream.end())
return constructError("empty YAML stream");
auto Root = I->getRoot();
if (!Root)
return constructError("no root");
OutputFileMap OFM;
auto *Map = dyn_cast<llvm::yaml::MappingNode>(Root);
if (!Map)
return constructError("root was not a MappingNode");
auto resolvePath =
[workingDirectory](
llvm::yaml::ScalarNode *Path,
llvm::SmallVectorImpl<char> &PathStorage) -> StringRef {
StringRef PathStr = Path->getValue(PathStorage);
if (workingDirectory.empty() || PathStr.empty() || PathStr == "-" ||
llvm::sys::path::is_absolute(PathStr)) {
return PathStr;
}
// Copy the path to avoid making assumptions about how getValue deals with
// Storage.
SmallString<128> PathStrCopy(PathStr);
PathStorage.clear();
PathStorage.reserve(PathStrCopy.size() + workingDirectory.size() + 1);
PathStorage.insert(PathStorage.begin(), workingDirectory.begin(),
workingDirectory.end());
llvm::sys::path::append(PathStorage, PathStrCopy);
return StringRef(PathStorage.data(), PathStorage.size());
};
for (auto &Pair : *Map) {
llvm::yaml::Node *Key = Pair.getKey();
llvm::yaml::Node *Value = Pair.getValue();
if (!Key)
return constructError("bad key");
if (!Value)
return constructError("bad value");
auto *InputPath = dyn_cast<llvm::yaml::ScalarNode>(Key);
if (!InputPath)
return constructError("input path not a scalar node");
llvm::yaml::MappingNode *OutputMapNode =
dyn_cast<llvm::yaml::MappingNode>(Value);
if (!OutputMapNode)
return constructError("output map not a MappingNode");
TypeToPathMap OutputMap;
for (auto &OutputPair : *OutputMapNode) {
llvm::yaml::Node *Key = OutputPair.getKey();
llvm::yaml::Node *Value = OutputPair.getValue();
auto *KindNode = dyn_cast<llvm::yaml::ScalarNode>(Key);
if (!KindNode)
return constructError("kind not a ScalarNode");
auto *Path = dyn_cast<llvm::yaml::ScalarNode>(Value);
if (!Path)
return constructError("path not a scalar node");
llvm::SmallString<16> KindStorage;
file_types::ID Kind =
file_types::lookupTypeForName(KindNode->getValue(KindStorage));
// Ignore unknown types, so that an older swiftc can be used with a newer
// build system.
if (Kind == file_types::TY_INVALID)
continue;
llvm::SmallString<128> PathStorage;
OutputMap.insert(std::pair<file_types::ID, std::string>(
Kind, resolvePath(Path, PathStorage)));
}
llvm::SmallString<128> InputStorage;
OFM.InputToOutputsMap[resolvePath(InputPath, InputStorage)] =
std::move(OutputMap);
}
if (YAMLStream.failed())
return constructError("Output file map parse failed");
return OFM;
}