From 3014733a5eb995f5c259f21c6bb2059bddc3929e Mon Sep 17 00:00:00 2001 From: Kalev Lember Date: Thu, 20 Dec 2012 21:51:48 +0100 Subject: [PATCH] Update to 3.7.3 Drop upstreamed patches; rebase the ignore-more-pep8-errors patch. --- ignore-more-pep8-errors.patch | 36 +-- property-lookup.patch | 497 ---------------------------------- pygobject3.spec | 14 +- sources | 2 +- 4 files changed, 27 insertions(+), 522 deletions(-) delete mode 100644 property-lookup.patch diff --git a/ignore-more-pep8-errors.patch b/ignore-more-pep8-errors.patch index 627453b..643a70e 100644 --- a/ignore-more-pep8-errors.patch +++ b/ignore-more-pep8-errors.patch @@ -1,22 +1,22 @@ ---- pygobject-3.4.1/tests/Makefile.in.ignore-more-pep8-errors 2012-10-15 07:54:06.000000000 +0200 -+++ pygobject-3.4.1/tests/Makefile.in 2012-10-17 18:20:43.199988314 +0200 -@@ -745,7 +745,7 @@ - @echo " CHECK Pyflakes" +--- pygobject-3.7.3/tests/Makefile.in.ignore-more-pep8-errors 2012-12-20 22:06:52.968605463 +0100 ++++ pygobject-3.7.3/tests/Makefile.in 2012-12-20 22:07:01.671440981 +0100 +@@ -755,7 +755,7 @@ @if type pyflakes >/dev/null 2>&1; then pyflakes $(top_srcdir); else echo "skipped, pyflakes not installed"; fi - @echo " CHECK PEP8" -- @if type pep8 >/dev/null 2>&1; then pep8 --ignore=E501,E123,E124 --repeat --show-source $(top_srcdir); else echo "skipped, pep8 not installed"; fi -+ @if type pep8 >/dev/null 2>&1; then pep8 --ignore=E501,E123,E124,E127 --repeat --show-source $(top_srcdir); else echo "skipped, pep8 not installed"; fi + @if test -z "$$SKIP_PEP8"; then \ + echo " CHECK PEP8"; \ +- if type pep8 >/dev/null 2>&1; then pep8 --ignore=E501,E123,E124 --repeat --show-source $(top_srcdir); else echo "skipped, pep8 not installed"; fi; \ ++ if type pep8 >/dev/null 2>&1; then pep8 --ignore=E501,E123,E124,E127 --repeat --show-source $(top_srcdir); else echo "skipped, pep8 not installed"; fi; \ + fi export `$(DBUS_LAUNCH)` && \ - $(RUN_TESTS_ENV_VARS) $(EXEC_NAME) $(PYTHON) -Wd -Werror::PendingDeprecationWarning -Werror::DeprecationWarning $(srcdir)/runtests.py; rc=$$?; \ - kill $$DBUS_SESSION_BUS_PID; \ ---- pygobject-3.4.1/tests/Makefile.am.ignore-more-pep8-errors 2012-10-15 07:39:55.000000000 +0200 -+++ pygobject-3.4.1/tests/Makefile.am 2012-10-17 18:20:43.199988314 +0200 -@@ -127,7 +127,7 @@ - @echo " CHECK Pyflakes" + $(RUN_TESTS_ENV_VARS) $(EXEC_NAME) $(PYTHON) -Wd -Werror::PendingDeprecationWarning -Werror::DeprecationWarning -Werror::RuntimeWarning $(srcdir)/runtests.py; rc=$$?; \ +--- pygobject-3.7.3/tests/Makefile.am.ignore-more-pep8-errors 2012-12-20 22:06:21.625197530 +0100 ++++ pygobject-3.7.3/tests/Makefile.am 2012-12-20 22:06:39.366862555 +0100 +@@ -133,7 +133,7 @@ @if type pyflakes >/dev/null 2>&1; then pyflakes $(top_srcdir); else echo "skipped, pyflakes not installed"; fi - @echo " CHECK PEP8" -- @if type pep8 >/dev/null 2>&1; then pep8 --ignore=E501,E123,E124 --repeat --show-source $(top_srcdir); else echo "skipped, pep8 not installed"; fi -+ @if type pep8 >/dev/null 2>&1; then pep8 --ignore=E501,E123,E124,E127 --repeat --show-source $(top_srcdir); else echo "skipped, pep8 not installed"; fi + @if test -z "$$SKIP_PEP8"; then \ + echo " CHECK PEP8"; \ +- if type pep8 >/dev/null 2>&1; then pep8 --ignore=E501,E123,E124 --repeat --show-source $(top_srcdir); else echo "skipped, pep8 not installed"; fi; \ ++ if type pep8 >/dev/null 2>&1; then pep8 --ignore=E501,E123,E124,E127 --repeat --show-source $(top_srcdir); else echo "skipped, pep8 not installed"; fi; \ + fi export `$(DBUS_LAUNCH)` && \ - $(RUN_TESTS_ENV_VARS) $(EXEC_NAME) $(PYTHON) -Wd -Werror::PendingDeprecationWarning -Werror::DeprecationWarning $(srcdir)/runtests.py; rc=$$?; \ - kill $$DBUS_SESSION_BUS_PID; \ + $(RUN_TESTS_ENV_VARS) $(EXEC_NAME) $(PYTHON) -Wd -Werror::PendingDeprecationWarning -Werror::DeprecationWarning -Werror::RuntimeWarning $(srcdir)/runtests.py; rc=$$?; \ diff --git a/property-lookup.patch b/property-lookup.patch deleted file mode 100644 index 395f85b..0000000 --- a/property-lookup.patch +++ /dev/null @@ -1,497 +0,0 @@ -From 901c1b6e3722a9cd999e4948297e2c460f101d20 Mon Sep 17 00:00:00 2001 -From: Daniel Drake -Date: Thu, 01 Nov 2012 14:46:22 +0000 -Subject: Fix property lookup in class hierarchy - -Commit 4bfe7972546413f46f5c36737ff03bb5612c1921 introduced a bug where -a Python subclass of a gi-provided base class overrides a property from the -base class. - -The new behaviour in the above commit causes pygobject to seek the property -in the base class and try to read it from there (resulting in confusion) -rather than noticing that the property is overridden and present in the -Python object instance. - -To provide a nicer solution here, we can exploit the fact that -g_object_class_find_property() will traverse the hierarchy in order to -find the right GParamSpec, and the returned GParamSpec can tell us exactly -which GType introduces the property. The strategy is: - - 1. Find pspec with g_object_class_find_property() - 2. Find the class that owns that property (pspec->owner_type) - 3. See if girepository owns that class. - 3a. If yes, get property from there. - 3b. If not, get property "directly" - -And the same for property setting. - -Now that _pygi_lookup_property_from_g_type is always passed the type that -implements the property, it no longer has to go recursing through parent -classes, which was the original cause of confusion. - -https://bugzilla.gnome.org/show_bug.cgi?id=686942 ---- -diff --git a/gi/_gobject/pygobject.c b/gi/_gobject/pygobject.c -index 6dfffef..23a4284 100644 ---- a/gi/_gobject/pygobject.c -+++ b/gi/_gobject/pygobject.c -@@ -51,6 +51,25 @@ GQuark pygobject_wrapper_key; - GQuark pygobject_has_updated_constructor_key; - GQuark pygobject_instance_data_key; - -+/* Copied from glib. gobject uses hyphens in property names, but in Python -+ * we can only represent hyphens as underscores. Convert underscores to -+ * hyphens for glib compatibility. */ -+static void -+canonicalize_key (gchar *key) -+{ -+ gchar *p; -+ -+ for (p = key; *p != 0; p++) -+ { -+ gchar c = *p; -+ -+ if (c != '-' && -+ (c < '0' || c > '9') && -+ (c < 'A' || c > 'Z') && -+ (c < 'a' || c > 'z')) -+ *p = '-'; -+ } -+} - - /* -------------- class <-> wrapper manipulation --------------- */ - -@@ -237,7 +256,7 @@ build_parameter_list(GObjectClass *class) - static PyObject* - PyGProps_getattro(PyGProps *self, PyObject *attr) - { -- char *attr_name; -+ char *attr_name, *property_name; - GObjectClass *class; - GParamSpec *pspec; - GValue value = { 0, }; -@@ -257,15 +276,13 @@ PyGProps_getattro(PyGProps *self, PyObject *attr) - return ret; - } - -- if (self->pygobject != NULL) { -- ret = pygi_get_property_value (self->pygobject, attr_name); -- if (ret != NULL) { -- g_type_class_unref(class); -- return ret; -- } -- } -- -- pspec = g_object_class_find_property(class, attr_name); -+ /* g_object_class_find_property recurses through the class hierarchy, -+ * so the resulting pspec tells us the owner_type that owns the property -+ * we're dealing with. */ -+ property_name = g_strdup(attr_name); -+ canonicalize_key(property_name); -+ pspec = g_object_class_find_property(class, property_name); -+ g_free(property_name); - g_type_class_unref(class); - - if (!pspec) { -@@ -278,14 +295,23 @@ PyGProps_getattro(PyGProps *self, PyObject *attr) - return NULL; - } - -- /* If we're doing it without an instance, return a GParamSpec */ - if (!self->pygobject) { -+ /* If we're doing it without an instance, return a GParamSpec */ - return pyg_param_spec_new(pspec); - } -+ -+ /* See if the property's class is from the gi repository. If so, -+ * use gi to correctly read the property value. */ -+ ret = pygi_get_property_value (self->pygobject, pspec); -+ if (ret != NULL) { -+ return ret; -+ } - -+ /* If we reach here, it must be a property defined outside of gi. -+ * Just do a straightforward read. */ - g_value_init(&value, G_PARAM_SPEC_VALUE_TYPE(pspec)); - pyg_begin_allow_threads; -- g_object_get_property(self->pygobject->obj, attr_name, &value); -+ g_object_get_property(self->pygobject->obj, pspec->name, &value); - pyg_end_allow_threads; - ret = pyg_param_gvalue_as_pyobject(&value, TRUE, pspec); - g_value_unset(&value); -@@ -295,7 +321,6 @@ PyGProps_getattro(PyGProps *self, PyObject *attr) - - static gboolean - set_property_from_pspec(GObject *obj, -- char *attr_name, - GParamSpec *pspec, - PyObject *pvalue) - { -@@ -304,13 +329,13 @@ set_property_from_pspec(GObject *obj, - if (pspec->flags & G_PARAM_CONSTRUCT_ONLY) { - PyErr_Format(PyExc_TypeError, - "property '%s' can only be set in constructor", -- attr_name); -+ pspec->name); - return FALSE; - } - - if (!(pspec->flags & G_PARAM_WRITABLE)) { - PyErr_Format(PyExc_TypeError, -- "property '%s' is not writable", attr_name); -+ "property '%s' is not writable", pspec->name); - return FALSE; - } - -@@ -322,7 +347,7 @@ set_property_from_pspec(GObject *obj, - } - - pyg_begin_allow_threads; -- g_object_set_property(obj, attr_name, &value); -+ g_object_set_property(obj, pspec->name, &value); - pyg_end_allow_threads; - - g_value_unset(&value); -@@ -336,7 +361,7 @@ static int - PyGProps_setattro(PyGProps *self, PyObject *attr, PyObject *pvalue) - { - GParamSpec *pspec; -- char *attr_name; -+ char *attr_name, *property_name; - GObject *obj; - int ret = -1; - -@@ -358,20 +383,33 @@ PyGProps_setattro(PyGProps *self, PyObject *attr, PyObject *pvalue) - return -1; - } - -- ret = pygi_set_property_value (self->pygobject, attr_name, pvalue); -+ obj = self->pygobject->obj; -+ -+ property_name = g_strdup(attr_name); -+ canonicalize_key(property_name); -+ -+ /* g_object_class_find_property recurses through the class hierarchy, -+ * so the resulting pspec tells us the owner_type that owns the property -+ * we're dealing with. */ -+ pspec = g_object_class_find_property(G_OBJECT_GET_CLASS(obj), -+ property_name); -+ g_free(property_name); -+ if (!pspec) { -+ return PyObject_GenericSetAttr((PyObject *)self, attr, pvalue); -+ } -+ -+ /* See if the property's class is from the gi repository. If so, -+ * use gi to correctly read the property value. */ -+ ret = pygi_set_property_value (self->pygobject, pspec, pvalue); - if (ret == 0) - return 0; - else if (ret == -1) - if (PyErr_Occurred()) - return -1; - -- obj = self->pygobject->obj; -- pspec = g_object_class_find_property(G_OBJECT_GET_CLASS(obj), attr_name); -- if (!pspec) { -- return PyObject_GenericSetAttr((PyObject *)self, attr, pvalue); -- } -- -- if (!set_property_from_pspec(obj, attr_name, pspec, pvalue)) -+ /* If we reach here, it must be a property defined outside of gi. -+ * Just do a straightforward set. */ -+ if (!set_property_from_pspec(obj, pspec, pvalue)) - return -1; - - return 0; -@@ -1458,7 +1496,7 @@ pygobject_set_property(PyGObject *self, PyObject *args) - return NULL; - } - -- if (!set_property_from_pspec(self->obj, param_name, pspec, pvalue)) -+ if (!set_property_from_pspec(self->obj, pspec, pvalue)) - return NULL; - - Py_INCREF(Py_None); -@@ -1496,7 +1534,7 @@ pygobject_set_properties(PyGObject *self, PyObject *args, PyObject *kwargs) - goto exit; - } - -- if (!set_property_from_pspec(G_OBJECT(self->obj), key_str, pspec, value)) -+ if (!set_property_from_pspec(G_OBJECT(self->obj), pspec, value)) - goto exit; - } - -diff --git a/gi/pygi-property.c b/gi/pygi-property.c -index 14564d0..4f09e70 100644 ---- a/gi/pygi-property.c -+++ b/gi/pygi-property.c -@@ -25,92 +25,95 @@ - - #include - --/* Copied from glib */ --static void --canonicalize_key (gchar *key) -+static GIPropertyInfo * -+lookup_property_from_object_info (GIObjectInfo *info, const gchar *attr_name) - { -- gchar *p; -+ gssize n_infos; -+ gssize i; - -- for (p = key; *p != 0; p++) -- { -- gchar c = *p; -+ n_infos = g_object_info_get_n_properties (info); -+ for (i = 0; i < n_infos; i++) { -+ GIPropertyInfo *property_info; - -- if (c != '-' && -- (c < '0' || c > '9') && -- (c < 'A' || c > 'Z') && -- (c < 'a' || c > 'z')) -- *p = '-'; -+ property_info = g_object_info_get_property (info, i); -+ g_assert (info != NULL); -+ -+ if (strcmp (attr_name, g_base_info_get_name (property_info)) == 0) { -+ return property_info; -+ } -+ -+ g_base_info_unref (property_info); - } -+ -+ return NULL; - } - - static GIPropertyInfo * --_pygi_lookup_property_from_g_type (GType g_type, const gchar *attr_name) -+lookup_property_from_interface_info (GIInterfaceInfo *info, -+ const gchar *attr_name) - { -- GIRepository *repository; -- GIBaseInfo *info; - gssize n_infos; - gssize i; -- GType parent; - -- repository = g_irepository_get_default(); -- info = g_irepository_find_by_gtype (repository, g_type); -- if (info != NULL) { -+ n_infos = g_interface_info_get_n_properties (info); -+ for (i = 0; i < n_infos; i++) { -+ GIPropertyInfo *property_info; - -- n_infos = g_object_info_get_n_properties ( (GIObjectInfo *) info); -- for (i = 0; i < n_infos; i++) { -- GIPropertyInfo *property_info; -+ property_info = g_interface_info_get_property (info, i); -+ g_assert (info != NULL); - -- property_info = g_object_info_get_property ( (GIObjectInfo *) info, -- i); -- g_assert (info != NULL); -- -- if (strcmp (attr_name, g_base_info_get_name (property_info)) == 0) { -- g_base_info_unref (info); -- return property_info; -- } -- -- g_base_info_unref (property_info); -+ if (strcmp (attr_name, g_base_info_get_name (property_info)) == 0) { -+ return property_info; - } - -- g_base_info_unref (info); -+ g_base_info_unref (property_info); - } - -- parent = g_type_parent (g_type); -- if (parent > 0) -- return _pygi_lookup_property_from_g_type (parent, attr_name); -- - return NULL; - } - -+static GIPropertyInfo * -+_pygi_lookup_property_from_g_type (GType g_type, const gchar *attr_name) -+{ -+ GIPropertyInfo *ret = NULL; -+ GIRepository *repository; -+ GIBaseInfo *info; -+ -+ repository = g_irepository_get_default(); -+ info = g_irepository_find_by_gtype (repository, g_type); -+ if (info == NULL) -+ return NULL; -+ -+ if (GI_IS_OBJECT_INFO (info)) -+ ret = lookup_property_from_object_info ((GIObjectInfo *) info, -+ attr_name); -+ else if (GI_IS_INTERFACE_INFO (info)) -+ ret = lookup_property_from_interface_info ((GIInterfaceInfo *) info, -+ attr_name); -+ -+ g_base_info_unref (info); -+ return ret; -+} -+ - PyObject * --pygi_get_property_value_real (PyGObject *instance, -- const gchar *attr_name) -+pygi_get_property_value_real (PyGObject *instance, GParamSpec *pspec) - { -- GType g_type; - GIPropertyInfo *property_info = NULL; -- char *property_name = g_strdup (attr_name); -- GParamSpec *pspec = NULL; - GValue value = { 0, }; - GIArgument arg = { 0, }; - PyObject *py_value = NULL; - GITypeInfo *type_info = NULL; - GITransfer transfer; - -- canonicalize_key (property_name); -- -- g_type = pyg_type_from_object ((PyObject *)instance); -- property_info = _pygi_lookup_property_from_g_type (g_type, property_name); -+ /* The owner_type of the pspec gives us the exact type that introduced the -+ * property, even if it is a parent class of the instance in question. */ -+ property_info = _pygi_lookup_property_from_g_type (pspec->owner_type, pspec->name); - - if (property_info == NULL) - goto out; - -- pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (instance->obj), -- attr_name); -- if (pspec == NULL) -- goto out; -- - g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (pspec)); -- g_object_get_property (instance->obj, attr_name, &value); -+ g_object_get_property (instance->obj, pspec->name, &value); - - type_info = g_property_info_get_type (property_info); - transfer = g_property_info_get_ownership_transfer (property_info); -@@ -243,7 +246,6 @@ pygi_get_property_value_real (PyGObject *instance, - py_value = _pygi_argument_to_object (&arg, type_info, transfer); - - out: -- g_free (property_name); - if (property_info != NULL) - g_base_info_unref (property_info); - if (type_info != NULL) -@@ -254,33 +256,24 @@ out: - - gint - pygi_set_property_value_real (PyGObject *instance, -- const gchar *attr_name, -+ GParamSpec *pspec, - PyObject *py_value) - { -- GType g_type; - GIPropertyInfo *property_info = NULL; -- char *property_name = g_strdup (attr_name); - GITypeInfo *type_info = NULL; - GITypeTag type_tag; - GITransfer transfer; - GValue value = { 0, }; - GIArgument arg = { 0, }; -- GParamSpec *pspec = NULL; - gint ret_value = -1; - -- canonicalize_key (property_name); -- -- g_type = pyg_type_from_object ((PyObject *)instance); -- property_info = _pygi_lookup_property_from_g_type (g_type, property_name); -- -+ /* The owner_type of the pspec gives us the exact type that introduced the -+ * property, even if it is a parent class of the instance in question. */ -+ property_info = _pygi_lookup_property_from_g_type (pspec->owner_type, -+ pspec->name); - if (property_info == NULL) - goto out; - -- pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (instance->obj), -- attr_name); -- if (pspec == NULL) -- goto out; -- - if (! (pspec->flags & G_PARAM_WRITABLE)) - goto out; - -@@ -413,12 +406,11 @@ pygi_set_property_value_real (PyGObject *instance, - goto out; - } - -- g_object_set_property (instance->obj, attr_name, &value); -+ g_object_set_property (instance->obj, pspec->name, &value); - - ret_value = 0; - - out: -- g_free (property_name); - if (property_info != NULL) - g_base_info_unref (property_info); - if (type_info != NULL) -diff --git a/gi/pygi-property.h b/gi/pygi-property.h -index 31d0e42..875d21e 100644 ---- a/gi/pygi-property.h -+++ b/gi/pygi-property.h -@@ -30,10 +30,10 @@ - #include "pygi.h" - - PyObject *pygi_get_property_value_real (PyGObject *instance, -- const gchar *attr_name); -+ GParamSpec *pspec); - - gint pygi_set_property_value_real (PyGObject *instance, -- const gchar *attr_name, -+ GParamSpec *pspec, - PyObject *py_value); - - #endif /* __PYGI_PROPERTY_H__ */ -diff --git a/gi/pygi.h b/gi/pygi.h -index 121eae5..86da07f 100644 ---- a/gi/pygi.h -+++ b/gi/pygi.h -@@ -77,9 +77,9 @@ typedef PyObject * (*PyGIArgOverrideReleaseFunc) (GITypeInfo *type_info, - struct PyGI_API { - PyObject* (*type_import_by_g_type) (GType g_type); - PyObject* (*get_property_value) (PyGObject *instance, -- const gchar *attr_name); -+ GParamSpec *pspec); - gint (*set_property_value) (PyGObject *instance, -- const gchar *attr_name, -+ GParamSpec *pspec, - PyObject *value); - GClosure * (*signal_closure_new) (PyGObject *instance, - const gchar *sig_name, -@@ -124,23 +124,23 @@ pygi_type_import_by_g_type (GType g_type) - - static inline PyObject * - pygi_get_property_value (PyGObject *instance, -- const gchar *attr_name) -+ GParamSpec *pspec) - { - if (_pygi_import() < 0) { - return NULL; - } -- return PyGI_API->get_property_value(instance, attr_name); -+ return PyGI_API->get_property_value(instance, pspec); - } - - static inline gint - pygi_set_property_value (PyGObject *instance, -- const gchar *attr_name, -+ GParamSpec *pspec, - PyObject *value) - { - if (_pygi_import() < 0) { - return -1; - } -- return PyGI_API->set_property_value(instance, attr_name, value); -+ return PyGI_API->set_property_value(instance, pspec, value); - } - - static inline GClosure * diff --git a/pygobject3.spec b/pygobject3.spec index 014b337..0c66e45 100644 --- a/pygobject3.spec +++ b/pygobject3.spec @@ -21,8 +21,8 @@ ### Abstract ### Name: pygobject3 -Version: 3.7.1 -Release: 3%{?dist} +Version: 3.7.3 +Release: 1%{?dist} License: LGPLv2+ and MIT Group: Development/Languages Summary: Python 2 bindings for GObject Introspection @@ -69,9 +69,6 @@ Patch2: pygobject-3.3.4-known-failures.patch # Not yet sent upstream: Patch3: test-list-marshalling.patch -# upstream fix for property type lookup, needed for basic Sugar operation -Patch4: property-lookup.patch - ### Build Dependencies ### BuildRequires: chrpath @@ -153,7 +150,6 @@ for use in Python 3 programs. %patch1 -p1 -b .ignore-more-pep8-errors %patch2 -p1 -b .known-failures %patch3 -p1 -b .test-list-marshalling -%patch4 -p1 -b .property-lookup %if 0%{?with_python3} rm -rf %{py3dir} @@ -245,6 +241,7 @@ xvfb-run make DESTDIR=$RPM_BUILD_ROOT check %{verbosity} %{python_sitearch}/gi/* %exclude %{python_sitearch}/gi/_gi_cairo.so %{python_sitearch}/pygobject-*.egg-info +%{python_sitearch}/pygtkcompat/ %files devel %defattr(644, root, root, 755) @@ -261,10 +258,15 @@ xvfb-run make DESTDIR=$RPM_BUILD_ROOT check %{verbosity} %dir %{python3_sitearch}/gi %{python3_sitearch}/gi/* %{python3_sitearch}/pygobject-*.egg-info +%{python3_sitearch}/pygtkcompat/ %endif # with_python3 %changelog +* Thu Dec 20 2012 Kalev Lember - 3.7.3-1 +- Update to 3.7.3 +- Drop upstreamed patches; rebase the ignore-more-pep8-errors patch + * Thu Dec 13 2012 Ray Strode 3.7.1-3 - Split non-cairo parts into a subpackage diff --git a/sources b/sources index 95bf324..ae8c92e 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -0dd1ae758e81ece7ddf36baddf093bf5 pygobject-3.7.1.tar.xz +5eb6c2d9f51d151c606e3a3bb2260668 pygobject-3.7.3.tar.xz