McuSupport: Support expanding multiple wildcards in a path

To support defining default paths such as "Microsoft Visual Studio/2019
/*/VC/Tools/MSVC/*/bin/Hostx64/x64"

Change-Id: I889439a0f2a05b15121a28fbf2b50acde2e74968
Reviewed-by: <github-actions-qt-creator@cristianadam.eu>
Reviewed-by: Rainer Keller <Rainer.Keller@qt.io>
Reviewed-by: Eike Ziller <eike.ziller@qt.io>
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
This commit is contained in:
Yasser Grimes
2022-11-02 15:16:37 +02:00
parent 7e0d545e64
commit 3c6a40b30e
3 changed files with 89 additions and 23 deletions

View File

@@ -30,23 +30,45 @@ using namespace Utils;
namespace McuSupport::Internal { namespace McuSupport::Internal {
static const Utils::FilePath expandWildcards(const Utils::FilePath& path) // Utils::FileFilter do not support globbing with "*" placed in the middle of the path,
// since it is required for paths such as "Microsoft Visual Studio/2019/*/VC/Tools/MSVC/*/bin/Hostx64/x64"
// The filter is applied for each time a wildcard character is found in a path component.
// Returns a pair of the longest path if multiple ones exists and the number of components that were not found.
static const std::pair<Utils::FilePath, int> expandWildcards(
const FilePath path, const QList<QStringView> patternComponents)
{ {
if (!path.fileName().contains("*") && !path.fileName().contains("?")) // Only absolute paths are currently supported
return path; // Call FilePath::cleanPath on the path before calling this function
if (!path.exists() || path.isRelativePath())
return {path, patternComponents.size()};
const FilePath p = path.parentDir(); // All components are found
if (patternComponents.empty())
return {path, patternComponents.size()};
auto entries = p.dirEntries( const QString currentComponent = patternComponents.front().toString();
Utils::FileFilter({path.fileName()}, QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot)); FilePath currentPath = path / currentComponent;
if (entries.isEmpty()) if (!currentComponent.contains("*") && !currentComponent.contains("?") && currentPath.exists())
return path; return expandWildcards(path / currentComponent,
{patternComponents.constBegin() + 1, patternComponents.constEnd()});
auto entries = path.dirEntries(
Utils::FileFilter({currentComponent}, QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot));
std::pair<FilePath, int> retPair = {path, patternComponents.size()};
// Return the last match (can correspond to the latest version)
sort(entries, [](const FilePath &a, const FilePath &b) { return a.fileName() < b.fileName(); }); sort(entries, [](const FilePath &a, const FilePath &b) { return a.fileName() < b.fileName(); });
for (const auto &entry : entries) {
auto [entry_path, remaining_components] = expandWildcards(entry,
{patternComponents.constBegin()
+ 1,
patternComponents.constEnd()});
if (remaining_components <= retPair.second)
retPair = {entry_path, remaining_components};
}
return entries.last(); return retPair;
} }
Macros *McuSdkRepository::globalMacros() Macros *McuSdkRepository::globalMacros()
@@ -60,7 +82,32 @@ void McuSdkRepository::expandVariablesAndWildcards()
for (const auto &target : std::as_const(mcuTargets)) { for (const auto &target : std::as_const(mcuTargets)) {
auto macroExpander = getMacroExpander(*target); auto macroExpander = getMacroExpander(*target);
for (const auto &package : target->packages()) { for (const auto &package : target->packages()) {
package->setPath(expandWildcards(macroExpander->expand(package->path()))); // Expand variables
const auto path = macroExpander->expand(package->path());
//expand wildcards
// Ignore expanding if no wildcards are found
if (!path.path().contains("*") && !path.path().contains("?")) {
package->setPath(path);
continue;
}
QStringList pathComponents = path.cleanPath().path().split("/");
// Path components example on linux: {"", "home", "username"}
// Path components example on windows: {"C:", "Users", "username"}
// 2 for empty_split_entry(linux)|root(windows) + at least one component
if (pathComponents.size() < 2) {
package->setPath(path);
continue;
}
// drop empty_split_entry(linux)|root(windows)
pathComponents.pop_front();
package->setPath(
expandWildcards(FilePath::fromString(QDir::rootPath()),
{pathComponents.constBegin(), pathComponents.constEnd()})
.first);
} }
} }
} }

View File

@@ -1703,31 +1703,43 @@ void McuSupportTest::test_addToSystemPathFlag()
void McuSupportTest::test_processWildcards_data() void McuSupportTest::test_processWildcards_data()
{ {
QTest::addColumn<QString>("package_label"); QTest::addColumn<QString>("package_label");
QTest::addColumn<QString>("path"); QTest::addColumn<QString>("expected_path");
QTest::addColumn<bool>("isFile"); QTest::addColumn<QStringList>("paths");
QTest::newRow("\"*\" at the end") << "FAKE_WILDCARD_TEST_1" QTest::newRow("wildcard_at_the_end") << "FAKE_WILDCARD_TEST_1" << "folder-123" << QStringList {"folder-123/" };
<< "folder-123" << false; QTest::newRow("wildcard_in_th_middle") << "FAKE_WILDCARD_TEST_2" << "file-123.exe" << QStringList {"file-123.exe"};
QTest::newRow("\"*\" in the middle") << "FAKE_WILDCARD_TEST_2" QTest::newRow("wildcard_at_the_end") << "FAKE_WILDCARD_TEST_3" << "123-file.exe" << QStringList( "123-file.exe");
<< "file-123.exe" << true; QTest::newRow("multi_wildcards")
QTest::newRow("\"*\" at the start") << "FAKE_WILDCARD_TEST_3" << "FAKE_WILDCARD_TEST_MULTI"
<< "123-file.exe" << true; << "2019/Community/VC/Tools/MSVC/14.29.30133/bin/Hostx64/x64"
<< QStringList{
"2019/Community/VC/Tools/MSVC/14.29.30133/bin/Hostx64/x64/",
"2019/Alpha/Beta/Gamma/",
"2019/Community/VC/Tools/MSVC/",
"2019/Community/VC/Tools/MSVC/14.29.30133/bin/",
"2019/Community/VC/Tools/MSVC/14.29.30133/bin/Hostx64/",
"2019/Enterprise/VC/Tools/MSVC/",
};
} }
void McuSupportTest::test_processWildcards() void McuSupportTest::test_processWildcards()
{ {
QFETCH(QString, package_label); QFETCH(QString, package_label);
QFETCH(QString, path); QFETCH(QString, expected_path);
QFETCH(bool, isFile); QFETCH(QStringList, paths);
QVERIFY(createFakePath(testing_output_dir / "wildcards" / path, isFile)); for (const auto &path : paths)
QVERIFY(createFakePath(testing_output_dir / "wildcards" / path, !path.endsWith("/")));
auto [targets, packages] = createTestingKitTargetsAndPackages(wildcards_test_kit); auto [targets, packages] = createTestingKitTargetsAndPackages(wildcards_test_kit);
auto testWildcardsPackage = findOrDefault(packages, [&](const McuPackagePtr &pkg) { auto testWildcardsPackage = findOrDefault(packages, [&](const McuPackagePtr &pkg) {
return (pkg->label() == package_label); return (pkg->label() == package_label);
}); });
QVERIFY(testWildcardsPackage != nullptr); QVERIFY(testWildcardsPackage != nullptr);
QCOMPARE(testWildcardsPackage->path().toString(), FilePath(testing_output_dir / "wildcards" / path).toString()); QVERIFY(paths.size() > 0);
// FilePaths with "/" at the end and without it evaluate to different paths.
QCOMPARE(testWildcardsPackage->path(),
FilePath(testing_output_dir / "wildcards" / expected_path));
} }
void McuSupportTest::test_nonemptyVersionDetector() void McuSupportTest::test_nonemptyVersionDetector()

View File

@@ -34,6 +34,13 @@ constexpr auto wildcards_test_kit = R"(
"defaultValue": "%{MCU_TESTING_FOLDER}/wildcards/*-file.exe", "defaultValue": "%{MCU_TESTING_FOLDER}/wildcards/*-file.exe",
"envVar": "", "envVar": "",
"type": "path" "type": "path"
},
{
"label": "FAKE_WILDCARD_TEST_MULTI",
"description": "Assert '*' is replaced by possible values",
"defaultValue": "%{MCU_TESTING_FOLDER}/wildcards/2019/*/VC/Tools/MSVC/*/bin/Hostx64/x64",
"envVar": "",
"type": "path"
} }
] ]
}, },