[code_assets] Support custom target OS and Architecture
diff --git a/pkgs/code_assets/CHANGELOG.md b/pkgs/code_assets/CHANGELOG.md
index 5cf7491..b63ce11 100644
--- a/pkgs/code_assets/CHANGELOG.md
+++ b/pkgs/code_assets/CHANGELOG.md
@@ -1,3 +1,9 @@
+## 2.0.0-wip
+
+- **Breaking change**: Overrode `==` and `hashCode` in `OS`, `Architecture`, `Sanitizer`, and `LinkModePreference` to support custom targets. These objects can no longer be used as keys of `const` maps and sets.
+  - *Migration*: Use `final` (runtime) maps and sets instead of `const`, or use the string representations (e.g., `OS.name`, `Architecture.name`) as keys if `const` is required.
+- Support custom target OS, architecture, and santizer in protocol extension.
+
 ## 1.2.1
 
 - Avoid throwing a null-assertion error/exception when `input.config.code` is
diff --git a/pkgs/code_assets/README.md b/pkgs/code_assets/README.md
index 37e7eda..6177b0d 100644
--- a/pkgs/code_assets/README.md
+++ b/pkgs/code_assets/README.md
@@ -113,5 +113,22 @@
 
 See the full example in [example/host_name/](example/host_name/).
 
+### Custom Operating Systems and Architectures
+
+SDK authors can define custom OSes and Architectures that they support in a helper package like:
+
+<!-- file://./../hooks_runner/test/build_runner/custom_os_arch_test.dart -->
+```dart
+extension CustomOS on OS {
+  static final ohos = OS.fromString('ohos');
+}
+
+extension CustomArchitecture on Architecture {
+  static final mips = Architecture.fromString('mips');
+}
+```
+
+The users of those SDKs can import this helper package to use the custom OSes and Architectures in their build hooks.
+
 For more information see [dart.dev/tools/hooks](https://dart.dev/tools/hooks).
 
diff --git a/pkgs/code_assets/example/sqlite_prebuilt/hook/build.dart b/pkgs/code_assets/example/sqlite_prebuilt/hook/build.dart
index 508de9f..db0abae 100644
--- a/pkgs/code_assets/example/sqlite_prebuilt/hook/build.dart
+++ b/pkgs/code_assets/example/sqlite_prebuilt/hook/build.dart
@@ -39,7 +39,7 @@
   });
 }
 
-const _windowsDownloadInfo = {
+final _windowsDownloadInfo = {
   Architecture.arm64: (
     url: 'https://sqlite.org/2025/sqlite-dll-win-arm64-3500400.zip',
     sha256: 'c4f3d245377f4ee2da5c08e882ecaff376b35a609198dc399dd8cec0add1ea43',
@@ -50,7 +50,7 @@
   ),
 };
 
-const _linuxPaths = {
+final _linuxPaths = {
   Architecture.arm64: <String>[],
   Architecture.x64: ['/usr/lib/x86_64-linux-gnu/libsqlite3.so'],
 };
diff --git a/pkgs/code_assets/lib/src/code_assets/architecture.dart b/pkgs/code_assets/lib/src/code_assets/architecture.dart
index 6fa35f0..5f082f2 100644
--- a/pkgs/code_assets/lib/src/code_assets/architecture.dart
+++ b/pkgs/code_assets/lib/src/code_assets/architecture.dart
@@ -81,33 +81,27 @@
   ///
   /// The name can be obtained from [Architecture.name] or
   /// [Architecture.toString].
-  factory Architecture.fromString(String name) =>
-      ArchitectureSyntaxExtension.fromSyntax(ArchitectureSyntax.fromJson(name));
+  factory Architecture.fromString(String name) => values.firstWhere(
+    (e) => e.name == name,
+    orElse: () => Architecture._(name),
+  );
 
   /// The current [Architecture].
   static final Architecture current = _abiToArch[Abi.current()]!;
+
+  @override
+  bool operator ==(Object other) => other is Architecture && other.name == name;
+
+  @override
+  int get hashCode => name.hashCode;
 }
 
 /// Extension methods for [Architecture] to convert to and from the syntax.
 extension ArchitectureSyntaxExtension on Architecture {
-  static final _toSyntax = {
-    for (final item in Architecture.values)
-      item: ArchitectureSyntax.fromJson(item.name),
-  };
-
-  static final _fromSyntax = {
-    for (var entry in _toSyntax.entries) entry.value: entry.key,
-  };
-
   /// Converts this [Architecture] to its corresponding [ArchitectureSyntax].
-  ArchitectureSyntax toSyntax() => _toSyntax[this]!;
+  ArchitectureSyntax toSyntax() => ArchitectureSyntax.fromJson(name);
 
   /// Converts an [ArchitectureSyntax] to its corresponding [Architecture].
   static Architecture fromSyntax(ArchitectureSyntax syntax) =>
-      switch (_fromSyntax[syntax]) {
-        null => throw FormatException(
-          'The architecture "${syntax.name}" is not known',
-        ),
-        final arch => arch,
-      };
+      Architecture.fromString(syntax.name);
 }
diff --git a/pkgs/code_assets/lib/src/code_assets/code_asset.dart b/pkgs/code_assets/lib/src/code_assets/code_asset.dart
index 74bef09..53dd3e5 100644
--- a/pkgs/code_assets/lib/src/code_assets/code_asset.dart
+++ b/pkgs/code_assets/lib/src/code_assets/code_asset.dart
@@ -189,7 +189,7 @@
 }
 
 /// The default name prefix for dynamic libraries per [OS].
-const _dylibPrefix = {
+final _dylibPrefix = {
   OS.android: 'lib',
   OS.fuchsia: 'lib',
   OS.iOS: 'lib',
@@ -199,7 +199,7 @@
 };
 
 /// The default extension for dynamic libraries per [OS].
-const _dylibExtension = {
+final _dylibExtension = {
   OS.android: 'so',
   OS.fuchsia: 'so',
   OS.iOS: 'dylib',
@@ -209,10 +209,10 @@
 };
 
 /// The default name prefix for static libraries per [OS].
-const _staticlibPrefix = _dylibPrefix;
+final _staticlibPrefix = _dylibPrefix;
 
 /// The default extension for static libraries per [OS].
-const _staticlibExtension = {
+final _staticlibExtension = {
   OS.android: 'a',
   OS.fuchsia: 'a',
   OS.iOS: 'a',
@@ -222,7 +222,7 @@
 };
 
 /// The default extension for executables per [OS].
-const _executableExtension = {
+final _executableExtension = {
   OS.android: '',
   OS.fuchsia: '',
   OS.iOS: '',
diff --git a/pkgs/code_assets/lib/src/code_assets/os.dart b/pkgs/code_assets/lib/src/code_assets/os.dart
index 6fd6b59..c427e3f 100644
--- a/pkgs/code_assets/lib/src/code_assets/os.dart
+++ b/pkgs/code_assets/lib/src/code_assets/os.dart
@@ -40,7 +40,7 @@
   static const List<OS> values = [android, fuchsia, iOS, linux, macOS, windows];
 
   /// Typical cross compilation between OSes.
-  static const osCrossCompilationDefault = {
+  static final osCrossCompilationDefault = {
     OS.macOS: [OS.macOS, OS.iOS, OS.android],
     OS.linux: [OS.linux, OS.android],
     OS.windows: [OS.windows, OS.android],
@@ -57,30 +57,25 @@
   ///
   /// The name can be obtained from [OS.name] or [OS.toString].
   factory OS.fromString(String name) =>
-      OSSyntaxExtension.fromSyntax(OSSyntax.fromJson(name));
+      values.firstWhere((e) => e.name == name, orElse: () => OS._(name));
 
   /// The current [OS].
   ///
   /// Consistent with the [Platform.version] string.
   static final OS current = OS.fromString(Platform.operatingSystem);
+
+  @override
+  bool operator ==(Object other) => other is OS && other.name == name;
+
+  @override
+  int get hashCode => name.hashCode;
 }
 
 /// Extension methods for [OS] to convert to and from the syntax model.
 extension OSSyntaxExtension on OS {
-  static final _toSyntax = {
-    for (final item in OS.values) item: OSSyntax.fromJson(item.name),
-  };
-
-  static final _fromSyntax = {
-    for (var entry in _toSyntax.entries) entry.value: entry.key,
-  };
-
   /// Converts this [OS] to its corresponding [OSSyntax].
-  OSSyntax toSyntax() => _toSyntax[this]!;
+  OSSyntax toSyntax() => OSSyntax.fromJson(name);
 
   /// Converts an [OSSyntax] to its corresponding [OS].
-  static OS fromSyntax(OSSyntax syntax) => switch (_fromSyntax[syntax]) {
-    null => throw FormatException('The OS "${syntax.name}" is not known'),
-    final e => e,
-  };
+  static OS fromSyntax(OSSyntax syntax) => OS.fromString(syntax.name);
 }
diff --git a/pkgs/code_assets/lib/src/code_assets/sanitizer.dart b/pkgs/code_assets/lib/src/code_assets/sanitizer.dart
index d64b9a2..57ff68e 100644
--- a/pkgs/code_assets/lib/src/code_assets/sanitizer.dart
+++ b/pkgs/code_assets/lib/src/code_assets/sanitizer.dart
@@ -60,28 +60,21 @@
   ///
   /// The name can be obtained from [Sanitizer.name] or [Sanitizer.toString].
   factory Sanitizer.fromString(String name) =>
-      SanitizerSyntaxExtension.fromSyntax(SanitizerSyntax.fromJson(name));
+      values.firstWhere((e) => e.name == name, orElse: () => Sanitizer._(name));
+
+  @override
+  bool operator ==(Object other) => other is Sanitizer && other.name == name;
+
+  @override
+  int get hashCode => name.hashCode;
 }
 
 /// Extension methods for [Sanitizer] to convert to and from the syntax model.
 extension SanitizerSyntaxExtension on Sanitizer {
-  static final _toSyntax = {
-    for (final item in Sanitizer.values)
-      item: SanitizerSyntax.fromJson(item.name),
-  };
-
-  static final _fromSyntax = {
-    for (var entry in _toSyntax.entries) entry.value: entry.key,
-  };
-
   /// Converts this [Sanitizer] to its corresponding [SanitizerSyntax].
-  SanitizerSyntax toSyntax() =>
-      _toSyntax[this] ?? SanitizerSyntax.fromJson(name);
+  SanitizerSyntax toSyntax() => SanitizerSyntax.fromJson(name);
 
   /// Converts an [SanitizerSyntax] to its corresponding [Sanitizer].
   static Sanitizer fromSyntax(SanitizerSyntax syntax) =>
-      switch (_fromSyntax[syntax]) {
-        null => Sanitizer._(syntax.name),
-        final e => e,
-      };
+      Sanitizer.fromString(syntax.name);
 }
diff --git a/pkgs/code_assets/pubspec.yaml b/pkgs/code_assets/pubspec.yaml
index 42c687a..8dfedb3 100644
--- a/pkgs/code_assets/pubspec.yaml
+++ b/pkgs/code_assets/pubspec.yaml
@@ -3,7 +3,7 @@
   This library contains the hook protocol specification for bundling native code
   with Dart packages.
 
-version: 1.2.1
+version: 2.0.0-wip
 
 repository: https://github.com/dart-lang/native/tree/main/pkgs/code_assets
 
diff --git a/pkgs/code_assets/test/code_assets/config_test.dart b/pkgs/code_assets/test/code_assets/config_test.dart
index 8c7cfe4..82f876a 100644
--- a/pkgs/code_assets/test/code_assets/config_test.dart
+++ b/pkgs/code_assets/test/code_assets/config_test.dart
@@ -325,27 +325,24 @@
     }
   });
 
-  test('BuildInput.config.code: invalid architecture', () {
+  test('BuildInput.config.code: custom architecture', () {
     final input = inputJson();
     traverseJson<Map<String, Object?>>(input, [
       'config',
       'extensions',
       'code_assets',
-    ])['target_architecture'] = 'invalid_architecture';
-    expect(
-      () => BuildInput(input).config.code.targetArchitecture,
-      throwsFormatException,
-    );
+    ])['target_architecture'] = 'mips';
+    expect(BuildInput(input).config.code.targetArchitecture.name, 'mips');
   });
 
-  test('LinkInput.config.code: invalid os', () {
+  test('LinkInput.config.code: custom os', () {
     final input = inputJson(hookType: 'link');
     traverseJson<Map<String, Object?>>(input, [
       'config',
       'extensions',
       'code_assets',
-    ])['target_os'] = 'invalid_os';
-    expect(() => LinkInput(input).config.code.targetOS, throwsFormatException);
+    ])['target_os'] = 'ohos';
+    expect(LinkInput(input).config.code.targetOS.name, 'ohos');
   });
 
   test('LinkInput.config.code.target_os invalid type', () {
diff --git a/pkgs/ffigen/CHANGELOG.md b/pkgs/ffigen/CHANGELOG.md
index 6765fc3..75cd728 100644
--- a/pkgs/ffigen/CHANGELOG.md
+++ b/pkgs/ffigen/CHANGELOG.md
@@ -1,8 +1,9 @@
-## 21.0.1-wip
+## 21.1.0-wip
 
 - Bump `package:meta` dependency to `^1.19.0` and stop generating
   `experimental_member_use` lint ignore in generated bindings when
   `@RecordUse()` is used.
+- Bump `package:code_assets` dependency to `^2.0.0`.
 
 ## 21.0.0
 
diff --git a/pkgs/ffigen/example/add/pubspec.yaml b/pkgs/ffigen/example/add/pubspec.yaml
index 74488a7..c6b8ce4 100644
--- a/pkgs/ffigen/example/add/pubspec.yaml
+++ b/pkgs/ffigen/example/add/pubspec.yaml
@@ -15,3 +15,13 @@
   ffigen:
     path: '../../'
   test: ^1.25.15
+
+dependency_overrides:
+  code_assets:
+    path: ../../../code_assets
+  hooks:
+    path: ../../../hooks
+  native_toolchain_c:
+    path: ../../../native_toolchain_c
+  record_use:
+    path: ../../../record_use
diff --git a/pkgs/ffigen/example/c_json/pubspec.yaml b/pkgs/ffigen/example/c_json/pubspec.yaml
index e218628..c52b424 100644
--- a/pkgs/ffigen/example/c_json/pubspec.yaml
+++ b/pkgs/ffigen/example/c_json/pubspec.yaml
@@ -15,3 +15,13 @@
   dart_flutter_team_lints: ^3.5.2
   ffigen:
     path: '../../'
+
+dependency_overrides:
+  code_assets:
+    path: ../../../code_assets
+  hooks:
+    path: ../../../hooks
+  native_toolchain_c:
+    path: ../../../native_toolchain_c
+  record_use:
+    path: ../../../record_use
diff --git a/pkgs/ffigen/example/ffinative/pubspec.yaml b/pkgs/ffigen/example/ffinative/pubspec.yaml
index e3a1b3d..8506a05 100644
--- a/pkgs/ffigen/example/ffinative/pubspec.yaml
+++ b/pkgs/ffigen/example/ffinative/pubspec.yaml
@@ -14,3 +14,13 @@
   dart_flutter_team_lints: ^3.5.2
   ffigen:
     path: '../../'
+
+dependency_overrides:
+  code_assets:
+    path: ../../../code_assets
+  hooks:
+    path: ../../../hooks
+  native_toolchain_c:
+    path: ../../../native_toolchain_c
+  record_use:
+    path: ../../../record_use
diff --git a/pkgs/ffigen/example/libclang-example/pubspec.yaml b/pkgs/ffigen/example/libclang-example/pubspec.yaml
index c2a2e38..504526e 100644
--- a/pkgs/ffigen/example/libclang-example/pubspec.yaml
+++ b/pkgs/ffigen/example/libclang-example/pubspec.yaml
@@ -14,3 +14,13 @@
   dart_flutter_team_lints: ^3.5.2
   ffigen:
     path: '../../'
+
+dependency_overrides:
+  code_assets:
+    path: ../../../code_assets
+  hooks:
+    path: ../../../hooks
+  native_toolchain_c:
+    path: ../../../native_toolchain_c
+  record_use:
+    path: ../../../record_use
diff --git a/pkgs/ffigen/example/objective_c/pubspec.yaml b/pkgs/ffigen/example/objective_c/pubspec.yaml
index f8b72c2..81391cf 100644
--- a/pkgs/ffigen/example/objective_c/pubspec.yaml
+++ b/pkgs/ffigen/example/objective_c/pubspec.yaml
@@ -19,5 +19,13 @@
     path: ../../
 
 dependency_overrides:
+  code_assets:
+    path: ../../../code_assets
+  hooks:
+    path: ../../../hooks
+  native_toolchain_c:
+    path: ../../../native_toolchain_c
   objective_c:
     path: ../../../objective_c/
+  record_use:
+    path: ../../../record_use
diff --git a/pkgs/ffigen/example/shared_bindings/pubspec.yaml b/pkgs/ffigen/example/shared_bindings/pubspec.yaml
index cc1256f..c6681f2 100644
--- a/pkgs/ffigen/example/shared_bindings/pubspec.yaml
+++ b/pkgs/ffigen/example/shared_bindings/pubspec.yaml
@@ -16,3 +16,15 @@
   dart_flutter_team_lints: ^3.5.2
   ffigen:
     path: '../../'
+
+dependency_overrides:
+  code_assets:
+    path: ../../../code_assets
+  hooks:
+    path: ../../../hooks
+  native_toolchain_c:
+    path: ../../../native_toolchain_c
+  objective_c:
+    path: ../../../objective_c
+  record_use:
+    path: ../../../record_use
diff --git a/pkgs/ffigen/example/simple/pubspec.yaml b/pkgs/ffigen/example/simple/pubspec.yaml
index 4b6df34..efcca57 100644
--- a/pkgs/ffigen/example/simple/pubspec.yaml
+++ b/pkgs/ffigen/example/simple/pubspec.yaml
@@ -14,3 +14,13 @@
   dart_flutter_team_lints: ^3.5.2
   ffigen:
     path: '../../'
+
+dependency_overrides:
+  code_assets:
+    path: ../../../code_assets
+  hooks:
+    path: ../../../hooks
+  native_toolchain_c:
+    path: ../../../native_toolchain_c
+  record_use:
+    path: ../../../record_use
diff --git a/pkgs/ffigen/example/swift/pubspec.yaml b/pkgs/ffigen/example/swift/pubspec.yaml
index 2ad0d9b..fc85465 100644
--- a/pkgs/ffigen/example/swift/pubspec.yaml
+++ b/pkgs/ffigen/example/swift/pubspec.yaml
@@ -18,5 +18,13 @@
     path: ../../
 
 dependency_overrides:
+  code_assets:
+    path: ../../../code_assets
+  hooks:
+    path: ../../../hooks
+  native_toolchain_c:
+    path: ../../../native_toolchain_c
   objective_c:
     path: ../../../objective_c/
+  record_use:
+    path: ../../../record_use
diff --git a/pkgs/ffigen/hook/build.dart b/pkgs/ffigen/hook/build.dart
index 4c1f5b4..b310704 100644
--- a/pkgs/ffigen/hook/build.dart
+++ b/pkgs/ffigen/hook/build.dart
@@ -221,12 +221,12 @@
   return appleClangMacosTargetFlags[architecture]!;
 }
 
-const appleClangMacosTargetFlags = {
+final appleClangMacosTargetFlags = {
   Architecture.arm64: 'arm64-apple-darwin',
   Architecture.x64: 'x86_64-apple-darwin',
 };
 
-const appleClangIosTargetFlags = {
+final appleClangIosTargetFlags = {
   Architecture.arm64: {
     IOSSdk.iPhoneOS: 'arm64-apple-ios',
     IOSSdk.iPhoneSimulator: 'arm64-apple-ios-simulator',
diff --git a/pkgs/ffigen/pubspec.yaml b/pkgs/ffigen/pubspec.yaml
index b4891dc..19a6979 100644
--- a/pkgs/ffigen/pubspec.yaml
+++ b/pkgs/ffigen/pubspec.yaml
@@ -3,7 +3,7 @@
 # BSD-style license that can be found in the LICENSE file.
 
 name: ffigen
-version: 21.0.1-wip
+version: 21.1.0-wip
 description: >
   Generator for FFI bindings, using LibClang to parse C, Objective-C, and Swift
   files.
@@ -21,7 +21,7 @@
 dependencies:
   args: ^2.6.0
   cli_util: ^0.4.2
-  code_assets: ^1.1.0
+  code_assets: ^2.0.0-wip
   collection: ^1.18.0
   dart_style: ^3.0.0
   ffi: ^2.0.1
diff --git a/pkgs/hooks/pubspec.yaml b/pkgs/hooks/pubspec.yaml
index 7fc6871..25cec02 100644
--- a/pkgs/hooks/pubspec.yaml
+++ b/pkgs/hooks/pubspec.yaml
@@ -30,7 +30,7 @@
 
 dev_dependencies:
   args: ^2.6.0
-  code_assets: ^1.0.0 # Used for running tests with real asset types.
+  code_assets: ^2.0.0-wip # Used for running tests with real asset types.
   dart_flutter_team_lints: ^3.5.2
   data_assets: any # Used for running tests with real asset types.
   file_testing: ^3.0.2
diff --git a/pkgs/hooks_runner/CHANGELOG.md b/pkgs/hooks_runner/CHANGELOG.md
index 3231f6e..400617c 100644
--- a/pkgs/hooks_runner/CHANGELOG.md
+++ b/pkgs/hooks_runner/CHANGELOG.md
@@ -1,5 +1,6 @@
 ## 1.6.0-wip
 
+- Bump `package:code_assets` dependency to `^2.0.0`.
 - Fix record_use path changing caching issue.
 
 ## 1.5.0
diff --git a/pkgs/hooks_runner/lib/src/model/target.dart b/pkgs/hooks_runner/lib/src/model/target.dart
index a485a46..9d14af3 100644
--- a/pkgs/hooks_runner/lib/src/model/target.dart
+++ b/pkgs/hooks_runner/lib/src/model/target.dart
@@ -151,16 +151,20 @@
 
   /// A list of supported target [Target]s from this host [os].
   List<Target> supportedTargetTargets({
-    Map<OS, List<OS>> osCrossCompilation = OS.osCrossCompilationDefault,
-  }) => Target.values
-      .where(
-        (target) =>
-            // Only valid cross compilation.
-            osCrossCompilation[os]!.contains(target.os) &&
-            // And no deprecated architectures.
-            target != Target.iOSArm,
-      )
-      .sorted;
+    Map<OS, List<OS>>? osCrossCompilation,
+  }) {
+    final osCrossCompilation_ =
+        osCrossCompilation ?? OS.osCrossCompilationDefault;
+    return Target.values
+        .where(
+          (target) =>
+              // Only valid cross compilation.
+              osCrossCompilation_[os]!.contains(target.os) &&
+              // And no deprecated architectures.
+              target != Target.iOSArm,
+        )
+        .sorted;
+  }
 }
 
 /// Common methods for manipulating iterables of [Target]s.
diff --git a/pkgs/hooks_runner/pubspec.yaml b/pkgs/hooks_runner/pubspec.yaml
index f3a3ddb..b0c2c83 100644
--- a/pkgs/hooks_runner/pubspec.yaml
+++ b/pkgs/hooks_runner/pubspec.yaml
@@ -12,7 +12,7 @@
   sdk: '>=3.10.0 <4.0.0'
 
 dependencies:
-  code_assets: ^1.2.0 # Needed for OS for Target for KernelAssets.
+  code_assets: ^2.0.0-wip # Needed for OS for Target for KernelAssets.
   collection: ^1.19.1
   crypto: ^3.0.6
   file: ^7.0.1
diff --git a/pkgs/hooks_runner/test/build_runner/custom_os_arch_test.dart b/pkgs/hooks_runner/test/build_runner/custom_os_arch_test.dart
new file mode 100644
index 0000000..ee3cee1
--- /dev/null
+++ b/pkgs/hooks_runner/test/build_runner/custom_os_arch_test.dart
@@ -0,0 +1,83 @@
+// Copyright (c) 2026, 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:io';
+
+import 'package:code_assets/code_assets.dart';
+import 'package:file/local.dart' show LocalFileSystem;
+import 'package:hooks_runner/hooks_runner.dart';
+import 'package:test/test.dart';
+
+import '../helpers.dart';
+import 'helpers.dart';
+
+const Timeout longTimeout = Timeout(Duration(minutes: 5));
+
+void main() async {
+  test('build custom OS and architecture', timeout: longTimeout, () async {
+    await inTempDir((tempUri) async {
+      await copyTestProjects(targetUri: tempUri);
+      final packageUri = tempUri.resolve('custom_os_arch/');
+
+      // First, run `pub get` to resolve our dependencies.
+      await runPubGet(workingDirectory: packageUri, logger: logger);
+
+      final packageLayout = await PackageLayout.fromWorkingDirectory(
+        const LocalFileSystem(),
+        packageUri,
+        'custom_os_arch',
+        includeDevDependencies: false,
+      );
+
+      final buildRunner = NativeAssetsBuildRunner(
+        logger: logger,
+        dartExecutable: dartExecutable,
+        fileSystem: const LocalFileSystem(),
+        packageLayout: packageLayout,
+      );
+
+      final result = await buildRunner.build(
+        extensions: [
+          CodeAssetExtension(
+            targetOS: CustomOS.ohos,
+            targetArchitecture: CustomArchitecture.mips,
+            linkModePreference: LinkModePreference.dynamic,
+            sanitizer: CustomSanitizer.mySanitizer,
+          ),
+        ],
+        linkingEnabled: false,
+      );
+
+      expect(result.isSuccess, isTrue);
+      final buildResult = result.success;
+
+      expect(buildResult.encodedAssets.length, 1);
+      final codeAsset = buildResult.encodedAssets.first.asCodeAsset;
+      expect(codeAsset.id, 'package:custom_os_arch/my_asset');
+
+      expect(codeAsset.linkMode, isA<DynamicLoadingBundled>());
+
+      final fileContents = await File.fromUri(codeAsset.file!).readAsString();
+      expect(
+        fileContents,
+        'dummy: ohos, mips, my_sanitizer',
+      );
+    });
+  });
+}
+
+// snippet-start
+extension CustomOS on OS {
+  static final ohos = OS.fromString('ohos');
+}
+
+extension CustomArchitecture on Architecture {
+  static final mips = Architecture.fromString('mips');
+}
+
+// snippet-end
+
+extension CustomSanitizer on Sanitizer {
+  static final mySanitizer = Sanitizer.fromString('my_sanitizer');
+}
diff --git a/pkgs/hooks_runner/test_data/custom_os_arch/hook/build.dart b/pkgs/hooks_runner/test_data/custom_os_arch/hook/build.dart
new file mode 100644
index 0000000..5d0e127
--- /dev/null
+++ b/pkgs/hooks_runner/test_data/custom_os_arch/hook/build.dart
@@ -0,0 +1,30 @@
+// Copyright (c) 2026, 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:io';
+
+import 'package:code_assets/code_assets.dart';
+import 'package:hooks/hooks.dart';
+
+void main(List<String> arguments) async {
+  await build(arguments, (input, output) async {
+    final targetOS = input.config.code.targetOS;
+    final targetArch = input.config.code.targetArchitecture;
+    final sanitizer = input.config.code.sanitizer;
+
+    final file = File.fromUri(input.outputDirectory.resolve('my_asset.so'));
+    await file.writeAsString(
+      'dummy: $targetOS, $targetArch, $sanitizer',
+    );
+
+    output.assets.code.add(
+      CodeAsset(
+        package: 'custom_os_arch',
+        name: 'my_asset',
+        linkMode: DynamicLoadingBundled(),
+        file: file.uri,
+      ),
+    );
+  });
+}
diff --git a/pkgs/hooks_runner/test_data/custom_os_arch/pubspec.yaml b/pkgs/hooks_runner/test_data/custom_os_arch/pubspec.yaml
new file mode 100644
index 0000000..bbff61f
--- /dev/null
+++ b/pkgs/hooks_runner/test_data/custom_os_arch/pubspec.yaml
@@ -0,0 +1,14 @@
+name: custom_os_arch
+description: A package to test custom OS and architecture settings.
+version: 0.1.0
+
+publish_to: none
+
+resolution: workspace
+
+environment:
+  sdk: '>=3.10.0 <4.0.0'
+
+dependencies:
+  code_assets: any
+  hooks: any
diff --git a/pkgs/hooks_runner/test_data/manifest.yaml b/pkgs/hooks_runner/test_data/manifest.yaml
index 1f2180f..cfe3ab8 100644
--- a/pkgs/hooks_runner/test_data/manifest.yaml
+++ b/pkgs/hooks_runner/test_data/manifest.yaml
@@ -23,6 +23,8 @@
 - complex_link_helper/hook/build.dart
 - complex_link_helper/lib/complex_link_helper.dart
 - complex_link_helper/pubspec.yaml
+- custom_os_arch/hook/build.dart
+- custom_os_arch/pubspec.yaml
 - cyclic_link_package_1/hook/link.dart
 - cyclic_link_package_1/pubspec.yaml
 - cyclic_link_package_2/hook/link.dart
diff --git a/pkgs/native_toolchain_c/CHANGELOG.md b/pkgs/native_toolchain_c/CHANGELOG.md
index e39828f..a79028c 100644
--- a/pkgs/native_toolchain_c/CHANGELOG.md
+++ b/pkgs/native_toolchain_c/CHANGELOG.md
@@ -1,5 +1,6 @@
 ## 0.19.3-wip
 
+- Bump `package:code_assets` dependency to `^2.0.0`.
 - Fixed building with MSVC on Windows when a source, include, or output path
   contains a space (e.g. a user name with a space in the default pub cache
   location). The compiler and archiver are now invoked directly instead of
@@ -14,7 +15,8 @@
 
 ## 0.19.2
 
-- Fixed compatibility with newer Xcode versions when cross-compiling static libraries on macOS hosts targeting Android and Linux.
+- Fixed compatibility with newer Xcode versions when cross-compiling static
+  libraries on macOS hosts targeting Android and Linux.
 
 ## 0.19.1
 
diff --git a/pkgs/native_toolchain_c/lib/src/cbuilder/run_cbuilder.dart b/pkgs/native_toolchain_c/lib/src/cbuilder/run_cbuilder.dart
index d87233a..96ed274 100644
--- a/pkgs/native_toolchain_c/lib/src/cbuilder/run_cbuilder.dart
+++ b/pkgs/native_toolchain_c/lib/src/cbuilder/run_cbuilder.dart
@@ -480,7 +480,7 @@
     return '$baseName.obj';
   }
 
-  static const androidNdkClangTargetFlags = {
+  static final androidNdkClangTargetFlags = {
     Architecture.arm: 'armv7a-linux-androideabi',
     Architecture.arm64: 'aarch64-linux-android',
     Architecture.ia32: 'i686-linux-android',
@@ -488,12 +488,12 @@
     Architecture.riscv64: 'riscv64-linux-android',
   };
 
-  static const appleClangMacosTargetFlags = {
+  static final appleClangMacosTargetFlags = {
     Architecture.arm64: 'arm64-apple-darwin',
     Architecture.x64: 'x86_64-apple-darwin',
   };
 
-  static const appleClangIosTargetFlags = {
+  static final appleClangIosTargetFlags = {
     Architecture.arm64: {
       IOSSdk.iPhoneOS: 'arm64-apple-ios',
       IOSSdk.iPhoneSimulator: 'arm64-apple-ios-simulator',
@@ -501,19 +501,19 @@
     Architecture.x64: {IOSSdk.iPhoneSimulator: 'x86_64-apple-ios-simulator'},
   };
 
-  static const clangWindowsTargetFlags = {
+  static final clangWindowsTargetFlags = {
     Architecture.arm64: 'arm64-pc-windows-msvc',
     Architecture.ia32: 'i386-pc-windows-msvc',
     Architecture.x64: 'x86_64-pc-windows-msvc',
   };
 
-  static const clTargetFlags = {
+  static final clTargetFlags = {
     Architecture.arm64: 'ARM64',
     Architecture.ia32: 'X86',
     Architecture.x64: 'X64',
   };
 
-  static const defaultCppLinkStdLib = {
+  static final defaultCppLinkStdLib = {
     OS.android: 'c++_shared',
     OS.fuchsia: 'c++',
     OS.iOS: 'c++',
@@ -521,13 +521,13 @@
     OS.macOS: 'c++',
   };
 
-  static const _clangSanitizers = {
+  static final _clangSanitizers = {
     Sanitizer.asan: '-fsanitize=address',
     Sanitizer.msan: '-fsanitize=memory',
     Sanitizer.tsan: '-fsanitize=thread',
   };
 
-  static const _msvcSanitizers = {
+  static final _msvcSanitizers = {
     Sanitizer.asan: '/fsanitize=address',
   };
 }
diff --git a/pkgs/native_toolchain_c/lib/src/native_toolchain/msvc.dart b/pkgs/native_toolchain_c/lib/src/native_toolchain/msvc.dart
index 8a1d20e..f22c6f2 100644
--- a/pkgs/native_toolchain_c/lib/src/native_toolchain/msvc.dart
+++ b/pkgs/native_toolchain_c/lib/src/native_toolchain/msvc.dart
@@ -219,7 +219,7 @@
   hostArchitecture: .current,
 );
 
-const _msvcArchNames = {
+final _msvcArchNames = {
   Architecture.ia32: 'x86',
   Architecture.x64: 'x64',
   Architecture.arm64: 'arm64',
diff --git a/pkgs/native_toolchain_c/pubspec.yaml b/pkgs/native_toolchain_c/pubspec.yaml
index 248df51..86dcfe6 100644
--- a/pkgs/native_toolchain_c/pubspec.yaml
+++ b/pkgs/native_toolchain_c/pubspec.yaml
@@ -17,7 +17,7 @@
   sdk: '>=3.10.0 <4.0.0'
 
 dependencies:
-  code_assets: ^1.2.0
+  code_assets: ^2.0.0-wip
   glob: ^2.1.1
   hooks: ^2.0.0
   logging: ^1.3.0
diff --git a/pkgs/native_toolchain_c/test/cbuilder/cbuilder_cross_macos_host_test.dart b/pkgs/native_toolchain_c/test/cbuilder/cbuilder_cross_macos_host_test.dart
index ed141fa..09511d6 100644
--- a/pkgs/native_toolchain_c/test/cbuilder/cbuilder_cross_macos_host_test.dart
+++ b/pkgs/native_toolchain_c/test/cbuilder/cbuilder_cross_macos_host_test.dart
@@ -25,7 +25,7 @@
 import '../helpers.dart';
 
 // Dont include 'mach-o' or 'Mach-O', different spelling is used.
-const objdumpFileFormat = {
+final objdumpFileFormat = {
   (OS.macOS, Architecture.arm64): 'arm64',
   (OS.macOS, Architecture.x64): '64-bit x86-64',
   (OS.linux, Architecture.arm): 'elf32-littlearm',
diff --git a/pkgs/native_toolchain_c/test/cbuilder/cbuilder_cross_windows_host_test.dart b/pkgs/native_toolchain_c/test/cbuilder/cbuilder_cross_windows_host_test.dart
index 6f644ed..8f08b17 100644
--- a/pkgs/native_toolchain_c/test/cbuilder/cbuilder_cross_windows_host_test.dart
+++ b/pkgs/native_toolchain_c/test/cbuilder/cbuilder_cross_windows_host_test.dart
@@ -53,7 +53,7 @@
     )).first.uri;
   });
 
-  const dumpbinMachine = {
+  final dumpbinMachine = {
     Architecture.arm64: 'ARM64',
     Architecture.ia32: 'x86',
     Architecture.x64: 'x64',
diff --git a/pkgs/native_toolchain_c/test/helpers.dart b/pkgs/native_toolchain_c/test/helpers.dart
index 2d84402..104b425 100644
--- a/pkgs/native_toolchain_c/test/helpers.dart
+++ b/pkgs/native_toolchain_c/test/helpers.dart
@@ -186,7 +186,7 @@
   return result.split('\n').firstWhere((e) => e.contains('Machine:'));
 }
 
-const readElfMachine = {
+final readElfMachine = {
   Architecture.arm: 'ARM',
   Architecture.arm64: 'AArch64',
   Architecture.ia32: 'Intel 80386',
@@ -381,7 +381,7 @@
 
 /// File-format strings used by the `objdump` tool for Android binaries that
 /// run on a given architecture.
-const objdumpFileFormatAndroid = {
+final objdumpFileFormatAndroid = {
   Architecture.arm: 'elf32-littlearm',
   Architecture.arm64: 'elf64-littleaarch64',
   Architecture.ia32: 'elf32-i386',
@@ -389,18 +389,18 @@
   Architecture.riscv64: 'elf64-littleriscv',
 };
 
-const objdumpFileFormatMacOS = {
+final objdumpFileFormatMacOS = {
   Architecture.arm64: 'mach-o arm64',
   Architecture.x64: 'mach-o 64-bit x86-64',
 };
 
 // Don't include 'mach-o' or 'Mach-O', different spelling is used.
-const objdumpFileFormatIOS = {
+final objdumpFileFormatIOS = {
   Architecture.arm64: 'arm64',
   Architecture.x64: '64-bit x86-64',
 };
 
-const objdumpFileFormatLinux = {
+final objdumpFileFormatLinux = {
   Architecture.arm: 'elf32-littlearm',
   Architecture.arm64: 'elf64-littleaarch64',
   Architecture.ia32: 'elf32-i386',
@@ -409,14 +409,14 @@
   Architecture.riscv64: 'elf64-riscv64',
 };
 
-const targetOSToObjdumpFileFormat = {
+final targetOSToObjdumpFileFormat = {
   OS.android: objdumpFileFormatAndroid,
   OS.macOS: objdumpFileFormatMacOS,
   OS.iOS: objdumpFileFormatMacOS,
   OS.linux: objdumpFileFormatLinux,
 };
 
-const dumpbinFileFormat = {
+final dumpbinFileFormat = {
   Architecture.arm64: 'ARM64',
   Architecture.ia32: 'x86',
   Architecture.x64: 'x64',
diff --git a/pkgs/objective_c/CHANGELOG.md b/pkgs/objective_c/CHANGELOG.md
index 66ccb0b..d9aaca2 100644
--- a/pkgs/objective_c/CHANGELOG.md
+++ b/pkgs/objective_c/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 9.4.2
+
+- Bump `package:code_assets` dependency to `^2.0.0`.
+
 ## 9.4.1
 
 - Fix a [bug](https://github.com/flutter/flutter/issues/186794) related to
diff --git a/pkgs/objective_c/example/command_line/pubspec.yaml b/pkgs/objective_c/example/command_line/pubspec.yaml
index fae3f21..1890859 100644
--- a/pkgs/objective_c/example/command_line/pubspec.yaml
+++ b/pkgs/objective_c/example/command_line/pubspec.yaml
@@ -13,3 +13,13 @@
 dev_dependencies:
   lints: ^6.0.0
   test: ^1.27.0
+
+dependency_overrides:
+  code_assets:
+    path: ../../../code_assets
+  hooks:
+    path: ../../../hooks
+  native_toolchain_c:
+    path: ../../../native_toolchain_c
+  record_use:
+    path: ../../../record_use
diff --git a/pkgs/objective_c/example/flutter_app/pubspec.yaml b/pkgs/objective_c/example/flutter_app/pubspec.yaml
index a8dc3ec..326a294 100644
--- a/pkgs/objective_c/example/flutter_app/pubspec.yaml
+++ b/pkgs/objective_c/example/flutter_app/pubspec.yaml
@@ -17,5 +17,17 @@
     sdk: flutter
   flutter_lints: ^6.0.0
 
+dependency_overrides:
+  code_assets:
+    path: ../../../code_assets
+  hooks:
+    path: ../../../hooks
+  native_toolchain_c:
+    path: ../../../native_toolchain_c
+  record_use:
+    path: ../../../record_use
+  objective_c:
+    path: ../..
+
 flutter:
   uses-material-design: true
diff --git a/pkgs/objective_c/example/path_provider_example/ios/Flutter/Debug.xcconfig b/pkgs/objective_c/example/path_provider_example/ios/Flutter/Debug.xcconfig
index 592ceee..ec97fc6 100644
--- a/pkgs/objective_c/example/path_provider_example/ios/Flutter/Debug.xcconfig
+++ b/pkgs/objective_c/example/path_provider_example/ios/Flutter/Debug.xcconfig
@@ -1 +1,2 @@
+#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
 #include "Generated.xcconfig"
diff --git a/pkgs/objective_c/example/path_provider_example/ios/Flutter/Release.xcconfig b/pkgs/objective_c/example/path_provider_example/ios/Flutter/Release.xcconfig
index 592ceee..c4855bf 100644
--- a/pkgs/objective_c/example/path_provider_example/ios/Flutter/Release.xcconfig
+++ b/pkgs/objective_c/example/path_provider_example/ios/Flutter/Release.xcconfig
@@ -1 +1,2 @@
+#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
 #include "Generated.xcconfig"
diff --git a/pkgs/objective_c/example/path_provider_example/ios/Podfile b/pkgs/objective_c/example/path_provider_example/ios/Podfile
new file mode 100644
index 0000000..620e46e
--- /dev/null
+++ b/pkgs/objective_c/example/path_provider_example/ios/Podfile
@@ -0,0 +1,43 @@
+# Uncomment this line to define a global platform for your project
+# platform :ios, '13.0'
+
+# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
+ENV['COCOAPODS_DISABLE_STATS'] = 'true'
+
+project 'Runner', {
+  'Debug' => :debug,
+  'Profile' => :release,
+  'Release' => :release,
+}
+
+def flutter_root
+  generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
+  unless File.exist?(generated_xcode_build_settings_path)
+    raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
+  end
+
+  File.foreach(generated_xcode_build_settings_path) do |line|
+    matches = line.match(/FLUTTER_ROOT\=(.*)/)
+    return matches[1].strip if matches
+  end
+  raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
+end
+
+require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
+
+flutter_ios_podfile_setup
+
+target 'Runner' do
+  use_frameworks!
+
+  flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
+  target 'RunnerTests' do
+    inherit! :search_paths
+  end
+end
+
+post_install do |installer|
+  installer.pods_project.targets.each do |target|
+    flutter_additional_ios_build_settings(target)
+  end
+end
diff --git a/pkgs/objective_c/example/path_provider_example/pubspec.yaml b/pkgs/objective_c/example/path_provider_example/pubspec.yaml
index 48c44f3..5200ef0 100644
--- a/pkgs/objective_c/example/path_provider_example/pubspec.yaml
+++ b/pkgs/objective_c/example/path_provider_example/pubspec.yaml
@@ -19,6 +19,14 @@
   flutter_lints: ^6.0.0
 
 dependency_overrides:
+  code_assets:
+    path: ../../../code_assets
+  hooks:
+    path: ../../../hooks
+  native_toolchain_c:
+    path: ../../../native_toolchain_c
+  record_use:
+    path: ../../../record_use
   objective_c:
     path: ../..
 
diff --git a/pkgs/objective_c/hook/build.dart b/pkgs/objective_c/hook/build.dart
index 280782f..646f7ad 100644
--- a/pkgs/objective_c/hook/build.dart
+++ b/pkgs/objective_c/hook/build.dart
@@ -25,7 +25,7 @@
       return;
     }
 
-    const supportedOSs = {OS.iOS, OS.macOS};
+    final supportedOSs = {OS.iOS, OS.macOS};
     final codeConfig = input.config.code;
     final os = codeConfig.targetOS;
     if (!supportedOSs.contains(os)) {
@@ -217,12 +217,12 @@
   return appleClangMacosTargetFlags[architecture]!;
 }
 
-const appleClangMacosTargetFlags = {
+final appleClangMacosTargetFlags = {
   Architecture.arm64: 'arm64-apple-darwin',
   Architecture.x64: 'x86_64-apple-darwin',
 };
 
-const appleClangIosTargetFlags = {
+final appleClangIosTargetFlags = {
   Architecture.arm64: {
     IOSSdk.iPhoneOS: 'arm64-apple-ios',
     IOSSdk.iPhoneSimulator: 'arm64-apple-ios-simulator',
diff --git a/pkgs/objective_c/pubspec.yaml b/pkgs/objective_c/pubspec.yaml
index aeefad9..84d2b62 100644
--- a/pkgs/objective_c/pubspec.yaml
+++ b/pkgs/objective_c/pubspec.yaml
@@ -3,7 +3,7 @@
 # BSD-style license that can be found in the LICENSE file.
 name: objective_c
 description: 'A library to access Objective C from Flutter that acts as a support library for package:ffigen.'
-version: 9.4.1
+version: 9.4.2
 repository: https://github.com/dart-lang/native/tree/main/pkgs/objective_c
 issue_tracker: https://github.com/dart-lang/native/issues?q=is%3Aissue+is%3Aopen+label%3Apackage%3Aobjective_c
 
@@ -17,7 +17,7 @@
   sdk: '>=3.10.0 <4.0.0'
 
 dependencies:
-  code_assets: ^1.1.0
+  code_assets: ^2.0.0-wip
   collection: ^1.19.1
   ffi: ^2.1.0
   hooks: ^2.0.0
@@ -42,6 +42,8 @@
     path: ../ffigen
   hooks:
     path: ../hooks
+  native_toolchain_c:
+    path: ../native_toolchain_c
   record_use:
     path: ../record_use
 
diff --git a/pkgs/swiftgen/example/pubspec.yaml b/pkgs/swiftgen/example/pubspec.yaml
index 8daae28..d4c931c 100644
--- a/pkgs/swiftgen/example/pubspec.yaml
+++ b/pkgs/swiftgen/example/pubspec.yaml
@@ -25,8 +25,14 @@
   test: ^1.21.1
 
 dependency_overrides:
+  code_assets:
+    path: ../../code_assets/
   ffigen:
     path: ../../ffigen/
+  hooks:
+    path: ../../hooks/
+  native_toolchain_c:
+    path: ../../native_toolchain_c/
   objective_c:
     path: ../../objective_c/
   swift2objc:
diff --git a/pkgs/swiftgen/pubspec.yaml b/pkgs/swiftgen/pubspec.yaml
index 033f36e..8df9bf2 100644
--- a/pkgs/swiftgen/pubspec.yaml
+++ b/pkgs/swiftgen/pubspec.yaml
@@ -33,8 +33,14 @@
   test: ^1.21.1
 
 dependency_overrides:
+  code_assets:
+    path: ../code_assets/
   ffigen:
     path: ../ffigen/
+  hooks:
+    path: ../hooks/
+  native_toolchain_c:
+    path: ../native_toolchain_c/
   objective_c:
     path: ../objective_c/
   swift2objc:
diff --git a/pubspec.yaml b/pubspec.yaml
index 83b6fcf..2e82757 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -37,6 +37,7 @@
   - pkgs/hooks_runner/test_data/add_asset_link
   - pkgs/hooks_runner/test_data/complex_link
   - pkgs/hooks_runner/test_data/complex_link_helper
+  - pkgs/hooks_runner/test_data/custom_os_arch
   - pkgs/hooks_runner/test_data/cyclic_link_package_1
   - pkgs/hooks_runner/test_data/cyclic_link_package_2
   - pkgs/hooks_runner/test_data/cyclic_package_1
diff --git a/tool/ci.dart b/tool/ci.dart
index fb184ca..b1d1d05 100644
--- a/tool/ci.dart
+++ b/tool/ci.dart
@@ -190,7 +190,10 @@
     final rootDir = Directory.fromUri(repositoryRoot.resolve('pkgs'));
     for (final entity in rootDir.listSync(recursive: true)) {
       if (entity is File && entity.path.endsWith('pubspec.yaml')) {
-        if (entity.path.split(Platform.pathSeparator).contains('.dart_tool')) {
+        final pathSegments = entity.path.split(Platform.pathSeparator);
+        if (pathSegments.contains('.dart_tool') ||
+            pathSegments.contains('ephemeral')) {
+          // Can contain generated or symlinked pubspecs.
           continue;
         }
         packages.add(