3
3
using System . Diagnostics . CodeAnalysis ;
4
4
using System . IO ;
5
5
using System . Linq ;
6
+ using System . Text ;
6
7
using BenchmarkDotNet . Columns ;
7
8
using BenchmarkDotNet . Configs ;
8
9
using BenchmarkDotNet . Diagnosers ;
@@ -75,6 +76,13 @@ public static (bool isSuccess, IConfig config, CommandLineOptions options) Parse
75
76
{
76
77
( bool isSuccess , IConfig config , CommandLineOptions options ) result = default ;
77
78
79
+ var ( expandSuccess , expandedArgs ) = ExpandResponseFile ( args , logger ) ;
80
+ if ( ! expandSuccess )
81
+ {
82
+ return ( false , default , default ) ;
83
+ }
84
+
85
+ args = expandedArgs ;
78
86
using ( var parser = CreateParser ( logger ) )
79
87
{
80
88
parser
@@ -86,6 +94,115 @@ public static (bool isSuccess, IConfig config, CommandLineOptions options) Parse
86
94
return result ;
87
95
}
88
96
97
+ private static ( bool Success , string [ ] ExpandedTokens ) ExpandResponseFile ( string [ ] args , ILogger logger )
98
+ {
99
+ List < string > result = new ( ) ;
100
+ foreach ( var arg in args )
101
+ {
102
+ if ( arg . StartsWith ( "@" ) )
103
+ {
104
+ var fileName = arg . Substring ( 1 ) ;
105
+ try
106
+ {
107
+ if ( File . Exists ( fileName ) )
108
+ {
109
+ var lines = File . ReadAllLines ( fileName ) ;
110
+ foreach ( var line in lines )
111
+ {
112
+ result . AddRange ( ConsumeTokens ( line ) ) ;
113
+ }
114
+ }
115
+ else
116
+ {
117
+ logger . WriteLineError ( $ "Response file { fileName } does not exists.") ;
118
+ return ( false , Array . Empty < string > ( ) ) ;
119
+ }
120
+ }
121
+ catch ( Exception ex )
122
+ {
123
+ logger . WriteLineError ( $ "Failed to parse RSP file: { fileName } , { ex . Message } ") ;
124
+ return ( false , Array . Empty < string > ( ) ) ;
125
+ }
126
+ }
127
+ else
128
+ {
129
+ if ( arg . Contains ( ' ' ) )
130
+ {
131
+ // Workaround for CommandLine library issue with parsing these kind of args.
132
+ result . Add ( " " + arg ) ;
133
+ }
134
+ else
135
+ {
136
+ result . Add ( arg ) ;
137
+ }
138
+ }
139
+ }
140
+
141
+ return ( true , result . ToArray ( ) ) ;
142
+ }
143
+
144
+ private static IEnumerable < string > ConsumeTokens ( string line )
145
+ {
146
+ bool insideQuotes = false ;
147
+ var token = new StringBuilder ( ) ;
148
+ for ( int i = 0 ; i < line . Length ; i ++ )
149
+ {
150
+ char currentChar = line [ i ] ;
151
+ if ( currentChar == ' ' && ! insideQuotes )
152
+ {
153
+ if ( token . Length > 0 )
154
+ {
155
+ yield return GetToken ( ) ;
156
+ token = new StringBuilder ( ) ;
157
+ }
158
+
159
+ continue ;
160
+ }
161
+
162
+ if ( currentChar == '"' )
163
+ {
164
+ insideQuotes = ! insideQuotes ;
165
+ continue ;
166
+ }
167
+
168
+ if ( currentChar == '\\ ' && insideQuotes )
169
+ {
170
+ if ( line [ i + 1 ] == '"' )
171
+ {
172
+ insideQuotes = false ;
173
+ i ++ ;
174
+ continue ;
175
+ }
176
+
177
+ if ( line [ i + 1 ] == '\\ ' )
178
+ {
179
+ token . Append ( '\\ ' ) ;
180
+ i ++ ;
181
+ continue ;
182
+ }
183
+ }
184
+
185
+ token . Append ( currentChar ) ;
186
+ }
187
+
188
+ if ( token . Length > 0 )
189
+ {
190
+ yield return GetToken ( ) ;
191
+ }
192
+
193
+ string GetToken ( )
194
+ {
195
+ var result = token . ToString ( ) ;
196
+ if ( result . Contains ( ' ' ) )
197
+ {
198
+ // Workaround for CommandLine library issue with parsing these kind of args.
199
+ return " " + result ;
200
+ }
201
+
202
+ return result ;
203
+ }
204
+ }
205
+
89
206
internal static bool TryUpdateArgs ( string [ ] args , out string [ ] ? updatedArgs , Action < CommandLineOptions > updater )
90
207
{
91
208
( bool isSuccess , CommandLineOptions options ) result = default ;
0 commit comments