diff --git a/DEPS b/DEPS index 012383fa192..22d4fa76080 100644 --- a/DEPS +++ b/DEPS @@ -147,7 +147,7 @@ vars = { "sync_http_rev": "c07f96f89a7eec7e3daac641fa6c587224fcfbaa", "tar_rev": "5a1ea943e70cdf3fa5e1102cdbb9418bd9b4b81a", "test_rev": "2be5ca067bdf09e999be2ad760ab8efab854e789", - "tools_rev": "e1b1b4c6f3a25bcd52c201a535f2f3ed66f7ac7e", + "tools_rev": "a4335eb80c55c3944a6af1a5ce20f5694298afdc", "vector_math_rev": "13f185f7e97d559e003f5ac79201da12f9a01049", "web_rev": "7e0853d6255d988a5813e680853565b4317da729", "webdev_rev": "7f376d242709e933fff70610503d0c5c09b2e17e", diff --git a/pkg/dart_data_home/OWNERS b/pkg/dart_data_home/OWNERS new file mode 100644 index 00000000000..3f330621604 --- /dev/null +++ b/pkg/dart_data_home/OWNERS @@ -0,0 +1,5 @@ +set noparent +file:/tools/OWNERS_INTEROP +bkonyi@google.com +# In addition allow global owners. +file:/OWNERS diff --git a/pkg/dart_data_home/lib/dart_data_home.dart b/pkg/dart_data_home/lib/dart_data_home.dart new file mode 100644 index 00000000000..87fe8bc8346 --- /dev/null +++ b/pkg/dart_data_home/lib/dart_data_home.dart @@ -0,0 +1,13 @@ +// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +/// @docImport 'src/dart_data_home.dart'; + +/// A package providing [getDartDataHome], a standardized way to access a +/// user-specific data directory for Dart and Flutter tooling, defaulting to +/// OS-conventions and configurable via the `DART_USER_HOME` environment +/// variable. +library; + +export 'src/dart_data_home.dart'; diff --git a/pkg/dart_data_home/lib/src/dart_data_home.dart b/pkg/dart_data_home/lib/src/dart_data_home.dart new file mode 100644 index 00000000000..afb11a98672 --- /dev/null +++ b/pkg/dart_data_home/lib/src/dart_data_home.dart @@ -0,0 +1,50 @@ +// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'dart:io'; + +import 'package:cli_util/cli_util.dart'; + +/// Get the file system location for storing global data on the users' system. +/// +/// The directory follows OS defaults and is not backed up or synchronized +/// across devices by the OS. +/// +/// The [packageName] must be a valid Dart package name. Prefer using the name +/// of the package calling this function to avoid name clashes. +/// +/// If provided, [environment] is used for environment variables. Otherwise, +/// [Platform.environment] is used. +/// +/// The directory location depends on the current [Platform.operatingSystem]: +/// - on **Windows**: +/// - `%LOCALAPPDATA%\Dart\` +/// - on **Mac OS**: +/// - `$HOME/Library/Application Support/Dart/` +/// - on **Linux**: +/// - `$XDG_STATE_HOME/Dart/` if `$XDG_STATE_HOME` is defined, +/// and +/// - `$HOME/.local/state/Dart/` otherwise. +/// +/// The Dart data home can be overridden with the `DART_DATA_HOME` environment +/// variable. +/// +/// The directory won't be created, this method merely returns the recommended +/// location. +String getDartDataHome(String packageName, {Map? environment}) { + environment ??= Platform.environment; + final overridden = environment['DART_DATA_HOME']; + final Directory dartDataHome; + if (overridden != null) { + dartDataHome = Directory(overridden); + } else { + final dartBaseDirectories = BaseDirectories( + 'Dart', + environment: environment, + ); + // Use 'state', not 'data': Don't synchronize across devices. + dartDataHome = Directory(dartBaseDirectories.stateHome); + } + return dartDataHome.uri.resolve('$packageName/').toFilePath(); +} diff --git a/pkg/dart_data_home/pubspec.yaml b/pkg/dart_data_home/pubspec.yaml new file mode 100644 index 00000000000..026673962e9 --- /dev/null +++ b/pkg/dart_data_home/pubspec.yaml @@ -0,0 +1,25 @@ +name: dart_data_home +version: 0.1.0-wip +description: >- + A package providing a standardized way to access a user-specific data + directory for Dart and Flutter tooling, defaulting to OS-conventions + and configurable via the `DART_USER_HOME` environment variable. + +# This package will be published at some point to be able to use it for Dart and +# Flutter tooling that does not live in the Dart SDK. + +environment: + sdk: ^3.8.0 + +resolution: workspace + +dependencies: + cli_util: 0.5.0-wip + +# We use 'any' version constraints here as we get our package versions from +# the dart-lang/sdk repo's DEPS file. Note that this is a special case; the +# best practice for packages is to specify their compatible version ranges. +# See also https://dart.dev/tools/pub/dependencies. +dev_dependencies: + path: any + test: any diff --git a/pkg/dart_data_home/test/dart_data_home_test.dart b/pkg/dart_data_home/test/dart_data_home_test.dart new file mode 100644 index 00000000000..021cbb4b1b3 --- /dev/null +++ b/pkg/dart_data_home/test/dart_data_home_test.dart @@ -0,0 +1,35 @@ +// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'dart:io'; + +import 'package:dart_data_home/dart_data_home.dart'; +import 'package:path/path.dart' as p; +import 'package:test/test.dart'; + +void main() { + test('returns a non-empty string', () { + final myAppHome = getDartDataHome('my_app'); + expect(myAppHome, isNotEmpty); + }); + + test('has an ancestor folder that exists', () { + void expectAncestorExists(String path) { + // We expect that first two segments of the path exist. This is really + // just a dummy check that some part of the path exists. + final ancestorPath = p.joinAll(p.split(path).take(2)); + expect(Directory(ancestorPath).existsSync(), isTrue); + } + + final myAppHome = getDartDataHome('my_app'); + expectAncestorExists(myAppHome); + }); + + test('empty environment throws exception', () async { + expect( + () => getDartDataHome('some_app', environment: {}), + throwsA(isA()), + ); + }); +} diff --git a/pubspec.yaml b/pubspec.yaml index f7cddc583fd..810057f0c95 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -34,6 +34,7 @@ workspace: - pkg/dart2native - pkg/dart2wasm - pkg/dartdev + - pkg/dart_data_home - pkg/dart_internal - pkg/dart_service_protocol_shared - pkg/dds diff --git a/tools/bots/test_matrix.json b/tools/bots/test_matrix.json index ca872eff402..63d82866ee7 100644 --- a/tools/bots/test_matrix.json +++ b/tools/bots/test_matrix.json @@ -170,6 +170,7 @@ "tests/ffi/", "pkg/_fe_analyzer_shared/", "pkg/build_integration/", + "pkg/dart_data_home/", "pkg/dart_internal/", "pkg/dart2native/", "pkg/dart2js_tools/", @@ -310,12 +311,12 @@ }, "vm-aot-mac-(debug|product|release)-(x64|x64c|arm64|arm64c)": { "options": { - "gen-snapshot-format" : "macho-dylib" + "gen-snapshot-format": "macho-dylib" } }, "vm-aot-(linux|mac|win)-(debug|product|release)-(simarm|simarm_x64|simarm64|simriscv32|simriscv64)": { "options": { - "gen-snapshot-format" : "elf" + "gen-snapshot-format": "elf" } }, "vm-asan-(linux|mac|win)-(debug|product|release)-(ia32|x64|arm64|simarm|simarm64|simriscv32|simriscv64)": {}, @@ -330,12 +331,12 @@ }, "vm-aot-lsan-(linux|mac)-(debug|product|release)-(x64|arm64|simarm|simarm64|simriscv32|simriscv64)": { "options": { - "gen-snapshot-format" : "elf" + "gen-snapshot-format": "elf" } }, "vm-aot-msan-linux-(debug|product|release)-(x64|arm64|simarm64|simriscv64)": { "options": { - "gen-snapshot-format" : "elf" + "gen-snapshot-format": "elf" } }, "vm-aot-tsan-(linux|mac)-(debug|product|release)-(x64|arm64|simarm64|simriscv64)": { @@ -345,27 +346,37 @@ }, "vm-aot-ubsan-(linux|mac|win)-(debug|product|release)-(x64|arm64|simarm|simarm64|simriscv32|simriscv64)": { "options": { - "gen-snapshot-format" : "elf" + "gen-snapshot-format": "elf" } }, "vm-linux-(debug|product|release)-simarm64_arm64": { "options": { - "vm-options": ["--use_simulator=true"], "use-qemu": true + "vm-options": [ + "--use_simulator=true" + ], + "use-qemu": true } }, "vm-linux-(debug|product|release)-simarm64_arm64-nosim": { "options": { - "vm-options": ["--use_simulator=false"], "use-qemu": true + "vm-options": [ + "--use_simulator=false" + ], + "use-qemu": true } }, "vm-mac-(debug|product|release)-simarm64_arm64": { "options": { - "vm-options": ["--use_simulator=true"] + "vm-options": [ + "--use_simulator=true" + ] } }, "vm-mac-(debug|product|release)-simarm64_arm64-nosim": { "options": { - "vm-options": ["--use_simulator=false"] + "vm-options": [ + "--use_simulator=false" + ] } }, "dart2js-(linux|win)-chrome": { @@ -501,12 +512,12 @@ "vm-aot-android-(debug|product|release)-arm_x64": { "options": { "builder-tag": "crossword", - "gen-snapshot-format" : "elf" + "gen-snapshot-format": "elf" } }, "vm-aot-android-(debug|product|release)-(ia32|x64|x64c|arm|arm64|arm64c|riscv64)": { "options": { - "gen-snapshot-format" : "elf" + "gen-snapshot-format": "elf" } }, "vm-aot-dwarf-linux-(debug|release|product)-x64": { @@ -515,7 +526,7 @@ "vm-options": [ "--dwarf_stack_traces" ], - "gen-snapshot-format" : "elf" + "gen-snapshot-format": "elf" } }, "vm-aot-obfuscate-linux-(debug|release|product)-x64": { @@ -2667,7 +2678,7 @@ }, { "name": "dart2wasm unit tests", - "script": "out/ReleaseX64/dart-sdk/bin/dart", + "script": "out/ReleaseX64/dart-sdk/bin/dart", "testRunner": true, "arguments": [ "pkg/dart2wasm/test/dry_run/dry_run_test.dart",