Commit a988ff10 authored by Christian Hergert's avatar Christian Hergert

qemu: add device provider for cross-architecture emulation

If qemu-user-static is installed, and we detect support from binfmt to run
non-native binaries, we can allow the user to select an alternate
architecture to do a native cross-build. This means that you're building
for another architecture, but instead of a cross-compiler, you're using
a native compiler runding under CPU emulation.

Not all build-systems/runtimes support this, and in the future we'll need
to improve the situation by warning the user about non-sensical setups.

However, if you're using flatpak, this allows us to install an aarch64 or
arm runtime and build against it (and run the result).

For the arm/flatpak case, you'll need to use 3.26 since the master SDK is
not available on the updated sdk build hosts. *womp womp*.
parent 976bf20c
......@@ -61,6 +61,7 @@ option('with_phpize', type: 'boolean')
option('with_project_tree', type: 'boolean')
option('with_python_gi_imports_completion', type: 'boolean')
option('with_python_pack', type: 'boolean')
option('with_qemu', type: 'boolean')
option('with_quick_highlight', type: 'boolean')
option('with_retab', type: 'boolean')
option('with_rust_langserv', type: 'boolean')
......
......@@ -54,6 +54,7 @@ subdir('phpize')
subdir('project-tree')
subdir('python-gi-imports-completion')
subdir('python-pack')
subdir('qemu')
subdir('quick-highlight')
subdir('recent')
subdir('retab')
......@@ -130,6 +131,7 @@ status += [
'Project Tree .......... : @0@'.format(get_option('with_project_tree')),
'Python GI Completion .. : @0@'.format(get_option('with_python_gi_imports_completion')),
'Python Language Pack .. : @0@'.format(get_option('with_python_pack')),
'Qemu .................. : @0@'.format(get_option('with_qemu')),
'Quick Highlight ....... : @0@'.format(get_option('with_quick_highlight')),
'Retab ................. : @0@'.format(get_option('with_retab')),
'Rust Language Server .. : @0@'.format(get_option('with_rust_langserv')),
......
/* gbp-qemu-device-provider.c
*
* Copyright 2018 Christian Hergert <chergert@redhat.com>
*
* 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.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#define G_LOG_DOMAIN "gbp-qemu-device-provider"
#include <glib/gi18n.h>
#include "gbp-qemu-device-provider.h"
struct _GbpQemuDeviceProvider
{
IdeDeviceProvider parent_instance;
};
G_DEFINE_TYPE (GbpQemuDeviceProvider, gbp_qemu_device_provider, IDE_TYPE_DEVICE_PROVIDER)
static const struct {
const gchar *filename;
const gchar *arch;
const gchar *suffix;
} machines[] = {
/* translators: format is "CPU emulation". Only translate "emulation" */
{ "qemu-aarch64", "aarch64", N_("Aarch64 Emulation") },
{ "qemu-arm", "arm", N_("Arm Emulation") },
};
#ifdef __linux__
static void
gbp_qemu_device_provider_load_worker (GTask *task,
gpointer source_object,
gpointer task_data,
GCancellable *cancellable)
{
g_autofree gchar *mounts = NULL;
g_autofree gchar *status = NULL;
g_autoptr(GError) error = NULL;
g_autoptr(GPtrArray) devices = NULL;
IdeContext *context;
IDE_ENTRY;
g_assert (G_IS_TASK (task));
g_assert (GBP_IS_QEMU_DEVICE_PROVIDER (source_object));
g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
devices = g_ptr_array_new_with_free_func (g_object_unref);
/* The first thing we need to do is ensure that binfmt is available
* in /proc/mounts so that the system knows about binfmt hooks.
*/
if (!g_file_get_contents ("/proc/mounts", &mounts, NULL, &error))
{
g_task_return_error (task, g_steal_pointer (&error));
IDE_EXIT;
}
/* @mounts is guaranteed to have a \0 suffix */
if (strstr (mounts, "binfmt") == NULL)
{
g_task_return_new_error (task,
G_IO_ERROR,
G_IO_ERROR_FAILED,
"binfmt is missing from /proc/mounts");
IDE_EXIT;
}
if (!g_file_get_contents ("/proc/sys/fs/binfmt_misc/status", &status, NULL, &error))
{
g_task_return_error (task, g_steal_pointer (&error));
IDE_EXIT;
}
/* @status is guaranteed to have a \0 suffix */
g_strstrip (status);
if (!g_str_equal (status, "enabled"))
{
g_task_return_new_error (task,
G_IO_ERROR,
G_IO_ERROR_FAILED,
"binfmt hooks are not currently enabled");
IDE_EXIT;
}
context = ide_object_get_context (source_object);
/* Now locate which of the machines are registered. Qemu has a huge
* list of these, so we only check for ones we thing are likely to
* be used. If you want support for more, let us know.
*/
for (guint i = 0; i < G_N_ELEMENTS (machines); i++)
{
g_autofree gchar *path = NULL;
path = g_build_filename ("/proc/sys/fs/binfmt_misc", machines[i].filename, NULL);
if (g_file_test (path, G_FILE_TEST_EXISTS))
{
g_autoptr(IdeLocalDevice) device = NULL;
g_autofree gchar *display_name = NULL;
IDE_TRACE_MSG ("Discovered QEMU device \"%s\"\n", machines[i].arch);
/* translators: first %s is replaced with hostname, second %s with the CPU architecture */
display_name = g_strdup_printf (_("My Computer (%s) %s"),
g_get_host_name (),
machines[i].suffix);
device = g_object_new (IDE_TYPE_LOCAL_DEVICE,
"id", machines[i].filename,
"arch", machines[i].arch,
"context", context,
"display-name", display_name,
NULL);
g_ptr_array_add (devices, g_steal_pointer (&device));
}
}
g_task_return_pointer (task,
g_steal_pointer (&devices),
(GDestroyNotify)g_ptr_array_unref);
IDE_EXIT;
}
#endif
static void
gbp_qemu_device_provider_load_async (IdeDeviceProvider *provider,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_autoptr(GTask) task = NULL;
IDE_ENTRY;
g_assert (GBP_IS_QEMU_DEVICE_PROVIDER (provider));
g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
task = g_task_new (provider, cancellable, callback, user_data);
g_task_set_source_tag (task, gbp_qemu_device_provider_load_async);
#ifdef __linux__
g_task_run_in_thread (task, gbp_qemu_device_provider_load_worker);
#else
g_task_return_new_error (task,
G_IO_ERROR,
G_IO_ERROR_NOT_SUPPORTED,
"Qemu device hooks are only supported on Linux");
#endif
IDE_EXIT;
}
static gboolean
gbp_qemu_device_provider_load_finish (IdeDeviceProvider *provider,
GAsyncResult *result,
GError **error)
{
g_autoptr(GPtrArray) devices = NULL;
IDE_ENTRY;
g_assert (IDE_IS_DEVICE_PROVIDER (provider));
g_assert (G_IS_ASYNC_RESULT (result));
if ((devices = g_task_propagate_pointer (G_TASK (result), error)))
{
for (guint i = 0; i < devices->len; i++)
{
IdeDevice *device = g_ptr_array_index (devices, i);
ide_device_provider_emit_device_added (provider, device);
}
}
IDE_RETURN (!!devices);
}
static void
gbp_qemu_device_provider_finalize (GObject *object)
{
G_OBJECT_CLASS (gbp_qemu_device_provider_parent_class)->finalize (object);
}
static void
gbp_qemu_device_provider_class_init (GbpQemuDeviceProviderClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
IdeDeviceProviderClass *provider_class = IDE_DEVICE_PROVIDER_CLASS (klass);
object_class->finalize = gbp_qemu_device_provider_finalize;
provider_class->load_async = gbp_qemu_device_provider_load_async;
provider_class->load_finish = gbp_qemu_device_provider_load_finish;
}
static void
gbp_qemu_device_provider_init (GbpQemuDeviceProvider *self)
{
}
/* gbp-qemu-device-provider.h
*
* Copyright 2018 Christian Hergert <chergert@redhat.com>
*
* 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.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#pragma once
#include <ide.h>
G_BEGIN_DECLS
#define GBP_TYPE_QEMU_DEVICE_PROVIDER (gbp_qemu_device_provider_get_type())
G_DECLARE_FINAL_TYPE (GbpQemuDeviceProvider, gbp_qemu_device_provider, GBP, QEMU_DEVICE_PROVIDER, IdeDeviceProvider)
G_END_DECLS
/* gbp-qemu-plugin.c
*
* Copyright 2018 Christian Hergert <chergert@redhat.com>
*
* 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.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#include <ide.h>
#include <libpeas/peas.h>
#include "gbp-qemu-device-provider.h"
void
gbp_qemu_register_types (PeasObjectModule *module)
{
peas_object_module_register_extension_type (module, IDE_TYPE_DEVICE_PROVIDER, GBP_TYPE_QEMU_DEVICE_PROVIDER);
}
if get_option('with_qemu')
qemu_resources = gnome.compile_resources(
'qemu-resources',
'qemu.gresource.xml',
c_name: 'gbp_qemu',
)
qemu_sources = [
'gbp-qemu-plugin.c',
'gbp-qemu-device-provider.c',
]
gnome_builder_plugins_sources += files(qemu_sources)
gnome_builder_plugins_sources += qemu_resources[0]
endif
<?xml version="1.0" encoding="UTF-8"?>
<gresources>
<gresource prefix="/org/gnome/builder/plugins">
<file>qemu.plugin</file>
</gresource>
<gresource prefix="/org/gnome/builder/plugins/qemu-plugin">
</gresource>
</gresources>
[Plugin]
Module=qemu-plugin
Name=Qemu
Description=Integration with Qemu cross-architecture emulation
Authors=Christian Hergert <christian@hergert.me>
Copyright=Copyright © 2018 Christian Hergert
Builtin=true
Embedded=gbp_qemu_register_types
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment