forked from qt-creator/qt-creator
Docker: Implement some DockerDevice functions
Container file system accesses are primarily done through the host file system by prepending the appropriate path. This is Linux-only, and only in case the volumes are mounted to the same relative location in the container. As a fallback, provide the functionality also via 'docker exec'. This is, however, too slow to be used in the future. Change-Id: I23185ae45184cb7b1de7c723a01ab96fe9c0801e Reviewed-by: Christian Stenger <christian.stenger@qt.io>
This commit is contained in:
@@ -32,10 +32,15 @@
|
||||
|
||||
#include <projectexplorer/devicesupport/idevicewidget.h>
|
||||
#include <projectexplorer/runcontrol.h>
|
||||
#include <projectexplorer/toolchain.h>
|
||||
#include <projectexplorer/toolchainmanager.h>
|
||||
|
||||
#include <qtsupport/baseqtversion.h>
|
||||
#include <qtsupport/qtversionfactory.h>
|
||||
#include <qtsupport/qtversionmanager.h>
|
||||
|
||||
#include <utils/algorithm.h>
|
||||
#include <utils/basetreeview.h>
|
||||
#include <utils/consoleprocess.h>
|
||||
#include <utils/environment.h>
|
||||
#include <utils/hostosinfo.h>
|
||||
#include <utils/layoutbuilder.h>
|
||||
@@ -43,18 +48,25 @@
|
||||
#include <utils/qtcassert.h>
|
||||
#include <utils/qtcprocess.h>
|
||||
#include <utils/stringutils.h>
|
||||
#include <utils/temporaryfile.h>
|
||||
#include <utils/treemodel.h>
|
||||
|
||||
#include <QDialog>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QFileSystemWatcher>
|
||||
#include <QHeaderView>
|
||||
#include <QPushButton>
|
||||
#include <QTextBrowser>
|
||||
#include <QThread>
|
||||
|
||||
using namespace Core;
|
||||
using namespace ProjectExplorer;
|
||||
using namespace QtSupport;
|
||||
using namespace Utils;
|
||||
|
||||
#define LOG(x)
|
||||
//#define LOG(x) qDebug() << x
|
||||
|
||||
namespace Docker {
|
||||
namespace Internal {
|
||||
|
||||
@@ -83,8 +95,7 @@ public:
|
||||
qint64 write(const QByteArray &data) override { return m_process.write(data); }
|
||||
|
||||
private:
|
||||
QProcess m_process;
|
||||
ConsoleProcess m_consoleProcess;
|
||||
QtcProcess m_process;
|
||||
};
|
||||
|
||||
DockerDeviceProcess::DockerDeviceProcess(const QSharedPointer<const IDevice> &device,
|
||||
@@ -101,50 +112,27 @@ void DockerDeviceProcess::start(const Runnable &runnable)
|
||||
|
||||
const QStringList dockerRunFlags = runnable.extraData[Constants::DOCKER_RUN_FLAGS].toStringList();
|
||||
|
||||
const DockerDeviceData &data = dockerDevice->data();
|
||||
CommandLine cmd("docker");
|
||||
cmd.addArg("run");
|
||||
cmd.addArgs(dockerRunFlags);
|
||||
cmd.addArg(data.imageId);
|
||||
if (!runnable.executable.isEmpty())
|
||||
cmd.addArgs(runnable.commandLine());
|
||||
|
||||
disconnect(&m_consoleProcess);
|
||||
disconnect(&m_process);
|
||||
|
||||
if (runInTerminal()) {
|
||||
connect(&m_consoleProcess, &ConsoleProcess::errorOccurred,
|
||||
this, &DeviceProcess::error);
|
||||
connect(&m_consoleProcess, &ConsoleProcess::processStarted,
|
||||
this, &DeviceProcess::started);
|
||||
connect(&m_consoleProcess, &ConsoleProcess::stubStopped,
|
||||
this, &DeviceProcess::finished);
|
||||
|
||||
m_consoleProcess.setAbortOnMetaChars(false);
|
||||
m_consoleProcess.setSettings(Core::ICore::settings());
|
||||
m_consoleProcess.setEnvironment(runnable.environment);
|
||||
m_consoleProcess.setCommand(cmd);
|
||||
m_consoleProcess.start();
|
||||
} else {
|
||||
m_process.setProcessEnvironment(runnable.environment.toProcessEnvironment());
|
||||
connect(&m_process, &QProcess::errorOccurred, this, &DeviceProcess::error);
|
||||
connect(&m_process, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished),
|
||||
this, &DeviceProcess::finished);
|
||||
connect(&m_process, &QProcess::readyReadStandardOutput,
|
||||
this, &DeviceProcess::readyReadStandardOutput);
|
||||
connect(&m_process, &QProcess::readyReadStandardError,
|
||||
this, &DeviceProcess::readyReadStandardError);
|
||||
connect(&m_process, &QProcess::started, this, &DeviceProcess::started);
|
||||
m_process.setWorkingDirectory(runnable.workingDirectory);
|
||||
m_process.start(cmd.executable().toString(), cmd.splitArguments());
|
||||
}
|
||||
|
||||
connect(this, &DeviceProcess::readyReadStandardOutput, this, [this] {
|
||||
MessageManager::writeSilently(QString::fromLocal8Bit(readAllStandardError()));
|
||||
});
|
||||
connect(this, &DeviceProcess::readyReadStandardError, this, [this] {
|
||||
MessageManager::writeDisrupting(QString::fromLocal8Bit(readAllStandardError()));
|
||||
});
|
||||
|
||||
disconnect(&m_process);
|
||||
|
||||
m_process.setCommand(runnable.commandLine());
|
||||
m_process.setProcessEnvironment(runnable.environment.toProcessEnvironment());
|
||||
m_process.setWorkingDirectory(runnable.workingDirectory);
|
||||
connect(&m_process, &QProcess::errorOccurred, this, &DeviceProcess::error);
|
||||
connect(&m_process, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished),
|
||||
this, &DeviceProcess::finished);
|
||||
connect(&m_process, &QProcess::readyReadStandardOutput,
|
||||
this, &DeviceProcess::readyReadStandardOutput);
|
||||
connect(&m_process, &QProcess::readyReadStandardError,
|
||||
this, &DeviceProcess::readyReadStandardError);
|
||||
connect(&m_process, &QProcess::started, this, &DeviceProcess::started);
|
||||
dockerDevice->runProcess(m_process);
|
||||
}
|
||||
|
||||
void DockerDeviceProcess::interrupt()
|
||||
@@ -272,9 +260,38 @@ IDeviceWidget *DockerDevice::createWidget()
|
||||
return new DockerDeviceWidget(sharedFromThis());
|
||||
}
|
||||
|
||||
DockerDevice::DockerDevice(const DockerDeviceData &data)
|
||||
: m_data(data)
|
||||
|
||||
// DockerDevice
|
||||
|
||||
class DockerDevicePrivate : public QObject
|
||||
{
|
||||
public:
|
||||
DockerDevicePrivate()
|
||||
{
|
||||
connect(&m_mergedDirWatcher, &QFileSystemWatcher::fileChanged, this, [](const QString &path) {
|
||||
LOG("Container watcher change, file: " << path);
|
||||
});
|
||||
connect(&m_mergedDirWatcher, &QFileSystemWatcher::directoryChanged, this, [](const QString &path) {
|
||||
LOG("Container watcher change, directory: " << path);
|
||||
});
|
||||
}
|
||||
|
||||
~DockerDevicePrivate() { delete m_shell; }
|
||||
|
||||
DockerDeviceData m_data;
|
||||
|
||||
// For local file access
|
||||
QPointer<QtcProcess> m_shell;
|
||||
QString m_container;
|
||||
QString m_mergedDir;
|
||||
QFileSystemWatcher m_mergedDirWatcher;
|
||||
};
|
||||
|
||||
DockerDevice::DockerDevice(const DockerDeviceData &data)
|
||||
: d(new DockerDevicePrivate)
|
||||
{
|
||||
d->m_data = data;
|
||||
|
||||
setDisplayType(tr("Docker"));
|
||||
setOsType(OsTypeOtherUnix);
|
||||
setDefaultDisplayName(tr("Docker Image"));;
|
||||
@@ -313,9 +330,125 @@ DockerDevice::DockerDevice(const DockerDeviceData &data)
|
||||
}
|
||||
}
|
||||
|
||||
DockerDevice::~DockerDevice()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
const DockerDeviceData &DockerDevice::data() const
|
||||
{
|
||||
return m_data;
|
||||
return d->m_data;
|
||||
}
|
||||
|
||||
void DockerDevice::autoDetectQtVersion() const
|
||||
{
|
||||
QString error;
|
||||
QString source = "docker:" + d->m_data.imageId;
|
||||
const QStringList candidates = {"/usr/local/bin/qmake", "/usr/bin/qmake"};
|
||||
for (const QString &candidate : candidates) {
|
||||
const FilePath qmake = mapToGlobalPath(FilePath::fromString(candidate));
|
||||
if (auto qtVersion = QtVersionFactory::createQtVersionFromQMakePath(qmake, false, source, &error)) {
|
||||
QtVersionManager::addVersion(qtVersion);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DockerDevice::autoDetectToolChains()
|
||||
{
|
||||
const QList<ToolChainFactory *> factories = ToolChainFactory::allToolChainFactories();
|
||||
|
||||
QList<ToolChain *> toolChains;
|
||||
for (ToolChainFactory *factory : factories) {
|
||||
const QList<ToolChain *> newToolChains = factory->autoDetect(toolChains, sharedFromThis());
|
||||
for (ToolChain *toolChain : newToolChains) {
|
||||
LOG("Found ToolChain: " << toolChain->compilerCommand().toUserOutput());
|
||||
ToolChainManager::registerToolChain(toolChain);
|
||||
toolChains.append(toolChain);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DockerDevice::tryCreateLocalFileAccess() const
|
||||
{
|
||||
if (!d->m_container.isEmpty())
|
||||
return;
|
||||
|
||||
QString tempFileName;
|
||||
|
||||
{
|
||||
TemporaryFile temp("qtc-docker-XXXXXX");
|
||||
temp.open();
|
||||
tempFileName = temp.fileName();
|
||||
}
|
||||
|
||||
d->m_shell = new QtcProcess;
|
||||
// FIXME: Make mounts flexible
|
||||
d->m_shell->setCommand({"docker", {"run", "-i", "--cidfile=" + tempFileName,
|
||||
"-v", "/opt:/opt",
|
||||
"-v", "/data:/data",
|
||||
"-e", "DISPLAY=:0",
|
||||
"-e", "XAUTHORITY=/.Xauthority",
|
||||
"--net", "host",
|
||||
d->m_data.imageId, "/bin/sh"}});
|
||||
LOG("RUNNING: " << d->m_shell->commandLine().toUserOutput());
|
||||
d->m_shell->start();
|
||||
d->m_shell->waitForStarted();
|
||||
|
||||
LOG("CHECKING: " << tempFileName);
|
||||
for (int i = 0; i <= 10; ++i) {
|
||||
QFile file(tempFileName);
|
||||
file.open(QIODevice::ReadOnly);
|
||||
d->m_container = QString::fromUtf8(file.readAll()).trimmed();
|
||||
if (!d->m_container.isEmpty()) {
|
||||
LOG("Container: " << d->m_container);
|
||||
break;
|
||||
}
|
||||
if (i == 10) {
|
||||
qWarning("Docker cid file empty.");
|
||||
return; // No
|
||||
}
|
||||
QThread::msleep(100);
|
||||
}
|
||||
|
||||
QtcProcess proc;
|
||||
proc.setCommand({"docker", {"inspect", "--format={{.GraphDriver.Data.MergedDir}}", d->m_container}});
|
||||
//LOG(proc2.commandLine().toUserOutput());
|
||||
proc.start();
|
||||
proc.waitForFinished();
|
||||
const QByteArray out = proc.readAllStandardOutput();
|
||||
d->m_mergedDir = QString::fromUtf8(out).trimmed();
|
||||
if (d->m_mergedDir.endsWith('/'))
|
||||
d->m_mergedDir.chop(1);
|
||||
|
||||
d->m_mergedDirWatcher.addPath(d->m_mergedDir);
|
||||
}
|
||||
|
||||
bool DockerDevice::hasLocalFileAccess() const
|
||||
{
|
||||
return !d->m_mergedDir.isEmpty();
|
||||
}
|
||||
|
||||
FilePath DockerDevice::mapToLocalAccess(const FilePath &filePath) const
|
||||
{
|
||||
QTC_ASSERT(!d->m_mergedDir.isEmpty(), return {});
|
||||
QString path = filePath.path();
|
||||
if (path.startsWith('/'))
|
||||
return FilePath::fromString(d->m_mergedDir + path);
|
||||
return FilePath::fromString(d->m_mergedDir + '/' + path);
|
||||
}
|
||||
|
||||
FilePath DockerDevice::mapFromLocalAccess(const FilePath &filePath) const
|
||||
{
|
||||
QTC_ASSERT(!filePath.needsDevice(), return {});
|
||||
return mapFromLocalAccess(filePath.toString());
|
||||
}
|
||||
|
||||
FilePath DockerDevice::mapFromLocalAccess(const QString &filePath) const
|
||||
{
|
||||
QTC_ASSERT(!d->m_mergedDir.isEmpty(), return {});
|
||||
QTC_ASSERT(filePath.startsWith(d->m_mergedDir), return FilePath::fromString(filePath));
|
||||
return mapToGlobalPath(FilePath::fromString(filePath.mid(d->m_mergedDir.size())));
|
||||
}
|
||||
|
||||
const char DockerDeviceDataImageIdKey[] = "DockerDeviceDataImageId";
|
||||
@@ -326,24 +459,22 @@ const char DockerDeviceDataSizeKey[] = "DockerDeviceDataSize";
|
||||
void DockerDevice::fromMap(const QVariantMap &map)
|
||||
{
|
||||
ProjectExplorer::IDevice::fromMap(map);
|
||||
m_data.imageId = map.value(DockerDeviceDataImageIdKey).toString();
|
||||
m_data.repo = map.value(DockerDeviceDataRepoKey).toString();
|
||||
m_data.tag = map.value(DockerDeviceDataTagKey).toString();
|
||||
m_data.size = map.value(DockerDeviceDataSizeKey).toString();
|
||||
d->m_data.imageId = map.value(DockerDeviceDataImageIdKey).toString();
|
||||
d->m_data.repo = map.value(DockerDeviceDataRepoKey).toString();
|
||||
d->m_data.tag = map.value(DockerDeviceDataTagKey).toString();
|
||||
d->m_data.size = map.value(DockerDeviceDataSizeKey).toString();
|
||||
}
|
||||
|
||||
QVariantMap DockerDevice::toMap() const
|
||||
{
|
||||
QVariantMap map = ProjectExplorer::IDevice::toMap();
|
||||
map.insert(DockerDeviceDataImageIdKey, m_data.imageId);
|
||||
map.insert(DockerDeviceDataRepoKey, m_data.repo);
|
||||
map.insert(DockerDeviceDataTagKey, m_data.tag);
|
||||
map.insert(DockerDeviceDataSizeKey, m_data.size);
|
||||
map.insert(DockerDeviceDataImageIdKey, d->m_data.imageId);
|
||||
map.insert(DockerDeviceDataRepoKey, d->m_data.repo);
|
||||
map.insert(DockerDeviceDataTagKey, d->m_data.tag);
|
||||
map.insert(DockerDeviceDataSizeKey, d->m_data.size);
|
||||
return map;
|
||||
}
|
||||
|
||||
DockerDevice::~DockerDevice() = default;
|
||||
|
||||
DeviceProcess *DockerDevice::createProcess(QObject *parent) const
|
||||
{
|
||||
return new DockerDeviceProcess(sharedFromThis(), parent);
|
||||
@@ -379,6 +510,167 @@ DeviceEnvironmentFetcher::Ptr DockerDevice::environmentFetcher() const
|
||||
return DeviceEnvironmentFetcher::Ptr();
|
||||
}
|
||||
|
||||
FilePath DockerDevice::mapToGlobalPath(const FilePath &pathOnDevice) const
|
||||
{
|
||||
QUrl url = pathOnDevice.toUrl();
|
||||
if (url.isValid()) {
|
||||
QTC_CHECK(url.host() == d->m_data.imageId);
|
||||
QTC_CHECK(url.scheme() == "docker");
|
||||
return pathOnDevice;
|
||||
}
|
||||
url.setScheme("docker");
|
||||
url.setHost(d->m_data.imageId);
|
||||
url.setPath(pathOnDevice.toString());
|
||||
return FilePath::fromUrl(url);
|
||||
}
|
||||
|
||||
bool DockerDevice::handlesFile(const FilePath &filePath) const
|
||||
{
|
||||
const QUrl &url = filePath.toUrl();
|
||||
return url.scheme() == "docker" && url.host() == d->m_data.imageId;
|
||||
}
|
||||
|
||||
bool DockerDevice::isExecutableFile(const FilePath &filePath) const
|
||||
{
|
||||
QTC_ASSERT(handlesFile(filePath), return false);
|
||||
tryCreateLocalFileAccess();
|
||||
if (hasLocalFileAccess()) {
|
||||
const FilePath localAccess = mapToLocalAccess(filePath);
|
||||
const bool res = localAccess.isExecutableFile();
|
||||
LOG("Executable? " << filePath.toUserOutput() << localAccess.toUserOutput() << res);
|
||||
return res;
|
||||
}
|
||||
const QString path = filePath.toUrl().path();
|
||||
const CommandLine cmd("test", {"-x", path});
|
||||
const int exitCode = runSynchronously(cmd);
|
||||
return exitCode == 0;
|
||||
}
|
||||
|
||||
bool DockerDevice::isReadableFile(const FilePath &filePath) const
|
||||
{
|
||||
QTC_ASSERT(handlesFile(filePath), return false);
|
||||
tryCreateLocalFileAccess();
|
||||
if (hasLocalFileAccess()) {
|
||||
const FilePath localAccess = mapToLocalAccess(filePath);
|
||||
const bool res = localAccess.isReadableFile();
|
||||
LOG("ReadableFile? " << filePath.toUserOutput() << localAccess.toUserOutput() << res);
|
||||
return res;
|
||||
}
|
||||
const QString path = filePath.toUrl().path();
|
||||
const CommandLine cmd("test", {"-r", path, "-a", "-f", path});
|
||||
const int exitCode = runSynchronously(cmd);
|
||||
return exitCode == 0;
|
||||
}
|
||||
|
||||
bool DockerDevice::isReadableDirectory(const FilePath &filePath) const
|
||||
{
|
||||
QTC_ASSERT(handlesFile(filePath), return false);
|
||||
tryCreateLocalFileAccess();
|
||||
if (hasLocalFileAccess()) {
|
||||
const FilePath localAccess = mapToLocalAccess(filePath);
|
||||
const bool res = localAccess.isReadableDir();
|
||||
LOG("ReadableDirectory? " << filePath.toUserOutput() << localAccess.toUserOutput() << res);
|
||||
return res;
|
||||
}
|
||||
const QString path = filePath.toUrl().path();
|
||||
const CommandLine cmd("test", {"-x", path, "-a", "-d", path});
|
||||
const int exitCode = runSynchronously(cmd);
|
||||
return exitCode == 0;
|
||||
}
|
||||
|
||||
bool DockerDevice::isWritableDirectory(const FilePath &filePath) const
|
||||
{
|
||||
QTC_ASSERT(handlesFile(filePath), return false);
|
||||
tryCreateLocalFileAccess();
|
||||
if (hasLocalFileAccess()) {
|
||||
const FilePath localAccess = mapToLocalAccess(filePath);
|
||||
const bool res = localAccess.isReadableDir();
|
||||
LOG("WritableDirectory? " << filePath.toUserOutput() << localAccess.toUserOutput() << res);
|
||||
return res;
|
||||
}
|
||||
const QString path = filePath.toUrl().path();
|
||||
const CommandLine cmd("test", {"-x", path, "-a", "-d", path});
|
||||
const int exitCode = runSynchronously(cmd);
|
||||
return exitCode == 0;
|
||||
}
|
||||
|
||||
bool DockerDevice::createDirectory(const FilePath &filePath) const
|
||||
{
|
||||
QTC_ASSERT(handlesFile(filePath), return false);
|
||||
tryCreateLocalFileAccess();
|
||||
if (hasLocalFileAccess()) {
|
||||
const FilePath localAccess = mapToLocalAccess(filePath);
|
||||
const bool res = localAccess.createDir();
|
||||
LOG("CreateDirectory? " << filePath.toUserOutput() << localAccess.toUserOutput() << res);
|
||||
return res;
|
||||
}
|
||||
const QString path = filePath.toUrl().path();
|
||||
const CommandLine cmd("mkdir", {"-p", path});
|
||||
const int exitCode = runSynchronously(cmd);
|
||||
return exitCode == 0;
|
||||
}
|
||||
|
||||
QList<FilePath> DockerDevice::directoryEntries(const FilePath &filePath,
|
||||
const QStringList &nameFilters,
|
||||
QDir::Filters filters) const
|
||||
{
|
||||
QTC_ASSERT(handlesFile(filePath), return {});
|
||||
tryCreateLocalFileAccess();
|
||||
if (hasLocalFileAccess()) {
|
||||
const FilePath localAccess = mapToLocalAccess(filePath);
|
||||
const QFileInfoList entryInfoList = QDir(localAccess.toString()).entryInfoList(nameFilters, filters);
|
||||
return Utils::transform(entryInfoList, [this](const QFileInfo &fi) {
|
||||
return mapFromLocalAccess(fi.absoluteFilePath());
|
||||
});
|
||||
}
|
||||
QTC_CHECK(false); // FIXME: Implement
|
||||
return {};
|
||||
}
|
||||
|
||||
QByteArray DockerDevice::fileContents(const FilePath &filePath, int limit) const
|
||||
{
|
||||
QTC_ASSERT(handlesFile(filePath), return {});
|
||||
tryCreateLocalFileAccess();
|
||||
if (hasLocalFileAccess())
|
||||
return mapToLocalAccess(filePath).fileContents(limit);
|
||||
|
||||
QTC_CHECK(false); // FIXME: Implement
|
||||
return {};
|
||||
}
|
||||
|
||||
void DockerDevice::runProcess(QtcProcess &process) const
|
||||
{
|
||||
CommandLine dcmd{"docker", {"exec"}};
|
||||
dcmd.addArgs({"-w", process.workingDirectory()});
|
||||
dcmd.addArg(d->m_container);
|
||||
dcmd.addArgs(process.commandLine());
|
||||
|
||||
LOG("Run" << dcmd.toUserOutput());
|
||||
|
||||
process.setCommand(dcmd);
|
||||
process.start();
|
||||
}
|
||||
|
||||
int DockerDevice::runSynchronously(const CommandLine &cmd, QByteArray *out, QByteArray *err) const
|
||||
{
|
||||
CommandLine dcmd{"docker", {"exec", d->m_container}};
|
||||
dcmd.addArgs(cmd);
|
||||
|
||||
QtcProcess proc;
|
||||
proc.setCommand(dcmd);
|
||||
proc.setWorkingDirectory("/tmp");
|
||||
proc.start();
|
||||
proc.waitForFinished();
|
||||
|
||||
if (out)
|
||||
*out = proc.readAllStandardOutput();
|
||||
if (err)
|
||||
*err = proc.readAllStandardError();
|
||||
|
||||
LOG("Run sync:" << dcmd.toUserOutput() << " result: " << proc.exitCode());
|
||||
return proc.exitCode();
|
||||
}
|
||||
|
||||
// Factory
|
||||
|
||||
DockerDeviceFactory::DockerDeviceFactory()
|
||||
@@ -427,13 +719,16 @@ public:
|
||||
: QDialog(ICore::dialogParent())
|
||||
{
|
||||
setWindowTitle(tr("Docker Image Selection"));
|
||||
resize(800, 600);
|
||||
|
||||
m_model.setHeader({"Image", "Repository", "Tag", "Size"});
|
||||
|
||||
m_view = new TreeView;
|
||||
m_view->setModel(&m_model);
|
||||
m_view->header()->setStretchLastSection(true);
|
||||
m_view->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
|
||||
m_view->setSelectionBehavior(QAbstractItemView::SelectRows);
|
||||
m_view->setSelectionMode(QAbstractItemView::SingleSelection);
|
||||
m_view->setModel(&m_model);
|
||||
|
||||
auto output = new QTextBrowser;
|
||||
output->setEnabled(false);
|
||||
@@ -504,6 +799,8 @@ public:
|
||||
device->setupId(IDevice::ManuallyAdded, Utils::Id());
|
||||
device->setType(Constants::DOCKER_DEVICE_TYPE);
|
||||
device->setMachineType(IDevice::Hardware);
|
||||
device->autoDetectToolChains();
|
||||
device->autoDetectQtVersion();
|
||||
return device;
|
||||
}
|
||||
|
||||
|
@@ -52,7 +52,9 @@ public:
|
||||
using Ptr = QSharedPointer<DockerDevice>;
|
||||
using ConstPtr = QSharedPointer<const DockerDevice>;
|
||||
|
||||
explicit DockerDevice(const DockerDeviceData &data);
|
||||
~DockerDevice();
|
||||
|
||||
static Ptr create(const DockerDeviceData &data) { return Ptr(new DockerDevice(data)); }
|
||||
|
||||
ProjectExplorer::IDeviceWidget *createWidget() override;
|
||||
@@ -68,15 +70,40 @@ public:
|
||||
ProjectExplorer::DeviceProcessSignalOperation::Ptr signalOperation() const override;
|
||||
ProjectExplorer::DeviceEnvironmentFetcher::Ptr environmentFetcher() const override;
|
||||
|
||||
Utils::FilePath mapToGlobalPath(const Utils::FilePath &pathOnDevice) const override;
|
||||
|
||||
bool handlesFile(const Utils::FilePath &filePath) const override;
|
||||
bool isExecutableFile(const Utils::FilePath &filePath) const override;
|
||||
bool isReadableFile(const Utils::FilePath &filePath) const override;
|
||||
bool isReadableDirectory(const Utils::FilePath &filePath) const override;
|
||||
bool isWritableDirectory(const Utils::FilePath &filePath) const override;
|
||||
bool createDirectory(const Utils::FilePath &filePath) const override;
|
||||
QList<Utils::FilePath> directoryEntries(const Utils::FilePath &filePath,
|
||||
const QStringList &nameFilters,
|
||||
QDir::Filters filters) const override;
|
||||
QByteArray fileContents(const Utils::FilePath &filePath, int limit) const override;
|
||||
void runProcess(Utils::QtcProcess &process) const override;
|
||||
|
||||
int runSynchronously(const Utils::CommandLine &cmd,
|
||||
QByteArray *out = nullptr,
|
||||
QByteArray *err = nullptr) const override;
|
||||
|
||||
const DockerDeviceData &data() const;
|
||||
void autoDetectQtVersion() const;
|
||||
void autoDetectToolChains();
|
||||
|
||||
void tryCreateLocalFileAccess() const;
|
||||
bool hasLocalFileAccess() const;
|
||||
|
||||
Utils::FilePath mapToLocalAccess(const Utils::FilePath &filePath) const;
|
||||
Utils::FilePath mapFromLocalAccess(const Utils::FilePath &filePath) const;
|
||||
Utils::FilePath mapFromLocalAccess(const QString &filePath) const;
|
||||
|
||||
private:
|
||||
explicit DockerDevice(const DockerDeviceData &data);
|
||||
|
||||
void fromMap(const QVariantMap &map) final;
|
||||
QVariantMap toMap() const final;
|
||||
|
||||
DockerDeviceData m_data;
|
||||
class DockerDevicePrivate *d = nullptr;
|
||||
};
|
||||
|
||||
class DockerDeviceFactory final : public ProjectExplorer::IDeviceFactory
|
||||
|
Reference in New Issue
Block a user