• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

dart-lang / ffigen / 4249261578

pending completion
4249261578

Pull #521

github

GitHub
Merge 1b39e5484 into 4d86a326b
Pull Request #521: Read LLVM path from environment variable.

5 of 5 new or added lines in 1 file covered. (100.0%)

3165 of 3447 relevant lines covered (91.82%)

24.71 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

74.54
/lib/src/config_provider/spec_utils.dart
1
// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
2
// for details. All rights reserved. Use of this source code is governed by a
3
// BSD-style license that can be found in the LICENSE file.
4

5
import 'dart:io';
6

7
import 'package:ffigen/src/code_generator.dart';
8
import 'package:ffigen/src/code_generator/utils.dart';
9
import 'package:file/local.dart';
10
import 'package:glob/glob.dart';
11
import 'package:logging/logging.dart';
12
import 'package:package_config/package_config.dart';
13
import 'package:path/path.dart' as p;
14
import 'package:quiver/pattern.dart' as quiver;
15
import 'package:yaml/yaml.dart';
16

17
import '../strings.dart' as strings;
18
import 'config_types.dart';
19

20
final _logger = Logger('ffigen.config_provider.spec_utils');
144✔
21

22
/// Replaces the path separators according to current platform.
23
String _replaceSeparators(String path) {
48✔
24
  if (Platform.isWindows) {
48✔
25
    return path.replaceAll(p.posix.separator, p.windows.separator);
×
26
  } else {
27
    return path.replaceAll(p.windows.separator, p.posix.separator);
240✔
28
  }
29
}
30

31
/// Replaces the path separators according to current platform. If a relative
32
/// path is passed in, it is resolved relative to the config path, and the
33
/// absolute path is returned.
34
String _normalizePath(String path, String? configFilename) {
48✔
35
  final skipNormalization =
36
      (configFilename == null) || p.isAbsolute(path) || path.startsWith("**");
2✔
37
  return _replaceSeparators(
48✔
38
      skipNormalization ? path : p.join(p.dirname(configFilename), path));
2✔
39
}
40

41
/// Checks if type of value is [T], logs an error if it's not.
42
///
43
/// [key] is printed as `'item1 -> item2 => item3'` in log message.
44
bool checkType<T>(List<String> keys, dynamic value) {
48✔
45
  if (value is! T) {
48✔
46
    _logger.severe(
×
47
        "Expected value of key '${keys.join(' -> ')}' to be of type '$T'.");
×
48
    return false;
49
  }
50
  return true;
51
}
52

53
/// Checks if there are nested [key] in [map].
54
bool checkKeyInYaml(List<String> key, YamlMap map) {
48✔
55
  dynamic last = map;
56
  for (final k in key) {
96✔
57
    if (last is YamlMap) {
48✔
58
      if (!last.containsKey(k)) return false;
48✔
59
      last = last[k];
48✔
60
    } else {
61
      return false;
62
    }
63
  }
64
  // The entry for the last key may be null.
65
  return true;
66
}
67

68
/// Extracts value of nested [key] from [map].
69
dynamic getKeyValueFromYaml(List<String> key, YamlMap map) {
48✔
70
  if (checkKeyInYaml(key, map)) {
48✔
71
    dynamic last = map;
72
    for (final k in key) {
96✔
73
      last = last[k];
48✔
74
    }
75
    return last;
76
  }
77

78
  return null;
79
}
80

81
/// Recursively checks the keys in [configKeyMap] from [allowedKeyList].
82
void warnUnknownKeys(List<List<String>> allowedKeyList, YamlMap configKeyMap) {
48✔
83
  final allowedKeyMap = <String, dynamic>{};
48✔
84
  for (final specKeys in allowedKeyList) {
96✔
85
    var _item = allowedKeyMap;
86
    for (final specSubKey in specKeys) {
96✔
87
      _item.putIfAbsent(specSubKey, () => <String, dynamic>{});
144✔
88
      _item = _item[specSubKey] as Map<String, dynamic>;
48✔
89
    }
90
    // Add empty key to mark that any sub-keys of this key are allowed.
91
    _item[''] = <String, dynamic>{};
96✔
92
  }
93
  _warnUnknownKeysInMap(allowedKeyMap, configKeyMap, <dynamic>[]);
96✔
94
}
95

96
/// Recursive function to check a key set in a configKeyMap.
97
void _warnUnknownKeysInMap(Map<String, dynamic> allowedKeyMap,
48✔
98
    dynamic configKeyMap, List<dynamic> prev) {
99
  if (allowedKeyMap.containsKey('') || configKeyMap is! YamlMap) {
96✔
100
    return;
101
  }
102
  for (final key in configKeyMap.keys) {
96✔
103
    if (allowedKeyMap.containsKey(key)) {
48✔
104
      prev.add(key);
48✔
105
      _warnUnknownKeysInMap(
48✔
106
          allowedKeyMap[key] as Map<String, dynamic>, configKeyMap[key], prev);
96✔
107
      prev.removeLast();
48✔
108
    } else {
109
      prev.add(key);
1✔
110
      _logger.warning('Unknown key - ${prev.join(' -> ')}.');
4✔
111
      prev.removeLast();
1✔
112
    }
113
  }
114
}
115

116
bool booleanExtractor(dynamic value) => value as bool;
18✔
117

118
bool booleanValidator(List<String> name, dynamic value) =>
18✔
119
    checkType<bool>(name, value);
18✔
120

121
Map<String, LibraryImport> libraryImportsExtractor(dynamic yamlConfig) {
1✔
122
  final resultMap = <String, LibraryImport>{};
1✔
123
  final typeMap = yamlConfig as YamlMap?;
124
  if (typeMap != null) {
125
    for (final typeName in typeMap.keys) {
2✔
126
      resultMap[typeName as String] =
1✔
127
          LibraryImport(typeName, typeMap[typeName] as String);
2✔
128
    }
129
  }
130
  return resultMap;
131
}
132

133
bool libraryImportsValidator(List<String> name, dynamic yamlConfig) {
1✔
134
  if (!checkType<YamlMap>(name, yamlConfig)) {
1✔
135
    return false;
136
  }
137
  for (final key in (yamlConfig as YamlMap).keys) {
2✔
138
    if (!checkType<String>([...name, key as String], yamlConfig[key])) {
4✔
139
      return false;
140
    }
141
    if (strings.predefinedLibraryImports.containsKey(key)) {
2✔
142
      _logger.severe(
×
143
          'library-import -> $key should not collide with any predefined imports - ${strings.predefinedLibraryImports.keys}.');
×
144
      return false;
145
    }
146
  }
147
  return true;
148
}
149

150
void loadImportedTypes(YamlMap fileConfig,
1✔
151
    Map<String, ImportedType> usrTypeMappings, LibraryImport libraryImport) {
152
  final symbols = fileConfig['symbols'] as YamlMap;
1✔
153
  for (final key in symbols.keys) {
2✔
154
    final usr = key as String;
155
    final value = symbols[usr]! as YamlMap;
1✔
156
    usrTypeMappings[usr] = ImportedType(
2✔
157
        libraryImport, value['name'] as String, value['name'] as String);
2✔
158
  }
159
}
160

161
YamlMap loadSymbolFile(String symbolFilePath, String? configFileName,
1✔
162
    PackageConfig? packageConfig) {
163
  final path = symbolFilePath.startsWith('package:')
1✔
164
      ? packageConfig!.resolve(Uri.parse(symbolFilePath))!.toFilePath()
×
165
      : _normalizePath(symbolFilePath, configFileName);
1✔
166

167
  return loadYaml(File(path).readAsStringSync()) as YamlMap;
3✔
168
}
169

170
Map<String, ImportedType> symbolFileImportExtractor(
1✔
171
    dynamic yamlConfig,
172
    Map<String, LibraryImport> libraryImports,
173
    String? configFileName,
174
    PackageConfig? packageConfig) {
175
  final resultMap = <String, ImportedType>{};
1✔
176
  for (final item in (yamlConfig as YamlList)) {
2✔
177
    String symbolFilePath;
178
    if (item is String) {
1✔
179
      symbolFilePath = item;
180
    } else {
181
      symbolFilePath = item[strings.symbolFile] as String;
×
182
    }
183
    final symbolFile =
184
        loadSymbolFile(symbolFilePath, configFileName, packageConfig);
1✔
185
    final formatVersion = symbolFile[strings.formatVersion] as String;
1✔
186
    if (formatVersion.split('.')[0] !=
3✔
187
        strings.symbolFileFormatVersion.split('.')[0]) {
2✔
188
      _logger.severe(
×
189
          'Incompatible format versions for file $symbolFilePath: ${strings.symbolFileFormatVersion}(ours), $formatVersion(theirs).');
×
190
      exit(1);
×
191
    }
192
    final uniqueNamer = UniqueNamer(libraryImports.keys
2✔
193
        .followedBy([strings.defaultSymbolFileImportPrefix]).toSet());
3✔
194
    for (final file in (symbolFile[strings.files] as YamlMap).keys) {
3✔
195
      final existingImports =
196
          libraryImports.values.where((element) => element.importPath == file);
2✔
197
      if (existingImports.isEmpty) {
1✔
198
        final name =
199
            uniqueNamer.makeUnique(strings.defaultSymbolFileImportPrefix);
1✔
200
        libraryImports[name] = LibraryImport(name, file as String);
2✔
201
      }
202
      final libraryImport = libraryImports.values.firstWhere(
2✔
203
        (element) => element.importPath == file,
3✔
204
      );
205
      loadImportedTypes(
1✔
206
          symbolFile[strings.files][file] as YamlMap, resultMap, libraryImport);
2✔
207
    }
208
  }
209
  return resultMap;
210
}
211

212
bool symbolFileImportValidator(List<String> name, dynamic yamlConfig) {
1✔
213
  if (!checkType<YamlList>(name, yamlConfig)) {
1✔
214
    return false;
215
  }
216
  var result = true;
217
  (yamlConfig as YamlList).asMap().forEach((idx, value) {
3✔
218
    if (value is YamlMap) {
1✔
219
      if (!value.keys.contains(strings.symbolFile)) {
×
220
        result = false;
221
        _logger
×
222
            .severe('Key $name -> $idx -> ${strings.symbolFile} is required.');
×
223
      }
224
      for (final key in value.keys) {
×
225
        if (key == strings.symbolFile) {
×
226
          if (!checkType<String>(
×
227
              [...name, idx.toString(), key as String], value[key])) {
×
228
            result = false;
229
          }
230
        } else {
231
          result = false;
232
          _logger.severe('Unknown key : $name -> $idx -> $key.');
×
233
        }
234
      }
235
    } else if (value is! String) {
1✔
236
      result = false;
237
      _logger.severe('Expected $name -> $idx should be a String or Map.');
×
238
    }
239
  });
240
  return result;
241
}
242

243
Map<String, List<String>> typeMapExtractor(dynamic yamlConfig) {
3✔
244
  // Key - type_name, Value - [lib, cType, dartType].
245
  final resultMap = <String, List<String>>{};
3✔
246
  final typeMap = yamlConfig as YamlMap?;
247
  if (typeMap != null) {
248
    for (final typeName in typeMap.keys) {
6✔
249
      final typeConfigItem = typeMap[typeName] as YamlMap;
3✔
250
      resultMap[typeName as String] = [
6✔
251
        typeConfigItem[strings.lib] as String,
3✔
252
        typeConfigItem[strings.cType] as String,
3✔
253
        typeConfigItem[strings.dartType] as String,
3✔
254
      ];
255
    }
256
  }
257
  return resultMap;
258
}
259

260
bool typeMapValidator(List<String> name, dynamic yamlConfig) {
3✔
261
  if (!checkType<YamlMap>(name, yamlConfig)) {
3✔
262
    return false;
263
  }
264
  var result = true;
265
  for (final key in (yamlConfig as YamlMap).keys) {
6✔
266
    if (!checkType<YamlMap>([...name, key as String], yamlConfig[key])) {
12✔
267
      return false;
268
    }
269
    final lib = (yamlConfig[key] as YamlMap).containsKey(strings.lib);
6✔
270
    if (!lib) {
271
      _logger.severe("Key '${strings.lib}' in $name -> $key is required.");
×
272
      result = false;
273
    }
274
    final cType = (yamlConfig[key] as YamlMap).containsKey(strings.cType);
6✔
275
    if (!cType) {
276
      _logger.severe("Key '${strings.cType}' in $name -> $key is required.");
×
277
      result = false;
278
    }
279
    final dartType = (yamlConfig[key] as YamlMap).containsKey(strings.dartType);
6✔
280
    if (!dartType) {
281
      _logger.severe("Key '${strings.dartType}' in $name -> $key is required.");
×
282
      result = false;
283
    }
284
  }
285
  return result;
286
}
287

288
Map<String, String> stringStringMapExtractor(dynamic yamlConfig) {
2✔
289
  final resultMap = <String, String>{};
2✔
290
  final inputMap = yamlConfig as YamlMap?;
291
  if (inputMap != null) {
292
    for (final key in inputMap.keys) {
4✔
293
      resultMap[key as String] = inputMap[key] as String;
4✔
294
    }
295
  }
296
  return resultMap;
297
}
298

299
bool stringStringMapValidator(List<String> name, dynamic yamlConfig) {
2✔
300
  if (!checkType<YamlMap>(name, yamlConfig)) {
2✔
301
    return false;
302
  }
303
  for (final key in (yamlConfig as YamlMap).keys) {
4✔
304
    if (!checkType<String>([...name, key as String], yamlConfig[key])) {
8✔
305
      return false;
306
    }
307
  }
308
  return true;
309
}
310

311
Map<String, ImportedType> makeImportTypeMapping(
48✔
312
    Map<String, List<String>> rawTypeMappings,
313
    Map<String, LibraryImport> libraryImportsMap) {
314
  final typeMappings = <String, ImportedType>{};
48✔
315
  for (final key in rawTypeMappings.keys) {
51✔
316
    final lib = rawTypeMappings[key]![0];
6✔
317
    final cType = rawTypeMappings[key]![1];
6✔
318
    final dartType = rawTypeMappings[key]![2];
6✔
319
    if (strings.predefinedLibraryImports.containsKey(lib)) {
6✔
320
      typeMappings[key] =
3✔
321
          ImportedType(strings.predefinedLibraryImports[lib]!, cType, dartType);
9✔
322
    } else if (libraryImportsMap.containsKey(lib)) {
1✔
323
      typeMappings[key] =
1✔
324
          ImportedType(libraryImportsMap[lib]!, cType, dartType);
2✔
325
    } else {
326
      throw Exception("Please declare $lib under library-imports.");
×
327
    }
328
  }
329
  return typeMappings;
330
}
331

332
final _quoteMatcher = RegExp(r'''^["'](.*)["']$''', dotAll: true);
18✔
333
final _cmdlineArgMatcher = RegExp(r'''['"](\\"|[^"])*?['"]|[^ ]+''');
18✔
334
List<String> compilerOptsToList(String compilerOpts) {
6✔
335
  final list = <String>[];
6✔
336
  _cmdlineArgMatcher.allMatches(compilerOpts).forEach((element) {
24✔
337
    var match = element.group(0);
6✔
338
    if (match != null) {
339
      if (quiver.matchesFull(_quoteMatcher, match)) {
12✔
340
        match = _quoteMatcher.allMatches(match).first.group(1)!;
4✔
341
      }
342
      list.add(match);
6✔
343
    }
344
  });
345

346
  return list;
347
}
348

349
List<String> compilerOptsExtractor(dynamic value) {
5✔
350
  if (value is String) {
5✔
351
    return compilerOptsToList(value);
5✔
352
  }
353

354
  final list = <String>[];
×
355
  for (final el in (value as YamlList)) {
×
356
    if (el is String) {
×
357
      list.addAll(compilerOptsToList(el));
×
358
    }
359
  }
360
  return list;
361
}
362

363
bool compilerOptsValidator(List<String> name, dynamic value) {
5✔
364
  if (value is String || value is YamlList) {
5✔
365
    return true;
366
  } else {
367
    _logger.severe('Expected $name to be a String or List of String.');
×
368
    return false;
369
  }
370
}
371

372
CompilerOptsAuto compilerOptsAutoExtractor(dynamic value) {
1✔
373
  return CompilerOptsAuto(
1✔
374
    macIncludeStdLib: getKeyValueFromYaml(
1✔
375
      [strings.macos, strings.includeCStdLib],
1✔
376
      value as YamlMap,
377
    ) as bool?,
378
  );
379
}
380

381
bool compilerOptsAutoValidator(List<String> name, dynamic value) {
1✔
382
  var _result = true;
383

384
  if (!checkType<YamlMap>(name, value)) {
1✔
385
    return false;
386
  }
387

388
  for (final oskey in (value as YamlMap).keys) {
2✔
389
    if (oskey == strings.macos) {
1✔
390
      if (!checkType<YamlMap>([...name, oskey as String], value[oskey])) {
4✔
391
        return false;
392
      }
393

394
      for (final inckey in (value[oskey] as YamlMap).keys) {
3✔
395
        if (inckey == strings.includeCStdLib) {
1✔
396
          if (!checkType<bool>(
1✔
397
              [...name, oskey, inckey as String], value[oskey][inckey])) {
5✔
398
            _result = false;
399
          }
400
        } else {
401
          _logger.severe("Unknown key '$inckey' in '$name -> $oskey.");
×
402
          _result = false;
403
        }
404
      }
405
    } else {
406
      _logger.severe("Unknown key '$oskey' in '$name'.");
×
407
      _result = false;
408
    }
409
  }
410
  return _result;
411
}
412

413
Headers headersExtractor(dynamic yamlConfig, String? configFilename) {
48✔
414
  final entryPoints = <String>[];
48✔
415
  final includeGlobs = <quiver.Glob>[];
48✔
416
  for (final key in (yamlConfig as YamlMap).keys) {
96✔
417
    if (key == strings.entryPoints) {
48✔
418
      for (final h in (yamlConfig[key] as YamlList)) {
144✔
419
        final headerGlob = _normalizePath(h as String, configFilename);
48✔
420
        // Add file directly to header if it's not a Glob but a File.
421
        if (File(headerGlob).existsSync()) {
96✔
422
          final osSpecificPath = headerGlob;
423
          entryPoints.add(osSpecificPath);
33✔
424
          _logger.fine('Adding header/file: $headerGlob');
99✔
425
        } else {
426
          final glob = Glob(headerGlob);
15✔
427
          for (final file in glob.listFileSystemSync(const LocalFileSystem(),
15✔
428
              followLinks: true)) {
×
429
            final fixedPath = file.path;
×
430
            entryPoints.add(fixedPath);
×
431
            _logger.fine('Adding header/file: $fixedPath');
×
432
          }
433
        }
434
      }
435
    }
436
    if (key == strings.includeDirectives) {
48✔
437
      for (final h in (yamlConfig[key] as YamlList)) {
30✔
438
        final headerGlob = h as String;
439
        final fixedGlob = _normalizePath(headerGlob, configFilename);
10✔
440
        includeGlobs.add(quiver.Glob(fixedGlob));
20✔
441
      }
442
    }
443
  }
444
  return Headers(
48✔
445
    entryPoints: entryPoints,
446
    includeFilter: GlobHeaderFilter(
48✔
447
      includeGlobs: includeGlobs,
448
    ),
449
  );
450
}
451

452
bool headersValidator(List<String> name, dynamic value) {
48✔
453
  if (!checkType<YamlMap>(name, value)) {
48✔
454
    return false;
455
  }
456
  if (!(value as YamlMap).containsKey(strings.entryPoints)) {
48✔
457
    _logger.severe("Required '$name -> ${strings.entryPoints}'.");
×
458
    return false;
459
  } else {
460
    for (final key in value.keys) {
96✔
461
      if (key == strings.entryPoints || key == strings.includeDirectives) {
58✔
462
        if (!checkType<YamlList>([...name, key as String], value[key])) {
192✔
463
          return false;
464
        }
465
      } else {
466
        _logger.severe("Unknown key '$key' in '$name'.");
×
467
        return false;
468
      }
469
    }
470
    return true;
471
  }
472
}
473

474
/// Returns location of dynamic library by searching default locations. Logs
475
/// error and throws an Exception if not found.
476
String findDylibAtDefaultLocations() {
48✔
477
  String? k;
478
  // Dylib locations specified by environment variable
479
  final llvmPathEnv = Platform.environment[strings.llvmPathEnv];
96✔
480
  if (llvmPathEnv != null) {
481
    if (p.extension(llvmPathEnv).isNotEmpty && File(llvmPathEnv).existsSync()) {
×
482
      k = llvmPathEnv;
483
    } else {
484
      k = findLibclangDylib(p.join(llvmPathEnv, strings.dynamicLibParentName));
×
485
    }
486
    if (k != null) {
487
      _logger.info("Found libclang: $k");
×
488
      return k;
489
    } else {
490
      _logger.warning("Cannot find libclang "
×
491
          "in the path specified by ${strings.llvmPathEnv}");
492
    }
493
  }
494
  if (Platform.isLinux) {
48✔
495
    for (final l in strings.linuxDylibLocations) {
×
496
      k = findLibclangDylib(l);
×
497
      if (k != null) return k;
498
    }
499
    Process.runSync('ldconfig', ['-p']);
×
500
    final ldConfigResult = Process.runSync('ldconfig', ['-p']);
×
501
    if (ldConfigResult.exitCode == 0) {
×
502
      final lines = (ldConfigResult.stdout as String).split('\n');
×
503
      final paths = [
×
504
        for (final line in lines)
×
505
          if (line.contains('libclang')) line.split(' => ')[1],
×
506
      ];
507
      for (final location in paths) {
×
508
        if (File(location).existsSync()) {
×
509
          return location;
510
        }
511
      }
512
    }
513
  } else if (Platform.isWindows) {
48✔
514
    for (final l in strings.windowsDylibLocations) {
×
515
      k = findLibclangDylib(l);
×
516
      if (k != null) return k;
517
    }
518
  } else if (Platform.isMacOS) {
48✔
519
    for (final l in strings.macOsDylibLocations) {
96✔
520
      k = findLibclangDylib(l);
48✔
521
      if (k != null) return k;
522
    }
523
    final findLibraryResult =
524
        Process.runSync('xcodebuild', ['-find-library', 'libclang.dylib']);
×
525
    if (findLibraryResult.exitCode == 0) {
×
526
      final location = (findLibraryResult.stdout as String).split('\n').first;
×
527
      if (File(location).existsSync()) {
×
528
        return location;
529
      }
530
    }
531
    final xcodePathResult = Process.runSync('xcode-select', ['-print-path']);
×
532
    if (xcodePathResult.exitCode == 0) {
×
533
      final xcodePath = (xcodePathResult.stdout as String).split('\n').first;
×
534
      final location =
535
          p.join(xcodePath, strings.xcodeDylibLocation, strings.dylibFileName);
×
536
      if (File(location).existsSync()) {
×
537
        return location;
538
      }
539
    }
540
  } else {
541
    throw Exception('Unsupported Platform.');
×
542
  }
543

544
  _logger.severe("Couldn't find dynamic library in default locations.");
×
545
  _logger.severe(
×
546
      "Please supply one or more path/to/llvm in ffigen's config under the key '${strings.llvmPath}'.");
×
547
  throw Exception("Couldn't find dynamic library in default locations.");
×
548
}
549

550
String? findLibclangDylib(String parentFolder) {
48✔
551
  final location = p.join(parentFolder, strings.dylibFileName);
96✔
552
  if (File(location).existsSync()) {
96✔
553
    return location;
554
  } else {
555
    return null;
556
  }
557
}
558

559
String llvmPathExtractor(dynamic value) {
×
560
  // Extract libclang's dylib from user specified paths.
561
  for (final path in (value as YamlList)) {
×
562
    if (path is! String) continue;
×
563
    final dylibPath =
564
        findLibclangDylib(p.join(path, strings.dynamicLibParentName));
×
565
    if (dylibPath != null) {
566
      _logger.fine('Found dynamic library at: $dylibPath');
×
567
      return dylibPath;
568
    }
569
    // Check if user has specified complete path to dylib.
570
    final completeDylibPath = path;
571
    if (p.extension(completeDylibPath).isNotEmpty &&
×
572
        File(completeDylibPath).existsSync()) {
×
573
      _logger.info(
×
574
          'Using complete dylib path: $completeDylibPath from llvm-path.');
×
575
      return completeDylibPath;
576
    }
577
  }
578
  _logger.fine(
×
579
      "Couldn't find dynamic library under paths specified by ${strings.llvmPath}.");
×
580
  // Extract path from default locations.
581
  try {
582
    final res = findDylibAtDefaultLocations();
×
583
    return res;
584
  } catch (e) {
585
    _logger.severe(
×
586
        "Couldn't find ${p.join(strings.dynamicLibParentName, strings.dylibFileName)} in specified locations.");
×
587
    exit(1);
×
588
  }
589
}
590

591
bool llvmPathValidator(List<String> name, dynamic value) {
×
592
  if (!checkType<YamlList>(name, value)) {
×
593
    return false;
594
  }
595
  return true;
596
}
597

598
OutputConfig outputExtractor(
48✔
599
    dynamic value, String? configFilename, PackageConfig? packageConfig) {
600
  if (value is String) {
48✔
601
    return OutputConfig(_normalizePath(value, configFilename), null);
96✔
602
  }
603
  value = value as YamlMap;
604
  return OutputConfig(
1✔
605
    _normalizePath((value)[strings.bindings] as String, configFilename),
2✔
606
    value.containsKey(strings.symbolFile)
1✔
607
        ? symbolFileOutputExtractor(
1✔
608
            value[strings.symbolFile], configFilename, packageConfig)
1✔
609
        : null,
610
  );
611
}
612

613
bool outputValidator(List<String> name, dynamic value) {
48✔
614
  if (value is String) {
48✔
615
    return true;
616
  } else if (value is YamlMap) {
1✔
617
    final keys = value.keys;
1✔
618
    var result = true;
619
    for (final key in keys) {
2✔
620
      if (key == strings.bindings) {
1✔
621
        if (!checkType<String>([...name, key as String], value[key])) {
4✔
622
          result = false;
623
        }
624
      } else if (key == strings.symbolFile) {
1✔
625
        result = symbolFileOutputValidator(
1✔
626
            [...name, strings.symbolFile], value[key]);
3✔
627
      } else {
628
        result = false;
629
        _logger.severe("Unknown key '$key' in '$name'.");
×
630
      }
631
    }
632
    return result;
633
  } else {
634
    _logger.severe(
×
635
        "Expected value of key '${name.join(' -> ')}' to be a String or Map.");
×
636
    return false;
637
  }
638
}
639

640
SymbolFile symbolFileOutputExtractor(
1✔
641
    dynamic value, String? configFilename, PackageConfig? packageConfig) {
642
  value = value as YamlMap;
643
  var output = value[strings.output] as String;
1✔
644
  if (Uri.parse(output).scheme != "package") {
3✔
645
    _logger.warning(
2✔
646
        'Consider using a Package Uri for ${strings.symbolFile} -> ${strings.output}: $output so that external packages can use it.');
1✔
647
    output = _normalizePath(output, configFilename);
1✔
648
  } else {
649
    output = packageConfig!.resolve(Uri.parse(output))!.toFilePath();
×
650
  }
651
  final importPath = value[strings.importPath] as String;
1✔
652
  if (Uri.parse(importPath).scheme != "package") {
3✔
653
    _logger.warning(
×
654
        'Consider using a Package Uri for ${strings.symbolFile} -> ${strings.importPath}: $importPath so that external packages can use it.');
×
655
  }
656
  return SymbolFile(importPath, output);
1✔
657
}
658

659
bool symbolFileOutputValidator(List<String> name, dynamic value) {
1✔
660
  if (!checkType<YamlMap>(name, value)) {
1✔
661
    return false;
662
  }
663
  if (!(value as YamlMap).containsKey(strings.output)) {
1✔
664
    _logger.severe("Required '$name -> ${strings.output}'.");
×
665
    return false;
666
  }
667
  if (!(value).containsKey(strings.importPath)) {
1✔
668
    _logger.severe("Required '$name -> ${strings.importPath}'.");
×
669
    return false;
670
  }
671
  for (final key in value.keys) {
2✔
672
    if (key == strings.output || key == strings.importPath) {
2✔
673
      if (!checkType<String>([...name, key as String], value[key])) {
4✔
674
        return false;
675
      }
676
    } else {
677
      _logger.severe("Unknown key '$key' in '$name'.");
×
678
      return false;
679
    }
680
  }
681
  return true;
682
}
683

684
Language languageExtractor(dynamic value) {
17✔
685
  if (value == strings.langC) {
17✔
686
    return Language.c;
687
  } else if (value == strings.langObjC) {
17✔
688
    return Language.objc;
689
  }
690
  return Language.c;
691
}
692

693
bool languageValidator(List<String> name, dynamic value) {
17✔
694
  if (value is String) {
17✔
695
    if (value == strings.langC) {
17✔
696
      return true;
697
    }
698
    if (value == strings.langObjC) {
17✔
699
      _logger.severe('Objective C support is EXPERIMENTAL. The API may change '
34✔
700
          'in a breaking way without notice.');
701
      return true;
702
    }
703
    _logger.severe("'$name' must be one of the following - "
×
704
        "{${strings.langC}, ${strings.langObjC}}");
705
    return false;
706
  }
707
  _logger.severe("Expected value of key '$name' to be a String.");
×
708
  return false;
709
}
710

711
/// Returns true if [str] is not a full name.
712
///
713
/// E.g `abc` is a full name, `abc.*` is not.
714
bool isFullDeclarationName(String str) =>
27✔
715
    quiver.matchesFull(RegExp('[a-zA-Z_0-9]*'), str);
54✔
716

717
Includer _extractIncluderFromYaml(dynamic yamlMap) {
29✔
718
  final includeMatchers = <RegExp>[],
29✔
719
      includeFull = <String>{},
720
      excludeMatchers = <RegExp>[],
29✔
721
      excludeFull = <String>{};
722

723
  final include = (yamlMap[strings.include] as YamlList?)?.cast<String>();
51✔
724
  if (include != null) {
725
    if (include.isEmpty) {
22✔
726
      return Includer.excludeByDefault();
×
727
    }
728
    for (final str in include) {
44✔
729
      if (isFullDeclarationName(str)) {
22✔
730
        includeFull.add(str);
22✔
731
      } else {
732
        includeMatchers.add(RegExp(str, dotAll: true));
2✔
733
      }
734
    }
735
  }
736

737
  final exclude = (yamlMap[strings.exclude] as YamlList?)?.cast<String>();
35✔
738
  if (exclude != null) {
739
    for (final str in exclude) {
12✔
740
      if (isFullDeclarationName(str)) {
6✔
741
        excludeFull.add(str);
6✔
742
      } else {
743
        excludeMatchers.add(RegExp(str, dotAll: true));
×
744
      }
745
    }
746
  }
747

748
  return Includer(
29✔
749
    includeMatchers: includeMatchers,
750
    includeFull: includeFull,
751
    excludeMatchers: excludeMatchers,
752
    excludeFull: excludeFull,
753
  );
754
}
755

756
Declaration declarationConfigExtractor(dynamic yamlMap) {
29✔
757
  final renamePatterns = <RegExpRenamer>[];
29✔
758
  final renameFull = <String, String>{};
29✔
759
  final memberRenamePatterns = <RegExpMemberRenamer>[];
29✔
760
  final memberRenamerFull = <String, Renamer>{};
29✔
761

762
  final includer = _extractIncluderFromYaml(yamlMap);
29✔
763

764
  Includer? symbolIncluder;
765
  if (yamlMap[strings.symbolAddress] != null) {
29✔
766
    symbolIncluder = _extractIncluderFromYaml(yamlMap[strings.symbolAddress]);
6✔
767
  }
768

769
  final rename = (yamlMap[strings.rename] as YamlMap?)?.cast<String, String>();
31✔
770

771
  if (rename != null) {
772
    for (final str in rename.keys) {
4✔
773
      if (isFullDeclarationName(str)) {
2✔
774
        renameFull[str] = rename[str]!;
2✔
775
      } else {
776
        renamePatterns
777
            .add(RegExpRenamer(RegExp(str, dotAll: true), rename[str]!));
8✔
778
      }
779
    }
780
  }
781

782
  final memberRename =
783
      (yamlMap[strings.memberRename] as YamlMap?)?.cast<String, YamlMap>();
30✔
784

785
  if (memberRename != null) {
786
    for (final decl in memberRename.keys) {
2✔
787
      final renamePatterns = <RegExpRenamer>[];
1✔
788
      final renameFull = <String, String>{};
1✔
789

790
      final memberRenameMap = memberRename[decl]!.cast<String, String>();
2✔
791
      for (final member in memberRenameMap.keys) {
2✔
792
        if (isFullDeclarationName(member)) {
1✔
793
          renameFull[member] = memberRenameMap[member]!;
2✔
794
        } else {
795
          renamePatterns.add(RegExpRenamer(
2✔
796
              RegExp(member, dotAll: true), memberRenameMap[member]!));
2✔
797
        }
798
      }
799
      if (isFullDeclarationName(decl)) {
1✔
800
        memberRenamerFull[decl] = Renamer(
2✔
801
          renameFull: renameFull,
802
          renamePatterns: renamePatterns,
803
        );
804
      } else {
805
        memberRenamePatterns.add(
1✔
806
          RegExpMemberRenamer(
1✔
807
            RegExp(decl, dotAll: true),
1✔
808
            Renamer(
1✔
809
              renameFull: renameFull,
810
              renamePatterns: renamePatterns,
811
            ),
812
          ),
813
        );
814
      }
815
    }
816
  }
817

818
  return Declaration(
29✔
819
    includer: includer,
820
    renamer: Renamer(
29✔
821
      renameFull: renameFull,
822
      renamePatterns: renamePatterns,
823
    ),
824
    memberRenamer: MemberRenamer(
29✔
825
      memberRenameFull: memberRenamerFull,
826
      memberRenamePattern: memberRenamePatterns,
827
    ),
828
    symbolAddressIncluder: symbolIncluder,
829
  );
830
}
831

832
bool declarationConfigValidator(List<String> name, dynamic value) {
29✔
833
  var _result = true;
834
  if (value is YamlMap) {
29✔
835
    for (final key in value.keys) {
58✔
836
      if (key == strings.include || key == strings.exclude) {
44✔
837
        if (!checkType<YamlList>([...name, key as String], value[key])) {
100✔
838
          _result = false;
839
        }
840
      } else if (key == strings.rename) {
10✔
841
        if (!checkType<YamlMap>([...name, key as String], value[key])) {
8✔
842
          _result = false;
843
        } else {
844
          for (final subkey in (value[key] as YamlMap).keys) {
6✔
845
            if (!checkType<String>(
2✔
846
                [...name, key, subkey as String], value[key][subkey])) {
10✔
847
              _result = false;
848
            }
849
          }
850
        }
851
      } else if (key == strings.memberRename) {
9✔
852
        if (!checkType<YamlMap>([...name, key as String], value[key])) {
4✔
853
          _result = false;
854
        } else {
855
          for (final declNameKey in (value[key] as YamlMap).keys) {
3✔
856
            if (!checkType<YamlMap>([...name, key, declNameKey as String],
4✔
857
                value[key][declNameKey])) {
2✔
858
              _result = false;
859
            } else {
860
              for (final memberNameKey
861
                  in ((value[key] as YamlMap)[declNameKey] as YamlMap).keys) {
4✔
862
                if (!checkType<String>([
2✔
863
                  ...name,
864
                  key,
1✔
865
                  declNameKey,
1✔
866
                  memberNameKey as String,
1✔
867
                ], value[key][declNameKey][memberNameKey])) {
3✔
868
                  _result = false;
869
                }
870
              }
871
            }
872
          }
873
        }
874
      } else if (key == strings.symbolAddress) {
8✔
875
        if (!checkType<YamlMap>([...name, key as String], value[key])) {
12✔
876
          _result = false;
877
        } else {
878
          for (final subkey in (value[key] as YamlMap).keys) {
9✔
879
            if (subkey == strings.include || subkey == strings.exclude) {
3✔
880
              if (!checkType<YamlList>(
3✔
881
                  [...name, key, subkey as String], value[key][subkey])) {
15✔
882
                _result = false;
883
              }
884
            } else {
885
              _logger.severe("Unknown key '$subkey' in '$name -> $key'.");
×
886
              _result = false;
887
            }
888
          }
889
        }
890
      }
891
    }
892
  } else {
893
    _logger.severe("Expected value '$name' to be a Map.");
×
894
    _result = false;
895
  }
896
  return _result;
897
}
898

899
Includer exposeFunctionTypeExtractor(dynamic value) =>
1✔
900
    _extractIncluderFromYaml(value);
1✔
901

902
bool exposeFunctionTypeValidator(List<String> name, dynamic value) {
1✔
903
  var _result = true;
904

905
  if (!checkType<YamlMap>(name, value)) {
1✔
906
    _result = false;
907
  } else {
908
    final mp = value as YamlMap;
909
    for (final key in mp.keys) {
2✔
910
      if (key == strings.include || key == strings.exclude) {
1✔
911
        if (!checkType<YamlList>([...name, key as String], value[key])) {
4✔
912
          _result = false;
913
        }
914
      } else {
915
        _logger.severe("Unknown subkey '$key' in '$name'.");
×
916
        _result = false;
917
      }
918
    }
919
  }
920

921
  return _result;
922
}
923

924
Includer leafFunctionExtractor(dynamic value) =>
1✔
925
    _extractIncluderFromYaml(value);
1✔
926

927
bool leafFunctionValidator(List<String> name, dynamic value) {
1✔
928
  var _result = true;
929

930
  if (!checkType<YamlMap>(name, value)) {
1✔
931
    _result = false;
932
  } else {
933
    final mp = value as YamlMap;
934
    for (final key in mp.keys) {
2✔
935
      if (key == strings.include || key == strings.exclude) {
1✔
936
        if (!checkType<YamlList>([...name, key as String], value[key])) {
4✔
937
          _result = false;
938
        }
939
      } else {
940
        _logger.severe("Unknown subkey '$key' in '$name'.");
×
941
        _result = false;
942
      }
943
    }
944
  }
945

946
  return _result;
947
}
948

949
SupportedNativeType nativeSupportedType(int value, {bool signed = true}) {
×
950
  switch (value) {
951
    case 1:
×
952
      return signed ? SupportedNativeType.Int8 : SupportedNativeType.Uint8;
953
    case 2:
×
954
      return signed ? SupportedNativeType.Int16 : SupportedNativeType.Uint16;
955
    case 4:
×
956
      return signed ? SupportedNativeType.Int32 : SupportedNativeType.Uint32;
957
    case 8:
×
958
      return signed ? SupportedNativeType.Int64 : SupportedNativeType.Uint64;
959
    default:
960
      throw Exception(
×
961
          'Unsupported value given to sizemap, Allowed values for sizes are: 1, 2, 4, 8');
962
  }
963
}
964

965
String stringExtractor(dynamic value) => value as String;
48✔
966

967
bool nonEmptyStringValidator(List<String> name, dynamic value) {
47✔
968
  if (value is String && value.isNotEmpty) {
94✔
969
    return true;
970
  } else {
971
    _logger.severe("Expected value of key '$name' to be a non-empty String.");
×
972
    return false;
973
  }
974
}
975

976
bool dartClassNameValidator(List<String> name, dynamic value) {
48✔
977
  if (value is String &&
48✔
978
      quiver.matchesFull(RegExp('[a-zA-Z]+[_a-zA-Z0-9]*'), value)) {
96✔
979
    return true;
980
  } else {
981
    _logger.severe(
×
982
        "Expected value of key '$name' to be a valid public class name.");
×
983
    return false;
984
  }
985
}
986

987
CommentType commentExtractor(dynamic value) {
4✔
988
  if (value is bool) {
4✔
989
    if (value) {
990
      return CommentType.def();
×
991
    } else {
992
      return CommentType.none();
1✔
993
    }
994
  }
995
  final ct = CommentType.def();
3✔
996
  if (value is YamlMap) {
3✔
997
    for (final key in value.keys) {
6✔
998
      if (key == strings.style) {
3✔
999
        if (value[key] == strings.any) {
6✔
1000
          ct.style = CommentStyle.any;
2✔
1001
        } else if (value[key] == strings.doxygen) {
4✔
1002
          ct.style = CommentStyle.doxygen;
2✔
1003
        }
1004
      } else if (key == strings.length) {
3✔
1005
        if (value[key] == strings.full) {
6✔
1006
          ct.length = CommentLength.full;
3✔
1007
        } else if (value[key] == strings.brief) {
2✔
1008
          ct.length = CommentLength.brief;
1✔
1009
        }
1010
      }
1011
    }
1012
  }
1013
  return ct;
1014
}
1015

1016
bool commentValidator(List<String> name, dynamic value) {
4✔
1017
  if (value is bool) {
4✔
1018
    return true;
1019
  } else if (value is YamlMap) {
3✔
1020
    var result = true;
1021
    for (final key in value.keys) {
6✔
1022
      if (key == strings.style) {
3✔
1023
        if (value[key] is! String ||
6✔
1024
            !(value[key] == strings.doxygen || value[key] == strings.any)) {
10✔
1025
          _logger.severe(
×
1026
              "'$name'>'${strings.style}' must be one of the following - {${strings.doxygen}, ${strings.any}}");
×
1027
          result = false;
1028
        }
1029
      } else if (key == strings.length) {
3✔
1030
        if (value[key] is! String ||
6✔
1031
            !(value[key] == strings.brief || value[key] == strings.full)) {
12✔
1032
          _logger.severe(
×
1033
              "'$name'>'${strings.length}' must be one of the following - {${strings.brief}, ${strings.full}}");
×
1034
          result = false;
1035
        }
1036
      } else {
1037
        _logger.severe("Unknown key '$key' in '$name'.");
×
1038
        result = false;
1039
      }
1040
    }
1041
    return result;
1042
  } else {
1043
    _logger.severe("Expected value of key '$name' to be a bool or a Map.");
×
1044
    return false;
1045
  }
1046
}
1047

1048
CompoundDependencies dependencyOnlyExtractor(dynamic value) {
1✔
1049
  var result = CompoundDependencies.full;
1050
  if (value == strings.opaqueCompoundDependencies) {
1✔
1051
    result = CompoundDependencies.opaque;
1052
  }
1053
  return result;
1054
}
1055

1056
bool dependencyOnlyValidator(List<String> name, dynamic value) {
1✔
1057
  var result = true;
1058
  if (value is! String ||
1✔
1059
      !(value == strings.fullCompoundDependencies ||
1✔
1060
          value == strings.opaqueCompoundDependencies)) {
1✔
1061
    _logger.severe(
×
1062
        "'$name' must be one of the following - {${strings.fullCompoundDependencies}, ${strings.opaqueCompoundDependencies}}");
×
1063
    result = false;
1064
  }
1065
  return result;
1066
}
1067

1068
StructPackingOverride structPackingOverrideExtractor(dynamic value) {
1✔
1069
  final matcherMap = <RegExp, int?>{};
1✔
1070
  for (final key in (value as YamlMap).keys) {
2✔
1071
    matcherMap[RegExp(key as String, dotAll: true)] =
2✔
1072
        strings.packingValuesMap[value[key]];
2✔
1073
  }
1074
  return StructPackingOverride(matcherMap: matcherMap);
1✔
1075
}
1076

1077
bool structPackingOverrideValidator(List<String> name, dynamic value) {
1✔
1078
  var _result = true;
1079

1080
  if (!checkType<YamlMap>([...name], value)) {
2✔
1081
    _result = false;
1082
  } else {
1083
    for (final key in (value as YamlMap).keys) {
2✔
1084
      if (!(strings.packingValuesMap.keys.contains(value[key]))) {
3✔
1085
        _logger.severe(
2✔
1086
            "'$name -> $key' must be one of the following - ${strings.packingValuesMap.keys.toList()}");
3✔
1087
        _result = false;
1088
      }
1089
    }
1090
  }
1091

1092
  return _result;
1093
}
1094

1095
FfiNativeConfig ffiNativeExtractor(dynamic yamlConfig) {
1✔
1096
  final yamlMap = yamlConfig as YamlMap?;
1097
  return FfiNativeConfig(
1✔
1098
    enabled: true,
1099
    asset: yamlMap?[strings.ffiNativeAsset] as String?,
×
1100
  );
1101
}
1102

1103
bool ffiNativeValidator(List<String> name, dynamic yamlConfig) {
1✔
1104
  if (!checkType<YamlMap?>(name, yamlConfig)) {
1✔
1105
    return false;
1106
  }
1107
  if (yamlConfig == null) {
1108
    // Empty means no asset name.
1109
    return true;
1110
  }
1111
  for (final key in (yamlConfig as YamlMap).keys) {
×
1112
    if (!checkType<String>([...name, key as String], yamlConfig[key])) {
×
1113
      return false;
1114
    }
1115
    if (key != strings.ffiNativeAsset) {
×
1116
      _logger.severe("'$name -> $key' must be one of the following - ${[
×
1117
        strings.ffiNativeAsset
1118
      ]}");
×
1119
    }
1120
  }
1121
  return true;
1122
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc