From 61974a394a9d5b7a2e893f10bcbe4b9c42041efe Mon Sep 17 00:00:00 2001 From: Brian Quinlan Date: Fri, 5 Sep 2025 12:44:10 -0700 Subject: [PATCH 01/14] Extract the POSIX API into a separate package --- .github/workflows/unix_api.yml | 65 ++ pkgs/io_file/lib/src/libc.dart | 7 - .../io_file/lib/src/vm_posix_file_system.dart | 2 +- pkgs/io_file/pubspec.yaml | 2 + pkgs/io_file/test/copy_file_test.dart | 2 +- .../test/remove_directory_tree_test.dart | 3 +- pkgs/unix_api/.gitattributes | 6 + pkgs/unix_api/.gitignore | 7 + pkgs/unix_api/CHANGELOG.md | 3 + pkgs/unix_api/README.md | 32 + pkgs/unix_api/analysis_options.yaml | 30 + .../constants-ffigen.yaml | 2 +- pkgs/{io_file => unix_api}/constants.json | 0 pkgs/unix_api/example/unix_api_example.dart | 6 + pkgs/{io_file => unix_api}/ffigen.yaml | 2 +- pkgs/{io_file => unix_api}/hook/build.dart | 0 .../unix_api/lib/src/constant_bindings.g.dart | 193 ++++++ pkgs/unix_api/lib/src/constants.g.dart | 555 ++++++++++++++++++ pkgs/unix_api/lib/src/libc_bindings.g.dart | 195 ++++++ pkgs/unix_api/lib/unix_api.dart | 7 + pkgs/unix_api/pubspec.yaml | 21 + pkgs/{io_file => unix_api}/src/constants.g.c | 0 pkgs/{io_file => unix_api}/src/constants.g.h | 0 pkgs/{io_file => unix_api}/src/libc_shim.c | 0 pkgs/{io_file => unix_api}/src/libc_shim.h | 0 pkgs/unix_api/test/dirent_test.dart | 43 ++ .../tool/build_constants.dart | 0 pkgs/{io_file => unix_api}/tool/generate.dart | 0 28 files changed, 1171 insertions(+), 12 deletions(-) create mode 100644 .github/workflows/unix_api.yml delete mode 100644 pkgs/io_file/lib/src/libc.dart create mode 100644 pkgs/unix_api/.gitattributes create mode 100644 pkgs/unix_api/.gitignore create mode 100644 pkgs/unix_api/CHANGELOG.md create mode 100644 pkgs/unix_api/README.md create mode 100644 pkgs/unix_api/analysis_options.yaml rename pkgs/{io_file => unix_api}/constants-ffigen.yaml (93%) rename pkgs/{io_file => unix_api}/constants.json (100%) create mode 100644 pkgs/unix_api/example/unix_api_example.dart rename pkgs/{io_file => unix_api}/ffigen.yaml (93%) rename pkgs/{io_file => unix_api}/hook/build.dart (100%) create mode 100644 pkgs/unix_api/lib/src/constant_bindings.g.dart create mode 100644 pkgs/unix_api/lib/src/constants.g.dart create mode 100644 pkgs/unix_api/lib/src/libc_bindings.g.dart create mode 100644 pkgs/unix_api/lib/unix_api.dart create mode 100644 pkgs/unix_api/pubspec.yaml rename pkgs/{io_file => unix_api}/src/constants.g.c (100%) rename pkgs/{io_file => unix_api}/src/constants.g.h (100%) rename pkgs/{io_file => unix_api}/src/libc_shim.c (100%) rename pkgs/{io_file => unix_api}/src/libc_shim.h (100%) create mode 100644 pkgs/unix_api/test/dirent_test.dart rename pkgs/{io_file => unix_api}/tool/build_constants.dart (100%) rename pkgs/{io_file => unix_api}/tool/generate.dart (100%) diff --git a/.github/workflows/unix_api.yml b/.github/workflows/unix_api.yml new file mode 100644 index 00000000..44234921 --- /dev/null +++ b/.github/workflows/unix_api.yml @@ -0,0 +1,65 @@ +name: package:io_file + +permissions: read-all + +on: + # Run CI on pushes to the main branch, and on PRs against main. + push: + branches: [ main ] + paths: + - '.github/workflows/unix_api.yml' + - 'pkgs/unix_api/**' + pull_request: + branches: [ main ] + paths: + - '.github/workflows/unix_api.yml' + - 'pkgs/unix_api/**' + schedule: + - cron: '0 0 * * 0' # weekly + +defaults: + run: + working-directory: pkgs/unix_api + +jobs: + analyze_and_format: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 + - uses: dart-lang/setup-dart@e51d8e571e22473a2ddebf0ef8a2123f0ab2c02c + with: + sdk: dev + - run: dart pub get + - run: dart analyze --fatal-infos + - run: dart format --output=none --set-exit-if-changed . + + check-generation: + # Verify that the generated code is up-to-date. Every platform/arch should + # generate the same API bindings. + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, ubuntu-24.04-arm, macos-latest, windows-latest] + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 + - uses: dart-lang/setup-dart@e51d8e571e22473a2ddebf0ef8a2123f0ab2c02c + with: + sdk: dev + - run: dart pub get + - run: dart --enable-experiment=native-assets run tool/generate.dart + - run: git diff --exit-code + + desktop-vm-test: + strategy: + fail-fast: false + matrix: + sdk: [dev] + os: [ubuntu-latest, ubuntu-24.04-arm, windows-11-arm, windows-latest, macos-latest] + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 + - uses: dart-lang/setup-dart@e51d8e571e22473a2ddebf0ef8a2123f0ab2c02c + with: + sdk: ${{ matrix.sdk }} + - run: dart --enable-experiment=native-assets test --reporter=failures-only --test-randomize-ordering-seed=random --platform vm diff --git a/pkgs/io_file/lib/src/libc.dart b/pkgs/io_file/lib/src/libc.dart deleted file mode 100644 index 9c1ca96f..00000000 --- a/pkgs/io_file/lib/src/libc.dart +++ /dev/null @@ -1,7 +0,0 @@ -import 'libc_bindings.g.dart' as libc; - -export 'constants.g.dart'; -export 'libc_bindings.g.dart' hide errno, seterrno; - -int get errno => libc.errno(); -set errno(int err) => libc.seterrno(err); diff --git a/pkgs/io_file/lib/src/vm_posix_file_system.dart b/pkgs/io_file/lib/src/vm_posix_file_system.dart index 48f9565d..692ea847 100644 --- a/pkgs/io_file/lib/src/vm_posix_file_system.dart +++ b/pkgs/io_file/lib/src/vm_posix_file_system.dart @@ -10,11 +10,11 @@ import 'dart:typed_data'; import 'package:ffi/ffi.dart' as ffi; import 'package:path/path.dart' as p; +import 'package:unix_api/unix_api.dart' as libc; import 'exceptions.dart'; import 'file_system.dart'; import 'internal_constants.dart'; -import 'libc.dart' as libc; /// The default `mode` to use with `open` calls that may create a file. const _defaultMode = 438; // => 0666 => rw-rw-rw- diff --git a/pkgs/io_file/pubspec.yaml b/pkgs/io_file/pubspec.yaml index 4752ebf0..c110a2f8 100644 --- a/pkgs/io_file/pubspec.yaml +++ b/pkgs/io_file/pubspec.yaml @@ -15,6 +15,8 @@ dependencies: meta: ^1.16.0 native_toolchain_c: ^0.16.0 path: ^1.9.1 + unix_api: + path: ../unix_api win32: ^5.14.0 dev_dependencies: diff --git a/pkgs/io_file/test/copy_file_test.dart b/pkgs/io_file/test/copy_file_test.dart index 79b7aa93..438ec04c 100644 --- a/pkgs/io_file/test/copy_file_test.dart +++ b/pkgs/io_file/test/copy_file_test.dart @@ -12,10 +12,10 @@ import 'package:ffi/ffi.dart'; import 'package:io_file/io_file.dart'; import 'package:io_file/posix_file_system.dart'; import 'package:io_file/src/internal_constants.dart' show blockSize; -import 'package:io_file/src/libc.dart' as libc; import 'package:io_file/src/vm_windows_file_system.dart'; import 'package:path/path.dart' as p; import 'package:test/test.dart'; +import 'package:unix_api/unix_api.dart' as libc; import 'package:win32/win32.dart' as win32; import 'errors.dart' as errors; diff --git a/pkgs/io_file/test/remove_directory_tree_test.dart b/pkgs/io_file/test/remove_directory_tree_test.dart index ba38f524..85ef2fc4 100644 --- a/pkgs/io_file/test/remove_directory_tree_test.dart +++ b/pkgs/io_file/test/remove_directory_tree_test.dart @@ -9,10 +9,11 @@ import 'dart:io' as io; import 'package:ffi/ffi.dart'; import 'package:io_file/io_file.dart'; -import 'package:io_file/src/libc.dart' as libc; import 'package:path/path.dart' as p; import 'package:test/test.dart'; +import 'package:unix_api/unix_api.dart' as libc; import 'package:win32/win32.dart' as win32; + import 'errors.dart' as errors; import 'test_utils.dart'; diff --git a/pkgs/unix_api/.gitattributes b/pkgs/unix_api/.gitattributes new file mode 100644 index 00000000..e57a4357 --- /dev/null +++ b/pkgs/unix_api/.gitattributes @@ -0,0 +1,6 @@ +# ffigen generated code +lib/src/constant_bindings.g.dart linguist-generated +lib/src/constants.g.dart linguist-generated +lib/src/libc_bindings.g.dart linguist-generated +src/constants.g.c linguist-generated +src/constants.g.h linguist-generated diff --git a/pkgs/unix_api/.gitignore b/pkgs/unix_api/.gitignore new file mode 100644 index 00000000..3cceda55 --- /dev/null +++ b/pkgs/unix_api/.gitignore @@ -0,0 +1,7 @@ +# https://dart.dev/guides/libraries/private-files +# Created by `dart pub` +.dart_tool/ + +# Avoid committing pubspec.lock for library packages; see +# https://dart.dev/guides/libraries/private-files#pubspeclock. +pubspec.lock diff --git a/pkgs/unix_api/CHANGELOG.md b/pkgs/unix_api/CHANGELOG.md new file mode 100644 index 00000000..effe43c8 --- /dev/null +++ b/pkgs/unix_api/CHANGELOG.md @@ -0,0 +1,3 @@ +## 1.0.0 + +- Initial version. diff --git a/pkgs/unix_api/README.md b/pkgs/unix_api/README.md new file mode 100644 index 00000000..afab16c1 --- /dev/null +++ b/pkgs/unix_api/README.md @@ -0,0 +1,32 @@ +This package provides **experimental** bindings to POSIX APIs e.g. `open`, `close`. + +## Why have another POSIX API implementation for Dart? + +| Package | Requires Toolchain | Fixed | iOS | macOS | Windows | Fake POSIX | Fake Windows | +| :--- | :---: | :---: | :---: | :---: | :----: | :--------: | :----------: | +| canonicalize path | | | | | | | | + + +Thare are two existing packages that provide POSIX API bindings for Dart: +1. [`package:posix`](https://pub.dev/packages/posix) +2. [`package:stdlibc`](https://pub.dev/packages/stdlibc) + + + + + + +## Status: Experimental + +**NOTE**: This package is currently experimental and published under the +[labs.dart.dev](https://dart.dev/dart-team-packages) pub publisher in order to +solicit feedback. + +For packages in the labs.dart.dev publisher we generally plan to either graduate +the package into a supported publisher (dart.dev, tools.dart.dev) after a period +of feedback and iteration, or discontinue the package. These packages have a +much higher expected rate of API and breaking changes. + +Your feedback is valuable and will help us evolve this package. For general +feedback, suggestions, and comments, please file an issue in the +[bug tracker](https://github.com/dart-lang/labs/issues). diff --git a/pkgs/unix_api/analysis_options.yaml b/pkgs/unix_api/analysis_options.yaml new file mode 100644 index 00000000..dee8927a --- /dev/null +++ b/pkgs/unix_api/analysis_options.yaml @@ -0,0 +1,30 @@ +# This file configures the static analysis results for your project (errors, +# warnings, and lints). +# +# This enables the 'recommended' set of lints from `package:lints`. +# This set helps identify many issues that may lead to problems when running +# or consuming Dart code, and enforces writing Dart using a single, idiomatic +# style and format. +# +# If you want a smaller set of lints you can change this to specify +# 'package:lints/core.yaml'. These are just the most critical lints +# (the recommended set includes the core lints). +# The core lints are also what is used by pub.dev for scoring packages. + +include: package:lints/recommended.yaml + +# Uncomment the following section to specify additional rules. + +# linter: +# rules: +# - camel_case_types + +# analyzer: +# exclude: +# - path/to/excluded/files/** + +# For more information about the core and recommended set of lints, see +# https://dart.dev/go/core-lints + +# For additional information about configuring this file, see +# https://dart.dev/guides/language/analysis-options diff --git a/pkgs/io_file/constants-ffigen.yaml b/pkgs/unix_api/constants-ffigen.yaml similarity index 93% rename from pkgs/io_file/constants-ffigen.yaml rename to pkgs/unix_api/constants-ffigen.yaml index 1ab76307..82efa618 100644 --- a/pkgs/io_file/constants-ffigen.yaml +++ b/pkgs/unix_api/constants-ffigen.yaml @@ -15,7 +15,7 @@ comments: style: any length: full ffi-native: - asset-id: package:io_file/libc_shim + asset-id: package:unix_api/libc_shim structs: rename: 'libc_shim_(.*)': '$1' diff --git a/pkgs/io_file/constants.json b/pkgs/unix_api/constants.json similarity index 100% rename from pkgs/io_file/constants.json rename to pkgs/unix_api/constants.json diff --git a/pkgs/unix_api/example/unix_api_example.dart b/pkgs/unix_api/example/unix_api_example.dart new file mode 100644 index 00000000..9531c899 --- /dev/null +++ b/pkgs/unix_api/example/unix_api_example.dart @@ -0,0 +1,6 @@ +import 'package:unix_api/unix_api.dart'; + +void main() { + var awesome = Awesome(); + print('awesome: ${awesome.isAwesome}'); +} diff --git a/pkgs/io_file/ffigen.yaml b/pkgs/unix_api/ffigen.yaml similarity index 93% rename from pkgs/io_file/ffigen.yaml rename to pkgs/unix_api/ffigen.yaml index 2d83623c..a6789d09 100644 --- a/pkgs/io_file/ffigen.yaml +++ b/pkgs/unix_api/ffigen.yaml @@ -17,7 +17,7 @@ comments: style: any length: full ffi-native: - asset-id: package:io_file/libc_shim + asset-id: package:unix_api/libc_shim structs: rename: 'libc_shim_(.*)': '$1' diff --git a/pkgs/io_file/hook/build.dart b/pkgs/unix_api/hook/build.dart similarity index 100% rename from pkgs/io_file/hook/build.dart rename to pkgs/unix_api/hook/build.dart diff --git a/pkgs/unix_api/lib/src/constant_bindings.g.dart b/pkgs/unix_api/lib/src/constant_bindings.g.dart new file mode 100644 index 00000000..3368cb9c --- /dev/null +++ b/pkgs/unix_api/lib/src/constant_bindings.g.dart @@ -0,0 +1,193 @@ +// AUTO GENERATED FILE, DO NOT EDIT. +// +// Generated by `package:ffigen`. +// ignore_for_file: type=lint +@ffi.DefaultAsset('package:unix_api/libc_shim') +library; + +import 'dart:ffi' as ffi; + +@ffi.Native(symbol: 'libc_shim_get_DT_BLK') +external int get_DT_BLK(); + +@ffi.Native(symbol: 'libc_shim_get_DT_CHR') +external int get_DT_CHR(); + +@ffi.Native(symbol: 'libc_shim_get_DT_DIR') +external int get_DT_DIR(); + +@ffi.Native(symbol: 'libc_shim_get_DT_FIFO') +external int get_DT_FIFO(); + +@ffi.Native(symbol: 'libc_shim_get_DT_LNK') +external int get_DT_LNK(); + +@ffi.Native(symbol: 'libc_shim_get_DT_REG') +external int get_DT_REG(); + +@ffi.Native(symbol: 'libc_shim_get_DT_SOCK') +external int get_DT_SOCK(); + +@ffi.Native(symbol: 'libc_shim_get_DT_UNKNOWN') +external int get_DT_UNKNOWN(); + +@ffi.Native(symbol: 'libc_shim_get_EACCES') +external int get_EACCES(); + +@ffi.Native(symbol: 'libc_shim_get_EEXIST') +external int get_EEXIST(); + +@ffi.Native(symbol: 'libc_shim_get_EINTR') +external int get_EINTR(); + +@ffi.Native(symbol: 'libc_shim_get_EMFILE') +external int get_EMFILE(); + +@ffi.Native(symbol: 'libc_shim_get_ENOENT') +external int get_ENOENT(); + +@ffi.Native(symbol: 'libc_shim_get_ENOSPC') +external int get_ENOSPC(); + +@ffi.Native(symbol: 'libc_shim_get_ENOTDIR') +external int get_ENOTDIR(); + +@ffi.Native(symbol: 'libc_shim_get_ENOTEMPTY') +external int get_ENOTEMPTY(); + +@ffi.Native(symbol: 'libc_shim_get_EPERM') +external int get_EPERM(); + +@ffi.Native(symbol: 'libc_shim_get_AT_FDCWD') +external int get_AT_FDCWD(); + +@ffi.Native(symbol: 'libc_shim_get_AT_REMOVEDIR') +external int get_AT_REMOVEDIR(); + +@ffi.Native(symbol: 'libc_shim_get_O_APPEND') +external int get_O_APPEND(); + +@ffi.Native(symbol: 'libc_shim_get_O_CLOEXEC') +external int get_O_CLOEXEC(); + +@ffi.Native(symbol: 'libc_shim_get_O_CREAT') +external int get_O_CREAT(); + +@ffi.Native(symbol: 'libc_shim_get_O_DIRECTORY') +external int get_O_DIRECTORY(); + +@ffi.Native(symbol: 'libc_shim_get_O_EXCL') +external int get_O_EXCL(); + +@ffi.Native(symbol: 'libc_shim_get_O_RDONLY') +external int get_O_RDONLY(); + +@ffi.Native(symbol: 'libc_shim_get_O_TRUNC') +external int get_O_TRUNC(); + +@ffi.Native(symbol: 'libc_shim_get_O_WRONLY') +external int get_O_WRONLY(); + +@ffi.Native(symbol: 'libc_shim_get_PATH_MAX') +external int get_PATH_MAX(); + +@ffi.Native(symbol: 'libc_shim_get_AT_SYMLINK_NOFOLLOW') +external int get_AT_SYMLINK_NOFOLLOW(); + +@ffi.Native(symbol: 'libc_shim_get_AT_RESOLVE_BENEATH') +external int get_AT_RESOLVE_BENEATH(); + +@ffi.Native(symbol: 'libc_shim_get_AT_EMPTY_PATH') +external int get_AT_EMPTY_PATH(); + +@ffi.Native(symbol: 'libc_shim_get_S_IEXEC') +external int get_S_IEXEC(); + +@ffi.Native(symbol: 'libc_shim_get_S_IFBLK') +external int get_S_IFBLK(); + +@ffi.Native(symbol: 'libc_shim_get_S_IFCHR') +external int get_S_IFCHR(); + +@ffi.Native(symbol: 'libc_shim_get_S_IFDIR') +external int get_S_IFDIR(); + +@ffi.Native(symbol: 'libc_shim_get_S_IFIFO') +external int get_S_IFIFO(); + +@ffi.Native(symbol: 'libc_shim_get_S_IFLNK') +external int get_S_IFLNK(); + +@ffi.Native(symbol: 'libc_shim_get_S_IFMT') +external int get_S_IFMT(); + +@ffi.Native(symbol: 'libc_shim_get_S_IFREG') +external int get_S_IFREG(); + +@ffi.Native(symbol: 'libc_shim_get_S_IFSOCK') +external int get_S_IFSOCK(); + +@ffi.Native(symbol: 'libc_shim_get_S_IFWHT') +external int get_S_IFWHT(); + +@ffi.Native(symbol: 'libc_shim_get_S_IREAD') +external int get_S_IREAD(); + +@ffi.Native(symbol: 'libc_shim_get_S_IRGRP') +external int get_S_IRGRP(); + +@ffi.Native(symbol: 'libc_shim_get_S_IROTH') +external int get_S_IROTH(); + +@ffi.Native(symbol: 'libc_shim_get_S_IRUSR') +external int get_S_IRUSR(); + +@ffi.Native(symbol: 'libc_shim_get_S_IRWXG') +external int get_S_IRWXG(); + +@ffi.Native(symbol: 'libc_shim_get_S_IRWXO') +external int get_S_IRWXO(); + +@ffi.Native(symbol: 'libc_shim_get_S_IRWXU') +external int get_S_IRWXU(); + +@ffi.Native(symbol: 'libc_shim_get_S_ISGID') +external int get_S_ISGID(); + +@ffi.Native(symbol: 'libc_shim_get_S_ISTXT') +external int get_S_ISTXT(); + +@ffi.Native(symbol: 'libc_shim_get_S_ISUID') +external int get_S_ISUID(); + +@ffi.Native(symbol: 'libc_shim_get_S_ISVTX') +external int get_S_ISVTX(); + +@ffi.Native(symbol: 'libc_shim_get_S_IWGRP') +external int get_S_IWGRP(); + +@ffi.Native(symbol: 'libc_shim_get_S_IWOTH') +external int get_S_IWOTH(); + +@ffi.Native(symbol: 'libc_shim_get_S_IWRITE') +external int get_S_IWRITE(); + +@ffi.Native(symbol: 'libc_shim_get_S_IWUSR') +external int get_S_IWUSR(); + +@ffi.Native(symbol: 'libc_shim_get_S_IXGRP') +external int get_S_IXGRP(); + +@ffi.Native(symbol: 'libc_shim_get_S_IXOTH') +external int get_S_IXOTH(); + +@ffi.Native(symbol: 'libc_shim_get_S_IXUSR') +external int get_S_IXUSR(); + +@ffi.Native(symbol: 'libc_shim_get_UF_APPEND') +external int get_UF_APPEND(); + +@ffi.Native(symbol: 'libc_shim_get_UF_HIDDEN') +external int get_UF_HIDDEN(); + +const int libc_shim_UNDEFINED = 5635263260456932693; diff --git a/pkgs/unix_api/lib/src/constants.g.dart b/pkgs/unix_api/lib/src/constants.g.dart new file mode 100644 index 00000000..3196b33c --- /dev/null +++ b/pkgs/unix_api/lib/src/constants.g.dart @@ -0,0 +1,555 @@ +// AUTO GENERATED FILE, DO NOT EDIT. +// Regenerate with `dart run tool/build_constants.dart`. + +// ignore_for_file: non_constant_identifier_names + +import 'constant_bindings.g.dart'; + +int get DT_BLK { + final v = get_DT_BLK(); + if (v == libc_shim_UNDEFINED) { + throw UnsupportedError('DT_BLK'); + } else { + return v; + } +} + +int get DT_CHR { + final v = get_DT_CHR(); + if (v == libc_shim_UNDEFINED) { + throw UnsupportedError('DT_CHR'); + } else { + return v; + } +} + +int get DT_DIR { + final v = get_DT_DIR(); + if (v == libc_shim_UNDEFINED) { + throw UnsupportedError('DT_DIR'); + } else { + return v; + } +} + +int get DT_FIFO { + final v = get_DT_FIFO(); + if (v == libc_shim_UNDEFINED) { + throw UnsupportedError('DT_FIFO'); + } else { + return v; + } +} + +int get DT_LNK { + final v = get_DT_LNK(); + if (v == libc_shim_UNDEFINED) { + throw UnsupportedError('DT_LNK'); + } else { + return v; + } +} + +int get DT_REG { + final v = get_DT_REG(); + if (v == libc_shim_UNDEFINED) { + throw UnsupportedError('DT_REG'); + } else { + return v; + } +} + +int get DT_SOCK { + final v = get_DT_SOCK(); + if (v == libc_shim_UNDEFINED) { + throw UnsupportedError('DT_SOCK'); + } else { + return v; + } +} + +int get DT_UNKNOWN { + final v = get_DT_UNKNOWN(); + if (v == libc_shim_UNDEFINED) { + throw UnsupportedError('DT_UNKNOWN'); + } else { + return v; + } +} + +int get EACCES { + final v = get_EACCES(); + if (v == libc_shim_UNDEFINED) { + throw UnsupportedError('EACCES'); + } else { + return v; + } +} + +int get EEXIST { + final v = get_EEXIST(); + if (v == libc_shim_UNDEFINED) { + throw UnsupportedError('EEXIST'); + } else { + return v; + } +} + +int get EINTR { + final v = get_EINTR(); + if (v == libc_shim_UNDEFINED) { + throw UnsupportedError('EINTR'); + } else { + return v; + } +} + +int get EMFILE { + final v = get_EMFILE(); + if (v == libc_shim_UNDEFINED) { + throw UnsupportedError('EMFILE'); + } else { + return v; + } +} + +int get ENOENT { + final v = get_ENOENT(); + if (v == libc_shim_UNDEFINED) { + throw UnsupportedError('ENOENT'); + } else { + return v; + } +} + +int get ENOSPC { + final v = get_ENOSPC(); + if (v == libc_shim_UNDEFINED) { + throw UnsupportedError('ENOSPC'); + } else { + return v; + } +} + +int get ENOTDIR { + final v = get_ENOTDIR(); + if (v == libc_shim_UNDEFINED) { + throw UnsupportedError('ENOTDIR'); + } else { + return v; + } +} + +int get ENOTEMPTY { + final v = get_ENOTEMPTY(); + if (v == libc_shim_UNDEFINED) { + throw UnsupportedError('ENOTEMPTY'); + } else { + return v; + } +} + +int get EPERM { + final v = get_EPERM(); + if (v == libc_shim_UNDEFINED) { + throw UnsupportedError('EPERM'); + } else { + return v; + } +} + +int get AT_FDCWD { + final v = get_AT_FDCWD(); + if (v == libc_shim_UNDEFINED) { + throw UnsupportedError('AT_FDCWD'); + } else { + return v; + } +} + +int get AT_REMOVEDIR { + final v = get_AT_REMOVEDIR(); + if (v == libc_shim_UNDEFINED) { + throw UnsupportedError('AT_REMOVEDIR'); + } else { + return v; + } +} + +int get O_APPEND { + final v = get_O_APPEND(); + if (v == libc_shim_UNDEFINED) { + throw UnsupportedError('O_APPEND'); + } else { + return v; + } +} + +int get O_CLOEXEC { + final v = get_O_CLOEXEC(); + if (v == libc_shim_UNDEFINED) { + throw UnsupportedError('O_CLOEXEC'); + } else { + return v; + } +} + +int get O_CREAT { + final v = get_O_CREAT(); + if (v == libc_shim_UNDEFINED) { + throw UnsupportedError('O_CREAT'); + } else { + return v; + } +} + +int get O_DIRECTORY { + final v = get_O_DIRECTORY(); + if (v == libc_shim_UNDEFINED) { + throw UnsupportedError('O_DIRECTORY'); + } else { + return v; + } +} + +int get O_EXCL { + final v = get_O_EXCL(); + if (v == libc_shim_UNDEFINED) { + throw UnsupportedError('O_EXCL'); + } else { + return v; + } +} + +int get O_RDONLY { + final v = get_O_RDONLY(); + if (v == libc_shim_UNDEFINED) { + throw UnsupportedError('O_RDONLY'); + } else { + return v; + } +} + +int get O_TRUNC { + final v = get_O_TRUNC(); + if (v == libc_shim_UNDEFINED) { + throw UnsupportedError('O_TRUNC'); + } else { + return v; + } +} + +int get O_WRONLY { + final v = get_O_WRONLY(); + if (v == libc_shim_UNDEFINED) { + throw UnsupportedError('O_WRONLY'); + } else { + return v; + } +} + +int get PATH_MAX { + final v = get_PATH_MAX(); + if (v == libc_shim_UNDEFINED) { + throw UnsupportedError('PATH_MAX'); + } else { + return v; + } +} + +int get AT_SYMLINK_NOFOLLOW { + final v = get_AT_SYMLINK_NOFOLLOW(); + if (v == libc_shim_UNDEFINED) { + throw UnsupportedError('AT_SYMLINK_NOFOLLOW'); + } else { + return v; + } +} + +int get AT_RESOLVE_BENEATH { + final v = get_AT_RESOLVE_BENEATH(); + if (v == libc_shim_UNDEFINED) { + throw UnsupportedError('AT_RESOLVE_BENEATH'); + } else { + return v; + } +} + +int get AT_EMPTY_PATH { + final v = get_AT_EMPTY_PATH(); + if (v == libc_shim_UNDEFINED) { + throw UnsupportedError('AT_EMPTY_PATH'); + } else { + return v; + } +} + +int get S_IEXEC { + final v = get_S_IEXEC(); + if (v == libc_shim_UNDEFINED) { + throw UnsupportedError('S_IEXEC'); + } else { + return v; + } +} + +int get S_IFBLK { + final v = get_S_IFBLK(); + if (v == libc_shim_UNDEFINED) { + throw UnsupportedError('S_IFBLK'); + } else { + return v; + } +} + +int get S_IFCHR { + final v = get_S_IFCHR(); + if (v == libc_shim_UNDEFINED) { + throw UnsupportedError('S_IFCHR'); + } else { + return v; + } +} + +int get S_IFDIR { + final v = get_S_IFDIR(); + if (v == libc_shim_UNDEFINED) { + throw UnsupportedError('S_IFDIR'); + } else { + return v; + } +} + +int get S_IFIFO { + final v = get_S_IFIFO(); + if (v == libc_shim_UNDEFINED) { + throw UnsupportedError('S_IFIFO'); + } else { + return v; + } +} + +int get S_IFLNK { + final v = get_S_IFLNK(); + if (v == libc_shim_UNDEFINED) { + throw UnsupportedError('S_IFLNK'); + } else { + return v; + } +} + +int get S_IFMT { + final v = get_S_IFMT(); + if (v == libc_shim_UNDEFINED) { + throw UnsupportedError('S_IFMT'); + } else { + return v; + } +} + +int get S_IFREG { + final v = get_S_IFREG(); + if (v == libc_shim_UNDEFINED) { + throw UnsupportedError('S_IFREG'); + } else { + return v; + } +} + +int get S_IFSOCK { + final v = get_S_IFSOCK(); + if (v == libc_shim_UNDEFINED) { + throw UnsupportedError('S_IFSOCK'); + } else { + return v; + } +} + +int get S_IFWHT { + final v = get_S_IFWHT(); + if (v == libc_shim_UNDEFINED) { + throw UnsupportedError('S_IFWHT'); + } else { + return v; + } +} + +int get S_IREAD { + final v = get_S_IREAD(); + if (v == libc_shim_UNDEFINED) { + throw UnsupportedError('S_IREAD'); + } else { + return v; + } +} + +int get S_IRGRP { + final v = get_S_IRGRP(); + if (v == libc_shim_UNDEFINED) { + throw UnsupportedError('S_IRGRP'); + } else { + return v; + } +} + +int get S_IROTH { + final v = get_S_IROTH(); + if (v == libc_shim_UNDEFINED) { + throw UnsupportedError('S_IROTH'); + } else { + return v; + } +} + +int get S_IRUSR { + final v = get_S_IRUSR(); + if (v == libc_shim_UNDEFINED) { + throw UnsupportedError('S_IRUSR'); + } else { + return v; + } +} + +int get S_IRWXG { + final v = get_S_IRWXG(); + if (v == libc_shim_UNDEFINED) { + throw UnsupportedError('S_IRWXG'); + } else { + return v; + } +} + +int get S_IRWXO { + final v = get_S_IRWXO(); + if (v == libc_shim_UNDEFINED) { + throw UnsupportedError('S_IRWXO'); + } else { + return v; + } +} + +int get S_IRWXU { + final v = get_S_IRWXU(); + if (v == libc_shim_UNDEFINED) { + throw UnsupportedError('S_IRWXU'); + } else { + return v; + } +} + +int get S_ISGID { + final v = get_S_ISGID(); + if (v == libc_shim_UNDEFINED) { + throw UnsupportedError('S_ISGID'); + } else { + return v; + } +} + +int get S_ISTXT { + final v = get_S_ISTXT(); + if (v == libc_shim_UNDEFINED) { + throw UnsupportedError('S_ISTXT'); + } else { + return v; + } +} + +int get S_ISUID { + final v = get_S_ISUID(); + if (v == libc_shim_UNDEFINED) { + throw UnsupportedError('S_ISUID'); + } else { + return v; + } +} + +int get S_ISVTX { + final v = get_S_ISVTX(); + if (v == libc_shim_UNDEFINED) { + throw UnsupportedError('S_ISVTX'); + } else { + return v; + } +} + +int get S_IWGRP { + final v = get_S_IWGRP(); + if (v == libc_shim_UNDEFINED) { + throw UnsupportedError('S_IWGRP'); + } else { + return v; + } +} + +int get S_IWOTH { + final v = get_S_IWOTH(); + if (v == libc_shim_UNDEFINED) { + throw UnsupportedError('S_IWOTH'); + } else { + return v; + } +} + +int get S_IWRITE { + final v = get_S_IWRITE(); + if (v == libc_shim_UNDEFINED) { + throw UnsupportedError('S_IWRITE'); + } else { + return v; + } +} + +int get S_IWUSR { + final v = get_S_IWUSR(); + if (v == libc_shim_UNDEFINED) { + throw UnsupportedError('S_IWUSR'); + } else { + return v; + } +} + +int get S_IXGRP { + final v = get_S_IXGRP(); + if (v == libc_shim_UNDEFINED) { + throw UnsupportedError('S_IXGRP'); + } else { + return v; + } +} + +int get S_IXOTH { + final v = get_S_IXOTH(); + if (v == libc_shim_UNDEFINED) { + throw UnsupportedError('S_IXOTH'); + } else { + return v; + } +} + +int get S_IXUSR { + final v = get_S_IXUSR(); + if (v == libc_shim_UNDEFINED) { + throw UnsupportedError('S_IXUSR'); + } else { + return v; + } +} + +int get UF_APPEND { + final v = get_UF_APPEND(); + if (v == libc_shim_UNDEFINED) { + throw UnsupportedError('UF_APPEND'); + } else { + return v; + } +} + +int get UF_HIDDEN { + final v = get_UF_HIDDEN(); + if (v == libc_shim_UNDEFINED) { + throw UnsupportedError('UF_HIDDEN'); + } else { + return v; + } +} diff --git a/pkgs/unix_api/lib/src/libc_bindings.g.dart b/pkgs/unix_api/lib/src/libc_bindings.g.dart new file mode 100644 index 00000000..8b2c165a --- /dev/null +++ b/pkgs/unix_api/lib/src/libc_bindings.g.dart @@ -0,0 +1,195 @@ +// ignore_for_file: type=lint, unused_field + +// AUTO GENERATED FILE, DO NOT EDIT. +// +// Generated by `package:ffigen`. +@ffi.DefaultAsset('package:unix_api/libc_shim') +library; + +import 'dart:ffi' as ffi; + +/// Returns `libc_shim_dirent.d_name`. +/// +/// TODO(brianquinlan): Remove `libc_shim_d_name_ptr` when there is a fix for: +/// https://github.com/dart-lang/sdk/issues/41237 +@ffi.Native Function(ffi.Pointer)>( + symbol: 'libc_shim_d_name_ptr', +) +external ffi.Pointer d_name_ptr(ffi.Pointer d); + +@ffi.Native)>(symbol: 'libc_shim_closedir') +external int closedir(ffi.Pointer d); + +@ffi.Native Function(ffi.Int)>(symbol: 'libc_shim_fdopendir') +external ffi.Pointer fdopendir(int fd); + +@ffi.Native Function(ffi.Pointer)>( + symbol: 'libc_shim_opendir', +) +external ffi.Pointer opendir(ffi.Pointer path); + +@ffi.Native Function(ffi.Pointer)>( + symbol: 'libc_shim_readdir', +) +external ffi.Pointer readdir(ffi.Pointer d); + +/// +@ffi.Native(symbol: 'libc_shim_seterrno') +external void seterrno(int err); + +@ffi.Native(symbol: 'libc_shim_errno') +external int errno(); + +/// +@ffi.Native, ffi.Int, ffi.Int)>( + symbol: 'libc_shim_open', +) +external int open(ffi.Pointer pathname, int flags, int mode); + +@ffi.Native, ffi.Int, ffi.Int)>( + symbol: 'libc_shim_openat', +) +external int openat( + int fd, + ffi.Pointer pathname, + int flags, + int mode, +); + +@ffi.Native, ffi.Int)>( + symbol: 'libc_shim_chmod', +) +external int chmod(ffi.Pointer path, int mode); + +@ffi.Native, ffi.Int)>( + symbol: 'libc_shim_mkdir', +) +external int mkdir(ffi.Pointer pathname, int mode); + +@ffi.Native, ffi.Pointer)>( + symbol: 'libc_shim_stat', +) +external int stat(ffi.Pointer path, ffi.Pointer buf); + +@ffi.Native, ffi.Pointer)>( + symbol: 'libc_shim_lstat', +) +external int lstat(ffi.Pointer path, ffi.Pointer buf); + +@ffi.Native)>( + symbol: 'libc_shim_fstat', +) +external int fstat(int fd, ffi.Pointer buf); + +@ffi.Native< + ffi.Int Function(ffi.Int, ffi.Pointer, ffi.Pointer, ffi.Int) +>(symbol: 'libc_shim_fstatat') +external int fstatat( + int fd, + ffi.Pointer path, + ffi.Pointer buf, + int flag, +); + +/// +@ffi.Native, ffi.Pointer)>( + symbol: 'libc_shim_rename', +) +external int rename(ffi.Pointer old, ffi.Pointer newy); + +/// +@ffi.Native Function(ffi.Pointer)>( + symbol: 'libc_shim_getenv', +) +external ffi.Pointer getenv(ffi.Pointer name); + +@ffi.Native Function(ffi.Pointer)>( + symbol: 'libc_shim_mkdtemp', +) +external ffi.Pointer mkdtemp(ffi.Pointer template); + +/// +@ffi.Native Function(ffi.Int)>( + symbol: 'libc_shim_strerror', +) +external ffi.Pointer strerror(int errnum); + +/// +@ffi.Native)>(symbol: 'libc_shim_chdir') +external int chdir(ffi.Pointer path); + +@ffi.Native(symbol: 'libc_shim_close') +external int close(int fd); + +@ffi.Native Function(ffi.Pointer, ffi.Int64)>( + symbol: 'libc_shim_getcwd', +) +external ffi.Pointer getcwd(ffi.Pointer buf, int size); + +@ffi.Native, ffi.Int)>( + symbol: 'libc_shim_unlinkat', +) +external int unlinkat(int dirfd, ffi.Pointer pathname, int flags); + +/// +final class dirent extends ffi.Struct { + /// POSIX + @ffi.Int64() + external int d_ino; + + /// Linux, macOS/iOS + @ffi.Uint8() + external int d_type; + + /// POSIX; __DARWIN_MAXNAMLEN = 1024 + @ffi.Array.multi([1025]) + external ffi.Array d_name; +} + +final class DIR extends ffi.Struct { + external dirent libc_shim_dirent; + + external ffi.Pointer _dir; +} + +/// +final class timespec extends ffi.Struct { + @ffi.Int64() + external int tv_sec; + + @ffi.Int64() + external int tv_nsec; +} + +final class Stat extends ffi.Struct { + @ffi.Int64() + external int st_dev; + + @ffi.Int64() + external int st_ino; + + @ffi.Int64() + external int st_mode; + + @ffi.Int64() + external int st_nlink; + + @ffi.Int64() + external int std_uid; + + @ffi.Int64() + external int st_size; + + external timespec st_atim; + + external timespec st_mtim; + + external timespec st_ctim; + + /// Only valid on macOS/iOS + external timespec st_btime; + + /// Only valid on macOS/iOS + @ffi.Int64() + external int st_flags; +} diff --git a/pkgs/unix_api/lib/unix_api.dart b/pkgs/unix_api/lib/unix_api.dart new file mode 100644 index 00000000..18e670e3 --- /dev/null +++ b/pkgs/unix_api/lib/unix_api.dart @@ -0,0 +1,7 @@ +import 'src/libc_bindings.g.dart' as libc; + +export 'src/constants.g.dart'; +export 'src/libc_bindings.g.dart' hide errno, seterrno; + +int get errno => libc.errno(); +set errno(int err) => libc.seterrno(err); diff --git a/pkgs/unix_api/pubspec.yaml b/pkgs/unix_api/pubspec.yaml new file mode 100644 index 00000000..ba82a3e7 --- /dev/null +++ b/pkgs/unix_api/pubspec.yaml @@ -0,0 +1,21 @@ +name: unix_api +description: A starting point for Dart libraries or applications. +version: 0.1.0-wip +repository: https://github.com/dart-lang/labs/tree/main/pkgs/unix_api +publish_to: none + +environment: + sdk: ^3.9.2 + +dependencies: + code_assets: ^0.19.0 + ffi: ^2.1.4 + native_toolchain_c: ^0.16.0 + +dev_dependencies: + benchmark_harness: ^2.3.1 + dart_flutter_team_lints: ^3.4.0 + errno: ^1.4.1 + ffigen: ^19.0.0 + path: ^1.9.1 + test: ^1.24.0 diff --git a/pkgs/io_file/src/constants.g.c b/pkgs/unix_api/src/constants.g.c similarity index 100% rename from pkgs/io_file/src/constants.g.c rename to pkgs/unix_api/src/constants.g.c diff --git a/pkgs/io_file/src/constants.g.h b/pkgs/unix_api/src/constants.g.h similarity index 100% rename from pkgs/io_file/src/constants.g.h rename to pkgs/unix_api/src/constants.g.h diff --git a/pkgs/io_file/src/libc_shim.c b/pkgs/unix_api/src/libc_shim.c similarity index 100% rename from pkgs/io_file/src/libc_shim.c rename to pkgs/unix_api/src/libc_shim.c diff --git a/pkgs/io_file/src/libc_shim.h b/pkgs/unix_api/src/libc_shim.h similarity index 100% rename from pkgs/io_file/src/libc_shim.h rename to pkgs/unix_api/src/libc_shim.h diff --git a/pkgs/unix_api/test/dirent_test.dart b/pkgs/unix_api/test/dirent_test.dart new file mode 100644 index 00000000..cc286b30 --- /dev/null +++ b/pkgs/unix_api/test/dirent_test.dart @@ -0,0 +1,43 @@ +import 'dart:ffi'; +import 'dart:io'; + +import 'package:ffi/ffi.dart' as ffi; +import 'package:unix_api/unix_api.dart'; +import 'package:test/test.dart'; +import 'package:path/path.dart' as p; + +void main() { + group('dirent', () { + late Directory tmp; + + setUp(() { + tmp = Directory.systemTemp.createTempSync('dirent'); + }); + + tearDown(() { + tmp.deleteSync(recursive: true); + }); + + test('test', () { + File(p.join(tmp.path, 'test1')).createSync(); + Directory(p.join(tmp.path, 'dir1')).createSync(); + + ffi.using((arena) { + final dir = opendir(tmp.path.toNativeUtf8(allocator: arena).cast()); + expect(dir, isNot(nullptr)); + + final paths = []; + for ( + var dirent = readdir(dir); + dirent != nullptr; + dirent = readdir(dir) + ) { + paths.add(d_name_ptr(dirent).cast().toDartString()); + } + + expect(paths, containsAll(['test1', 'dir1'])); + expect(closedir(dir), 0); + }); + }); + }); +} diff --git a/pkgs/io_file/tool/build_constants.dart b/pkgs/unix_api/tool/build_constants.dart similarity index 100% rename from pkgs/io_file/tool/build_constants.dart rename to pkgs/unix_api/tool/build_constants.dart diff --git a/pkgs/io_file/tool/generate.dart b/pkgs/unix_api/tool/generate.dart similarity index 100% rename from pkgs/io_file/tool/generate.dart rename to pkgs/unix_api/tool/generate.dart From 1ab43b2d0480378cee9544a5af9664e9e613b2df Mon Sep 17 00:00:00 2001 From: Brian Quinlan Date: Fri, 5 Sep 2025 12:46:28 -0700 Subject: [PATCH 02/14] Fixes --- .github/workflows/unix_api.yml | 2 +- pkgs/io_file/.gitattributes | 6 ------ 2 files changed, 1 insertion(+), 7 deletions(-) delete mode 100644 pkgs/io_file/.gitattributes diff --git a/.github/workflows/unix_api.yml b/.github/workflows/unix_api.yml index 44234921..8e98cdc0 100644 --- a/.github/workflows/unix_api.yml +++ b/.github/workflows/unix_api.yml @@ -1,4 +1,4 @@ -name: package:io_file +name: package:unix_api permissions: read-all diff --git a/pkgs/io_file/.gitattributes b/pkgs/io_file/.gitattributes deleted file mode 100644 index e57a4357..00000000 --- a/pkgs/io_file/.gitattributes +++ /dev/null @@ -1,6 +0,0 @@ -# ffigen generated code -lib/src/constant_bindings.g.dart linguist-generated -lib/src/constants.g.dart linguist-generated -lib/src/libc_bindings.g.dart linguist-generated -src/constants.g.c linguist-generated -src/constants.g.h linguist-generated From ba3c7a65ffaf6898fb67330ff2afdd6aa0d6112d Mon Sep 17 00:00:00 2001 From: Brian Quinlan Date: Fri, 5 Sep 2025 12:49:36 -0700 Subject: [PATCH 03/14] Update io_file.yml --- .github/workflows/io_file.yml | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/.github/workflows/io_file.yml b/.github/workflows/io_file.yml index 9fe0639e..8302cc35 100644 --- a/.github/workflows/io_file.yml +++ b/.github/workflows/io_file.yml @@ -33,23 +33,6 @@ jobs: - run: dart analyze --fatal-infos - run: dart format --output=none --set-exit-if-changed . - check-generation: - # Verify that the generated code is up-to-date. Every platform/arch should - # generate the same API bindings. - strategy: - fail-fast: false - matrix: - os: [ubuntu-latest, ubuntu-24.04-arm, macos-latest] - runs-on: ${{ matrix.os }} - steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 - - uses: dart-lang/setup-dart@e51d8e571e22473a2ddebf0ef8a2123f0ab2c02c - with: - sdk: dev - - run: dart pub get - - run: dart --enable-experiment=native-assets run tool/generate.dart - - run: git diff --exit-code - desktop-vm-test: strategy: fail-fast: false From 9b7aa90cc819b4abc850c92d9a1516c2b30a0afe Mon Sep 17 00:00:00 2001 From: Brian Quinlan Date: Fri, 5 Sep 2025 13:01:36 -0700 Subject: [PATCH 04/14] More cleanup --- pkgs/io_file/pubspec.yaml | 1 - pkgs/unix_api/example/unix_api_example.dart | 3 +-- pkgs/unix_api/hook/build.dart | 9 ++++----- pkgs/unix_api/lib/src/libc_bindings.g.dart | 6 ++++++ pkgs/unix_api/pubspec.yaml | 1 + pkgs/unix_api/src/libc_shim.c | 3 +++ pkgs/unix_api/src/libc_shim.h | 2 ++ 7 files changed, 17 insertions(+), 8 deletions(-) diff --git a/pkgs/io_file/pubspec.yaml b/pkgs/io_file/pubspec.yaml index c110a2f8..b1d5c664 100644 --- a/pkgs/io_file/pubspec.yaml +++ b/pkgs/io_file/pubspec.yaml @@ -11,7 +11,6 @@ dependencies: code_assets: ^0.19.0 ffi: ^2.1.4 hooks: ^0.19.0 - logging: ^1.3.0 meta: ^1.16.0 native_toolchain_c: ^0.16.0 path: ^1.9.1 diff --git a/pkgs/unix_api/example/unix_api_example.dart b/pkgs/unix_api/example/unix_api_example.dart index 9531c899..d0caffb9 100644 --- a/pkgs/unix_api/example/unix_api_example.dart +++ b/pkgs/unix_api/example/unix_api_example.dart @@ -1,6 +1,5 @@ import 'package:unix_api/unix_api.dart'; void main() { - var awesome = Awesome(); - print('awesome: ${awesome.isAwesome}'); + print('I am ${getpid()} and my parent is ${getppid()}'); } diff --git a/pkgs/unix_api/hook/build.dart b/pkgs/unix_api/hook/build.dart index ca386598..c190cba1 100644 --- a/pkgs/unix_api/hook/build.dart +++ b/pkgs/unix_api/hook/build.dart @@ -10,7 +10,7 @@ import 'package:native_toolchain_c/native_toolchain_c.dart'; void main(List args) async { await build(args, (input, output) async { final packageName = input.packageName; - if (input.config.code.targetOS == OS.windows) return; + // XXX if (input.config.code.targetOS == OS.windows) return; await CBuilder.library( name: packageName, assetName: 'libc_shim', @@ -19,10 +19,9 @@ void main(List args) async { ).run( input: input, output: output, - logger: - Logger('') - ..level = Level.ALL - ..onRecord.listen((record) => print(record.message)), + logger: Logger('') + ..level = Level.ALL + ..onRecord.listen((record) => print(record.message)), ); }); } diff --git a/pkgs/unix_api/lib/src/libc_bindings.g.dart b/pkgs/unix_api/lib/src/libc_bindings.g.dart index 8b2c165a..98085bd8 100644 --- a/pkgs/unix_api/lib/src/libc_bindings.g.dart +++ b/pkgs/unix_api/lib/src/libc_bindings.g.dart @@ -126,6 +126,12 @@ external int close(int fd); ) external ffi.Pointer getcwd(ffi.Pointer buf, int size); +@ffi.Native(symbol: 'libc_shim_getpid') +external int getpid(); + +@ffi.Native(symbol: 'libc_shim_getppid') +external int getppid(); + @ffi.Native, ffi.Int)>( symbol: 'libc_shim_unlinkat', ) diff --git a/pkgs/unix_api/pubspec.yaml b/pkgs/unix_api/pubspec.yaml index ba82a3e7..4905837d 100644 --- a/pkgs/unix_api/pubspec.yaml +++ b/pkgs/unix_api/pubspec.yaml @@ -10,6 +10,7 @@ environment: dependencies: code_assets: ^0.19.0 ffi: ^2.1.4 + logging: ^1.3.0 native_toolchain_c: ^0.16.0 dev_dependencies: diff --git a/pkgs/unix_api/src/libc_shim.c b/pkgs/unix_api/src/libc_shim.c index afd28df7..bb2c1c64 100644 --- a/pkgs/unix_api/src/libc_shim.c +++ b/pkgs/unix_api/src/libc_shim.c @@ -181,6 +181,9 @@ int libc_shim_close(int fd) { return close(fd); } char *libc_shim_getcwd(char *buf, int64_t size) { return getcwd(buf, size); } +long libc_shim_getpid() { return getpid(); } +long libc_shim_getppid() { return getppid(); } + int libc_shim_unlinkat(int dirfd, const char *pathname, int flags) { return unlinkat(dirfd, pathname, flags); } diff --git a/pkgs/unix_api/src/libc_shim.h b/pkgs/unix_api/src/libc_shim.h index 138c661b..ee75105d 100644 --- a/pkgs/unix_api/src/libc_shim.h +++ b/pkgs/unix_api/src/libc_shim.h @@ -121,5 +121,7 @@ LIBC_SHIM_EXPORT char *libc_shim_strerror(int errnum); LIBC_SHIM_EXPORT int libc_shim_chdir(const char *path); LIBC_SHIM_EXPORT int libc_shim_close(int fd); LIBC_SHIM_EXPORT char *libc_shim_getcwd(char *buf, int64_t size); +LIBC_SHIM_EXPORT long libc_shim_getpid(); +LIBC_SHIM_EXPORT long libc_shim_getppid(); LIBC_SHIM_EXPORT int libc_shim_unlinkat(int dirfd, const char *pathname, int flags); From c2478fb2af0a566c1b25cd422649db5c1fdbcbad Mon Sep 17 00:00:00 2001 From: Brian Quinlan Date: Fri, 5 Sep 2025 13:08:46 -0700 Subject: [PATCH 05/14] f --- pkgs/unix_api/hook/build.dart | 4 ++-- pkgs/unix_api/pubspec.yaml | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/pkgs/unix_api/hook/build.dart b/pkgs/unix_api/hook/build.dart index c190cba1..6944f6c6 100644 --- a/pkgs/unix_api/hook/build.dart +++ b/pkgs/unix_api/hook/build.dart @@ -10,12 +10,12 @@ import 'package:native_toolchain_c/native_toolchain_c.dart'; void main(List args) async { await build(args, (input, output) async { final packageName = input.packageName; - // XXX if (input.config.code.targetOS == OS.windows) return; + // if (input.config.code.targetOS == OS.windows) return; await CBuilder.library( name: packageName, assetName: 'libc_shim', sources: ['src/libc_shim.c', 'src/constants.g.c'], - flags: ['-Weverything'], + // flags: ['-Weverything'], ).run( input: input, output: output, diff --git a/pkgs/unix_api/pubspec.yaml b/pkgs/unix_api/pubspec.yaml index 4905837d..01623682 100644 --- a/pkgs/unix_api/pubspec.yaml +++ b/pkgs/unix_api/pubspec.yaml @@ -10,6 +10,7 @@ environment: dependencies: code_assets: ^0.19.0 ffi: ^2.1.4 + hooks: ^0.19.5 logging: ^1.3.0 native_toolchain_c: ^0.16.0 From 2dbe248de040c71ed6a6c96cfcc91daba9df0b55 Mon Sep 17 00:00:00 2001 From: Brian Quinlan Date: Fri, 5 Sep 2025 13:12:14 -0700 Subject: [PATCH 06/14] Update build.dart --- pkgs/unix_api/hook/build.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/unix_api/hook/build.dart b/pkgs/unix_api/hook/build.dart index 6944f6c6..534cdba3 100644 --- a/pkgs/unix_api/hook/build.dart +++ b/pkgs/unix_api/hook/build.dart @@ -10,12 +10,12 @@ import 'package:native_toolchain_c/native_toolchain_c.dart'; void main(List args) async { await build(args, (input, output) async { final packageName = input.packageName; - // if (input.config.code.targetOS == OS.windows) return; + if (input.config.code.targetOS == OS.windows) return; await CBuilder.library( name: packageName, assetName: 'libc_shim', sources: ['src/libc_shim.c', 'src/constants.g.c'], - // flags: ['-Weverything'], + flags: ['-Weverything'], ).run( input: input, output: output, From ad215271cc31467d88c205df6c7fcd1f5ba81968 Mon Sep 17 00:00:00 2001 From: Brian Quinlan Date: Fri, 5 Sep 2025 13:19:41 -0700 Subject: [PATCH 07/14] Update unix_api.yml --- .github/workflows/unix_api.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/unix_api.yml b/.github/workflows/unix_api.yml index 8e98cdc0..970e5213 100644 --- a/.github/workflows/unix_api.yml +++ b/.github/workflows/unix_api.yml @@ -39,7 +39,7 @@ jobs: strategy: fail-fast: false matrix: - os: [ubuntu-latest, ubuntu-24.04-arm, macos-latest, windows-latest] + os: [macos-latest, ubuntu-latest, ubuntu-24.04-arm] runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 @@ -55,7 +55,7 @@ jobs: fail-fast: false matrix: sdk: [dev] - os: [ubuntu-latest, ubuntu-24.04-arm, windows-11-arm, windows-latest, macos-latest] + os: [macos-latest, ubuntu-latest, ubuntu-24.04-arm] runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 From 403068292204c7b9ca80d896cbbfeec433d9969a Mon Sep 17 00:00:00 2001 From: Brian Quinlan Date: Fri, 5 Sep 2025 15:43:40 -0700 Subject: [PATCH 08/14] Updates --- pkgs/unix_api/README.md | 59 +++++++++++++++++++++++++++++++++----- pkgs/unix_api/pubspec.yaml | 2 +- 2 files changed, 53 insertions(+), 8 deletions(-) diff --git a/pkgs/unix_api/README.md b/pkgs/unix_api/README.md index afab16c1..f1680e67 100644 --- a/pkgs/unix_api/README.md +++ b/pkgs/unix_api/README.md @@ -1,19 +1,64 @@ -This package provides **experimental** bindings to POSIX APIs e.g. `open`, `close`. +This package provides **experimental** bindings to POSIX APIs e.g. `open`, +`close`. ## Why have another POSIX API implementation for Dart? -| Package | Requires Toolchain | Fixed | iOS | macOS | Windows | Fake POSIX | Fake Windows | -| :--- | :---: | :---: | :---: | :---: | :----: | :--------: | :----------: | -| canonicalize path | | | | | | | | - - Thare are two existing packages that provide POSIX API bindings for Dart: 1. [`package:posix`](https://pub.dev/packages/posix) 2. [`package:stdlibc`](https://pub.dev/packages/stdlibc) - +`package:unix_api` requires a native tool chain and has a small amount of +native code that cannot be tree shaken away. In exchange, it works on all +POSIX platforms that Dart supports. + +| Package | Required Tools | Supported Platforms | Fix Disk Usage | +| :--- | :-------------- | :------------------------------------ | :------------ | +| `posix` | Dart | iOS (arm64), Linux (x64), macOS (arm64) | 0 KiB | +| `stdlibc` | Dart | iOS (arm64), Linux (x64), macOS (arm64) | 0 KiB | +| `unix_api` | Dart, C compiler | Android (x64, arm32, arm64), iOS (arm64), Linux (x64, arm64), macOS (x64, arm64) | ~60 KiB | + +## Design + +The POSIX API is a defined in terms of source, not object compatibility. + +For example, glibc defines `stat` as: + +`#define stat(fname, buf) __xstat (_STAT_VER, fname, buf)` + +So running `ffigen` on `sys/stat.h` will not produce an entry for `stat`. + +libc may also reorder `struct` fields across architectures, add extra +fields, etc. For example, the glibc definition of `struct stat` starts +with: + +```c +struct stat +{ + __dev_t st_dev; /* Device. */ +#ifndef __x86_64__ + unsigned short int __pad1; +#endif +#if defined __x86_64__ || !defined __USE_FILE_OFFSET64 + __ino_t st_ino; /* File serial number. */ +#else +``` + + +`package:unix_api` works around this problem by defining a native (C) function +for every POSIX function. The native function just calls the corresponding +POSIX function. For example: + +```c +int libc_shim_rename(const char *old, const char *newy) { + return rename(old, newy); +} +``` +This allows the platforms C compiler to deal with macro expansions, +platform-specific struct layout, etc. +Then `package:unix_api` uses `package:ffigen` to provide Dart bindings to +these functions. ## Status: Experimental diff --git a/pkgs/unix_api/pubspec.yaml b/pkgs/unix_api/pubspec.yaml index 01623682..4e3a79a9 100644 --- a/pkgs/unix_api/pubspec.yaml +++ b/pkgs/unix_api/pubspec.yaml @@ -1,5 +1,5 @@ name: unix_api -description: A starting point for Dart libraries or applications. +description: Access to POSIX APIs. version: 0.1.0-wip repository: https://github.com/dart-lang/labs/tree/main/pkgs/unix_api publish_to: none From 3ce6ab2ee3ee9c8a9e7f8623a71cf1b173560b74 Mon Sep 17 00:00:00 2001 From: Brian Quinlan Date: Fri, 5 Sep 2025 17:29:10 -0700 Subject: [PATCH 09/14] Fix version --- pkgs/unix_api/CHANGELOG.md | 2 +- pkgs/unix_api/analysis_options.yaml | 30 ----------------------------- 2 files changed, 1 insertion(+), 31 deletions(-) delete mode 100644 pkgs/unix_api/analysis_options.yaml diff --git a/pkgs/unix_api/CHANGELOG.md b/pkgs/unix_api/CHANGELOG.md index effe43c8..1b9970e5 100644 --- a/pkgs/unix_api/CHANGELOG.md +++ b/pkgs/unix_api/CHANGELOG.md @@ -1,3 +1,3 @@ -## 1.0.0 +## 0.1.0-wip - Initial version. diff --git a/pkgs/unix_api/analysis_options.yaml b/pkgs/unix_api/analysis_options.yaml deleted file mode 100644 index dee8927a..00000000 --- a/pkgs/unix_api/analysis_options.yaml +++ /dev/null @@ -1,30 +0,0 @@ -# This file configures the static analysis results for your project (errors, -# warnings, and lints). -# -# This enables the 'recommended' set of lints from `package:lints`. -# This set helps identify many issues that may lead to problems when running -# or consuming Dart code, and enforces writing Dart using a single, idiomatic -# style and format. -# -# If you want a smaller set of lints you can change this to specify -# 'package:lints/core.yaml'. These are just the most critical lints -# (the recommended set includes the core lints). -# The core lints are also what is used by pub.dev for scoring packages. - -include: package:lints/recommended.yaml - -# Uncomment the following section to specify additional rules. - -# linter: -# rules: -# - camel_case_types - -# analyzer: -# exclude: -# - path/to/excluded/files/** - -# For more information about the core and recommended set of lints, see -# https://dart.dev/go/core-lints - -# For additional information about configuring this file, see -# https://dart.dev/guides/language/analysis-options From 1f14c7c30f6c56f2e2869026ed75da88bc82f541 Mon Sep 17 00:00:00 2001 From: Brian Quinlan Date: Mon, 8 Sep 2025 16:19:42 -0700 Subject: [PATCH 10/14] Review feedback --- pkgs/io_file/pubspec.yaml | 7 +++++-- pkgs/unix_api/README.md | 8 ++++---- pkgs/unix_api/example/unix_api_example.dart | 4 ++++ pkgs/unix_api/lib/unix_api.dart | 4 ++++ pkgs/unix_api/test/dirent_test.dart | 4 ++++ 5 files changed, 21 insertions(+), 6 deletions(-) diff --git a/pkgs/io_file/pubspec.yaml b/pkgs/io_file/pubspec.yaml index b1d5c664..ebc4c7ab 100644 --- a/pkgs/io_file/pubspec.yaml +++ b/pkgs/io_file/pubspec.yaml @@ -14,8 +14,7 @@ dependencies: meta: ^1.16.0 native_toolchain_c: ^0.16.0 path: ^1.9.1 - unix_api: - path: ../unix_api + unix_api: ^0.1.0 win32: ^5.14.0 dev_dependencies: @@ -30,3 +29,7 @@ dev_dependencies: url: https://github.com/canonical/stdlibc.dart.git test: ^1.24.0 uuid: ^4.5.1 + +dependency_overrides: + unix_api: + path: ../unix_api diff --git a/pkgs/unix_api/README.md b/pkgs/unix_api/README.md index f1680e67..b4b52ef7 100644 --- a/pkgs/unix_api/README.md +++ b/pkgs/unix_api/README.md @@ -11,10 +11,10 @@ Thare are two existing packages that provide POSIX API bindings for Dart: native code that cannot be tree shaken away. In exchange, it works on all POSIX platforms that Dart supports. -| Package | Required Tools | Supported Platforms | Fix Disk Usage | -| :--- | :-------------- | :------------------------------------ | :------------ | -| `posix` | Dart | iOS (arm64), Linux (x64), macOS (arm64) | 0 KiB | -| `stdlibc` | Dart | iOS (arm64), Linux (x64), macOS (arm64) | 0 KiB | +| Package | Required Tools | Supported Platforms | Fixed Disk Usage | +| :--- | :-------------- | :------------------------------------ | :-------------- | +| `posix` | Dart | iOS (arm64), Linux (x64), macOS (arm64) | 0 KiB | +| `stdlibc` | Dart | iOS (arm64), Linux (x64), macOS (arm64) | 0 KiB | | `unix_api` | Dart, C compiler | Android (x64, arm32, arm64), iOS (arm64), Linux (x64, arm64), macOS (x64, arm64) | ~60 KiB | ## Design diff --git a/pkgs/unix_api/example/unix_api_example.dart b/pkgs/unix_api/example/unix_api_example.dart index d0caffb9..40c6934e 100644 --- a/pkgs/unix_api/example/unix_api_example.dart +++ b/pkgs/unix_api/example/unix_api_example.dart @@ -1,3 +1,7 @@ +// Copyright (c) 2025, 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. + import 'package:unix_api/unix_api.dart'; void main() { diff --git a/pkgs/unix_api/lib/unix_api.dart b/pkgs/unix_api/lib/unix_api.dart index 18e670e3..5e74e965 100644 --- a/pkgs/unix_api/lib/unix_api.dart +++ b/pkgs/unix_api/lib/unix_api.dart @@ -1,3 +1,7 @@ +// Copyright (c) 2025, 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. + import 'src/libc_bindings.g.dart' as libc; export 'src/constants.g.dart'; diff --git a/pkgs/unix_api/test/dirent_test.dart b/pkgs/unix_api/test/dirent_test.dart index cc286b30..00bd91c9 100644 --- a/pkgs/unix_api/test/dirent_test.dart +++ b/pkgs/unix_api/test/dirent_test.dart @@ -1,3 +1,7 @@ +// Copyright (c) 2025, 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. + import 'dart:ffi'; import 'dart:io'; From b402a97b1eb78170ad9a04c80997f3e0221d2ea4 Mon Sep 17 00:00:00 2001 From: Brian Quinlan Date: Mon, 8 Sep 2025 16:30:03 -0700 Subject: [PATCH 11/14] Update pubspec.yaml --- pkgs/io_file/mobile_test/pubspec.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pkgs/io_file/mobile_test/pubspec.yaml b/pkgs/io_file/mobile_test/pubspec.yaml index b718c3bb..28db97c9 100644 --- a/pkgs/io_file/mobile_test/pubspec.yaml +++ b/pkgs/io_file/mobile_test/pubspec.yaml @@ -37,3 +37,7 @@ dev_dependencies: url: https://github.com/canonical/stdlibc.dart.git test: ^1.24.0 uuid: ^4.5.1 + +dependency_overrides: + unix_api: + path: ../unix_api From 7fd1efa58462f7767ac9615d55699048271e5b65 Mon Sep 17 00:00:00 2001 From: Brian Quinlan Date: Mon, 8 Sep 2025 16:37:04 -0700 Subject: [PATCH 12/14] Update pubspec.yaml --- pkgs/io_file/pubspec.yaml | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/pkgs/io_file/pubspec.yaml b/pkgs/io_file/pubspec.yaml index ebc4c7ab..b1d5c664 100644 --- a/pkgs/io_file/pubspec.yaml +++ b/pkgs/io_file/pubspec.yaml @@ -14,7 +14,8 @@ dependencies: meta: ^1.16.0 native_toolchain_c: ^0.16.0 path: ^1.9.1 - unix_api: ^0.1.0 + unix_api: + path: ../unix_api win32: ^5.14.0 dev_dependencies: @@ -29,7 +30,3 @@ dev_dependencies: url: https://github.com/canonical/stdlibc.dart.git test: ^1.24.0 uuid: ^4.5.1 - -dependency_overrides: - unix_api: - path: ../unix_api From 5c8b0d24be5ef85f3f4d4ddec49c6594b116018f Mon Sep 17 00:00:00 2001 From: Brian Quinlan Date: Mon, 8 Sep 2025 16:42:15 -0700 Subject: [PATCH 13/14] Update pubspec.yaml --- pkgs/io_file/mobile_test/pubspec.yaml | 3 --- 1 file changed, 3 deletions(-) diff --git a/pkgs/io_file/mobile_test/pubspec.yaml b/pkgs/io_file/mobile_test/pubspec.yaml index 28db97c9..6d9cdb59 100644 --- a/pkgs/io_file/mobile_test/pubspec.yaml +++ b/pkgs/io_file/mobile_test/pubspec.yaml @@ -38,6 +38,3 @@ dev_dependencies: test: ^1.24.0 uuid: ^4.5.1 -dependency_overrides: - unix_api: - path: ../unix_api From f273308555b3f7b0cb79e35b02788f2479d08db8 Mon Sep 17 00:00:00 2001 From: Brian Quinlan Date: Mon, 8 Sep 2025 17:16:04 -0700 Subject: [PATCH 14/14] Update pubspec.yaml --- pkgs/io_file/pubspec.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/pkgs/io_file/pubspec.yaml b/pkgs/io_file/pubspec.yaml index b1d5c664..490fe6e4 100644 --- a/pkgs/io_file/pubspec.yaml +++ b/pkgs/io_file/pubspec.yaml @@ -15,6 +15,7 @@ dependencies: native_toolchain_c: ^0.16.0 path: ^1.9.1 unix_api: + # Change this to a released version when this package is published. path: ../unix_api win32: ^5.14.0