"name": "test",
"image": "ubuntu",
"commands": [
- "apt-get update && apt-get install -y clang-8 cmake ninja-build lld-8 llvm-8-dev libc++-8-dev libc++abi-8-dev libz-dev",
+ "apt-get update && apt-get install -y clang-8 cmake ninja-build lld-8 llvm-8-dev libc++-8-dev libc++abi-8-dev libz-dev git",
+ "git clone https://github.com/llvm/llvm-project",
+ "mkdir llvm-project/build && cd llvm-project/build",
+ 'env CC=clang-8 CXX=clang++-8 CXXFLAGS="-stdlib=libc++" LDFLAGS="-fuse-ld=lld" cmake -GNinja -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=../install -DLLVM_TARGETS_TO_BUILD=host -DLLVM_ENABLE_PROJECTS="clang;mlir" ../llvm',
+ "ninja install",
+ "cd ../..",
"mkdir build && cd build",
- 'env CC=clang-8 CXX=clang++-8 CXXFLAGS="-UNDEBUG" LDFLAGS="-fuse-ld=lld" cmake -GNinja -DCMAKE_BUILD_TYPE=Release ..',
+ 'env CC=clang-8 CXX=clang++-8 CXXFLAGS="-UNDEBUG -stdlib=libc++" LDFLAGS="-fuse-ld=lld" cmake -GNinja -DCMAKE_BUILD_TYPE=Release .. -DLLVM_DIR=/drone/src/llvm-project/install/lib/cmake/llvm -DLLVM_EXTERNAL_LIT=/drone/src/llvm-project/build/bin/llvm-lit',
"ninja -j8",
"ctest --output-on-failure -j24",
+ "ninja check-all",
],
},
],
"name": "test",
"image": "gcc",
"commands": [
- "apt-get update && apt-get install -y cmake ninja-build llvm-dev libz-dev",
+ "apt-get update && apt-get install -y cmake ninja-build llvm-dev libz-dev git",
+ "git clone https://github.com/llvm/llvm-project",
+ "mkdir llvm-project/build && cd llvm-project/build",
+ 'env CC=gcc CXX=g++ LDFLAGS="-fuse-ld=gold" cmake -GNinja -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=../install -DLLVM_TARGETS_TO_BUILD=host -DLLVM_ENABLE_PROJECTS="clang;mlir" ../llvm',
+ "ninja install",
+ "cd ../..",
"mkdir build && cd build",
- 'env CC=gcc CXX=g++ CXXFLAGS="-UNDEBUG" LDFLAGS="-fuse-ld=gold" cmake -GNinja -DCMAKE_BUILD_TYPE=Release ..',
+ 'env CC=gcc CXX=g++ CXXFLAGS="-UNDEBUG" LDFLAGS="-fuse-ld=gold" cmake -GNinja -DCMAKE_BUILD_TYPE=Release .. -DLLVM_DIR=/drone/src/llvm-project/install/lib/cmake/llvm -DLLVM_EXTERNAL_LIT=/drone/src/llvm-project/build/bin/llvm-lit',
"ninja -j8",
"ctest --output-on-failure -j24",
+ "ninja check-all",
],
},
],
#include "flang/Semantics/semantics.h"
#include "flang/Semantics/symbol.h"
#include "flang/Semantics/tools.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/raw_ostream.h"
#include <algorithm>
-#include <cerrno>
#include <fstream>
#include <ostream>
#include <set>
#include <string_view>
-#include <sys/file.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
#include <vector>
namespace Fortran::semantics {
static std::ostream &PutAttr(std::ostream &, Attr);
static std::ostream &PutType(std::ostream &, const DeclTypeSpec &);
static std::ostream &PutLower(std::ostream &, const std::string &);
-static int WriteFile(const std::string &, const std::string &);
+static std::error_code WriteFile(
+ const std::string &, const std::string &, bool = true);
static bool FileContentsMatch(
const std::string &, const std::string &, const std::string &);
-static std::size_t GetFileSize(const std::string &);
static std::string CheckSum(const std::string_view &);
// Collect symbols needed for a subprogram interface
auto path{context_.moduleDirectory() + '/' +
ModFileName(symbol.name(), ancestorName, context_.moduleFileSuffix())};
PutSymbols(DEREF(symbol.scope()));
- if (int error{WriteFile(path, GetAsString(symbol))}) {
- context_.Say(symbol.name(), "Error writing %s: %s"_err_en_US, path,
- std::strerror(error));
+ if (std::error_code error{
+ WriteFile(path, GetAsString(symbol), context_.debugModuleWriter())}) {
+ context_.Say(
+ symbol.name(), "Error writing %s: %s"_err_en_US, path, error.message());
}
}
}
struct Temp {
- Temp() = delete;
+ Temp(llvm::sys::fs::file_t fd, std::string path) : fd{fd}, path{path} {}
+ Temp(Temp &&t) : fd{std::exchange(t.fd, -1)}, path{std::move(t.path)} {}
~Temp() {
- close(fd);
- unlink(path.c_str());
+ if (fd >= 0) {
+ llvm::sys::fs::closeFile(fd);
+ llvm::sys::fs::remove(path.c_str());
+ }
}
- int fd;
+ llvm::sys::fs::file_t fd;
std::string path;
};
// Create a temp file in the same directory and with the same suffix as path.
// Return an open file descriptor and its path.
-static Temp MkTemp(const std::string &path) {
+static llvm::ErrorOr<Temp> MkTemp(const std::string &path) {
auto length{path.length()};
auto dot{path.find_last_of("./")};
- std::string suffix{dot < length && path[dot] == '.' ? path.substr(dot) : ""};
+ std::string suffix{
+ dot < length && path[dot] == '.' ? path.substr(dot + 1) : ""};
CHECK(length > suffix.length() &&
path.substr(length - suffix.length()) == suffix);
- auto tempPath{path.substr(0, length - suffix.length()) + "XXXXXX" + suffix};
- int fd{mkstemps(&tempPath[0], suffix.length())};
- auto mask{umask(0777)};
- umask(mask);
- chmod(tempPath.c_str(), 0666 & ~mask); // temp is created with mode 0600
- return Temp{fd, tempPath};
+ auto prefix{path.substr(0, length - suffix.length())};
+ llvm::sys::fs::file_t fd;
+ llvm::SmallString<16> tempPath;
+ if (std::error_code err{llvm::sys::fs::createUniqueFile(
+ prefix + "%%%%%%" + suffix, fd, tempPath)}) {
+ return err;
+ }
+ return Temp{fd, tempPath.c_str()};
}
// Write the module file at path, prepending header. If an error occurs,
// return errno, otherwise 0.
-static int WriteFile(const std::string &path, const std::string &contents) {
+static std::error_code WriteFile(
+ const std::string &path, const std::string &contents, bool debug) {
auto header{std::string{ModHeader::bom} + ModHeader::magic +
CheckSum(contents) + ModHeader::terminator};
+ if (debug) {
+ llvm::dbgs() << "Processing module " << path << ": ";
+ }
if (FileContentsMatch(path, header, contents)) {
- return 0;
+ if (debug) {
+ llvm::dbgs() << "module unchanged, not writing\n";
+ }
+ return {};
}
- Temp temp{MkTemp(path)};
- if (temp.fd < 0) {
- return errno;
+ llvm::ErrorOr<Temp> temp{MkTemp(path)};
+ if (!temp) {
+ return temp.getError();
}
- if (write(temp.fd, header.c_str(), header.size()) !=
- static_cast<ssize_t>(header.size()) ||
- write(temp.fd, contents.c_str(), contents.size()) !=
- static_cast<ssize_t>(contents.size())) {
- return errno;
+ llvm::raw_fd_ostream writer(temp->fd, /*shouldClose=*/false);
+ writer << header;
+ writer << contents;
+ writer.flush();
+ if (writer.has_error()) {
+ return writer.error();
}
- if (std::rename(temp.path.c_str(), path.c_str()) == -1) {
- return errno;
+ if (debug) {
+ llvm::dbgs() << "module written\n";
}
- return 0;
+ return llvm::sys::fs::rename(temp->path, path);
}
// Return true if the stream matches what we would write for the mod file.
const std::string &header, const std::string &contents) {
std::size_t hsize{header.size()};
std::size_t csize{contents.size()};
- if (GetFileSize(path) != hsize + csize) {
+ auto buf_or{llvm::MemoryBuffer::getFile(path)};
+ if (!buf_or) {
return false;
}
- int fd{open(path.c_str(), O_RDONLY)};
- if (fd < 0) {
+ auto buf = std::move(buf_or.get());
+ if (buf->getBufferSize() != hsize + csize) {
return false;
}
- constexpr std::size_t bufSize{4096};
- std::string buffer(bufSize, '\0');
- if (read(fd, &buffer[0], hsize) != static_cast<ssize_t>(hsize) ||
- std::memcmp(&buffer[0], &header[0], hsize) != 0) {
- close(fd);
- return false; // header doesn't match
- }
- for (auto remaining{csize};;) {
- auto bytes{std::min(bufSize, remaining)};
- auto got{read(fd, &buffer[0], bytes)};
- if (got != static_cast<ssize_t>(bytes) ||
- std::memcmp(&buffer[0], &contents[csize - remaining], bytes) != 0) {
- close(fd);
- return false;
- }
- if (bytes == 0 && remaining == 0) {
- close(fd);
- return true;
- }
- remaining -= bytes;
+ if (!std::equal(header.begin(), header.end(), buf->getBufferStart(),
+ buf->getBufferStart() + hsize)) {
+ return false;
}
+
+ return std::equal(contents.begin(), contents.end(),
+ buf->getBufferStart() + hsize, buf->getBufferEnd());
}
// Compute a simple hash of the contents of a module file and
return expectSum == actualSum;
}
-static std::size_t GetFileSize(const std::string &path) {
- struct stat statbuf;
- if (stat(path.c_str(), &statbuf) == 0) {
- return static_cast<std::size_t>(statbuf.st_size);
- } else {
- return 0;
- }
-}
-
Scope *ModFileReader::Read(const SourceName &name, Scope *ancestor) {
std::string ancestorName; // empty for module
if (ancestor) {