diff --git a/.gitignore b/.gitignore index 1d21ef08785940014aa818162bc68d5d07c11324..95cedbc9140774598a570d6f0ca36692f19380df 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1,16 @@ -# Idea +# IDE /build/ /nbproject/ /.idea/ *.iml -# Eclipse .project .pydevproject .settings/ + +.vscode + +# Our stuff +po/*.po~ +*.pyc +/builddir diff --git a/CMakeLists.txt b/CMakeLists.txt deleted file mode 100644 index 170c9f755802737674f1325eea22ce4d5e4cf6d3..0000000000000000000000000000000000000000 --- a/CMakeLists.txt +++ /dev/null @@ -1,305 +0,0 @@ -# GNOME Shell integration for Chrome build script - -cmake_minimum_required (VERSION 2.8) -project (chrome-gnome-shell NONE) - -# Suppress warning -if(NOT DEFINED CMAKE_SIZEOF_VOID_P) - set(CMAKE_SIZEOF_VOID_P 8) -endif(NOT DEFINED CMAKE_SIZEOF_VOID_P) - -# Variables -set(PROJECT_VERSION "10.1") - -set(ARCHIVE_NAME ${CMAKE_PROJECT_NAME}-${PROJECT_VERSION}) -set(ARCHIVE_FULL_NAME ${ARCHIVE_NAME}.tar.xz) -set(ARCHIVE_DEB_NAME ${CMAKE_PROJECT_NAME}_${PROJECT_VERSION}.orig.tar.xz) -set(DEB_DIR ${CMAKE_CURRENT_BINARY_DIR}/deb) - -set(EXTENSION_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/extension) -set(EXTENSION_BUILD_DIR ${CMAKE_BINARY_DIR}/extension) -set(CHROME_BUILD_DIR ${EXTENSION_BUILD_DIR}/chrome) -set(OPERA_BUILD_DIR ${EXTENSION_BUILD_DIR}/opera) -set(FIREFOX_BUILD_DIR ${EXTENSION_BUILD_DIR}/firefox) - -# Options -option(BUILD_EXTENSION "Build extension zip package" TRUE) -option(BUILD_CONNECTOR "Build native messaging host" TRUE) -option(BUILD_SOURCE_PACKAGE "Build source package" FALSE) -option(BUILD_DEB "Build debian package" FALSE) -option(BUILD_MESSAGES "Update translation strings" FALSE) -option(USE_DEBIAN_LAYOUT "Use --install-layout=deb distutils parameter" FALSE) - -# Default extension key for Chrome web store -if(NOT DEFINED CHROME_EXTENSION_KEY) - set(CHROME_EXTENSION_KEY "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlig8TAPPQZinMkJnptC0ldizx6fG9jSjZDJ9c8GuLcXeGRH+NMlQuPC9bR5IQlT7+4VY/1tm1+IZ4xvITx1wXCNTR+KXzZv3VNc2D+logehK7oIRTRj0fLhixrx4NLSNK7L7HgV2xcIoW6QV0jOdFcTPL0mWXodXSzZePrvXuflF7qpwNxLzYVi04Vh3xu2oR2Pc9SwfZ4SNbyCaunH/p8n5AYmDuogI2Ah++RZw0ctnqn7mmHrGXteBu/vkpcHZu3B3eW9PFSrv69rRs8duybYR9C91hJm6yzRqZqIpindIU3k2HnNWeCFWkRVpZPhaNVoxcBUO7wWUUwdIflW2JwIDAQAB") -endif(NOT DEFINED CHROME_EXTENSION_KEY) - -# Default extension key for Opera addons -if(NOT DEFINED OPERA_EXTENSION_KEY) - set(OPERA_EXTENSION_KEY "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1QBzvBFxSTBP1Z3u+B3GnxOhZiT/LLGJ9R3Mu3R5ap1YgvbHNqSj0CmrWE+MvJUYA+AxHSRP6ctKfPBZl/kdGYGLPgvagGBEEbutCerj9www7T2LAsFNc5gIgDXaU0P74Yik8MjLZIXOC3Q91Kien5Jbtit382HNVHl2nRbhgGZ9LZ+6UJnzSseW9NHw0/XRRnT0kv0Ih7lgC55xWfP7guam1uaT5DPxC7W5cy9V1z7mljBf50OxgnbEmf7Xvfz4QpQfDyQWE5hqKlBuPc8W/8bvYmWN+FDBqhls/FCml3icAElNMBg0YryFEDzu6xCTzgHqDKYu5SN49u+m6tyamQIDAQAB") -endif(NOT DEFINED OPERA_EXTENSION_KEY) - -# Debian variables -if(NOT DEFINED DEBIAN_VERSION) - set(DEBIAN_VERSION "0ubuntu1") -endif(NOT DEFINED DEBIAN_VERSION) - -if(NOT DEFINED DEBIAN_DISTRO) - set(DEBIAN_DISTRO "trusty") -endif(NOT DEFINED DEBIAN_DISTRO) - -macro(find_program_ex) - string(TOUPPER ${ARGV0} _PROGRAM_UPPER) - - set(_NAMES ${ARGV}) - math(EXPR _NAMES_LAST_INDEX "${ARGC}-1") - list(GET _NAMES ${_NAMES_LAST_INDEX} _MESSAGE_STATUS) - list(REMOVE_AT _NAMES ${_NAMES_LAST_INDEX}) - - find_program(${_PROGRAM_UPPER}_EXECUTABLE NAMES ${_NAMES}) - - if(${_PROGRAM_UPPER}_EXECUTABLE) - message(STATUS "Found ${ARGV0}: ${${_PROGRAM_UPPER}_EXECUTABLE}") - set(${_PROGRAM_UPPER}_FOUND TRUE) - else(${_PROGRAM_UPPER}_EXECUTABLE) - message(${_MESSAGE_STATUS} "Could NOT find ${ARGV0}.") - endif(${_PROGRAM_UPPER}_EXECUTABLE) -endmacro(find_program_ex) - -include(GNUInstallDirs) - -# Options validating -if(NOT BUILD_EXTENSION AND NOT BUILD_CONNECTOR AND NOT BUILD_MESSAGES) - message(FATAL_ERROR "No build options selected") -endif(NOT BUILD_EXTENSION AND NOT BUILD_CONNECTOR AND NOT BUILD_MESSAGES) - -if(BUILD_DEB AND NOT BUILD_SOURCE_PACKAGE) - message(STATUS "Building of debian package enabled. Turning on building source package.") - set(BUILD_SOURCE_PACKAGE TRUE) -endif(BUILD_DEB AND NOT BUILD_SOURCE_PACKAGE) - -if(BUILD_SOURCE_PACKAGE AND NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/.git/) - message(FATAL_ERROR "Unable to build source package outside of git repository.") -endif(BUILD_SOURCE_PACKAGE AND NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/.git/) - -if(BUILD_MESSAGES AND NOT DEFINED GETTEXT_REPORT_EMAIL) - message(FATAL_ERROR "GETTEXT_REPORT_EMAIL must be specified to generate po template.") -endif(BUILD_MESSAGES AND NOT DEFINED GETTEXT_REPORT_EMAIL) - -if(BUILD_EXTENSION OR BUILD_CONNECTOR) - find_program_ex(base64 gbase64 FATAL_ERROR) - find_program_ex(sha256sum gsha256sum FATAL_ERROR) - find_program_ex(ghead head FATAL_ERROR) - find_program_ex(tr gtr FATAL_ERROR) - find_program_ex(jq FATAL_ERROR) - - # https://github.com/adobe/chromium/blob/master/chrome/common/extensions/extension.cc#L696 - # http://stackoverflow.com/questions/23873623/obtaining-chrome-extension-id-for-development - execute_process(COMMAND echo "${CHROME_EXTENSION_KEY}" - COMMAND ${BASE64_EXECUTABLE} -d - COMMAND ${SHA256SUM_EXECUTABLE} - COMMAND ${GHEAD_EXECUTABLE} -c32 - COMMAND ${TR_EXECUTABLE} 0-9a-f a-p - OUTPUT_VARIABLE CHROME_EXTENSION_ID) - message(STATUS "Calculated Chrome extension id: ${CHROME_EXTENSION_ID}") - - execute_process(COMMAND echo "${OPERA_EXTENSION_KEY}" - COMMAND ${BASE64_EXECUTABLE} -d - COMMAND ${SHA256SUM_EXECUTABLE} - COMMAND ${GHEAD_EXECUTABLE} -c32 - COMMAND ${TR_EXECUTABLE} 0-9a-f a-p - OUTPUT_VARIABLE OPERA_EXTENSION_ID) - message(STATUS "Calculated Opera extension id: ${OPERA_EXTENSION_ID}") -endif(BUILD_EXTENSION OR BUILD_CONNECTOR) - -# Options handling -if(BUILD_EXTENSION) - find_program_ex(7z FATAL_ERROR) - - file(COPY "${EXTENSION_SOURCES}/" DESTINATION "${CHROME_BUILD_DIR}" - PATTERN "manifest*.json" EXCLUDE) - - file(COPY "${EXTENSION_SOURCES}/" DESTINATION "${OPERA_BUILD_DIR}" - PATTERN "manifest*.json" EXCLUDE) - - file(COPY "${EXTENSION_SOURCES}/" DESTINATION "${FIREFOX_BUILD_DIR}" - PATTERN "manifest*.json" EXCLUDE) - - set(PUBLIC_KEY ${CHROME_EXTENSION_KEY}) - configure_file("${EXTENSION_SOURCES}/manifest.json" "${CHROME_BUILD_DIR}/") - - set(PUBLIC_KEY ${OPERA_EXTENSION_KEY}) - configure_file("${EXTENSION_SOURCES}/manifest.json" "${OPERA_BUILD_DIR}/") - - unset(PUBLIC_KEY) - - add_custom_target(chrome-extension ALL - COMMAND "${7Z_EXECUTABLE}" a -tzip "${CMAKE_BINARY_DIR}/extension-chrome.zip" ./ - WORKING_DIRECTORY "${CHROME_BUILD_DIR}") - add_custom_target(opera-extension ALL - COMMAND "${7Z_EXECUTABLE}" a -tzip "${CMAKE_BINARY_DIR}/extension-opera.zip" ./ - WORKING_DIRECTORY "${OPERA_BUILD_DIR}") - add_custom_target(firefox-extension ALL - COMMAND "${JQ_EXECUTABLE}" -s "'add|del(.key)'" - '${EXTENSION_SOURCES}/manifest.json' '${EXTENSION_SOURCES}/manifest.firefox.json' - > "${FIREFOX_BUILD_DIR}/manifest.json" - COMMAND "${7Z_EXECUTABLE}" a -tzip "${CMAKE_BINARY_DIR}/extension-firefox.zip" ./ - WORKING_DIRECTORY "${FIREFOX_BUILD_DIR}") -endif(BUILD_EXTENSION) - -if(BUILD_CONNECTOR) - find_package(PythonInterp REQUIRED) - - set(CONNECTOR_SETUP "${CMAKE_CURRENT_SOURCE_DIR}/connector/setup.py") - add_custom_target(build-connector ALL - COMMAND ${PYTHON_EXECUTABLE} ${CONNECTOR_SETUP} build) - - configure_file("${CMAKE_CURRENT_SOURCE_DIR}/connector/org.gnome.chrome_gnome_shell.json" - "${CMAKE_BINARY_DIR}/") - configure_file("${CMAKE_CURRENT_SOURCE_DIR}/connector/org.gnome.ChromeGnomeShell.service.in" - "${CMAKE_BINARY_DIR}/org.gnome.ChromeGnomeShell.service") - - add_custom_target(firefox-native-manifest ALL - COMMAND "${JQ_EXECUTABLE}" -s "'add|del(.allowed_origins)'" - "${CMAKE_BINARY_DIR}/org.gnome.chrome_gnome_shell.json" - "${CMAKE_CURRENT_SOURCE_DIR}/connector/org.gnome.chrome_gnome_shell.firefox.json" - > "${CMAKE_BINARY_DIR}/org.gnome.chrome_gnome_shell.firefox.json") - - if(USE_DEBIAN_LAYOUT) - set(DISTUTILS_EXTRA_ARGS "--install-layout=deb") - else() - set(DISTUTILS_EXTRA_ARGS "") - endif(USE_DEBIAN_LAYOUT) - - install(CODE " - if(DEFINED ENV{DESTDIR}) - set(DESTDIR \"\$ENV{DESTDIR}\") - else(DEFINED ENV{DESTDIR}) - set(DESTDIR \"/\") - endif(DEFINED ENV{DESTDIR}) - - execute_process(COMMAND ${PYTHON_EXECUTABLE} ${CONNECTOR_SETUP} - install ${DISTUTILS_EXTRA_ARGS} - --root \"\${DESTDIR}\" - --prefix \"${CMAKE_INSTALL_PREFIX}\")") - - if(NOT CMAKE_SYSTEM_NAME MATCHES "DragonFly.*|FreeBSD") - install(FILES "${CMAKE_BINARY_DIR}/org.gnome.chrome_gnome_shell.json" DESTINATION "/etc/chromium/native-messaging-hosts/") - install(FILES "${CMAKE_BINARY_DIR}/org.gnome.chrome_gnome_shell.json" DESTINATION "/etc/opt/chrome/native-messaging-hosts/") - else(NOT CMAKE_SYSTEM_NAME MATCHES "DragonFly.*|FreeBSD") - # FreeBSD uses patch that forces Chromium to look into undocumented - # "/usr/local/etc/chrome/native-messaging-hosts" folder for native messaging host manifest. - # https://github.com/freebsd/freebsd-ports/blob/master/www/chromium/files/patch-chrome_common_chrome__paths.cc - install(FILES "${CMAKE_BINARY_DIR}/org.gnome.chrome_gnome_shell.json" DESTINATION "/usr/local/etc/chrome/native-messaging-hosts/") - endif(NOT CMAKE_SYSTEM_NAME MATCHES "DragonFly.*|FreeBSD") - - install(FILES "${CMAKE_BINARY_DIR}/org.gnome.chrome_gnome_shell.firefox.json" - DESTINATION "${CMAKE_INSTALL_LIBDIR}/mozilla/native-messaging-hosts/" - RENAME "org.gnome.chrome_gnome_shell.json") - - install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/connector/org.gnome.ChromeGnomeShell.desktop" DESTINATION "${CMAKE_INSTALL_DATADIR}/applications/") - install(FILES "${CMAKE_BINARY_DIR}/org.gnome.ChromeGnomeShell.service" DESTINATION "${CMAKE_INSTALL_DATADIR}/dbus-1/services/") - - install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/extension/icons/GnomeLogo-16.png" - DESTINATION "${CMAKE_INSTALL_DATADIR}/icons/hicolor/16x16/apps/" - RENAME org.gnome.ChromeGnomeShell.png) - install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/extension/icons/GnomeLogo-48.png" - DESTINATION "${CMAKE_INSTALL_DATADIR}/icons/hicolor/48x48/apps/" - RENAME org.gnome.ChromeGnomeShell.png) - install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/extension/icons/GnomeLogo-128.png" - DESTINATION "${CMAKE_INSTALL_DATADIR}/icons/hicolor/128x128/apps/" - RENAME org.gnome.ChromeGnomeShell.png) -endif(BUILD_CONNECTOR) - -if(BUILD_SOURCE_PACKAGE) - set(MESSAGE_NO_DIST "Unable to generate dist target. Dependencies missing.") - - find_program_ex(xz WARNING) - find_package(Git) - - if(GIT_FOUND AND XZ_FOUND) - # http://agateau.com/2009/cmake-and-make-dist-the-simple-version/ - add_custom_target(dist - COMMAND ${GIT_EXECUTABLE} archive --prefix=${ARCHIVE_NAME}/ HEAD | ${XZ_EXECUTABLE} -z > ${CMAKE_BINARY_DIR}/${ARCHIVE_FULL_NAME} - WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) - else(GIT_FOUND AND XZ_FOUND) - if(BUILD_DEB) - message(FATAL_ERROR ${MESSAGE_NO_DIST}) - else(BUILD_DEB) - message(WARNING ${MESSAGE_NO_DIST}) - endif(BUILD_DEB) - endif(GIT_FOUND AND XZ_FOUND) - - if(BUILD_DEB) - find_program_ex(debuild FATAL_ERROR) - find_program_ex(tar FATAL_ERROR) - - if(GPG_KEY) - message(STATUS "Using GPG key ${GPG_KEY}") - set(DEBUILD_KEY "-k${GPG_KEY}") - else(GPG_KEY) - message(STATUS "GPG_KEY not set. Using default GPG key") - endif(GPG_KEY) - - configure_file("${CMAKE_CURRENT_SOURCE_DIR}/contrib/ubuntu/changelog" - "${CMAKE_BINARY_DIR}/debian_changelog") - - add_custom_target(deb_prepare - DEPENDS dist - COMMAND ${CMAKE_COMMAND} -E make_directory "${DEB_DIR}/${ARCHIVE_NAME}" - COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_BINARY_DIR}/${ARCHIVE_FULL_NAME}" "${DEB_DIR}/${ARCHIVE_DEB_NAME}" - COMMAND ${TAR_EXECUTABLE} -xvf "${DEB_DIR}/${ARCHIVE_DEB_NAME}" -C "${DEB_DIR}/${ARCHIVE_NAME}" --strip-components=1) - - add_custom_target(deb ALL - DEPENDS deb_prepare - COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_CURRENT_SOURCE_DIR}/contrib/ubuntu" "${DEB_DIR}/${ARCHIVE_NAME}/debian" - COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_BINARY_DIR}/debian_changelog" "${DEB_DIR}/${ARCHIVE_NAME}/debian/changelog" - COMMAND ${DEBUILD_EXECUTABLE} -S ${DEBUILD_KEY} - WORKING_DIRECTORY ${DEB_DIR}/${ARCHIVE_NAME}) - endif(BUILD_DEB) -endif(BUILD_SOURCE_PACKAGE) - -if(BUILD_MESSAGES) - find_package(Gettext) - if(NOT GETTEXT_FOUND) - message(FATAL_ERROR "Gettext not found") - endif(NOT GETTEXT_FOUND) - - add_custom_target(generate-pot - COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/contrib/chrome-messages2po.py - --email ${GETTEXT_REPORT_EMAIL} - --reference-lang en - --write-pot - ${CMAKE_CURRENT_SOURCE_DIR}/contrib/chrome-web-store/ - ${CMAKE_CURRENT_SOURCE_DIR}/extension/_locales/ - ${CMAKE_CURRENT_SOURCE_DIR}/po/) - - add_custom_target(update-po - DEPENDS generate-pot) - - file(GLOB PO_FILES - LIST_DIRECTORIES false - RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}/po/ - ${CMAKE_CURRENT_SOURCE_DIR}/po/*.po) - - foreach(PO ${PO_FILES}) - add_custom_command(TARGET update-po - COMMENT "Updating ${PO}" - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/po - COMMAND ${GETTEXT_MSGMERGE_EXECUTABLE} -q --update - ${PO} - ${CMAKE_CURRENT_SOURCE_DIR}/po/chrome-gnome-shell.pot) - endforeach() - - add_custom_command(TARGET update-po - COMMAND ${CMAKE_COMMAND} -E remove ${CMAKE_CURRENT_SOURCE_DIR}/po/*.po~) - - add_custom_target(build-messages ALL - DEPENDS update-po - COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/contrib/po2chrome-messages.py - --reference-lang en - ${CMAKE_CURRENT_SOURCE_DIR}/contrib/chrome-web-store/ - ${CMAKE_CURRENT_SOURCE_DIR}/extension/_locales/ - ${CMAKE_CURRENT_SOURCE_DIR}/po/) -endif(BUILD_MESSAGES) diff --git a/README.md b/README.md index 4fd48dd13f5a5fac47e7b3724e4cafa901b7437d..832756757cefe53b2509f8a0ddc17c55f9cd7919 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,58 @@ -GNOME Shell integration for Chrome -============================================ -[![Code Health](https://landscape.io/github/nE0sIghT/chrome-gnome-shell-mirror/master/landscape.svg?style=flat)](https://landscape.io/github/nE0sIghT/chrome-gnome-shell-mirror/master) +# GNOME Shell browser extension +## Introduction -Introduction ------------- +This repository contains browser extension that provides integration with GNOME Shell and the corresponding +extensions repository https://extensions.gnome.org/. -This repository contains Browser extension for Google Chrome/Chromium, Firefox, Vivaldi, Opera (and other -Browser Extension, Chrome Extension or WebExtensions capable browsers) and native host messaging connector -that provides integration with GNOME Shell and the corresponding extensions repository https://extensions.gnome.org/. +This extension works in conjuction with [os-native counterpart](https://gitlab.gnome.org/nE0sIghT/gnome-browser-connector). -Requirements ------------- - * GNOME Shell - * Python 2.7+ or 3.x - * PyGObject - * Python Requests >= 2.4.2 +## Build -Installation ------------- +First you need to install build requirements: +- meson +- python3 +- gettext +- p7zip +- polib python module -Recent installation instructions are [available in GNOME wiki](https://wiki.gnome.org/Projects/GnomeShellIntegrationForChrome/Installation). +Then invoke meson to build extension: +```shell + meson builddir + cd builddir + meson compile +``` + +This will produce 2 zip files in `builddir` folder: +- extension-chrome.zip +- extension-firefox + +Those files can be uploaded to corresponding browser Addons website. +There are also unpacked extensions available under `builddir/extension` folder that can be loaded in web browsers for development purposes. + +## Translations + +This project uses [GNOME Translation Project](https://wiki.gnome.org/TranslationProject). Translation statistics can be obtained on corresponding Damned Lies page. + +### Translation strings handling + +All user-visible web extension strings are maintained by developers in messages.json file at `extension/extension/_locale/en/` folder. File format is described [here](https://developer.chrome.com/extensions/i18n-messages). + +Gettext template located in `po/chrome-gnome-shell.pot` and auto generated using meson: + +```shell + meson -Dbuild_messages=true -Dbuild_extension=false builddir + cd builddir + meson compile +``` + +As result of those commands: +1. New gettext template will be saved to `po/chrome-gnome-shell.pot` file. +2. All `po/*.po` files will be updated by `msgmerge` using new gettext template. +3. All extension locales (messages.json) will be generated from gettext po files. + +This process is fully compatible with Damned Lies. + +There is limited number of [supported locales](https://developer.chrome.com/webstore/i18n?csw=1#localeTable). +If you use an unsupported locale, Google Chrome will ignore it. + +To create a new translation you can use msginit command (or any po editor) and gettext template. Please refer [Translation Project wiki page](https://wiki.gnome.org/TranslationProject) for further info about optimal translation workflow. diff --git a/chrome-gnome-shell.doap b/chrome-gnome-shell.doap index d6d2973af081289340f3145e15dca9aca2d20fd7..629d0daffb04a44fed8c28d4b44077c7b3831278 100644 --- a/chrome-gnome-shell.doap +++ b/chrome-gnome-shell.doap @@ -5,20 +5,15 @@ xmlns:gnome="http://api.gnome.org/doap-extensions#" xmlns="http://usefulinc.com/ns/doap#"> - chrome-gnome-shell - GNOME Shell integration for Chrome + gnome-browser-extension + GNOME Shell browser extension - Browser extension for Google Chrome/Chromium, Firefox, Vivaldi, Opera (and other Browser Extension, - Chrome Extension or WebExtensions capable browsers) and native host messaging connector that provides - integration with GNOME Shell and the corresponding extensions repository https://extensions.gnome.org. + Browser extension that provides integration with GNOME Shell and the corresponding + extensions repository https://extensions.gnome.org/. - - + JavaScript Python diff --git a/connector/chrome-gnome-shell.py b/connector/chrome-gnome-shell.py deleted file mode 100755 index 9cfc57a4085552e8cbbb78137445b9d6dc1d9eb4..0000000000000000000000000000000000000000 --- a/connector/chrome-gnome-shell.py +++ /dev/null @@ -1,567 +0,0 @@ -#!/usr/bin/env python -# -*- coding: UTF-8 -*- - -""" - GNOME Shell integration for Chrome - Copyright (C) 2016-2019 Yuri Konotopov - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. -""" - -from __future__ import unicode_literals -from __future__ import print_function -from gi.repository import GLib, Gio -import json -import os -import re -import signal -import struct -import sys -import traceback - -CONNECTOR_VERSION = 10 -DEBUG_ENABLED = False - -SHELL_SCHEMA = "org.gnome.shell" -ENABLED_EXTENSIONS_KEY = "enabled-extensions" -EXTENSION_DISABLE_VERSION_CHECK_KEY = "disable-extension-version-validation" -DISABLE_USER_EXTENSIONS_KEY = "disable-user-extensions" - -# https://developer.chrome.com/extensions/nativeMessaging#native-messaging-host-protocol -MESSAGE_LENGTH_SIZE = 4 - - -# https://wiki.gnome.org/Projects/GnomeShell/Extensions/UUIDGuidelines -def is_uuid(uuid): - return uuid is not None and re.match('[-a-zA-Z0-9@._]+$', uuid) is not None - - -def debug(message): - if DEBUG_ENABLED: - log_error(message) - - -def log_error(message): - print('[%d] %s' % (os.getpid(), message), file=sys.stderr) - - -class ChromeGNOMEShell(Gio.Application): - def __init__(self, run_as_service): - Gio.Application.__init__( - self, - application_id='org.gnome.ChromeGnomeShell', - flags=Gio.ApplicationFlags.IS_SERVICE if run_as_service - else Gio.ApplicationFlags.IS_LAUNCHER | Gio.ApplicationFlags.HANDLES_OPEN - ) - - self.gio_settings = None - self.shellAppearedId = None - self.shellSignalId = None - self.disableUserExtensionsSignalId = None - self.disableVersionCheckSignalId = None - - # Set custom exception hook - # noinspection SpellCheckingInspection - sys.excepthook = self.default_exception_hook - - self.register() - - if not run_as_service: - self.shell_proxy = Gio.DBusProxy.new_sync(self.get_dbus_connection(), - Gio.DBusProxyFlags.NONE, - None, - 'org.gnome.Shell', - '/org/gnome/Shell', - 'org.gnome.Shell.Extensions', - None) - - self.get_dbus_connection().signal_subscribe( - self.get_application_id(), - self.get_application_id(), - None, - "/org/gnome/ChromeGnomeShell", - None, - Gio.DBusSignalFlags.NONE, - self.on_dbus_signal, - None - ) - - stdin = GLib.IOChannel.unix_new(sys.stdin.fileno()) - stdin.set_encoding(None) - stdin.set_buffered(False) - - GLib.io_add_watch(stdin, GLib.PRIORITY_DEFAULT, GLib.IOCondition.IN, self.on_input, None) - GLib.io_add_watch(stdin, GLib.PRIORITY_DEFAULT, GLib.IOCondition.HUP, self.on_hup, None) - GLib.io_add_watch(stdin, GLib.PRIORITY_DEFAULT, GLib.IOCondition.ERR, self.on_hup, None) - else: - self.add_simple_action("create-notification", self.on_create_notification, 'a{sv}') - self.add_simple_action("on-notification-clicked", self.on_notification_clicked, 's') - self.add_simple_action("on-notification-action", self.on_notification_action, '(si)') - - GLib.timeout_add_seconds(5 * 60, self.on_service_timeout, None) - - GLib.unix_signal_add(GLib.PRIORITY_DEFAULT, signal.SIGINT, self.on_sigint, None) - - if not run_as_service or not self.get_is_remote(): - self.hold() - - # Is there any way to hook this to shutdown? - def clean_release(self): - debug('Release') - - if self.shellAppearedId: - Gio.bus_unwatch_name(self.shellAppearedId) - - if self.shellSignalId: - dbus_connection = self.get_dbus_connection() - - if dbus_connection is not None: - dbus_connection.signal_unsubscribe(self.shellSignalId) - - if self.disableUserExtensionsSignalId: - if self.gio_settings is not None: - self.gio_settings.disconnect(self.disableUserExtensionsSignalId) - - if self.disableVersionCheckSignalId: - if self.gio_settings is not None: - self.gio_settings.disconnect(self.disableVersionCheckSignalId) - - self.release() - - def default_exception_hook(self, exception_type, value, tb): - log_error("Uncaught exception of type %s occured" % exception_type) - traceback.print_tb(tb) - log_error("Exception: %s" % value) - - self.clean_release() - - def add_simple_action(self, name, callback, parameter_type): - action = Gio.SimpleAction.new( - name, - GLib.VariantType.new(parameter_type) if parameter_type is not None else None - ) - action.connect('activate', callback) - self.add_action(action) - - # Service events - # noinspection PyUnusedLocal - def on_create_notification(self, source, request): - debug('On create notification') - - request = request.unpack() - - notification = Gio.Notification.new(request['title']) - notification.set_body(request['message']) - notification.set_priority(Gio.NotificationPriority.NORMAL) - notification.set_default_action_and_target( - "app.on-notification-clicked", - GLib.Variant.new_string(request['name']) - ) - - if 'buttons' in request: - for button_id, button in enumerate(request['buttons']): - notification.add_button_with_target( - button['title'], - "app.on-notification-action", - GLib.Variant.new_tuple( - GLib.Variant.new_string(request['name']), - GLib.Variant.new_int32(button_id) - ) - ) - - self.send_notification(request['name'], notification) - - # noinspection PyUnusedLocal - def on_notification_action(self, notification, parameters): - debug('Notification %s action: %s' % parameters.unpack()) - - self.get_dbus_connection().emit_signal( - None, - self.get_dbus_object_path(), - self.get_application_id(), - "NotificationAction", - parameters - ) - - # noinspection PyUnusedLocal - def on_notification_clicked(self, notification, notification_name): - debug('Notification %s clicked' % notification_name) - - self.get_dbus_connection().emit_signal( - None, - self.get_dbus_object_path(), - self.get_application_id(), - "NotificationClicked", - GLib.Variant.new_tuple(notification_name) - ) - - # noinspection PyUnusedLocal - def on_service_timeout(self, data): - debug('On service timeout') - self.clean_release() - - return False - - # Native messaging events - # noinspection PyUnusedLocal - def on_input(self, source, condition, data): - debug('On input') - text_length_bytes = source.read(MESSAGE_LENGTH_SIZE) - - if len(text_length_bytes) == 0: - debug('Release condition: %s' % str(condition)) - self.clean_release() - return - - # Unpack message length as 4 byte integer. - text_length = struct.unpack(b'i', text_length_bytes)[0] - - # Read the text (JSON object) of the message. - text = source.read(text_length).decode('utf-8') - - request = json.loads(text) - - if 'execute' in request: - if 'uuid' in request and not is_uuid(request['uuid']): - return - - self.process_request(request) - - return True - - # noinspection SpellCheckingInspection,PyUnusedLocal - def on_dbus_signal(self, connection, sender_name, object_path, interface_name, signal_name, parameters, user_data): - debug('Signal %s from %s' % (signal_name, interface_name)) - - if interface_name == "org.gnome.Shell.Extensions" and signal_name == 'ExtensionStatusChanged': - self.send_message({'signal': signal_name, 'parameters': parameters.unpack()}) - elif interface_name == self.get_application_id(): - if signal_name == 'NotificationAction': - notification_name, button_id = parameters.unpack() - - self.send_message({ - 'signal': "NotificationAction", - 'name': notification_name, - 'button_id': button_id - }) - elif signal_name == 'NotificationClicked': - (notification_name,) = parameters.unpack() - - self.send_message({ - 'signal': "NotificationClicked", - 'name': notification_name - }) - - # noinspection PyUnusedLocal - def on_shell_appeared(self, connection, name, name_owner): - debug('Signal: to %s' % name) - self.send_message({'signal': name}) - debug('Signal: from %s' % name) - - def on_setting_changed(self, settings, key): - if not key in (DISABLE_USER_EXTENSIONS_KEY, EXTENSION_DISABLE_VERSION_CHECK_KEY): - return - - debug('on_setting_changed: %s=%s' % (key, settings.get_value(key).unpack())) - self.send_message({ - 'signal': 'ShellSettingsChanged', - 'key': key, - 'value': settings.get_value(key).unpack() - }) - - # General events - # noinspection PyUnusedLocal - def on_hup(self, source, condition, data): - debug('On hup: %s' % str(condition)) - self.clean_release() - - return False - - # noinspection PyUnusedLocal - def on_sigint(self, data): - debug('On sigint') - self.clean_release() - - return False - - # Helpers - # noinspection SpellCheckingInspection - def dbus_call_response(self, method, parameters, result_property): - try: - result = self.shell_proxy.call_sync(method, - parameters, - Gio.DBusCallFlags.NONE, - -1, - None) - - self.send_message({'success': True, result_property: result.unpack()[0]}) - except GLib.GError as e: - self.send_error(e.message) - - def send_error(self, message): - self.send_message({'success': False, 'message': message}) - - @staticmethod - def send_message(response): - """ - Helper function that sends a message to the webapp. - :param response: dictionary of response data - :return: None - """ - - message = json.dumps(response) - message_length = len(message.encode('utf-8')) - - if message_length > 1024*1024: - log_error('Too long message (%d): "%s"' % (message_length, message)) - return - - try: - stdout = GLib.IOChannel.unix_new(sys.stdout.fileno()) - stdout.set_encoding(None) - stdout.set_buffered(False) - - stdout.write_chars(struct.pack(b'I', message_length), MESSAGE_LENGTH_SIZE) - - # Write the message itself. - stdout.write_chars(message.encode('utf-8'), message_length) - except IOError as e: - log_error('IOError occured: %s' % e.strerror) - sys.exit(1) - - def get_variant(self, data, basic_type=False): - if isinstance(data, ("".__class__, u"".__class__)) or type(data) is int or basic_type: - if isinstance(data, ("".__class__, u"".__class__)): - return GLib.Variant.new_string(data) - elif type(data) is int: - return GLib.Variant.new_int32(data) - else: - raise Exception("Unknown basic data type: %s, %s" % (type(data), str(data))) - elif type(data) is list: - variant_builder = GLib.VariantBuilder.new(GLib.VariantType.new('av')) - - for value in data: - variant_builder.add_value(GLib.Variant.new_variant(self.get_variant(value))) - - return variant_builder.end() - - elif type(data) is dict: - variant_builder = GLib.VariantBuilder.new(GLib.VariantType.new('a{sv}')) - - for key in data: - if data[key] is None: - continue - - if sys.version < '3': - # pylint: disable=E0602 - # noinspection PyUnresolvedReferences - key_string = unicode(key) - else: - key_string = str(key) - - variant_builder.add_value( - GLib.Variant.new_dict_entry( - self.get_variant(key_string, True), GLib.Variant.new_variant(self.get_variant(data[key])) - ) - ) - - return variant_builder.end() - else: - raise Exception("Unknown data type: %s" % type(data)) - - def obtain_gio_settings(self): - if not self.gio_settings: - source = Gio.SettingsSchemaSource.get_default() - - if source is not None and source.lookup(SHELL_SCHEMA, True) is not None: - self.gio_settings = Gio.Settings.new(SHELL_SCHEMA) - - def set_shell_boolean(self, key, value): - self.obtain_gio_settings() - if key in self.gio_settings.keys(): - return self.gio_settings.set_boolean(key, True if value else False) - - return False - - def process_request(self, request): - debug('Execute: to %s' % request['execute']) - - if request['execute'] == 'initialize': - shell_version = self.shell_proxy.get_cached_property("ShellVersion") - - if shell_version is not None: - self.obtain_gio_settings() - - if EXTENSION_DISABLE_VERSION_CHECK_KEY in self.gio_settings.keys(): - disable_version_check = self.gio_settings.get_boolean(EXTENSION_DISABLE_VERSION_CHECK_KEY) - else: - disable_version_check = False - - if DISABLE_USER_EXTENSIONS_KEY in self.gio_settings.keys(): - disable_user_extensions = self.gio_settings.get_boolean(DISABLE_USER_EXTENSIONS_KEY) - else: - disable_user_extensions = False - - supports = ['notifications', 'v6'] - - self.send_message( - { - 'success': True, - 'properties': { - 'connectorVersion': CONNECTOR_VERSION, - 'shellVersion': shell_version.unpack(), - 'versionValidationEnabled': not disable_version_check, - 'userExtensionsDisabled': disable_user_extensions, - 'supports': supports - } - } - ) - else: - self.send_message( - { - 'success': False, - 'message': "no_gnome_shell" - } - ) - - elif request['execute'] == 'subscribeSignals': - if not self.shellAppearedId: - self.shellAppearedId = Gio.bus_watch_name_on_connection( - self.get_dbus_connection(), - 'org.gnome.Shell', - Gio.BusNameWatcherFlags.NONE, - self.on_shell_appeared, - None - ) - - if not self.shellSignalId: - self.shellSignalId = self.get_dbus_connection().signal_subscribe( - "org.gnome.Shell", - "org.gnome.Shell.Extensions", - "ExtensionStatusChanged", - "/org/gnome/Shell", - None, - Gio.DBusSignalFlags.NONE, - self.on_dbus_signal, - None - ) - - if not self.disableUserExtensionsSignalId: - self.obtain_gio_settings() - self.disableUserExtensionsSignalId = self.gio_settings.connect( - "changed::%s" % DISABLE_USER_EXTENSIONS_KEY, - self.on_setting_changed) - - if not self.disableVersionCheckSignalId: - self.obtain_gio_settings() - self.disableVersionCheckSignalId = self.gio_settings.connect( - "changed::%s" % EXTENSION_DISABLE_VERSION_CHECK_KEY, - self.on_setting_changed) - - elif request['execute'] == 'installExtension': - self.dbus_call_response( - "InstallRemoteExtension", - GLib.Variant.new_tuple(GLib.Variant.new_string(request['uuid'])), - "status" - ) - - elif request['execute'] == 'listExtensions': - self.dbus_call_response("ListExtensions", None, "extensions") - - elif request['execute'] == 'enableExtension': - self.obtain_gio_settings() - uuids = self.gio_settings.get_strv(ENABLED_EXTENSIONS_KEY) - - extensions = [] - if 'extensions' in request: - extensions = request['extensions'] - else: - extensions.append({'uuid': request['uuid'], 'enable': request['enable']}) - - for extension in extensions: - if not is_uuid(extension['uuid']): - continue - - if extension['enable']: - if not extension['uuid'] in uuids: - uuids.append(extension['uuid']) - elif extension['uuid'] in uuids: - uuids = [value for value in uuids if value != extension['uuid']] - - self.gio_settings.set_strv(ENABLED_EXTENSIONS_KEY, uuids) - - self.send_message({'success': True}) - - elif request['execute'] == 'launchExtensionPrefs': - self.shell_proxy.call("LaunchExtensionPrefs", - GLib.Variant.new_tuple(GLib.Variant.new_string(request['uuid'])), - Gio.DBusCallFlags.NONE, - -1, - None, - None, - None) - - elif request['execute'] == 'getExtensionErrors': - self.dbus_call_response("GetExtensionErrors", - GLib.Variant.new_tuple(GLib.Variant.new_string(request['uuid'])), - "extensionErrors") - - elif request['execute'] == 'getExtensionInfo': - self.dbus_call_response("GetExtensionInfo", - GLib.Variant.new_tuple(GLib.Variant.new_string(request['uuid'])), - "extensionInfo") - - elif request['execute'] == 'uninstallExtension': - self.dbus_call_response("UninstallExtension", - GLib.Variant.new_tuple(GLib.Variant.new_string(request['uuid'])), - "status") - - elif request['execute'] == 'setUserExtensionsDisabled': - self.send_message({ - 'success': self.set_shell_boolean( - DISABLE_USER_EXTENSIONS_KEY, - request['disable'] - ) - }) - - elif request['execute'] == 'setVersionValidationDisabled': - self.send_message({ - 'success': self.set_shell_boolean( - EXTENSION_DISABLE_VERSION_CHECK_KEY, - request['disable'] - ) - }) - - elif request['execute'] == 'createNotification': - Gio.DBusActionGroup.get( - app.get_dbus_connection(), - app.get_application_id(), - app.get_dbus_object_path() - ).activate_action('create-notification', self.get_variant({ - 'name': request['name'], - 'title': request['options']['title'], - 'message': request['options']['message'], - 'buttons': request['options']['buttons'] - })) - - elif request['execute'] == 'removeNotification': - self.withdraw_notification(request['name']) - - debug('Execute: from %s' % request['execute']) - - -if __name__ == '__main__': - debug('Main. Use Ctrl+D to quit.') - - run_as_service = False - if '--gapplication-service' in sys.argv: - run_as_service = True - sys.argv.remove('--gapplication-service') - - app = ChromeGNOMEShell(run_as_service) - app.run(sys.argv) - - debug('Quit') diff --git a/connector/org.gnome.ChromeGnomeShell.desktop b/connector/org.gnome.ChromeGnomeShell.desktop deleted file mode 100644 index 2eb4ab520fc6546ef6afd7274ed908f616913912..0000000000000000000000000000000000000000 --- a/connector/org.gnome.ChromeGnomeShell.desktop +++ /dev/null @@ -1,8 +0,0 @@ -[Desktop Entry] -Type=Application -Encoding=UTF-8 -Name=GNOME Shell integration -Comment=Provides integration with GNOME Shell and the corresponding extensions repository https://extensions.gnome.org. -Icon=org.gnome.ChromeGnomeShell -DBusActivatable=true -NoDisplay=true diff --git a/connector/org.gnome.ChromeGnomeShell.service.in b/connector/org.gnome.ChromeGnomeShell.service.in deleted file mode 100644 index f22fec4bbe2b124cfad576b4e19cb1225dc4c298..0000000000000000000000000000000000000000 --- a/connector/org.gnome.ChromeGnomeShell.service.in +++ /dev/null @@ -1,3 +0,0 @@ -[D-BUS Service] -Name=org.gnome.ChromeGnomeShell -Exec=${CMAKE_INSTALL_FULL_BINDIR}/chrome-gnome-shell --gapplication-service diff --git a/connector/org.gnome.chrome_gnome_shell.firefox.json b/connector/org.gnome.chrome_gnome_shell.firefox.json deleted file mode 100644 index d93ca735d909c032bde626e2c4fa0d627755cdcf..0000000000000000000000000000000000000000 --- a/connector/org.gnome.chrome_gnome_shell.firefox.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "allowed_extensions": [ "chrome-gnome-shell@gnome.org" ] -} diff --git a/connector/org.gnome.chrome_gnome_shell.json b/connector/org.gnome.chrome_gnome_shell.json deleted file mode 100644 index 3d94d5cc42b6eb7829350505ba40219640ebddd2..0000000000000000000000000000000000000000 --- a/connector/org.gnome.chrome_gnome_shell.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "name": "org.gnome.chrome_gnome_shell", - "description": "Native connector for extensions.gnome.org", - "path": "${CMAKE_INSTALL_FULL_BINDIR}/chrome-gnome-shell", - "type": "stdio", - "allowed_origins": [ - "chrome-extension://${CHROME_EXTENSION_ID}/", - "chrome-extension://${OPERA_EXTENSION_ID}/" - ] -} diff --git a/connector/setup.py b/connector/setup.py deleted file mode 100755 index ee68bdb88caf9c16acf00db573da5d0f683c3707..0000000000000000000000000000000000000000 --- a/connector/setup.py +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env python - -import os -import shutil -from distutils.core import setup - -SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__)) -BUILD_DIR = SCRIPT_DIR + '/../build' -SCRIPT_PATH = BUILD_DIR + '/chrome-gnome-shell' - -if not os.path.exists(BUILD_DIR): - os.makedirs(BUILD_DIR) -shutil.copyfile(SCRIPT_DIR + '/chrome-gnome-shell.py', SCRIPT_PATH) - -setup( - name='chrome-gnome-shell', - description='Provides integration with GNOME Shell extensions repository for Chrome browser', - author='Yuri Konotopov', - url='https://wiki.gnome.org/Projects/GnomeShellIntegrationForChrome', - scripts=[SCRIPT_PATH] -) diff --git a/contrib/cp.py b/contrib/cp.py new file mode 100755 index 0000000000000000000000000000000000000000..8de9ac8f10dd60d11626c86e926f9c95d2eb8f7d --- /dev/null +++ b/contrib/cp.py @@ -0,0 +1,50 @@ +#!/usr/bin/python3 + +import argparse +import os +import shutil +import sys + + +def main(): + parser = argparse.ArgumentParser(os.path.basename(sys.argv[0])) + parser.add_argument( + "--ignore", + help="Ignore files when copying directory" + ) + parser.add_argument( + "--rename", + help="Rename file" + ) + parser.add_argument( + "src", + help="Source" + ) + parser.add_argument( + "dest", + help="Destination" + ) + args = parser.parse_args() + + os.makedirs(args.dest, exist_ok=True) + + if os.path.isdir(args.src): + shutil.copytree( + args.src, + args.dest, + ignore=shutil.ignore_patterns(args.ignore), + dirs_exist_ok=True + ) + else: + shutil.copy( + args.src, + ( + args.dest + if not args.rename + else os.path.join(args.dest, args.rename) + ) + ) + + +if __name__ == '__main__': + main() diff --git a/contrib/merge_json.py b/contrib/merge_json.py new file mode 100755 index 0000000000000000000000000000000000000000..870f07c794d04cffcf91c48bc884334580eae2c0 --- /dev/null +++ b/contrib/merge_json.py @@ -0,0 +1,43 @@ +#!/usr/bin/python3 + +import argparse +import json +import os +import sys + + +def main(): + parser = argparse.ArgumentParser(os.path.basename(sys.argv[0])) + parser.add_argument( + "--delete", + default='', + help="Remove keys from json, comma separated" + ) + parser.add_argument( + "--output", + required=True, + help="Write output to file" + ) + parser.add_argument( + "input", + nargs='+', + help="Chrome extension key" + ) + args = parser.parse_args() + + output = {} + for file in args.input: + with open(file, 'r') as fp: + data = json.load(fp) + for key in args.delete.split(','): + if key in data: + del data[key] + + output = {**output, **data} + + with open(args.output, 'w') as fp: + json.dump(output, fp, ensure_ascii=False, indent=2) + + +if __name__ == '__main__': + main() diff --git a/meson.build b/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..c331b65fc88637a8e689601106511d99fbcfb0fe --- /dev/null +++ b/meson.build @@ -0,0 +1,159 @@ +project('gnome-browser-extension', + license: 'GPL-3', + version : '10.1', +) + +# Constants +CONTRIB_PATH = meson.global_source_root() / 'contrib' +EXTENSION_PATH = meson.global_source_root() / 'extension' +PO_PATH = meson.global_source_root() / 'po' + +EXTENSION_BUILD_DIR = meson.current_build_dir() / 'extension' +CHROME_BUILD_DIR = EXTENSION_BUILD_DIR / 'chrome' +FIREFOX_BUILD_DIR = EXTENSION_BUILD_DIR / 'firefox' + +# Copy helper +cp = CONTRIB_PATH / 'cp.py' + +# Filter that excludes manifest templates +extension_exclude = 'manifest*.json' + +# Build requirements +msgmerge = find_program('msgmerge', required: get_option('build_messages')) +sevenz = find_program('7z', required: get_option('build_source_package')) + +if get_option('build_messages') + py_modules = ['polib'] +else + py_modules = [] +endif + +py = import('python') +py.find_installation('python3', required: true, modules: py_modules) + +# Chrome Store public extension key +chrome_key = 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlig8TAPPQZinMkJnptC0ldizx6fG9jSjZDJ9c8GuLcXeGRH+NMlQuPC9bR5IQlT7+4VY/1tm1+IZ4xvITx1wXCNTR+KXzZv3VNc2D+logehK7oIRTRj0fLhixrx4NLSNK7L7HgV2xcIoW6QV0jOdFcTPL0mWXodXSzZePrvXuflF7qpwNxLzYVi04Vh3xu2oR2Pc9SwfZ4SNbyCaunH/p8n5AYmDuogI2Ah++RZw0ctnqn7mmHrGXteBu/vkpcHZu3B3eW9PFSrv69rRs8duybYR9C91hJm6yzRqZqIpindIU3k2HnNWeCFWkRVpZPhaNVoxcBUO7wWUUwdIflW2JwIDAQAB' + +# Simple python helper that enumerates translations +lang_glob = ''' +import glob +import os + +os.chdir(os.path.join(os.getenv("MESON_SOURCE_ROOT"), "po")) +print("\n".join([l.split('.')[0] for l in glob.glob("*.po")]), end='') +''' + +if get_option('build_extension') + conf_data = configuration_data() + conf_data.set('PUBLIC_KEY', chrome_key) + + # Prepare Chrome manifest + configure_file( + input: EXTENSION_PATH / 'manifest.json', + output: 'manifest-chrome.json', + configuration: conf_data, + format: 'cmake', + ) + + # Prepare Firefox manifest + configure_file( + input: [ + EXTENSION_PATH / 'manifest.json', + EXTENSION_PATH / 'manifest.firefox.json', + ], + output: 'manifest-firefox.json', + command: [ + CONTRIB_PATH / 'merge_json.py', + '--delete', 'key', + '--output', '@OUTPUT@', + '@INPUT@' + ], + ) + + # Build Chrome extension + run_command( + cp, + '--rename', 'manifest.json', + meson.current_build_dir() / 'manifest-chrome.json', + CHROME_BUILD_DIR, + check: true, + ) + run_command( + cp, + '--ignore', extension_exclude, + EXTENSION_PATH, + CHROME_BUILD_DIR, + check: true, + ) + custom_target('chrome-zip', + command: [ + sevenz, 'a', '-tzip', + meson.current_build_dir() / 'extension-chrome.zip', + CHROME_BUILD_DIR / '*', + ], + output: 'extension-chrome.zip', + build_by_default: true, + ) + + # Build Firefox extension + run_command( + cp, + '--rename', 'manifest.json', + meson.current_build_dir() / 'manifest-firefox.json', + FIREFOX_BUILD_DIR, + check: true, + ) + run_command( + cp, + '--ignore', extension_exclude, + EXTENSION_PATH, + FIREFOX_BUILD_DIR, + check: true, + ) + custom_target('firefox-zip', + command: [ + sevenz, 'a', '-tzip', + meson.current_build_dir() / 'extension-firefox.zip', + FIREFOX_BUILD_DIR / '*', + ], + output: 'extension-firefox.zip', + build_by_default: true, + ) +endif + +if get_option('build_messages') + messages_deps = [] + + # Update gettext's pot template from Chrome's messages.json + run_command( + meson.global_source_root() / 'contrib/chrome-messages2po.py', + '--email', get_option('gettext_report_email'), + '--reference-lang', 'en', + '--write-pot', + meson.global_source_root() / 'contrib/chrome-web-store/', + meson.global_source_root() / 'extension/_locales/', + meson.global_source_root() / 'po/', + check: true, + ) + + # Update *.po from generated template + langs = run_command('python3', '-c', lang_glob, check: true).stdout().split('\n') + foreach lang : langs + run_command( + msgmerge, '-q', '--update', + PO_PATH / lang + '.po', + PO_PATH / 'chrome-gnome-shell.pot', + check: true, + ) + endforeach + + # Regenerate extension locales + run_command( + CONTRIB_PATH / 'po2chrome-messages.py', + '--reference-lang', 'en', + CONTRIB_PATH / 'chrome-web-store/', + meson.global_source_root() / 'extension' / '_locales/', + PO_PATH, + check: true, + ) +endif diff --git a/meson_options.txt b/meson_options.txt new file mode 100644 index 0000000000000000000000000000000000000000..43ed6ae2e7c83a8ce6035c4a7e59f7327e22b6a7 --- /dev/null +++ b/meson_options.txt @@ -0,0 +1,10 @@ +option('build_extension', type: 'boolean', value: true, description: 'Build extension zip package') +option('build_messages', type: 'boolean', value: false, description: 'Update translation strings') +option('build_source_package', type: 'boolean', value: false, description: 'Build source package') + +option( + 'gettext_report_email', + type: 'string', + value: 'ykonotopov@gnome.org', + description : 'Report-Msgid-Bugs-To field value in pot file' +)