New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add Ubuntu backend with langpack support #18
Changes from all commits
39a07ff
e21d55d
98f2df5
b7c4ccf
aa04719
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| /* | ||
| * Copyright (C) 2016 Matthias Klumpp <matthias@tenstral.net> | ||
| * | ||
| * Licensed under the GNU Lesser General Public License Version 3 | ||
| * | ||
| * This program is free software: you can redistribute it and/or modify | ||
| * it under the terms of the GNU Lesser General Public License as published by | ||
| * the Free Software Foundation, either version 3 of the license, or | ||
| * (at your option) any later version. | ||
| * | ||
| * This software 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 Lesser General Public License for more details. | ||
| * | ||
| * You should have received a copy of the GNU Lesser General Public License | ||
| * along with this software. If not, see <http://www.gnu.org/licenses/>. | ||
| */ | ||
|
|
||
| module backends.ubuntu; | ||
|
|
||
| public import backends.ubuntu.ubupkg; | ||
| public import backends.ubuntu.ubupkgindex; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,191 @@ | ||
| /* | ||
| * Copyright (C) 2016 Canonical Ltd | ||
| * Author: Iain Lane <iain.lane@canonical.com> | ||
| * | ||
| * Licensed under the GNU Lesser General Public License Version 3 | ||
| * | ||
| * This program is free software: you can redistribute it and/or modify | ||
| * it under the terms of the GNU Lesser General Public License as published by | ||
| * the Free Software Foundation, either version 3 of the license, or | ||
| * (at your option) any later version. | ||
| * | ||
| * This software 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 Lesser General Public License for more details. | ||
| * | ||
| * You should have received a copy of the GNU Lesser General Public License | ||
| * along with this software. If not, see <http://www.gnu.org/licenses/>. | ||
| */ | ||
|
|
||
| module backends.ubuntu.ubupkg; | ||
|
|
||
| import appstream.Component; | ||
|
|
||
| import backends.debian.debpkg; | ||
| import backends.interfaces; | ||
|
|
||
| import glib.Internationalization; | ||
| import glib.KeyFile; | ||
|
|
||
| import std.container : Array; | ||
|
|
||
| import logging; | ||
|
|
||
| import utils : DESKTOP_GROUP; | ||
|
|
||
| extern (C) char *bindtextdomain (const char *domainname, const char *dirName) nothrow @nogc; | ||
|
|
||
| class UbuntuPackage : DebPackage | ||
| { | ||
| this (string pname, string pver, string parch, string globalTmpDir, ref Array!Package allPackages) | ||
| { | ||
| this.globalTmpDir = globalTmpDir; | ||
| this.langpackDir = buildPath (globalTmpDir, "langpacks"); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How big are the langpacks? Does it make sense to unpack them into an in-memory location for speed? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Too big, as above, but a future optimisation is to extract the |
||
| this.localeDir = buildPath (langpackDir, "locales"); | ||
| this.allPackages = allPackages; | ||
| super (pname, pver, parch); | ||
| } | ||
|
|
||
| override string[string] getDesktopFileTranslations (KeyFile desktopFile, const string text) | ||
| { | ||
| string langpackdomain; | ||
|
|
||
| try { | ||
| langpackdomain = desktopFile.getString (DESKTOP_GROUP, | ||
| "X-Ubuntu-Gettext-Domain"); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I also came across |
||
| } catch { | ||
| try { | ||
| langpackdomain = desktopFile.getString (DESKTOP_GROUP, | ||
| "X-GNOME-Gettext-Domain"); | ||
| } catch { | ||
| return null; | ||
| } | ||
| } | ||
|
|
||
| logDebug ("%s has langpack domain %s", name, langpackdomain); | ||
|
|
||
| synchronized { | ||
| extractLangpacks (); | ||
| return getTranslations (langpackdomain, text); | ||
| } | ||
| } | ||
|
|
||
| private: | ||
| string globalTmpDir; | ||
| string langpackDir; | ||
| string localeDir; | ||
| string[] langpackLocales; | ||
| Array!Package allPackages; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If you plan to add more stuff to the array, use an Appender: |
||
|
|
||
| private void extractLangpacks () | ||
| { | ||
| import std.algorithm : filter, map; | ||
| import std.array : appender, array, split; | ||
| import std.file : dirEntries, exists, SpanMode, readText; | ||
| import std.parallelism : parallel; | ||
| import std.path : baseName; | ||
| import std.process : Pid, spawnProcess, wait; | ||
| import std.string : splitLines, startsWith; | ||
|
|
||
| auto path = buildPath (langpackDir, "usr", "share", "locale-langpack"); | ||
|
|
||
| if (!langpackDir.exists) { | ||
| bool[string] extracted; | ||
|
|
||
| langpackDir.mkdirRecurse (); | ||
|
|
||
| foreach (pkg; allPackages) { | ||
| if (!pkg.name.startsWith ("language-pack") || pkg.name in extracted) | ||
| continue; | ||
|
|
||
| UbuntuPackage upkg = to!UbuntuPackage (pkg); | ||
|
|
||
| logDebug ("Extracting %s", pkg.name); | ||
| upkg.extractPackage (langpackDir); | ||
|
|
||
| extracted[pkg.name] = true; | ||
| } | ||
|
|
||
| auto supportedd = buildPath (langpackDir, "var", "lib", "locales", "supported.d"); | ||
|
|
||
| localeDir.mkdirRecurse (); | ||
|
|
||
| foreach (locale; parallel (supportedd.dirEntries (SpanMode.shallow), 5)) | ||
| { | ||
| foreach (ref line; locale.readText.splitLines) { | ||
| auto components = line.split (" "); | ||
| auto localecharset = components[0].split ("."); | ||
|
|
||
| auto outdir = buildPath (localeDir, components[0]); | ||
| logDebug ("Generating locale in %s", outdir); | ||
|
|
||
| auto pid = spawnProcess (["/usr/bin/localedef", | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ugly... But I know you tried to avoid this... |
||
| "--no-archive", | ||
| "-i", | ||
| localecharset[0], | ||
| "-c", | ||
| "-f", | ||
| components[1], | ||
| outdir]); | ||
|
|
||
| scope (exit) wait (pid); | ||
| } | ||
| } | ||
|
|
||
| } | ||
|
|
||
| if (langpackLocales is null) | ||
| langpackLocales = localeDir.dirEntries (SpanMode.shallow) | ||
| .filter!(f => f.isDir) | ||
| .map!(f => f.name.baseName) | ||
| .array; | ||
| } | ||
|
|
||
| string[string] getTranslations (const string domain, const string text) | ||
| { | ||
| import core.stdc.locale; | ||
| import core.stdc.string : strdup; | ||
|
|
||
| import std.c.stdlib : getenv, setenv, unsetenv; | ||
| import std.string : fromStringz, toStringz; | ||
|
|
||
| char *[char *] env; | ||
|
|
||
| foreach (ref var; ["LC_ALL", "LANG", "LANGUAGE", "LC_MESSAGES"]) { | ||
| auto value = getenv (var.toStringz); | ||
| if (value !is null) { | ||
| env[var.toStringz] = getenv (var.toStringz).strdup; | ||
| unsetenv (var.toStringz); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess I should in fact retrieve and restore these? Do we care about preserving the values, since asgen isn't translated? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm, not sure... If we - for whatever reason - add translation stuff to asgen in future, we wouldn't need to chase down bugs which for some reason only occur with the Ubuntu backend. |
||
| } | ||
| } | ||
|
|
||
| scope (exit) { | ||
| foreach (key, val; env) | ||
| setenv (key, val, false); | ||
| } | ||
|
|
||
| setenv ("LOCPATH", localeDir.toStringz, true); | ||
|
|
||
| auto initialLocale = setlocale (LC_ALL, ""); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is all very thread unsafe. It's only called from There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, if it's called from There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'll switch to the new There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The slightly annoying result of that will be that we'll need to package libmo in Debian and Ubuntu first, or abandon the dub-based build system in favor of Meson, because dub is unable to check for the existence of libraries and is also unable to conditionally compile stuff based on flags (otherwise one could just disable the Ubuntu backend if they don't have libmo). On the other hand, libmo will likely be super-low on maintenance, so getting it into Debian will be easy. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. On Tue, Aug 30, 2016 at 08:23:41AM -0700, Matthias Klumpp wrote:
I would kill dub if it can't do what we want any more. Would ideally use Iain Lane [ iain@orangesquash.org.uk ] There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's probably also relatively easy to have a D implementation - last time, I just needed to copy the C and massage it slightly ^^ But yeah, long-term I want to get rid of dub anyway, the things that are preventing me from doing so right now are that Meson still needs to do a release with D support (but that should happen very soon, a release is overdue), and that this needs to be updated in Debian. |
||
| scope(exit) setlocale (LC_ALL, initialLocale); | ||
|
|
||
| auto dir = buildPath (langpackDir, | ||
| "usr", | ||
| "share", | ||
| "locale-langpack"); | ||
|
|
||
| string[string] ret; | ||
|
|
||
| foreach (ref locale; langpackLocales) { | ||
| setlocale (LC_ALL, locale.toStringz); | ||
| bindtextdomain (domain.toStringz, dir.toStringz); | ||
| auto translatedtext = Internationalization.dgettext (domain, text); | ||
|
|
||
| if (text != translatedtext) | ||
| ret[locale] = translatedtext; | ||
| } | ||
|
|
||
| return ret; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,62 @@ | ||
| /* | ||
| * Copyright (C) 2016 Canonical Ltd | ||
| * Author: Iain Lane <iain.lane@canonical.com> | ||
| * | ||
| * Licensed under the GNU Lesser General Public License Version 3 | ||
| * | ||
| * This program is free software: you can redistribute it and/or modify | ||
| * it under the terms of the GNU Lesser General Public License as published by | ||
| * the Free Software Foundation, either version 3 of the license, or | ||
| * (at your option) any later version. | ||
| * | ||
| * This software 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 Lesser General Public License for more details. | ||
| * | ||
| * You should have received a copy of the GNU Lesser General Public License | ||
| * along with this software. If not, see <http://www.gnu.org/licenses/>. | ||
| */ | ||
|
|
||
| module backends.ubuntu.ubupkgindex; | ||
|
|
||
| import backends.debian; | ||
| import backends.interfaces; | ||
| import backends.ubuntu.ubupkg; | ||
|
|
||
| import std.container : Array, make; | ||
|
|
||
| class UbuntuPackageIndex : DebianPackageIndex | ||
| { | ||
|
|
||
| public: | ||
| this (string dir) | ||
| { | ||
| /* | ||
| * UbuntuPackage needs to extract the langpacks, so we give it an array | ||
| * of all packages. We don't do this here, as you migh think makes | ||
| * sense, because it is a very expensive operation and we want to avoid | ||
| * doing it if it's not necessary (when no packages being processed are | ||
| * using langpacks). | ||
| */ | ||
| allPackages = make!(Array!Package); | ||
| super (dir); | ||
| } | ||
|
|
||
| override DebPackage newPackage (string name, string ver, string arch) | ||
| { | ||
| return new UbuntuPackage (name, ver, arch, tmpDir, allPackages); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's sort of ugly to push There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. also we push There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Right on both points: We don't use Pushing Also: 921M!!! What is in there? That's an insane size for translations! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Tons of translations, yelp files, translated images, KDE stuff. The big size is why langpacks exist in the first place. :) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Crazy! |
||
| } | ||
|
|
||
| override Package[] packagesFor (string suite, string section, string arch) | ||
| { | ||
| auto pkgs = super.packagesFor (suite, section, arch); | ||
|
|
||
| allPackages ~= pkgs; | ||
|
|
||
| return pkgs; | ||
| } | ||
| } | ||
|
|
||
| private: | ||
| Array!Package allPackages; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It needs to be an There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Aha! So, ignore my appender commend from above, or check if RefAppender does what I think it does ^^ There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I can't make There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. By the way, did you see that this private attribute is not part of the class but on the global (thread-local) scope? |
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need to either make a
DebPackageor anUbuntuPackage, so I added a factory method here whichUbuntuPackageIndexoverrides. Let me know if you can think of a nicer way to do that.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The nicest way I can think of would be making use of D's awesome templating system.
That would allow you to write one function for
DebPackageand Ubuntu packages which gets compiled for the right type at build-time.E.g.:
(call with
loadPackages!DebPackage ("xenial", "main", "amd64"))The current solution isn't very unclean though, so I would be okay with that too (the templating stuff is probably the cleanest way though).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't like having to name the classes in the code like that, so I prefer to leave it as is unless there's a way around that. It's nice that the Debian backend doesn't mention the Ubuntu one at all currently.
I don't think the
static ifbit would be required in this case, though. There's no Ubuntu specific stuff at this level.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay - I am fine with the
newPackagestuff too, I don't think it's very ugly.