@@ -158,7 +158,7 @@ pub fn next(self: *Tokenizer) ?Token {
158
158
'"' = > {
159
159
self .index += 1 ;
160
160
self .state = .rhs ;
161
- return Token { . prereq = self .bytes [start .. self .index - 1 ] } ;
161
+ return finishPrereq ( must_resolve , self .bytes [start .. self .index - 1 ]) ;
162
162
},
163
163
else = > {
164
164
self .index += 1 ;
@@ -167,11 +167,11 @@ pub fn next(self: *Tokenizer) ?Token {
167
167
.prereq = > switch (char ) {
168
168
'\t ' , ' ' = > {
169
169
self .state = .rhs ;
170
- return Token { . prereq = self .bytes [start .. self .index ] } ;
170
+ return finishPrereq ( must_resolve , self .bytes [start .. self .index ]) ;
171
171
},
172
172
'\n ' , '\r ' = > {
173
173
self .state = .lhs ;
174
- return Token { . prereq = self .bytes [start .. self .index ] } ;
174
+ return finishPrereq ( must_resolve , self .bytes [start .. self .index ]) ;
175
175
},
176
176
'\\ ' = > {
177
177
self .state = .prereq_continuation ;
@@ -185,12 +185,22 @@ pub fn next(self: *Tokenizer) ?Token {
185
185
'\n ' = > {
186
186
self .index += 1 ;
187
187
self .state = .rhs ;
188
- return Token { . prereq = self .bytes [start .. self .index - 2 ] } ;
188
+ return finishPrereq ( must_resolve , self .bytes [start .. self .index - 2 ]) ;
189
189
},
190
190
'\r ' = > {
191
191
self .state = .prereq_continuation_linefeed ;
192
192
self .index += 1 ;
193
193
},
194
+ '\\ ' = > {
195
+ // The previous \ wasn't a continuation, but this one might be.
196
+ self .index += 1 ;
197
+ },
198
+ ' ' = > {
199
+ // not continuation, but escaped space must be resolved
200
+ must_resolve = true ;
201
+ self .state = .prereq ;
202
+ self .index += 1 ;
203
+ },
194
204
else = > {
195
205
// not continuation
196
206
self .state = .prereq ;
@@ -201,7 +211,7 @@ pub fn next(self: *Tokenizer) ?Token {
201
211
'\n ' = > {
202
212
self .index += 1 ;
203
213
self .state = .rhs ;
204
- return Token { . prereq = self .bytes [start .. self .index - 1 ] } ;
214
+ return finishPrereq ( must_resolve , self .bytes [start .. self .index - 3 ]) ;
205
215
},
206
216
else = > {
207
217
return errorIllegalChar (.continuation_eol , self .index , char );
@@ -251,15 +261,15 @@ pub fn next(self: *Tokenizer) ?Token {
251
261
},
252
262
.prereq = > {
253
263
self .state = .lhs ;
254
- return Token { . prereq = self .bytes [start .. ] } ;
264
+ return finishPrereq ( must_resolve , self .bytes [start .. ]) ;
255
265
},
256
266
.prereq_continuation = > {
257
267
self .state = .lhs ;
258
- return Token { . prereq = self .bytes [start .. self .index - 1 ] } ;
268
+ return finishPrereq ( must_resolve , self .bytes [start .. self .index - 1 ]) ;
259
269
},
260
270
.prereq_continuation_linefeed = > {
261
271
self .state = .lhs ;
262
- return Token { . prereq = self .bytes [start .. self .index - 2 ] } ;
272
+ return finishPrereq ( must_resolve , self .bytes [start .. self .index - 2 ]) ;
263
273
},
264
274
}
265
275
}
@@ -278,6 +288,10 @@ fn finishTarget(must_resolve: bool, bytes: []const u8) Token {
278
288
return if (must_resolve ) .{ .target_must_resolve = bytes } else .{ .target = bytes };
279
289
}
280
290
291
+ fn finishPrereq (must_resolve : bool , bytes : []const u8 ) Token {
292
+ return if (must_resolve ) .{ .prereq_must_resolve = bytes } else .{ .prereq = bytes };
293
+ }
294
+
281
295
const State = enum {
282
296
lhs ,
283
297
target ,
@@ -298,6 +312,7 @@ pub const Token = union(enum) {
298
312
target : []const u8 ,
299
313
target_must_resolve : []const u8 ,
300
314
prereq : []const u8 ,
315
+ prereq_must_resolve : []const u8 ,
301
316
302
317
incomplete_quoted_prerequisite : IndexAndBytes ,
303
318
incomplete_target : IndexAndBytes ,
@@ -318,48 +333,76 @@ pub const Token = union(enum) {
318
333
bytes : []const u8 ,
319
334
};
320
335
321
- /// Resolve escapes in target. Only valid with .target_must_resolve.
336
+ /// Resolve escapes in target or prereq . Only valid with .target_must_resolve or .prereq_must_resolve .
322
337
pub fn resolve (self : Token , writer : anytype ) @TypeOf (writer ).Error ! void {
323
- const bytes = self .target_must_resolve ; // resolve called on incorrect token
324
-
325
- var state : enum { start , escape , dollar } = .start ;
326
- for (bytes ) | c | {
327
- switch (state ) {
328
- .start = > {
329
- switch (c ) {
330
- '\\ ' = > state = .escape ,
331
- '$' = > state = .dollar ,
332
- else = > try writer .writeByte (c ),
333
- }
334
- },
335
- .escape = > {
336
- switch (c ) {
337
- ' ' , '#' , '\\ ' = > {},
338
- '$' = > {
339
- try writer .writeByte ('\\ ' );
340
- state = .dollar ;
341
- continue ;
338
+ switch (self ) {
339
+ .target_must_resolve = > | bytes | {
340
+ var state : enum { start , escape , dollar } = .start ;
341
+ for (bytes ) | c | {
342
+ switch (state ) {
343
+ .start = > {
344
+ switch (c ) {
345
+ '\\ ' = > state = .escape ,
346
+ '$' = > state = .dollar ,
347
+ else = > try writer .writeByte (c ),
348
+ }
349
+ },
350
+ .escape = > {
351
+ switch (c ) {
352
+ ' ' , '#' , '\\ ' = > {},
353
+ '$' = > {
354
+ try writer .writeByte ('\\ ' );
355
+ state = .dollar ;
356
+ continue ;
357
+ },
358
+ else = > try writer .writeByte ('\\ ' ),
359
+ }
360
+ try writer .writeByte (c );
361
+ state = .start ;
362
+ },
363
+ .dollar = > {
364
+ try writer .writeByte ('$' );
365
+ switch (c ) {
366
+ '$' = > {},
367
+ else = > try writer .writeByte (c ),
368
+ }
369
+ state = .start ;
342
370
},
343
- else = > try writer .writeByte ('\\ ' ),
344
371
}
345
- try writer .writeByte (c );
346
- state = .start ;
347
- },
348
- .dollar = > {
349
- try writer .writeByte ('$' );
350
- switch (c ) {
351
- '$' = > {},
352
- else = > try writer .writeByte (c ),
372
+ }
373
+ },
374
+ .prereq_must_resolve = > | bytes | {
375
+ var state : enum { start , escape } = .start ;
376
+ for (bytes ) | c | {
377
+ switch (state ) {
378
+ .start = > {
379
+ switch (c ) {
380
+ '\\ ' = > state = .escape ,
381
+ else = > try writer .writeByte (c ),
382
+ }
383
+ },
384
+ .escape = > {
385
+ switch (c ) {
386
+ ' ' = > {},
387
+ '\\ ' = > {
388
+ try writer .writeByte (c );
389
+ continue ;
390
+ },
391
+ else = > try writer .writeByte ('\\ ' ),
392
+ }
393
+ try writer .writeByte (c );
394
+ state = .start ;
395
+ },
353
396
}
354
- state = .start ;
355
- },
356
- }
397
+ }
398
+ },
399
+ else = > unreachable ,
357
400
}
358
401
}
359
402
360
403
pub fn printError (self : Token , writer : anytype ) @TypeOf (writer ).Error ! void {
361
404
switch (self ) {
362
- .target , .target_must_resolve , .prereq = > unreachable , // not an error
405
+ .target , .target_must_resolve , .prereq , .prereq_must_resolve = > unreachable , // not an error
363
406
.incomplete_quoted_prerequisite ,
364
407
.incomplete_target ,
365
408
= > | index_and_bytes | {
@@ -387,7 +430,7 @@ pub const Token = union(enum) {
387
430
388
431
fn errStr (self : Token ) []const u8 {
389
432
return switch (self ) {
390
- .target , .target_must_resolve , .prereq = > unreachable , // not an error
433
+ .target , .target_must_resolve , .prereq , .prereq_must_resolve = > unreachable , // not an error
391
434
.incomplete_quoted_prerequisite = > "incomplete quoted prerequisite" ,
392
435
.incomplete_target = > "incomplete target" ,
393
436
.invalid_target = > "invalid target" ,
@@ -538,6 +581,15 @@ test "prereq continuation" {
538
581
, expect );
539
582
}
540
583
584
+ test "prereq continuation (CRLF)" {
585
+ const expect =
586
+ \\target = {foo.o}
587
+ \\prereq = {foo.h}
588
+ \\prereq = {bar.h}
589
+ ;
590
+ try depTokenizer ("foo.o: foo.h\\ \r \n bar.h" , expect );
591
+ }
592
+
541
593
test "multiple prereqs" {
542
594
const expect =
543
595
\\target = {foo.o}
@@ -728,6 +780,32 @@ test "windows funky targets" {
728
780
);
729
781
}
730
782
783
+ test "windows funky prereqs" {
784
+ // Note we don't support unquoted escaped spaces at the very beginning of a relative path
785
+ // e.g. `\ SpaceAtTheBeginning.c`
786
+ // This typically wouldn't be seen in the wild, since depfiles usually use absolute paths
787
+ // and supporting it would degrade error messages for cases where it was meant to be a
788
+ // continuation, but the line ending is missing.
789
+ try depTokenizer (
790
+ \\cimport.o: \
791
+ \\ trailingbackslash\\
792
+ \\ C:\Users\John\ Smith\AppData\Local\zig\p\1220d14057af1a9d6dde4643293527bd5ee5099517d655251a066666a4320737ea7c\cimport.c \
793
+ \\ somedir\\ a.c\
794
+ \\ somedir/\ a.c\
795
+ \\ somedir\\ \ \ b.c\
796
+ \\ somedir\\ \\ \c.c\
797
+ \\
798
+ ,
799
+ \\target = {cimport.o}
800
+ \\prereq = {trailingbackslash\}
801
+ \\prereq = {C:\Users\John Smith\AppData\Local\zig\p\1220d14057af1a9d6dde4643293527bd5ee5099517d655251a066666a4320737ea7c\cimport.c}
802
+ \\prereq = {somedir\ a.c}
803
+ \\prereq = {somedir/ a.c}
804
+ \\prereq = {somedir\ b.c}
805
+ \\prereq = {somedir\ \ \c.c}
806
+ );
807
+ }
808
+
731
809
test "windows drive and forward slashes" {
732
810
try depTokenizer (
733
811
\\C:/msys64/what/zig-cache\tmp\48ac4d78dd531abd-cxa_thread_atexit.obj: \
@@ -915,6 +993,15 @@ fn depTokenizer(input: []const u8, expect: []const u8) !void {
915
993
resolve_buf .items .len = 0 ;
916
994
try buffer .appendSlice ("}" );
917
995
},
996
+ .prereq_must_resolve = > {
997
+ try buffer .appendSlice ("prereq = {" );
998
+ try token .resolve (resolve_buf .writer ());
999
+ for (resolve_buf .items ) | b | {
1000
+ try buffer .append (printable_char_tab [b ]);
1001
+ }
1002
+ resolve_buf .items .len = 0 ;
1003
+ try buffer .appendSlice ("}" );
1004
+ },
918
1005
else = > {
919
1006
try buffer .appendSlice ("ERROR: " );
920
1007
try token .printError (buffer .writer ());
0 commit comments