Skip to content
This repository was archived by the owner on Oct 18, 2024. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/test-package.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ jobs:
matrix:
# Add macos-latest and/or windows-latest if relevant for this package.
os: [ubuntu-latest, windows-latest, macos-latest]
sdk: [2.18.0, dev]
sdk: [3.0.0, dev]
steps:
- uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9
- uses: dart-lang/setup-dart@d6a63dab3335f427404425de0fbfed4686d93c4f
Expand Down
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
## 2.0.2-dev

- Require Dart 2.18
- Require Dart 3.0
- Make work with VM's platform-constants.

## 2.0.1

Expand Down
35 changes: 24 additions & 11 deletions analysis_options.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,42 +14,55 @@ linter:
- avoid_catching_errors
- avoid_classes_with_only_static_members
- avoid_dynamic_calls
- avoid_empty_else
- avoid_private_typedef_functions
- avoid_redundant_argument_values
- avoid_returning_null_for_future
- avoid_relative_lib_imports
- avoid_returning_this
- avoid_shadowing_type_parameters
- avoid_types_as_parameter_names
- avoid_unused_constructor_parameters
- avoid_void_async
- cancel_subscriptions
- await_only_futures
- camel_case_extensions
- collection_methods_unrelated_type
- comment_references
- curly_braces_in_flow_control_structures
- directives_ordering
- join_return_with_assignment
- empty_catches
- file_names
- hash_and_equals
- lines_longer_than_80_chars
- literal_only_boolean_expressions
- missing_whitespace_between_adjacent_strings
- no_adjacent_strings_in_list
- no_duplicate_case_values
- no_runtimeType_toString
- omit_local_variable_types
- only_throw_errors
- package_api_docs
- prefer_asserts_in_initializer_lists
- prefer_const_constructors
- prefer_const_declarations
- prefer_expression_function_bodies
- prefer_final_locals
- prefer_generic_function_type_aliases
- prefer_is_empty
- prefer_is_not_empty
- prefer_iterable_whereType
- prefer_relative_imports
- prefer_single_quotes
- prefer_typing_uninitialized_variables
- provide_deprecation_message
- sort_pub_dependencies
- test_types_in_equals
- throw_in_finally
- type_annotate_public_apis
- unawaited_futures
- unnecessary_await_in_return
- unnecessary_lambdas
- unnecessary_overrides
- unnecessary_parenthesis
- unnecessary_raw_strings
- unnecessary_statements
- use_if_null_to_convert_nulls_to_bools
- unrelated_type_equality_checks
- use_is_even_rather_than_modulo
- void_checks
- use_raw_strings
- use_string_buffers
- use_super_parameters


40 changes: 40 additions & 0 deletions bin/os_detect.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// 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.

/// Prints the operating system detected by the current compilation environment.
library pkg.os_detect.run;

import 'package:os_detect/os_detect.dart' as os_detect;

void main() {
final knownName = knownOSName();
print('OS name : ${os_detect.operatingSystem} '
'${knownName != null ? '($knownName)' : ''}');
print('OS version : ${os_detect.operatingSystemVersion}');
}

String? knownOSName() {
if (os_detect.isAndroid) {
return 'Android';
}
if (os_detect.isBrowser) {
return 'Browser';
}
if (os_detect.isFuchsia) {
return 'Fuchsia';
}
if (os_detect.isIOS) {
return 'iOS';
}
if (os_detect.isLinux) {
return 'Linux';
}
if (os_detect.isMacOS) {
return 'MacOS';
}
if (os_detect.isWindows) {
return 'Windows';
}
return null;
}
20 changes: 17 additions & 3 deletions example/example.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,21 @@ import 'package:os_detect/os_detect.dart' as os_detect;

void main() {
print('''
OS: ${os_detect.operatingSystem}
OS Version: ${os_detect.operatingSystemVersion}
''');
OS ID: ${os_detect.operatingSystem}
OS Version: ${os_detect.operatingSystemVersion}''');
if (os_detect.isAndroid) {
print(' OS Type: Android');
} else if (os_detect.isBrowser) {
print(' OS Type: Browser');
} else if (os_detect.isFuchsia) {
print(' OS Type: Fuchsia');
} else if (os_detect.isIOS) {
print(' OS Type: iOS');
} else if (os_detect.isLinux) {
print(' OS Type: Linux');
} else if (os_detect.isMacOS) {
print(' OS Type: MacOS');
} else if (os_detect.isWindows) {
print(' OS Type: Windows');
}
}
29 changes: 29 additions & 0 deletions example/tree_shaking.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// 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.

// Try compiling this example with (if on Linux):
//
// dart compile exe --target-os=linux tree_shaking.dart
//
// then check that "SOMETHING ELSE" does not occur in the
// output `tree_shaking.exe` program, e.g.:
//
// strings tree_shaking.exe | grep SOMETHING
//
// which shows no matches.

import 'package:os_detect/os_detect.dart' as platform;

void main() {
if (platform.isLinux) {
print('Is Linux');
} else {
print('SOMETHING ELSE');
}
if (platform.operatingSystem == 'linux') {
print('Is Linux');
} else {
print('SOMETHING ELSE');
}
}
44 changes: 41 additions & 3 deletions lib/os_detect.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,19 @@
/// Information about the current operating system.
library pkg.os_detect;

import 'override.dart';
import 'src/os_override.dart';

/// Identification of the current operating system or platform.
///
/// Specific known operating systems are reported by a unique string.
/// Specific known operating systems are reported by a unique known string,
/// and all the `is<Name>` values are computed by comparing the
/// [operatingSystem] string against those known strings.
/// That means that *at most* one of those value can be `true`,
/// and usually precisely one will be `true`.
///
/// **Notice:** Programs running in a browser will report their
/// operating system as `"browser"`, not the operating system
/// that browser is running on. See [isBrowser].
String get operatingSystem => OperatingSystem.current.id;

/// Representation of the version of the current operating system or platform.
Expand All @@ -24,37 +32,59 @@ String get operatingSystemVersion => OperatingSystem.current.version;
///
/// This value is `false` if the operating system is a specialized
/// version of Linux that identifies itself by a different name,
/// for example Android (see [isAndroid]).
/// for example Android (see [isAndroid]),
/// or if the code is running inside a browser (see [isBrowser]).
@pragma('vm:prefer-inline')

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Notice that this annotation will not make guarantees, the new @pragma('vm:platform-const') will make guarantees (/cc @sstrickl )

Even if the method is inlined, it may happen in the C++ compiler backend. That has also a tree-shaker, but we want to ensure it's tree shaken before our kernel-level global analysis pass. We have this new platform-const annotation for that purpose.

If there's subsets of expression/statement language missing to make it work, @sstrickl can implement that.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not expecting this getter to be platform-const, that's going to be impossible when we also have the ability to do runtime overrides.

However, if inlined twice, and all but one OSKind subclass has been tree-shaken, the compiler can still deduce that the value is always true or false. (And it works! So don't get worse at treeshaking!)

bool get isLinux => OperatingSystem.current.isLinux;

/// Whether the current operating system is a version of
/// [macOS](https://en.wikipedia.org/wiki/MacOS).
///
/// Identified by [operatingSystem] being the string `macos`.
///
/// The value is `false` if the code is running inside a browser,
/// even if that browser is running on MacOS (see [isBrowser]).
@pragma('vm:prefer-inline')
bool get isMacOS => OperatingSystem.current.isMacOS;

/// Whether the current operating system is a version of
/// [Microsoft Windows](https://en.wikipedia.org/wiki/Microsoft_Windows).
///
/// Identified by [operatingSystem] being the string `windows`.
///
/// The value is `false` if the code is running inside a browser,
/// even if that browser is running on Windows (see [isBrowser]).
@pragma('vm:prefer-inline')
bool get isWindows => OperatingSystem.current.isWindows;

/// Whether the current operating system is a version of
/// [Android](https://en.wikipedia.org/wiki/Android_%28operating_system%29).
///
/// Identified by [operatingSystem] being the string `android`.
///
/// The value is `false` if the code is running inside a browser,
/// even if that browser is running on Android (see [isBrowser]).
@pragma('vm:prefer-inline')
bool get isAndroid => OperatingSystem.current.isAndroid;

/// Whether the current operating system is a version of
/// [iOS](https://en.wikipedia.org/wiki/IOS).
///
/// Identified by [operatingSystem] being the string `ios`.
///
/// The value is `false` if the code is running inside a browser,
/// even if that browser is running on iOS (see [isBrowser]).
@pragma('vm:prefer-inline')
bool get isIOS => OperatingSystem.current.isIOS;

/// Whether the current operating system is a version of
/// [Fuchsia](https://en.wikipedia.org/wiki/Google_Fuchsia).
///
/// Identified by [operatingSystem] being the string `fuchsia`.
///
/// The value is `false` if the code is running inside a browser,
/// even if that browser is running on Fuchsia (see [isBrowser]).
@pragma('vm:prefer-inline')
bool get isFuchsia => OperatingSystem.current.isFuchsia;

/// Whether running in a web browser.
Expand All @@ -63,4 +93,12 @@ bool get isFuchsia => OperatingSystem.current.isFuchsia;
///
/// If so, the [operatingSystemVersion] is the string made available
/// through `window.navigator.appVersion`.
///
/// The value is `true` when the code is running inside a browser,
/// no matter which operating system the browser is itself running on.
/// No attempt is made to detect the underlying operating system.
/// That information *may* be derived from [operatingSystemVersion],
/// but browsers are able to lie in the app-version/user-agent
/// string.
@pragma('vm:prefer-inline')
bool get isBrowser => OperatingSystem.current.isBrowser;
98 changes: 3 additions & 95 deletions lib/override.dart
Original file line number Diff line number Diff line change
@@ -1,100 +1,8 @@
// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
// 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.

/// Functionality to override information about the current platform.
library pkg.os_detect.override;
library;

import 'dart:async' show Zone, runZoned;

import 'src/osid_unknown.dart'
if (dart.library.io) 'src/osid_io.dart'
if (dart.library.html) 'src/osid_html.dart';

/// The name and version of an operating system.
class OperatingSystem {
/// The current operating system ID.
///
/// Defaults to what information is available
/// from known platform specific libraries,
/// but can be overridden using functionality from the
/// `osid_override.dart` library.
static OperatingSystem get current =>
Zone.current[#_os] as OperatingSystem? ?? platformOS;

/// A string representing the operating system or platform.
final String id;

/// A string representing the version of the operating system or platform.
///
/// May be empty if no version is known or available.
final String version;

/// Creates an operating system ID with the provided values.
const OperatingSystem(this.id, this.version);
}

/// Convenience operations on [OperatingSystem].
///
/// Implemented as extensions to allow users to *implement* [OperatingSystem]
/// without having to implement all of these getters.
extension OperatingSystemGetters on OperatingSystem {
/// Whether the operating system is a version of
/// [Linux](https://en.wikipedia.org/wiki/Linux).
///
/// Identified by [id] being the string `linux`.
///
/// This value is `false` if the operating system is a specialized
/// version of Linux that identifies itself by a different name,
/// for example Android (see [isAndroid]).
bool get isLinux => 'linux' == id;

/// Whether the operating system is a version of
/// [macOS](https://en.wikipedia.org/wiki/MacOS).
///
/// Identified by [id] being the string `macos`.
bool get isMacOS => 'macos' == id;

/// Whether the operating system is a version of
/// [Microsoft Windows](https://en.wikipedia.org/wiki/Microsoft_Windows).
///
/// Identified by [id] being the string `windows`.
bool get isWindows => 'windows' == id;

/// Whether the operating system is a version of
/// [Android](https://en.wikipedia.org/wiki/Android_%28operating_system%29).
///
/// Identified by [id] being the string `android`.
bool get isAndroid => 'android' == id;

/// Whether the operating system is a version of
/// [iOS](https://en.wikipedia.org/wiki/IOS).
///
/// Identified by [id] being the string `ios`.
bool get isIOS => 'ios' == id;

/// Whether the operating system is a version of
/// [Fuchsia](https://en.wikipedia.org/wiki/Google_Fuchsia).
///
/// Identified by [id] being the string `fuchsia`.
bool get isFuchsia => 'fuchsia' == id;

/// Whether running in a web browser.
///
/// Identified by [id] being the string `browser`.
///
/// If so, the [version] is the string made available
/// through `window.navigator.appVersion`.
bool get isBrowser => 'browser' == id;
}

/// Run [body] in a zone with platform overrides.
///
/// Overrides [OperatingSystem.current] with the supplied [operatingSystem]
/// value while running in a new zone, and then runs [body] in that zone.
///
/// This override affects the `operatingSystem` and `version`
/// exported by `package:osid/osid.dart`.
R overrideOperatingSystem<R>(
OperatingSystem operatingSystem, R Function() body) =>
runZoned(body, zoneValues: {#_os: operatingSystem});
export 'src/os_override.dart' show OperatingSystem, overrideOperatingSystem;
Loading