Index: README.md
===================================================================
--- a/README.md
+++ b/README.md
@@ -1,130 +1,53 @@
-Adblock Browser
-===============
+# Adblock Browser
The Adblock Plus browser, based on Firefox for Android.
-Building
---------
+## Simple build
-First ensure that all dependencies are up to date by calling:
+To generate a simple single-locale build, first ensure that all dependencies are up to date by calling:
- ./ensure_dependencies.py
+```
+./ensure_dependencies.py
+```
-Then just follow Mozilla's [Firefox for Android build instructions][1].
+Then just follow Mozilla's [Simple Firefox for Android build][1].
+## Multi-locale build
-Creating a multilocale build
-----------------------------
+While the manual process can be followed in Mozilla's [Instructions for multi-locale builds][2], the easiest way to create multi-locale builds is to follow the following Instructions:
-### Automation ###
+### Configuration
-While the manual process is still documented below, the easiest way to create a
-multi-locale build is to use [adblockbrowser-build][2].
+All the configuration resources required to generate multi-locale builds are located inside the `abb-build` folder. For most cases, you only will need to edit one file.
-### Preparations ###
+1. Make a copy of `config.py.sample` and rename it to `config.py`
-1. Create a folder outside of the Adblock Browser repo, e.g `abb-multi`
-2. Change into `abb-multi` and clone mozharness via:
+2. Inside the `config.py` file, edit `ANDROID_SDK_PATH` to point to your Android SDK path
- hg clone ssh://hg@bitbucket.org/adblockplus/mozharness -r adblockbrowser
+3. Edit `ANDROID_NDK_PATH` to point to your Android NDK path
-3. Symlink the adblockbrowser repo into `abb-multi`, e.g.:
+4. If you intend to sign the .apk, edit `ANDROID_KEYSTORE_PATH` to point to your keystore path
- ln -s ../adblockbrowser adblockbrowser
+5. Also if you intend to sign the .apk, edit `ANDROID_KEY_NAME` to reflect your keystore name / alias
-4. Copy your `mozconfig` from `adblockbrowser` to `abb-multi`, e.g.:
+6. Edit `ARCHITECTURE` for the desired architecture. Currently available options are `arm` and `x86`
- cp adblockbrowser/mozconfig ./mozconfig
+7. Edit `DISTRIBUTION_MODE` for the desired distribution mode. Currently available options are `standalone` and `store`
-5. Create a copy of the build configuration, e.g.:
+8. Edit `BUILD_MODE` for the desired build mode. Currently available options are `devbuild` and `release`
- cp mozharness/configs/multi_locale/standalone_mozilla-central.py abb-multi-cfg.py
+9. Optionally, edit the `mozconfig-custom` file if you want to add any custom build configuration
-### Configuration ###
+### Building and signing
-Apply the following changes to `abb-multi-cfg.py`:
+There are 3 ways of running the `build.py` script:
-* Change `BUILD_DIR = "mozilla-central"` to `BUILD_DIR = "adblockbrowser"`
-* Make sure `OBJDIR = "objdir-droid"` matches your build output
- folder
-* Change `L10N_REPO_PATH` to point to `releases/l10n/mozilla-release`
-* If your `mozconfig` is not called `mozconfig`, change `MOZCONFIG`
-* **Remove the repository pulling part** from `config`.
- The code to remove looks like this:
+1. Run `./build.py build` To generate an unsigned .apk.
- "repos": [{
- "repo": "https://hg.mozilla.org/%s" % REPO_PATH,
- "tag": "default",
- "dest": BUILD_DIR,
- }],
+2. Run `./build.py sign /path/to/apk` to sign an existing .apk
-* Change `"hg_l10n_tag"` in `config` from `default` to `FENNEC_45_0_2_RELEASE`
-* Add `en-ZA` to `ignore_locales` since it isn't currently supported
-* [Issue #3047][3] introduced a custom build step named `abb-transform-locales`
- for manipulating search engine availability and order. This must be inserted
- into `default_actions` right before `package-multi`
-* You might want to remove the `"backup-objdir"` build step if you do
- not want your objdir to be backed up. Keep in mind that you then need to
- clobber for a single-locale build.
-
-An example configuration could look like this:
-
- import os
- # The name of the directory we'll pull our source into.
- BUILD_DIR = "adblockbrowser"
- # This is everything that comes after https://hg.mozilla.org/
- # e.g. "releases/mozilla-aurora"
- REPO_PATH = "mozilla-central"
- # This is where the l10n repos are (everything after https://hg.mozilla.org/)
- # for mozilla-central, that's "l10n-central".
- # For mozilla-aurora, that's "releases/l10n/mozilla-aurora"
- L10N_REPO_PATH = "releases/l10n/mozilla-release"
- # Currently this is assumed to be a subdirectory of your build dir
- OBJDIR = "obj-android"
- # Set this to mobile/xul for XUL Fennec
- ANDROID_DIR = "mobile/android"
- # Absolute path to your mozconfig.
- # By default it looks at "./mozconfig"
- MOZCONFIG = os.path.join(os.getcwd(), "mozconfig")
-
- config = {
- "work_dir": ".",
- "log_name": "multilocale",
- "objdir": OBJDIR,
- "locales_file": "%s/%s/locales/maemo-locales" % (BUILD_DIR, ANDROID_DIR),
- "locales_dir": "%s/locales" % ANDROID_DIR,
- "ignore_locales": ["en-US", "en-ZA", "multi"],
- "l10n_repos": [{
- "repo": "https://hg.mozilla.org/build/compare-locales",
- "tag": "RELEASE_AUTOMATION"
- }],
- "hg_l10n_base": "https://hg.mozilla.org/%s" % L10N_REPO_PATH,
- "hg_l10n_tag": "FENNEC_45_0_2_RELEASE",
- "l10n_dir": "l10n",
- "merge_locales": True,
- "mozilla_dir": BUILD_DIR,
- "mozconfig": MOZCONFIG,
- "default_actions": [
- "pull-locale-source",
- "build",
- "package-en-US",
- "restore-objdir",
- "add-locales",
- "abb-transform-locales",
- "package-multi",
- "summary",
- ],
- }
-
-### Building ###
-
-For performing the multilocale build, invoke
-
- ./mozharness/scripts/multil10n.py --cfg abb-multi-cfg.py
-
-from inside `abb-multi`. The resulting build is located in
-`BUILD_DIR/OBJ_DIR/dist/`
+3. Run `./build.py build-and-sign` to generate a signed .apk
[1]: https://developer.mozilla.org/en-US/docs/Mozilla/Developer_guide/Build_Instructions/Simple_Firefox_for_Android_build
-[2]: https://hg.adblockplus.org/adblockbrowser-build/
-[3]: https://issues.adblockplus.org/ticket/3047
+
+[2]: https://searchfox.org/mozilla-central/source/build/docs/locales.rst
Index: abb-build/README.md
===================================================================
deleted file mode 100644
--- a/abb-build/README.md
+++ /dev/null
@@ -1,29 +0,0 @@
-Adblock Browser Build Automation
-================================
-
-Build automation for Adblock Browser for Android.
-
-Getting/updating the dependencies
----------------------------------
-
-This repository has dependencies that aren't part of this repository. They are
-retrieved and updated during the build process, but you can also manually update
-them by running the following:
-
- ./ensure_dependencies.py
-
-Creating a build
-----------------
-
-### Configuration
-
-First, create a `config.py` file:
-
- cp config.py.sample config.py
-
-Then modify it for your system.
-
-### Building
-
-Builds are generated using the `build.py` script, just run it without parameters
-to learn how to use it.
Index: abb-build/adblockbrowser-cfg.py
===================================================================
deleted file mode 100644
--- a/abb-build/adblockbrowser-cfg.py
+++ /dev/null
@@ -1,48 +0,0 @@
-import os
-# The name of the directory we'll pull our source into.
-BUILD_DIR = os.path.abspath('..')
-# This is everything that comes after https://hg.mozilla.org/
-# e.g. "releases/mozilla-aurora"
-REPO_PATH = "mozilla-central"
-# This is where the l10n repos are (everything after https://hg.mozilla.org/)
-# for mozilla-central, that's "l10n-central".
-# For mozilla-aurora, that's "releases/l10n/mozilla-aurora"
-L10N_REPO_PATH = "releases/l10n/mozilla-release"
-# Currently this is assumed to be a subdirectory of your build dir
-if not "OBJDIR" in os.environ:
- raise "No objdir specified - please set OBJDIR"
-OBJDIR = os.environ.get("OBJDIR")
-# Set this to mobile/xul for XUL Fennec
-ANDROID_DIR = "mobile/android"
-# Absolute path to your mozconfig.
-# By default it looks at "./mozconfig"
-if not "MOZCONFIG" in os.environ:
- raise "No mozconfig specified - please set MOZCONFIG"
-MOZCONFIG = os.environ.get("MOZCONFIG")
-LOCALES_JSON_BASE_URL = "https://product-details.mozilla.org/1.0/l10n/%s.json"
-
-config = {
- "work_dir": ".",
- "log_name": "multilocale",
- "objdir": OBJDIR,
- "locales_json_url": LOCALES_JSON_BASE_URL % "Fennec-56.0-build1",
- "locales_dir": "%s/locales" % ANDROID_DIR,
- "ignore_locales": ["en-US", "multi"],
- "l10n_repos": [{
- "repo": "https://hg.mozilla.org/build/compare-locales",
- "tag": "RELEASE_AUTOMATION"
- }],
- "hg_l10n_base": "https://hg.mozilla.org/%s" % L10N_REPO_PATH,
- "l10n_dir": "l10n",
- "merge_locales": True,
- "mozilla_dir": BUILD_DIR,
- "mozconfig": MOZCONFIG,
- "default_actions": [
- "pull-locale-source",
- "build",
- "add-locales",
- "abb-transform-locales",
- "package-multi",
- "summary",
- ],
-}
Index: abb-build/build.py
===================================================================
--- a/abb-build/build.py
+++ b/abb-build/build.py
@@ -1,10 +1,9 @@
#!/usr/bin/env python
-
# This file is part of Adblock Plus ,
# Copyright (C) 2006-present eyeo GmbH
#
# Adblock Plus is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 3 as
# published by the Free Software Foundation.
#
# Adblock Plus is distributed in the hope that it will be useful,
@@ -31,18 +30,22 @@
_GENERATED_PATH = os.path.join(_BASE_DIR, "generated")
_GENERATED_MOZCONFIG_PATH = os.path.join(_GENERATED_PATH, "mozconfig")
_MOZCONFIG_COMMON_PATH = os.path.join(_BASE_DIR, "mozconfig-common")
_MOZCONFIG_ARM_PATH = os.path.join(_BASE_DIR, "mozconfig-arm")
_MOZCONFIG_X86_PATH = os.path.join(_BASE_DIR, "mozconfig-x86")
_MOZCONFIG_STORE_PATH = os.path.join(_BASE_DIR, "mozconfig-store")
_MOZCONFIG_RELEASE_PATH = os.path.join(_BASE_DIR, "mozconfig-release")
_MOZCONFIG_CUSTOM_PATH = os.path.join(_BASE_DIR, "mozconfig-custom")
-_MULTI_L10N_PATH = os.path.join(_BASE_DIR, "mozharness", "scripts",
- "multil10n.py")
+_MAEMO_LOCALES_PATH = os.path.join(_ABB_PATH, "mobile", "android", "locales",
+ "maemo-locales")
+
+_L10N_BASE_PATH = os.path.join(_BASE_DIR, "l10n-central")
+_LOCALE_CHANGESETS_PATH = os.path.join(_ABB_PATH, "mobile", "locales",
+ "l10n-changesets.json")
_CMD_BUILD = "build"
_CMD_SIGN = "sign"
_CMD_BUILD_SIGN = "build-and-sign"
_ARCH_ARM = "arm"
_ARCH_X86 = "x86"
_ARCH_X86_I386 = "i386"
_ARCHS = (_ARCH_ARM, _ARCH_X86)
@@ -51,122 +54,186 @@
_DIST_MODES = (_DIST_STANDALONE, _DIST_STORE)
_BUILD_DEVBUILD = "devbuild"
_BUILD_RELEASE = "release"
_BUILD_MODES = (_BUILD_DEVBUILD, _BUILD_RELEASE)
_OBJDIR_ARM = "obj-arm-linux-androideabi"
_OBJDIR_X86 = "obj-i386-linux-android"
-def print_usage():
+def _print_usage():
print >>sys.stderr, string.Template("""\
Usage: $name %s
$name %s APK_PATH
$name %s\
""" % (_CMD_BUILD, _CMD_SIGN,
_CMD_BUILD_SIGN)).substitute({"name": os.path.basename(sys.argv[0])})
def _generate_mozconfig(architecture, distribution_mode, build_mode):
if not os.path.exists(_GENERATED_PATH):
os.makedirs(_GENERATED_PATH)
+
with open(_GENERATED_MOZCONFIG_PATH, "w+") as mozconfig:
mozconfig.write(". \"%s\"\n" % _MOZCONFIG_COMMON_PATH)
if architecture == _ARCH_X86:
mozconfig.write(". \"%s\"\n" % _MOZCONFIG_X86_PATH)
else:
mozconfig.write(". \"%s\"\n" % _MOZCONFIG_ARM_PATH)
if distribution_mode == _DIST_STORE:
mozconfig.write(". \"%s\"\n" % _MOZCONFIG_STORE_PATH)
if build_mode == _BUILD_RELEASE:
mozconfig.write(". \"%s\"\n" % _MOZCONFIG_RELEASE_PATH)
mozconfig.write(". \"%s\"\n" % _MOZCONFIG_CUSTOM_PATH)
return _GENERATED_MOZCONFIG_PATH
-def _build(architecture, distribution_mode, build_mode, sdk_path, ndk_path):
- build_environment = os.environ.copy()
- build_environment["ANDROID_SDK_PATH"] = sdk_path
- build_environment["ANDROID_NDK_PATH"] = ndk_path
- build_environment["MOZCONFIG"] = _generate_mozconfig(
- architecture, distribution_mode, build_mode)
- obj_dir = _OBJDIR_X86 if architecture == _ARCH_X86 else _OBJDIR_ARM
- build_environment["OBJDIR"] = obj_dir
- subprocess.check_call([os.path.join(_ABB_PATH, "mach"), "clobber"],
- env=build_environment)
- subprocess.check_call([_MULTI_L10N_PATH, "--cfg", "adblockbrowser-cfg.py"],
- env=build_environment)
+def _checkout_l10n():
+ with open(_LOCALE_CHANGESETS_PATH, "r") as fd:
+ changesets = json.load(fd)
+ if not os.path.exists(_L10N_BASE_PATH):
+ os.makedirs(_L10N_BASE_PATH)
+
+ l10n_base_repo = "https://hg.mozilla.org/l10n-central/%s/"
+ for locale in changesets.keys():
+ revision = changesets[locale]["revision"]
+ repo_path = os.path.join(_L10N_BASE_PATH, locale)
+ if os.path.exists(repo_path):
+ update_cmd = ["hg", "up", "-r", revision]
+ if subprocess.call(update_cmd, cwd=repo_path):
+ subprocess.check_call(["hg", "pull"], cwd=repo_path)
+ subprocess.check_call(update_cmd, cwd=repo_path)
+
+ else:
+ repo = l10n_base_repo % locale
+ subprocess.check_call(["hg", "clone", repo, "-r", revision],
+ cwd=_L10N_BASE_PATH)
+
+
+def _read_locales():
+ with open(_MAEMO_LOCALES_PATH, "r") as fd:
+ lines = fd.readlines()
+
+ return [line.strip() for line in lines]
+
+
+def _config_build_env(architecture, distribution_mode, build_mode, sdk_path,
+ ndk_path, obj_dir, locales):
+ build_env = os.environ.copy()
+ build_env["ANDROID_SDK_PATH"] = sdk_path
+ build_env["ANDROID_NDK_PATH"] = ndk_path
+ mozconfig_path = _generate_mozconfig(architecture, distribution_mode,
+ build_mode)
+
+ build_env["MOZCONFIG"] = mozconfig_path
+ build_env["OBJDIR"] = obj_dir
+ build_env["L10NBASEDIR"] = _L10N_BASE_PATH
+ build_env["MOZ_CHROME_MULTILOCALE"] = ' '.join(locales)
+ build_env["AB_CD"] = "multi"
+ return build_env
+
+
+def _create_target_apk(architecture, distribution_mode, build_mode, obj_dir):
dist_path = os.path.join(_ABB_PATH, obj_dir, "dist")
arch_suffix = _ARCH_X86_I386 if architecture == _ARCH_X86 else _ARCH_ARM
[manifest_path] = glob.glob(os.path.join(
- dist_path, "fennec-*.multi.android-%s.json" % arch_suffix))
+ dist_path, "fennec-*.en-US.android-%s.json" % arch_suffix))
+
with open(manifest_path) as manifest_file:
manifest = json.load(manifest_file)
- apk_name = "fennec-%s.multi.android-%s-unsigned-unaligned.apk" % (
- manifest["moz_app_version"], arch_suffix)
+
+ apk_name = "fennec-%s.en-US.android-%s-unsigned-unaligned.apk" % (
+ manifest["moz_app_version"], arch_suffix)
+
apk_path = os.path.join(dist_path, apk_name)
build_suffix = ("-%s" % manifest["buildid"]
if build_mode == _BUILD_DEVBUILD
else "")
+
dist_suffix = ("-%s" % _DIST_STANDALONE
if distribution_mode == _DIST_STANDALONE
else "")
+
target_apk_name = "adblockbrowser-%s%s-%s%s.apk" % (
manifest["moz_app_version"], build_suffix, architecture, dist_suffix)
+
target_apk_path = os.path.join(dist_path, target_apk_name)
shutil.copyfile(apk_path, target_apk_path)
-
target_manifest_path = re.sub(r".apk$", ".json", target_apk_path)
shutil.copyfile(manifest_path, target_manifest_path)
+ return target_apk_path
- return target_apk_path
+
+def _build(architecture, distribution_mode, build_mode, sdk_path, ndk_path):
+ _checkout_l10n()
+ obj_dir = _OBJDIR_X86 if architecture == _ARCH_X86 else _OBJDIR_ARM
+ locales = _read_locales()
+ build_env = _config_build_env(architecture, distribution_mode, build_mode,
+ sdk_path, ndk_path, obj_dir, locales)
+
+ mach_path = os.path.join(_ABB_PATH, "mach")
+ subprocess.check_call([mach_path, "clobber"], env=build_env, cwd=_ABB_PATH)
+ subprocess.check_call([mach_path, "build"], env=build_env, cwd=_ABB_PATH)
+ for locale in locales:
+ chrome_str = "chrome-%s" % locale
+ subprocess.check_call([mach_path, "build", chrome_str], env=build_env,
+ cwd=_ABB_PATH)
+
+ obj_dir_path = os.path.join(_ABB_PATH, obj_dir)
+ import transform_locales as tl
+ tl.transform_locales(_ABB_PATH, obj_dir_path)
+ subprocess.check_call([mach_path, "package"], env=build_env, cwd=_ABB_PATH)
+ return _create_target_apk(architecture, distribution_mode, build_mode,
+ obj_dir)
def _sign(apk_path, key_store, key_name, sdk_path):
sys.path.append(os.path.join(_ABB_PATH, "python", "mozboot", "mozboot"))
from android import ANDROID_BUILD_TOOLS_VERSION
zipalign_path = os.path.join(sdk_path, "build-tools",
ANDROID_BUILD_TOOLS_VERSION, "zipalign")
+
temp_apk_path = tempfile.NamedTemporaryFile().name
shutil.copyfile(apk_path, temp_apk_path)
try:
subprocess.check_call(["jarsigner", "-verbose",
"-sigalg", "SHA1withRSA",
"-digestalg", "SHA1",
"-keystore", key_store,
temp_apk_path, key_name])
+
os.remove(apk_path)
subprocess.check_call([zipalign_path, "-v", "4", temp_apk_path,
apk_path])
+
finally:
os.remove(temp_apk_path)
if __name__ == "__main__":
if len(sys.argv) < 2:
- print_usage()
+ _print_usage()
sys.exit(1)
mode = sys.argv[1]
do_build = mode in (_CMD_BUILD, _CMD_BUILD_SIGN)
do_sign = mode in (_CMD_SIGN, _CMD_BUILD_SIGN)
if not do_build and not do_sign:
- print_usage()
+ _print_usage()
sys.exit(2)
if do_sign:
if len(sys.argv) < 3:
- print_usage()
+ _print_usage()
sys.exit(3)
apk_path = sys.argv[2]
subprocess.check_call([_ENSURE_DEPENDENCIES_PATH])
+ error_msg = "Invalid %s, check config.py. Supported %s: %s"
import config
-
- error_msg = "Invalid %s, check config.py. Supported %s: %s"
distribution_mode = config.DISTRIBUTION_MODE
if distribution_mode not in _DIST_MODES:
print >>sys.stderr, error_msg % (
"distribution mode", "distribution modes", _DIST_MODES)
sys.exit(4)
build_mode = config.BUILD_MODE
if build_mode not in _BUILD_MODES:
@@ -182,9 +249,9 @@
if do_build:
apk_path = _build(architecture, distribution_mode, build_mode,
config.ANDROID_SDK_PATH, config.ANDROID_NDK_PATH)
if do_sign:
_sign(apk_path, config.ANDROID_KEYSTORE_PATH, config.ANDROID_KEY_NAME,
config.ANDROID_SDK_PATH)
else:
- print apk_path
+ print(apk_path)
Index: abb-build/mozconfig-common
===================================================================
--- a/abb-build/mozconfig-common
+++ b/abb-build/mozconfig-common
@@ -1,12 +1,6 @@
# Do not modify this file. If you want to add custom configuration, use the mozconfig-custom file instead.
ac_add_options --enable-application=mobile/android
ac_add_options --with-android-sdk="$ANDROID_SDK_PATH"
ac_add_options --with-android-ndk="$ANDROID_NDK_PATH"
-# Taken from mobile/android/config/mozconfigs/common
-ac_add_options --enable-elf-hack
-ac_add_options --with-android-gnu-compiler-version=4.9
-ac_add_options --with-system-zlib
-ac_add_options --enable-stdcxx-compat
-
export MOZILLA_OFFICIAL=1
Index: abb-build/transform_locales.py
===================================================================
new file mode 100644
--- /dev/null
+++ b/abb-build/transform_locales.py
@@ -0,0 +1,423 @@
+# This file is part of Adblock Plus
+# Copyright (C) 2006-present eyeo GmbH
+#
+# Adblock Plus is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 3 as
+# published by the Free Software Foundation.
+#
+# Adblock Plus is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Adblock Plus. If not, see .
+
+import json
+import os
+import re
+import shutil
+import sys
+
+_LOCALE_RE = re.compile("^([a-z]{2,3}(?:-[A-Z]{2})?)$")
+_VALUES_LOCALE_RE = re.compile("^values-([a-z]{2,3}(?:-r[A-Z]{2})?)$")
+
+_SEARCH_PROPS_RE = re.compile("^browser\.search\."
+ "(defaultenginename|order\.).*$")
+_SHORTNAME_RE = re.compile("^(.*)$")
+
+_PROPERTY_FORMAT_RE = re.compile("^(([^=]*)=)(.*)$")
+_ENTITY_FORMAT_RE = re.compile("^(\s*)$")
+_STRING_FORMAT_RE = re.compile(
+ "^(\s*)(.*)()$")
+
+_MOZBUILD_PATH = os.path.join("python", "mozbuild")
+
+_CHROME_PATH = os.path.join("dist", "bin", "chrome")
+_RES_PATH = os.path.join("mobile", "android", "base", "res")
+_L10N_PATH = os.path.join("abb-build", "l10n")
+_LOCALES_PATH = os.path.join("mobile", "locales")
+_LISTJSON_PATH = os.path.join(_LOCALES_PATH, "search", "list.json")
+_GENERAL_SEARCHPLUGINS_PATH = os.path.join(_LOCALES_PATH, "searchplugins")
+
+_BROWSER_DIR = "browser"
+_REGION_PROPS_PATH = os.path.join(_BROWSER_DIR, "region.properties")
+_LOCALE_SEARCHPLUGINS_PATH = os.path.join(_BROWSER_DIR, "searchplugins")
+
+_APPSTRINGS_PROPS_PATH = os.path.join(_BROWSER_DIR, "appstrings.properties")
+_STRINGS_XML_PATH = "strings.xml"
+
+_DEFAULT_LOCALE = "en-US"
+_DEF_ENGINES = "visibleDefaultEngines"
+
+# Add Ecosia as secondary search engine.
+# See https://issues.adblockplus.org/ticket/5518
+_ECOSIA_ID = "ecosia"
+_ECOSIA_PATH = os.path.join(_GENERAL_SEARCHPLUGINS_PATH, "ecosia.xml")
+
+_SEARCH_ENGINE_ORDER_DEFAULT = [
+ "duckduckgo",
+ "yahoo",
+ "google",
+ "wikipedia",
+ "amazondotcom"]
+
+_SEARCH_ENGINE_ORDER_ECOSIA = [
+ "duckduckgo",
+ "yahoo",
+ "google",
+ _ECOSIA_ID,
+ "wikipedia",
+ "amazon"]
+
+_SEARCH_ENGINE_ORDER = {
+ "de": _SEARCH_ENGINE_ORDER_ECOSIA,
+ "en-GB": _SEARCH_ENGINE_ORDER_ECOSIA,
+ "en-US": _SEARCH_ENGINE_ORDER_ECOSIA,
+ "fr": _SEARCH_ENGINE_ORDER_ECOSIA,
+ "nl": _SEARCH_ENGINE_ORDER_ECOSIA,
+ "zh-CN": ["baidu",
+ "duckduckgo",
+ "yahoo",
+ "google",
+ "wikipedia",
+ "amazon"
+ ]
+}
+
+_FIREFOX_REPLACE_STR = "Firefox"
+_ABB_REPLACEMENT_STR = "Adblock Browser"
+
+# Some string values that contain Firefox such as 'Firefox Sync' shouldn't be
+# replaced, so we keep a list of ids that are exceptions
+_ENTITY_EXCEPTIONS = [
+ "overlay_no_synced_devices",
+ "home_remote_tabs_need_to_sign_in",
+ "home_remote_tabs_need_to_finish_migrating",
+ "home_remote_tabs_need_to_verify",
+ "syncBrand.fullName.label",
+ "sync.subtitle.connectlocation2.label",
+ "sync.subtitle.failmultiple.label",
+ "fxaccount_full_label",
+ "fxaccount_create_account_header2",
+ "fxaccount_create_account_policy_text2",
+ "fxaccount_status_header2",
+ "fxaccount_status_needs_finish_migrating",
+ "fxaccount_remove_account_dialog_title",
+ "fxaccount_remove_account_toast",
+ "fxaccount_account_type_label",
+]
+
+
+def _check_path_exists(path, logger):
+ if not os.path.exists(path):
+ logger.fatal("'%s' does not exist" % path)
+
+
+def _get_locales_from_path(path, locale_re):
+ locales = []
+ for dir_name in next(os.walk(path))[1]:
+ match = locale_re.match(dir_name)
+ if match:
+ locales.append(match.group(1))
+ locales.sort
+ return locales
+
+
+def _get_shortname_from_id(needle, engine_ids, engine_names):
+ """Fuzzy finds needle in engine_ids and returns ShortName"""
+ for engine in engine_ids:
+ if engine.startswith(needle):
+ return engine_names[engine]
+ return None
+
+
+def _replace_in_value(format_re, str, old, new, exceptions=[]):
+ match = format_re.match(str)
+ if match and match.lastindex > 2:
+ # The prefix contains all characters that precedes the value, including
+ # the id/key
+ str_value_prefix = match.group(1)
+ str_id = match.group(2)
+ str_value = match.group(3)
+ if str_id not in exceptions and old in str_value:
+ new_str = str_value_prefix + str_value.replace(old, new)
+ if match.lastindex == 4:
+ # The suffix contains all characters that succeeds the value
+ str_value_suffix = match.group(4)
+ new_str = new_str + str_value_suffix
+ return new_str
+ return None
+
+
+def _write_lines(filename, lines):
+ """Writes lines into file appending \\n"""
+ with open(filename, "w") as fd:
+ for l in lines:
+ fd.write("%s\n" % l)
+
+
+def _transform_locale(data, locale, project_dir, locale_path, logger):
+ logger.info("Processing locale '%s'..." % locale)
+
+ # Check for region.properties existence
+ region_file_path = os.path.join(locale_path, _REGION_PROPS_PATH)
+ _check_path_exists(region_file_path, logger)
+
+ # Check for appstrings.properties existence
+ appstrings_file_path = os.path.join(locale_path, _APPSTRINGS_PROPS_PATH)
+ _check_path_exists(appstrings_file_path, logger)
+
+ ecosia_dst = os.path.join(locale_path,
+ _LOCALE_SEARCHPLUGINS_PATH, "ecosia.xml")
+
+ # Get whitelist and build regex
+ whitelist = _SEARCH_ENGINE_ORDER.get(locale,
+ _SEARCH_ENGINE_ORDER_DEFAULT)
+ white_re = re.compile("^(%s).*$" % "|".join(whitelist))
+
+ if _ECOSIA_ID in whitelist and not os.path.exists(ecosia_dst):
+ shutil.copyfile(os.path.join(project_dir, _ECOSIA_PATH), ecosia_dst)
+
+ all_engine_ids = []
+ engine_ids = []
+ removed_engine_ids = []
+
+ for item in data['locales'][locale]['default'][_DEF_ENGINES]:
+ all_engine_ids.append(item)
+ if len(item) > 0:
+ if white_re.match(item):
+ engine_ids.append(item)
+ else:
+ removed_engine_ids.append(item)
+
+ if _ECOSIA_ID in whitelist and _ECOSIA_ID not in all_engine_ids:
+ all_engine_ids.append(_ECOSIA_ID)
+ engine_ids.append(_ECOSIA_ID)
+
+ # Make sure we still have search engines left
+ if len(engine_ids) == 0:
+ logger.fatal("No search engines left over for '%s'" % locale)
+
+ data['locales'][locale]['default'][_DEF_ENGINES] = all_engine_ids
+
+ # 'Parse' XML to get matching 'ShortName' for all engine IDs
+ engine_names = {}
+ search_plugins_path = os.path.join(project_dir,
+ _GENERAL_SEARCHPLUGINS_PATH)
+ for eid in engine_ids[:]:
+ xml_file_path = os.path.join(search_plugins_path, "%s.xml" % eid)
+ if not os.path.exists(xml_file_path):
+ logger.info("Missing xml file for plugin %s. Searched in path %s" %
+ (eid, xml_file_path))
+ engine_ids.remove(eid)
+ continue
+ short_name = None
+ with open(xml_file_path, "r") as fd:
+ for line in fd:
+ line = line.strip()
+ match = _SHORTNAME_RE.match(line)
+ if match:
+ short_name = match.group(1).strip()
+
+ if not short_name:
+ logger.fatal("No ShortName defined for '%s' in '%s" %
+ (eid, locale))
+ engine_names[eid] = short_name
+
+ logger.info("Removed search engine IDs: %s" %
+ ", ".join(removed_engine_ids))
+ logger.info("Remaining search engine IDs: %s" % ", ".join(engine_ids))
+
+ # Create search engine order with real engine names
+ engine_order = []
+ for eid in whitelist:
+ sn = _get_shortname_from_id(eid, engine_ids, engine_names)
+ if sn:
+ engine_order.append(sn)
+
+ logger.info("Resulting search engine ordered list: %s" %
+ (", ".join(engine_order)))
+
+ # Read region.properties and remove browser.search.* lines
+ props = []
+ with open(region_file_path, "r") as fd:
+ for line in fd:
+ line = line.rstrip("\r\n")
+ if not _SEARCH_PROPS_RE.match(line.strip()):
+ props.append(line)
+
+ # Append default search engine name
+ props.append("browser.search.defaultenginename=%s" % engine_order[0])
+
+ # Append search engine order
+ for i in range(0, len(engine_order)):
+ props.append("browser.search.order.%d=%s" % (i + 1, engine_order[i]))
+
+ # Write back region.properties
+ _write_lines(region_file_path, props)
+
+ # Replaces ocurrences of 'Firefox' by 'Adblock Browser' in
+ # 'appstrings.properties'
+ lines = []
+ replacement_count = 0
+
+ with open(appstrings_file_path, "r") as fd:
+ for line in fd:
+ line = line.rstrip("\r\n")
+ replacement = _replace_in_value(_PROPERTY_FORMAT_RE, line,
+ _FIREFOX_REPLACE_STR,
+ _ABB_REPLACEMENT_STR)
+ if replacement:
+ line = replacement
+ replacement_count += 1
+ lines.append(line)
+
+ # Apply changes to appstrings.properties
+ _write_lines(appstrings_file_path, lines)
+ logger.info("Replaced %d ocurrences of %s in 'appstrings.properties'" %
+ (replacement_count, _FIREFOX_REPLACE_STR))
+
+
+def _generate_browser_search(locale, locale_path, res_path, project_dir):
+ raw_dir = "raw" if locale == _DEFAULT_LOCALE else (
+ "raw-%s" % locale.replace("-", "-r"))
+
+ browser_path = os.path.join(locale_path, _BROWSER_DIR)
+ browsersearch_file_path = os.path.join(res_path, raw_dir,
+ "browsersearch.json")
+
+ sys.path.append(os.path.join(project_dir, _MOZBUILD_PATH))
+ import mozbuild.action.generate_browsersearch as generate_browsersearch
+
+ # Call generate_browsersearch.py script to regenerate
+ # res/raw-LOCALE/browsersearch.json with the updated search engines
+ generate_browsersearch.main(["--verbose", "--srcdir", browser_path,
+ browsersearch_file_path])
+
+
+def _generate_search_json(locale, locale_path, project_dir):
+ script_path = os.path.join(project_dir, "python", "mozbuild",
+ "mozbuild", "action", "generate_searchjson.py")
+ list_json_path = os.path.join(project_dir, _LISTJSON_PATH)
+ searchplugins_path = os.path.join(locale_path, _BROWSER_DIR,
+ "searchplugins", "list.json")
+
+ import subprocess as s
+ # Call generate_searchjson.py script
+ s.check_call(['python', script_path, list_json_path,
+ locale, searchplugins_path])
+
+
+def _transform_values_locale(locale, path, logger):
+ logger.info("Processing values-%s..." % locale)
+
+ # Check for strings.xml existence
+ strings_file_path = os.path.join(path, _STRINGS_XML_PATH)
+ _check_path_exists(strings_file_path, logger)
+
+ # Replaces ocurrences of 'Firefox' by 'Adblock Browser' in 'strings.xml'
+ lines = []
+ replacement_count = 0
+
+ with open(strings_file_path, "r") as fd:
+ for line in fd:
+ line = line.rstrip("\r\n")
+ replacement = _replace_in_value(_ENTITY_FORMAT_RE, line,
+ _FIREFOX_REPLACE_STR,
+ _ABB_REPLACEMENT_STR,
+ _ENTITY_EXCEPTIONS)
+ if replacement:
+ line = replacement
+ replacement_count += 1
+ else:
+ replacement = _replace_in_value(_STRING_FORMAT_RE, line,
+ _FIREFOX_REPLACE_STR,
+ _ABB_REPLACEMENT_STR)
+ if replacement:
+ line = replacement
+ replacement_count += 1
+ lines.append(line)
+
+ # Apply changes to strings.xml
+ _write_lines(strings_file_path, lines)
+ logger.info("Replaced %d ocurrences of %s in 'strings.xml'" %
+ (replacement_count, _FIREFOX_REPLACE_STR))
+
+
+class MinimalLogger:
+ def info(self, s):
+ print "INFO: %s" % s
+
+ def error(self, s):
+ print "ERROR: %s" % s
+
+ def fatal(self, s):
+ print "FATAL: %s" % s
+ exit(1)
+
+
+def transform_locales(project_dir, obj_dir, logger=MinimalLogger()):
+ chrome_path = os.path.join(obj_dir, _CHROME_PATH)
+ _check_path_exists(chrome_path, logger)
+
+ res_path = os.path.join(obj_dir, _RES_PATH)
+ _check_path_exists(res_path, logger)
+
+ list_json_path = os.path.join(project_dir, _LISTJSON_PATH)
+ _check_path_exists(list_json_path, logger)
+
+ locales = _get_locales_from_path(chrome_path, _LOCALE_RE)
+ values_locales = _get_locales_from_path(res_path, _VALUES_LOCALE_RE)
+
+ locales_found_msg = "Found %d locales in %s"
+ logger.info(locales_found_msg % (len(locales), chrome_path))
+ logger.info(locales_found_msg % (len(values_locales), res_path))
+
+ # open the Mozilla list of search engines, put it into a buffer and
+ # close the JSON file after reading
+ with open(list_json_path, 'r') as json_file:
+ data = json.load(json_file)
+
+ # set default search engine order
+ data['default'][_DEF_ENGINES] = _SEARCH_ENGINE_ORDER_DEFAULT
+
+ for locale in locales:
+ locale_path = os.path.join(chrome_path, locale, "locale", locale)
+ if os.path.exists(locale_path):
+
+ # Mozilla default list does not contain locale bn-BD,
+ # so we create it and use the values from locale bn-IN
+ if locale == 'bn-BD':
+ data['locales'].update({locale: {'default':
+ {_DEF_ENGINES:
+ data['locales']['bn-IN']['default'][_DEF_ENGINES]}}})
+ # Mozilla default list does not contain locale wo, so we use
+ # the default order. In case they will not support any other
+ # locales in the future, we want the build to fail, to decide
+ # which order to use
+ elif locale == 'wo':
+ data['locales'].update({locale: {'default':
+ {_DEF_ENGINES:
+ _SEARCH_ENGINE_ORDER_DEFAULT}}})
+
+ _transform_locale(data, locale, project_dir, locale_path,
+ logger)
+ _generate_browser_search(locale, locale_path, res_path,
+ project_dir)
+ else:
+ logger.error("Missing folder for locale '%s' in path: %s" %
+ (locale, locale_path))
+
+ # Save changes to list.json
+ with open(list_json_path, 'w') as outfile:
+ json.dump(data, outfile, indent=4, sort_keys=True)
+
+ # Generate search.json for each locale
+ for locale in locales:
+ locale_path = os.path.join(chrome_path, locale, "locale", locale)
+ _generate_search_json(locale, locale_path, project_dir)
+
+ for locale in values_locales:
+ locale_path = os.path.join(res_path, "values-" + locale)
+ _transform_values_locale(locale, locale_path, logger)
Index: dependencies
===================================================================
--- a/dependencies
+++ b/dependencies
@@ -1,5 +1,4 @@
_root = hg:https://hg.adblockplus.org/ git:https://github.com/adblockplus/
_self = buildtools/ensure_dependencies.py
adblockplus/adblockpluschrome = adblockpluschrome hg:1d3cba5f04ac git:aaaf8ea
buildtools = buildtools hg:47429f8d9b6a git:bd1a0d2
-abb-build/mozharness = mozharness hg:608852415a6a
Index: mobile/android/installer/allowed-dupes.mn
===================================================================
--- a/mobile/android/installer/allowed-dupes.mn
+++ b/mobile/android/installer/allowed-dupes.mn
@@ -44,76 +44,8 @@
res/table-remove-column-active.gif
res/table-remove-column-hover.gif
res/table-remove-column.gif
res/table-remove-row-active.gif
res/table-remove-row-hover.gif
res/table-remove-row.gif
modules/commonjs/index.js
chrome/toolkit/content/global/XPCNativeWrapper.js
-
-# Allowed dupes in Adblock Plus extension
-# See https://issues.adblockplus.org/ticket/5412
-assets/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/locale/ar/common.properties
-assets/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/locale/az/common.properties
-assets/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/locale/bg/common.properties
-assets/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/locale/bn-BD/common.properties
-assets/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/locale/ca/common.properties
-assets/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/locale/cs/common.properties
-assets/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/locale/cy/common.properties
-assets/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/locale/da/common.properties
-assets/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/locale/de/common.properties
-assets/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/locale/dsb/common.properties
-assets/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/locale/el/common.properties
-assets/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/locale/en-GB/common.properties
-assets/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/locale/en-US/common.properties
-assets/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/locale/en-ZA/common.properties
-assets/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/locale/eo/common.properties
-assets/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/locale/es-AR/common.properties
-assets/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/locale/es-CL/common.properties
-assets/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/locale/es-ES/common.properties
-assets/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/locale/es-MX/common.properties
-assets/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/locale/et/common.properties
-assets/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/locale/eu/common.properties
-assets/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/locale/fa/common.properties
-assets/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/locale/fi/common.properties
-assets/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/locale/fr/common.properties
-assets/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/locale/fy-NL/common.properties
-assets/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/locale/gl/common.properties
-assets/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/locale/he/common.properties
-assets/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/locale/hr/common.properties
-assets/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/locale/hsb/common.properties
-assets/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/locale/hu/common.properties
-assets/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/locale/hy-AM/common.properties
-assets/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/locale/id/common.properties
-assets/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/locale/is/common.properties
-assets/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/locale/it/common.properties
-assets/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/locale/ja/common.properties
-assets/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/locale/kk/common.properties
-assets/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/locale/ko/common.properties
-assets/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/locale/lt/common.properties
-assets/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/locale/lv/common.properties
-assets/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/locale/ms/common.properties
-assets/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/locale/nb-NO/common.properties
-assets/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/locale/nl/common.properties
-assets/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/locale/pl/common.properties
-assets/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/locale/pt-BR/common.properties
-assets/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/locale/pt-PT/common.properties
-assets/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/locale/ro/common.properties
-assets/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/locale/ru/common.properties
-assets/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/locale/sk/common.properties
-assets/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/locale/sl/common.properties
-assets/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/locale/sq/common.properties
-assets/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/locale/sr/common.properties
-assets/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/locale/sv-SE/common.properties
-assets/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/locale/th/common.properties
-assets/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/locale/tr/common.properties
-assets/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/locale/uk/common.properties
-assets/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/locale/vi/common.properties
-assets/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/locale/zh-CN/common.properties
-assets/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/locale/zh-TW/common.properties
-assets/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/locale/az/firstRun.properties
-assets/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/locale/en-US/firstRun.properties
-assets/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/locale/en-ZA/firstRun.properties
-assets/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/ext/common.js
-assets/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/lib/ext_common.js
-# See https://issues.adblockplus.org/ticket/5518
-chrome/en-US/locale/en-US/browser/searchplugins/ecosia.xml
Index: toolkit/mozapps/installer/find-dupes.py
===================================================================
--- a/toolkit/mozapps/installer/find-dupes.py
+++ b/toolkit/mozapps/installer/find-dupes.py
@@ -112,9 +112,11 @@
pp.do_filter('substitution')
pp.do_include(filename)
allowed_dupes.extend([line.partition('#')[0].rstrip()
for line in pp.out.getvalue().splitlines()])
find_dupes(args.directory, bail=not args.warning, allowed_dupes=allowed_dupes)
if __name__ == "__main__":
- main()
+ # We allow dupes, so we skip this check
+ # See https://issues.adblockplus.org/ticket/7238
+ print("Skipping find-dupes.py")