mirror of
https://github.com/dart-lang/sdk
synced 2024-10-04 19:59:42 +00:00
[pkg][vm] Native Assets builder
This package contains the logic for building native assets. This package is the backend that invokes toplevel `build.dart` scripts. For more info on these scripts see https://github.com/dart-lang/native. This is a separate package so that dartdev and flutter_tools can reuse the same logic without flutter_tools having to import dartdev. Some design decisions: * We don't yet have `build_dependencies`, so we use the ordinary dependency graph for ordering of native assets builds. (If there is a cycle we refuse to run.) Bug: https://github.com/dart-lang/pub/issues/3794 * Builds are cached based on all the configuration provided by the caller. Environment variables are ignored in caching. This CL also contains a unit test that invokes the build by not passing through environment variables. However, for Windows we need to pass through at least `SYSTEMROOT` for MSVC to run correctly. So we might need to further explore if we can/want to lock env variables down. Bug: https://github.com/dart-lang/native/issues/32 Bug: https://github.com/dart-lang/native/issues/33 Run tests: ``` dart tools/generate_package_config.dart && \ tools/test.py -n unittest-asserts-release-linux pkg/native_assets_builder ``` Bug: https://github.com/dart-lang/sdk/issues/50565 Change-Id: I133052d7195373e87d20924d61e1e96e3d34ce8f Cq-Include-Trybots: luci.dart.try:pkg-linux-debug-try,pkg-linux-release-try,pkg-mac-release-arm64-try,pkg-mac-release-try,pkg-win-release-try Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/300203 Reviewed-by: Liam Appelbe <liama@google.com> Commit-Queue: Daco Harkes <dacoharkes@google.com> Reviewed-by: Martin Kustermann <kustermann@google.com> Reviewed-by: Hossein Yousefi <yousefi@google.com>
This commit is contained in:
parent
05e77a934e
commit
d360edf2f3
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -75,6 +75,9 @@ client/tests/drt
|
|||
packages
|
||||
pubspec.lock
|
||||
|
||||
# Files for native assets auto-generated per checkout.
|
||||
/.dart_tool/native_assets_builder/
|
||||
|
||||
# The top level package file (this is auto-generated per checkout).
|
||||
/.dart_tool/package_config.json
|
||||
|
||||
|
|
2
DEPS
2
DEPS
|
@ -152,7 +152,7 @@ vars = {
|
|||
"matcher_rev": "4dfd9adfdcd3de95e6964fc295075a32635e5b37",
|
||||
"mime_rev": "eb9d54b8f6fab9442b2b733e19137dc25fb0b868",
|
||||
"mockito_rev": "51a7728927c505a907384860f4a407f8b7219004",
|
||||
"native_rev": "64aa5b500fd71b51a34bf4ef0dc05ec24379be00",
|
||||
"native_rev": "e01aa63e851ce971390bc45fc17d16240b8d164a",
|
||||
"package_config_rev": "f41f92cd4d2c539d910f2ef041ba6f6bd8469e78",
|
||||
"path_rev": "f8d15c2775835fd61ea5e06bb0ab99232f5ec446",
|
||||
"pool_rev": "86b4f4328a32a9e9eb4f1601c8296e22d3e50552",
|
||||
|
|
|
@ -858,6 +858,9 @@ const AllowedExperimentalFlags defaultAllowedExperimentalFlags =
|
|||
"meta": {
|
||||
ExperimentalFlag.nonNullable,
|
||||
},
|
||||
"native_assets_builder": {
|
||||
ExperimentalFlag.nonNullable,
|
||||
},
|
||||
"native_stack_traces": {
|
||||
ExperimentalFlag.nonNullable,
|
||||
},
|
||||
|
|
|
@ -204,6 +204,8 @@ asserted
|
|||
assertion
|
||||
assertions
|
||||
asserts
|
||||
asset
|
||||
assets
|
||||
assign
|
||||
assignability
|
||||
assignable
|
||||
|
@ -3114,6 +3116,7 @@ tokens
|
|||
too
|
||||
took
|
||||
tool
|
||||
toolchain
|
||||
tools
|
||||
top
|
||||
topologically
|
||||
|
|
48
pkg/native_assets_builder/.gitignore
vendored
Normal file
48
pkg/native_assets_builder/.gitignore
vendored
Normal file
|
@ -0,0 +1,48 @@
|
|||
# Miscellaneous
|
||||
*.class
|
||||
*.log
|
||||
*.pyc
|
||||
*.swp
|
||||
.DS_Store
|
||||
.atom/
|
||||
.buildlog/
|
||||
.history
|
||||
.svn/
|
||||
|
||||
# IntelliJ related
|
||||
*.iml
|
||||
*.ipr
|
||||
*.iws
|
||||
.idea/
|
||||
|
||||
# The .vscode folder contains launch configuration and tasks you configure in
|
||||
# VS Code which you may wish to be included in version control, so this line
|
||||
# is commented out by default.
|
||||
#.vscode/
|
||||
|
||||
# Flutter/Dart/Pub related
|
||||
**/doc/api/
|
||||
**/ios/Flutter/.last_build_id
|
||||
.dart_tool/
|
||||
.flutter-plugins
|
||||
.flutter-plugins-dependencies
|
||||
.packages
|
||||
.pub-cache/
|
||||
.pub/
|
||||
/build/
|
||||
|
||||
# Web related
|
||||
lib/generated_plugin_registrant.dart
|
||||
|
||||
# Symbolication related
|
||||
app.*.symbols
|
||||
|
||||
# Obfuscation related
|
||||
app.*.map.json
|
||||
|
||||
# Android Studio will place build artifacts here
|
||||
/android/app/debug
|
||||
/android/app/profile
|
||||
/android/app/release
|
||||
|
||||
pubspec.lock
|
6
pkg/native_assets_builder/AUTHORS
Normal file
6
pkg/native_assets_builder/AUTHORS
Normal file
|
@ -0,0 +1,6 @@
|
|||
# Below is a list of people and organizations that have contributed
|
||||
# to the Dart project. Names should be added to the list like so:
|
||||
#
|
||||
# Name/Organization <email address>
|
||||
|
||||
Google LLC
|
27
pkg/native_assets_builder/LICENSE
Normal file
27
pkg/native_assets_builder/LICENSE
Normal file
|
@ -0,0 +1,27 @@
|
|||
Copyright 2022, the Dart project authors.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following
|
||||
disclaimer in the documentation and/or other materials provided
|
||||
with the distribution.
|
||||
* Neither the name of Google LLC nor the names of its
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
1
pkg/native_assets_builder/OWNERS
Normal file
1
pkg/native_assets_builder/OWNERS
Normal file
|
@ -0,0 +1 @@
|
|||
file:/tools/OWNERS_VM
|
7
pkg/native_assets_builder/README.md
Normal file
7
pkg/native_assets_builder/README.md
Normal file
|
@ -0,0 +1,7 @@
|
|||
This package contains the logic for building native assets.
|
||||
|
||||
This package is the backend that invokes toplevel `build.dart` scripts.
|
||||
For more info on these scripts see https://github.com/dart-lang/native.
|
||||
|
||||
This is a separate package so that dartdev and flutter_tools can reuse
|
||||
the same logic with flutter_tools having to import dartdev.
|
27
pkg/native_assets_builder/analysis_options.yaml
Normal file
27
pkg/native_assets_builder/analysis_options.yaml
Normal file
|
@ -0,0 +1,27 @@
|
|||
include: package:lints/recommended.yaml
|
||||
|
||||
analyzer:
|
||||
language:
|
||||
strict-raw-types: true
|
||||
strict-inference: true
|
||||
# The test projects do _not_ have resolved dependencies through
|
||||
# `dart tools/package_deps/bin/package_deps.dart`.
|
||||
# So don't analyze them in the IDE or on the CI.
|
||||
exclude:
|
||||
- test/test_projects/**.dart
|
||||
|
||||
linter:
|
||||
rules:
|
||||
- always_declare_return_types
|
||||
- avoid_dynamic_calls
|
||||
- camel_case_types
|
||||
- depend_on_referenced_packages
|
||||
- directives_ordering
|
||||
- prefer_const_declarations
|
||||
- prefer_expression_function_bodies
|
||||
- prefer_final_in_for_each
|
||||
- prefer_final_locals
|
||||
- prefer_relative_imports
|
||||
- prefer_single_quotes
|
||||
- sort_pub_dependencies
|
||||
- unawaited_futures
|
6
pkg/native_assets_builder/lib/native_assets_builder.dart
Normal file
6
pkg/native_assets_builder/lib/native_assets_builder.dart
Normal file
|
@ -0,0 +1,6 @@
|
|||
// Copyright (c) 2023, 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.
|
||||
|
||||
export 'package:native_assets_builder/src/build_runner/build_runner.dart';
|
||||
export 'package:native_assets_builder/src/package_layout/package_layout.dart';
|
|
@ -0,0 +1,102 @@
|
|||
// Copyright (c) 2023, 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:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:kernel/util/graph.dart' as graph;
|
||||
import 'package:package_config/package_config.dart';
|
||||
|
||||
class NativeAssetsBuildPlanner {
|
||||
final PackageGraph packageGraph;
|
||||
final List<Package> packagesWithNativeAssets;
|
||||
final Uri dartExecutable;
|
||||
|
||||
NativeAssetsBuildPlanner({
|
||||
required this.packageGraph,
|
||||
required this.packagesWithNativeAssets,
|
||||
required this.dartExecutable,
|
||||
});
|
||||
|
||||
static Future<NativeAssetsBuildPlanner> fromRootPackageRoot({
|
||||
required Uri rootPackageRoot,
|
||||
required List<Package> packagesWithNativeAssets,
|
||||
required Uri dartExecutable,
|
||||
}) async {
|
||||
final result = await Process.run(
|
||||
dartExecutable.toFilePath(),
|
||||
[
|
||||
'pub',
|
||||
'deps',
|
||||
'--json',
|
||||
],
|
||||
workingDirectory: rootPackageRoot.toFilePath(),
|
||||
);
|
||||
final packageGraph = PackageGraph.fromPubDepsJsonString(result.stdout);
|
||||
return NativeAssetsBuildPlanner(
|
||||
packageGraph: packageGraph,
|
||||
packagesWithNativeAssets: packagesWithNativeAssets,
|
||||
dartExecutable: dartExecutable,
|
||||
);
|
||||
}
|
||||
|
||||
List<Package> plan() {
|
||||
final packageMap = {
|
||||
for (final package in packagesWithNativeAssets) package.name: package
|
||||
};
|
||||
final packagesToBuild = packageMap.keys.toSet();
|
||||
final stronglyConnectedComponents = packageGraph.computeStrongComponents();
|
||||
final result = <Package>[];
|
||||
for (final stronglyConnectedComponent in stronglyConnectedComponents) {
|
||||
final stronglyConnectedComponentWithNativeAssets = [
|
||||
for (final packageName in stronglyConnectedComponent)
|
||||
if (packagesToBuild.contains(packageName)) packageName
|
||||
];
|
||||
if (stronglyConnectedComponentWithNativeAssets.length > 1) {
|
||||
throw Exception(
|
||||
'Cyclic dependency for native asset builds in the following '
|
||||
'packages: $stronglyConnectedComponent.',
|
||||
);
|
||||
} else if (stronglyConnectedComponentWithNativeAssets.length == 1) {
|
||||
result.add(
|
||||
packageMap[stronglyConnectedComponentWithNativeAssets.single]!);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
class PackageGraph implements graph.Graph<String> {
|
||||
final Map<String, List<String>> map;
|
||||
|
||||
PackageGraph(this.map);
|
||||
|
||||
/// Construct a graph from the JSON produced by `dart pub deps --json`.
|
||||
factory PackageGraph.fromPubDepsJsonString(String json) =>
|
||||
PackageGraph.fromPubDepsJson(jsonDecode(json) as Map<dynamic, dynamic>);
|
||||
|
||||
/// Construct a graph from the JSON produced by `dart pub deps --json`.
|
||||
factory PackageGraph.fromPubDepsJson(Map<dynamic, dynamic> map) {
|
||||
final result = <String, List<String>>{};
|
||||
final packages = map['packages'] as List<dynamic>;
|
||||
for (final package in packages) {
|
||||
final package_ = package as Map<dynamic, dynamic>;
|
||||
final name = package_['name'] as String;
|
||||
final dependencies = (package_['dependencies'] as List<dynamic>)
|
||||
.whereType<String>()
|
||||
.toList();
|
||||
result[name] = dependencies;
|
||||
}
|
||||
return PackageGraph(result);
|
||||
}
|
||||
|
||||
@override
|
||||
Iterable<String> neighborsOf(String vertex) => map[vertex] ?? [];
|
||||
|
||||
@override
|
||||
Iterable<String> get vertices => map.keys;
|
||||
|
||||
List<List<String>> computeStrongComponents() =>
|
||||
graph.computeStrongComponents(this);
|
||||
}
|
220
pkg/native_assets_builder/lib/src/build_runner/build_runner.dart
Normal file
220
pkg/native_assets_builder/lib/src/build_runner/build_runner.dart
Normal file
|
@ -0,0 +1,220 @@
|
|||
// Copyright (c) 2023, 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:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:native_assets_cli/native_assets_cli.dart';
|
||||
|
||||
import '../package_layout/package_layout.dart';
|
||||
import '../utils/run_process.dart';
|
||||
import 'build_planner.dart';
|
||||
|
||||
typedef DependencyMetadata = Map<String, Metadata>;
|
||||
|
||||
/// The programmatic API to be used by Dart launchers to invoke native builds.
|
||||
///
|
||||
/// These methods are invoked by launchers such as dartdev (for `dart run`)
|
||||
/// and flutter_tools (for `flutter run` and `flutter build`).
|
||||
class NativeAssetsBuildRunner {
|
||||
final Logger logger;
|
||||
final Uri dartExecutable;
|
||||
|
||||
NativeAssetsBuildRunner({
|
||||
required this.logger,
|
||||
required this.dartExecutable,
|
||||
});
|
||||
|
||||
final _metadata = <Target, DependencyMetadata>{};
|
||||
|
||||
/// [workingDirectory] is expected to contain `.dart_tool`.
|
||||
///
|
||||
/// This method is invoked by launchers such as dartdev (for `dart run`) and
|
||||
/// flutter_tools (for `flutter run` and `flutter build`).
|
||||
///
|
||||
/// Completes the future with an error if the build fails.
|
||||
Future<List<Asset>> build({
|
||||
required LinkModePreference linkModePreference,
|
||||
required Target target,
|
||||
required Uri workingDirectory,
|
||||
CCompilerConfig? cCompilerConfig,
|
||||
required bool includeParentEnvironment,
|
||||
}) async {
|
||||
assert(_metadata.isEmpty);
|
||||
final packageLayout =
|
||||
await PackageLayout.fromRootPackageRoot(workingDirectory);
|
||||
final packagesWithNativeAssets =
|
||||
await packageLayout.packagesWithNativeAssets;
|
||||
final planner = await NativeAssetsBuildPlanner.fromRootPackageRoot(
|
||||
rootPackageRoot: packageLayout.rootPackageRoot,
|
||||
packagesWithNativeAssets: packagesWithNativeAssets,
|
||||
dartExecutable: Uri.file(Platform.resolvedExecutable),
|
||||
);
|
||||
final plan = planner.plan();
|
||||
final assetList = <Asset>[];
|
||||
for (final package in plan) {
|
||||
final dependencyMetadata = _metadataForPackage(
|
||||
packageGraph: planner.packageGraph,
|
||||
packageName: package.name,
|
||||
targetMetadata: _metadata[target],
|
||||
);
|
||||
final config = await _cliConfig(
|
||||
packageName: package.name,
|
||||
packageRoot: packageLayout.packageRoot(package.name),
|
||||
target: target,
|
||||
linkMode: linkModePreference,
|
||||
buildParentDir: packageLayout.dartToolNativeAssetsBuilder,
|
||||
dependencyMetadata: dependencyMetadata,
|
||||
cCompilerConfig: cCompilerConfig,
|
||||
);
|
||||
final assets = await _buildPackageCached(
|
||||
config,
|
||||
packageLayout.packageConfigUri,
|
||||
workingDirectory,
|
||||
includeParentEnvironment,
|
||||
);
|
||||
assetList.addAll(assets);
|
||||
}
|
||||
return assetList;
|
||||
}
|
||||
|
||||
Future<List<Asset>> _buildPackageCached(
|
||||
BuildConfig config,
|
||||
Uri packageConfigUri,
|
||||
Uri workingDirectory,
|
||||
bool includeParentEnvironment,
|
||||
) async {
|
||||
final packageName = config.packageName;
|
||||
final outDir = config.outDir;
|
||||
if (!await Directory.fromUri(outDir).exists()) {
|
||||
await Directory.fromUri(outDir).create(recursive: true);
|
||||
}
|
||||
|
||||
final buildOutput = await BuildOutput.readFromFile(outDir: outDir);
|
||||
final lastBuilt = buildOutput?.timestamp.roundDownToSeconds() ??
|
||||
DateTime.fromMillisecondsSinceEpoch(0);
|
||||
final dependencies = buildOutput?.dependencies;
|
||||
final lastChange = await dependencies?.lastModified() ?? DateTime.now();
|
||||
|
||||
if (lastBuilt.isAfter(lastChange)) {
|
||||
logger.info('Skipping build for $packageName in $outDir. '
|
||||
'Last build on $lastBuilt, last input change on $lastChange.');
|
||||
// All build flags go into [outDir]. Therefore we do not have to check
|
||||
// here whether the config is equal.
|
||||
|
||||
setMetadata(config.target, packageName, buildOutput?.metadata);
|
||||
return buildOutput!.assets;
|
||||
}
|
||||
|
||||
return _buildPackage(
|
||||
config,
|
||||
packageConfigUri,
|
||||
workingDirectory,
|
||||
includeParentEnvironment,
|
||||
);
|
||||
}
|
||||
|
||||
Future<List<Asset>> _buildPackage(
|
||||
BuildConfig config,
|
||||
Uri packageConfigUri,
|
||||
Uri workingDirectory,
|
||||
bool includeParentEnvironment,
|
||||
) async {
|
||||
final outDir = config.outDir;
|
||||
final configFile = outDir.resolve('config.yaml');
|
||||
final buildDotDart = config.packageRoot.resolve('build.dart');
|
||||
final configFileContents = config.toYamlString();
|
||||
logger.info('config.yaml contents: $configFileContents');
|
||||
await File.fromUri(configFile).writeAsString(configFileContents);
|
||||
final buildOutputFile = File.fromUri(outDir.resolve(BuildOutput.fileName));
|
||||
if (await buildOutputFile.exists()) {
|
||||
// Ensure we'll never read outdated build results.
|
||||
await buildOutputFile.delete();
|
||||
}
|
||||
await runProcess(
|
||||
workingDirectory: workingDirectory,
|
||||
executable: dartExecutable.toFilePath(),
|
||||
arguments: [
|
||||
'--packages=${packageConfigUri.toFilePath()}',
|
||||
buildDotDart.toFilePath(),
|
||||
'--config=${configFile.toFilePath()}',
|
||||
],
|
||||
logger: logger,
|
||||
includeParentEnvironment: includeParentEnvironment,
|
||||
);
|
||||
final buildOutput = await BuildOutput.readFromFile(outDir: outDir);
|
||||
setMetadata(config.target, config.packageName, buildOutput?.metadata);
|
||||
return buildOutput?.assets ?? [];
|
||||
}
|
||||
|
||||
void setMetadata(Target target, String packageName, Metadata? metadata) {
|
||||
if (metadata == null) {
|
||||
return;
|
||||
}
|
||||
_metadata[target] ??= {};
|
||||
_metadata[target]![packageName] = metadata;
|
||||
}
|
||||
|
||||
static Future<BuildConfig> _cliConfig({
|
||||
required String packageName,
|
||||
required Uri packageRoot,
|
||||
required Target target,
|
||||
IOSSdk? targetIOSSdk,
|
||||
required LinkModePreference linkMode,
|
||||
required Uri buildParentDir,
|
||||
CCompilerConfig? cCompilerConfig,
|
||||
DependencyMetadata? dependencyMetadata,
|
||||
}) async {
|
||||
final buildDirName = BuildConfig.checksum(
|
||||
packageRoot: packageRoot,
|
||||
target: target,
|
||||
linkModePreference: linkMode,
|
||||
targetIOSSdk: targetIOSSdk,
|
||||
cCompiler: cCompilerConfig,
|
||||
dependencyMetadata: dependencyMetadata,
|
||||
);
|
||||
final outDirUri = buildParentDir.resolve('$buildDirName/');
|
||||
final outDir = Directory.fromUri(outDirUri);
|
||||
if (!await outDir.exists()) {
|
||||
// TODO(https://dartbug.com/50565): Purge old or unused folders.
|
||||
await outDir.create(recursive: true);
|
||||
}
|
||||
return BuildConfig(
|
||||
outDir: outDirUri,
|
||||
packageRoot: packageRoot,
|
||||
target: target,
|
||||
linkModePreference: linkMode,
|
||||
targetIOSSdk: targetIOSSdk,
|
||||
cCompiler: cCompilerConfig,
|
||||
dependencyMetadata: dependencyMetadata,
|
||||
);
|
||||
}
|
||||
|
||||
DependencyMetadata? _metadataForPackage({
|
||||
required PackageGraph packageGraph,
|
||||
required String packageName,
|
||||
DependencyMetadata? targetMetadata,
|
||||
}) {
|
||||
if (targetMetadata == null) {
|
||||
return null;
|
||||
}
|
||||
final dependencies = packageGraph.neighborsOf(packageName).toSet();
|
||||
return {
|
||||
for (final entry in targetMetadata.entries)
|
||||
if (dependencies.contains(entry.key)) entry.key: entry.value,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
extension on DateTime {
|
||||
DateTime roundDownToSeconds() =>
|
||||
DateTime.fromMillisecondsSinceEpoch(millisecondsSinceEpoch -
|
||||
millisecondsSinceEpoch % Duration(seconds: 1).inMilliseconds);
|
||||
}
|
||||
|
||||
extension on BuildConfig {
|
||||
String get packageName =>
|
||||
packageRoot.pathSegments.lastWhere((e) => e.isNotEmpty);
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
// Copyright (c) 2023, 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:package_config/package_config.dart';
|
||||
|
||||
/// Directory layout for dealing with native assets.
|
||||
///
|
||||
/// Build scripts for native assets will be run from the context of another
|
||||
/// root package.
|
||||
///
|
||||
/// The directory layout follows pub's convention for caching:
|
||||
/// https://dart.dev/tools/pub/package-layout#project-specific-caching-for-tools
|
||||
class PackageLayout {
|
||||
/// The root folder of the current dart invocation root package.
|
||||
///
|
||||
/// `$rootPackageRoot`.
|
||||
final Uri rootPackageRoot;
|
||||
|
||||
/// Package config containing the information of where to foot the root [Uri]s
|
||||
/// of other packages.
|
||||
///
|
||||
/// Can be `null` to enable quick construction of a
|
||||
/// [PackageLayout].
|
||||
final PackageConfig packageConfig;
|
||||
|
||||
final Uri packageConfigUri;
|
||||
|
||||
PackageLayout._(
|
||||
this.rootPackageRoot, this.packageConfig, this.packageConfigUri);
|
||||
|
||||
static Future<PackageLayout> fromRootPackageRoot(Uri rootPackageRoot) async {
|
||||
rootPackageRoot = rootPackageRoot.normalizePath();
|
||||
final packageConfigUri =
|
||||
rootPackageRoot.resolve('.dart_tool/package_config.json');
|
||||
assert(await File.fromUri(packageConfigUri).exists());
|
||||
final packageConfig = await loadPackageConfigUri(packageConfigUri);
|
||||
return PackageLayout._(rootPackageRoot, packageConfig, packageConfigUri);
|
||||
}
|
||||
|
||||
/// The .dart_tool directory is used to store built artifacts and caches.
|
||||
///
|
||||
/// `$rootPackageRoot/.dart_tool/`.
|
||||
///
|
||||
/// Each package should only modify the subfolder of `.dart_tool/` with its
|
||||
/// own name.
|
||||
/// https://dart.dev/tools/pub/package-layout#project-specific-caching-for-tools
|
||||
late final Uri dartTool = rootPackageRoot.resolve('.dart_tool/');
|
||||
|
||||
/// The directory where `package:native_assets_builder` stores all persistent
|
||||
/// information.
|
||||
///
|
||||
/// This folder is owned by `package:native_assets_builder`, no other package
|
||||
/// should read or modify it.
|
||||
/// https://dart.dev/tools/pub/package-layout#project-specific-caching-for-tools
|
||||
///
|
||||
/// `$rootPackageRoot/.dart_tool/native_assets_builder/`.
|
||||
late final Uri dartToolNativeAssetsBuilder =
|
||||
dartTool.resolve('native_assets_builder/');
|
||||
|
||||
/// The root of `package:$packageName`.
|
||||
///
|
||||
/// `$packageName/`.
|
||||
///
|
||||
/// This folder is owned by pub, and should _never_ be written to.
|
||||
Uri packageRoot(String packageName) {
|
||||
final package = packageConfig[packageName];
|
||||
if (package == null) {
|
||||
throw StateError('Package $packageName not found in packageConfig.');
|
||||
}
|
||||
return package.root;
|
||||
}
|
||||
|
||||
/// All packages in [packageConfig] with native assets.
|
||||
///
|
||||
/// Whether a package has native assets is defined by whether it contains
|
||||
/// a `build.dart`.
|
||||
///
|
||||
/// `package:native` itself is excluded.
|
||||
late final Future<List<Package>> packagesWithNativeAssets = () async {
|
||||
final result = <Package>[];
|
||||
for (final package in packageConfig.packages) {
|
||||
final packageRoot = package.root;
|
||||
if (packageRoot.scheme == 'file') {
|
||||
if (await File.fromUri(packageRoot.resolve('build.dart')).exists()) {
|
||||
result.add(package);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}();
|
||||
}
|
66
pkg/native_assets_builder/lib/src/utils/file.dart
Normal file
66
pkg/native_assets_builder/lib/src/utils/file.dart
Normal file
|
@ -0,0 +1,66 @@
|
|||
// Copyright (c) 2023, 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:convert';
|
||||
import 'dart:io';
|
||||
|
||||
extension FileExtension on File {
|
||||
Future<File> writeAsStringCreateDirectory(String contents,
|
||||
{FileMode mode = FileMode.write,
|
||||
Encoding encoding = utf8,
|
||||
bool flush = false}) async {
|
||||
if (!await parent.exists()) {
|
||||
await parent.create(recursive: true);
|
||||
}
|
||||
return await writeAsString(contents,
|
||||
mode: mode, encoding: encoding, flush: flush);
|
||||
}
|
||||
}
|
||||
|
||||
extension FileSystemEntityExtension on FileSystemEntity {
|
||||
Future<DateTime> lastModified() async {
|
||||
final this_ = this;
|
||||
if (this_ is Link || await FileSystemEntity.isLink(this_.path)) {
|
||||
// Don't follow links.
|
||||
return DateTime.fromMicrosecondsSinceEpoch(0);
|
||||
}
|
||||
if (this_ is File) {
|
||||
if (!await this_.exists()) {
|
||||
// If the file was deleted, regard it is modified recently.
|
||||
return DateTime.now();
|
||||
}
|
||||
return await this_.lastModified();
|
||||
}
|
||||
if (this_ is Directory) {
|
||||
return await this_.lastModified();
|
||||
}
|
||||
throw Exception('Unknown FileSystemEntity $runtimeType');
|
||||
}
|
||||
}
|
||||
|
||||
extension FileSystemEntityIterable on Iterable<FileSystemEntity> {
|
||||
Future<DateTime> lastModified() async {
|
||||
var last = DateTime.fromMillisecondsSinceEpoch(0);
|
||||
for (final entity in this) {
|
||||
final entityTimestamp = await entity.lastModified();
|
||||
if (entityTimestamp.isAfter(last)) {
|
||||
last = entityTimestamp;
|
||||
}
|
||||
}
|
||||
return last;
|
||||
}
|
||||
}
|
||||
|
||||
extension DirectoryExtension on Directory {
|
||||
Future<DateTime> lastModified() async {
|
||||
var last = DateTime.fromMillisecondsSinceEpoch(0);
|
||||
await for (final entity in list()) {
|
||||
final entityTimestamp = await entity.lastModified();
|
||||
if (entityTimestamp.isAfter(last)) {
|
||||
last = entityTimestamp;
|
||||
}
|
||||
}
|
||||
return last;
|
||||
}
|
||||
}
|
141
pkg/native_assets_builder/lib/src/utils/run_process.dart
Normal file
141
pkg/native_assets_builder/lib/src/utils/run_process.dart
Normal file
|
@ -0,0 +1,141 @@
|
|||
// Copyright (c) 2023, 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:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:logging/logging.dart';
|
||||
|
||||
/// Runs a process async and captures the exit code and standard out.
|
||||
///
|
||||
/// Supports streaming output using a [Logger].
|
||||
Future<RunProcessResult> runProcess({
|
||||
required String executable,
|
||||
required List<String> arguments,
|
||||
Uri? workingDirectory,
|
||||
Map<String, String>? environment,
|
||||
bool includeParentEnvironment = true,
|
||||
bool throwOnFailure = true,
|
||||
required Logger logger,
|
||||
}) async {
|
||||
if (Platform.isWindows && !includeParentEnvironment) {
|
||||
const winEnvKeys = [
|
||||
'SYSTEMROOT',
|
||||
];
|
||||
final newEnvironment = {
|
||||
...{
|
||||
for (final winEnvKey in winEnvKeys)
|
||||
winEnvKey: Platform.environment[winEnvKey]!,
|
||||
},
|
||||
if (environment != null) ...environment,
|
||||
};
|
||||
environment = newEnvironment;
|
||||
}
|
||||
|
||||
final printWorkingDir =
|
||||
workingDirectory != null && workingDirectory != Directory.current.uri;
|
||||
final commandString = [
|
||||
if (printWorkingDir) '(cd ${workingDirectory.toFilePath()};',
|
||||
...?environment?.entries.map((entry) => '${entry.key}=${entry.value}'),
|
||||
executable,
|
||||
...arguments.map((a) => a.contains(' ') ? "'$a'" : a),
|
||||
if (printWorkingDir) ')',
|
||||
].join(' ');
|
||||
logger.info('Running `$commandString`.');
|
||||
|
||||
final stdoutBuffer = <String>[];
|
||||
final stderrBuffer = <String>[];
|
||||
final stdoutCompleter = Completer<Object?>();
|
||||
final stderrCompleter = Completer<Object?>();
|
||||
final Process process = await Process.start(
|
||||
executable,
|
||||
arguments,
|
||||
workingDirectory: workingDirectory?.toFilePath(),
|
||||
environment: environment,
|
||||
includeParentEnvironment: includeParentEnvironment,
|
||||
runInShell: Platform.isWindows && !includeParentEnvironment,
|
||||
);
|
||||
|
||||
process.stdout.transform(utf8.decoder).transform(const LineSplitter()).listen(
|
||||
(s) {
|
||||
logger.fine(s);
|
||||
stdoutBuffer.add(s);
|
||||
},
|
||||
onDone: stdoutCompleter.complete,
|
||||
);
|
||||
process.stderr.transform(utf8.decoder).transform(const LineSplitter()).listen(
|
||||
(s) {
|
||||
logger.severe(s);
|
||||
stderrBuffer.add(s);
|
||||
},
|
||||
onDone: stderrCompleter.complete,
|
||||
);
|
||||
|
||||
final int exitCode = await process.exitCode;
|
||||
await stdoutCompleter.future;
|
||||
final String stdout = stdoutBuffer.join();
|
||||
await stderrCompleter.future;
|
||||
final String stderr = stderrBuffer.join();
|
||||
final result = RunProcessResult(
|
||||
pid: process.pid,
|
||||
command: '$executable ${arguments.join(' ')}',
|
||||
exitCode: exitCode,
|
||||
stdout: stdout,
|
||||
stderr: stderr,
|
||||
);
|
||||
if (throwOnFailure && result.exitCode != 0) {
|
||||
throw ProcessInvocationException(result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
class RunProcessResult extends ProcessResult {
|
||||
final String command;
|
||||
|
||||
final int _exitCode;
|
||||
|
||||
// For some reason super.exitCode returns 0.
|
||||
@override
|
||||
int get exitCode => _exitCode;
|
||||
|
||||
final String _stderrString;
|
||||
|
||||
@override
|
||||
String get stderr => _stderrString;
|
||||
|
||||
final String _stdoutString;
|
||||
|
||||
@override
|
||||
String get stdout => _stdoutString;
|
||||
|
||||
RunProcessResult({
|
||||
required int pid,
|
||||
required this.command,
|
||||
required int exitCode,
|
||||
required String stderr,
|
||||
required String stdout,
|
||||
}) : _exitCode = exitCode,
|
||||
_stderrString = stderr,
|
||||
_stdoutString = stdout,
|
||||
super(pid, exitCode, stdout, stderr);
|
||||
|
||||
@override
|
||||
String toString() => '''command: $command
|
||||
exitCode: $exitCode
|
||||
stdout: $stdout
|
||||
stderr: $stderr''';
|
||||
}
|
||||
|
||||
class ProcessInvocationException implements Exception {
|
||||
final RunProcessResult runProcessResult;
|
||||
|
||||
ProcessInvocationException(this.runProcessResult);
|
||||
|
||||
String get message => '''A process run failed.
|
||||
$runProcessResult''';
|
||||
|
||||
@override
|
||||
String toString() => message;
|
||||
}
|
21
pkg/native_assets_builder/pubspec.yaml
Normal file
21
pkg/native_assets_builder/pubspec.yaml
Normal file
|
@ -0,0 +1,21 @@
|
|||
name: native_assets_builder
|
||||
version: 0.0.1
|
||||
|
||||
# This package is not intended for consumption on pub.dev. DO NOT publish.
|
||||
publish_to: none
|
||||
|
||||
environment:
|
||||
sdk: ">=2.17.0 <3.0.0"
|
||||
|
||||
# Use 'any' constraints here; we get our versions from the DEPS file.
|
||||
dependencies:
|
||||
kernel: any # For Graph topological sort.
|
||||
logging: any
|
||||
native_assets_cli: any
|
||||
package_config: any
|
||||
|
||||
# Use 'any' constraints here; we get our versions from the DEPS file.
|
||||
dev_dependencies:
|
||||
lints: any
|
||||
test: any
|
||||
yaml: any
|
|
@ -0,0 +1,41 @@
|
|||
// Copyright (c) 2023, 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:test/test.dart';
|
||||
|
||||
import '../helpers.dart';
|
||||
import 'helpers.dart';
|
||||
|
||||
const Timeout longTimeout = Timeout(Duration(minutes: 5));
|
||||
|
||||
void main(List<String> args) async {
|
||||
test('dart_app build dependencies', timeout: longTimeout, () async {
|
||||
await inTempDir((tempUri) async {
|
||||
await copyTestProjects(targetUri: tempUri);
|
||||
final packageUri = tempUri.resolve('dart_app/');
|
||||
|
||||
// First, run `pub get`, we need pub to resolve our dependencies.
|
||||
await runPubGet(
|
||||
workingDirectory: packageUri,
|
||||
logger: logger,
|
||||
);
|
||||
|
||||
// Trigger a build, should invoke build for libraries with native assets.
|
||||
{
|
||||
final logMessages = <String>[];
|
||||
final assets = await build(packageUri, logger, dartExecutable,
|
||||
capturedLogs: logMessages);
|
||||
expect(
|
||||
logMessages.join('\n'),
|
||||
stringContainsInOrder([
|
||||
'native_add${Platform.pathSeparator}build.dart',
|
||||
'native_subtract${Platform.pathSeparator}build.dart'
|
||||
]));
|
||||
expect(assets.length, 2);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
// Copyright (c) 2023, 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:native_assets_builder/native_assets_builder.dart';
|
||||
import 'package:native_assets_builder/src/build_runner/build_planner.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import '../helpers.dart';
|
||||
import 'helpers.dart';
|
||||
|
||||
void main() async {
|
||||
test('build dependency graph from pub', () async {
|
||||
await inTempDir((tempUri) async {
|
||||
await copyTestProjects(targetUri: tempUri);
|
||||
final nativeAddUri = tempUri.resolve('native_add/');
|
||||
|
||||
// First, run `pub get`, we need pub to resolve our dependencies.
|
||||
await runPubGet(workingDirectory: nativeAddUri, logger: logger);
|
||||
|
||||
final result = await runProcess(
|
||||
executable: Platform.resolvedExecutable,
|
||||
arguments: [
|
||||
'pub',
|
||||
'deps',
|
||||
'--json',
|
||||
],
|
||||
workingDirectory: nativeAddUri,
|
||||
logger: logger,
|
||||
);
|
||||
expect(result.exitCode, 0);
|
||||
|
||||
final graph = PackageGraph.fromPubDepsJsonString(result.stdout);
|
||||
|
||||
final packageLayout =
|
||||
await PackageLayout.fromRootPackageRoot(nativeAddUri);
|
||||
final packagesWithNativeAssets =
|
||||
await packageLayout.packagesWithNativeAssets;
|
||||
|
||||
final planner = NativeAssetsBuildPlanner(
|
||||
packageGraph: graph,
|
||||
packagesWithNativeAssets: packagesWithNativeAssets,
|
||||
dartExecutable: Uri.file(Platform.resolvedExecutable),
|
||||
);
|
||||
final buildPlan = planner.plan();
|
||||
expect(buildPlan.length, 1);
|
||||
expect(buildPlan.single.name, 'native_add');
|
||||
});
|
||||
});
|
||||
test('build dependency graph fromPackageRoot', () async {
|
||||
await inTempDir((tempUri) async {
|
||||
await copyTestProjects(targetUri: tempUri);
|
||||
final nativeAddUri = tempUri.resolve('native_add/');
|
||||
|
||||
// First, run `pub get`, we need pub to resolve our dependencies.
|
||||
await runPubGet(workingDirectory: nativeAddUri, logger: logger);
|
||||
|
||||
final packageLayout =
|
||||
await PackageLayout.fromRootPackageRoot(nativeAddUri);
|
||||
final packagesWithNativeAssets =
|
||||
await packageLayout.packagesWithNativeAssets;
|
||||
final buildPlan = (await NativeAssetsBuildPlanner.fromRootPackageRoot(
|
||||
rootPackageRoot: nativeAddUri,
|
||||
packagesWithNativeAssets: packagesWithNativeAssets,
|
||||
dartExecutable: Uri.file(Platform.resolvedExecutable),
|
||||
))
|
||||
.plan();
|
||||
expect(buildPlan.length, 1);
|
||||
expect(buildPlan.single.name, 'native_add');
|
||||
});
|
||||
});
|
||||
}
|
|
@ -0,0 +1,99 @@
|
|||
// Copyright (c) 2023, 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:test/test.dart';
|
||||
|
||||
import '../helpers.dart';
|
||||
import 'helpers.dart';
|
||||
|
||||
const Timeout longTimeout = Timeout(Duration(minutes: 5));
|
||||
|
||||
void main(List<String> args) async {
|
||||
test('cached build', timeout: longTimeout, () async {
|
||||
await inTempDir((tempUri) async {
|
||||
await copyTestProjects(targetUri: tempUri);
|
||||
final packageUri = tempUri.resolve('native_add/');
|
||||
|
||||
await runPubGet(
|
||||
workingDirectory: packageUri,
|
||||
logger: logger,
|
||||
);
|
||||
|
||||
{
|
||||
final logMessages = <String>[];
|
||||
await build(packageUri, logger, dartExecutable,
|
||||
capturedLogs: logMessages);
|
||||
expect(
|
||||
logMessages.join('\n'),
|
||||
stringContainsInOrder(
|
||||
['native_add${Platform.pathSeparator}build.dart']));
|
||||
}
|
||||
|
||||
{
|
||||
final logMessages = <String>[];
|
||||
await build(packageUri, logger, dartExecutable,
|
||||
capturedLogs: logMessages);
|
||||
expect(logMessages.join('\n'),
|
||||
stringContainsInOrder(['Skipping build for native_add']));
|
||||
expect(
|
||||
false,
|
||||
logMessages
|
||||
.join('\n')
|
||||
.contains('native_add${Platform.pathSeparator}build.dart'));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
test('modify C file', timeout: longTimeout, () async {
|
||||
await inTempDir((tempUri) async {
|
||||
await copyTestProjects(targetUri: tempUri);
|
||||
final packageUri = tempUri.resolve('native_add/');
|
||||
|
||||
await runPubGet(
|
||||
workingDirectory: packageUri,
|
||||
logger: logger,
|
||||
);
|
||||
|
||||
{
|
||||
final assets = await build(packageUri, logger, dartExecutable);
|
||||
await expectSymbols(asset: assets.single, symbols: ['add']);
|
||||
}
|
||||
|
||||
await copyTestProjects(
|
||||
sourceUri: testProjectsUri.resolve('native_add_add_symbol/'),
|
||||
targetUri: packageUri,
|
||||
);
|
||||
|
||||
{
|
||||
final assets = await build(packageUri, logger, dartExecutable);
|
||||
await expectSymbols(asset: assets.single, symbols: ['add', 'subtract']);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
test('add C file, modify script', timeout: longTimeout, () async {
|
||||
await inTempDir((tempUri) async {
|
||||
await copyTestProjects(targetUri: tempUri);
|
||||
final packageUri = tempUri.resolve('native_add/');
|
||||
|
||||
await runPubGet(workingDirectory: packageUri, logger: logger);
|
||||
|
||||
{
|
||||
final assets = await build(packageUri, logger, dartExecutable);
|
||||
await expectSymbols(asset: assets.single, symbols: ['add']);
|
||||
}
|
||||
|
||||
await copyTestProjects(
|
||||
sourceUri: testProjectsUri.resolve('native_add_add_source/'),
|
||||
targetUri: packageUri);
|
||||
|
||||
{
|
||||
final assets = await build(packageUri, logger, dartExecutable);
|
||||
await expectSymbols(asset: assets.single, symbols: ['add', 'multiply']);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
// Copyright (c) 2023, 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:native_assets_cli/native_assets_cli.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import '../helpers.dart';
|
||||
import 'helpers.dart';
|
||||
|
||||
const Timeout longTimeout = Timeout(Duration(minutes: 5));
|
||||
|
||||
void main(List<String> args) async {
|
||||
test('break build', timeout: longTimeout, () async {
|
||||
await inTempDir((tempUri) async {
|
||||
await copyTestProjects(targetUri: tempUri);
|
||||
final packageUri = tempUri.resolve('native_add/');
|
||||
|
||||
await runPubGet(
|
||||
workingDirectory: packageUri,
|
||||
logger: logger,
|
||||
);
|
||||
|
||||
{
|
||||
final assets = await build(packageUri, logger, dartExecutable);
|
||||
expect(assets.length, 1);
|
||||
await expectSymbols(asset: assets.single, symbols: ['add']);
|
||||
}
|
||||
|
||||
await copyTestProjects(
|
||||
sourceUri: testProjectsUri.resolve('native_add_break_build/'),
|
||||
targetUri: packageUri,
|
||||
);
|
||||
|
||||
{
|
||||
bool buildFailed = false;
|
||||
final assets = await build(packageUri, logger, dartExecutable)
|
||||
.onError((error, stackTrace) {
|
||||
buildFailed = true;
|
||||
return [];
|
||||
});
|
||||
expect(buildFailed, true);
|
||||
expect(assets, <Asset>[]);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
// Copyright (c) 2023, 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:native_assets_cli/native_assets_cli.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import '../helpers.dart';
|
||||
import 'helpers.dart';
|
||||
|
||||
const Timeout longTimeout = Timeout(Duration(minutes: 5));
|
||||
|
||||
void main(List<String> args) async {
|
||||
if (Platform.isMacOS) {
|
||||
// We don't set any compiler paths on MacOS in
|
||||
// pkg/test_runner/lib/src/configuration.dart
|
||||
// nativeCompilerEnvironmentVariables.
|
||||
return;
|
||||
}
|
||||
|
||||
test('run in isolation', timeout: longTimeout, () async {
|
||||
await inTempDir((tempUri) async {
|
||||
String unparseKey(String key) => key.replaceAll('.', '__').toUpperCase();
|
||||
final arKey = unparseKey(CCompilerConfig.arConfigKeyFull);
|
||||
final ccKey = unparseKey(CCompilerConfig.ccConfigKeyFull);
|
||||
final ldKey = unparseKey(CCompilerConfig.ldConfigKeyFull);
|
||||
final envScriptKey = unparseKey(CCompilerConfig.envScriptConfigKeyFull);
|
||||
final envScriptArgsKey =
|
||||
unparseKey(CCompilerConfig.envScriptArgsConfigKeyFull);
|
||||
|
||||
await copyTestProjects(targetUri: tempUri);
|
||||
final packageUri = tempUri.resolve('native_add/');
|
||||
|
||||
await runPubGet(workingDirectory: packageUri, logger: logger);
|
||||
|
||||
final cc = Platform.environment[ccKey]?.fileUri;
|
||||
printOnFailure(
|
||||
'Platform.environment[ccKey]: ${Platform.environment[ccKey]}');
|
||||
printOnFailure('cc: $cc');
|
||||
|
||||
final assets = await build(
|
||||
packageUri,
|
||||
logger,
|
||||
dartExecutable,
|
||||
// Manually pass in a compiler.
|
||||
cCompilerConfig: CCompilerConfig(
|
||||
ar: Platform.environment[arKey]?.fileUri,
|
||||
cc: cc,
|
||||
envScript: Platform.environment[envScriptKey]?.fileUri,
|
||||
envScriptArgs: Platform.environment[envScriptArgsKey]?.split(' '),
|
||||
ld: Platform.environment[ldKey]?.fileUri,
|
||||
),
|
||||
// Prevent any other environment variables.
|
||||
includeParentEnvironment: false,
|
||||
);
|
||||
expect(assets.length, 1);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
extension on String {
|
||||
Uri get fileUri => Uri.file(this);
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
// Copyright (c) 2023, 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:test/test.dart';
|
||||
|
||||
import '../helpers.dart';
|
||||
import 'helpers.dart';
|
||||
|
||||
const Timeout longTimeout = Timeout(Duration(minutes: 5));
|
||||
|
||||
void main(List<String> args) async {
|
||||
test('native_add build', timeout: longTimeout, () async {
|
||||
await inTempDir((tempUri) async {
|
||||
await copyTestProjects(targetUri: tempUri);
|
||||
final packageUri = tempUri.resolve('native_add/');
|
||||
|
||||
// First, run `pub get`, we need pub to resolve our dependencies.
|
||||
await runPubGet(
|
||||
workingDirectory: packageUri,
|
||||
logger: logger,
|
||||
);
|
||||
|
||||
// Trigger a build, should invoke build for libraries with native assets.
|
||||
{
|
||||
final logMessages = <String>[];
|
||||
final assets = await build(packageUri, logger, dartExecutable,
|
||||
capturedLogs: logMessages);
|
||||
expect(
|
||||
logMessages.join('\n'),
|
||||
stringContainsInOrder(
|
||||
['native_add${Platform.pathSeparator}build.dart']));
|
||||
expect(assets.length, 1);
|
||||
}
|
||||
|
||||
// Trigger a build, should not invoke anything.
|
||||
{
|
||||
final logMessages = <String>[];
|
||||
final assets = await build(packageUri, logger, dartExecutable,
|
||||
capturedLogs: logMessages);
|
||||
expect(
|
||||
false,
|
||||
logMessages
|
||||
.join('\n')
|
||||
.contains('native_add${Platform.pathSeparator}build.dart'));
|
||||
expect(assets.length, 1);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
94
pkg/native_assets_builder/test/build_runner/helpers.dart
Normal file
94
pkg/native_assets_builder/test/build_runner/helpers.dart
Normal file
|
@ -0,0 +1,94 @@
|
|||
// Copyright (c) 2023, 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:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:native_assets_builder/native_assets_builder.dart';
|
||||
import 'package:native_assets_cli/native_assets_cli.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import '../helpers.dart';
|
||||
|
||||
Future<void> runPubGet({
|
||||
required Uri workingDirectory,
|
||||
required Logger logger,
|
||||
}) async {
|
||||
final result = await runProcess(
|
||||
executable: Platform.resolvedExecutable,
|
||||
arguments: ['pub', 'get'],
|
||||
workingDirectory: workingDirectory,
|
||||
logger: logger,
|
||||
);
|
||||
expect(result.exitCode, 0);
|
||||
}
|
||||
|
||||
Future<List<Asset>> build(
|
||||
Uri packageUri,
|
||||
Logger logger,
|
||||
Uri dartExecutable, {
|
||||
LinkModePreference linkModePreference = LinkModePreference.dynamic,
|
||||
CCompilerConfig? cCompilerConfig,
|
||||
bool includeParentEnvironment = true,
|
||||
List<String>? capturedLogs,
|
||||
}) async {
|
||||
StreamSubscription<LogRecord>? subscription;
|
||||
if (capturedLogs != null) {
|
||||
subscription =
|
||||
logger.onRecord.listen((event) => capturedLogs.add(event.message));
|
||||
}
|
||||
|
||||
final assets = await NativeAssetsBuildRunner(
|
||||
logger: logger,
|
||||
dartExecutable: dartExecutable,
|
||||
).build(
|
||||
linkModePreference: linkModePreference,
|
||||
target: Target.current,
|
||||
workingDirectory: packageUri,
|
||||
cCompilerConfig: cCompilerConfig,
|
||||
includeParentEnvironment: includeParentEnvironment,
|
||||
);
|
||||
await expectAssetsExist(assets);
|
||||
|
||||
if (subscription != null) {
|
||||
await subscription.cancel();
|
||||
}
|
||||
|
||||
return assets;
|
||||
}
|
||||
|
||||
Future<void> expectAssetsExist(List<Asset> assets) async {
|
||||
for (final asset in assets) {
|
||||
final uri = (asset.path as AssetAbsolutePath).uri;
|
||||
expect(
|
||||
uri.toFilePath(),
|
||||
contains('${Platform.pathSeparator}.dart_tool${Platform.pathSeparator}'
|
||||
'native_assets_builder${Platform.pathSeparator}'));
|
||||
final file = File.fromUri(uri);
|
||||
expect(await file.exists(), true);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> expectSymbols({
|
||||
required Asset asset,
|
||||
required List<String> symbols,
|
||||
}) async {
|
||||
if (Platform.isLinux) {
|
||||
final assetUri = (asset.path as AssetAbsolutePath).uri;
|
||||
final nmResult = await runProcess(
|
||||
executable: 'nm',
|
||||
arguments: [
|
||||
'-D',
|
||||
assetUri.toFilePath(),
|
||||
],
|
||||
logger: logger,
|
||||
);
|
||||
|
||||
expect(
|
||||
nmResult.stdout,
|
||||
stringContainsInOrder(symbols),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
// Copyright (c) 2023, 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:test/test.dart';
|
||||
|
||||
import '../helpers.dart';
|
||||
import 'helpers.dart';
|
||||
|
||||
const Timeout longTimeout = Timeout(Duration(minutes: 5));
|
||||
|
||||
void main(List<String> args) async {
|
||||
test('get dependency metadata', timeout: longTimeout, () async {
|
||||
await inTempDir((tempUri) async {
|
||||
await copyTestProjects(targetUri: tempUri);
|
||||
final packageUri = tempUri.resolve('package_reading_metadata/');
|
||||
|
||||
// First, run `pub get`, we need pub to resolve our dependencies.
|
||||
await runPubGet(
|
||||
workingDirectory: packageUri,
|
||||
logger: logger,
|
||||
);
|
||||
|
||||
// Trigger a build, should invoke build for libraries with native assets.
|
||||
{
|
||||
final logMessages = <String>[];
|
||||
await build(packageUri, logger, dartExecutable,
|
||||
capturedLogs: logMessages);
|
||||
expect(
|
||||
logMessages.join('\n'),
|
||||
stringContainsInOrder([
|
||||
'package_with_metadata${Platform.pathSeparator}build.dart',
|
||||
'package_reading_metadata${Platform.pathSeparator}build.dart',
|
||||
'{some_int: 3, some_key: some_value}',
|
||||
]));
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
// Copyright (c) 2023, 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:native_assets_cli/native_assets_cli.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import '../helpers.dart';
|
||||
import 'helpers.dart';
|
||||
|
||||
const Timeout longTimeout = Timeout(Duration(minutes: 5));
|
||||
|
||||
void main(List<String> args) async {
|
||||
test('link mode preference', timeout: longTimeout, () async {
|
||||
await inTempDir((tempUri) async {
|
||||
await copyTestProjects(targetUri: tempUri);
|
||||
final packageUri = tempUri.resolve('native_add/');
|
||||
|
||||
// First, run `pub get`, we need pub to resolve our dependencies.
|
||||
await runPubGet(
|
||||
workingDirectory: packageUri,
|
||||
logger: logger,
|
||||
);
|
||||
|
||||
final assetsDynamic = await build(
|
||||
packageUri,
|
||||
logger,
|
||||
dartExecutable,
|
||||
linkModePreference: LinkModePreference.dynamic,
|
||||
);
|
||||
|
||||
final assetsPreferDynamic = await build(
|
||||
packageUri,
|
||||
logger,
|
||||
dartExecutable,
|
||||
linkModePreference: LinkModePreference.preferDynamic,
|
||||
);
|
||||
|
||||
final assetsStatic = await build(
|
||||
packageUri,
|
||||
logger,
|
||||
dartExecutable,
|
||||
linkModePreference: LinkModePreference.static,
|
||||
);
|
||||
|
||||
final assetsPreferStatic = await build(
|
||||
packageUri,
|
||||
logger,
|
||||
dartExecutable,
|
||||
linkModePreference: LinkModePreference.preferStatic,
|
||||
);
|
||||
|
||||
// This package honors preferences.
|
||||
expect(assetsDynamic.single.linkMode, LinkMode.dynamic);
|
||||
expect(assetsPreferDynamic.single.linkMode, LinkMode.dynamic);
|
||||
expect(assetsStatic.single.linkMode, LinkMode.static);
|
||||
expect(assetsPreferStatic.single.linkMode, LinkMode.static);
|
||||
});
|
||||
});
|
||||
}
|
196
pkg/native_assets_builder/test/helpers.dart
Normal file
196
pkg/native_assets_builder/test/helpers.dart
Normal file
|
@ -0,0 +1,196 @@
|
|||
// Copyright (c) 2023, 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:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:test/test.dart';
|
||||
import 'package:yaml/yaml.dart';
|
||||
|
||||
extension UriExtension on Uri {
|
||||
Uri get parent => File(toFilePath()).parent.uri;
|
||||
}
|
||||
|
||||
const keepTempKey = 'KEEP_TEMPORARY_DIRECTORIES';
|
||||
|
||||
Future<void> inTempDir(
|
||||
Future<void> Function(Uri tempUri) fun, {
|
||||
String? prefix,
|
||||
bool keepTemp = false,
|
||||
}) async {
|
||||
final tempDir = await Directory.systemTemp.createTemp(prefix);
|
||||
// Deal with Windows temp folder aliases.
|
||||
final tempUri =
|
||||
Directory(await tempDir.resolveSymbolicLinks()).uri.normalizePath();
|
||||
try {
|
||||
await fun(tempUri);
|
||||
} finally {
|
||||
if ((!Platform.environment.containsKey(keepTempKey) ||
|
||||
Platform.environment[keepTempKey]!.isEmpty) &&
|
||||
!keepTemp) {
|
||||
await tempDir.delete(recursive: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Runs a process async and captures the exit code and standard out.
|
||||
Future<RunProcessResult> runProcess({
|
||||
required String executable,
|
||||
required List<String> arguments,
|
||||
Uri? workingDirectory,
|
||||
Map<String, String>? environment,
|
||||
bool throwOnFailure = true,
|
||||
required Logger logger,
|
||||
}) async {
|
||||
final printWorkingDir =
|
||||
workingDirectory != null && workingDirectory != Directory.current.uri;
|
||||
final commandString = [
|
||||
if (printWorkingDir) '(cd ${workingDirectory.toFilePath()};',
|
||||
...?environment?.entries.map((entry) => '${entry.key}=${entry.value}'),
|
||||
executable,
|
||||
...arguments.map((a) => a.contains(' ') ? "'$a'" : a),
|
||||
if (printWorkingDir) ')',
|
||||
].join(' ');
|
||||
|
||||
logger.info('Running `$commandString`.');
|
||||
|
||||
final stdoutBuffer = <String>[];
|
||||
final stderrBuffer = <String>[];
|
||||
final stdoutCompleter = Completer<Object?>();
|
||||
final stderrCompleter = Completer<Object?>();
|
||||
final Process process = await Process.start(
|
||||
executable,
|
||||
arguments,
|
||||
workingDirectory: workingDirectory?.toFilePath(),
|
||||
environment: environment,
|
||||
);
|
||||
|
||||
process.stdout.transform(utf8.decoder).listen(
|
||||
(s) {
|
||||
logger.fine(' $s');
|
||||
stdoutBuffer.add(s);
|
||||
},
|
||||
onDone: stdoutCompleter.complete,
|
||||
);
|
||||
process.stderr.transform(utf8.decoder).listen(
|
||||
(s) {
|
||||
logger.shout(' $s');
|
||||
stderrBuffer.add(s);
|
||||
},
|
||||
onDone: stderrCompleter.complete,
|
||||
);
|
||||
|
||||
final int exitCode = await process.exitCode;
|
||||
await stdoutCompleter.future;
|
||||
final String stdout = stdoutBuffer.join();
|
||||
await stderrCompleter.future;
|
||||
final String stderr = stderrBuffer.join();
|
||||
final result = RunProcessResult(
|
||||
pid: process.pid,
|
||||
command: '$executable ${arguments.join(' ')}',
|
||||
exitCode: exitCode,
|
||||
stdout: stdout,
|
||||
stderr: stderr,
|
||||
);
|
||||
if (throwOnFailure && result.exitCode != 0) {
|
||||
throw result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
class RunProcessResult extends ProcessResult {
|
||||
final String command;
|
||||
|
||||
final int _exitCode;
|
||||
|
||||
@override
|
||||
int get exitCode => _exitCode;
|
||||
|
||||
final String _stderrString;
|
||||
|
||||
@override
|
||||
String get stderr => _stderrString;
|
||||
|
||||
final String _stdoutString;
|
||||
|
||||
@override
|
||||
String get stdout => _stdoutString;
|
||||
|
||||
RunProcessResult({
|
||||
required int pid,
|
||||
required this.command,
|
||||
required int exitCode,
|
||||
required String stderr,
|
||||
required String stdout,
|
||||
}) : _exitCode = exitCode,
|
||||
_stderrString = stderr,
|
||||
_stdoutString = stdout,
|
||||
super(pid, exitCode, stdout, stderr);
|
||||
|
||||
@override
|
||||
String toString() => '''command: $command
|
||||
exitCode: $exitCode
|
||||
stdout: $stdout
|
||||
stderr: $stderr''';
|
||||
}
|
||||
|
||||
final pkgNativeAssetsBuilderUri = Platform.script.resolve('../../');
|
||||
final testProjectsUri =
|
||||
pkgNativeAssetsBuilderUri.resolve('test/test_projects/');
|
||||
|
||||
Future<void> copyTestProjects({
|
||||
Uri? sourceUri,
|
||||
required Uri targetUri,
|
||||
}) async {
|
||||
sourceUri ??= testProjectsUri;
|
||||
final manifestUri = sourceUri.resolve('manifest.yaml');
|
||||
final manifestFile = File.fromUri(manifestUri);
|
||||
final manifestString = await manifestFile.readAsString();
|
||||
final manifestYaml = loadYamlDocument(manifestString);
|
||||
final manifest = [
|
||||
for (final path in manifestYaml.contents as YamlList) Uri(path: path)
|
||||
];
|
||||
final filesToCopy =
|
||||
manifest.where((e) => e.pathSegments.last != 'pubspec.yaml').toList();
|
||||
final filesToModify =
|
||||
manifest.where((e) => e.pathSegments.last == 'pubspec.yaml').toList();
|
||||
|
||||
for (final pathToCopy in filesToCopy) {
|
||||
final sourceFile = File.fromUri(sourceUri.resolveUri(pathToCopy));
|
||||
final targetFileUri = targetUri.resolveUri(pathToCopy);
|
||||
final targetDirUri = targetFileUri.parent;
|
||||
final targetDir = Directory.fromUri(targetDirUri);
|
||||
if (!(await targetDir.exists())) {
|
||||
await targetDir.create(recursive: true);
|
||||
}
|
||||
|
||||
// Copying files on MacOS and Windows preserves the source timestamps.
|
||||
// The builder will use the cached build if the timestamps are equal.
|
||||
// So just write the file instead.
|
||||
final targetFile = File.fromUri(targetFileUri);
|
||||
await targetFile.writeAsBytes(await sourceFile.readAsBytes());
|
||||
}
|
||||
for (final pathToModify in filesToModify) {
|
||||
final sourceFile = File.fromUri(sourceUri.resolveUri(pathToModify));
|
||||
final targetFileUri = targetUri.resolveUri(pathToModify);
|
||||
final sourceString = await sourceFile.readAsString();
|
||||
final modifiedString = sourceString.replaceAll(
|
||||
'path: ../../../',
|
||||
'path: ${pkgNativeAssetsBuilderUri.toFilePath().replaceAll('\\', '/')}',
|
||||
);
|
||||
await File.fromUri(targetFileUri)
|
||||
.writeAsString(modifiedString, flush: true);
|
||||
}
|
||||
}
|
||||
|
||||
/// Logger that outputs the full trace when a test fails.
|
||||
final logger = Logger('')
|
||||
..level = Level.ALL
|
||||
..onRecord.listen((record) {
|
||||
printOnFailure('${record.level.name}: ${record.time}: ${record.message}');
|
||||
});
|
||||
|
||||
final dartExecutable = File(Platform.resolvedExecutable).uri;
|
6
pkg/native_assets_builder/test/test_projects/README.md
Normal file
6
pkg/native_assets_builder/test/test_projects/README.md
Normal file
|
@ -0,0 +1,6 @@
|
|||
The `dart_app` depends on multiple packages with native assets.
|
||||
|
||||
`manifest.yaml` contains a list of all the files of the test projects.
|
||||
This is used to copy the test projects to temporary folders when running tests,
|
||||
to prevent tests accidentally using temporary files or interferring with each
|
||||
other.
|
2
pkg/native_assets_builder/test/test_projects/dart_app/.gitignore
vendored
Normal file
2
pkg/native_assets_builder/test/test_projects/dart_app/.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
.dart_tool
|
||||
bin/dart_app/
|
|
@ -0,0 +1,27 @@
|
|||
// Copyright (c) 2023, 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:native_add/native_add.dart';
|
||||
import 'package:native_subtract/native_subtract.dart';
|
||||
|
||||
void main() {
|
||||
testNativeAdd();
|
||||
testNativeSubtract();
|
||||
}
|
||||
|
||||
void testNativeAdd() {
|
||||
final answer = add(5, 6);
|
||||
if (answer != 5 + 6) {
|
||||
throw 'Wrong answer';
|
||||
}
|
||||
print('add(5, 6) = $answer');
|
||||
}
|
||||
|
||||
void testNativeSubtract() {
|
||||
final answer = subtract(5, 6);
|
||||
if (answer != 5 - 6) {
|
||||
throw 'Wrong answer';
|
||||
}
|
||||
print('subtract(5, 6) = $answer');
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
name: dart_app
|
||||
|
||||
publish_to: none
|
||||
|
||||
environment:
|
||||
sdk: ">=2.18.0 <3.0.0"
|
||||
|
||||
dependencies:
|
||||
native_add:
|
||||
path: ../native_add
|
||||
native_subtract:
|
||||
path: ../native_subtract
|
||||
|
||||
dependency_overrides:
|
||||
native_assets_cli:
|
||||
path: ../../../../../third_party/pkg/native/pkgs/native_assets_cli/
|
25
pkg/native_assets_builder/test/test_projects/manifest.yaml
Normal file
25
pkg/native_assets_builder/test/test_projects/manifest.yaml
Normal file
|
@ -0,0 +1,25 @@
|
|||
# The list of files to copy to a temporary folder to ensure running tests from
|
||||
# a completely clean setup.
|
||||
- dart_app/bin/dart_app.dart
|
||||
- dart_app/pubspec.yaml
|
||||
- native_add/build.dart
|
||||
- native_add/ffigen.yaml
|
||||
- native_add/lib/native_add.dart
|
||||
- native_add/lib/src/native_add_bindings_generated.dart
|
||||
- native_add/lib/src/native_add.dart
|
||||
- native_add/pubspec.yaml
|
||||
- native_add/src/native_add.c
|
||||
- native_add/src/native_add.h
|
||||
- native_add/test/native_add_test.dart
|
||||
- native_subtract/build.dart
|
||||
- native_subtract/ffigen.yaml
|
||||
- native_subtract/lib/native_subtract.dart
|
||||
- native_subtract/lib/src/native_subtract_bindings_generated.dart
|
||||
- native_subtract/lib/src/native_subtract.dart
|
||||
- native_subtract/pubspec.yaml
|
||||
- native_subtract/src/native_subtract.c
|
||||
- native_subtract/src/native_subtract.h
|
||||
- package_reading_metadata/build.dart
|
||||
- package_reading_metadata/pubspec.yaml
|
||||
- package_with_metadata/build.dart
|
||||
- package_with_metadata/pubspec.yaml
|
|
@ -0,0 +1,32 @@
|
|||
// Copyright (c) 2023, 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:c_compiler/c_compiler.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:native_assets_cli/native_assets_cli.dart';
|
||||
|
||||
const packageName = 'native_add';
|
||||
|
||||
void main(List<String> args) async {
|
||||
final buildConfig = await BuildConfig.fromArgs(args);
|
||||
final buildOutput = BuildOutput();
|
||||
final cbuilder = CBuilder.library(
|
||||
name: packageName,
|
||||
assetName:
|
||||
'package:$packageName/src/${packageName}_bindings_generated.dart',
|
||||
sources: [
|
||||
'src/$packageName.c',
|
||||
],
|
||||
);
|
||||
await cbuilder.run(
|
||||
buildConfig: buildConfig,
|
||||
buildOutput: buildOutput,
|
||||
logger: Logger('')
|
||||
..level = Level.ALL
|
||||
..onRecord.listen((record) {
|
||||
print('${record.level.name}: ${record.time}: ${record.message}');
|
||||
}),
|
||||
);
|
||||
await buildOutput.writeToFile(outDir: buildConfig.outDir);
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
# Run with `flutter pub run ffigen --config ffigen.yaml`.
|
||||
name: NativeAddBindings
|
||||
description: |
|
||||
Bindings for `src/native_add.h`.
|
||||
|
||||
Regenerate bindings with `flutter pub run ffigen --config ffigen.yaml`.
|
||||
output: "lib/src/native_add_bindings_generated.dart"
|
||||
headers:
|
||||
entry-points:
|
||||
- "src/native_add.h"
|
||||
include-directives:
|
||||
- "src/native_add.h"
|
||||
preamble: |
|
||||
// Copyright (c) 2023, 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.
|
||||
comments:
|
||||
style: any
|
||||
length: full
|
||||
ffi-native:
|
|
@ -0,0 +1,5 @@
|
|||
// Copyright (c) 2023, 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.
|
||||
|
||||
export 'src/native_add.dart';
|
|
@ -0,0 +1,9 @@
|
|||
// Copyright (c) 2023, 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 'native_add_bindings_generated.dart' as bindings;
|
||||
|
||||
// TODO(dacoharkes): This will fail lookup until the Dart SDK consumes the
|
||||
// output of `build.dart`.
|
||||
int add(int a, int b) => bindings.add(a, b);
|
|
@ -0,0 +1,15 @@
|
|||
// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
// AUTO GENERATED FILE, DO NOT EDIT.
|
||||
//
|
||||
// Generated by `package:ffigen`.
|
||||
// ignore_for_file: type=lint
|
||||
import 'dart:ffi' as ffi;
|
||||
|
||||
@ffi.FfiNative<ffi.Int32 Function(ffi.Int32, ffi.Int32)>('add')
|
||||
external int add(
|
||||
int a,
|
||||
int b,
|
||||
);
|
|
@ -0,0 +1,26 @@
|
|||
name: native_add
|
||||
description: Sums two numbers with native code.
|
||||
version: 0.1.0
|
||||
|
||||
publish_to: none
|
||||
|
||||
environment:
|
||||
sdk: ">=2.19.3 <4.0.0"
|
||||
|
||||
dependencies:
|
||||
c_compiler:
|
||||
path: ../../../../../third_party/pkg/native/pkgs/c_compiler/
|
||||
cli_config: ^0.1.1
|
||||
logging: ^1.1.1
|
||||
native_assets_cli:
|
||||
path: ../../../../../third_party/pkg/native/pkgs/native_assets_cli/
|
||||
|
||||
dev_dependencies:
|
||||
ffigen: ^7.2.9
|
||||
lints: ^2.0.0
|
||||
test: ^1.23.1
|
||||
|
||||
dependency_overrides:
|
||||
native_assets_cli:
|
||||
path: ../../../../../third_party/pkg/native/pkgs/native_assets_cli/
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
// Copyright (c) 2023, 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.
|
||||
|
||||
#include "native_add.h"
|
||||
|
||||
int32_t add(int32_t a, int32_t b) {
|
||||
return a + b;
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
// Copyright (c) 2023, 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.
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#if _WIN32
|
||||
#define MYLIB_EXPORT __declspec(dllexport)
|
||||
#else
|
||||
#define MYLIB_EXPORT
|
||||
#endif
|
||||
|
||||
MYLIB_EXPORT int32_t add(int32_t a, int32_t b);
|
|
@ -0,0 +1,13 @@
|
|||
// Copyright (c) 2023, 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:native_add/native_add.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
void main() {
|
||||
test('native add test', () {
|
||||
final result = add(4, 6);
|
||||
expect(result, equals(10));
|
||||
});
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
Used in automated testing to modify the native_add project.
|
|
@ -0,0 +1,33 @@
|
|||
// Copyright (c) 2023, 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:c_compiler/c_compiler.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:native_assets_cli/native_assets_cli.dart';
|
||||
|
||||
const packageName = 'native_add';
|
||||
|
||||
void main(List<String> args) async {
|
||||
final buildConfig = await BuildConfig.fromArgs(args);
|
||||
final buildOutput = BuildOutput();
|
||||
final cbuilder = CBuilder.library(
|
||||
name: packageName,
|
||||
assetName:
|
||||
'package:$packageName/src/${packageName}_bindings_generated.dart',
|
||||
sources: [
|
||||
'src/$packageName.c',
|
||||
'src/native_multiply.c',
|
||||
],
|
||||
);
|
||||
await cbuilder.run(
|
||||
buildConfig: buildConfig,
|
||||
buildOutput: buildOutput,
|
||||
logger: Logger('')
|
||||
..level = Level.ALL
|
||||
..onRecord.listen((record) {
|
||||
print('${record.level.name}: ${record.time}: ${record.message}');
|
||||
}),
|
||||
);
|
||||
await buildOutput.writeToFile(outDir: buildConfig.outDir);
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
- build.dart
|
||||
- src/native_multiply.c
|
||||
- src/native_multiply.h
|
|
@ -0,0 +1,9 @@
|
|||
// Copyright (c) 2023, 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.
|
||||
|
||||
#include "native_multiply.h"
|
||||
|
||||
MYLIB_EXPORT intptr_t multiply(intptr_t a, intptr_t b) {
|
||||
return a * b;
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
// Copyright (c) 2023, 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.
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#if _WIN32
|
||||
#define MYLIB_EXPORT __declspec(dllexport)
|
||||
#else
|
||||
#define MYLIB_EXPORT
|
||||
#endif
|
||||
|
||||
MYLIB_EXPORT intptr_t multiply(intptr_t a, intptr_t b);
|
|
@ -0,0 +1 @@
|
|||
Used in automated testing to modify the native_add project.
|
|
@ -0,0 +1,2 @@
|
|||
- src/native_add.c
|
||||
- src/native_add.h
|
|
@ -0,0 +1,14 @@
|
|||
// Copyright (c) 2023, 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.
|
||||
|
||||
#include "native_add.h"
|
||||
|
||||
MYLIB_EXPORT int32_t add(int32_t a, int32_t b) {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
|
||||
MYLIB_EXPORT intptr_t subtract(intptr_t a, intptr_t b) {
|
||||
return a - b;
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
// Copyright (c) 2023, 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.
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#if _WIN32
|
||||
#define MYLIB_EXPORT __declspec(dllexport)
|
||||
#else
|
||||
#define MYLIB_EXPORT
|
||||
#endif
|
||||
|
||||
MYLIB_EXPORT int32_t add(int32_t a, int32_t b);
|
||||
|
||||
MYLIB_EXPORT intptr_t subtract(intptr_t a, intptr_t b);
|
|
@ -0,0 +1 @@
|
|||
Used in automated testing to modify the native_add project.
|
|
@ -0,0 +1 @@
|
|||
- src/native_add.c
|
|
@ -0,0 +1,9 @@
|
|||
// Copyright (c) 2023, 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.
|
||||
|
||||
#include "native_add.h"
|
||||
|
||||
MYLIB_EXPORT int32_t add(int32_t different_parameters) {
|
||||
return a + b;
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
// Copyright (c) 2023, 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:c_compiler/c_compiler.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:native_assets_cli/native_assets_cli.dart';
|
||||
|
||||
const packageName = 'native_subtract';
|
||||
|
||||
void main(List<String> args) async {
|
||||
final buildConfig = await BuildConfig.fromArgs(args);
|
||||
final buildOutput = BuildOutput();
|
||||
final cbuilder = CBuilder.library(
|
||||
name: packageName,
|
||||
assetName:
|
||||
'package:$packageName/src/${packageName}_bindings_generated.dart',
|
||||
sources: [
|
||||
'src/$packageName.c',
|
||||
],
|
||||
);
|
||||
await cbuilder.run(
|
||||
buildConfig: buildConfig,
|
||||
buildOutput: buildOutput,
|
||||
logger: Logger('')
|
||||
..level = Level.ALL
|
||||
..onRecord.listen((record) {
|
||||
print('${record.level.name}: ${record.time}: ${record.message}');
|
||||
}),
|
||||
);
|
||||
await buildOutput.writeToFile(outDir: buildConfig.outDir);
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
# Run with `flutter pub run ffigen --config ffigen.yaml`.
|
||||
name: NativeAddBindings
|
||||
description: |
|
||||
Bindings for `src/native_subtract.h`.
|
||||
|
||||
Regenerate bindings with `flutter pub run ffigen --config ffigen.yaml`.
|
||||
output: "lib/src/native_subtract_bindings_generated.dart"
|
||||
headers:
|
||||
entry-points:
|
||||
- "src/native_subtract.h"
|
||||
include-directives:
|
||||
- "src/native_subtract.h"
|
||||
preamble: |
|
||||
// Copyright (c) 2023, 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.
|
||||
comments:
|
||||
style: any
|
||||
length: full
|
||||
ffi-native:
|
|
@ -0,0 +1,5 @@
|
|||
// Copyright (c) 2023, 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.
|
||||
|
||||
export 'src/native_subtract.dart';
|
|
@ -0,0 +1,9 @@
|
|||
// Copyright (c) 2023, 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 'native_subtract_bindings_generated.dart' as bindings;
|
||||
|
||||
// TODO(dacoharkes): This will fail lookup until the Dart SDK consumes the
|
||||
// output of `build.dart`.
|
||||
int subtract(int a, int b) => bindings.subtract(a, b);
|
|
@ -0,0 +1,15 @@
|
|||
// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
// AUTO GENERATED FILE, DO NOT EDIT.
|
||||
//
|
||||
// Generated by `package:ffigen`.
|
||||
// ignore_for_file: type=lint
|
||||
import 'dart:ffi' as ffi;
|
||||
|
||||
@ffi.FfiNative<ffi.Int32 Function(ffi.Int32, ffi.Int32)>('subtract')
|
||||
external int subtract(
|
||||
int a,
|
||||
int b,
|
||||
);
|
|
@ -0,0 +1,25 @@
|
|||
name: native_subtract
|
||||
description: Subtracts two numbers with native code.
|
||||
version: 0.1.0
|
||||
|
||||
publish_to: none
|
||||
|
||||
environment:
|
||||
sdk: ">=2.19.3 <4.0.0"
|
||||
|
||||
dependencies:
|
||||
c_compiler:
|
||||
path: ../../../../../third_party/pkg/native/pkgs/c_compiler/
|
||||
cli_config: ^0.1.1
|
||||
logging: ^1.1.1
|
||||
native_assets_cli:
|
||||
path: ../../../../../third_party/pkg/native/pkgs/native_assets_cli/
|
||||
|
||||
dev_dependencies:
|
||||
ffigen: ^7.2.9
|
||||
lints: ^2.0.0
|
||||
test: ^1.23.1
|
||||
|
||||
dependency_overrides:
|
||||
native_assets_cli:
|
||||
path: ../../../../../third_party/pkg/native/pkgs/native_assets_cli/
|
|
@ -0,0 +1,9 @@
|
|||
// Copyright (c) 2023, 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.
|
||||
|
||||
#include "native_subtract.h"
|
||||
|
||||
int32_t subtract(int32_t a, int32_t b) {
|
||||
return a - b;
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
// Copyright (c) 2023, 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.
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#if _WIN32
|
||||
#define MYLIB_EXPORT __declspec(dllexport)
|
||||
#else
|
||||
#define MYLIB_EXPORT
|
||||
#endif
|
||||
|
||||
MYLIB_EXPORT int32_t subtract(int32_t a, int32_t b);
|
|
@ -0,0 +1,18 @@
|
|||
// Copyright (c) 2023, 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:cli_config/cli_config.dart';
|
||||
import 'package:native_assets_cli/native_assets_cli.dart';
|
||||
|
||||
void main(List<String> args) async {
|
||||
final config = await Config.fromArgs(args: args);
|
||||
final buildConfig = BuildConfig.fromConfig(config);
|
||||
final metadata =
|
||||
buildConfig.dependencyMetadata!['package_with_metadata']!.metadata;
|
||||
final someValue = metadata['some_key']!;
|
||||
final someInt = metadata['some_int']!;
|
||||
print(metadata);
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
name: package_reading_metadata
|
||||
description: Reads some metadata in its build.dart
|
||||
version: 0.1.0
|
||||
|
||||
publish_to: none
|
||||
|
||||
environment:
|
||||
sdk: ">=2.19.3 <4.0.0"
|
||||
|
||||
dependencies:
|
||||
cli_config: ^0.1.1
|
||||
native_assets_cli:
|
||||
path: ../../../../../third_party/pkg/native/pkgs/native_assets_cli/
|
||||
package_with_metadata:
|
||||
path: ../package_with_metadata/
|
||||
yaml: ^3.1.1
|
||||
yaml_edit: ^2.1.0
|
||||
|
||||
dev_dependencies:
|
||||
lints: ^2.0.0
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
// Copyright (c) 2023, 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:native_assets_cli/native_assets_cli.dart';
|
||||
|
||||
const packageName = 'native_add';
|
||||
|
||||
void main(List<String> args) async {
|
||||
final buildConfig = await BuildConfig.fromArgs(args);
|
||||
final buildOutput = BuildOutput(
|
||||
metadata: Metadata({
|
||||
'some_key': 'some_value',
|
||||
'some_int': 3,
|
||||
}),
|
||||
);
|
||||
await buildOutput.writeToFile(outDir: buildConfig.outDir);
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
name: package_with_metadata
|
||||
description: Sets some metadata in its build.dart
|
||||
version: 0.1.0
|
||||
|
||||
publish_to: none
|
||||
|
||||
environment:
|
||||
sdk: ">=2.19.3 <4.0.0"
|
||||
|
||||
dependencies:
|
||||
cli_config: ^0.1.1
|
||||
native_assets_cli:
|
||||
path: ../../../../../third_party/pkg/native/pkgs/native_assets_cli/
|
||||
yaml: ^3.1.1
|
||||
yaml_edit: ^2.1.0
|
||||
|
||||
dev_dependencies:
|
||||
lints: ^2.0.0
|
||||
|
|
@ -63,6 +63,7 @@ front_end/test/incremental_dart2js_test: Pass, Slow
|
|||
front_end/testcases/*: Skip # These are not tests but input for tests.
|
||||
front_end/tool/incremental_perf_test: Slow, Pass
|
||||
kernel/testcases/*: Skip # These are not tests but input for tests.
|
||||
native_assets_builder/test/test_projects/*: SkipByDesign # These test projects don't work without running pub in the project itself.
|
||||
vm/test/kernel_front_end_test: Slow, Pass
|
||||
vm/test/transformations/type_flow/transformer_test: Slow, Pass
|
||||
vm/testcases/*: SkipByDesign # These are not tests but input for tests.
|
||||
|
@ -89,6 +90,7 @@ front_end/tool/*: SkipByDesign # Only meant to run on vm
|
|||
modular_test/test/memory_pipeline_test: Slow, Pass
|
||||
modular_test/test/validate_pipeline_test: Slow, Pass
|
||||
modular_test/test/validate_suite_test: Slow, Pass
|
||||
native_assets_builder/test/*: SkipByDesign # Only meant to run on vm
|
||||
nnbd_migration/test/*: SkipByDesign # Uses mirrors
|
||||
nnbd_migration/tool/*: SkipByDesign # Only meant to run on vm
|
||||
smith/test/*: SkipByDesign # Only meant to run on vm
|
||||
|
|
|
@ -105,6 +105,9 @@
|
|||
"meta": {
|
||||
"experimentSet": "nullSafety"
|
||||
},
|
||||
"native_assets_builder": {
|
||||
"experimentSet": "nullSafety"
|
||||
},
|
||||
"native_stack_traces": {
|
||||
"experimentSet": "nullSafety"
|
||||
},
|
||||
|
|
|
@ -276,6 +276,7 @@
|
|||
"pkg/kernel/",
|
||||
"pkg/meta/",
|
||||
"pkg/native_stack_traces/",
|
||||
"pkg/native_assets_builder/",
|
||||
"pkg/pkg.status",
|
||||
"pkg/smith/",
|
||||
"pkg/status_file/",
|
||||
|
|
|
@ -353,6 +353,11 @@ class Package implements Comparable<Package> {
|
|||
continue;
|
||||
}
|
||||
|
||||
// Skip 'pkg/native_assets_builder/test/test_projects/'.
|
||||
if (name == 'test_projects') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!name.startsWith('.')) {
|
||||
_collectDartFiles(entity, files);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue