Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 51061c6

Browse files
committed
Add an option to use a prebuilt Dart SDK
1 parent 8dd9194 commit 51061c6

File tree

10 files changed

+414
-99
lines changed

10 files changed

+414
-99
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,3 +130,6 @@ app.*.symbols
130130
!**/ios/**/default.perspectivev3
131131
!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
132132
!/dev/ci/**/Gemfile.lock
133+
134+
# Prebuilt binaries.
135+
/prebuilts/

BUILD.gn

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import("//flutter/common/config.gni")
66
import("//flutter/shell/platform/config.gni")
77
import("//flutter/shell/platform/glfw/config.gni")
88
import("//flutter/testing/testing.gni")
9+
import("//third_party/dart/build/dart/copy_tree.gni")
910

1011
# Whether to build the dartdevc sdk, libraries, and source files
1112
# required for the flutter web sdk.
@@ -35,6 +36,35 @@ config("export_dynamic_symbols") {
3536
}
3637
}
3738

39+
if (flutter_prebuilt_dart_sdk) {
40+
copy_trees("_copy_trees") {
41+
sources = [
42+
{
43+
target = "copy_dart_sdk"
44+
visibility = [ ":dart_sdk" ]
45+
source = target_prebuilt_dart_sdk
46+
dest = "$root_out_dir/dart-sdk"
47+
ignore_patterns = "{}"
48+
},
49+
]
50+
}
51+
}
52+
53+
# Flutter SDK artifacts should only be built when either doing host builds, or
54+
# for cross-compiled desktop targets.
55+
_build_engine_artifacts =
56+
current_toolchain == host_toolchain || (is_linux && !is_chromeos) || is_mac
57+
58+
group("dart_sdk") {
59+
if (_build_engine_artifacts) {
60+
if (flutter_prebuilt_dart_sdk) {
61+
public_deps = [ ":copy_dart_sdk" ]
62+
} else {
63+
public_deps = [ "//third_party/dart:create_sdk" ]
64+
}
65+
}
66+
}
67+
3868
group("flutter") {
3969
testonly = true
4070

@@ -44,22 +74,17 @@ group("flutter") {
4474
"//flutter/sky",
4575
]
4676

47-
# Flutter SDK artifacts should only be built when either doing host builds, or
48-
# for cross-compiled desktop targets.
49-
build_engine_artifacts = current_toolchain == host_toolchain ||
50-
(is_linux && !is_chromeos) || is_mac
51-
5277
# If enbaled, compile the SDK / snapshot.
5378
if (!is_fuchsia) {
5479
public_deps += [
5580
"//flutter/lib/snapshot:generate_snapshot_bin",
5681
"//flutter/lib/snapshot:kernel_platform_files",
5782
]
5883

59-
if (build_engine_artifacts) {
84+
if (_build_engine_artifacts) {
6085
public_deps += [
86+
":dart_sdk",
6187
"//flutter/flutter_frontend_server:frontend_server",
62-
"//third_party/dart:create_sdk",
6388

6489
# This must be listed explicitly for desktop cross-builds since
6590
# //flutter/lib/snapshot:generate_snapshot_bin will only build
@@ -73,7 +98,7 @@ group("flutter") {
7398
}
7499
}
75100

76-
if (build_engine_artifacts) {
101+
if (_build_engine_artifacts) {
77102
public_deps += [
78103
"//flutter/shell/testing",
79104
"//flutter/tools/const_finder",

DEPS

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -713,6 +713,14 @@ hooks = [
713713
'src/build/win/generate_winrt_headers.py',
714714
]
715715
},
716+
{
717+
'name': 'Download prebuilt Dart SDK',
718+
'pattern': '.',
719+
'action': [
720+
'python3',
721+
'src/flutter/tools/download_dart_sdk.py',
722+
]
723+
},
716724
{
717725
'name': 'Setup githooks',
718726
'pattern': '.',

ci/licenses_golden/tool_signature

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
Signature: b9105737a36020f19487fe9854f4be92
1+
Signature: 6f6132591430df787d08771477f3374d
22

common/config.gni

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ declare_args() {
1919

2020
# Whether to use the Skia text shaper module for all text rendering
2121
flutter_always_use_skshaper = false
22+
23+
# Whether to use a prebuilt Dart SDK instead of building one.
24+
flutter_prebuilt_dart_sdk = false
2225
}
2326

2427
# feature_defines_list ---------------------------------------------------------
@@ -62,3 +65,17 @@ if (is_ios || is_mac) {
6265
flutter_cflags_objc_arc = flutter_cflags_objc + [ "-fobjc-arc" ]
6366
flutter_cflags_objcc_arc = flutter_cflags_objc_arc
6467
}
68+
69+
# prebuilt Dart SDK location
70+
71+
if (flutter_prebuilt_dart_sdk) {
72+
_os_name = target_os
73+
if (_os_name == "mac") {
74+
_os_name = "macos"
75+
} else if (_os_name == "win" || _os_name == "winuwp") {
76+
_os_name = "windows"
77+
}
78+
target_prebuilt_dart_sdk =
79+
"//flutter/prebuilts/$_os_name-$target_cpu/dart-sdk"
80+
host_prebuilt_dart_sdk = "//flutter/prebuilts/$_os_name-$host_cpu/dart-sdk"
81+
}

testing/testing.gni

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import("//build/compiled_action.gni")
66
import("//flutter/common/config.gni")
77
import("//third_party/dart/build/dart/dart_action.gni")
8+
import("//third_party/dart/sdk_args.gni")
89

910
is_aot_test =
1011
flutter_runtime_mode == "profile" || flutter_runtime_mode == "release"
@@ -56,18 +57,23 @@ template("dart_snapshot_kernel") {
5657
dart_action(target_name) {
5758
testonly = true
5859

59-
deps = [
60-
# This generates the Frontend server snapshot used in this Dart action as
61-
# well as the patched SDK.
62-
"//third_party/dart/utils/kernel-service:frontend_server",
63-
]
60+
if (flutter_prebuilt_dart_sdk) {
61+
deps = [ "//flutter:dart_sdk" ]
62+
script =
63+
"$root_out_dir/dart-sdk/bin/snapshots/frontend_server.dart.snapshot"
64+
} else {
65+
deps = [
66+
# This generates the Frontend server snapshot used in this Dart action as
67+
# well as the patched SDK.
68+
"//third_party/dart/utils/kernel-service:frontend_server",
69+
]
70+
script = "$root_out_dir/frontend_server.dart.snapshot"
71+
}
6472

6573
if (is_aot_test) {
6674
deps += [ "//flutter/lib/snapshot:strong_platform" ]
6775
}
6876

69-
script = "$root_out_dir/frontend_server.dart.snapshot"
70-
7177
inputs = [ invoker.dart_main ]
7278

7379
outputs = [ invoker.dart_kernel ]

tools/download_dart_sdk.py

Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
#!/usr/bin/env python3
2+
# Copyright 2013 The Flutter Authors. All rights reserved.
3+
# Use of this source code is governed by a BSD-style license that can be
4+
# found in the LICENSE file.
5+
6+
# When the environment variable in FLUTTER_PREBUILTS_ENV_VAR below is defined
7+
# and is not '0' or 'false', this script downloads the Dart SDK that matches the
8+
# version in the source tree and puts it in prebuilts/.
9+
#
10+
# The return code of this script will always be 0, even if there is an error,
11+
# unless the --fail-loudly flag is passed.
12+
13+
# TODO(zra): Eliminate this script and download through the DEPS file if/when
14+
# the Dart SDKs pulled by this script are uploaded to cipd.
15+
16+
import argparse
17+
import os
18+
import multiprocessing
19+
import platform
20+
import re
21+
import shutil
22+
import subprocess
23+
import sys
24+
import zipfile
25+
26+
27+
SRC_ROOT = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
28+
FLUTTER_DIR = os.path.join(SRC_ROOT, 'flutter')
29+
FLUTTER_PREBUILTS_DIR = os.path.join(FLUTTER_DIR, 'prebuilts')
30+
DART_DIR = os.path.join(SRC_ROOT, 'third_party', 'dart')
31+
DART_VERSION = os.path.join(DART_DIR, 'tools', 'VERSION')
32+
FLUTTER_PREBUILTS_ENV_VAR = 'FLUTTER_PREBUILT_DART_SDK'
33+
34+
35+
# The Dart SDK script is the source of truth about the sematic version.
36+
sys.path.append(os.path.join(DART_DIR, 'tools'))
37+
import utils
38+
39+
40+
# Prints to stderr.
41+
def eprint(*args, **kwargs):
42+
print(*args, file=sys.stderr, **kwargs)
43+
44+
45+
# Try to guess the host operating system.
46+
def GuessOS():
47+
os_name = utils.GuessOS()
48+
if os_name == 'win32':
49+
os_name = 'windows'
50+
if os_name not in ['linux', 'macos', 'windows']:
51+
eprint('Could not determine the OS: "%s"' % os_name)
52+
return None
53+
return os_name
54+
55+
56+
# For os `os_name` return a list of architectures for which prebuilts are
57+
# supported. Kepp in sync with `can_use_prebuilt_dart` in //flutter/tools/gn.
58+
def ArchitecturesForOS(os_name):
59+
if os_name == 'linux':
60+
return ['x64', 'arm64']
61+
elif os_name == 'macos':
62+
return ['x64', 'arm64']
63+
elif os_name =='windows':
64+
return ['x64']
65+
66+
eprint('Could not determine architectures for os "%s"' % os_name)
67+
return None
68+
69+
70+
# Downloads a Dart SDK to //flutter/prebuilts.
71+
def DownloadDartSDK(channel, version, os_name, arch):
72+
file = 'dartsdk-{}-{}-release.zip'.format(os_name, arch)
73+
url = 'https://storage.googleapis.com/dart-archive/channels/{}/raw/{}/sdk/{}'.format(
74+
channel, version, file,
75+
)
76+
dest = os.path.join(FLUTTER_PREBUILTS_DIR, file)
77+
78+
stamp_file = '{}.stamp'.format(dest)
79+
version_stamp = None
80+
try:
81+
with open(stamp_file) as fd:
82+
version_stamp = fd.read()
83+
except:
84+
version_stamp = 'none'
85+
86+
if version == version_stamp:
87+
# The prebuilt Dart SDK is already up-to-date. Indicate that the download
88+
# should be skipped by returning the empty string.
89+
return ''
90+
91+
if os.path.isfile(dest):
92+
os.unlink(dest)
93+
94+
curl_command = ['curl', url, '-o', dest]
95+
curl_result = subprocess.run(
96+
curl_command,
97+
stdout=subprocess.PIPE,
98+
stderr=subprocess.PIPE,
99+
universal_newlines=True,
100+
)
101+
if curl_result.returncode != 0:
102+
eprint('Failed to download: stdout:\n{}\nstderr:\n{}'.format(
103+
curl_result.stdout, curl_result.stderr,
104+
))
105+
return None
106+
107+
return dest
108+
109+
110+
# A custom ZipFile class that preserves file permissions.
111+
class ZipFileWithPermissions(zipfile.ZipFile):
112+
def _extract_member(self, member, targetpath, pwd):
113+
if not isinstance(member, zipfile.ZipInfo):
114+
member = self.getinfo(member)
115+
116+
targetpath = super()._extract_member(member, targetpath, pwd)
117+
118+
attr = member.external_attr >> 16
119+
if attr != 0:
120+
os.chmod(targetpath, attr)
121+
return targetpath
122+
123+
124+
# Extracts a Dart SDK in //fluter/prebuilts
125+
def ExtractDartSDK(archive, os_name, arch):
126+
os_arch = '{}-{}'.format(os_name, arch)
127+
dart_sdk = os.path.join(FLUTTER_PREBUILTS_DIR, os_arch, 'dart-sdk')
128+
if os.path.isdir(dart_sdk):
129+
shutil.rmtree(dart_sdk)
130+
131+
extract_dest = os.path.join(FLUTTER_PREBUILTS_DIR, os_arch)
132+
os.makedirs(extract_dest, exist_ok=True)
133+
134+
with ZipFileWithPermissions(archive, "r") as z:
135+
z.extractall(extract_dest)
136+
137+
138+
def DownloadAndExtract(channel, version, os_name, arch):
139+
archive = DownloadDartSDK(channel, version, os_name, arch)
140+
if archive == None:
141+
return 1
142+
if archive == '':
143+
return 0
144+
ExtractDartSDK(archive, os_name, arch)
145+
try:
146+
stamp_file = '{}.stamp'.format(archive)
147+
with open(stamp_file, "w") as fd:
148+
fd.write(version)
149+
except Exception as e:
150+
eprint('Failed to write Dart SDK version stamp file:\n{}'.format(e))
151+
return 1
152+
return 0
153+
154+
155+
def Main():
156+
parser = argparse.ArgumentParser()
157+
parser.add_argument(
158+
'--fail-loudly',
159+
action='store_true',
160+
default=False,
161+
help="Return an error code if a prebuilt couldn't be fetched and extracted")
162+
args = parser.parse_args()
163+
fail_loudly = 1 if args.fail_loudly else 0
164+
165+
prebuilt_enabled = os.environ.get(FLUTTER_PREBUILTS_ENV_VAR, 'false')
166+
if prebuilt_enabled == '0' or prebuilt_enabled.lower() == 'false':
167+
return 0
168+
169+
os.makedirs(FLUTTER_PREBUILTS_DIR, exist_ok=True)
170+
171+
# Read //third_party/dart/tools/VERSION to extract information about the
172+
# Dart SDK version.
173+
version = utils.ReadVersionFile()
174+
if version == None:
175+
return fail_loudly
176+
channel = version.channel
177+
178+
# A short Dart SDK version string used in the download url.
179+
if channel == 'be':
180+
dart_git_rev = utils.GetGitRevision()
181+
semantic_version = 'hash/{}'.format(dart_git_rev)
182+
semantic_version = utils.GetSemanticSDKVersion()
183+
184+
os_name = GuessOS()
185+
if os_name == None:
186+
return fail_loudly
187+
188+
architectures = ArchitecturesForOS(os_name)
189+
if architectures == None:
190+
return fail_loudly
191+
192+
# Download and extract variants in parallel
193+
pool = multiprocessing.Pool()
194+
tasks = [(channel, semantic_version, os_name, arch) for arch in architectures]
195+
async_results = [pool.apply_async(DownloadAndExtract, t) for t in tasks]
196+
success = True
197+
for async_result in async_results:
198+
result = async_result.get()
199+
success = success and (result == 0)
200+
201+
return 0 if success else fail_loudly
202+
203+
204+
if __name__ == '__main__':
205+
sys.exit(Main())

0 commit comments

Comments
 (0)