Skip to content

Commit

Permalink
[ffigenpad] cleanup
Browse files Browse the repository at this point in the history
  • Loading branch information
TheComputerM committed Sep 4, 2024
1 parent 1755e12 commit 2312d63
Show file tree
Hide file tree
Showing 5 changed files with 106 additions and 66 deletions.
6 changes: 3 additions & 3 deletions pkgs/ffigenpad/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@ However the files listed do not have an ffigen counterpart:
### Prerequisites

- dart
- emscripten
- emscripten (>= 3.1.61)
- for website
- nodejs
- pnpm
- nodejs (>= ^20.17.0)
- pnpm (optional but recommended)

### Steps

Expand Down
112 changes: 69 additions & 43 deletions pkgs/ffigenpad/tool/build_libclang.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,33 +12,92 @@ import 'package:yaml/yaml.dart';

final _formatter = DartFormatter();

/// Used to mock the Clang class in ffigen.
/// Generates `lib/src/header_parser/clang_bindings/clang_wrapper.dart`.
void _generateClangClassWrapper(List<String> exportedFunctions) {
final wrapperFunctions = exportedFunctions.map((func) {
final funcAlias = func.replaceFirst(RegExp(r'_wrap'), '');
return 'final $funcAlias = c.$func;';
}).join('\n');

final output = """
// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
// 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.
// AUTO GENERATED FILE, DO NOT EDIT.
// ignore_for_file: camel_case_types, non_constant_identifier_names
import 'clang_bindings.dart' as c;
class Clang {
$wrapperFunctions
}
""";

File(p.joinAll([
p.dirname(Platform.script.path),
'..',
'lib',
'src',
'header_parser',
'clang_bindings',
'clang_wrapper.dart'
])).writeAsStringSync(_formatter.format(output));
}

void _generateClangExports(String filePath, List<String> exportedFunctions) {
// export malloc and free additionally to use to do memory
// management in ffigenpad
final functions = [...exportedFunctions, 'malloc', 'free']
.map((func) => '_$func')
.join('\n');
File(filePath).writeAsStringSync(functions);
}

void _checkEmscriptenVersion() {
final process = Process.runSync('emcc', ['--version']);
final versionExp = RegExp(r'\d+.\d+.\d+');
final version = versionExp.stringMatch(process.stdout.toString());
if (version == null) {
throw Exception('Failed to extract emscripten version.');
}
final versionList = version.split('.').map(int.parse).toList(growable: false);
if (!(versionList[0] > 3 || versionList[1] > 1 || versionList[2] > 60)) {
throw Exception('Upgrade to atleast v3.1.61 of emscripten to proceed.');
}
print('Acceptable emscripten version: $version');
}

void main(List<String> args) async {
// Load the ffigen config for libclang
// Load the ffigen config for libclang to extract included functions.
final configYaml = await File(p.join(
p.dirname(Platform.script.path),
'libclang_config.yaml',
)).readAsString();
final config = loadYaml(configYaml);
final config = loadYaml(configYaml) as YamlMap;

// Get the list of functions to be exported from libclang
// Get the list of functions to be exported from libclang.
final exportedFunctions =
// ignore: avoid_dynamic_calls
List<String>.from(config['functions']['include'] as Iterable<String>);
List<String>.from(config['functions']['include'] as YamlList);

final libclangDir = p.joinAll(
[p.dirname(Platform.script.path), '..', 'third_party', 'libclang'],
);

print('Writing third_party/libclang/libclang.exports');
await File(p.join(libclangDir, 'libclang.exports')).writeAsString([
...exportedFunctions,
'malloc',
'free'
].map((func) => '_$func').join('\n'));
_generateClangExports(
p.join(libclangDir, 'libclang.exports'),
exportedFunctions,
);

print('Writing lib/src/header_parser/clang_wrapper.dart');
_generateClangClassWrapper(exportedFunctions);

print('Checking emscripten version');
_checkEmscriptenVersion();

print('Building bin/libclang.wasm');
final archiveFiles =
await Directory(p.join(libclangDir, 'llvm-project', 'lib'))
Expand All @@ -64,6 +123,7 @@ void main(List<String> args) async {
'./llvm-project/lib/clang@/lib/clang',
'-sEXPORTED_FUNCTIONS=@libclang.exports',
'-sFS_DEBUG',
// ignore: lines_longer_than_80_chars
'-sEXPORTED_RUNTIME_METHODS=FS,wasmExports,wasmMemory,addFunction,removeFunction',
// used for production builds
if (args.contains('--optimize')) ...['-O3', '-lexports.js']
Expand All @@ -74,37 +134,3 @@ void main(List<String> args) async {
stdout.write(result.stdout);
stderr.write(result.stderr);
}

void _generateClangClassWrapper(List<String> exportedFunctions) async {
final wrapperFunctions = exportedFunctions.map((func) {
final funcAlias = func.endsWith('_wrap')
? func.substring(0, func.length - '_wrap'.length)
: func;
return ' final $funcAlias = c.$func;';
}).join('\n');

final output = """
// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
// 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.
// AUTO GENERATED FILE, DO NOT EDIT.
// ignore_for_file: camel_case_types, non_constant_identifier_names
import 'clang_bindings.dart' as c;
class Clang {
$wrapperFunctions
}
""";

await File(p.joinAll([
p.dirname(Platform.script.path),
'..',
'lib',
'src',
'header_parser',
'clang_bindings',
'clang_wrapper.dart'
])).writeAsString(_formatter.format(output));
}
11 changes: 9 additions & 2 deletions pkgs/ffigenpad/tool/setup.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,19 @@ import 'package:path/path.dart' as p;
const llvmCompiledRelease =
'https://github.com/TheComputerM/libclang-wasm/releases/download/v0/llvm-build.tar.gz';

Future<void> main() async {
Future<void> main(List<String> args) async {
final libclangDir = p.joinAll(
[p.dirname(Platform.script.path), '..', 'third_party', 'libclang'],
);
final llvmDir = p.join(libclangDir, 'llvm-project');

if (args.contains('-f') && await Directory(llvmDir).exists()) {
await Directory(llvmDir).delete(recursive: true);
}

if (await Directory(llvmDir).exists()) {
print('Compiled LLVM archives already exist');
print('Compiled LLVM archives already exist.');
print('Use `-f` to forcefully download archives.');
return;
}
print('Downloading compiled LLVM archives');
Expand Down Expand Up @@ -49,6 +55,7 @@ Future<void> main() async {
print('Archive files extracted successfully.');
} else {
print('Error: ${result.stderr}');
return;
}

// remove temp .tar.gz file
Expand Down
4 changes: 4 additions & 0 deletions pkgs/ffigenpad/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,16 @@
"solid-js": "^1.8.21"
},
"devDependencies": {
"@babel/types": "^7.25.6",
"@pandacss/dev": "^0.45.1",
"@park-ui/panda-preset": "^0.42.0",
"@types/node": "^22.4.0",
"typescript": "^5.5.4",
"vite": "^5.4.1",
"vite-plugin-solid": "^2.10.2",
"vite-tsconfig-paths": "^5.0.1"
},
"engines": {
"node": ">=20.17.0"
}
}
39 changes: 21 additions & 18 deletions pkgs/ffigenpad/web/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 2312d63

Please sign in to comment.