forked from qt-creator/qt-creator
CMakePM: Use junctions for source/build dirs on Windows
This way we have fixed small (max 64 bytes) paths for CMake's configure / build / install steps. This allows the user to have longer paths and still compile with MSVC / GCC MinGW compilers. Fixes: QTCREATORBUG-26786 Task-number: QTBUG-117413 Change-Id: I0cff6521626dd2ce78d0223d46f0b480e977d5c5 Reviewed-by: <github-actions-qt-creator@cristianadam.eu> Reviewed-by: Alessandro Portale <alessandro.portale@qt.io>
This commit is contained in:
@@ -11,6 +11,7 @@
|
|||||||
#include "cmakeprojectconstants.h"
|
#include "cmakeprojectconstants.h"
|
||||||
#include "cmakeprojectmanagertr.h"
|
#include "cmakeprojectmanagertr.h"
|
||||||
#include "cmaketool.h"
|
#include "cmaketool.h"
|
||||||
|
#include "cmaketoolmanager.h"
|
||||||
|
|
||||||
#include <android/androidconstants.h>
|
#include <android/androidconstants.h>
|
||||||
|
|
||||||
@@ -438,7 +439,7 @@ CommandLine CMakeBuildStep::cmakeCommand() const
|
|||||||
if (buildConfiguration())
|
if (buildConfiguration())
|
||||||
buildDirectory = buildConfiguration()->buildDirectory();
|
buildDirectory = buildConfiguration()->buildDirectory();
|
||||||
|
|
||||||
cmd.addArgs({"--build", buildDirectory.path()});
|
cmd.addArgs({"--build", CMakeToolManager::mappedFilePath(buildDirectory).path()});
|
||||||
|
|
||||||
cmd.addArg("--target");
|
cmd.addArg("--target");
|
||||||
cmd.addArgs(Utils::transform(m_buildTargets, [this](const QString &s) {
|
cmd.addArgs(Utils::transform(m_buildTargets, [this](const QString &s) {
|
||||||
|
@@ -13,6 +13,7 @@
|
|||||||
#include "cmakeprojectconstants.h"
|
#include "cmakeprojectconstants.h"
|
||||||
#include "cmakeprojectmanagertr.h"
|
#include "cmakeprojectmanagertr.h"
|
||||||
#include "cmakespecificsettings.h"
|
#include "cmakespecificsettings.h"
|
||||||
|
#include "cmaketoolmanager.h"
|
||||||
#include "projecttreehelper.h"
|
#include "projecttreehelper.h"
|
||||||
|
|
||||||
#include <android/androidconstants.h>
|
#include <android/androidconstants.h>
|
||||||
@@ -2292,7 +2293,7 @@ MakeInstallCommand CMakeBuildSystem::makeInstallCommand(const FilePath &installR
|
|||||||
buildDirectory = bc->buildDirectory();
|
buildDirectory = bc->buildDirectory();
|
||||||
|
|
||||||
cmd.command.addArg("--build");
|
cmd.command.addArg("--build");
|
||||||
cmd.command.addArg(buildDirectory.path());
|
cmd.command.addArg(CMakeToolManager::mappedFilePath(buildDirectory).path());
|
||||||
cmd.command.addArg("--target");
|
cmd.command.addArg("--target");
|
||||||
cmd.command.addArg(installTarget);
|
cmd.command.addArg(installTarget);
|
||||||
|
|
||||||
|
@@ -8,6 +8,7 @@
|
|||||||
#include "cmakeprojectconstants.h"
|
#include "cmakeprojectconstants.h"
|
||||||
#include "cmakeprojectmanagertr.h"
|
#include "cmakeprojectmanagertr.h"
|
||||||
#include "cmakespecificsettings.h"
|
#include "cmakespecificsettings.h"
|
||||||
|
#include "cmaketoolmanager.h"
|
||||||
|
|
||||||
#include <coreplugin/progressmanager/processprogress.h>
|
#include <coreplugin/progressmanager/processprogress.h>
|
||||||
#include <projectexplorer/buildsystem.h>
|
#include <projectexplorer/buildsystem.h>
|
||||||
@@ -144,7 +145,10 @@ void CMakeProcess::run(const BuildDirParameters ¶meters, const QStringList &
|
|||||||
});
|
});
|
||||||
|
|
||||||
CommandLine commandLine(cmakeExecutable);
|
CommandLine commandLine(cmakeExecutable);
|
||||||
commandLine.addArgs({"-S", sourceDirectory.path(), "-B", buildDirectory.path()});
|
commandLine.addArgs({"-S",
|
||||||
|
CMakeToolManager::mappedFilePath(sourceDirectory).path(),
|
||||||
|
"-B",
|
||||||
|
CMakeToolManager::mappedFilePath(buildDirectory).path()});
|
||||||
commandLine.addArgs(arguments);
|
commandLine.addArgs(arguments);
|
||||||
|
|
||||||
TaskHub::clearTasks(ProjectExplorer::Constants::TASK_CATEGORY_BUILDSYSTEM);
|
TaskHub::clearTasks(ProjectExplorer::Constants::TASK_CATEGORY_BUILDSYSTEM);
|
||||||
|
@@ -11,6 +11,7 @@
|
|||||||
|
|
||||||
#include <projectexplorer/projectexplorerconstants.h>
|
#include <projectexplorer/projectexplorerconstants.h>
|
||||||
|
|
||||||
|
#include <utils/hostosinfo.h>
|
||||||
#include <utils/layoutbuilder.h>
|
#include <utils/layoutbuilder.h>
|
||||||
|
|
||||||
using namespace Utils;
|
using namespace Utils;
|
||||||
@@ -34,6 +35,7 @@ CMakeSpecificSettings::CMakeSpecificSettings()
|
|||||||
askBeforePresetsReload,
|
askBeforePresetsReload,
|
||||||
showSourceSubFolders,
|
showSourceSubFolders,
|
||||||
showAdvancedOptionsByDefault,
|
showAdvancedOptionsByDefault,
|
||||||
|
useJunctionsForSourceAndBuildDirectories,
|
||||||
st
|
st
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
@@ -87,6 +89,21 @@ CMakeSpecificSettings::CMakeSpecificSettings()
|
|||||||
showAdvancedOptionsByDefault.setLabelText(
|
showAdvancedOptionsByDefault.setLabelText(
|
||||||
::CMakeProjectManager::Tr::tr("Show advanced options by default"));
|
::CMakeProjectManager::Tr::tr("Show advanced options by default"));
|
||||||
|
|
||||||
|
useJunctionsForSourceAndBuildDirectories.setSettingsKey(
|
||||||
|
"UseJunctionsForSourceAndBuildDirectories");
|
||||||
|
useJunctionsForSourceAndBuildDirectories.setDefaultValue(false);
|
||||||
|
useJunctionsForSourceAndBuildDirectories.setLabelText(::CMakeProjectManager::Tr::tr(
|
||||||
|
"Use Junctions for CMake configuration and build operations"));
|
||||||
|
useJunctionsForSourceAndBuildDirectories.setVisible(Utils::HostOsInfo().isWindowsHost());
|
||||||
|
useJunctionsForSourceAndBuildDirectories.setToolTip(::CMakeProjectManager::Tr::tr(
|
||||||
|
"Create and use junctions for the source and build directories. This helps to overcome "
|
||||||
|
"issues with long paths on Windows.<br><br>"
|
||||||
|
"They are stored under <tt>C:\\ProgramData\\QtCreator\\Links</tt> (overridable via "
|
||||||
|
"<tt>QTC_CMAKE_JUNCTIONS_DIR</tt> environment variable).<br><br>"
|
||||||
|
"With <tt>QTC_CMAKE_JUNCTIONS_HASH_LENGTH</tt> the MD5 hash key length can be shortened "
|
||||||
|
"to a value smaller than the default length value of 32.<br><br>"
|
||||||
|
"They are used for CMake configure, build and install operations."));
|
||||||
|
|
||||||
readSettings();
|
readSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -19,6 +19,7 @@ public:
|
|||||||
Utils::BoolAspect askBeforePresetsReload{this};
|
Utils::BoolAspect askBeforePresetsReload{this};
|
||||||
Utils::BoolAspect showSourceSubFolders{this};
|
Utils::BoolAspect showSourceSubFolders{this};
|
||||||
Utils::BoolAspect showAdvancedOptionsByDefault{this};
|
Utils::BoolAspect showAdvancedOptionsByDefault{this};
|
||||||
|
Utils::BoolAspect useJunctionsForSourceAndBuildDirectories{this};
|
||||||
};
|
};
|
||||||
|
|
||||||
CMakeSpecificSettings &settings();
|
CMakeSpecificSettings &settings();
|
||||||
|
@@ -25,19 +25,66 @@
|
|||||||
|
|
||||||
#include <nanotrace/nanotrace.h>
|
#include <nanotrace/nanotrace.h>
|
||||||
|
|
||||||
|
#include <QCryptographicHash>
|
||||||
|
#include <QStandardPaths>
|
||||||
#include <stack>
|
#include <stack>
|
||||||
|
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
#include <qt_windows.h>
|
||||||
|
#include <winioctl.h>
|
||||||
|
|
||||||
|
// taken from qtbase/src/corelib/io/qfilesystemengine_win.cpp
|
||||||
|
#if !defined(REPARSE_DATA_BUFFER_HEADER_SIZE)
|
||||||
|
typedef struct _REPARSE_DATA_BUFFER {
|
||||||
|
ULONG ReparseTag;
|
||||||
|
USHORT ReparseDataLength;
|
||||||
|
USHORT Reserved;
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
USHORT SubstituteNameOffset;
|
||||||
|
USHORT SubstituteNameLength;
|
||||||
|
USHORT PrintNameOffset;
|
||||||
|
USHORT PrintNameLength;
|
||||||
|
ULONG Flags;
|
||||||
|
WCHAR PathBuffer[1];
|
||||||
|
} SymbolicLinkReparseBuffer;
|
||||||
|
struct {
|
||||||
|
USHORT SubstituteNameOffset;
|
||||||
|
USHORT SubstituteNameLength;
|
||||||
|
USHORT PrintNameOffset;
|
||||||
|
USHORT PrintNameLength;
|
||||||
|
WCHAR PathBuffer[1];
|
||||||
|
} MountPointReparseBuffer;
|
||||||
|
struct {
|
||||||
|
UCHAR DataBuffer[1];
|
||||||
|
} GenericReparseBuffer;
|
||||||
|
};
|
||||||
|
} REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;
|
||||||
|
# define REPARSE_DATA_BUFFER_HEADER_SIZE FIELD_OFFSET(REPARSE_DATA_BUFFER, GenericReparseBuffer)
|
||||||
|
#endif // !defined(REPARSE_DATA_BUFFER_HEADER_SIZE)
|
||||||
|
|
||||||
|
#ifndef FSCTL_SET_REPARSE_POINT
|
||||||
|
#define FSCTL_SET_REPARSE_POINT CTL_CODE(FILE_DEVICE_FILE_SYSTEM,41,METHOD_BUFFERED,FILE_ANY_ACCESS)
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
using namespace Core;
|
using namespace Core;
|
||||||
using namespace Utils;
|
using namespace Utils;
|
||||||
|
|
||||||
namespace CMakeProjectManager {
|
namespace CMakeProjectManager {
|
||||||
|
|
||||||
|
static Q_LOGGING_CATEGORY(cmakeToolManagerLog, "qtc.cmake.toolmanager", QtWarningMsg);
|
||||||
|
|
||||||
class CMakeToolManagerPrivate
|
class CMakeToolManagerPrivate
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Id m_defaultCMake;
|
Id m_defaultCMake;
|
||||||
std::vector<std::unique_ptr<CMakeTool>> m_cmakeTools;
|
std::vector<std::unique_ptr<CMakeTool>> m_cmakeTools;
|
||||||
Internal::CMakeToolSettingsAccessor m_accessor;
|
Internal::CMakeToolSettingsAccessor m_accessor;
|
||||||
|
FilePath m_junctionsDir;
|
||||||
|
int m_junctionsHashLength = 32;
|
||||||
|
|
||||||
|
CMakeToolManagerPrivate();
|
||||||
};
|
};
|
||||||
|
|
||||||
class HtmlHandler : public rst::ContentHandler
|
class HtmlHandler : public rst::ContentHandler
|
||||||
@@ -313,6 +360,65 @@ void CMakeToolManager::updateDocumentation()
|
|||||||
Core::HelpManager::registerDocumentation(docs);
|
Core::HelpManager::registerDocumentation(docs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void createJunction(const FilePath &from, const FilePath &to)
|
||||||
|
{
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
to.createDir();
|
||||||
|
const QString toString = to.path();
|
||||||
|
|
||||||
|
HANDLE handle = ::CreateFile((wchar_t *) toString.utf16(),
|
||||||
|
GENERIC_WRITE,
|
||||||
|
0,
|
||||||
|
nullptr,
|
||||||
|
OPEN_EXISTING,
|
||||||
|
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT,
|
||||||
|
nullptr);
|
||||||
|
if (handle == INVALID_HANDLE_VALUE) {
|
||||||
|
qCDebug(cmakeToolManagerLog())
|
||||||
|
<< "Failed to open" << toString << "to create a junction." << ::GetLastError();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString fromString("\\??\\");
|
||||||
|
fromString.append(from.absoluteFilePath().nativePath());
|
||||||
|
|
||||||
|
auto fromStringLength = uint16_t(fromString.length() * sizeof(wchar_t));
|
||||||
|
auto toStringLength = uint16_t(toString.length() * sizeof(wchar_t));
|
||||||
|
auto reparseDataLength = fromStringLength + toStringLength + 12;
|
||||||
|
|
||||||
|
std::vector<char> buf(reparseDataLength + REPARSE_DATA_BUFFER_HEADER_SIZE, 0);
|
||||||
|
REPARSE_DATA_BUFFER &reparse = *reinterpret_cast<REPARSE_DATA_BUFFER *>(buf.data());
|
||||||
|
|
||||||
|
reparse.ReparseTag = IO_REPARSE_TAG_MOUNT_POINT;
|
||||||
|
reparse.ReparseDataLength = reparseDataLength;
|
||||||
|
|
||||||
|
reparse.MountPointReparseBuffer.SubstituteNameOffset = 0;
|
||||||
|
reparse.MountPointReparseBuffer.SubstituteNameLength = fromStringLength;
|
||||||
|
fromString.toWCharArray(reparse.MountPointReparseBuffer.PathBuffer);
|
||||||
|
|
||||||
|
reparse.MountPointReparseBuffer.PrintNameOffset = fromStringLength + sizeof(UNICODE_NULL);
|
||||||
|
reparse.MountPointReparseBuffer.PrintNameLength = toStringLength;
|
||||||
|
toString.toWCharArray(reparse.MountPointReparseBuffer.PathBuffer + fromString.length() + 1);
|
||||||
|
|
||||||
|
DWORD retsize = 0;
|
||||||
|
if (!::DeviceIoControl(handle,
|
||||||
|
FSCTL_SET_REPARSE_POINT,
|
||||||
|
&reparse,
|
||||||
|
uint16_t(buf.size()),
|
||||||
|
nullptr,
|
||||||
|
0,
|
||||||
|
&retsize,
|
||||||
|
nullptr)) {
|
||||||
|
qCDebug(cmakeToolManagerLog()) << "Failed to create junction from" << fromString << "to"
|
||||||
|
<< toString << "GetLastError:" << ::GetLastError();
|
||||||
|
}
|
||||||
|
::CloseHandle(handle);
|
||||||
|
#else
|
||||||
|
Q_UNUSED(from)
|
||||||
|
Q_UNUSED(to)
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
QString CMakeToolManager::toolTipForRstHelpFile(const FilePath &helpFile)
|
QString CMakeToolManager::toolTipForRstHelpFile(const FilePath &helpFile)
|
||||||
{
|
{
|
||||||
static QHash<FilePath, QString> map;
|
static QHash<FilePath, QString> map;
|
||||||
@@ -335,6 +441,32 @@ QString CMakeToolManager::toolTipForRstHelpFile(const FilePath &helpFile)
|
|||||||
return tooltip;
|
return tooltip;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FilePath CMakeToolManager::mappedFilePath(const FilePath &path)
|
||||||
|
{
|
||||||
|
if (!HostOsInfo::isWindowsHost())
|
||||||
|
return path;
|
||||||
|
|
||||||
|
if (path.needsDevice())
|
||||||
|
return path;
|
||||||
|
|
||||||
|
Internal::settings();
|
||||||
|
if (!Internal::settings().useJunctionsForSourceAndBuildDirectories())
|
||||||
|
return path;
|
||||||
|
|
||||||
|
if (!d->m_junctionsDir.isDir())
|
||||||
|
return path;
|
||||||
|
|
||||||
|
const auto hashPath = QString::fromUtf8(
|
||||||
|
QCryptographicHash::hash(path.path().toUtf8(), QCryptographicHash::Md5).toHex(0));
|
||||||
|
const auto fullHashPath = d->m_junctionsDir.pathAppended(
|
||||||
|
hashPath.left(d->m_junctionsHashLength));
|
||||||
|
|
||||||
|
if (!fullHashPath.exists())
|
||||||
|
createJunction(path, fullHashPath);
|
||||||
|
|
||||||
|
return fullHashPath.exists() ? fullHashPath : path;
|
||||||
|
}
|
||||||
|
|
||||||
QList<Id> CMakeToolManager::autoDetectCMakeForDevice(const FilePaths &searchPaths,
|
QList<Id> CMakeToolManager::autoDetectCMakeForDevice(const FilePaths &searchPaths,
|
||||||
const QString &detectionSource,
|
const QString &detectionSource,
|
||||||
QString *logMessage)
|
QString *logMessage)
|
||||||
@@ -441,4 +573,28 @@ void Internal::setupCMakeToolManager(QObject *guard)
|
|||||||
m_instance->setParent(guard);
|
m_instance->setParent(guard);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CMakeToolManagerPrivate::CMakeToolManagerPrivate()
|
||||||
|
{
|
||||||
|
if (HostOsInfo::isWindowsHost()) {
|
||||||
|
const QStringList locations = QStandardPaths::standardLocations(
|
||||||
|
QStandardPaths::GenericConfigLocation);
|
||||||
|
m_junctionsDir = FilePath::fromString(*std::min_element(locations.begin(), locations.end()))
|
||||||
|
.pathAppended("QtCreator/Links");
|
||||||
|
|
||||||
|
if (Utils::qtcEnvironmentVariableIsSet("QTC_CMAKE_JUNCTIONS_DIR")) {
|
||||||
|
m_junctionsDir = FilePath::fromUserInput(
|
||||||
|
Utils::qtcEnvironmentVariable("QTC_CMAKE_JUNCTIONS_DIR"));
|
||||||
|
}
|
||||||
|
if (Utils::qtcEnvironmentVariableIsSet("QTC_CMAKE_JUNCTIONS_HASH_LENGTH")) {
|
||||||
|
bool ok = false;
|
||||||
|
const int hashLength
|
||||||
|
= Utils::qtcEnvironmentVariableIntValue("QTC_CMAKE_JUNCTIONS_HASH_LENGTH", &ok);
|
||||||
|
if (ok && hashLength >= 4 && hashLength < 32)
|
||||||
|
m_junctionsHashLength = hashLength;
|
||||||
|
}
|
||||||
|
if (!m_junctionsDir.exists())
|
||||||
|
m_junctionsDir.createDir();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // CMakeProjectManager
|
} // CMakeProjectManager
|
||||||
|
@@ -44,6 +44,8 @@ public:
|
|||||||
|
|
||||||
static QString toolTipForRstHelpFile(const Utils::FilePath &helpFile);
|
static QString toolTipForRstHelpFile(const Utils::FilePath &helpFile);
|
||||||
|
|
||||||
|
static Utils::FilePath mappedFilePath(const Utils::FilePath &path);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
QList<Utils::Id> autoDetectCMakeForDevice(const Utils::FilePaths &searchPaths,
|
QList<Utils::Id> autoDetectCMakeForDevice(const Utils::FilePaths &searchPaths,
|
||||||
const QString &detectionSource,
|
const QString &detectionSource,
|
||||||
|
Reference in New Issue
Block a user