Skip to content

Commit 066918c

Browse files
committed
Add -find switch to search files in root
Resolves #162
1 parent 64fe6e9 commit 066918c

36 files changed

+1082
-174
lines changed

docker/Tests/find.tests.ps1

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
# Copyright (C) Microsoft Corporation. All rights reserved.
2+
# Licensed under the MIT license. See LICENSE.txt in the project root for license information.
3+
4+
# Instances and results are sorted for consistency.
5+
Describe 'vswhere -sort -find' {
6+
BeforeAll {
7+
# Always write to 32-bit registry key.
8+
$key = New-Item -Path Registry::HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\VisualStudio\Setup\Reboot -Force
9+
$null = $key | New-ItemProperty -Name 3 -Value 1 -Force
10+
11+
$files = @(
12+
'MSBuild\15.0\Bin\MSBuild.exe'
13+
'MSBuild\15.0\Bin\MSBuild.exe.config'
14+
'MSBuild\15.0\Bin\amd64\MSBuild.exe'
15+
'MSBuild\15.0\Bin\amd64\MSBuild.exe.config'
16+
)
17+
# Populate each instance with files to find.
18+
$instances = C:\bin\vswhere.exe -all -prerelease -products * -format json | ConvertFrom-Json
19+
foreach ($file in $files) {
20+
$filePath = Join-Path -Path $instances.installationPath -ChildPath $file
21+
$null = New-Item -Path $filePath -ItemType 'File' -Value '1' -Force
22+
}
23+
}
24+
25+
Context 'msbuild\15.0\bin\msbuild.exe' {
26+
It 'returns 2 matches' {
27+
$files = C:\bin\vswhere.exe -sort -find 'msbuild\15.0\bin\msbuild.exe'
28+
29+
$files.Count | Should Be 2
30+
$files[0] | Should Be 'C:\VS\Enterprise\MSBuild\15.0\Bin\MSBuild.exe'
31+
$files[1] | Should Be 'C:\VS\Community\MSBuild\15.0\Bin\MSBuild.exe'
32+
}
33+
34+
It '-format json returns 2 matches' {
35+
$files = C:\bin\vswhere.exe -sort -find 'msbuild\15.0\bin\msbuild.exe' -format json | ConvertFrom-Json
36+
37+
$files.Count | Should Be 2
38+
$files[0] | Should Be 'C:\VS\Enterprise\MSBuild\15.0\Bin\MSBuild.exe'
39+
$files[1] | Should Be 'C:\VS\Community\MSBuild\15.0\Bin\MSBuild.exe'
40+
}
41+
42+
It '-format xml returns 2 matches' {
43+
$doc = [xml](C:\bin\vswhere.exe -sort -find 'msbuild\15.0\bin\msbuild.exe' -format xml)
44+
45+
$doc.files.file.Count | Should Be 2
46+
$doc.files.file[0] | Should Be 'C:\VS\Enterprise\MSBuild\15.0\Bin\MSBuild.exe'
47+
$doc.files.file[1] | Should Be 'C:\VS\Community\MSBuild\15.0\Bin\MSBuild.exe'
48+
}
49+
}
50+
51+
Context 'msbuild\**\msbuild.exe' {
52+
It 'returns 4 matches' {
53+
$files = C:\bin\vswhere.exe -sort -find 'msbuild\**\msbuild.exe'
54+
55+
$files.Count | Should Be 4
56+
$files[0] | Should Be 'C:\VS\Enterprise\MSBuild\15.0\Bin\amd64\MSBuild.exe'
57+
$files[1] | Should Be 'C:\VS\Enterprise\MSBuild\15.0\Bin\MSBuild.exe'
58+
$files[2] | Should Be 'C:\VS\Community\MSBuild\15.0\Bin\amd64\MSBuild.exe'
59+
$files[3] | Should Be 'C:\VS\Community\MSBuild\15.0\Bin\MSBuild.exe'
60+
}
61+
62+
It '-format json returns 4 matches' {
63+
$files = C:\bin\vswhere.exe -sort -find 'msbuild\**\msbuild.exe' -format json | ConvertFrom-Json
64+
65+
$files.Count | Should Be 4
66+
$files[0] | Should Be 'C:\VS\Enterprise\MSBuild\15.0\Bin\amd64\MSBuild.exe'
67+
$files[1] | Should Be 'C:\VS\Enterprise\MSBuild\15.0\Bin\MSBuild.exe'
68+
$files[2] | Should Be 'C:\VS\Community\MSBuild\15.0\Bin\amd64\MSBuild.exe'
69+
$files[3] | Should Be 'C:\VS\Community\MSBuild\15.0\Bin\MSBuild.exe'
70+
}
71+
72+
It '-format xml returns 4 matches' {
73+
$doc = [xml](C:\bin\vswhere.exe -sort -find 'msbuild\**\msbuild.exe' -format xml)
74+
75+
$doc.files.file.Count | Should Be 4
76+
$doc.files.file[0] | Should Be 'C:\VS\Enterprise\MSBuild\15.0\Bin\amd64\MSBuild.exe'
77+
$doc.files.file[1] | Should Be 'C:\VS\Enterprise\MSBuild\15.0\Bin\MSBuild.exe'
78+
$doc.files.file[2] | Should Be 'C:\VS\Community\MSBuild\15.0\Bin\amd64\MSBuild.exe'
79+
$doc.files.file[3] | Should Be 'C:\VS\Community\MSBuild\15.0\Bin\MSBuild.exe'
80+
}
81+
}
82+
83+
Context 'msbuild\**\msbuild.* -latest' {
84+
It 'returns 4 matches' {
85+
$files = C:\bin\vswhere.exe -sort -find 'msbuild\**\msbuild.*' -latest
86+
87+
$files.Count | Should Be 4
88+
$files[0] | Should Be 'C:\VS\Enterprise\MSBuild\15.0\Bin\amd64\MSBuild.exe'
89+
$files[1] | Should Be 'C:\VS\Enterprise\MSBuild\15.0\Bin\amd64\MSBuild.exe.config'
90+
$files[2] | Should Be 'C:\VS\Enterprise\MSBuild\15.0\Bin\MSBuild.exe'
91+
$files[3] | Should Be 'C:\VS\Enterprise\MSBuild\15.0\Bin\MSBuild.exe.config'
92+
}
93+
94+
It '-format json returns 4 matches' {
95+
$files = C:\bin\vswhere.exe -sort -find 'msbuild\**\msbuild.*' -latest -format json | ConvertFrom-Json
96+
97+
$files.Count | Should Be 4
98+
$files[0] | Should Be 'C:\VS\Enterprise\MSBuild\15.0\Bin\amd64\MSBuild.exe'
99+
$files[1] | Should Be 'C:\VS\Enterprise\MSBuild\15.0\Bin\amd64\MSBuild.exe.config'
100+
$files[2] | Should Be 'C:\VS\Enterprise\MSBuild\15.0\Bin\MSBuild.exe'
101+
$files[3] | Should Be 'C:\VS\Enterprise\MSBuild\15.0\Bin\MSBuild.exe.config'
102+
}
103+
104+
It '-format xml returns 4 matches' {
105+
$doc = [xml](C:\bin\vswhere.exe -sort -find 'msbuild\**\msbuild.*' -latest -format xml)
106+
107+
$doc.files.file.Count | Should Be 4
108+
$doc.files.file[0] | Should Be 'C:\VS\Enterprise\MSBuild\15.0\Bin\amd64\MSBuild.exe'
109+
$doc.files.file[1] | Should Be 'C:\VS\Enterprise\MSBuild\15.0\Bin\amd64\MSBuild.exe.config'
110+
$doc.files.file[2] | Should Be 'C:\VS\Enterprise\MSBuild\15.0\Bin\MSBuild.exe'
111+
$doc.files.file[3] | Should Be 'C:\VS\Enterprise\MSBuild\15.0\Bin\MSBuild.exe.config'
112+
}
113+
}
114+
}

docker/Tests/vswhere.tests.ps1

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -304,8 +304,7 @@ Describe 'vswhere' {
304304

305305
It 'returns 0 instances using "json"' {
306306
$instances = C:\bin\vswhere.exe -property invalid -format json | ConvertFrom-Json
307-
$instances.Count | Should Be 2
308-
$instances | ForEach-Object { $_.instanceId | Should BeNullOrEmpty }
307+
$instances.Count | Should Be 0
309308
}
310309

311310
It 'returns 0 instances using "value"' {
@@ -315,8 +314,7 @@ Describe 'vswhere' {
315314

316315
It 'returns 0 instances using "xml"' {
317316
$instances = [xml](C:\bin\vswhere.exe -property invalid -format xml)
318-
@($instances.instances.instance).Count | Should Be 2
319-
@($instances.instances.instance) | ForEach-Object { $_.instanceId | Should BeNullOrEmpty }
317+
$instances.instances.instance.Count | Should Be 0
320318
}
321319
}
322320

src/vswhere.lib/CommandArgs.cpp

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,8 +110,24 @@ void CommandArgs::Parse(_In_ vector<CommandParser::Token> args)
110110
}
111111
else if (ArgumentEquals(arg.Value, L"property"))
112112
{
113+
if (m_find.length())
114+
{
115+
auto message = ResourceManager::FormatString(IDS_E_ARGINCOMPAT, L"property", L"find");
116+
throw win32_error(ERROR_INVALID_PARAMETER, message);
117+
}
118+
113119
m_property = ParseArgument(it, args.end(), arg);
114120
}
121+
else if (ArgumentEquals(arg.Value, L"find"))
122+
{
123+
if (m_property.length())
124+
{
125+
auto message = ResourceManager::FormatString(IDS_E_ARGINCOMPAT, L"find", L"property");
126+
throw win32_error(ERROR_INVALID_PARAMETER, message);
127+
}
128+
129+
m_find = ParseArgument(it, args.end(), arg);
130+
}
115131
else if (ArgumentEquals(arg.Value, L"nologo"))
116132
{
117133
m_nologo = true;
@@ -150,7 +166,7 @@ void CommandArgs::Parse(_In_ vector<CommandParser::Token> args)
150166
}
151167
}
152168

153-
if (!m_property.empty() && m_format.empty())
169+
if ((m_property.length() || m_find.length()) && m_format.empty())
154170
{
155171
m_format = L"value";
156172
}

src/vswhere.lib/CommandArgs.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ class CommandArgs
3535
m_prerelease(obj.m_prerelease),
3636
m_format(obj.m_format),
3737
m_property(obj.m_property),
38+
m_find(obj.m_find),
3839
m_nologo(obj.m_nologo),
3940
m_utf8(obj.m_utf8),
4041
m_help(obj.m_help)
@@ -111,6 +112,11 @@ class CommandArgs
111112
return m_property;
112113
}
113114

115+
const std::wstring& get_Find() const noexcept
116+
{
117+
return m_find;
118+
}
119+
114120
const bool get_Logo() const noexcept
115121
{
116122
return !m_nologo;
@@ -150,6 +156,7 @@ class CommandArgs
150156
bool m_prerelease;
151157
std::wstring m_format;
152158
std::wstring m_property;
159+
std::wstring m_find;
153160
bool m_nologo;
154161
bool m_utf8;
155162
bool m_help;

src/vswhere.lib/Formatter.cpp

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ Formatter::FormatterMap Formatter::Formatters =
4242
const wstring Formatter::s_delims(L"./_");
4343
ci_equal Formatter::s_comparer;
4444

45-
std::unique_ptr<Formatter> Formatter::Create(const std::wstring& type)
45+
std::unique_ptr<Formatter> Formatter::Create(const wstring& type)
4646
{
4747
auto it = Formatters.find(type);
4848
if (it != Formatters.end())
@@ -56,6 +56,20 @@ std::unique_ptr<Formatter> Formatter::Create(const std::wstring& type)
5656
throw win32_error(ERROR_NOT_SUPPORTED);
5757
}
5858

59+
void Formatter::Write(_In_ Console& console, _In_ const std::wstring& root, _In_ const std::wstring& name, _In_ const std::vector<std::wstring> values)
60+
{
61+
StartDocument(console);
62+
StartArray(console, root);
63+
64+
for (const auto& value : values)
65+
{
66+
WriteProperty(console, name, value);
67+
}
68+
69+
EndArray(console);
70+
EndDocument(console);
71+
}
72+
5973
void Formatter::Write(_In_ const CommandArgs& args, _In_ Console& console, _In_ ISetupInstance* pInstance)
6074
{
6175
StartDocument(console);
@@ -67,7 +81,7 @@ void Formatter::Write(_In_ const CommandArgs& args, _In_ Console& console, _In_
6781
EndDocument(console);
6882
}
6983

70-
void Formatter::Write(_In_ const CommandArgs& args, _In_ Console& console, _In_ std::vector<ISetupInstancePtr> instances)
84+
void Formatter::Write(_In_ const CommandArgs& args, _In_ Console& console, _In_ vector<ISetupInstancePtr> instances)
7185
{
7286
StartDocument(console);
7387
StartArray(console);
@@ -81,6 +95,31 @@ void Formatter::Write(_In_ const CommandArgs& args, _In_ Console& console, _In_
8195
EndDocument(console);
8296
}
8397

98+
void Formatter::WriteFiles(_In_ const CommandArgs& args, _In_ Console& console, vector<ISetupInstancePtr> instances)
99+
{
100+
StartDocument(console);
101+
StartArray(console, L"files");
102+
103+
bstr_t bstrInstallationPath;
104+
for (const auto& instance : instances)
105+
{
106+
auto hr = instance->GetInstallationPath(bstrInstallationPath.GetAddress());
107+
if (SUCCEEDED(hr))
108+
{
109+
Glob glob(static_cast<LPCWSTR>(bstrInstallationPath), args.get_Find());
110+
111+
auto entries = glob.Entries(args.get_Sort());
112+
for (const auto& entry : entries)
113+
{
114+
WriteProperty(console, L"file", entry);
115+
}
116+
}
117+
}
118+
119+
EndArray(console);
120+
EndDocument(console);
121+
}
122+
84123
wstring Formatter::FormatDateISO8601(_In_ const FILETIME& value)
85124
{
86125
SYSTEMTIME st = {};
@@ -156,7 +195,7 @@ void Formatter::WriteInternal(_In_ const CommandArgs& args, _In_ Console& consol
156195
variant_t vtValue;
157196
bool found = false;
158197

159-
for (const auto property : m_properties)
198+
for (const auto& property : m_properties)
160199
{
161200
if (specified.empty() || PropertyEqual(specified, property))
162201
{

src/vswhere.lib/Formatter.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,10 @@ class Formatter
1919
{
2020
}
2121

22+
void Write(_In_ Console& console, _In_ const std::wstring& root, _In_ const std::wstring& name, _In_ const std::vector<std::wstring> values);
2223
void Write(_In_ const CommandArgs& args, _In_ Console& console, _In_ ISetupInstance* pInstance);
2324
void Write(_In_ const CommandArgs& args, _In_ Console& console, _In_ std::vector<ISetupInstancePtr> instances);
25+
void WriteFiles(_In_ const CommandArgs& args, _In_ Console& console, std::vector<ISetupInstancePtr> instances);
2426

2527
virtual bool ShowLogo() const
2628
{
@@ -38,7 +40,7 @@ class Formatter
3840
static std::wstring FormatDateISO8601(_In_ const FILETIME& value);
3941

4042
virtual void StartDocument(_In_ Console& console) {}
41-
virtual void StartArray(_In_ Console& console) {}
43+
virtual void StartArray(_In_ Console& console, _In_opt_ const std::wstring& name = empty_wstring) {}
4244
virtual void StartObject(_In_ Console& console, _In_opt_ const std::wstring& name = empty_wstring) {}
4345
virtual void WriteProperty(_In_ Console& console, _In_ const std::wstring& name, _In_ const std::wstring& value) {}
4446
virtual void EndObject(_In_ Console& console) {}

0 commit comments

Comments
 (0)