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 "cmakeprojectmanagertr.h"
|
||||
#include "cmaketool.h"
|
||||
#include "cmaketoolmanager.h"
|
||||
|
||||
#include <android/androidconstants.h>
|
||||
|
||||
@@ -438,7 +439,7 @@ CommandLine CMakeBuildStep::cmakeCommand() const
|
||||
if (buildConfiguration())
|
||||
buildDirectory = buildConfiguration()->buildDirectory();
|
||||
|
||||
cmd.addArgs({"--build", buildDirectory.path()});
|
||||
cmd.addArgs({"--build", CMakeToolManager::mappedFilePath(buildDirectory).path()});
|
||||
|
||||
cmd.addArg("--target");
|
||||
cmd.addArgs(Utils::transform(m_buildTargets, [this](const QString &s) {
|
||||
|
@@ -13,6 +13,7 @@
|
||||
#include "cmakeprojectconstants.h"
|
||||
#include "cmakeprojectmanagertr.h"
|
||||
#include "cmakespecificsettings.h"
|
||||
#include "cmaketoolmanager.h"
|
||||
#include "projecttreehelper.h"
|
||||
|
||||
#include <android/androidconstants.h>
|
||||
@@ -2292,7 +2293,7 @@ MakeInstallCommand CMakeBuildSystem::makeInstallCommand(const FilePath &installR
|
||||
buildDirectory = bc->buildDirectory();
|
||||
|
||||
cmd.command.addArg("--build");
|
||||
cmd.command.addArg(buildDirectory.path());
|
||||
cmd.command.addArg(CMakeToolManager::mappedFilePath(buildDirectory).path());
|
||||
cmd.command.addArg("--target");
|
||||
cmd.command.addArg(installTarget);
|
||||
|
||||
|
@@ -8,6 +8,7 @@
|
||||
#include "cmakeprojectconstants.h"
|
||||
#include "cmakeprojectmanagertr.h"
|
||||
#include "cmakespecificsettings.h"
|
||||
#include "cmaketoolmanager.h"
|
||||
|
||||
#include <coreplugin/progressmanager/processprogress.h>
|
||||
#include <projectexplorer/buildsystem.h>
|
||||
@@ -144,7 +145,10 @@ void CMakeProcess::run(const BuildDirParameters ¶meters, const QStringList &
|
||||
});
|
||||
|
||||
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);
|
||||
|
||||
TaskHub::clearTasks(ProjectExplorer::Constants::TASK_CATEGORY_BUILDSYSTEM);
|
||||
|
@@ -11,6 +11,7 @@
|
||||
|
||||
#include <projectexplorer/projectexplorerconstants.h>
|
||||
|
||||
#include <utils/hostosinfo.h>
|
||||
#include <utils/layoutbuilder.h>
|
||||
|
||||
using namespace Utils;
|
||||
@@ -34,6 +35,7 @@ CMakeSpecificSettings::CMakeSpecificSettings()
|
||||
askBeforePresetsReload,
|
||||
showSourceSubFolders,
|
||||
showAdvancedOptionsByDefault,
|
||||
useJunctionsForSourceAndBuildDirectories,
|
||||
st
|
||||
};
|
||||
});
|
||||
@@ -87,6 +89,21 @@ CMakeSpecificSettings::CMakeSpecificSettings()
|
||||
showAdvancedOptionsByDefault.setLabelText(
|
||||
::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();
|
||||
}
|
||||
|
||||
|
@@ -19,6 +19,7 @@ public:
|
||||
Utils::BoolAspect askBeforePresetsReload{this};
|
||||
Utils::BoolAspect showSourceSubFolders{this};
|
||||
Utils::BoolAspect showAdvancedOptionsByDefault{this};
|
||||
Utils::BoolAspect useJunctionsForSourceAndBuildDirectories{this};
|
||||
};
|
||||
|
||||
CMakeSpecificSettings &settings();
|
||||
|
@@ -25,19 +25,66 @@
|
||||
|
||||
#include <nanotrace/nanotrace.h>
|
||||
|
||||
#include <QCryptographicHash>
|
||||
#include <QStandardPaths>
|
||||
#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 Utils;
|
||||
|
||||
namespace CMakeProjectManager {
|
||||
|
||||
static Q_LOGGING_CATEGORY(cmakeToolManagerLog, "qtc.cmake.toolmanager", QtWarningMsg);
|
||||
|
||||
class CMakeToolManagerPrivate
|
||||
{
|
||||
public:
|
||||
Id m_defaultCMake;
|
||||
std::vector<std::unique_ptr<CMakeTool>> m_cmakeTools;
|
||||
Internal::CMakeToolSettingsAccessor m_accessor;
|
||||
FilePath m_junctionsDir;
|
||||
int m_junctionsHashLength = 32;
|
||||
|
||||
CMakeToolManagerPrivate();
|
||||
};
|
||||
|
||||
class HtmlHandler : public rst::ContentHandler
|
||||
@@ -313,6 +360,65 @@ void CMakeToolManager::updateDocumentation()
|
||||
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)
|
||||
{
|
||||
static QHash<FilePath, QString> map;
|
||||
@@ -335,6 +441,32 @@ QString CMakeToolManager::toolTipForRstHelpFile(const FilePath &helpFile)
|
||||
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,
|
||||
const QString &detectionSource,
|
||||
QString *logMessage)
|
||||
@@ -441,4 +573,28 @@ void Internal::setupCMakeToolManager(QObject *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
|
||||
|
@@ -44,6 +44,8 @@ public:
|
||||
|
||||
static QString toolTipForRstHelpFile(const Utils::FilePath &helpFile);
|
||||
|
||||
static Utils::FilePath mappedFilePath(const Utils::FilePath &path);
|
||||
|
||||
public slots:
|
||||
QList<Utils::Id> autoDetectCMakeForDevice(const Utils::FilePaths &searchPaths,
|
||||
const QString &detectionSource,
|
||||
|
Reference in New Issue
Block a user