Implement macOS builds

This commit is contained in:
Janek Bevendorff 2025-03-09 17:47:06 +01:00
parent 4c7902577d
commit a1bb966666
No known key found for this signature in database
GPG Key ID: 2CF41D2AA8438F99

View File

@ -325,11 +325,6 @@ class Check(Command):
if not Path(src_dir).is_dir():
raise Error(f'Source directory "{src_dir}" does not exist!')
@staticmethod
def check_output_dir_does_not_exist(output_dir):
if Path(output_dir).is_dir():
raise Error(f'Output directory "{output_dir}" already exists. Please choose a different folder.')
@staticmethod
def check_git_repository(cwd):
if _run(['git', 'rev-parse', '--is-inside-work-tree'], check=False, cwd=cwd).returncode != 0:
@ -456,7 +451,8 @@ class Merge(Command):
tag_cmd.extend(['--sign', '--local-user', sign_key])
_run(tag_cmd, cwd=src_dir)
log_msg = ('All done! Don\'t forget to push the tags:\n'
log_msg = ('All done! Don\'t forget to push the release branch and the new tags:\n'
f' {_TERM_BOLD}git push origin {release_branch}{_TERM_RES}\n'
f' {_TERM_BOLD}git push origin tag {tag_name}{_TERM_RES}')
if not no_latest:
log_msg += f'\n {_TERM_BOLD}git push origin tag latest --force{_TERM_RES}'
@ -474,16 +470,20 @@ class Build(Command):
parser.add_argument('-o', '--output-dir', default='release',
help='Build output directory (default: %(default)s.')
parser.add_argument('-g', '--cmake-generator', help='Override default CMake generator.')
parser.add_argument('-i', '--install-prefix', help='Build install prefix.')
parser.add_argument('-i', '--install-prefix', default='/usr/local',
help='Build install prefix (default: %(default)s).')
parser.add_argument('-n', '--no-source-tarball', help='Don\'t create a source tarball.', action='store_true')
parser.add_argument('--snapshot', help='Build snapshot from current HEAD.', action='store_true')
parser.add_argument('--use-system-deps', help='Use system dependencies instead of vcpkg.', action='store_true')
parser.add_argument('-j', '--parallelism', default=os.cpu_count(), type=int,
help='Build parallelism (default: %(default)s).')
parser.add_argument('-y', '--yes', help='Bypass confirmation prompts.', action='store_true')
if sys.platform == 'darwin':
parser.add_argument('--macos-target', default=MACOSX_DEPLOYMENT_TARGET, metavar='MACOSX_DEPLOYMENT_TARGET',
help='macOS deployment target version (default: %(default)s).')
parser.add_argument('--platform-target', default=platform.uname().machine,
help='Build target platform (default: %(default)s).')
parser.add_argument('-p', '--platform-target', default=platform.uname().machine,
help='Build target platform (default: %(default)s).', choices=['x86_64', 'arm64'])
elif sys.platform == 'linux':
parser.add_argument('-d', '--docker-image', help='Run build in Docker image.')
parser.add_argument('--container-name', default='keepassxc-build',
@ -492,15 +492,29 @@ class Build(Command):
parser.add_argument('-c', '--cmake-opts', nargs=argparse.REMAINDER,
help='Additional CMake options (no other arguments can be specified after this).')
def run(self, tag_name, no_source_tarball, cmake_generator, **kwargs):
version, src_dir, output_dir = kwargs['version'], kwargs['src_dir'], kwargs['output_dir']
def run(self, version, src_dir, output_dir, tag_name, snapshot, no_source_tarball,
cmake_generator, install_prefix, yes, **kwargs):
Check.perform_basic_checks(src_dir)
Check.check_output_dir_does_not_exist(output_dir)
Path(output_dir).mkdir(parents=True)
src_dir = Path(src_dir).resolve()
output_dir = Path(output_dir)
if output_dir.is_dir():
logger.warning(f'Output directory "{output_dir}" already exists.')
if not yes and not _yes_no_prompt('Reuse existing output directory?'):
raise Error('Build aborted!')
else:
logger.info('Creating output directory...')
output_dir.mkdir(parents=True)
tag_name = tag_name or version
cmake_opts = ['-DWITH_XC_ALL=ON']
if kwargs['snapshot']:
cmake_opts = [
'-DWITH_XC_ALL=ON',
'-DCMAKE_BUILD_TYPE=Release',
'-DCMAKE_INSTALL_PREFIX=' + install_prefix
]
if not kwargs['use_system_deps']:
cmake_opts.append(f'-DCMAKE_TOOLCHAIN_FILE={self._get_vcpkg_toolchain_file()}')
if snapshot:
logger.info('Building a snapshot from HEAD.')
try:
Check.check_version_in_cmake(version, src_dir)
@ -515,18 +529,18 @@ class Build(Command):
cmake_opts.append('-DKEEPASSXC_BUILD_TYPE=Release')
if cmake_generator:
cmake_opts.append(f'-G"{cmake_generator}"')
cmake_opts.extend(['-G', cmake_generator])
kwargs['cmake_opts'] = cmake_opts + (kwargs['cmake_opts'] or [])
if not no_source_tarball:
self.build_source_tarball(version, tag_name, kwargs['src_dir'], kwargs['output_dir'])
if sys.platform == 'win32':
return self.build_windows(**kwargs)
return self.build_windows(version, src_dir, output_dir, **kwargs)
if sys.platform == 'darwin':
return self.build_macos(**kwargs)
return self.build_macos(version, src_dir, output_dir, **kwargs)
if sys.platform == 'linux':
return self.build_linux(**kwargs)
return self.build_linux(version, src_dir, output_dir, **kwargs)
raise Error('Unsupported build platform: %s', sys.platform)
@staticmethod
@ -537,7 +551,7 @@ class Build(Command):
toolchain = Path(vcpkg).parent / 'scripts' / 'buildsystems' / 'vcpkg.cmake'
if not toolchain.is_file():
raise Error('Toolchain file not found in vcpkg installation directory.')
return toolchain
return toolchain.resolve()
# noinspection PyMethodMayBeStatic
def build_source_tarball(self, version, tag_name, src_dir, output_dir):
@ -571,21 +585,35 @@ class Build(Command):
_run([comp, '-6', '--force', str(output_file.absolute())], cwd=src_dir)
# noinspection PyMethodMayBeStatic
def build_windows(self, version, src_dir, output_dir, snapshot, install_prefix, no_source_tarball,
use_system_deps, cmake_opts):
def build_windows(self, version, src_dir, output_dir, *, use_system_deps, parallelism, cmake_opts):
pass
# noinspection PyMethodMayBeStatic
def build_macos(self, version, src_dir, output_dir, snapshot, install_prefix, use_system_deps,
cmake_opts, macos_target, platform_target):
def build_macos(self, version, src_dir, output_dir, *, use_system_deps, parallelism, cmake_opts,
macos_target, platform_target):
if not use_system_deps:
cmake_opts.append(f'-DCMAKE_TOOLCHAIN_FILE={self._get_vcpkg_toolchain_file()}')
cmake_opts.append(f'-DVCPKG_TARGET_TRIPLET={platform_target.replace("86_", "")}-osx-dynamic-release')
cmake_opts.append(f'-DCMAKE_OSX_DEPLOYMENT_TARGET={macos_target}')
cmake_opts.append(f'-DCMAKE_OSX_ARCHITECTURES={platform_target}')
with tempfile.TemporaryDirectory() as build_dir:
logger.info('Configuring build...')
_run(['cmake', *cmake_opts, str(src_dir)], cwd=build_dir, capture_output=False)
logger.info('Compiling sources...')
_run(['cmake', '--build', '.', '--parallel', str(parallelism)], cwd=build_dir, capture_output=False)
logger.info('Packaging application...')
_run(['cpack', '-G', 'DragNDrop'], cwd=build_dir, capture_output=False)
output_file = Path(build_dir) / f'KeePassXC-{version}.dmg'
output_file.rename(output_dir / f'KeePassXC-{version}-{platform_target}.dmg')
logger.info('All done! Please don\'t forget to sign the binaries before distribution.')
# noinspection PyMethodMayBeStatic
def build_linux(self, version, src_dir, output_dir, snapshot, install_prefix, use_system_deps,
cmake_opts, docker_image, container_name):
def build_linux(self, version, src_dir, output_dir, *, use_system_deps, parallelism, cmake_opts,
docker_image, container_name):
pass