@@ -19,6 +19,7 @@ import (
19
19
"strconv"
20
20
"strings"
21
21
22
+ "github.com/google/shlex"
22
23
"golang.org/x/tools/go/ast/astutil"
23
24
)
24
25
@@ -235,6 +236,7 @@ func Process(files []*ast.File, dir string, fset *token.FileSet, cflags []string
235
236
}
236
237
237
238
// Find `import "C"` statements in the file.
239
+ var statements []* ast.GenDecl
238
240
for _ , f := range files {
239
241
for i := 0 ; i < len (f .Decls ); i ++ {
240
242
decl := f .Decls [i ]
@@ -258,14 +260,9 @@ func Process(files []*ast.File, dir string, fset *token.FileSet, cflags []string
258
260
if path != "C" {
259
261
continue
260
262
}
261
- cgoComment := genDecl .Doc .Text ()
262
263
263
- pos := genDecl .Pos ()
264
- if genDecl .Doc != nil {
265
- pos = genDecl .Doc .Pos ()
266
- }
267
- position := fset .PositionFor (pos , true )
268
- p .parseFragment (cgoComment + cgoTypes , cflags , position .Filename , position .Line )
264
+ // Found a CGo statement.
265
+ statements = append (statements , genDecl )
269
266
270
267
// Remove this import declaration.
271
268
f .Decls = append (f .Decls [:i ], f .Decls [i + 1 :]... )
@@ -276,6 +273,93 @@ func Process(files []*ast.File, dir string, fset *token.FileSet, cflags []string
276
273
//ast.Print(fset, f)
277
274
}
278
275
276
+ // Find all #cgo lines.
277
+ for _ , genDecl := range statements {
278
+ if genDecl .Doc == nil {
279
+ continue
280
+ }
281
+ for _ , comment := range genDecl .Doc .List {
282
+ for {
283
+ // Extract the #cgo line, and replace it with spaces.
284
+ // Replacing with spaces makes sure that error locations are
285
+ // still correct, while not interfering with parsing in any way.
286
+ lineStart := strings .Index (comment .Text , "#cgo " )
287
+ if lineStart < 0 {
288
+ break
289
+ }
290
+ lineLen := strings .IndexByte (comment .Text [lineStart :], '\n' )
291
+ if lineLen < 0 {
292
+ lineLen = len (comment .Text ) - lineStart
293
+ }
294
+ lineEnd := lineStart + lineLen
295
+ line := comment .Text [lineStart :lineEnd ]
296
+ spaces := make ([]byte , len (line ))
297
+ for i := range spaces {
298
+ spaces [i ] = ' '
299
+ }
300
+ lenBefore := len (comment .Text )
301
+ comment .Text = comment .Text [:lineStart ] + string (spaces ) + comment .Text [lineEnd :]
302
+ if len (comment .Text ) != lenBefore {
303
+ println (lenBefore , len (comment .Text ))
304
+ panic ("length of preamble changed!" )
305
+ }
306
+
307
+ // Get the text before the colon in the #cgo directive.
308
+ colon := strings .IndexByte (line , ':' )
309
+ if colon < 0 {
310
+ p .addErrorAfter (comment .Slash , comment .Text [:lineStart ], "missing colon in #cgo line" )
311
+ continue
312
+ }
313
+
314
+ // Extract the fields before the colon. These fields are a list
315
+ // of build tags and the C environment variable.
316
+ fields := strings .Fields (line [4 :colon ])
317
+ if len (fields ) == 0 {
318
+ p .addErrorAfter (comment .Slash , comment .Text [:lineStart + colon - 1 ], "invalid #cgo line" )
319
+ continue
320
+ }
321
+
322
+ if len (fields ) > 1 {
323
+ p .addErrorAfter (comment .Slash , comment .Text [:lineStart + 5 ], "not implemented: build constraints in #cgo line" )
324
+ continue
325
+ }
326
+
327
+ name := fields [len (fields )- 1 ]
328
+ value := line [colon + 1 :]
329
+ switch name {
330
+ case "CFLAGS" :
331
+ flags , err := shlex .Split (value )
332
+ if err != nil {
333
+ // TODO: find the exact location where the error happened.
334
+ p .addErrorAfter (comment .Slash , comment .Text [:lineStart + colon + 1 ], "failed to parse flags in #cgo line: " + err .Error ())
335
+ continue
336
+ }
337
+ if err := checkCompilerFlags (name , flags ); err != nil {
338
+ p .addErrorAfter (comment .Slash , comment .Text [:lineStart + colon + 1 ], err .Error ())
339
+ continue
340
+ }
341
+ cflags = append (cflags , flags ... )
342
+ default :
343
+ startPos := strings .LastIndex (line [4 :colon ], name ) + 4
344
+ p .addErrorAfter (comment .Slash , comment .Text [:lineStart + startPos ], "invalid #cgo line: " + name )
345
+ continue
346
+ }
347
+ }
348
+ }
349
+ }
350
+
351
+ // Process all CGo imports.
352
+ for _ , genDecl := range statements {
353
+ cgoComment := genDecl .Doc .Text ()
354
+
355
+ pos := genDecl .Pos ()
356
+ if genDecl .Doc != nil {
357
+ pos = genDecl .Doc .Pos ()
358
+ }
359
+ position := fset .PositionFor (pos , true )
360
+ p .parseFragment (cgoComment + cgoTypes , cflags , position .Filename , position .Line )
361
+ }
362
+
279
363
// Declare functions found by libclang.
280
364
p .addFuncDecls ()
281
365
0 commit comments