diff --git a/Makefile b/Makefile
index 4937b7f..e54c8bd 100644
--- a/Makefile
+++ b/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 \
diff --git a/README.md b/README.md
index 08eeba7..d68f6be 100644
--- a/README.md
+++ b/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
```
+
+
+CLI Parameters
+
+
+```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
+```
+
+
+
### 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
```
diff --git a/launcher/build.sh b/launcher/build.sh
index 9bf839b..41b0ac6 100644
--- a/launcher/build.sh
+++ b/launcher/build.sh
@@ -2,7 +2,7 @@
if [ $# -ne 2 ]; then
echo "Usage: $0 "
- 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
diff --git a/multibuild.py b/multibuild.py
index e1d63a8..add5240 100644
--- a/multibuild.py
+++ b/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()
diff --git a/scripts/_mixin.py b/scripts/_mixin.py
new file mode 100644
index 0000000..3b4e818
--- /dev/null
+++ b/scripts/_mixin.py
@@ -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)
diff --git a/scripts/developer.py b/scripts/developer.py
index fbd55a8..ac29a02 100644
--- a/scripts/developer.py
+++ b/scripts/developer.py
@@ -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()
diff --git a/scripts/package.py b/scripts/package.py
index 1c65127..0047c77 100644
--- a/scripts/package.py
+++ b/scripts/package.py
@@ -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}")
diff --git a/scripts/patch.py b/scripts/patch.py
index 0e8156d..4fcdd07 100644
--- a/scripts/patch.py
+++ b/scripts/patch.py
@@ -9,151 +9,103 @@ Run:
python3 scripts/init-patch.py
"""
-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/.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/.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