Skip to content

Commit 236733d

Browse files
authored
Merge pull request #140 from meta-flutter/feature/remote-platforms
Remote platforms
2 parents ef660bd + 39a9e70 commit 236733d

7 files changed

Lines changed: 158 additions & 46 deletions

File tree

.github/workflows/fedora.yml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,6 @@ jobs:
5050
echo "user ALL=(ALL) NOPASSWD: ALL" | tee /etc/sudoers.d/user
5151
5252
- uses: actions/checkout@v4
53-
with:
54-
fetch-depth: 1
5553

5654
- name: Create workspace
5755
shell: bash

.github/workflows/ubuntu-legacy.yml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,6 @@ jobs:
5555
echo "user ALL=(ALL) NOPASSWD: ALL" | tee /etc/sudoers.d/user
5656
5757
- uses: actions/checkout@v4
58-
with:
59-
fetch-depth: 1
6058

6159
- name: Fix workspace permissions
6260
shell: bash

.github/workflows/ubuntu.yml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,6 @@ jobs:
4545
env
4646
4747
- uses: actions/checkout@v4
48-
with:
49-
fetch-depth: 1
5048

5149
- name: Create workspace
5250
shell: bash

.github/workflows/windows.yaml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,6 @@ jobs:
4242
env
4343
4444
- uses: actions/checkout@v4
45-
with:
46-
fetch-depth: 1
4745

4846
- name: Build libcurl from source
4947
if: matrix.os == 'windows-11-arm'

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,8 @@ app
145145
flutter_*.log
146146
qemu_run.scpt
147147

148+
configs/remote_*.json
149+
148150
dart-revision.json
149151
engine-revision.json
150152

configs/desktop-homescreen.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
"HOMESCREEN_SRC_DIR": "${FLUTTER_WORKSPACE}/app/ivi-homescreen",
2020
"HOMESCREEN_BUILD_DIR": "${FLUTTER_WORKSPACE}/app/ivi-homescreen/build",
2121
"PLUGINS_DIR": "${FLUTTER_WORKSPACE}/app/ivi-homescreen-plugins/plugins",
22+
"+(;)EXT_PLUGINS_DIRS": "",
2223
"FIREBASE_SRC_DIR": "${FLUTTER_WORKSPACE}/app/firebase-cpp-sdk",
2324
"FIREBASE_BUILD_DIR": "${FLUTTER_WORKSPACE}/app/firebase-cpp-sdk/build",
2425
"PLUGIN_FIREBASE": "-DBUILD_PLUGIN_CLOUD_FIRESTORE=${FLUTTER_WORKSPACE_FIREBASE_CPP_SDK_LOAD} -DBUILD_PLUGIN_FIREBASE_AUTH=${FLUTTER_WORKSPACE_FIREBASE_CPP_SDK_LOAD} -DBUILD_PLUGIN_FIREBASE_STORAGE=${FLUTTER_WORKSPACE_FIREBASE_CPP_SDK_LOAD} -DFIREBASE_CPP_SDK_DIR=${FIREBASE_SRC_DIR} -DFIREBASE_SDK_LIBDIR=${FIREBASE_BUILD_DIR}",

flutter_workspace.py

Lines changed: 155 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,12 @@
2929
#
3030

3131
import argparse
32+
import glob
3233
import io
3334
import json
3435
import os
3536
import platform
37+
import re
3638
import shlex
3739
import shutil
3840
import signal
@@ -117,6 +119,7 @@ def main():
117119
parser.add_argument('--plex', default='', type=str, help='Platform Load Excludes')
118120
parser.add_argument('--enable', default='', type=str, help='Platform Load Enable Override')
119121
parser.add_argument('--disable', default='', type=str, help='Platform Load Disable Override')
122+
parser.add_argument('--remote', default='', type=str, help='Remote Platform Load Git Repo')
120123
parser.add_argument('--enable-plugin', default='', type=str, help='Plugin Enable')
121124
parser.add_argument('--disable-plugin', default='', type=str, help='Plugin Disable')
122125
parser.add_argument('--fastboot', default='', type=str, help='Update the selected platform using fastboot')
@@ -225,7 +228,11 @@ def main():
225228
#
226229
# Create Workspace
227230
#
228-
os.makedirs(workspace, exist_ok=True)
231+
os.makedirs(workspace, exist_ok=True) #
232+
app_folder = os.path.join(workspace, 'app')
233+
if not os.path.exists(app_folder):
234+
os.makedirs(app_folder)
235+
229236

230237
if os.path.exists(workspace):
231238
os.environ['FLUTTER_WORKSPACE'] = workspace
@@ -238,6 +245,27 @@ def main():
238245
get_flutter_engine_runtime(True, get_flutter_arch())
239246
return
240247

248+
249+
250+
#
251+
# Load Remote Platforms
252+
#
253+
254+
# First, clear linked platforms
255+
configs_dir = os.path.join(os.getcwd(), 'configs')
256+
if os.path.exists(configs_dir):
257+
for filename in sorted(glob.glob(os.path.join(configs_dir, 'remote_*.json'))):
258+
print(f'Unlinking remote config file: {filename}')
259+
os.unlink(filename)
260+
# Setup remote platform
261+
app_folder = os.path.join(workspace, 'app')
262+
if args.remote:
263+
# comma-separated list of git repos
264+
print(f"Loading Remote Platforms from: {args.remote}")
265+
remote_repos = args.remote.split(',')
266+
for repo in remote_repos:
267+
load_remote_platform(repo, app_folder)
268+
241269
#
242270
# Limit compiler threads
243271
#
@@ -332,7 +360,6 @@ def main():
332360
print("Invalid platform configuration")
333361
exit(1)
334362

335-
app_folder = os.path.join(workspace, 'app')
336363
flutter_sdk_folder = os.path.join(workspace, 'flutter')
337364

338365
vscode_folder = os.path.join(workspace, '.vscode')
@@ -378,7 +405,7 @@ def main():
378405
return
379406

380407
#
381-
# App folder setup
408+
# Get Repos
382409
#
383410
is_exist = os.path.exists(app_folder)
384411
if not is_exist:
@@ -867,14 +894,11 @@ def validate_custom_device_config(config):
867894
return True
868895

869896

870-
def get_repo(base_folder, uri, branch, rev):
897+
def get_repo(base_folder, uri, ref, branch=None):
871898
""" Clone Git Repo """
872899
if not uri:
873900
print("repo entry needs a 'uri' key. Skipping")
874901
return
875-
if not branch:
876-
print("repo entry needs a 'branch' key. Skipping")
877-
return
878902

879903
# get repo folder name
880904
repo_name = uri.rsplit('/', 1)[-1]
@@ -897,8 +921,14 @@ def get_repo(base_folder, uri, branch, rev):
897921
subprocess.check_call(cmd, cwd=git_folder)
898922

899923
# print_banner(f'git pull: {repo_name}')
900-
cmd = ['git', 'pull', 'origin', branch]
901-
subprocess.check_call(cmd, cwd=git_folder)
924+
cmd = ['git', 'pull', '--ff-only']
925+
if branch:
926+
print(f'Using branch: {branch}')
927+
cmd.extend(['origin', branch])
928+
try:
929+
subprocess.check_call(cmd, cwd=git_folder)
930+
except subprocess.CalledProcessError as e:
931+
print(f"WARNING: git pull failed, continuing anyway")
902932
else:
903933
# print_banner(f'Checking if folder exists: {git_folder}')
904934
if os.path.exists(git_folder):
@@ -907,19 +937,21 @@ def get_repo(base_folder, uri, branch, rev):
907937
except subprocess.CalledProcessError:
908938
pass
909939

910-
# print_banner(f'git clone {uri} -b {branch} {repo_name}')
911-
cmd = ['git', 'clone', uri, '-b', branch, repo_name]
940+
cmd = ['git', 'clone', uri, repo_name]
941+
if branch:
942+
print(f'Using branch: {branch}')
943+
cmd.extend(['-b', branch])
912944
subprocess.check_call(cmd, cwd=base_folder)
913945

914-
if rev:
915-
# print_banner(f'git checkout {rev}')
916-
cmd = ['git', 'checkout', rev]
946+
if ref:
947+
print(f'git checkout {ref}')
948+
cmd = ['git', 'checkout', ref]
917949
subprocess.check_call(cmd, cwd=git_folder)
918-
else:
919-
# print_banner(f'git checkout {branch}')
950+
elif branch:
951+
print(f'git checkout {branch}')
920952
cmd = ['git', 'checkout', branch]
921953
subprocess.check_call(cmd, cwd=git_folder)
922-
954+
923955
# get lfs
924956
git_lfs_file = os.path.join(base_folder, repo_name, '.gitattributes')
925957
# print_banner(f'Checking if folder exists: {git_lfs_file}')
@@ -939,6 +971,62 @@ def get_repo(base_folder, uri, branch, rev):
939971
print_banner(f'Fetched: {repo_name}')
940972

941973

974+
# Load Remote Platforms
975+
#
976+
# For reach --remote=<git repo> specified on command line
977+
# clone the repo into app/<repo name>
978+
# and link the files in app/<repo name>/configs/... to configs/...
979+
# (but make sure not to overwrite existing files, throw error if so)
980+
def load_remote_platform(remote, app_folder):
981+
""" Load Remote Platforms from GIT repo """
982+
if not remote:
983+
return
984+
985+
print_banner(f'Loading Remote Platforms from: {remote}')
986+
987+
remote_parts = remote.split('#')
988+
print(f'remote_parts: {remote_parts}')
989+
# get repo folder name
990+
repo_name = remote_parts[0].rsplit('/', 1)
991+
print(f'repo_name parts: {repo_name}')
992+
repo_name = repo_name[-1]
993+
print(f'repo_name before split: {repo_name}')
994+
repo_name = repo_name.split(".")[0]
995+
print(f'repo_name: {repo_name}')
996+
# get git ref from remote_uri
997+
git_uri = remote_parts[0]
998+
git_ref = remote_parts[1] if len(remote_parts) == 2 else None
999+
1000+
# get branch from ref (if starts with 'heads/)
1001+
git_branch = None
1002+
if git_ref and git_ref.startswith('heads/'):
1003+
git_branch = git_ref.split('heads/', 1)[1]
1004+
git_ref = None
1005+
1006+
get_repo(base_folder=app_folder, uri=git_uri, ref=git_ref, branch=git_branch)
1007+
git_folder = str(os.path.join(app_folder, repo_name))
1008+
1009+
# link files in app/<repo name>/configs/... to configs/...
1010+
remote_config_folder = os.path.join(git_folder, 'configs')
1011+
if os.path.exists(remote_config_folder):
1012+
import glob
1013+
for filename in sorted(glob.glob(os.path.join(remote_config_folder, '*.json'))):
1014+
1015+
filepath = os.path.join(os.getcwd(), filename)
1016+
_, tail = os.path.split(filename)
1017+
1018+
# link file as 'configs/remote_<file>'
1019+
dest_filepath = os.path.join(os.getcwd(), 'configs', f'remote_{tail}')
1020+
1021+
if os.path.exists(dest_filepath):
1022+
print(f'Config file already exists! skipping: {dest_filepath}')
1023+
else:
1024+
print(f'Linking config file: {dest_filepath}')
1025+
os.symlink(filepath, dest_filepath)
1026+
else:
1027+
print(f'No configs folder found in remote platform repo: {remote_config_folder}')
1028+
1029+
9421030
def get_workspace_repos(base_folder, config):
9431031
""" Clone GIT repos referenced in config repos dict to base_folder """
9441032
import concurrent.futures
@@ -952,7 +1040,7 @@ def get_workspace_repos(base_folder, config):
9521040
futures = []
9531041
for repo in repos:
9541042
futures.append(executor.submit(get_repo, base_folder=base_folder, uri=repo.get(
955-
'uri'), branch=repo.get('branch'), rev=repo.get('rev')))
1043+
'uri'), ref=repo.get('rev'), branch=repo.get('branch')))
9561044
validate_sudo_user()
9571045

9581046
for _ in concurrent.futures.as_completed(futures):
@@ -987,7 +1075,7 @@ def get_platform_src(src, base_folder: str):
9871075
futures = []
9881076
for repo in src:
9891077
futures.append(executor.submit(get_repo, base_folder=base_folder, uri=repo.get(
990-
'uri'), branch=repo.get('branch'), rev=repo.get('rev')))
1078+
'uri'), ref=repo.get('rev'), branch=repo.get('branch')))
9911079
validate_sudo_user()
9921080

9931081
for future in concurrent.futures.as_completed(futures):
@@ -2089,34 +2177,63 @@ def handle_dotenv(dotenv_files):
20892177
print(f'Loaded: {dotenv_path}')
20902178

20912179

2092-
def handle_env(env_variables, local_env, build_type=None):
2180+
def handle_env(env_variables, env=None, build_type=None):
20932181
if not env_variables:
20942182
return
2183+
2184+
if env is None:
2185+
env = os.environ
20952186

2187+
# If k starts with +, append to existing variable
20962188
for k, v in env_variables.items():
2097-
if local_env:
2098-
if 'PATH_PREPEND' in k:
2099-
local_env['PATH'] = os.path.normpath(os.path.expandvars(v)) + os.pathsep + local_env['PATH']
2100-
continue
2101-
if 'PATH_APPEND' in k:
2102-
local_env['PATH'] = local_env['PATH'] + os.pathsep + os.path.normpath(os.path.expandvars(v))
2103-
continue
2189+
print(f'Processing env var: {k} = {v}')
2190+
2191+
# Append to existing variable if the key starts with +
2192+
# (then remove the + from the key)
2193+
append = False
2194+
if k.startswith('+'):
2195+
append = True
2196+
k = k[1:]
2197+
2198+
# TODO: obsolete? remove in the future
2199+
if 'PATH_PREPEND' in k:
2200+
env['PATH'] = os.path.normpath(os.path.expandvars(v)) + os.pathsep + env['PATH']
2201+
continue
2202+
if 'PATH_APPEND' in k:
2203+
env['PATH'] = env['PATH'] + os.pathsep + os.path.normpath(os.path.expandvars(v))
2204+
continue
21042205

2105-
handle_build_type(local_env, build_type)
2206+
handle_build_type(env, build_type)
21062207

2107-
local_env[k] = os.path.normpath(os.path.expandvars(v))
2108-
else:
2109-
if 'PATH_PREPEND' in k:
2110-
os.environ['PATH'] = os.path.normpath(os.path.expandvars(v)) + os.pathsep + os.environ['PATH']
2111-
continue
2112-
if 'PATH_APPEND' in k:
2113-
os.environ['PATH'] = os.environ['PATH'] + os.pathsep + os.path.normpath(os.path.expandvars(v))
2208+
v = os.path.expandvars(v)
2209+
if append:
2210+
# If append empty string, skip
2211+
if k == '':
21142212
continue
21152213

2116-
handle_build_type(os.environ, build_type)
2214+
# if a separator is specified in the key like `(;)SOMETHING_SOMETHING`, extract it
2215+
2216+
# Separator extraction logic:
2217+
# sep = between '(' and ')'
2218+
# default separator is " "
2219+
sep = " "
2220+
if k.startswith('(') and ')' in k:
2221+
sep = k.split('(')[1].split(')')[0]
2222+
k = k.split(')')[1]
2223+
2224+
# Append to existing value
2225+
old_value = env.get(k, '')
2226+
if old_value != '':
2227+
v = old_value + sep + v
2228+
# skip append if there's no old value
2229+
2230+
env[k] = v
2231+
2232+
# NOTE: no idea why this is here but it works, DO NOT REMOVE IT (it's been here for 6 months)
2233+
if not env is os.environ:
2234+
os.environ[k] = v
21172235

2118-
os.environ[k] = os.path.normpath(os.path.expandvars(v))
2119-
# print(f'global: {k} = {os.environ[k]}')
2236+
print(f'Final env var: {k} = {os.environ[k]}')
21202237

21212238

21222239
def handle_build_type(env, build_type=None):

0 commit comments

Comments
 (0)