mirror of
https://github.com/ldc-developers/ldc.git
synced 2025-04-29 22:50:53 +03:00
222 lines
5.7 KiB
C++
222 lines
5.7 KiB
C++
//===-- args.cpp ----------------------------------------------------------===//
|
||
//
|
||
// LDC – the LLVM D compiler
|
||
//
|
||
// This file is distributed under the BSD-style LDC license. See the LICENSE
|
||
// file for details.
|
||
//
|
||
//===----------------------------------------------------------------------===//
|
||
|
||
#include "args.h"
|
||
|
||
#include "llvm/ADT/SmallString.h"
|
||
#include "llvm/Support/CommandLine.h"
|
||
#include "llvm/Support/FileSystem.h"
|
||
#include "llvm/Support/StringSaver.h"
|
||
|
||
#include <cstdlib>
|
||
|
||
#ifdef _WIN32
|
||
#include "llvm/Support/ConvertUTF.h"
|
||
#endif
|
||
|
||
#if LDC_WINDOWS_WMAIN
|
||
#define WIN32_LEAN_AND_MEAN
|
||
#include <windows.h>
|
||
#endif
|
||
|
||
extern "C" {
|
||
// in driver/main.d:
|
||
int _Dmain(/*string[] args*/);
|
||
|
||
// in druntime:
|
||
int _d_run_main(int argc, const char **argv, int (*dMain)());
|
||
#if LDC_WINDOWS_WMAIN
|
||
int _d_wrun_main(int argc, const wchar_t **wargv, int (*dMain)());
|
||
#endif
|
||
}
|
||
|
||
namespace args {
|
||
static llvm::BumpPtrAllocator allocator;
|
||
|
||
void getCommandLineArguments(int argc, const CArgChar **argv,
|
||
llvm::SmallVectorImpl<const char *> &result) {
|
||
#if LDC_WINDOWS_WMAIN
|
||
// convert from UTF-16 to UTF-8
|
||
result.reserve(argc);
|
||
for (int i = 0; i < argc; ++i) {
|
||
const wchar_t *warg = argv[i];
|
||
const size_t wlen = wcslen(warg) + 1; // incl. terminating null
|
||
const int len = WideCharToMultiByte(CP_UTF8, 0, warg, wlen, nullptr, 0,
|
||
nullptr, nullptr);
|
||
char *arg = allocator.Allocate<char>(len);
|
||
WideCharToMultiByte(CP_UTF8, 0, warg, wlen, arg, len, nullptr, nullptr);
|
||
result.push_back(arg);
|
||
}
|
||
#else
|
||
#ifdef _WIN32
|
||
#pragma message ("Using current code page instead of UTF-8 for command-line args")
|
||
#endif
|
||
result.insert(result.end(), argv, argv + argc);
|
||
#endif
|
||
}
|
||
|
||
void expandResponseFiles(llvm::SmallVectorImpl<const char *> &args) {
|
||
llvm::StringSaver saver(allocator);
|
||
llvm::cl::ExpandResponseFiles(saver,
|
||
#ifdef _WIN32
|
||
llvm::cl::TokenizeWindowsCommandLine
|
||
#else
|
||
llvm::cl::TokenizeGNUCommandLine
|
||
#endif
|
||
,
|
||
args);
|
||
}
|
||
|
||
int forwardToDruntime(int argc, const CArgChar **argv) {
|
||
#if LDC_WINDOWS_WMAIN
|
||
return _d_wrun_main(argc, argv, &_Dmain);
|
||
#else
|
||
return _d_run_main(argc, argv, &_Dmain);
|
||
#endif
|
||
}
|
||
|
||
bool isRunArg(const char *arg) {
|
||
return strcmp(arg, "-run") == 0 || strcmp(arg, "--run") == 0;
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
|
||
namespace {
|
||
std::vector<llvm::StringRef> toRefsVector(llvm::ArrayRef<const char *> args) {
|
||
std::vector<llvm::StringRef> refs;
|
||
refs.reserve(args.size());
|
||
for (const char *arg : args)
|
||
refs.emplace_back(arg);
|
||
return refs;
|
||
}
|
||
|
||
struct ResponseFile {
|
||
llvm::SmallString<128> path;
|
||
|
||
// Deletes the file (if path is non-empty).
|
||
~ResponseFile() {
|
||
if (!path.empty())
|
||
llvm::sys::fs::remove(path);
|
||
}
|
||
|
||
// Creates an appropriate response file if needed (=> path non-empty).
|
||
// Returns false on error.
|
||
bool setup(llvm::ArrayRef<const char *> fullArgs,
|
||
llvm::sys::WindowsEncodingMethod encoding) {
|
||
assert(path.empty());
|
||
|
||
const auto args = fullArgs.slice(1);
|
||
if (llvm::sys::commandLineFitsWithinSystemLimits(fullArgs[0], args))
|
||
return true; // nothing to do
|
||
|
||
#if defined(_WIN32)
|
||
const llvm::ErrorOr<std::wstring> wcontent =
|
||
llvm::sys::flattenWindowsCommandLine(toRefsVector(args));
|
||
|
||
std::string content;
|
||
if (!wcontent || !llvm::convertWideToUTF8(*wcontent, content))
|
||
return false;
|
||
#else
|
||
std::string content;
|
||
content.reserve(65536);
|
||
for (llvm::StringRef arg : args) {
|
||
content += '"';
|
||
for (char c : arg) {
|
||
#ifdef _WIN32
|
||
if (c == '"')
|
||
content += '"'; // " => ""
|
||
#else
|
||
if (c == '\\' || c == '"')
|
||
content += '\\'; // \ => \\, " => \"
|
||
#endif
|
||
content += c;
|
||
}
|
||
content += "\"\n";
|
||
}
|
||
#endif
|
||
|
||
if (llvm::sys::fs::createTemporaryFile("ldc", "rsp", path))
|
||
return false;
|
||
|
||
if (llvm::sys::writeFileWithEncoding(path, content, encoding))
|
||
return false;
|
||
|
||
return true;
|
||
}
|
||
};
|
||
} // anonymous namespace
|
||
|
||
int executeAndWait(std::vector<const char *> fullArgs,
|
||
llvm::sys::WindowsEncodingMethod responseFileEncoding,
|
||
std::string *errorMsg) {
|
||
args::ResponseFile rspFile;
|
||
if (!rspFile.setup(fullArgs, responseFileEncoding)) {
|
||
if (errorMsg)
|
||
*errorMsg = "could not write temporary response file";
|
||
return -1;
|
||
}
|
||
|
||
std::string rspArg;
|
||
if (!rspFile.path.empty()) {
|
||
rspArg = ("@" + rspFile.path).str();
|
||
fullArgs.resize(1); // executable only
|
||
fullArgs.push_back(rspArg.c_str());
|
||
}
|
||
|
||
const std::vector<llvm::StringRef> argv = toRefsVector(fullArgs);
|
||
#if LDC_LLVM_VER < 1600
|
||
auto envVars = llvm::None;
|
||
#else
|
||
auto envVars = std::nullopt;
|
||
#endif
|
||
|
||
return llvm::sys::ExecuteAndWait(argv[0], argv, envVars, {}, 0, 0, errorMsg);
|
||
}
|
||
} // namespace args
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
|
||
namespace env {
|
||
#ifdef _WIN32
|
||
static wchar_t *wget(const char *name) {
|
||
llvm::SmallVector<wchar_t, 32> wname;
|
||
llvm::sys::windows::UTF8ToUTF16(name, wname);
|
||
wname.push_back(0);
|
||
return _wgetenv(wname.data());
|
||
}
|
||
#endif
|
||
|
||
bool has(const char *name) {
|
||
#ifdef _WIN32
|
||
return wget(name) != nullptr;
|
||
#else
|
||
return getenv(name) != nullptr;
|
||
#endif
|
||
}
|
||
|
||
#ifdef _WIN32
|
||
bool has(const wchar_t *wname) { return _wgetenv(wname) != nullptr; }
|
||
#endif
|
||
|
||
std::string get(const char *name) {
|
||
#ifdef _WIN32
|
||
using llvm::UTF16;
|
||
const wchar_t *wvalue = wget(name);
|
||
std::string value;
|
||
if (wvalue) {
|
||
llvm::convertUTF16ToUTF8String(
|
||
{reinterpret_cast<const UTF16 *>(wvalue), wcslen(wvalue)}, value);
|
||
}
|
||
return value;
|
||
#else
|
||
const char *value = getenv(name);
|
||
return value ? value : "";
|
||
#endif
|
||
}
|
||
}
|