Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implemented custom directory structure #269

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
7 changes: 3 additions & 4 deletions fbs/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
from fbs._state import LOADED_PROFILES
from fbs_runtime import FbsError, _source
from fbs_runtime._fbs import get_core_settings, get_default_profiles
from fbs_runtime._settings import load_settings, expand_placeholders
from fbs_runtime._source import get_settings_paths
from fbs_runtime._settings import expand_placeholders
from fbs_runtime._source import load_settings_from_paths
from os.path import abspath

import sys
Expand Down Expand Up @@ -38,9 +38,8 @@ def activate_profile(profile_name):
"""
LOADED_PROFILES.append(profile_name)
project_dir = SETTINGS['project_dir']
json_paths = get_settings_paths(project_dir, LOADED_PROFILES)
core_settings = get_core_settings(project_dir)
SETTINGS.update(load_settings(json_paths, core_settings))
SETTINGS.update(load_settings_from_paths(project_dir, LOADED_PROFILES, core_settings))

def path(path_str):
"""
Expand Down
7 changes: 6 additions & 1 deletion fbs/_defaults/src/build/settings/base.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@
"src/unittest/python",
"src/integrationtest/python"
],
"python_dir": "src/main/python",
"icons_dir": "src/main/icons",
"resources_dir": "src/main/resources",
"settings_dir": "src/build/settings",
"freeze_config_dir": "src/freeze",
"files_to_filter": [
"src/build/docker/ubuntu/.bashrc", "src/build/docker/ubuntu/Dockerfile",
"src/build/docker/arch/.bashrc", "src/build/docker/arch/Dockerfile",
Expand All @@ -12,7 +17,7 @@
],
"hidden_imports": [],
"extra_pyinstaller_args": [],
"public_settings": ["app_name", "author", "version", "environment"],
"public_settings": ["app_name", "author", "version", "environment", "icons_dir", "resources_dir"],
"docker_images": {
"ubuntu": {
"build_files": ["requirements/", "src/sign/linux/"],
Expand Down
2 changes: 1 addition & 1 deletion fbs/builtin_commands/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ def run():
" pip install PySide2==5.12.2"
)
env = dict(os.environ)
pythonpath = path('src/main/python')
pythonpath = path(SETTINGS['python_dir'])
old_pythonpath = env.get('PYTHONPATH', '')
if old_pythonpath:
pythonpath += os.pathsep + old_pythonpath
Expand Down
4 changes: 2 additions & 2 deletions fbs/freeze/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,5 +81,5 @@ def _generate_resources():
resources_dest_dir = freeze_dir
for path_fn in default_path, path:
for profile in LOADED_PROFILES:
_copy(path_fn, 'src/main/resources/' + profile, resources_dest_dir)
_copy(path_fn, 'src/freeze/' + profile, freeze_dir)
_copy(path_fn, path('${resources_dir}/%s' % profile), resources_dest_dir)
_copy(path_fn, path('${freeze_config_dir}/%s' % profile), freeze_dir)
2 changes: 1 addition & 1 deletion fbs/freeze/linux.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
def freeze_linux(debug=False):
run_pyinstaller(debug=debug)
_generate_resources()
copy(path('src/main/icons/Icon.ico'), path('${freeze_dir}'))
copy(path('${icons_dir}/Icon.ico'), path('${freeze_dir}'))
# For some reason, PyInstaller packages libstdc++.so.6 even though it is
# available on most Linux distributions. If we include it and run our app on
# a different Ubuntu version, then Popen(...) calls fail with errors
Expand Down
6 changes: 3 additions & 3 deletions fbs/freeze/windows.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,14 @@ def freeze_windows(debug=False):
# The --windowed flag below prevents us from seeing any console output.
# We therefore only add it when we're not debugging.
args.append('--windowed')
args.extend(['--icon', path('src/main/icons/Icon.ico')])
args.extend(['--icon', path('${icons_dir}/Icon.ico')])
for path_fn in default_path, path:
_copy(path_fn, 'src/freeze/windows/version_info.py', path('target/PyInstaller'))
_copy(path_fn, '%s/windows/version_info.py' % SETTINGS['freeze_config_dir'], path('target/PyInstaller'))
args.extend(['--version-file', path('target/PyInstaller/version_info.py')])
run_pyinstaller(args, debug)
_restore_corrupted_python_dlls()
_generate_resources()
copy(path('src/main/icons/Icon.ico'), path('${freeze_dir}'))
copy(path('${icons_dir}/Icon.ico'), path('${freeze_dir}'))
_add_missing_dlls()

def _restore_corrupted_python_dlls():
Expand Down
2 changes: 1 addition & 1 deletion fbs/resources.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ def get_icons():
"""
result = {}
for profile in LOADED_PROFILES:
icons_dir = 'src/main/icons/' + profile
icons_dir = path('${icons_dir}/%s' % profile)
for icon_path in glob(path(icons_dir + '/*.png')):
name = splitext(basename(icon_path))[0]
match = re.match('(\d+)(?:@(\d+)x)?', name)
Expand Down
2 changes: 1 addition & 1 deletion fbs/sign/windows.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ def sign_windows():
if 'windows_sign_pass' not in SETTINGS:
raise FbsError(
"Please set 'windows_sign_pass' to the password of %s in either "
"src/build/settings/secret.json, .../windows.json or .../base.json."
"%s/secret.json, .../windows.json or .../base.json." % SETTINGS['settings_dir']
% _CERTIFICATE_PATH
)
for subdir, _, files in os.walk(path('${freeze_dir}')):
Expand Down
23 changes: 15 additions & 8 deletions fbs_runtime/_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,16 +31,23 @@ def load_settings(json_paths, base=None):
for json_path in json_paths:
with open(json_path, 'r', encoding='utf-8') as f:
data = json.load(f)
result = data if result is None else _merge(result, data)
result = data if result is None else merge_settings(result, data)

return expland_all_placeholders(result)

def expland_all_placeholders(settings):
if settings is None:
return {}

while True:
for key, value in result.items():
new_value = expand_placeholders(value, result)
for key, value in settings.items():
new_value = expand_placeholders(value, settings)
if new_value != value:
result[key] = new_value
settings[key] = new_value
break
else:
break
return result
return settings

def expand_placeholders(obj, settings):
if isinstance(obj, str):
Expand All @@ -52,14 +59,14 @@ def expand_placeholders(obj, settings):
return {k: expand_placeholders(v, settings) for k, v in obj.items()}
return obj

def _merge(a, b):
def merge_settings(a, b):
if type(a) != type(b):
raise ValueError('Cannot merge %r and %r' % (a, b))
if isinstance(a, list):
return a + b
if isinstance(a, dict):
result = dict(a)
for k, v in b.items():
result[k] = _merge(a[k], v) if k in a else v
return result
result[k] = merge_settings(a[k], v) if k in a else v
return expland_all_placeholders(result)
return b
43 changes: 31 additions & 12 deletions fbs_runtime/_source.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from fbs_runtime import FbsError
from fbs_runtime._fbs import get_default_profiles, get_core_settings, \
filter_public_settings
from fbs_runtime._settings import load_settings
from fbs_runtime._settings import load_settings, merge_settings
from os.path import join, normpath, dirname, pardir, exists
from pathlib import Path

Expand All @@ -15,17 +15,17 @@
def get_project_dir():
result = Path(os.getcwd())
while result != result.parent:
if (result / 'src' / 'main' / 'python').is_dir():
if (result / 'src' / 'build' / 'settings').is_dir() or Path('fbs_directories.json').is_file():
return str(result)
result = result.parent
raise FbsError(
'Could not determine the project base directory. '
'Was expecting src/main/python.'
'Was expecting src/build/settings or fbs_directories.json.'
)

def get_resource_dirs(project_dir):
result = [path(project_dir, 'src/main/icons')]
resources = path(project_dir, 'src/main/resources')
def get_resource_dirs(project_dir, settings):
result = [path(project_dir, settings['icons_dir'])]
resources = path(project_dir, settings['resources_dir'])
result.extend(
join(resources, profile)
# Resource dirs are listed most-specific first whereas profiles are
Expand All @@ -37,17 +37,36 @@ def get_resource_dirs(project_dir):
def load_build_settings(project_dir):
core_settings = get_core_settings(project_dir)
profiles = get_default_profiles()
json_paths = get_settings_paths(project_dir, profiles)
all_settings = load_settings(json_paths, core_settings)

all_settings = load_settings_from_paths(project_dir, profiles, core_settings)

return filter_public_settings(all_settings)

def get_settings_paths(project_dir, profiles):
return list(filter(exists, (
path_fn('src/build/settings/%s.json' % profile)
for path_fn in (default_path, lambda p: path(project_dir, p))
def load_settings_from_paths(project_dir, profiles, core_settings):
initial_settings_paths = get_settings_paths(lambda p: default_path("src/build/settings/%s" % p), profiles)

path_settings_file = path(project_dir, "fbs_directories.json")
if exists(path_settings_file):
initial_settings_paths.append(path_settings_file)

initial_settings = load_settings(initial_settings_paths, core_settings)

# extract project settings paths
user_settings_dirs = get_settings_paths(
lambda p: path(project_dir, "%s/%s" % (initial_settings['settings_dir'], p)), profiles)

user_settings = load_settings(user_settings_dirs)

return merge_settings(initial_settings, user_settings)

def get_settings_paths(path_fn, profiles):
build_settings_paths = list(filter(exists, (
path_fn('%s.json' % profile)
for profile in profiles
)))

return build_settings_paths

def default_path(path_str):
defaults_dir = join(dirname(__file__), pardir, 'fbs', '_defaults')
return path(defaults_dir, path_str)
Expand Down
2 changes: 1 addition & 1 deletion fbs_runtime/application_context/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ def _resource_locator(self):
if is_frozen():
resource_dirs = _frozen.get_resource_dirs()
else:
resource_dirs = _source.get_resource_dirs(self._project_dir)
resource_dirs = _source.get_resource_dirs(self._project_dir, self.build_settings)
return ResourceLocator(resource_dirs)
@cached_property
def _project_dir(self):
Expand Down