mirror of
https://forge.fsky.io/oneflux/omegafox.git
synced 2026-02-10 04:52:03 -08:00
multibuild: Allow multiple build targets, Makefile changes, more.
- Allow multiple OS & arch build targets to be passed in multibuild.py - Add make set-target to change target OS & arch - Cleanup/refactor patch.py and package.py, move common functions to mixin file
This commit is contained in:
parent
717aa9db36
commit
bf245006b2
8 changed files with 410 additions and 282 deletions
22
Makefile
22
Makefile
|
|
@ -8,7 +8,7 @@ debs := python3 python3-dev python3-pip p7zip-full golang-go msitools wget aria2
|
|||
rpms := python3 python3-devel p7zip golang msitools wget aria2c
|
||||
pacman := python python-pip p7zip go msitools wget aria2c
|
||||
|
||||
.PHONY: help fetch setup setup-minimal clean distclean build package build-launcher check-arch revert edits run bootstrap mozbootstrap dir package-common package-linux package-macos package-windows
|
||||
.PHONY: help fetch setup setup-minimal clean set-target distclean build package build-launcher check-arch revert edits run bootstrap mozbootstrap dir package-linux package-macos package-windows
|
||||
|
||||
help:
|
||||
@echo "Available targets:"
|
||||
|
|
@ -23,6 +23,7 @@ help:
|
|||
@echo " clean - Remove build artifacts"
|
||||
@echo " distclean - Remove everything including downloads"
|
||||
@echo " build - Build Camoufox"
|
||||
@echo " set-target - Change the build target with BUILD_TARGET"
|
||||
@echo " package-linux - Package Camoufox for Linux"
|
||||
@echo " package-macos - Package Camoufox for macOS"
|
||||
@echo " package-windows - Package Camoufox for Windows"
|
||||
|
|
@ -63,6 +64,9 @@ dir:
|
|||
python3 scripts/patch.py $(version) $(release)
|
||||
touch $(cf_source_dir)/_READY
|
||||
|
||||
set-target:
|
||||
python3 scripts/patch.py $(version) $(release) --mozconfig-only
|
||||
|
||||
mozbootstrap:
|
||||
cd $(cf_source_dir) && MOZBUILD_STATE_PATH=$$HOME/.mozbuild ./mach --no-interactive bootstrap --application-choice=browser
|
||||
|
||||
|
|
@ -93,19 +97,15 @@ edits:
|
|||
python ./scripts/developer.py
|
||||
|
||||
check-arch:
|
||||
@if [ "$(arch)" != "x64" ] && [ "$(arch)" != "x86" ] && [ "$(arch)" != "arm64" ]; then \
|
||||
echo "Error: Invalid arch value. Must be x64, x86, or arm64."; \
|
||||
@if ! echo "x86_64 i686 arm64" | grep -qw "$(arch)"; then \
|
||||
echo "Error: Invalid arch value. Must be x86_64, i686, or arm64."; \
|
||||
exit 1; \
|
||||
fi
|
||||
|
||||
build-launcher: check-arch
|
||||
cd launcher && ./build.sh $(arch) $(os)
|
||||
cd launcher && bash build.sh $(arch) $(os)
|
||||
|
||||
package-common: check-arch
|
||||
cd $(cf_source_dir) && cat browser/locales/shipped-locales | xargs ./mach package-multi-locale --locales
|
||||
cp -v $(cf_source_dir)/obj-*/dist/camoufox-$(version)-$(release).*.* .
|
||||
|
||||
package-linux: package-common
|
||||
package-linux:
|
||||
make build-launcher arch=$(arch) os=linux;
|
||||
python3 scripts/package.py linux \
|
||||
--includes \
|
||||
|
|
@ -116,7 +116,7 @@ package-linux: package-common
|
|||
--arch $(arch) \
|
||||
--fonts windows macos linux
|
||||
|
||||
package-macos: package-common
|
||||
package-macos:
|
||||
make build-launcher arch=$(arch) os=macos;
|
||||
python3 scripts/package.py macos \
|
||||
--includes \
|
||||
|
|
@ -126,7 +126,7 @@ package-macos: package-common
|
|||
--arch $(arch) \
|
||||
--fonts windows linux
|
||||
|
||||
package-windows: package-common
|
||||
package-windows:
|
||||
make build-launcher arch=$(arch) os=windows;
|
||||
python3 scripts/package.py windows \
|
||||
--includes \
|
||||
|
|
|
|||
44
README.md
44
README.md
|
|
@ -217,7 +217,7 @@ Miscellaneous (WebGl spoofing, battery status, etc)
|
|||
|
||||
#### Playwright support
|
||||
|
||||
- A more updated version of Playwright's Juggler for the latest Firefox, maintained by me
|
||||
- Updated Playwright's Juggler for the latest Firefox
|
||||
- Various config patches to evade bot detection
|
||||
|
||||
#### Debloat/Optimizations
|
||||
|
|
@ -322,16 +322,33 @@ After that, you have to bootstrap your system to be able to build Camoufox. You
|
|||
make bootstrap
|
||||
```
|
||||
|
||||
Finally you can build Camoufox and then package it with the following commands:
|
||||
Finally you can build and package Camoufox the following command:
|
||||
|
||||
```bash
|
||||
make build
|
||||
# Example package commands:
|
||||
make package-linux arch=x64
|
||||
make package-windows arch=x86
|
||||
make package-macos arch=amd64
|
||||
python3 multibuild.py --target linux windows macos --arch x86_64 arm64 i686
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>
|
||||
CLI Parameters
|
||||
</summary>
|
||||
|
||||
```bash
|
||||
Options:
|
||||
-h, --help show this help message and exit
|
||||
--target {linux,windows,macos} [{linux,windows,macos} ...]
|
||||
Target platforms to build
|
||||
--arch {x86_64,arm64,i686} [{x86_64,arm64,i686} ...]
|
||||
Target architectures to build for each platform
|
||||
--bootstrap Bootstrap the build system
|
||||
--clean Clean the build directory before starting
|
||||
|
||||
Example:
|
||||
$ python3 multibuild.py --target linux windows macos --arch x86_64 arm64
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
### Using Docker
|
||||
|
||||
Camoufox can be built through Docker on all platforms.
|
||||
|
|
@ -374,10 +391,15 @@ Docker CLI Parameters
|
|||
```bash
|
||||
Options:
|
||||
-h, --help show this help message and exit
|
||||
--target {linux,windows,macos}
|
||||
Target platform for the build
|
||||
--arch {x86_64,arm64,i686}
|
||||
Target architecture for the build
|
||||
--target {linux,windows,macos} [{linux,windows,macos} ...]
|
||||
Target platforms to build
|
||||
--arch {x86_64,arm64,i686} [{x86_64,arm64,i686} ...]
|
||||
Target architectures to build for each platform
|
||||
--bootstrap Bootstrap the build system
|
||||
--clean Clean the build directory before starting
|
||||
|
||||
Example:
|
||||
$ docker run -v "$(pwd)/dist:/app/dist" camoufox-builder --target windows macos linux --arch x86_64 arm64 i686
|
||||
```
|
||||
|
||||
</details>
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
if [ $# -ne 2 ]; then
|
||||
echo "Usage: $0 <arch> <os>"
|
||||
echo "arch: x64, x86, arm64"
|
||||
echo "arch: x86_64, i686, arm64"
|
||||
echo "os: linux, windows, macos"
|
||||
exit 1
|
||||
fi
|
||||
|
|
@ -11,10 +11,10 @@ ARCH=$1
|
|||
OS=$2
|
||||
|
||||
case $ARCH in
|
||||
x64) GOARCH=amd64 ;;
|
||||
x86) GOARCH=386 ;;
|
||||
arm64) GOARCH=arm64 ;;
|
||||
*) echo "Invalid architecture"; exit 1 ;;
|
||||
x86_64) GOARCH=amd64 ;;
|
||||
i686) GOARCH=386 ;;
|
||||
arm64) GOARCH=arm64 ;;
|
||||
*) echo "Invalid architecture"; exit 1 ;;
|
||||
esac
|
||||
|
||||
case $OS in
|
||||
|
|
|
|||
110
multibuild.py
110
multibuild.py
|
|
@ -1,5 +1,18 @@
|
|||
"""
|
||||
Easy build CLI for Camoufox
|
||||
|
||||
options:
|
||||
-h, --help show this help message and exit
|
||||
--target {linux,windows,macos} [{linux,windows,macos} ...]
|
||||
Target platforms to build
|
||||
--arch {x86_64,arm64,i686} [{x86_64,arm64,i686} ...]
|
||||
Target architectures to build for each platform
|
||||
--bootstrap Bootstrap the build system
|
||||
--clean Clean the build directory before starting
|
||||
|
||||
Example:
|
||||
$ python3 multibuild.py --target linux windows macos --arch x86_64 arm64
|
||||
|
||||
Since Camoufox is NOT meant to be used as a daily driver, no installers are provided.
|
||||
"""
|
||||
|
||||
|
|
@ -15,11 +28,11 @@ AVAILABLE_TARGETS = ["linux", "windows", "macos"]
|
|||
AVAILABLE_ARCHS = ["x86_64", "arm64", "i686"]
|
||||
|
||||
|
||||
def exec(cmd, exit_on_fail=True):
|
||||
def run(cmd, exit_on_fail=True):
|
||||
print(f'\n------------\n{cmd}\n------------\n')
|
||||
retval = os.system(cmd)
|
||||
if retval != 0 and exit_on_fail:
|
||||
print("fatal error: command '{}' failed".format(cmd))
|
||||
print(f"fatal error: command '{cmd}' failed")
|
||||
sys.exit(1)
|
||||
return retval
|
||||
|
||||
|
|
@ -29,51 +42,43 @@ class BSYS:
|
|||
target: str
|
||||
arch: str
|
||||
|
||||
def bootstrap(self):
|
||||
exec('make bootstrap')
|
||||
@staticmethod
|
||||
def bootstrap():
|
||||
"""Bootstrap the build system"""
|
||||
run('make bootstrap')
|
||||
|
||||
def build(self):
|
||||
"""Build the Camoufox source code"""
|
||||
os.environ['BUILD_TARGET'] = f'{self.target},{self.arch}'
|
||||
exec(f'make build')
|
||||
run('make build')
|
||||
|
||||
def package(self):
|
||||
if self.arch == 'x86_64':
|
||||
_arch = 'x64'
|
||||
else:
|
||||
_arch = 'arm64'
|
||||
exec(f'make package-{self.target} arch={_arch}')
|
||||
"""Package the Camoufox source code"""
|
||||
run(f'make package-{self.target} arch={self.arch}')
|
||||
|
||||
def update_target(self):
|
||||
"""Change the build target"""
|
||||
os.environ['BUILD_TARGET'] = f'{self.target},{self.arch}'
|
||||
run('make set-target')
|
||||
|
||||
@property
|
||||
def assets(self) -> List[str]:
|
||||
package_pattern = f'camoufox-*.en-US.*.zip'
|
||||
"""Get the list of assets"""
|
||||
package_pattern = 'camoufox-*.en-US.*.zip'
|
||||
return glob.glob(package_pattern)
|
||||
|
||||
def clean(self):
|
||||
exec('make clean')
|
||||
@staticmethod
|
||||
def clean():
|
||||
"""Clean the Camoufox directory"""
|
||||
run('make clean')
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Easy build CLI for Camoufox")
|
||||
parser.add_argument(
|
||||
"--target", choices=AVAILABLE_TARGETS, required=True, help="Target platform for the build"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--arch", choices=AVAILABLE_ARCHS, required=True, help="Target architecture for the build"
|
||||
)
|
||||
parser.add_argument("--bootstrap", action="store_true", help="Bootstrap the build system")
|
||||
parser.add_argument(
|
||||
"--clean", action="store_true", help="Clean the build directory before starting"
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
builder = BSYS(args.target, args.arch)
|
||||
# Run bootstrap if requested
|
||||
if args.bootstrap:
|
||||
builder.bootstrap()
|
||||
# Clean if requested
|
||||
if args.clean:
|
||||
builder.clean()
|
||||
def run_build(target, arch):
|
||||
"""
|
||||
Run the build for the given target and architecture
|
||||
"""
|
||||
builder = BSYS(target, arch)
|
||||
builder.update_target()
|
||||
# Run build
|
||||
builder.build()
|
||||
# Run package
|
||||
|
|
@ -83,5 +88,40 @@ def main():
|
|||
os.rename(asset, f'dist/{asset}')
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Easy build CLI for Camoufox")
|
||||
parser.add_argument(
|
||||
"--target",
|
||||
choices=AVAILABLE_TARGETS,
|
||||
nargs='+',
|
||||
required=True,
|
||||
help="Target platform for the build",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--arch",
|
||||
choices=AVAILABLE_ARCHS,
|
||||
nargs='+',
|
||||
required=True,
|
||||
help="Target architecture for the build",
|
||||
)
|
||||
parser.add_argument("--bootstrap", action="store_true", help="Bootstrap the build system")
|
||||
parser.add_argument(
|
||||
"--clean", action="store_true", help="Clean the build directory before starting"
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
# Run bootstrap if requested
|
||||
if args.bootstrap:
|
||||
BSYS.bootstrap()
|
||||
# Clean if requested
|
||||
if args.clean:
|
||||
BSYS.clean()
|
||||
# Run build
|
||||
for target in args.target:
|
||||
for arch in args.arch:
|
||||
run_build(target, arch)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
|
|||
133
scripts/_mixin.py
Normal file
133
scripts/_mixin.py
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
"""
|
||||
Common functions used across the Camoufox build system.
|
||||
Not meant to be called directly.
|
||||
"""
|
||||
|
||||
import contextlib
|
||||
import fnmatch
|
||||
import optparse
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
|
||||
start_time = time.time()
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def temp_cd(path):
|
||||
"""Temporarily change to a different working directory"""
|
||||
_old_cwd = os.getcwd()
|
||||
abs_path = os.path.abspath(path)
|
||||
assert os.path.exists(abs_path), f'{abs_path} does not exist.'
|
||||
os.chdir(abs_path)
|
||||
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
os.chdir(_old_cwd)
|
||||
|
||||
|
||||
def get_options():
|
||||
"""Get options"""
|
||||
parser = optparse.OptionParser()
|
||||
parser.add_option('--mozconfig-only', dest='mozconfig_only', default=False, action="store_true")
|
||||
parser.add_option(
|
||||
'-P', '--no-settings-pane', dest='settings_pane', default=True, action="store_false"
|
||||
)
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def find_src_dir(root_dir='.', version=None, release=None):
|
||||
"""Get the source directory"""
|
||||
if version and release:
|
||||
name = os.path.join(root_dir, f'camoufox-{version}-{release}')
|
||||
assert os.path.exists(name), f'{name} does not exist.'
|
||||
return name
|
||||
folders = os.listdir(root_dir)
|
||||
for folder in folders:
|
||||
if os.path.isdir(folder) and folder.startswith('camoufox-'):
|
||||
return os.path.join(root_dir, folder)
|
||||
raise FileNotFoundError('No camoufox-* folder found')
|
||||
|
||||
|
||||
def get_moz_target(target, arch):
|
||||
"""Get moz_target from target and arch"""
|
||||
if target == "linux":
|
||||
return "aarch64-unknown-linux-gnu" if arch == "arm64" else f"{arch}-pc-linux-gnu"
|
||||
if target == "windows":
|
||||
return f"{arch}-pc-mingw32"
|
||||
if target == "macos":
|
||||
return "aarch64-apple-darwin" if arch == "arm64" else f"{arch}-apple-darwin"
|
||||
raise ValueError(f"Unsupported target: {target}")
|
||||
|
||||
|
||||
def list_files(root_dir, suffix):
|
||||
"""List files in a directory"""
|
||||
for root, _, files in os.walk(root_dir):
|
||||
for file in fnmatch.filter(files, suffix):
|
||||
full_path = os.path.join(root, file)
|
||||
relative_path = os.path.relpath(full_path, root_dir)
|
||||
yield os.path.join(root_dir, relative_path).replace('\\', '/')
|
||||
|
||||
|
||||
def list_patches(root_dir='../patches', suffix='*.patch'):
|
||||
"""List all patch files"""
|
||||
return sorted(list_files(root_dir, suffix), key=os.path.basename)
|
||||
|
||||
|
||||
def script_exit(statuscode):
|
||||
"""Exit the script"""
|
||||
if (time.time() - start_time) > 60:
|
||||
# print elapsed time
|
||||
elapsed = time.strftime("%H:%M:%S", time.gmtime(time.time() - start_time))
|
||||
print(f"\n\aElapsed time: {elapsed}")
|
||||
sys.stdout.flush()
|
||||
|
||||
sys.exit(statuscode)
|
||||
|
||||
|
||||
def run(cmd, exit_on_fail=True, do_print=True):
|
||||
"""Run a command"""
|
||||
if not cmd:
|
||||
return
|
||||
if do_print:
|
||||
print(cmd)
|
||||
sys.stdout.flush()
|
||||
retval = os.system(cmd)
|
||||
if retval != 0 and exit_on_fail:
|
||||
print(f"fatal error: command '{cmd}' failed")
|
||||
sys.stdout.flush()
|
||||
script_exit(1)
|
||||
return retval
|
||||
|
||||
|
||||
def patch(patchfile, reverse=False, silent=False):
|
||||
"""Run a patch file"""
|
||||
if reverse:
|
||||
cmd = f"patch -p1 -R -i {patchfile}"
|
||||
else:
|
||||
cmd = f"patch -p1 -i {patchfile}"
|
||||
if silent:
|
||||
cmd += ' > /dev/null'
|
||||
else:
|
||||
print(f"\n*** -> {cmd}")
|
||||
sys.stdout.flush()
|
||||
run(cmd)
|
||||
|
||||
|
||||
__all__ = [
|
||||
'get_moz_target',
|
||||
'list_patches',
|
||||
'patch',
|
||||
'run',
|
||||
'script_exit',
|
||||
'temp_cd',
|
||||
'get_options',
|
||||
]
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
print('This is a module, not meant to be called directly.')
|
||||
sys.exit(1)
|
||||
|
|
@ -4,44 +4,28 @@
|
|||
GUI for managing Camoufox patches.
|
||||
"""
|
||||
|
||||
import contextlib
|
||||
import os
|
||||
import re
|
||||
|
||||
import easygui
|
||||
from patch import list_patches, patch, run
|
||||
from _mixin import find_src_dir, list_patches, patch, run, temp_cd
|
||||
|
||||
|
||||
def into_camoufox_dir():
|
||||
"""Find and cd to the camoufox-* folder (this is located ..)"""
|
||||
os.chdir(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
folders = os.listdir('.')
|
||||
for folder in folders:
|
||||
if os.path.isdir(folder) and folder.startswith('camoufox-'):
|
||||
os.chdir(folder)
|
||||
break
|
||||
else:
|
||||
raise FileNotFoundError('No camoufox-* folder found')
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def temp_cd(path):
|
||||
# Temporarily change to a different working directory
|
||||
_old_cwd = os.getcwd()
|
||||
os.chdir(os.path.abspath(path))
|
||||
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
os.chdir(_old_cwd)
|
||||
|
||||
"""Cd to the camoufox-* folder"""
|
||||
this_script = os.path.dirname(os.path.abspath(__file__))
|
||||
# Go one directory up from the current script path
|
||||
os.chdir(os.path.dirname(this_script))
|
||||
os.chdir(find_src_dir('.'))
|
||||
|
||||
def reset_camoufox():
|
||||
"""Reset the Camoufox source"""
|
||||
with temp_cd('..'):
|
||||
run('make clean')
|
||||
|
||||
|
||||
def run_patches(reverse=False):
|
||||
"""Apply patches"""
|
||||
patch_files = list_patches()
|
||||
if reverse:
|
||||
title = "Unpatch files"
|
||||
|
|
@ -141,12 +125,13 @@ def check_patch(patch_file):
|
|||
|
||||
|
||||
def is_broken(patch_file):
|
||||
"""Check if a patch file is broken"""
|
||||
_, _, is_broken = check_patch(patch_file)
|
||||
return is_broken
|
||||
|
||||
|
||||
def get_rejects(patch_file):
|
||||
# Returns a broken patch's rejects
|
||||
"""Get rejects from a patch file"""
|
||||
cmd = f'patch -p1 -i "{patch_file}" | tee /dev/stderr | sed -n -E \'s/^.*saving rejects to file (.*\\.rej)$/\\1/p\''
|
||||
result = os.popen(cmd).read().strip()
|
||||
return result.split('\n') if result else []
|
||||
|
|
@ -173,6 +158,7 @@ GUI Choicebox
|
|||
|
||||
|
||||
def handle_choice(choice):
|
||||
"""Handle UI choice"""
|
||||
match choice:
|
||||
case "Reset workspace":
|
||||
reset_camoufox()
|
||||
|
|
|
|||
|
|
@ -3,23 +3,23 @@
|
|||
import argparse
|
||||
import glob
|
||||
import os
|
||||
import shlex
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
from _mixin import find_src_dir, get_moz_target, run, temp_cd
|
||||
|
||||
UNNEEDED_PATHS = {'uninstall', 'pingsender.exe', 'pingsender', 'vaapitest', 'glxtest'}
|
||||
|
||||
|
||||
def run_command(command):
|
||||
result = subprocess.run(command, capture_output=True, text=True)
|
||||
if result.returncode != 0:
|
||||
print(f"Error executing command: {' '.join(command)}")
|
||||
print(f"Error output: {result.stderr}")
|
||||
exit(1)
|
||||
return result.stdout
|
||||
"""Execute a command with subprocess"""
|
||||
cmd = ' '.join(shlex.quote(arg) for arg in command)
|
||||
run(cmd)
|
||||
|
||||
|
||||
def add_includes_to_package(package_file, includes, fonts, new_file, os):
|
||||
def add_includes_to_package(package_file, includes, fonts, new_file, target):
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
# Extract package
|
||||
run_command(['7z', 'x', package_file, f'-o{temp_dir}'])
|
||||
|
|
@ -33,10 +33,10 @@ def add_includes_to_package(package_file, includes, fonts, new_file, os):
|
|||
includes=includes,
|
||||
fonts=fonts,
|
||||
new_file=new_file,
|
||||
os=os,
|
||||
target=target,
|
||||
)
|
||||
|
||||
if os == 'macos':
|
||||
if target == 'macos':
|
||||
# Move Nightly/Nightly.app -> Camoufox.app
|
||||
nightly_dir = os.path.join(temp_dir, 'Nightly')
|
||||
shutil.move(
|
||||
|
|
@ -56,7 +56,7 @@ def add_includes_to_package(package_file, includes, fonts, new_file, os):
|
|||
os.rmdir(camoufox_dir)
|
||||
|
||||
# Create target_dir
|
||||
if os == 'macos':
|
||||
if target == 'macos':
|
||||
target_dir = os.path.join(temp_dir, 'Camoufox.app', 'Contents', 'Resources')
|
||||
else:
|
||||
target_dir = temp_dir
|
||||
|
|
@ -84,7 +84,7 @@ def add_includes_to_package(package_file, includes, fonts, new_file, os):
|
|||
# Add launcher from launcher/dist/launch to temp_dir
|
||||
shutil.copy2(
|
||||
os.path.join('launcher', 'dist', 'launch'),
|
||||
os.path.join(temp_dir, 'launch' + ('.exe' if os == 'windows' else '')),
|
||||
os.path.join(temp_dir, 'launch' + ('.exe' if target == 'windows' else '')),
|
||||
)
|
||||
|
||||
# Remove unneeded paths
|
||||
|
|
@ -98,7 +98,8 @@ def add_includes_to_package(package_file, includes, fonts, new_file, os):
|
|||
run_command(['7z', 'u', new_file, f'{temp_dir}/*', '-r', '-mx=9'])
|
||||
|
||||
|
||||
def main():
|
||||
def get_args():
|
||||
"""Get CLI parameters"""
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Package Camoufox for different operating systems.'
|
||||
)
|
||||
|
|
@ -109,20 +110,47 @@ def main():
|
|||
parser.add_argument('--version', required=True, help='Camoufox version')
|
||||
parser.add_argument('--release', required=True, help='Camoufox release number')
|
||||
parser.add_argument(
|
||||
'--arch', choices=['x64', 'x86', 'arm64'], help='Architecture for Windows build'
|
||||
'--arch', choices=['x86_64', 'i686', 'arm64'], help='Architecture for Windows build'
|
||||
)
|
||||
parser.add_argument('--no-locales', action='store_true', help='Do not package locales')
|
||||
parser.add_argument('--fonts', nargs='+', help='Font directories to include under fonts/')
|
||||
args = parser.parse_args()
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def main():
|
||||
"""The main packaging function"""
|
||||
args = get_args()
|
||||
|
||||
# Determine file extension based on OS
|
||||
file_extensions = {'linux': 'tar.bz2', 'macos': 'dmg', 'windows': 'zip'}
|
||||
file_ext = file_extensions[args.os]
|
||||
|
||||
# Remove xpt_artifacts file if it exists
|
||||
xpt_artifacts_pattern = f'camoufox-{args.version}-{args.release}.*.xpt_artifacts.*'
|
||||
for xpt_file in glob.glob(xpt_artifacts_pattern):
|
||||
if os.path.exists(xpt_file):
|
||||
os.remove(xpt_file)
|
||||
# Build the package
|
||||
src_dir = find_src_dir('.', args.version, args.release)
|
||||
moz_target = get_moz_target(target=args.os, arch=args.arch)
|
||||
with temp_cd(src_dir):
|
||||
# Create package files
|
||||
if args.no_locales:
|
||||
run('./mach package')
|
||||
else:
|
||||
run('cat browser/locales/shipped-locales | xargs ./mach package-multi-locale --locales')
|
||||
# Find package files
|
||||
search_path = os.path.abspath(
|
||||
f'obj-{moz_target}/dist/camoufox-{args.version}-{args.release}.*.{file_ext}'
|
||||
)
|
||||
|
||||
# Copy package files
|
||||
for file in glob.glob(search_path):
|
||||
if 'xpt_artifacts' in file:
|
||||
print(f'Skipping xpt artifacts: {file}')
|
||||
continue
|
||||
print(f'Found package: {file}')
|
||||
# Copy to root
|
||||
shutil.copy2(file, '.')
|
||||
break
|
||||
else:
|
||||
print(f"Error: No package file found matching pattern: {search_path}")
|
||||
sys.exit(1)
|
||||
|
||||
# Find the package file
|
||||
package_pattern = f'camoufox-{args.version}-{args.release}.en-US.*.{file_ext}'
|
||||
|
|
@ -139,7 +167,7 @@ def main():
|
|||
includes=args.includes,
|
||||
fonts=args.fonts,
|
||||
new_file=new_name,
|
||||
os=args.os,
|
||||
target=args.os,
|
||||
)
|
||||
|
||||
print(f"Packaging complete for {args.os}")
|
||||
|
|
|
|||
267
scripts/patch.py
267
scripts/patch.py
|
|
@ -9,151 +9,103 @@ Run:
|
|||
python3 scripts/init-patch.py <version> <release>
|
||||
"""
|
||||
|
||||
import fnmatch
|
||||
import hashlib
|
||||
import optparse
|
||||
import os
|
||||
import shutil
|
||||
import sys
|
||||
import time
|
||||
from dataclasses import dataclass
|
||||
|
||||
start_time = time.time()
|
||||
parser = optparse.OptionParser()
|
||||
parser.add_option('-n', '--no-execute', dest='no_execute', default=False, action="store_true")
|
||||
parser.add_option(
|
||||
'-P', '--no-settings-pane', dest='settings_pane', default=True, action="store_false"
|
||||
from _mixin import (
|
||||
find_src_dir,
|
||||
get_moz_target,
|
||||
get_options,
|
||||
list_patches,
|
||||
patch,
|
||||
run,
|
||||
temp_cd,
|
||||
)
|
||||
options, args = parser.parse_args()
|
||||
|
||||
options, args = get_options()
|
||||
|
||||
"""
|
||||
Helper functions
|
||||
Main patcher functions
|
||||
"""
|
||||
|
||||
|
||||
def script_exit(statuscode):
|
||||
if (time.time() - start_time) > 60:
|
||||
# print elapsed time
|
||||
elapsed = time.strftime("%H:%M:%S", time.gmtime(time.time() - start_time))
|
||||
print(f"\n\aElapsed time: {elapsed}")
|
||||
sys.stdout.flush()
|
||||
@dataclass
|
||||
class Patcher:
|
||||
"""Patch and prepare the Camoufox source"""
|
||||
|
||||
sys.exit(statuscode)
|
||||
moz_target: str
|
||||
target: str
|
||||
|
||||
|
||||
def run(cmd, exit_on_fail=True, do_print=True):
|
||||
if not cmd:
|
||||
return
|
||||
if do_print:
|
||||
print(cmd)
|
||||
sys.stdout.flush()
|
||||
if options.no_execute:
|
||||
return None
|
||||
retval = os.system(cmd)
|
||||
if retval != 0 and exit_on_fail:
|
||||
print(f"fatal error: command '{cmd}' failed")
|
||||
sys.stdout.flush()
|
||||
script_exit(1)
|
||||
return retval
|
||||
|
||||
|
||||
def patch(patchfile, reverse=False, silent=False):
|
||||
if reverse:
|
||||
cmd = f"patch -p1 -R -i {patchfile}"
|
||||
else:
|
||||
cmd = f"patch -p1 -i {patchfile}"
|
||||
if silent:
|
||||
cmd += ' > /dev/null'
|
||||
else:
|
||||
print(f"\n*** -> {cmd}")
|
||||
sys.stdout.flush()
|
||||
if options.no_execute:
|
||||
return
|
||||
retval = os.system(cmd)
|
||||
if retval != 0:
|
||||
print(f"fatal error: patch '{patchfile}' failed")
|
||||
sys.stdout.flush()
|
||||
script_exit(1)
|
||||
|
||||
|
||||
def enter_srcdir(_dir=None):
|
||||
if _dir is None:
|
||||
def camoufox_patches(self):
|
||||
"""
|
||||
Apply all patches
|
||||
"""
|
||||
version, release = extract_args()
|
||||
dir = f"camoufox-{version}-{release}"
|
||||
else:
|
||||
dir = _dir
|
||||
print(f"cd {dir}")
|
||||
sys.stdout.flush()
|
||||
if options.no_execute:
|
||||
return
|
||||
try:
|
||||
os.chdir(dir)
|
||||
except:
|
||||
print(f"fatal error: can't change to '{dir}' folder.")
|
||||
sys.stdout.flush()
|
||||
script_exit(1)
|
||||
with temp_cd(find_src_dir('.', version, release)):
|
||||
# Create the base mozconfig file
|
||||
run('cp -v ../assets/base.mozconfig mozconfig')
|
||||
# Set cross building target
|
||||
print(f'Using target: {self.moz_target}')
|
||||
self._update_mozconfig()
|
||||
|
||||
if not options.mozconfig_only:
|
||||
# Apply all other patches
|
||||
for patch_file in list_patches():
|
||||
patch(patch_file)
|
||||
|
||||
def leave_srcdir():
|
||||
print("cd ..")
|
||||
sys.stdout.flush()
|
||||
if not options.no_execute:
|
||||
os.chdir("..")
|
||||
print('Complete!')
|
||||
|
||||
def _update_mozconfig(self):
|
||||
"""
|
||||
Helper for adding additional mozconfig code from assets/<target>.mozconfig
|
||||
"""
|
||||
mozconfig_backup = "mozconfig.backup"
|
||||
mozconfig = "mozconfig"
|
||||
mozconfig_hash = "mozconfig.hash"
|
||||
|
||||
def list_files(root_dir, suffix):
|
||||
for root, _, files in os.walk(root_dir):
|
||||
for file in fnmatch.filter(files, suffix):
|
||||
full_path = os.path.join(root, file)
|
||||
relative_path = os.path.relpath(full_path, root_dir)
|
||||
yield os.path.join('..', 'patches', relative_path).replace('\\', '/')
|
||||
# Create backup if it doesn't exist
|
||||
if not os.path.exists(mozconfig_backup):
|
||||
if os.path.exists(mozconfig):
|
||||
shutil.copy2(mozconfig, mozconfig_backup)
|
||||
else:
|
||||
with open(mozconfig_backup, 'w', encoding='utf-8') as f:
|
||||
pass
|
||||
|
||||
# Read backup content
|
||||
with open(mozconfig_backup, 'r', encoding='utf-8') as f:
|
||||
content = f.read()
|
||||
|
||||
def list_patches(root_dir='../patches', suffix='*.patch'):
|
||||
return sorted(list_files(root_dir, suffix), key=lambda f: os.path.basename(f))
|
||||
# Add target option
|
||||
content += f"\nac_add_options --target={self.moz_target}\n"
|
||||
|
||||
# Add target-specific mozconfig if it exists
|
||||
target_mozconfig = os.path.join("..", "assets", f"{self.target}.mozconfig")
|
||||
if os.path.exists(target_mozconfig):
|
||||
with open(target_mozconfig, 'r', encoding='utf-8') as f:
|
||||
content += f.read()
|
||||
|
||||
# Calculate new hash
|
||||
new_hash = hashlib.sha256(content.encode()).hexdigest()
|
||||
|
||||
# Update mozconfig
|
||||
print(f"-> Updating mozconfig, target is {self.moz_target}")
|
||||
with open(mozconfig, 'w', encoding='utf-8') as f:
|
||||
f.write(content)
|
||||
with open(mozconfig_hash, 'w', encoding='utf-8') as f:
|
||||
f.write(new_hash)
|
||||
|
||||
|
||||
def add_rustup(*targets):
|
||||
"""Add rust targets"""
|
||||
for rust_target in targets:
|
||||
os.system(f'~/.cargo/bin/rustup target add "{rust_target}"')
|
||||
|
||||
|
||||
"""
|
||||
Main patcher function
|
||||
"""
|
||||
|
||||
|
||||
def camoufox_patches():
|
||||
enter_srcdir()
|
||||
|
||||
# Create the right mozconfig file
|
||||
run('cp -v ../assets/base.mozconfig mozconfig')
|
||||
# Set cross building
|
||||
print(f'Using target: {moz_target}')
|
||||
_update_mozconfig()
|
||||
|
||||
# Then apply all other patches
|
||||
for patch_file in list_patches():
|
||||
patch(patch_file)
|
||||
|
||||
leave_srcdir()
|
||||
|
||||
|
||||
"""
|
||||
Helpers for adding additional mozconfig code from assets/<target>.mozconfig
|
||||
"""
|
||||
|
||||
|
||||
def _get_moz_target():
|
||||
if target == "linux":
|
||||
return "aarch64-unknown-linux-gnu" if arch == "arm64" else f"{arch}-pc-linux-gnu"
|
||||
if target == "windows":
|
||||
return f"{arch}-pc-mingw32"
|
||||
if target == "macos":
|
||||
return "aarch64-apple-darwin" if arch == "arm64" else f"{arch}-apple-darwin"
|
||||
raise ValueError(f"Unsupported target: {target}")
|
||||
run(f'~/.cargo/bin/rustup target add "{rust_target}"')
|
||||
|
||||
|
||||
def _update_rustup(target):
|
||||
"""Add rust targets for the given target"""
|
||||
if target == "linux":
|
||||
add_rustup("aarch64-unknown-linux-gnu", "i686-unknown-linux-gnu")
|
||||
elif target == "windows":
|
||||
|
|
@ -162,71 +114,25 @@ def _update_rustup(target):
|
|||
add_rustup("x86_64-apple-darwin", "aarch64-apple-darwin")
|
||||
|
||||
|
||||
def _update_mozconfig():
|
||||
mozconfig_backup = "mozconfig.backup"
|
||||
mozconfig = "mozconfig"
|
||||
mozconfig_hash = "mozconfig.hash"
|
||||
|
||||
# Create backup if it doesn't exist
|
||||
if not os.path.exists(mozconfig_backup):
|
||||
if os.path.exists(mozconfig):
|
||||
shutil.copy2(mozconfig, mozconfig_backup)
|
||||
else:
|
||||
with open(mozconfig_backup, 'w') as f:
|
||||
pass
|
||||
|
||||
# Read backup content
|
||||
with open(mozconfig_backup, 'r') as f:
|
||||
content = f.read()
|
||||
|
||||
# Add target option
|
||||
content += f"\nac_add_options --target={moz_target}\n"
|
||||
|
||||
# Add target-specific mozconfig if it exists
|
||||
target_mozconfig = os.path.join("..", "assets", f"{target}.mozconfig")
|
||||
if os.path.exists(target_mozconfig):
|
||||
with open(target_mozconfig, 'r') as f:
|
||||
content += f.read()
|
||||
|
||||
# Calculate new hash
|
||||
new_hash = hashlib.sha256(content.encode()).hexdigest()
|
||||
|
||||
# Read old hash
|
||||
old_hash = ''
|
||||
if os.path.exists(mozconfig_hash):
|
||||
with open(mozconfig_hash, 'r') as f:
|
||||
old_hash = f.read().strip()
|
||||
|
||||
# Update mozconfig if hash changed
|
||||
if new_hash != old_hash:
|
||||
print(f"-> Updating mozconfig, target is {moz_target}")
|
||||
with open(mozconfig, 'w') as f:
|
||||
f.write(content)
|
||||
with open(mozconfig_hash, 'w') as f:
|
||||
f.write(new_hash)
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
"""
|
||||
Preparation
|
||||
"""
|
||||
|
||||
|
||||
def extract_args():
|
||||
"""Get version and release from args"""
|
||||
if len(args) != 2:
|
||||
sys.stderr.write('error: please specify version and release of camoufox source')
|
||||
sys.exit(1)
|
||||
return args[0], args[1]
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Extract args
|
||||
version, release = extract_args()
|
||||
AVAILABLE_TARGETS = ["linux", "windows", "macos"]
|
||||
AVAILABLE_ARCHS = ["x86_64", "arm64", "i686"]
|
||||
|
||||
# Get moz_target if passed to BUILD_TARGET environment variable
|
||||
AVAILABLE_TARGETS = ["linux", "windows", "macos"]
|
||||
AVAILABLE_ARCHS = ["x86_64", "arm64", "i686"]
|
||||
|
||||
def extract_build_target():
|
||||
"""Get moz_target if passed to BUILD_TARGET environment variable"""
|
||||
|
||||
if os.environ.get('BUILD_TARGET'):
|
||||
target, arch = os.environ['BUILD_TARGET'].split(',')
|
||||
|
|
@ -234,15 +140,28 @@ if __name__ == "__main__":
|
|||
assert arch in AVAILABLE_ARCHS, f"Unsupported architecture: {arch}"
|
||||
else:
|
||||
target, arch = "linux", "x86_64"
|
||||
moz_target = _get_moz_target()
|
||||
_update_rustup(target=target)
|
||||
return target, arch
|
||||
|
||||
|
||||
"""
|
||||
Launcher
|
||||
"""
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Extract args
|
||||
VERSION, RELEASE = extract_args()
|
||||
|
||||
TARGET, ARCH = extract_build_target()
|
||||
MOZ_TARGET = get_moz_target(TARGET, ARCH)
|
||||
_update_rustup(TARGET)
|
||||
|
||||
# Check if the folder exists
|
||||
if not os.path.exists(f'camoufox-{version}-{release}/configure.py'):
|
||||
if not os.path.exists(f'camoufox-{VERSION}-{RELEASE}/configure.py'):
|
||||
sys.stderr.write('error: folder doesn\'t look like a Firefox folder.')
|
||||
sys.exit(1)
|
||||
|
||||
# Apply the patches
|
||||
camoufox_patches()
|
||||
patcher = Patcher(MOZ_TARGET, TARGET)
|
||||
patcher.camoufox_patches()
|
||||
|
||||
sys.exit(0) # ensure 0 exit code
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue