Use `package:config` WIP
diff --git a/lib/src/config_provider/config.dart b/lib/src/config_provider/config.dart index 6290bac..369668e 100644 --- a/lib/src/config_provider/config.dart +++ b/lib/src/config_provider/config.dart
@@ -6,6 +6,7 @@ import 'dart:io'; +import 'package:config/config.dart' as pkg_config; import 'package:ffigen/src/code_generator.dart'; import 'package:logging/logging.dart'; import 'package:package_config/package_config_types.dart'; @@ -22,11 +23,9 @@ /// Handles validation, extraction of confiurations from yaml file. class Config { /// Input filename. - String? get filename => _filename; String? _filename; /// Package config. - PackageConfig? get packageConfig => _packageConfig; PackageConfig? _packageConfig; /// Location for llvm/lib folder. @@ -177,17 +176,32 @@ /// Create config from Yaml map. factory Config.fromYaml(YamlMap map, {String? filename, PackageConfig? packageConfig}) { + final config = pkg_config.Config( + fileParsed: map.cast(), + fileSourceUri: Uri(path: filename), + ); + + return Config.fromConfig( + config, + filename: filename, + packageConfig: packageConfig, + ); + } + + /// Create config from Yaml map. + factory Config.fromConfig(pkg_config.Config config, + {String? filename, PackageConfig? packageConfig}) { final configspecs = Config._(filename, packageConfig); - _logger.finest('Config Map: $map'); + _logger.finest('Config: $config'); final specs = configspecs._getSpecs(); - final result = configspecs._checkConfigs(map, specs); + final result = configspecs._checkConfigs(config, specs); if (!result) { throw FormatException('Invalid configurations provided.'); } - configspecs._extract(map, specs); + configspecs._extract(config, specs); return configspecs; } @@ -212,12 +226,14 @@ } /// Validates Yaml according to given specs. - bool _checkConfigs(YamlMap map, Map<List<String>, Specification> specs) { + bool _checkConfigs( + pkg_config.Config config, Map<List<String>, Specification> specs) { var result = true; for (final key in specs.keys) { final spec = specs[key]; - if (checkKeyInYaml(key, map)) { - result = result && spec!.validator(key, getKeyValueFromYaml(key, map)); + final value = config.getFileValue<Object>(key.join('.')); + if (value != null) { + result = result && spec!.validator(key, value); } else if (spec!.requirement == Requirement.yes) { _logger.severe("Key '$key' is required."); result = false; @@ -226,7 +242,10 @@ } } // Warn about unknown keys. - warnUnknownKeys(specs.keys.toList(), map); + // warnUnknownKeys(specs.keys.toList(), map); + // TODO(dacoharkes): Should the config allow access to the list of keys? + // Should that access be uniform across env/cli/file? Environment is not + // going to be empty. return result; } @@ -234,23 +253,22 @@ /// Extracts variables from Yaml according to given specs. /// /// Validation must be done beforehand, using [_checkConfigs]. - void _extract(YamlMap map, Map<List<String>, Specification> specs) { + void _extract( + pkg_config.Config config, Map<List<String>, Specification> specs) { for (final key in specs.keys) { - final spec = specs[key]; - if (checkKeyInYaml(key, map)) { - spec!.extractedResult(spec.extractor(getKeyValueFromYaml(key, map))); - } else { - spec!.extractedResult(spec.defaultValue?.call()); - } + final spec = specs[key]!; + final value = spec.extractor(config) ?? spec.defaultValue?.call(); + spec.extractedResult(value); } } - /// Returns map of various specifications avaialble for our tool. + /// Returns map of various specifications available for our tool. /// /// Key: Name, Value: [Specification] Map<List<String>, Specification> _getSpecs() { return <List<String>, Specification>{ [strings.llvmPath]: Specification<String>( + key: [strings.llvmPath], requirement: Requirement.no, validator: llvmPathValidator, extractor: llvmPathExtractor, @@ -260,16 +278,18 @@ }, ), [strings.output]: Specification<OutputConfig>( + key: [strings.output], requirement: Requirement.yes, validator: outputValidator, extractor: (dynamic value) => - outputExtractor(value, filename, packageConfig), + outputExtractor(value, _filename, _packageConfig), extractedResult: (dynamic result) { _output = (result as OutputConfig).output; _symbolFile = result.symbolFile; }, ), [strings.language]: Specification<Language>( + key: [strings.language], requirement: Requirement.no, validator: languageValidator, extractor: languageExtractor, @@ -277,12 +297,14 @@ extractedResult: (dynamic result) => _language = result as Language, ), [strings.headers]: Specification<Headers>( + key: [strings.headers], requirement: Requirement.yes, validator: headersValidator, - extractor: (dynamic value) => headersExtractor(value, filename), + extractor: (dynamic value) => headersExtractor(value, _filename), extractedResult: (dynamic result) => _headers = result as Headers, ), [strings.compilerOpts]: Specification<List<String>>( + key: [strings.compilerOpts], requirement: Requirement.no, validator: compilerOptsValidator, extractor: compilerOptsExtractor, @@ -291,6 +313,7 @@ _compilerOpts = result as List<String>, ), [strings.compilerOptsAuto]: Specification<CompilerOptsAuto>( + key: [strings.compilerOptsAuto], requirement: Requirement.no, validator: compilerOptsAutoValidator, extractor: compilerOptsAutoExtractor, @@ -298,8 +321,10 @@ extractedResult: (dynamic result) { _compilerOpts .addAll((result as CompilerOptsAuto).extractCompilerOpts()); - }), + }, + ), [strings.functions]: Specification<Declaration>( + key: [strings.functions], requirement: Requirement.no, validator: declarationConfigValidator, extractor: declarationConfigExtractor, @@ -309,6 +334,7 @@ }, ), [strings.structs]: Specification<Declaration>( + key: [strings.structs], requirement: Requirement.no, validator: declarationConfigValidator, extractor: declarationConfigExtractor, @@ -318,6 +344,7 @@ }, ), [strings.unions]: Specification<Declaration>( + key: [strings.unions], requirement: Requirement.no, validator: declarationConfigValidator, extractor: declarationConfigExtractor, @@ -327,6 +354,7 @@ }, ), [strings.enums]: Specification<Declaration>( + key: [strings.enums], requirement: Requirement.no, validator: declarationConfigValidator, extractor: declarationConfigExtractor, @@ -336,6 +364,7 @@ }, ), [strings.unnamedEnums]: Specification<Declaration>( + key: [strings.unnamedEnums], requirement: Requirement.no, validator: declarationConfigValidator, extractor: declarationConfigExtractor, @@ -344,6 +373,7 @@ _unnamedEnumConstants = result as Declaration, ), [strings.globals]: Specification<Declaration>( + key: [strings.globals], requirement: Requirement.no, validator: declarationConfigValidator, extractor: declarationConfigExtractor, @@ -353,6 +383,7 @@ }, ), [strings.macros]: Specification<Declaration>( + key: [strings.macros], requirement: Requirement.no, validator: declarationConfigValidator, extractor: declarationConfigExtractor, @@ -362,6 +393,7 @@ }, ), [strings.typedefs]: Specification<Declaration>( + key: [strings.typedefs], requirement: Requirement.no, validator: declarationConfigValidator, extractor: declarationConfigExtractor, @@ -371,6 +403,7 @@ }, ), [strings.objcInterfaces]: Specification<Declaration>( + key: [strings.objcInterfaces], requirement: Requirement.no, validator: declarationConfigValidator, extractor: declarationConfigExtractor, @@ -381,6 +414,7 @@ ), [strings.objcInterfaces, strings.objcModule]: Specification<Map<String, String>>( + key: [strings.objcInterfaces, strings.objcModule], requirement: Requirement.no, validator: stringStringMapValidator, extractor: stringStringMapExtractor, @@ -389,6 +423,7 @@ ObjCModulePrefixer(result as Map<String, String>), ), [strings.libraryImports]: Specification<Map<String, LibraryImport>>( + key: [strings.libraryImports], validator: libraryImportsValidator, extractor: libraryImportsExtractor, defaultValue: () => <String, LibraryImport>{}, @@ -398,9 +433,10 @@ ), [strings.import, strings.symbolFilesImport]: Specification<Map<String, ImportedType>>( + key: [strings.import, strings.symbolFilesImport], validator: symbolFileImportValidator, extractor: (value) => symbolFileImportExtractor( - value, _libraryImports, filename, packageConfig), + value, _libraryImports, _filename, _packageConfig), defaultValue: () => <String, ImportedType>{}, extractedResult: (dynamic result) { _usrTypeMappings = result as Map<String, ImportedType>; @@ -408,6 +444,7 @@ ), [strings.typeMap, strings.typeMapTypedefs]: Specification<Map<String, List<String>>>( + key: [strings.typeMap, strings.typeMapTypedefs], validator: typeMapValidator, extractor: typeMapExtractor, defaultValue: () => <String, List<String>>{}, @@ -418,6 +455,7 @@ ), [strings.typeMap, strings.typeMapStructs]: Specification<Map<String, List<String>>>( + key: [strings.typeMap, strings.typeMapStructs], validator: typeMapValidator, extractor: typeMapExtractor, defaultValue: () => <String, List<String>>{}, @@ -428,6 +466,7 @@ ), [strings.typeMap, strings.typeMapUnions]: Specification<Map<String, List<String>>>( + key: [strings.typeMap, strings.typeMapUnions], validator: typeMapValidator, extractor: typeMapExtractor, defaultValue: () => <String, List<String>>{}, @@ -438,6 +477,7 @@ ), [strings.typeMap, strings.typeMapNativeTypes]: Specification<Map<String, List<String>>>( + key: [strings.typeMap, strings.typeMapNativeTypes], validator: typeMapValidator, extractor: typeMapExtractor, defaultValue: () => <String, List<String>>{}, @@ -447,29 +487,36 @@ }, ), [strings.excludeAllByDefault]: Specification<bool>( + key: [strings.excludeAllByDefault], requirement: Requirement.no, validator: booleanValidator, extractor: booleanExtractor, + extractor2: booleanExtractor2, defaultValue: () => false, extractedResult: (dynamic result) => _excludeAllByDefault = result as bool, ), [strings.sort]: Specification<bool>( + key: [strings.sort], requirement: Requirement.no, validator: booleanValidator, extractor: booleanExtractor, + extractor2: booleanExtractor2, defaultValue: () => false, extractedResult: (dynamic result) => _sort = result as bool, ), [strings.useSupportedTypedefs]: Specification<bool>( + key: [strings.useSupportedTypedefs], requirement: Requirement.no, validator: booleanValidator, extractor: booleanExtractor, + extractor2: booleanExtractor2, defaultValue: () => true, extractedResult: (dynamic result) => _useSupportedTypedefs = result as bool, ), [strings.comments]: Specification<CommentType>( + key: [strings.comments], requirement: Requirement.no, validator: commentValidator, extractor: commentExtractor, @@ -479,6 +526,7 @@ ), [strings.structs, strings.dependencyOnly]: Specification<CompoundDependencies>( + key: [strings.structs, strings.dependencyOnly], requirement: Requirement.no, validator: dependencyOnlyValidator, extractor: dependencyOnlyExtractor, @@ -488,6 +536,7 @@ ), [strings.unions, strings.dependencyOnly]: Specification<CompoundDependencies>( + key: [strings.unions, strings.dependencyOnly], requirement: Requirement.no, validator: dependencyOnlyValidator, extractor: dependencyOnlyExtractor, @@ -497,6 +546,7 @@ ), [strings.structs, strings.structPack]: Specification<StructPackingOverride>( + key: [strings.structs, strings.structPack], requirement: Requirement.no, validator: structPackingOverrideValidator, extractor: structPackingOverrideExtractor, @@ -505,6 +555,7 @@ _structPackingOverride = result as StructPackingOverride, ), [strings.name]: Specification<String>( + key: [strings.name], requirement: Requirement.prefer, validator: dartClassNameValidator, extractor: stringExtractor, @@ -512,6 +563,7 @@ extractedResult: (dynamic result) => _wrapperName = result as String, ), [strings.description]: Specification<String?>( + key: [strings.description], requirement: Requirement.prefer, validator: nonEmptyStringValidator, extractor: stringExtractor, @@ -520,20 +572,24 @@ _wrapperDocComment = result as String?, ), [strings.preamble]: Specification<String?>( + key: [strings.preamble], requirement: Requirement.no, validator: nonEmptyStringValidator, extractor: stringExtractor, extractedResult: (dynamic result) => _preamble = result as String?, ), [strings.useDartHandle]: Specification<bool>( + key: [strings.useDartHandle], requirement: Requirement.no, validator: booleanValidator, extractor: booleanExtractor, + extractor2: booleanExtractor2, defaultValue: () => true, extractedResult: (dynamic result) => _useDartHandle = result as bool, ), [strings.functions, strings.exposeFunctionTypedefs]: Specification<Includer>( + key: [strings.functions, strings.exposeFunctionTypedefs], requirement: Requirement.no, validator: exposeFunctionTypeValidator, extractor: exposeFunctionTypeExtractor, @@ -542,6 +598,7 @@ _exposeFunctionTypedefs = result as Includer, ), [strings.functions, strings.leafFunctions]: Specification<Includer>( + key: [strings.functions, strings.leafFunctions], requirement: Requirement.no, validator: leafFunctionValidator, extractor: leafFunctionExtractor, @@ -550,6 +607,7 @@ _leafFunctions = result as Includer, ), [strings.ffiNative]: Specification<FfiNativeConfig>( + key: [strings.ffiNative], requirement: Requirement.no, validator: ffiNativeValidator, extractor: ffiNativeExtractor,
diff --git a/lib/src/config_provider/config_types.dart b/lib/src/config_provider/config_types.dart index 8521bdb..a6b8f83 100644 --- a/lib/src/config_provider/config_types.dart +++ b/lib/src/config_provider/config_types.dart
@@ -5,6 +5,7 @@ /// Contains all the neccesary classes required by config. import 'dart:io'; +import 'package:config/config.dart' as pkg_config; import 'package:quiver/pattern.dart' as quiver; import 'path_finder.dart'; @@ -67,17 +68,21 @@ /// /// [E] is the return type of the extractedResult. class Specification<E> { + final List<String> key; final bool Function(List<String> name, dynamic value) validator; - final E Function(dynamic map) extractor; + final E Function(pkg_config.Config config) extractor; + final E? Function(pkg_config.Config config, List<String> key)? extractor2; final E Function()? defaultValue; final Requirement requirement; final void Function(dynamic result) extractedResult; Specification({ + required this.key, required this.extractedResult, required this.validator, required this.extractor, + this.extractor2, this.defaultValue, this.requirement = Requirement.no, });
diff --git a/lib/src/config_provider/spec_utils.dart b/lib/src/config_provider/spec_utils.dart index 9b03ebb..426a531 100644 --- a/lib/src/config_provider/spec_utils.dart +++ b/lib/src/config_provider/spec_utils.dart
@@ -4,6 +4,7 @@ import 'dart:io'; +import 'package:config/config.dart' as pkg_config; import 'package:ffigen/src/code_generator.dart'; import 'package:ffigen/src/code_generator/utils.dart'; import 'package:file/local.dart'; @@ -113,6 +114,9 @@ } } +bool? booleanExtractor2(pkg_config.Config config, List<String> key) => + config.getBool(key.join('.')); + bool booleanExtractor(dynamic value) => value as bool; bool booleanValidator(List<String> name, dynamic value) =>
diff --git a/lib/src/executables/ffigen.dart b/lib/src/executables/ffigen.dart index c37cb4f..473a815 100644 --- a/lib/src/executables/ffigen.dart +++ b/lib/src/executables/ffigen.dart
@@ -7,6 +7,7 @@ import 'package:args/args.dart'; import 'package:cli_util/cli_logging.dart' show Ansi; +import 'package:config/config.dart' as pkg_config; import 'package:ffigen/ffigen.dart'; import 'package:logging/logging.dart'; import 'package:package_config/package_config.dart'; @@ -75,9 +76,16 @@ // Parse config from yaml. if (result.wasParsed(conf)) { - config = getConfigFromCustomYaml(result[conf] as String, packageConfig); + config = getConfigFromCustomYaml( + result[conf] as String, + packageConfig, + result['defines'] as List<String>, + ); } else { - config = getConfigFromPubspec(packageConfig); + config = getConfigFromPubspec( + packageConfig, + result['defines'] as List<String>, + ); } // Add compiler options from command line. @@ -91,7 +99,10 @@ } /// Extracts configuration from pubspec file. -Config getConfigFromPubspec(PackageConfig? packageConfig) { +Config getConfigFromPubspec( + PackageConfig? packageConfig, + List<String> cliDefines, +) { final pubspecFile = File(pubspecName); if (!pubspecFile.existsSync()) { @@ -110,12 +121,24 @@ _logger.severe("Couldn't find an entry for '$configKey' in $pubspecName."); exit(1); } - return Config.fromYaml(bindingsConfigMap, + + final config = pkg_config.Config( + cliDefines: cliDefines, + environment: Platform.environment, + fileParsed: bindingsConfigMap.cast(), + fileSourceUri: pubspecFile.uri, + ); + + return Config.fromConfig(config, filename: pubspecFile.path, packageConfig: packageConfig); } /// Extracts configuration from a custom yaml file. -Config getConfigFromCustomYaml(String yamlPath, PackageConfig? packageConfig) { +Config getConfigFromCustomYaml( + String yamlPath, + PackageConfig? packageConfig, + List<String> cliDefines, +) { final yamlFile = File(yamlPath); if (!yamlFile.existsSync()) { @@ -123,7 +146,14 @@ exit(1); } - return Config.fromFile(yamlFile, packageConfig: packageConfig); + final config = pkg_config.Config( + cliDefines: cliDefines, + environment: Platform.environment, + fileContents: yamlFile.readAsStringSync(), + fileSourceUri: yamlFile.uri, + ); + + return Config.fromConfig(config, packageConfig: packageConfig); } /// Parses the cmd line arguments. @@ -158,6 +188,15 @@ compilerOpts, help: 'Compiler options for clang. (E.g --$compilerOpts "-I/headers -W")', ); + parser.addMultiOption( + 'define', + abbr: 'D', + help: '''Define or override a config property from command line. +The same option can be passed multiple times. +Keys should only contain lower-case alphanumeric characters, underscores, +and '.'s''', + ); + ArgResults results; try {
diff --git a/pubspec.yaml b/pubspec.yaml index 96c6027..3f31570 100644 --- a/pubspec.yaml +++ b/pubspec.yaml
@@ -2,6 +2,9 @@ # for details. All rights reserved. Use of this source code is governed by a # BSD-style license that can be found in the LICENSE file. +# DO NOT MERGE: prototype. +publish_to: none + name: ffigen version: 7.2.7 description: Generator for FFI bindings, using LibClang to parse C header files. @@ -22,6 +25,10 @@ file: ^6.0.0 package_config: ^2.1.0 yaml_edit: ^2.0.3 + config: + git: + url: https://github.com/dcharkes/config.git + ref: c9a6a3fb1050e2429289f0a98d5b5f97d5b988da dev_dependencies: lints: ^2.0.1