未验证 提交 9e8f8d08 编写于 作者: D Dan Field 提交者: GitHub

Drop android_sdk_downloader in favor of cipd (#8087)

* remove sdk downloader, use cipd

* roll buildroot to drop android_sdk_downloader
上级 21342863
......@@ -122,7 +122,7 @@ allowed_hosts = [
]
deps = {
'src': 'https://github.com/flutter/buildroot.git' + '@' + '4a12b0dfad16723b2190b697a669e3ae17b50b35',
'src': 'https://github.com/flutter/buildroot.git' + '@' + '3f54d4f03112098e164ee62f015fcc54b19d1eda',
# Fuchsia compatibility
#
......@@ -387,6 +387,61 @@ deps = {
'src/third_party/pkg/when':
Var('dart_git') + '/when.git' + '@' + '0.2.0',
'src/third_party/android_tools/ndk': {
'packages': [
{
'package': 'flutter/android/ndk/${{platform}}',
'version': 'version:r19b'
}
],
'condition': 'download_android_deps',
'dep_type': 'cipd',
},
'src/third_party/android_tools/sdk/build-tools': {
'packages': [
{
'package': 'flutter/android/sdk/build-tools/${{platform}}',
'version': 'version:28.0.3'
}
],
'condition': 'download_android_deps',
'dep_type': 'cipd',
},
'src/third_party/android_tools/sdk/platform-tools': {
'packages': [
{
'package': 'flutter/android/sdk/platform-tools/${{platform}}',
'version': 'version:28.0.1'
}
],
'condition': 'download_android_deps',
'dep_type': 'cipd',
},
'src/third_party/android_tools/sdk/platforms': {
'packages': [
{
'package': 'flutter/android/sdk/platforms',
'version': 'version:28r6'
}
],
'condition': 'download_android_deps',
'dep_type': 'cipd',
},
'src/third_party/android_tools/sdk/tools': {
'packages': [
{
'package': 'flutter/android/sdk/tools/${{platform}}',
'version': 'version:26.1.1'
}
],
'condition': 'download_android_deps',
'dep_type': 'cipd',
},
}
hooks = [
......@@ -414,34 +469,6 @@ hooks = [
'pattern': '.',
'action': ['python', 'src/tools/dart/update.py'],
},
{
'name': 'prepare_android_downloader',
'pattern': '.',
'condition': 'download_android_deps',
'cwd': 'src/flutter/tools/android_sdk_downloader',
'action': [
'../../../third_party/dart/tools/sdks/dart-sdk/bin/pub', # this hook _must_ be run _after_ the dart hook.
'get'
],
},
{
'name': 'download_android_tools',
'pattern': '.',
'condition': 'download_android_deps',
'action': [
'src/third_party/dart/tools/sdks/dart-sdk/bin/dart', # this hook _must_ be run _after_ the dart hook.
'--enable-asserts',
'src/flutter/tools/android_sdk_downloader/lib/main.dart',
'-y', # Accept licenses
'--out=src/third_party/android_tools',
'--platform=28',
'--platform-revision=6',
'--build-tools-version=28.0.3',
'--platform-tools-version=28.0.1',
'--tools-version=26.1.1',
'--ndk-version=19.1.5304403'
],
},
{
'name': 'download_android_support',
'pattern': '.',
......
.DS_Store
.atom/
.idea
.packages
.pub/
.dart_tool/
pubspec.lock
\ No newline at end of file
// Copyright 2013 The Flutter Authors. All rights reserved.
//
// 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 Inc. 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.
\ No newline at end of file
# Android SDK Downloader
This program assists with downloading the Android SDK and NDK artifacts for
Flutter engine development.
## Usage
```
-r, --repository-xml Specifies the location of the Android Repository XML file.
(defaults to "https://dl.google.com/android/repository/repository2-1.xml")
-p, --platform Specifies the Android platform version, e.g. 28
--platform-revision Specifies the Android platform revision, e.g. 6 for 28_r06
-o, --out The directory to write downloaded files to.
--os The OS type to download for. Defaults to current platform.
(defaults to current platform), accepts: [windows, macos, linux]
--build-tools-version The build-tools version to download. Must be in format of <major>.<minor>.<micro>, e.g. 28.0.3; or <major>.<minor>.<micro>.<rc/preview>, e.g. 28.0.0.2
--platform-tools-version The platform-tools version to download. Must be in format of <major>.<minor>.<micro>, e.g. 28.0.1; or <major>.<minor>.<micro>.<rc/preview>, e.g. 28.0.0.2
--tools-version The tools version to download. Must be in format of <major>.<minor>.<micro>, e.g. 26.1.1; or <major>.<minor>.<micro>.<rc/preview>, e.g. 28.1.1.2
--ndk-version The ndk version to download. Must be in format of <major>.<minor>.<micro>, e.g. 28.0.3; or <major>.<minor>.<micro>.<rc/preview>, e.g. 28.0.0.2
-y, --[no-]accept-licenses Automatically accept Android SDK licenses.
--[no-]overwrite Skip download if the target directory exists.
```
\ No newline at end of file
# Specify analysis options.
#
# Copied from https://github.com/flutter/flutter/blob/master/analysis_options.yaml
analyzer:
strong-mode:
implicit-dynamic: false
errors:
# treat missing required parameters as a warning (not a hint)
missing_required_param: warning
# treat missing returns as a warning (not a hint)
missing_return: warning
# allow having TODOs in the code
todo: ignore
exclude:
- 'bin/cache/**'
# the following two are relative to the stocks example and the flutter package respectively
# see https://github.com/dart-lang/sdk/issues/28463
- 'lib/i18n/stock_messages_*.dart'
- 'lib/src/http/**'
linter:
rules:
# these rules are documented on and in the same order as
# the Dart Lint rules page to make maintenance easier
# https://github.com/dart-lang/linter/blob/master/example/all.yaml
- always_declare_return_types
- always_put_control_body_on_new_line
# - always_put_required_named_parameters_first # we prefer having parameters in the same order as fields https://github.com/flutter/flutter/issues/10219
- always_require_non_null_named_parameters
- always_specify_types
- annotate_overrides
# - avoid_annotating_with_dynamic # conflicts with always_specify_types
- avoid_as
# - avoid_bool_literals_in_conditional_expressions # not yet tested
# - avoid_catches_without_on_clauses # we do this commonly
# - avoid_catching_errors # we do this commonly
- avoid_classes_with_only_static_members
# - avoid_double_and_int_checks # only useful when targeting JS runtime
- avoid_empty_else
- avoid_field_initializers_in_const_classes
- avoid_function_literals_in_foreach_calls
# - avoid_implementing_value_types # not yet tested
- avoid_init_to_null
# - avoid_js_rounded_ints # only useful when targeting JS runtime
- avoid_null_checks_in_equality_operators
# - avoid_positional_boolean_parameters # not yet tested
# - avoid_private_typedef_functions # we prefer having typedef (discussion in https://github.com/flutter/flutter/pull/16356)
- avoid_relative_lib_imports
- avoid_renaming_method_parameters
- avoid_return_types_on_setters
# - avoid_returning_null # there are plenty of valid reasons to return null
- avoid_returning_null_for_void
# - avoid_returning_this # there are plenty of valid reasons to return this
# - avoid_setters_without_getters # not yet tested
# - avoid_single_cascade_in_expression_statements # not yet tested
- avoid_slow_async_io
- avoid_types_as_parameter_names
# - avoid_types_on_closure_parameters # conflicts with always_specify_types
- avoid_unused_constructor_parameters
- avoid_void_async
- await_only_futures
- camel_case_types
- cancel_subscriptions
# - cascade_invocations # not yet tested
# - close_sinks # not reliable enough
# - comment_references # blocked on https://github.com/flutter/flutter/issues/20765
# - constant_identifier_names # needs an opt-out https://github.com/dart-lang/linter/issues/204
- control_flow_in_finally
# - curly_braces_in_flow_control_structures # not yet tested
- directives_ordering
- empty_catches
- empty_constructor_bodies
- empty_statements
# - file_names # not yet tested
- flutter_style_todos
- hash_and_equals
- implementation_imports
# - invariant_booleans # too many false positives: https://github.com/dart-lang/linter/issues/811
- iterable_contains_unrelated_type
# - join_return_with_assignment # not yet tested
- library_names
- library_prefixes
# - lines_longer_than_80_chars # not yet tested
- list_remove_unrelated_type
# - literal_only_boolean_expressions # too many false positives: https://github.com/dart-lang/sdk/issues/34181
- no_adjacent_strings_in_list
- no_duplicate_case_values
- non_constant_identifier_names
# - null_closures # not yet tested
# - omit_local_variable_types # opposite of always_specify_types
# - one_member_abstracts # too many false positives
# - only_throw_errors # https://github.com/flutter/flutter/issues/5792
- overridden_fields
- package_api_docs
- package_names
- package_prefixed_library_names
# - parameter_assignments # we do this commonly
- prefer_adjacent_string_concatenation
- prefer_asserts_in_initializer_lists
- prefer_collection_literals
- prefer_conditional_assignment
- prefer_const_constructors
- prefer_const_constructors_in_immutables
- prefer_const_declarations
- prefer_const_literals_to_create_immutables
# - prefer_constructors_over_static_methods # not yet tested
- prefer_contains
- prefer_equal_for_default_values
# - prefer_expression_function_bodies # conflicts with https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo#consider-using--for-short-functions-and-methods
- prefer_final_fields
- prefer_final_locals
- prefer_foreach
# - prefer_function_declarations_over_variables # not yet tested
- prefer_generic_function_type_aliases
- prefer_initializing_formals
# - prefer_int_literals # not yet tested
# - prefer_interpolation_to_compose_strings # not yet tested
- prefer_is_empty
- prefer_is_not_empty
- prefer_iterable_whereType
# - prefer_mixin # https://github.com/dart-lang/language/issues/32
- prefer_single_quotes
- prefer_typing_uninitialized_variables
- prefer_void_to_null
# - public_member_api_docs # enabled on a case-by-case basis; see e.g. packages/analysis_options.yaml
- recursive_getters
- slash_for_doc_comments
- sort_constructors_first
- sort_pub_dependencies
- sort_unnamed_constructors_first
- super_goes_last
- test_types_in_equals
- throw_in_finally
# - type_annotate_public_apis # subset of always_specify_types
- type_init_formals
# - unawaited_futures # too many false positives
- unnecessary_brace_in_string_interps
- unnecessary_const
- unnecessary_getters_setters
# - unnecessary_lambdas # has false positives: https://github.com/dart-lang/linter/issues/498
- unnecessary_new
- unnecessary_null_aware_assignments
- unnecessary_null_in_if_null_operators
- unnecessary_overrides
- unnecessary_parenthesis
- unnecessary_statements
- unnecessary_this
- unrelated_type_equality_checks
- use_rethrow_when_possible
# - use_setters_to_change_properties # not yet tested
# - use_string_buffers # has false positives: https://github.com/dart-lang/sdk/issues/34182
# - use_to_and_as_if_applicable # has false positives, so we prefer to catch this by code-review
- valid_regexps
# - void_checks # not yet tested
// Copyright 2013 The Flutter Authors. 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:args/args.dart';
import 'package:path/path.dart' as path;
import 'src/android_repository.dart';
import 'src/checksums.dart';
import 'src/http.dart';
import 'src/options.dart';
import 'src/zip.dart';
const String _kAndroidRepositoryXml = 'https://dl.google.com/android/repository/repository2-1.xml';
Future<void> main(List<String> args) async {
final ArgParser argParser = ArgParser()
..addOption(
'repository-xml',
abbr: 'r',
help: 'Specifies the location of the Android Repository XML file.',
defaultsTo: _kAndroidRepositoryXml,
)
..addOption(
'platform',
abbr: 'p',
help: 'Specifies the Android platform version, e.g. 28',
)
..addOption(
'platform-revision',
help: 'Specifies the Android platform revision, e.g. 6 for 28_r06',
)
..addOption(
'out',
abbr: 'o',
help: 'The directory to write downloaded files to.',
defaultsTo: '.',
)
..addOption(
'os',
help: 'The OS type to download for. Defaults to current platform.',
defaultsTo: Platform.operatingSystem,
allowed: osTypeMap.keys,
)
..addOption(
'build-tools-version',
help: 'The build-tools version to download. Must be in format of '
'<major>.<minor>.<micro>, e.g. 28.0.3; '
'or <major>.<minor>.<micro>.<rc/preview>, e.g. 28.0.0.2',
)
..addOption(
'platform-tools-version',
help: 'The platform-tools version to download. Must be in format of '
'<major>.<minor>.<micro>, e.g. 28.0.1; '
'or <major>.<minor>.<micro>.<rc/preview>, e.g. 28.0.0.2',
)
..addOption(
'tools-version',
help: 'The tools version to download. Must be in format of '
'<major>.<minor>.<micro>, e.g. 26.1.1; '
'or <major>.<minor>.<micro>.<rc/preview>, e.g. 28.1.1.2',
)
..addOption(
'ndk-version',
help: 'The ndk version to download. Must be in format of '
'<major>.<minor>.<micro>, e.g. 28.0.3; '
'or <major>.<minor>.<micro>.<rc/preview>, e.g. 28.0.0.2',
)
..addFlag('accept-licenses',
abbr: 'y',
defaultsTo: false,
help: 'Automatically accept Android SDK licenses.')
..addFlag(
'overwrite',
defaultsTo: false,
help: 'Skip download if the target directory exists.',
);
final bool help = args.contains('-h')
|| args.contains('--help')
|| (args.isNotEmpty && args.first == 'help');
if (help) {
print(argParser.usage);
return;
}
final Options options = Options.parseAndValidate(args, argParser);
final AndroidRepository androidRepository = await _getAndroidRepository(options.repositoryXmlUri);
assert(androidRepository.platforms.isNotEmpty);
assert(androidRepository.buildTools.isNotEmpty);
if (!options.acceptLicenses) {
for (final AndroidRepositoryLicense license in androidRepository.licenses) {
print('================================================================================\n\n');
print(license.text);
stdout.write('Do you accept? (Y/n): ');
final String result = stdin.readLineSync().trim().toLowerCase();
if (result != '' && result.startsWith('y') == false) {
print('Ending.');
exit(-1);
}
}
}
await options.outDirectory.create(recursive: true);
final Directory tempDir = await Directory(options.outDirectory.path).createTemp();
await tempDir.create(recursive: true);
final Directory ndkDir = Directory(path.join(options.outDirectory.path, 'ndk'));
final Directory sdkDir = Directory(path.join(options.outDirectory.path, 'sdk'));
final Directory platformDir = Directory(path.join(sdkDir.path, 'platforms', 'android-${options.platformApiLevel}'));
final Directory buildToolsDir = Directory(path.join(sdkDir.path, 'build-tools', options.buildToolsRevision.raw));
final Directory platformToolsDir = Directory(path.join(sdkDir.path, 'platform-tools'));
final Directory toolsDir = Directory(path.join(sdkDir.path, 'tools'));
final Map<String, String> checksums =
await loadChecksums(options.outDirectory);
print('Downloading Android SDK and NDK artifacts...');
final List<Future<void>> futures = <Future<void>>[];
futures.add(downloadArchive(
androidRepository.platforms,
OptionsRevision(null, options.platformRevision),
options.repositoryBase,
tempDir,
checksumToSkip: options.overwrite ? null : checksums[platformDir.path],
).then((ArchiveDownloadResult result) {
if (result != ArchiveDownloadResult.empty) {
return unzipFile(result.zipFileName, platformDir).then((_) {
checksums[platformDir.path] = result.checksum;
return writeChecksums(checksums, options.outDirectory);
});
}
return null;
}));
futures.add(downloadArchive(
androidRepository.buildTools,
options.buildToolsRevision,
options.repositoryBase,
tempDir,
osType: options.osType,
checksumToSkip: options.overwrite ? null : checksums[buildToolsDir.path],
).then((ArchiveDownloadResult result) {
if (result != ArchiveDownloadResult.empty) {
return unzipFile(result.zipFileName, buildToolsDir).then((_) {
checksums[buildToolsDir.path] = result.checksum;
return writeChecksums(checksums, options.outDirectory);
});
}
return null;
}));
futures.add(downloadArchive(
androidRepository.platformTools,
options.platformToolsRevision,
options.repositoryBase,
tempDir,
osType: options.osType,
checksumToSkip: options.overwrite ? null : checksums[platformToolsDir.path],
).then((ArchiveDownloadResult result) {
if (result != ArchiveDownloadResult.empty) {
return unzipFile(result.zipFileName, platformToolsDir).then((_) {
checksums[platformToolsDir.path] = result.checksum;
return writeChecksums(checksums, options.outDirectory);
});
}
return null;
}));
futures.add(downloadArchive(
androidRepository.tools,
options.toolsRevision,
options.repositoryBase,
tempDir,
osType: options.osType,
checksumToSkip: options.overwrite ? null : checksums[toolsDir.path],
).then((ArchiveDownloadResult result) {
if (result != ArchiveDownloadResult.empty) {
return unzipFile(result.zipFileName, toolsDir).then((_) {
checksums[toolsDir.path] = result.checksum;
return writeChecksums(checksums, options.outDirectory);
});
}
return null;
}));
futures.add(downloadArchive(
androidRepository.ndkBundles,
options.ndkRevision,
options.repositoryBase,
tempDir,
osType: options.osType,
checksumToSkip: options.overwrite ? null : checksums[ndkDir.path],
).then((ArchiveDownloadResult result) {
if (result != ArchiveDownloadResult.empty) {
return unzipFile(result.zipFileName, ndkDir).then((_) {
checksums[ndkDir.path] = result.checksum;
return writeChecksums(checksums, options.outDirectory);
});
}
return null;
}));
await Future.wait<void>(futures);
await tempDir.delete(recursive: true);
}
Future<AndroidRepository> _getAndroidRepository(Uri repositoryXmlUri) async {
final StringBuffer repoXmlBuffer = StringBuffer();
Future<void> _repositoryXmlHandler(HttpClientResponse response) async {
await response.transform(utf8.decoder).forEach(repoXmlBuffer.write);
}
await httpGet(repositoryXmlUri, _repositoryXmlHandler);
return parseAndroidRepositoryXml(repoXmlBuffer.toString());
}
// Copyright 2013 The Flutter Authors. 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:xml/xml.dart' as xml show parse;
import 'package:xml/xml.dart';
// see https://android.googlesource.com/platform/tools/base/+/master/sdklib/src/main/java/com/android/sdklib/repository/sdk-repository-10.xsd
const String _kXsi = 'http://www.w3.org/2001/XMLSchema-instance';
const String _kSdk = 'http://schemas.android.com/sdk/android/repo/repository2/01';
void _debugCheckElement(
XmlElement element,
String name, {
String namespace,
}) {
assert(element != null);
assert(element.name.local == name, '${element.name.local} != $name');
assert(element.name.namespaceUri == namespace,
'${element.name.namespaceUri} != $namespace');
}
XmlElement _firstOrDefault(Iterable<XmlElement> list) {
if (list?.isEmpty == true) {
return null;
}
return list.first;
}
String _getChildText(
XmlElement parent,
String childName, {
String def = '',
String namespace,
}) {
final String value = _firstOrDefault(
parent.findElements(
childName,
namespace: namespace,
),
)?.text;
return value ?? def;
}
OSType _parseHostType(String value) {
switch (value) {
case 'linux':
return OSType.linux;
case 'windows':
return OSType.windows;
case 'macosx':
return OSType.mac;
default:
return OSType.any;
}
}
/// Parses a the Android SDK's https://dl.google.com/android/repository/repository2-1.xml
/// into an [AndroidRepository] object.
AndroidRepository parseAndroidRepositoryXml(String rawXml) {
final XmlDocument doc = xml.parse(rawXml);
return AndroidRepository.fromXml(doc.rootElement);
}
XmlElement _getTypeDetails(XmlElement parent) {
final XmlElement typeDetails =
_firstOrDefault(parent.findAllElements('type-details'));
if (typeDetails == null) {
throw StateError('Missing <type-details>.');
}
return typeDetails;
}
String _getTypeDetailsType(XmlElement typeDetails) {
return typeDetails.getAttribute('type', namespace: _kXsi);
}
Iterable<XmlElement> _getArchives(XmlElement parent) {
assert(parent != null);
final XmlElement archives =
_firstOrDefault(parent.findAllElements('archives'));
if (archives == null) {
return null;
}
return archives.findElements('archive');
}
/// Object class for https://dl.google.com/android/repository/repository2-1.xml.
class AndroidRepository {
const AndroidRepository(
this.licenses,
this.platforms,
this.buildTools,
this.platformTools,
this.tools,
this.ndkBundles,
) : assert(licenses != null),
assert(platforms != null),
assert(buildTools != null),
assert(platformTools != null),
assert(tools != null),
assert(ndkBundles != null);
/// Parses the `<sdk-repository>` element.
factory AndroidRepository.fromXml(XmlElement element) {
_debugCheckElement(element, 'sdk-repository', namespace: _kSdk);
final List<AndroidRepositoryLicense> licenses =
<AndroidRepositoryLicense>[];
final List<AndroidRepositoryPlatform> platforms =
<AndroidRepositoryPlatform>[];
final List<AndroidRepositoryRemotePackage> buildTools =
<AndroidRepositoryRemotePackage>[];
final List<AndroidRepositoryRemotePackage> platformTools =
<AndroidRepositoryRemotePackage>[];
final List<AndroidRepositoryRemotePackage> tools =
<AndroidRepositoryRemotePackage>[];
final List<AndroidRepositoryRemotePackage> ndkBundles =
<AndroidRepositoryRemotePackage>[];
for (final XmlElement child in element.children.whereType<XmlElement>()) {
switch (child.name.local) {
case 'license':
licenses.add(AndroidRepositoryLicense.fromXml(child));
break;
case 'remotePackage':
final XmlElement typeDetails = _getTypeDetails(child);
switch (_getTypeDetailsType(typeDetails)) {
case 'sdk:platformDetailsType':
platforms.add(
AndroidRepositoryPlatform.fromXml(child, typeDetails),
);
break;
case 'generic:genericDetailsType':
final String path = child.getAttribute('path');
if (path.startsWith('build-tools;')) {
buildTools.add(AndroidRepositoryRemotePackage.fromXml(child));
} else if (path.startsWith('platform-tools')) {
platformTools
.add(AndroidRepositoryRemotePackage.fromXml(child));
} else if (path.startsWith('tools')) {
tools.add(AndroidRepositoryRemotePackage.fromXml(child));
} else if (path.startsWith('ndk-bundle')) {
ndkBundles.add(AndroidRepositoryRemotePackage.fromXml(child));
}
break;
default:
break;
}
break;
default:
break;
}
}
return AndroidRepository(
licenses,
platforms,
buildTools,
platformTools,
tools,
ndkBundles,
);
}
/// Licenses from the repository XML.
final List<AndroidRepositoryLicense> licenses;
/// Platform information from the repository XML.
final List<AndroidRepositoryPlatform> platforms;
/// Build tools information from the repostiory XML.
final List<AndroidRepositoryRemotePackage> buildTools;
/// Platform tools information from the repostiory XML.
final List<AndroidRepositoryRemotePackage> platformTools;
/// Tools information from the repostiory XML.
final List<AndroidRepositoryRemotePackage> tools;
/// Tools information from the repostiory XML.
final List<AndroidRepositoryRemotePackage> ndkBundles;
}
/// Object class for the `<license>` element in the Android repo XML.
///
/// This node contains license information for the packages in the SDK.
class AndroidRepositoryLicense {
/// Creates a new RepositoryLicense holder.
const AndroidRepositoryLicense(this.id, this.text)
: assert(id != null),
assert(text != null);
/// Parses a `<license>` element.
factory AndroidRepositoryLicense.fromXml(XmlElement element) {
_debugCheckElement(element, 'license');
return AndroidRepositoryLicense(element.getAttribute('id'), element.text);
}
/// The identifier for this license.
final String id;
/// The text of the license.
final String text;
}
/// Object class for the `<remotePackage>` nodes in the repo XML.
///
/// These nodes contain information about where to download the zipped
/// binaries for various components of the SDK.
class AndroidRepositoryRemotePackage {
const AndroidRepositoryRemotePackage(
this.revision,
this.displayName,
this.archives, {
this.isObsolete = false,
}) : assert(revision != null),
assert(displayName != null),
assert(archives != null),
assert(isObsolete != null);
factory AndroidRepositoryRemotePackage.fromXml(XmlElement element) {
_debugCheckElement(element, 'remotePackage');
return AndroidRepositoryRemotePackage(
AndroidRepositoryRevision.fromXml(
_firstOrDefault(element.findElements('revision'))),
_getChildText(element, 'display-name'),
_getArchives(element)
.map(
(XmlElement archive) => AndroidRepositoryArchive.fromXml(archive),
)
.toList(),
isObsolete: element.getAttribute('obsolete') == 'true',
);
}
/// The `<revision>` element, if any.
final AndroidRepositoryRevision revision;
/// The `<display-name>` element.
final String displayName;
/// The list of archives available for this package.
final List<AndroidRepositoryArchive> archives;
/// Whether this package is marked as obsolete.
final bool isObsolete;
@override
String toString() => '$runtimeType{revision: $revision, displayName: $displayName, archives: $archives}';
}
/// Object class for instances of `<remotePackage>` elements that are for the
/// platform package.
class AndroidRepositoryPlatform extends AndroidRepositoryRemotePackage {
const AndroidRepositoryPlatform(
AndroidRepositoryRevision revision,
String displayName,
List<AndroidRepositoryArchive> archives,
this.apiLevel, {
bool isObsolete = false,
}) : assert(apiLevel != null),
super(revision, displayName, archives, isObsolete: isObsolete);
/// Parses an platform from a `<remotePackage>` element.
factory AndroidRepositoryPlatform.fromXml(
XmlElement element,
XmlElement typeDetails,
) {
_debugCheckElement(element, 'remotePackage');
assert(typeDetails != null);
return AndroidRepositoryPlatform(
AndroidRepositoryRevision.fromXml(
_firstOrDefault(element.findElements('revision'))),
_getChildText(element, 'display-name'),
_getArchives(element)
.map(
(XmlElement archive) => AndroidRepositoryArchive.fromXml(archive),
)
.toList(),
int.parse(_getChildText(typeDetails, 'api-level', def: '0')),
isObsolete: element.getAttribute('obsolete') == 'true',
);
}
/// The API level for this Platform.
final int apiLevel;
@override
String toString() => '$runtimeType{revision: $revision, displayName: $displayName, archives: $archives, apiLevel: $apiLevel}';
}
/// The OS types supported by Android.
enum OSType {
/// Any OS is supported.
any,
/// Suppoorts Linux only.
linux,
/// Supports macOS only.
mac,
/// Supports windows only.
windows,
}
/// Object class for the `<archive>` element in the Android repo XML.
///
/// Contains information about the size, checksum, and location of a binary
/// zip archive. Optionally contains information about what host OS is
/// supported.
class AndroidRepositoryArchive {
/// Creates a new AndroidRepositoryArchive.
const AndroidRepositoryArchive(
this.size,
this.checksum,
this.url, {
this.hostOS = OSType.any,
}) : assert(size != null),
assert(checksum != null),
assert(url != null),
assert(hostOS != null);
/// Parses an `<archive>` element.
factory AndroidRepositoryArchive.fromXml(XmlElement element) {
_debugCheckElement(element, 'archive');
final XmlElement complete =
_firstOrDefault(element.findElements('complete'));
if (complete == null) {
throw StateError('Found <archive> element without a <complete> node!');
}
return AndroidRepositoryArchive(
int.parse(_getChildText(complete, 'size', def: '0')),
_getChildText(complete, 'checksum'),
_getChildText(complete, 'url'),
hostOS: _parseHostType(_getChildText(element, 'host-os')),
);
}
/// The download size in bytes of the archive.
final int size;
/// The SHA-1 checksum of the archive.
final String checksum;
/// The absolute or relative URL of the file.
final String url;
/// The OS type, if applicable, for this archive.
final OSType hostOS;
@override
String toString() => '$runtimeType{size: $size, checksum: $checksum, url: $url, hostOS: $hostOS}';
}
/// Object class for a `<revision>` node in the Android repo XML.
///
/// Contains information about the revision of the archive.
///
/// In the case of the platform package, this is the revision of the platform.
///
/// In all other cases, this basically works like semver.
class AndroidRepositoryRevision {
/// Creates a new Android repository revision object. All values are required.
const AndroidRepositoryRevision(
this.major, [
this.minor = 0,
this.micro = 0,
this.preview = 0,
]) : assert(major != null),
assert(minor != null),
assert(micro != null),
assert(preview != null);
/// Parses a `<revision>` element from the Android repository XML.
factory AndroidRepositoryRevision.fromXml(XmlElement element) {
if (element == null) {
return const AndroidRepositoryRevision(0);
}
_debugCheckElement(element, 'revision');
return AndroidRepositoryRevision(
int.tryParse(_getChildText(element, 'major', def: '0')),
int.tryParse(_getChildText(element, 'minor', def: '0')),
int.tryParse(_getChildText(element, 'micro', def: '0')),
);
}
/// The major revision value.
final int major;
/// The minor revision value.
final int minor;
/// The micro revision.
final int micro;
/// Preview/Release candidate version. A value of 0 indicates that
/// this is not a preview.
final int preview;
/// Whether this revision represents a preview or release.
bool get isPreview => preview > 0;
bool matches(int major, int minor, int micro, [int preview = 0]) {
return this.major == major &&
this.minor == minor &&
this.micro == micro &&
this.preview == preview;
}
@override
String toString() => '$runtimeType:{$major.$minor.$micro.$preview}';
}
// Copyright 2013 The Flutter Authors. 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:path/path.dart' as path;
Future<Map<String, String>> loadChecksums(Directory directory) async {
final File checksumFile = File(path.join(directory.path, 'checksums.json'));
if (!checksumFile.existsSync()) {
return <String, String>{};
}
final Map<String, String> result = <String, String>{};
final Map<String, dynamic> jsonResult =
json.decode(await checksumFile.readAsString());
for (final String key in jsonResult.keys) {
result[key] = jsonResult[key];
}
return result;
}
Future<void> writeChecksums(
Map<String, String> checksums,
Directory directory,
) async {
final File checksumFile = File(path.join(directory.path, 'checksums.json'));
const JsonEncoder encoder = JsonEncoder.withIndent(' ');
await checksumFile.writeAsString(encoder.convert(checksums));
}
// Copyright 2013 The Flutter Authors. 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:path/path.dart' as path;
import 'android_repository.dart';
import 'options.dart';
typedef HttpResponseHandler = Future<void> Function(HttpClientResponse);
Future<void> httpGet(
Uri url,
HttpResponseHandler handler,
) async {
assert(url != null);
assert(handler != null);
final HttpClient httpClient = HttpClient();
try {
final HttpClientRequest request = await httpClient.getUrl(url);
final HttpClientResponse response = await request.close();
await handler(response);
} finally {
httpClient.close();
}
}
class DownloadTracker {
DownloadTracker(this.name, this.total) : received = 0;
final String name;
final int total;
int received;
String get percent => '${((received / total) * 100).round()}'.padLeft(3) + '%';
@override
String toString() => '$name: $received/$total ($percent).';
}
final Map<String, DownloadTracker> _downloadTrackers =
<String, DownloadTracker>{};
void _printDownloadTrackers() {
for (final DownloadTracker tracker in _downloadTrackers.values) {
stdout.write(
'${tracker.name.replaceAll('Android ', '')}: ${tracker.percent} ');
}
if (_downloadTrackers.values
.every((DownloadTracker tracker) => tracker.received == tracker.total)) {
stdout.writeln();
print('Downloads complete.');
} else {
stdout.write('\r');
}
}
class ArchiveDownloadResult {
const ArchiveDownloadResult(this.zipFileName, this.checksum);
static const ArchiveDownloadResult empty = ArchiveDownloadResult(null, null);
final String zipFileName;
final String checksum;
}
Future<ArchiveDownloadResult> downloadArchive(
List<AndroidRepositoryRemotePackage> packages,
OptionsRevision revision,
String repositoryBase,
Directory outDirectory, {
OSType osType,
int apiLevel,
String checksumToSkip,
}) async {
AndroidRepositoryRemotePackage package;
for (final AndroidRepositoryRemotePackage p in packages) {
if (apiLevel != null && p is AndroidRepositoryPlatform) {
if (p.apiLevel != apiLevel) {
continue;
}
}
if (p.revision.matches(
revision.major, revision.minor, revision.micro, revision.preview)) {
package = p;
break;
}
}
if (package == null) {
throw StateError('Could not find package matching arguments: '
'$revision, $osType, $apiLevel');
}
final String displayName = package.displayName;
final AndroidRepositoryArchive archive = osType == null
? package.archives.first
: package.archives.firstWhere(
(AndroidRepositoryArchive archive) => archive.hostOS == osType,
);
if (archive.checksum == checksumToSkip) {
print('Skipping $displayName, checksum matches current asset.');
return ArchiveDownloadResult.empty;
}
Uri uri = Uri.parse(archive.url);
if (!uri.isAbsolute) {
uri = Uri.parse(repositoryBase + archive.url);
}
_downloadTrackers[displayName] = DownloadTracker(displayName, archive.size);
final String outFileName = path.join(outDirectory.path, archive.url);
final IOSink tempFileSink = File(outFileName).openWrite();
Future<void> _handlePlatformZip(HttpClientResponse response) async {
await for (List<int> data in response) {
_downloadTrackers[displayName].received += data.length;
tempFileSink.add(data);
_printDownloadTrackers();
}
await tempFileSink.close();
}
await httpGet(uri, _handlePlatformZip);
return ArchiveDownloadResult(outFileName, archive.checksum);
}
// Copyright 2013 The Flutter Authors. 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:args/args.dart';
import 'package:meta/meta.dart';
import 'android_repository.dart';
const Map<String, OSType> osTypeMap = <String, OSType>{
'windows': OSType.windows,
'macos': OSType.mac,
'linux': OSType.linux,
};
class OptionsRevision {
const OptionsRevision(
this.raw, [
this.major = 0,
this.minor = 0,
this.micro = 0,
this.preview = 0,
]);
/// Accepted formats: 1.2.3 or 1.2.3.4
factory OptionsRevision.fromRaw(String raw) {
final List<String> rawParts = raw.split('.');
if (rawParts == null || (rawParts.length != 3 && rawParts.length != 4)) {
throw ArgumentError('Invalid revision string $raw.');
}
return OptionsRevision(
raw,
int.parse(rawParts[0]),
int.parse(rawParts[1]),
int.parse(rawParts[2]),
rawParts.length == 4 ? int.parse(rawParts[3]) : 0,
);
}
final String raw;
final int major;
final int minor;
final int micro;
final int preview;
}
class Options {
const Options({
@required this.platformApiLevel,
@required this.platformRevision,
@required this.repositoryXml,
@required this.repositoryXmlUri,
@required this.buildToolsRevision,
@required this.platformToolsRevision,
@required this.toolsRevision,
@required this.ndkRevision,
@required this.outDirectory,
@required this.repositoryBase,
@required this.osType,
this.acceptLicenses = false,
this.overwrite = false,
});
static Options parseAndValidate(List<String> args, ArgParser argParser) {
final ArgResults argResults = argParser.parse(args);
final int platformApiLevel = int.parse(argResults['platform']);
final int platformRevision = int.parse(argResults['platform-revision']);
final Directory outDirectory = Directory(argResults['out']);
final String rawRepositoryXmlUri = argResults['repository-xml'];
final Uri repositoryXmlUri = Uri.tryParse(rawRepositoryXmlUri);
final int lastSlash = rawRepositoryXmlUri.lastIndexOf('/');
final String repositoryBase =
rawRepositoryXmlUri.substring(0, lastSlash + 1);
if (repositoryXmlUri == null) {
throw ArgumentError(
'Error: could not parse $rawRepositoryXmlUri as a valid URL.');
}
String getRawVersion(String argName) {
final String raw = argResults[argName];
if (raw?.isEmpty == true) {
print('Could not parse required argument $argName.');
print(argParser.usage);
exit(-1);
}
return raw;
}
final String rawBuildToolsVersion = getRawVersion('build-tools-version');
final String rawPlatformToolsVersion =
getRawVersion('platform-tools-version');
final String rawToolsVersion = getRawVersion('tools-version');
final String rawNdkVersion = getRawVersion('ndk-version');
return Options(
platformApiLevel: platformApiLevel,
platformRevision: platformRevision,
outDirectory: outDirectory,
repositoryXml: rawRepositoryXmlUri,
repositoryXmlUri: repositoryXmlUri,
repositoryBase: repositoryBase,
buildToolsRevision: OptionsRevision.fromRaw(rawBuildToolsVersion),
platformToolsRevision: OptionsRevision.fromRaw(rawPlatformToolsVersion),
toolsRevision: OptionsRevision.fromRaw(rawToolsVersion),
ndkRevision: OptionsRevision.fromRaw(rawNdkVersion),
osType: osTypeMap[argResults['os']],
acceptLicenses: argResults['accept-licenses'],
overwrite: argResults['overwrite'],
);
}
final int platformApiLevel;
final String repositoryXml;
final Uri repositoryXmlUri;
final int platformRevision;
final OptionsRevision buildToolsRevision;
final OptionsRevision platformToolsRevision;
final OptionsRevision toolsRevision;
final OptionsRevision ndkRevision;
final String repositoryBase;
final Directory outDirectory;
final OSType osType;
final bool acceptLicenses;
final bool overwrite;
}
// Copyright 2013 The Flutter Authors. 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';
// TODO(dnfield): if/when a streaming unzip routine is available for Dart, use that instead.
Future<void> unzipFile(String file, Directory outDir) async {
await outDir.parent.create(recursive: true);
final Directory tempDir = await outDir.parent.createTemp();
String command;
List<String> args;
if (Platform.isWindows) {
command = 'powershell.exe -nologo -noprofile -command '
'"& { '
'Add-Type -A \'System.IO.Compression.FileSystem\'; '
'[IO.Compression.ZipFile]::ExtractToDirectory(\'$file\', \'${tempDir.path}\'); '
'}"';
args = <String>[];
} else {
command = 'unzip';
args = <String>[
file,
'-d',
tempDir.path,
];
}
final ProcessResult result = await Process.run(command, args);
if (result.exitCode != 0) {
throw Exception('Failed to unzip archive!');
}
final Directory dir = await tempDir.list().first;
if (await outDir.exists()) {
await outDir.delete(recursive: true);
}
await dir.rename(outDir.path);
await tempDir.delete();
}
name: 'android_sdk_downloader'
publish_to: none
environment:
# The pub client defaults to an <2.0.0 sdk constraint which we need to explicitly overwrite.
sdk: ">=2.0.0-dev.68.0 <3.0.0"
dependencies:
args: ^1.5.1
meta: ^1.1.6
path: ^1.6.2
xml: ^3.2.3
#!/bin/bash
# This script requires depot_tools to be on path.
print_usage () {
echo "Usage: create_ndk_cipd_package.sh <PATH_TO_NDK_ASSETS> <PLATFORM_NAME> <VERSION_TAG>"
echo " where:"
echo " - PATH_TO_NDK_ASSETS is the path to the unzipped NDK folder"
echo " - PLATFORM_NAME is one of linux-amd64, mac-amd64, or windows-amd64"
echo " - VERSION_TAG is the version of the NDK, e.g. r19b"
}
if [[ $3 == "" ]]; then
print_usage
exit 1
fi
if [[ ! -d "$1" ]]; then
echo "Directory $1 not found."
print_usage
exit 1
fi
if [[ $2 != "linux-amd64" && $2 != "mac-amd64" && $2 != "windows-amd64" ]]; then
echo "Unsupported platform $2."
echo "Valid options are linux-amd64, mac-amd64, windows-amd64."
print_usage
exit 1
fi
cipd create -in $1 -name flutter/android/ndk/$2 -install-mode copy -tag version:$3
#!/bin/bash
# This script requires depot_tools to be on path.
print_usage () {
echo "Usage: create_ndk_cipd_package.sh <PACKAGE_TYPE> <PATH_TO_ASSETS> <PLATFORM_NAME> <VERSION_TAG>"
echo " where:"
echo " - PACKAGE_TYPE is one of build-tools, platform-tools, platforms, or tools"
echo " - PATH_TO_ASSETS is the path to the unzipped asset folder"
echo " - PLATFORM_NAME is one of linux-amd64, mac-amd64, or windows-amd64"
echo " - VERSION_TAG is the version of the package, e.g. 28r6 or 28.0.3"
}
if [[ $4 == "" ]]; then
print_usage
exit 1
fi
if [[ $1 != "build-tools" && $1 != "platform-tools" && $1 != "platforms" && $1 != "tools" ]]; then
echo "Unrecognized paackage type $1."
print_usage
exit 1
fi
if [[ ! -d "$2" ]]; then
echo "Directory $1 not found."
print_usage
exit 1
fi
if [[ $1 != "platforms" && $3 != "linux-amd64" && $3 != "mac-amd64" && $3 != "windows-amd64" ]]; then
echo "Unsupported platform $3."
echo "Valid options are linux-amd64, mac-amd64, windows-amd64."
print_usage
exit 1
fi
if [[ $1 == "platforms" ]]; then
echo "Ignoring PLATFORM_NAME - this package is cross-platform."
cipd create -in $2 -name flutter/android/sdk/$1 -install-mode copy -tag version:$4
else
cipd create -in $2 -name flutter/android/sdk/$1/$3 -install-mode copy -tag version:$4
fi
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册