Add xdg-app-builder

This is a tool that makes it easy to build applications and their
dependecies by automating the configure && make && make install steps.
tingping/wmclass
Alexander Larsson 2015-11-16 16:23:23 +01:00
parent 7e1a645f99
commit b2790349d6
31 changed files with 6730 additions and 0 deletions

1
.gitignore vendored
View File

@ -29,6 +29,7 @@ xdg-app
xdg-app-helper
xdg-app-session-helper
xdg-document-portal
xdg-app-builder
testdb
doc/*.1
*~

View File

@ -47,6 +47,7 @@ include libglnx/Makefile-libglnx.am.inc
include lib/Makefile.am.inc
include data/Makefile.am.inc
include app/Makefile.am.inc
include builder/Makefile.am.inc
include session-helper/Makefile.am.inc
include dbus-proxy/Makefile.am.inc
include document-portal/Makefile.am.inc

View File

@ -0,0 +1,35 @@
bin_PROGRAMS += \
xdg-app-builder \
$(NULL)
xdg_app_builder_SOURCES = \
builder/xdg-app-builder-main.c \
builder/builder-manifest.c \
builder/builder-manifest.h \
builder/builder-options.c \
builder/builder-options.h \
builder/builder-module.c \
builder/builder-module.h \
builder/builder-source.c \
builder/builder-source.h \
builder/builder-source-archive.c \
builder/builder-source-archive.h \
builder/builder-source-git.c \
builder/builder-source-git.h \
builder/builder-source-bzr.c \
builder/builder-source-bzr.h \
builder/builder-source-file.c \
builder/builder-source-file.h \
builder/builder-source-patch.c \
builder/builder-source-patch.h \
builder/builder-context.c \
builder/builder-context.h \
builder/builder-cache.c \
builder/builder-cache.h \
builder/builder-utils.c \
builder/builder-utils.h \
$(NULL)
xdg_app_builder_LDADD = $(BASE_LIBS) $(OSTREE_LIBS) $(JSON_LIBS) $(SOUP_LIBS) $(LIBARCHIVE_LIBS) libglnx.la libxdgapp.la
xdg_app_builder_CFLAGS = $(BASE_CFLAGS) $(OSTREE_CFLAGS) $(JSON_CFLAGS) $(SOUP_CFLAGS) $(LIBARCHIVE_CFLAGS)

View File

@ -0,0 +1,499 @@
/* builder-cache.c
*
* Copyright (C) 2015 Red Hat, Inc
*
* This file 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 file 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 General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Authors:
* Alexander Larsson <alexl@redhat.com>
*/
#include "config.h"
#include <string.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/statfs.h>
#include <gio/gio.h>
#include <ostree.h>
#include "libglnx/libglnx.h"
#include "libgsystem.h"
#include "xdg-app-utils.h"
#include "builder-utils.h"
#include "builder-cache.h"
struct BuilderCache {
GObject parent;
GChecksum *checksum;
GFile *cache_dir;
GFile *app_dir;
char *branch;
char *last_parent;
OstreeRepo *repo;
gboolean disabled;
};
typedef struct {
GObjectClass parent_class;
} BuilderCacheClass;
G_DEFINE_TYPE (BuilderCache, builder_cache, G_TYPE_OBJECT);
enum {
PROP_0,
PROP_CACHE_DIR,
PROP_APP_DIR,
PROP_BRANCH,
LAST_PROP
};
#define OSTREE_GIO_FAST_QUERYINFO ("standard::name,standard::type,standard::size,standard::is-symlink,standard::symlink-target," \
"unix::device,unix::inode,unix::mode,unix::uid,unix::gid,unix::rdev")
static void
builder_cache_finalize (GObject *object)
{
BuilderCache *self = (BuilderCache *)object;
g_clear_object (&self->cache_dir);
g_clear_object (&self->app_dir);
g_checksum_free (self->checksum);
g_free (self->branch);
g_free (self->last_parent);
G_OBJECT_CLASS (builder_cache_parent_class)->finalize (object);
}
static void
builder_cache_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
BuilderCache *self = BUILDER_CACHE (object);
switch (prop_id)
{
case PROP_CACHE_DIR:
g_value_set_object (value, self->cache_dir);
break;
case PROP_APP_DIR:
g_value_set_object (value, self->app_dir);
break;
case PROP_BRANCH:
g_value_set_string (value, self->branch);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
builder_cache_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
BuilderCache *self = BUILDER_CACHE (object);
switch (prop_id)
{
case PROP_BRANCH:
g_free (self->branch);
self->branch = g_value_dup_string (value);
break;
case PROP_CACHE_DIR:
g_set_object (&self->cache_dir, g_value_get_object (value));
break;
case PROP_APP_DIR:
g_set_object (&self->app_dir, g_value_get_object (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
builder_cache_class_init (BuilderCacheClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = builder_cache_finalize;
object_class->get_property = builder_cache_get_property;
object_class->set_property = builder_cache_set_property;
g_object_class_install_property (object_class,
PROP_CACHE_DIR,
g_param_spec_object ("cache-dir",
"",
"",
G_TYPE_FILE,
G_PARAM_READWRITE|G_PARAM_CONSTRUCT));
g_object_class_install_property (object_class,
PROP_APP_DIR,
g_param_spec_object ("app-dir",
"",
"",
G_TYPE_FILE,
G_PARAM_READWRITE|G_PARAM_CONSTRUCT));
g_object_class_install_property (object_class,
PROP_BRANCH,
g_param_spec_string ("branch",
"",
"",
NULL,
G_PARAM_READWRITE));
}
static void
builder_cache_init (BuilderCache *self)
{
self->checksum = g_checksum_new (G_CHECKSUM_SHA256);
}
BuilderCache *
builder_cache_new (GFile *cache_dir,
GFile *app_dir,
const char *branch)
{
return g_object_new (BUILDER_TYPE_CACHE,
"cache-dir", cache_dir,
"app-dir", app_dir,
"branch", branch,
NULL);
}
GChecksum *
builder_cache_get_checksum (BuilderCache *self)
{
return self->checksum;
}
gboolean
builder_cache_open (BuilderCache *self,
GError **error)
{
self->repo = ostree_repo_new (self->cache_dir);
if (!g_file_query_exists (self->cache_dir, NULL))
{
if (!ostree_repo_create (self->repo, OSTREE_REPO_MODE_BARE, NULL, error))
return FALSE;
}
if (!ostree_repo_open (self->repo, NULL, error))
return FALSE;
return TRUE;
}
static char *
builder_cache_get_current (BuilderCache *self)
{
g_autoptr(GChecksum) copy = g_checksum_copy (self->checksum);
return g_strdup (g_checksum_get_string (copy));
}
static gboolean
builder_cache_checkout (BuilderCache *self, const char *commit)
{
g_autoptr(GFile) root = NULL;
g_autoptr(GFileInfo) file_info = NULL;
if (!ostree_repo_read_commit (self->repo, commit, &root, NULL, NULL, NULL))
return FALSE;
file_info = g_file_query_info (root, OSTREE_GIO_FAST_QUERYINFO,
G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
NULL, NULL);
if (file_info == NULL)
return FALSE;
if (!ostree_repo_checkout_tree (self->repo,
OSTREE_REPO_CHECKOUT_MODE_NONE,
OSTREE_REPO_CHECKOUT_OVERWRITE_NONE,
self->app_dir,
OSTREE_REPO_FILE (root), file_info,
NULL, NULL))
return FALSE;
return TRUE;
}
void
builder_cache_ensure_checkout (BuilderCache *self)
{
if (self->disabled)
return;
if (self->last_parent)
{
g_print ("Everything cached, checking out from cache\n");
if (!builder_cache_checkout (self, self->last_parent))
g_error ("Failed to check out cache");
}
}
gboolean
builder_cache_lookup (BuilderCache *self)
{
g_autofree char *current = NULL;
g_autofree char *commit = NULL;
if (self->disabled)
return FALSE;
if (!ostree_repo_resolve_rev (self->repo, self->branch, TRUE, &commit, NULL))
goto checkout;
current = builder_cache_get_current (self);
while (commit != NULL)
{
g_autoptr(GVariant) variant = NULL;
const gchar *subject;
if (!ostree_repo_load_variant (self->repo, OSTREE_OBJECT_TYPE_COMMIT, commit,
&variant, NULL))
goto checkout;
g_variant_get (variant, "(a{sv}aya(say)&s&stayay)", NULL, NULL, NULL,
&subject, NULL, NULL, NULL, NULL);
if (strcmp (subject, current) == 0)
{
g_free (self->last_parent);
self->last_parent = g_steal_pointer (&commit);
return TRUE;
}
g_free (commit);
commit = ostree_commit_get_parent (variant);
}
checkout:
if (self->last_parent)
{
g_print ("Cache miss, checking out last cache hit\n");
if (!builder_cache_checkout (self, self->last_parent))
g_error ("Failed to check out cache");
}
self->disabled = TRUE; /* Don't use cache any more after first miss */
return FALSE;
}
gboolean
builder_cache_commit (BuilderCache *self,
const char *body,
GError **error)
{
g_autofree char *current = NULL;
OstreeRepoCommitModifier *modifier = NULL;
g_autoptr(OstreeMutableTree) mtree = NULL;
g_autoptr(GFile) root = NULL;
g_autofree char *commit_checksum = NULL;
gboolean res = FALSE;
if (!ostree_repo_prepare_transaction (self->repo, NULL, NULL, error))
return FALSE;
mtree = ostree_mutable_tree_new ();
modifier = ostree_repo_commit_modifier_new (OSTREE_REPO_COMMIT_MODIFIER_FLAGS_SKIP_XATTRS,
NULL, NULL, NULL);
if (!ostree_repo_write_directory_to_mtree (self->repo, self->app_dir,
mtree, modifier, NULL, error))
goto out;
if (!ostree_repo_write_mtree (self->repo, mtree, &root, NULL, error))
goto out;
current = builder_cache_get_current (self);
if (!ostree_repo_write_commit (self->repo, self->last_parent, current, body, NULL,
OSTREE_REPO_FILE (root),
&commit_checksum, NULL, error))
goto out;
ostree_repo_transaction_set_ref (self->repo, NULL, self->branch, commit_checksum);
if (!ostree_repo_commit_transaction (self->repo, NULL, NULL, error))
goto out;
g_free (self->last_parent);
self->last_parent = g_steal_pointer (&commit_checksum);
res = TRUE;
out:
if (!res)
{
if (!ostree_repo_abort_transaction (self->repo, NULL, NULL))
g_warning ("failed to abort transaction");
}
if (modifier)
ostree_repo_commit_modifier_unref (modifier);
return res;
}
GPtrArray *
builder_cache_get_changes (BuilderCache *self,
GError **error)
{
g_autoptr(GPtrArray) added = g_ptr_array_new_with_free_func (g_object_unref);
g_autoptr(GPtrArray) modified = g_ptr_array_new_with_free_func ((GDestroyNotify)ostree_diff_item_unref);
g_autoptr(GPtrArray) removed = g_ptr_array_new_with_free_func (g_object_unref);
g_autoptr(GPtrArray) added_paths = g_ptr_array_new_with_free_func (g_free);
g_autoptr(GFile) current_root = NULL;
g_autoptr(GFile) current_files = NULL;
g_autoptr(GFile) parent_root = NULL;
g_autoptr(GFile) parent_files = NULL;
g_autoptr(GVariant) variant = NULL;
g_autoptr(GHashTable) owned = NULL;
g_autofree char *parent_commit = NULL;
int i;
if (!ostree_repo_read_commit (self->repo, self->last_parent, &current_root, NULL, NULL, error))
return NULL;
current_files = g_file_get_child (current_root, "files");
if (!ostree_repo_load_variant (self->repo, OSTREE_OBJECT_TYPE_COMMIT, self->last_parent,
&variant, NULL))
return NULL;
parent_commit = ostree_commit_get_parent (variant);
if (parent_commit != NULL)
{
if (!ostree_repo_read_commit (self->repo, parent_commit, &parent_root, NULL, NULL, error))
return FALSE;
parent_files = g_file_get_child (parent_root, "files");
}
if (!ostree_diff_dirs (OSTREE_DIFF_FLAGS_NONE,
parent_files,
current_files,
modified,
removed,
added,
NULL, error))
return NULL;
for (i = 0; i < added->len; i++)
{
char *path = g_file_get_relative_path (current_files, g_ptr_array_index (added, i));
g_ptr_array_add (added_paths, path);
}
return g_steal_pointer (&added_paths);
}
void
builder_cache_disable_lookups (BuilderCache *self)
{
self->disabled = TRUE;
}
gboolean
builder_gc (BuilderCache *self,
GError **error)
{
gint objects_total;
gint objects_pruned;
guint64 pruned_object_size_total;
return ostree_repo_prune (self->repo,
OSTREE_REPO_PRUNE_FLAGS_REFS_ONLY, -1,
&objects_total,
&objects_pruned,
&pruned_object_size_total,
NULL, error);
}
void
builder_cache_checksum_str (BuilderCache *self,
const char *str)
{
/* We include the terminating zero so that we make
* a difference between NULL and "". */
if (str)
g_checksum_update (self->checksum, (const guchar *)str, strlen (str) + 1);
else
/* Always add something so we can't be fooled by a sequence like
NULL, "a" turning into "a", NULL. */
g_checksum_update (self->checksum, (const guchar *)"\1", 1);
}
void
builder_cache_checksum_strv (BuilderCache *self,
char **strv)
{
int i;
if (strv)
{
g_checksum_update (self->checksum, (const guchar *)"\1", 1);
for (i = 0; strv[i] != NULL; i++)
builder_cache_checksum_str (self, strv[i]);
}
else
g_checksum_update (self->checksum, (const guchar *)"\2", 1);
}
void
builder_cache_checksum_boolean (BuilderCache *self,
gboolean val)
{
if (val)
g_checksum_update (self->checksum, (const guchar *)"\1", 1);
else
g_checksum_update (self->checksum, (const guchar *)"\0", 1);
}
void
builder_cache_checksum_uint32 (BuilderCache *self,
guint32 val)
{
guchar v[4];
v[0] = (val >> 0) & 0xff;
v[1] = (val >> 8) & 0xff;
v[2] = (val >> 16) & 0xff;
v[3] = (val >> 24) & 0xff;
g_checksum_update (self->checksum, v, 4);
}
void
builder_cache_checksum_data (BuilderCache *self,
guint8 *data,
gsize len)
{
g_checksum_update (self->checksum, data, len);
}

View File

@ -0,0 +1,69 @@
/*
* Copyright © 2015 Red Hat, Inc
*
* 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 2 of the License, or (at your option) any later version.
*
* This library 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 library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors:
* Alexander Larsson <alexl@redhat.com>
*/
#ifndef __BUILDER_CACHE_H__
#define __BUILDER_CACHE_H__
#include <gio/gio.h>
G_BEGIN_DECLS
typedef struct BuilderCache BuilderCache;
#define BUILDER_TYPE_CACHE (builder_cache_get_type())
#define BUILDER_CACHE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), BUILDER_TYPE_CACHE, BuilderCache))
#define BUILDER_IS_CACHE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), BUILDER_TYPE_CACHE))
GType builder_cache_get_type (void);
BuilderCache *builder_cache_new (GFile *cache_dir,
GFile *app_dir,
const char *branch);
void builder_cache_disable_lookups (BuilderCache *self);
gboolean builder_cache_open (BuilderCache *self,
GError **error);
GChecksum * builder_cache_get_checksum (BuilderCache *self);
gboolean builder_cache_lookup (BuilderCache *self);
void builder_cache_ensure_checkout (BuilderCache *self);
gboolean builder_cache_commit (BuilderCache *self,
const char *body,
GError **error);
GPtrArray *builder_cache_get_changes (BuilderCache *self,
GError **error);
gboolean builder_gc (BuilderCache *self,
GError **error);
void builder_cache_checksum_str (BuilderCache *self,
const char *str);
void builder_cache_checksum_strv (BuilderCache *self,
char **strv);
void builder_cache_checksum_boolean (BuilderCache *self,
gboolean val);
void builder_cache_checksum_uint32 (BuilderCache *self,
guint32 val);
void builder_cache_checksum_data (BuilderCache *self,
guint8 *data,
gsize len);
G_DEFINE_AUTOPTR_CLEANUP_FUNC(BuilderCache, g_object_unref)
G_END_DECLS
#endif /* __BUILDER_CACHE_H__ */

View File

@ -0,0 +1,264 @@
/* builder-context.c
*
* Copyright (C) 2015 Red Hat, Inc
*
* This file 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 file 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 General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Authors:
* Alexander Larsson <alexl@redhat.com>
*/
#include "config.h"
#include <string.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/statfs.h>
#include "builder-context.h"
#include "xdg-app-utils.h"
struct BuilderContext {
GObject parent;
GFile *app_dir;
GFile *base_dir;
SoupSession *soup_session;
char *arch;
GFile *download_dir;
GFile *state_dir;
GFile *cache_dir;
BuilderOptions *options;
};
typedef struct {
GObjectClass parent_class;
} BuilderContextClass;
G_DEFINE_TYPE (BuilderContext, builder_context, G_TYPE_OBJECT);
enum {
PROP_0,
PROP_APP_DIR,
PROP_BASE_DIR,
LAST_PROP
};
static void
builder_context_finalize (GObject *object)
{
BuilderContext *self = (BuilderContext *)object;
g_clear_object (&self->app_dir);
g_clear_object (&self->base_dir);
g_clear_object (&self->soup_session);
g_clear_object (&self->options);
g_free (self->arch);
G_OBJECT_CLASS (builder_context_parent_class)->finalize (object);
}
static void
builder_context_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
BuilderContext *self = BUILDER_CONTEXT (object);
switch (prop_id)
{
case PROP_BASE_DIR:
g_value_set_object (value, self->base_dir);
break;
case PROP_APP_DIR:
g_value_set_object (value, self->app_dir);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
builder_context_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
BuilderContext *self = BUILDER_CONTEXT (object);
switch (prop_id)
{
case PROP_BASE_DIR:
g_set_object (&self->base_dir, g_value_get_object (value));
break;
case PROP_APP_DIR:
g_set_object (&self->app_dir, g_value_get_object (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
builder_context_constructed (GObject *object)
{
BuilderContext *self = BUILDER_CONTEXT (object);
self->state_dir = g_file_get_child (self->base_dir, ".xdg-app-builder");
self->download_dir = g_file_get_child (self->state_dir, "downloads");
self->cache_dir = g_file_get_child (self->state_dir, "cache");
}
static void
builder_context_class_init (BuilderContextClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->constructed = builder_context_constructed;
object_class->finalize = builder_context_finalize;
object_class->get_property = builder_context_get_property;
object_class->set_property = builder_context_set_property;
g_object_class_install_property (object_class,
PROP_APP_DIR,
g_param_spec_object ("app-dir",
"",
"",
G_TYPE_FILE,
G_PARAM_READWRITE|G_PARAM_CONSTRUCT));
g_object_class_install_property (object_class,
PROP_BASE_DIR,
g_param_spec_object ("base-dir",
"",
"",
G_TYPE_FILE,
G_PARAM_READWRITE|G_PARAM_CONSTRUCT));
}
static void
builder_context_init (BuilderContext *self)
{
}
GFile *
builder_context_get_base_dir (BuilderContext *self)
{
return self->base_dir;
}
GFile *
builder_context_get_state_dir (BuilderContext *self)
{
return self->state_dir;
}
GFile *
builder_context_get_app_dir (BuilderContext *self)
{
return self->app_dir;
}
GFile *
builder_context_get_download_dir (BuilderContext *self)
{
return self->download_dir;
}
GFile *
builder_context_get_cache_dir (BuilderContext *self)
{
return self->cache_dir;
}
SoupSession *
builder_context_get_soup_session (BuilderContext *self)
{
if (self->soup_session == NULL)
{
const char *http_proxy;
self->soup_session = soup_session_new_with_options (SOUP_SESSION_USER_AGENT, "xdg-app-builder ",
SOUP_SESSION_SSL_USE_SYSTEM_CA_FILE, TRUE,
SOUP_SESSION_USE_THREAD_CONTEXT, TRUE,
SOUP_SESSION_TIMEOUT, 60,
SOUP_SESSION_IDLE_TIMEOUT, 60,
NULL);
http_proxy = g_getenv ("http_proxy");
if (http_proxy)
{
g_autoptr(SoupURI) proxy_uri = soup_uri_new (http_proxy);
if (!proxy_uri)
g_warning ("Invalid proxy URI '%s'", http_proxy);
else
g_object_set (self->soup_session, SOUP_SESSION_PROXY_URI, proxy_uri, NULL);
}
}
return self->soup_session;
}
const char *
builder_context_get_arch (BuilderContext *self)
{
if (self->arch == NULL)
self->arch = g_strdup (xdg_app_get_arch ());
return (const char *)self->arch;
}
void
builder_context_set_arch (BuilderContext *self,
const char *arch)
{
g_free (self->arch);
self->arch = g_strdup (arch);
}
BuilderOptions *
builder_context_get_options (BuilderContext *self)
{
return self->options;
}
void
builder_context_set_options (BuilderContext *self,
BuilderOptions *option)
{
g_set_object (&self->options, option);
}
int
builder_context_get_n_cpu (BuilderContext *self)
{
return (int)sysconf (_SC_NPROCESSORS_ONLN);
}
BuilderContext *
builder_context_new (GFile *base_dir,
GFile *app_dir)
{
return g_object_new (BUILDER_TYPE_CONTEXT,
"base-dir", base_dir,
"app-dir", app_dir,
NULL);
}

View File

@ -0,0 +1,59 @@
/*
* Copyright © 2015 Red Hat, Inc
*
* 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 2 of the License, or (at your option) any later version.
*
* This library 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 library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors:
* Alexander Larsson <alexl@redhat.com>
*/
#ifndef __BUILDER_CONTEXT_H__
#define __BUILDER_CONTEXT_H__
#include <gio/gio.h>
#include <libsoup/soup.h>
#include "builder-options.h"
G_BEGIN_DECLS
/* BuilderContext defined in builder-options.h to fix include loop */
#define BUILDER_TYPE_CONTEXT (builder_context_get_type())
#define BUILDER_CONTEXT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), BUILDER_TYPE_CONTEXT, BuilderContext))
#define BUILDER_IS_CONTEXT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), BUILDER_TYPE_CONTEXT))
GType builder_context_get_type (void);
GFile * builder_context_get_app_dir (BuilderContext *self);
GFile * builder_context_get_base_dir (BuilderContext *self);
GFile * builder_context_get_state_dir (BuilderContext *self);
GFile * builder_context_get_cache_dir (BuilderContext *self);
GFile * builder_context_get_download_dir (BuilderContext *self);
SoupSession * builder_context_get_soup_session (BuilderContext *self);
const char * builder_context_get_arch (BuilderContext *self);
void builder_context_set_arch (BuilderContext *self,
const char *arch);
int builder_context_get_n_cpu (BuilderContext *self);
BuilderOptions *builder_context_get_options (BuilderContext *self);
void builder_context_set_options (BuilderContext *self,
BuilderOptions *option);
BuilderContext *builder_context_new (GFile *base_dir,
GFile *app_dir);
G_DEFINE_AUTOPTR_CLEANUP_FUNC(BuilderContext, g_object_unref)
G_END_DECLS
#endif /* __BUILDER_CONTEXT_H__ */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,73 @@
/*
* Copyright © 2015 Red Hat, Inc
*
* 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 2 of the License, or (at your option) any later version.
*
* This library 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 library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors:
* Alexander Larsson <alexl@redhat.com>
*/
#ifndef __BUILDER_MANIFEST_H__
#define __BUILDER_MANIFEST_H__
#include <json-glib/json-glib.h>
#include "builder-options.h"
#include "builder-module.h"
#include "builder-cache.h"
G_BEGIN_DECLS
typedef struct BuilderManifest BuilderManifest;
#define BUILDER_TYPE_MANIFEST (builder_manifest_get_type())
#define BUILDER_MANIFEST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), BUILDER_TYPE_MANIFEST, BuilderManifest))
#define BUILDER_IS_MANIFEST(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), BUILDER_TYPE_MANIFEST))
/* Bump this if format changes in incompatible ways to force rebuild */
#define BUILDER_MANIFEST_CHECKSUM_VERSION "1"
GType builder_manifest_get_type (void);
const char * builder_manifest_get_app_id (BuilderManifest *self);
BuilderOptions *builder_manifest_get_build_options (BuilderManifest *self);
GList * builder_manifest_get_modules (BuilderManifest *self);
gboolean builder_manifest_init_app_dir (BuilderManifest *self,
BuilderContext *context,
GError **error);
gboolean builder_manifest_download (BuilderManifest *self,
BuilderContext *context,
GError **error);
gboolean builder_manifest_build (BuilderManifest *self,
BuilderCache *cache,
BuilderContext *context,
GError **error);
void builder_manifest_checksum (BuilderManifest *self,
BuilderCache *cache,
BuilderContext *context);
gboolean builder_manifest_cleanup (BuilderManifest *self,
BuilderCache *cache,
BuilderContext *context,
GError **error);
gboolean builder_manifest_finish (BuilderManifest *self,
BuilderCache *cache,
BuilderContext *context,
GError **error);
G_DEFINE_AUTOPTR_CLEANUP_FUNC(BuilderManifest, g_object_unref)
G_END_DECLS
#endif /* __BUILDER_MANIFEST_H__ */

View File

@ -0,0 +1,784 @@
/* builder-module.c
*
* Copyright (C) 2015 Red Hat, Inc
*
* This file 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 file 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 General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Authors:
* Alexander Larsson <alexl@redhat.com>
*/
#include "config.h"
#include <string.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/statfs.h>
#include <gio/gio.h>
#include "libglnx/libglnx.h"
#include "libgsystem.h"
#include "builder-utils.h"
#include "builder-module.h"
struct BuilderModule {
GObject parent;
char *name;
char **config_opts;
char **make_args;
char **make_install_args;
gboolean rm_configure;
gboolean no_autogen;
BuilderOptions *build_options;
GPtrArray *changes;
char **cleanup;
GList *sources;
};
typedef struct {
GObjectClass parent_class;
} BuilderModuleClass;
static void serializable_iface_init (JsonSerializableIface *serializable_iface);
G_DEFINE_TYPE_WITH_CODE (BuilderModule, builder_module, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (JSON_TYPE_SERIALIZABLE, serializable_iface_init));
enum {
PROP_0,
PROP_NAME,
PROP_RM_CONFIGURE,
PROP_NO_AUTOGEN,
PROP_CONFIG_OPTS,
PROP_MAKE_ARGS,
PROP_MAKE_INSTALL_ARGS,
PROP_SOURCES,
PROP_BUILD_OPTIONS,
PROP_CLEANUP,
LAST_PROP
};
static void
builder_module_finalize (GObject *object)
{
BuilderModule *self = (BuilderModule *)object;
g_free (self->name);
g_strfreev (self->config_opts);
g_strfreev (self->make_args);
g_strfreev (self->make_install_args);
g_clear_object (&self->build_options);
g_list_free_full (self->sources, g_object_unref);
g_strfreev (self->cleanup);
if (self->changes)
g_ptr_array_unref (self->changes);
G_OBJECT_CLASS (builder_module_parent_class)->finalize (object);
}
static void
builder_module_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
BuilderModule *self = BUILDER_MODULE (object);
switch (prop_id)
{
case PROP_NAME:
g_value_set_string (value, self->name);
break;
case PROP_RM_CONFIGURE:
g_value_set_boolean (value, self->rm_configure);
break;
case PROP_NO_AUTOGEN:
g_value_set_boolean (value, self->no_autogen);
break;
case PROP_CONFIG_OPTS:
g_value_set_boxed (value, self->config_opts);
break;
case PROP_MAKE_ARGS:
g_value_set_boxed (value, self->make_args);
break;
case PROP_MAKE_INSTALL_ARGS:
g_value_set_boxed (value, self->make_install_args);
break;
case PROP_BUILD_OPTIONS:
g_value_set_object (value, self->build_options);
break;
case PROP_SOURCES:
g_value_set_pointer (value, self->sources);
break;
case PROP_CLEANUP:
g_value_set_boxed (value, self->cleanup);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
builder_module_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
BuilderModule *self = BUILDER_MODULE (object);
gchar **tmp;
switch (prop_id)
{
case PROP_NAME:
g_clear_pointer (&self->name, g_free);
self->name = g_value_dup_string (value);
break;
case PROP_RM_CONFIGURE:
self->rm_configure = g_value_get_boolean (value);
break;
case PROP_NO_AUTOGEN:
self->no_autogen = g_value_get_boolean (value);
break;
case PROP_CONFIG_OPTS:
tmp = self->config_opts;
self->config_opts = g_strdupv (g_value_get_boxed (value));
g_strfreev (tmp);
break;
case PROP_MAKE_ARGS:
tmp = self->make_args;
self->make_args = g_strdupv (g_value_get_boxed (value));
g_strfreev (tmp);
break;
case PROP_MAKE_INSTALL_ARGS:
tmp = self->make_install_args;
self->make_install_args = g_strdupv (g_value_get_boxed (value));
g_strfreev (tmp);
break;
case PROP_BUILD_OPTIONS:
g_set_object (&self->build_options, g_value_get_object (value));
break;
case PROP_SOURCES:
g_list_free_full (self->sources, g_object_unref);
/* NOTE: This takes ownership of the list! */
self->sources = g_value_get_pointer (value);
break;
case PROP_CLEANUP:
tmp = self->cleanup;
self->cleanup = g_strdupv (g_value_get_boxed (value));
g_strfreev (tmp);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
builder_module_class_init (BuilderModuleClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = builder_module_finalize;
object_class->get_property = builder_module_get_property;
object_class->set_property = builder_module_set_property;
g_object_class_install_property (object_class,
PROP_NAME,
g_param_spec_string ("name",
"",
"",
NULL,
G_PARAM_READWRITE));
g_object_class_install_property (object_class,
PROP_RM_CONFIGURE,
g_param_spec_boolean ("rm-configure",
"",
"",
FALSE,
G_PARAM_READWRITE));
g_object_class_install_property (object_class,
PROP_NO_AUTOGEN,
g_param_spec_boolean ("no-autogen",
"",
"",
FALSE,
G_PARAM_READWRITE));
g_object_class_install_property (object_class,
PROP_SOURCES,
g_param_spec_pointer ("sources",
"",
"",
G_PARAM_READWRITE));
g_object_class_install_property (object_class,
PROP_CONFIG_OPTS,
g_param_spec_boxed ("config-opts",
"",
"",
G_TYPE_STRV,
G_PARAM_READWRITE));
g_object_class_install_property (object_class,
PROP_MAKE_ARGS,
g_param_spec_boxed ("make-args",
"",
"",
G_TYPE_STRV,
G_PARAM_READWRITE));
g_object_class_install_property (object_class,
PROP_MAKE_INSTALL_ARGS,
g_param_spec_boxed ("make-install-args",
"",
"",
G_TYPE_STRV,
G_PARAM_READWRITE));
g_object_class_install_property (object_class,
PROP_BUILD_OPTIONS,
g_param_spec_object ("build-options",
"",
"",
BUILDER_TYPE_OPTIONS,
G_PARAM_READWRITE));
g_object_class_install_property (object_class,
PROP_CLEANUP,
g_param_spec_boxed ("cleanup",
"",
"",
G_TYPE_STRV,
G_PARAM_READWRITE));
}
static void
builder_module_init (BuilderModule *self)
{
}
static gboolean
builder_module_deserialize_property (JsonSerializable *serializable,
const gchar *property_name,
GValue *value,
GParamSpec *pspec,
JsonNode *property_node)
{
if (strcmp (property_name, "sources") == 0)
{
if (JSON_NODE_TYPE (property_node) == JSON_NODE_NULL)
{
g_value_set_pointer (value, NULL);
return TRUE;
}
else if (JSON_NODE_TYPE (property_node) == JSON_NODE_ARRAY)
{
JsonArray *array = json_node_get_array (property_node);
guint i, array_len = json_array_get_length (array);
GList *sources = NULL;
BuilderSource *source;
for (i = 0; i < array_len; i++)
{
JsonNode *element_node = json_array_get_element (array, i);
if (JSON_NODE_TYPE (element_node) != JSON_NODE_OBJECT)
{
g_list_free_full (sources, g_object_unref);
return FALSE;
}
source = builder_source_from_json (element_node);
if (source == NULL)
{
g_list_free_full (sources, g_object_unref);
return FALSE;
}
sources = g_list_prepend (sources, source);
}
g_value_set_pointer (value, g_list_reverse (sources));
return TRUE;
}
return FALSE;
}
else
return json_serializable_default_deserialize_property (serializable,
property_name,
value,
pspec, property_node);
}
static void
serializable_iface_init (JsonSerializableIface *serializable_iface)
{
serializable_iface->deserialize_property = builder_module_deserialize_property;
}
const char *
builder_module_get_name (BuilderModule *self)
{
return self->name;
}
GList *
builder_module_get_sources (BuilderModule *self)
{
return self->sources;
}
gboolean
builder_module_download_sources (BuilderModule *self,
BuilderContext *context,
GError **error)
{
GList *l;
for (l = self->sources; l != NULL; l = l->next)
{
BuilderSource *source = l->data;
if (!builder_source_download (source, context, error))
return FALSE;
}
return TRUE;
}
gboolean
builder_module_extract_sources (BuilderModule *self,
GFile *dest,
BuilderContext *context,
GError **error)
{
GList *l;
if (!g_file_query_exists (dest, NULL) &&
!g_file_make_directory_with_parents (dest, NULL, error))
return FALSE;
for (l = self->sources; l != NULL; l = l->next)
{
BuilderSource *source = l->data;
if (!builder_source_extract (source, dest, context, error))
return FALSE;
}
return TRUE;
}
static const char skip_arg[] = "skip";
static const char strv_arg[] = "strv";
static gboolean
build (GFile *app_dir,
GFile *source_dir,
GFile *cwd_dir,
char **xdg_app_opts,
char **env_vars,
GError **error,
const gchar *argv1,
...)
{
g_autoptr(GSubprocessLauncher) launcher = NULL;
g_autoptr(GSubprocess) subp = NULL;
g_autoptr(GPtrArray) args = NULL;
const gchar *arg;
const gchar **argv;
g_autofree char *commandline = NULL;
g_autofree char *source_dir_path = g_file_get_path (source_dir);
g_autofree char *source_dir_path_canonical = NULL;
g_autofree char *cwd_dir_path = NULL;
g_autofree char *cwd_dir_path_canonical = NULL;
va_list ap;
int i;
args = g_ptr_array_new_with_free_func (g_free);
g_ptr_array_add (args, g_strdup ("xdg-app"));
g_ptr_array_add (args, g_strdup ("build"));
source_dir_path_canonical = canonicalize_file_name (source_dir_path);
g_ptr_array_add (args, g_strdup ("--nofilesystem=host"));
g_ptr_array_add (args, g_strdup_printf ("--filesystem=%s", source_dir_path_canonical));
if (xdg_app_opts)
{
for (i = 0; xdg_app_opts[i] != NULL; i++)
g_ptr_array_add (args, g_strdup (xdg_app_opts[i]));
}
if (env_vars)
{
for (i = 0; env_vars[i] != NULL; i++)
g_ptr_array_add (args, g_strdup_printf ("--env=%s", env_vars[i]));
}
g_ptr_array_add (args, g_file_get_path (app_dir));
va_start (ap, argv1);
g_ptr_array_add (args, g_strdup (argv1));
while ((arg = va_arg (ap, const gchar *)))
{
if (arg == strv_arg)
{
argv = va_arg (ap, const gchar **);
if (argv != NULL)
{
for (i = 0; argv[i] != NULL; i++)
g_ptr_array_add (args, g_strdup (argv[i]));
}
}
else if (arg != skip_arg)
g_ptr_array_add (args, g_strdup (arg));
}
g_ptr_array_add (args, NULL);
va_end (ap);
commandline = g_strjoinv (" ", (char **) args->pdata);
g_print ("Running: %s\n", commandline);
launcher = g_subprocess_launcher_new (0);
if (cwd_dir)
{
cwd_dir_path = g_file_get_path (cwd_dir);
cwd_dir_path_canonical = canonicalize_file_name (cwd_dir_path);
g_subprocess_launcher_set_cwd (launcher, cwd_dir_path_canonical);
}
else
g_subprocess_launcher_set_cwd (launcher, source_dir_path_canonical);
subp = g_subprocess_launcher_spawnv (launcher, (const gchar * const *) args->pdata, error);
g_ptr_array_free (args, TRUE);
if (subp == NULL ||
!g_subprocess_wait_check (subp, NULL, error))
return FALSE;
return TRUE;
}
gboolean
builder_module_build (BuilderModule *self,
BuilderContext *context,
GError **error)
{
GFile *app_dir = builder_context_get_app_dir (context);
g_autofree char *make_j = NULL;
g_autofree char *make_l = NULL;
g_autofree char *makefile_content = NULL;
g_autoptr(GFile) configure_file = NULL;
const char *makefile_names[] = {"Makefile", "makefile", "GNUmakefile", NULL};
g_autoptr(GFile) build_dir = NULL;
gboolean has_configure;
gboolean var_require_builddir;
gboolean has_notparallel;
gboolean use_builddir;
int i;
g_auto(GStrv) env = NULL;
g_auto(GStrv) build_args = NULL;
const char *cflags, *cxxflags;
g_autofree char *buildname = NULL;
g_autoptr(GFile) source_dir = NULL;
g_autoptr(GFile) source_dir_template = NULL;
g_autofree char *source_dir_path = NULL;
buildname = g_strdup_printf ("build-%s-XXXXXX", self->name);
source_dir_template = g_file_get_child (builder_context_get_state_dir (context),
buildname);
source_dir_path = g_file_get_path (source_dir_template);;
if (g_mkdtemp (source_dir_path) == NULL)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Can't create build directory");
return FALSE;
}
source_dir = g_file_new_for_path (source_dir_path);
g_print ("Building module %s in %s\n", self->name, source_dir_path);
if (!builder_module_extract_sources (self, source_dir, context, error))
return FALSE;
env = builder_options_get_env (self->build_options, context);
build_args = builder_options_get_build_args (self->build_options, context);
cflags = builder_options_get_cflags (self->build_options, context);
if (cflags)
env = g_environ_setenv (env, "CFLAGS", cflags, TRUE);
cxxflags = builder_options_get_cxxflags (self->build_options, context);
if (cxxflags)
env = g_environ_setenv (env, "CXXFLAGS", cxxflags, TRUE);
configure_file = g_file_get_child (source_dir, "configure");
if (self->rm_configure)
{
if (!g_file_delete (configure_file, NULL, error))
return FALSE;
}
has_configure = g_file_query_exists (configure_file, NULL);
if (!has_configure && !self->no_autogen)
{
const char *autogen_names[] = {"autogen", "autogen.sh", "bootstrap", NULL};
g_autofree char *autogen_cmd = NULL;
g_auto(GStrv) env_with_noconfigure = NULL;
for (i = 0; autogen_names[i] != NULL; i++)
{
g_autoptr(GFile) autogen_file = g_file_get_child (source_dir, autogen_names[i]);
if (g_file_query_exists (autogen_file, NULL))
{
autogen_cmd = g_strdup_printf ("./%s", autogen_names[i]);
break;
}
}
if (autogen_cmd == NULL)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Can't find autogen, autogen.sh or bootstrap");
return FALSE;
}
env_with_noconfigure = g_environ_setenv (g_strdupv (env), "NOCONFIGURE", "1", TRUE);
if (!build (app_dir, source_dir, NULL, build_args, env_with_noconfigure, error,
autogen_cmd, NULL))
return FALSE;
if (!g_file_query_exists (configure_file, NULL))
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "autogen did not create configure");
return FALSE;
}
has_configure = TRUE;
}
if (has_configure)
{
const char *configure_cmd;
g_autofree char *configure_content = NULL;
if (!g_file_load_contents (configure_file, NULL, &configure_content, NULL, NULL, error))
return FALSE;
var_require_builddir = strstr (configure_content, "buildapi-variable-require-builddir") != NULL;
use_builddir = var_require_builddir;
if (use_builddir)
{
build_dir = g_file_get_child (source_dir, "_build");
configure_cmd = "../configure";
}
else
{
build_dir = g_object_ref (source_dir);
configure_cmd = "./configure";
}
if (!build (app_dir, source_dir, build_dir, build_args, env, error,
configure_cmd, "--prefix=/app", strv_arg, self->config_opts, NULL))
return FALSE;
}
else
build_dir = g_object_ref (source_dir);
for (i = 0; makefile_names[i] != NULL; i++)
{
g_autoptr(GFile) makefile_file = g_file_get_child (build_dir, makefile_names[i]);
if (g_file_query_exists (makefile_file, NULL))
{
if (!g_file_load_contents (makefile_file, NULL, &makefile_content, NULL, NULL, error))
return FALSE;
break;
}
}
if (makefile_content == NULL)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Can't find makefile");
return FALSE;
}
has_notparallel =
g_str_has_prefix (makefile_content, ".NOTPARALLEL") ||
(strstr (makefile_content, "\n.NOTPARALLEL") != NULL);
if (!has_notparallel)
{
make_j = g_strdup_printf ("-j%d", builder_context_get_n_cpu (context));
make_l = g_strdup_printf ("-l%d", 2*builder_context_get_n_cpu (context));
}
if (!build (app_dir, source_dir, build_dir, build_args, env, error,
"make", "all", make_j?make_j:skip_arg, make_l?make_l:skip_arg, strv_arg, self->make_args, NULL))
return FALSE;
if (!build (app_dir, source_dir, build_dir, build_args, env, error,
"make", "install", strv_arg, self->make_install_args, NULL))
return FALSE;
if (!gs_shutil_rm_rf (source_dir, NULL, error))
return FALSE;
return TRUE;
}
void
builder_module_checksum (BuilderModule *self,
BuilderCache *cache,
BuilderContext *context)
{
GList *l;
builder_cache_checksum_str (cache, BUILDER_MODULE_CHECKSUM_VERSION);
builder_cache_checksum_str (cache, self->name);
builder_cache_checksum_strv (cache, self->config_opts);
builder_cache_checksum_strv (cache, self->make_args);
builder_cache_checksum_strv (cache, self->make_install_args);
builder_cache_checksum_boolean (cache, self->rm_configure);
builder_cache_checksum_boolean (cache, self->no_autogen);
for (l = self->sources; l != NULL; l = l->next)
{
BuilderSource *source = l->data;
builder_source_checksum (source, cache, context);
}
}
void
builder_module_checksum_for_cleanup (BuilderModule *self,
BuilderCache *cache,
BuilderContext *context)
{
builder_cache_checksum_str (cache, BUILDER_MODULE_CHECKSUM_VERSION);
builder_cache_checksum_str (cache, self->name);
builder_cache_checksum_strv (cache, self->cleanup);
}
GPtrArray *
builder_module_get_changes (BuilderModule *self)
{
return self->changes;
}
void
builder_module_set_changes (BuilderModule *self,
GPtrArray *changes)
{
if (self->changes != changes)
{
if (self->changes)
g_ptr_array_unref (self->changes);
self->changes = g_ptr_array_ref (changes);
}
}
static void
collect_for_pattern (BuilderModule *self,
const char *pattern,
const char *path,
GHashTable *to_remove_ht)
{
const char *rest;
const char *last_slash;
g_autofree char *dir = NULL;
if (pattern[0] == '/')
{
/* Absolute path match */
rest = path_prefix_match (pattern+1, path);
}
else
{
/* Basename match */
last_slash = strrchr (path, '/');
if (last_slash)
{
dir = g_strndup (path, last_slash - path);
path = last_slash + 1;
}
rest = path_prefix_match (pattern, path);
}
while (rest != NULL)
{
const char *slash;
g_autofree char *prefix = g_strndup (path, rest-path);
g_autofree char *to_remove = NULL;
if (dir)
to_remove = g_strconcat (dir, "/", prefix, NULL);
else
to_remove = g_strdup (prefix);
g_hash_table_insert (to_remove_ht, g_steal_pointer (&to_remove), GINT_TO_POINTER (1));
while (*rest == '/')
rest++;
if (*rest == 0)
break;
slash = strchr (rest, '/');
rest = slash ? slash : rest + strlen (rest);
}
}
void
builder_module_cleanup_collect (BuilderModule *self,
char **global_patterns,
GHashTable *to_remove_ht)
{
GPtrArray *changed_files;
int i, j;
changed_files = self->changes;
for (i = 0; i < changed_files->len; i++)
{
const char *path = g_ptr_array_index (changed_files, i);
if (global_patterns)
{
for (j = 0; global_patterns[j] != NULL; j++)
collect_for_pattern (self, global_patterns[j], path, to_remove_ht);
}
if (self->cleanup)
{
for (j = 0; self->cleanup[j] != NULL; j++)
collect_for_pattern (self, self->cleanup[j], path, to_remove_ht);
}
}
}

View File

@ -0,0 +1,74 @@
/*
* Copyright © 2015 Red Hat, Inc
*
* 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 2 of the License, or (at your option) any later version.
*
* This library 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 library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors:
* Alexander Larsson <alexl@redhat.com>
*/
#ifndef __BUILDER_MODULE_H__
#define __BUILDER_MODULE_H__
#include <json-glib/json-glib.h>
#include "builder-source.h"
#include "builder-options.h"
G_BEGIN_DECLS
typedef struct BuilderModule BuilderModule;
#define BUILDER_TYPE_MODULE (builder_module_get_type())
#define BUILDER_MODULE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), BUILDER_TYPE_MODULE, BuilderModule))
#define BUILDER_IS_MODULE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), BUILDER_TYPE_MODULE))
/* Bump this if format changes in incompatible ways to force rebuild */
#define BUILDER_MODULE_CHECKSUM_VERSION "1"
GType builder_module_get_type (void);
const char * builder_module_get_name (BuilderModule *self);
GList * builder_module_get_sources (BuilderModule *self);
GPtrArray * builder_module_get_changes (BuilderModule *self);
void builder_module_set_changes (BuilderModule *self,
GPtrArray *changes);
gboolean builder_module_download_sources (BuilderModule *self,
BuilderContext *context,
GError **error);
gboolean builder_module_extract_sources (BuilderModule *self,
GFile *dest,
BuilderContext *context,
GError **error);
gboolean builder_module_build (BuilderModule *self,
BuilderContext *context,
GError **error);
void builder_module_checksum (BuilderModule *self,
BuilderCache *cache,
BuilderContext *context);
void builder_module_checksum_for_cleanup (BuilderModule *self,
BuilderCache *cache,
BuilderContext *context);
void builder_module_cleanup_collect (BuilderModule *self,
char **global_patterns,
GHashTable *to_remove_ht);
G_DEFINE_AUTOPTR_CLEANUP_FUNC(BuilderModule, g_object_unref)
G_END_DECLS
#endif /* __BUILDER_MODULE_H__ */

View File

@ -0,0 +1,445 @@
/* builder-options.c
*
* Copyright (C) 2015 Red Hat, Inc
*
* This file 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 file 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 General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Authors:
* Alexander Larsson <alexl@redhat.com>
*/
#include "config.h"
#include <string.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/statfs.h>
#include "builder-options.h"
#include "builder-context.h"
#include "builder-utils.h"
struct BuilderOptions {
GObject parent;
char *cflags;
char *cxxflags;
char **env;
char **build_args;
GHashTable *arch;
};
typedef struct {
GObjectClass parent_class;
} BuilderOptionsClass;
static void serializable_iface_init (JsonSerializableIface *serializable_iface);
G_DEFINE_TYPE_WITH_CODE (BuilderOptions, builder_options, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (JSON_TYPE_SERIALIZABLE, serializable_iface_init));
enum {
PROP_0,
PROP_CFLAGS,
PROP_CXXFLAGS,
PROP_ENV,
PROP_ARCH,
PROP_BUILD_ARGS,
LAST_PROP
};
static void
builder_options_finalize (GObject *object)
{
BuilderOptions *self = (BuilderOptions *)object;
g_free (self->cflags);
g_free (self->cxxflags);
g_strfreev (self->env);
g_strfreev (self->build_args);
G_OBJECT_CLASS (builder_options_parent_class)->finalize (object);
}
static void
builder_options_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
BuilderOptions *self = BUILDER_OPTIONS (object);
switch (prop_id)
{
case PROP_CFLAGS:
g_value_set_string (value, self->cflags);
break;
case PROP_CXXFLAGS:
g_value_set_string (value, self->cxxflags);
break;
case PROP_ENV:
g_value_set_boxed (value, self->env);
break;
case PROP_ARCH:
g_value_set_boxed (value, self->arch);
break;
case PROP_BUILD_ARGS:
g_value_set_boxed (value, self->build_args);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
builder_options_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
BuilderOptions *self = BUILDER_OPTIONS (object);
gchar **tmp;
switch (prop_id)
{
case PROP_CFLAGS:
g_clear_pointer (&self->cflags, g_free);
self->cflags = g_value_dup_string (value);
break;
case PROP_CXXFLAGS:
g_clear_pointer (&self->cxxflags, g_free);
self->cxxflags = g_value_dup_string (value);
break;
case PROP_ENV:
tmp = self->env;
self->env = g_strdupv (g_value_get_boxed (value));
g_strfreev (tmp);
break;
case PROP_ARCH:
g_hash_table_destroy (self->arch);
/* NOTE: This takes ownership of the hash table! */
self->arch = g_value_dup_boxed (value);
break;
case PROP_BUILD_ARGS:
tmp = self->build_args;
self->build_args = g_strdupv (g_value_get_boxed (value));
g_strfreev (tmp);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
builder_options_class_init (BuilderOptionsClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = builder_options_finalize;
object_class->get_property = builder_options_get_property;
object_class->set_property = builder_options_set_property;
g_object_class_install_property (object_class,
PROP_CFLAGS,
g_param_spec_string ("cflags",
"",
"",
NULL,
G_PARAM_READWRITE));
g_object_class_install_property (object_class,
PROP_CXXFLAGS,
g_param_spec_string ("cxxflags",
"",
"",
NULL,
G_PARAM_READWRITE));
g_object_class_install_property (object_class,
PROP_ENV,
g_param_spec_boxed ("env",
"",
"",
G_TYPE_STRV,
G_PARAM_READWRITE));
g_object_class_install_property (object_class,
PROP_ARCH,
g_param_spec_boxed ("arch",
"",
"",
G_TYPE_HASH_TABLE,
G_PARAM_READWRITE));
g_object_class_install_property (object_class,
PROP_BUILD_ARGS,
g_param_spec_boxed ("build-args",
"",
"",
G_TYPE_STRV,
G_PARAM_READWRITE));
}
static void
builder_options_init (BuilderOptions *self)
{
self->arch = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
}
static gboolean
builder_options_deserialize_property (JsonSerializable *serializable,
const gchar *property_name,
GValue *value,
GParamSpec *pspec,
JsonNode *property_node)
{
if (strcmp (property_name, "arch") == 0)
{
if (JSON_NODE_TYPE (property_node) == JSON_NODE_NULL)
{
g_value_set_boxed (value, NULL);
return TRUE;
}
else if (JSON_NODE_TYPE (property_node) == JSON_NODE_OBJECT)
{
JsonObject *object = json_node_get_object (property_node);
g_autoptr(GPtrArray) env = g_ptr_array_new_with_free_func (g_free);
g_autoptr(GHashTable) hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
g_autoptr(GList) members = NULL;
GList *l;
members = json_object_get_members (object);
for (l = members; l != NULL; l = l->next)
{
const char *member_name = l->data;
JsonNode *val;
GObject *option;
val = json_object_get_member (object, member_name);
option = json_gobject_deserialize (BUILDER_TYPE_OPTIONS, val);
if (option == NULL)
return FALSE;
g_hash_table_insert (hash, g_strdup (member_name), option);
}
g_value_set_boxed (value, hash);
return TRUE;
}
return FALSE;
}
else if (strcmp (property_name, "env") == 0)
{
if (JSON_NODE_TYPE (property_node) == JSON_NODE_NULL)
{
g_value_set_boxed (value, NULL);
return TRUE;
}
else if (JSON_NODE_TYPE (property_node) == JSON_NODE_OBJECT)
{
JsonObject *object = json_node_get_object (property_node);
g_autoptr(GPtrArray) env = g_ptr_array_new_with_free_func (g_free);
g_autoptr(GList) members = NULL;
GList *l;
members = json_object_get_members (object);
for (l = members; l != NULL; l = l->next)
{
const char *member_name = l->data;
JsonNode *val;
const char *val_str;
val = json_object_get_member (object, member_name);
val_str = json_node_get_string (val);
if (val_str == NULL)
return FALSE;
g_ptr_array_add (env, g_strdup_printf ("%s=%s", member_name, val_str));
}
g_ptr_array_add (env, NULL);
g_value_set_boxed (value, g_ptr_array_free (g_steal_pointer (&env), FALSE));
return TRUE;
}
return FALSE;
}
else
return json_serializable_default_deserialize_property (serializable,
property_name,
value,
pspec, property_node);
}
static void
serializable_iface_init (JsonSerializableIface *serializable_iface)
{
serializable_iface->deserialize_property = builder_options_deserialize_property;
}
static GList *
get_arched_options (BuilderOptions *self, BuilderContext *context)
{
GList *options = NULL;
const char *arch = builder_context_get_arch (context);
BuilderOptions *arch_options;
arch_options = g_hash_table_lookup (self->arch, arch);
if (arch_options)
options = g_list_prepend (options, arch_options);
options = g_list_prepend (options, self);
return options;
}
static GList *
get_all_options (BuilderOptions *self, BuilderContext *context)
{
GList *options = NULL;
BuilderOptions *global_options = builder_context_get_options (context);
if (self)
options = get_arched_options (self, context);
if (global_options && global_options != self)
options = g_list_concat (options, get_arched_options (global_options, context));
return options;
}
const char *
builder_options_get_cflags (BuilderOptions *self, BuilderContext *context)
{
g_autoptr(GList) options = get_all_options (self, context);
GList *l;
for (l = options; l != NULL; l = l->next)
{
BuilderOptions *o = l->data;
if (o->cflags)
return o->cflags;
}
return NULL;
}
const char *
builder_options_get_cxxflags (BuilderOptions *self, BuilderContext *context)
{
g_autoptr(GList) options = get_all_options (self, context);
GList *l;
for (l = options; l != NULL; l = l->next)
{
BuilderOptions *o = l->data;
if (o->cxxflags)
return o->cxxflags;
}
return NULL;
}
char **
builder_options_get_env (BuilderOptions *self, BuilderContext *context)
{
g_autoptr(GList) options = get_all_options (self, context);
GList *l;
int i;
char **envp = NULL;
for (l = options; l != NULL; l = l->next)
{
BuilderOptions *o = l->data;
if (o->env)
{
for (i = 0; o->env[i] != NULL; i++)
{
const char *line = o->env[i];
const char *eq = strchr (line, '=');
const char *value = "";
g_autofree char *key = NULL;
if (eq)
{
key = g_strndup (line, eq - line);
value = eq + 1;
}
else
key = g_strdup (key);
envp = g_environ_setenv (envp, key, value, FALSE);
}
}
}
return envp;
}
char **
builder_options_get_build_args (BuilderOptions *self,
BuilderContext *context)
{
g_autoptr(GList) options = get_all_options (self, context);
GList *l;
int i;
g_autoptr(GPtrArray) array = g_ptr_array_new_with_free_func (g_free);
/* Last argument wins, so reverse the list for per-module to win */
options = g_list_reverse (options);
for (l = options; l != NULL; l = l->next)
{
BuilderOptions *o = l->data;
if (o->build_args)
{
for (i = 0; o->build_args[i] != NULL; i++)
g_ptr_array_add (array, g_strdup (o->build_args[i]));
}
}
g_ptr_array_add (array, NULL);
return (char **)g_ptr_array_free (g_steal_pointer (&array), FALSE);
}
void
builder_options_checksum (BuilderOptions *self,
BuilderCache *cache,
BuilderContext *context)
{
BuilderOptions *arch_options;
builder_cache_checksum_str (cache, BUILDER_OPTION_CHECKSUM_VERSION);
builder_cache_checksum_str (cache, self->cflags);
builder_cache_checksum_str (cache, self->cxxflags);
builder_cache_checksum_strv (cache, self->env);
builder_cache_checksum_strv (cache, self->build_args);
arch_options = g_hash_table_lookup (self->arch, builder_context_get_arch (context));
if (arch_options)
builder_options_checksum (arch_options, cache, context);
}

View File

@ -0,0 +1,57 @@
/*
* Copyright © 2015 Red Hat, Inc
*
* 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 2 of the License, or (at your option) any later version.
*
* This library 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 library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors:
* Alexander Larsson <alexl@redhat.com>
*/
#ifndef __BUILDER_OPTIONS_H__
#define __BUILDER_OPTIONS_H__
#include <json-glib/json-glib.h>
#include "builder-cache.h"
G_BEGIN_DECLS
typedef struct BuilderContext BuilderContext;
typedef struct BuilderOptions BuilderOptions;
#define BUILDER_TYPE_OPTIONS (builder_options_get_type())
#define BUILDER_OPTIONS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), BUILDER_TYPE_OPTIONS, BuilderOptions))
#define BUILDER_IS_OPTIONS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), BUILDER_TYPE_OPTIONS))
/* Bump this if format changes in incompatible ways to force rebuild */
#define BUILDER_OPTION_CHECKSUM_VERSION "1"
GType builder_options_get_type (void);
const char *builder_options_get_cflags (BuilderOptions *self,
BuilderContext *context);
const char *builder_options_get_cxxflags (BuilderOptions *self,
BuilderContext *context);
char ** builder_options_get_env (BuilderOptions *self,
BuilderContext *context);
char ** builder_options_get_build_args (BuilderOptions *self,
BuilderContext *context);
void builder_options_checksum (BuilderOptions *self,
BuilderCache *cache,
BuilderContext *context);
G_DEFINE_AUTOPTR_CLEANUP_FUNC(BuilderOptions, g_object_unref)
G_END_DECLS
#endif /* __BUILDER_OPTIONS_H__ */

View File

@ -0,0 +1,613 @@
/* builder-source-archive.c
*
* Copyright (C) 2015 Red Hat, Inc
*
* This file 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 file 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 General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Authors:
* Alexander Larsson <alexl@redhat.com>
*/
#include "config.h"
#include <string.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/statfs.h>
#include "xdg-app-utils.h"
#include "builder-utils.h"
#include "builder-source-archive.h"
struct BuilderSourceArchive {
BuilderSource parent;
char *url;
char *sha256;
guint strip_components;
};
typedef struct {
BuilderSourceClass parent_class;
} BuilderSourceArchiveClass;
G_DEFINE_TYPE (BuilderSourceArchive, builder_source_archive, BUILDER_TYPE_SOURCE);
enum {
PROP_0,
PROP_URL,
PROP_SHA256,
PROP_STRIP_COMPONENTS,
LAST_PROP
};
typedef enum {
UNKNOWN,
TAR,
TAR_GZIP,
TAR_COMPRESS,
TAR_BZIP2,
TAR_LZIP,
TAR_LZMA,
TAR_LZOP,
TAR_XZ,
ZIP
} BuilderArchiveType;
gboolean
is_tar (BuilderArchiveType type)
{
return (type >= TAR) && (type <= TAR_XZ);
}
const char *
tar_decompress_flag (BuilderArchiveType type)
{
switch (type)
{
default:
case TAR:
return NULL;
case TAR_GZIP:
return "-z";
case TAR_COMPRESS:
return "-Z";
case TAR_BZIP2:
return "-j";
case TAR_LZIP:
return "--lzip";
case TAR_LZMA:
return "--lzma";
case TAR_LZOP:
return "--lzop";
case TAR_XZ:
return "-J";
}
}
static void
builder_source_archive_finalize (GObject *object)
{
BuilderSourceArchive *self = (BuilderSourceArchive *)object;
g_free (self->url);
g_free (self->sha256);
G_OBJECT_CLASS (builder_source_archive_parent_class)->finalize (object);
}
static void
builder_source_archive_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
BuilderSourceArchive *self = BUILDER_SOURCE_ARCHIVE (object);
switch (prop_id)
{
case PROP_URL:
g_value_set_string (value, self->url);
break;
case PROP_SHA256:
g_value_set_string (value, self->sha256);
break;
case PROP_STRIP_COMPONENTS:
g_value_set_uint (value, self->strip_components);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
builder_source_archive_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
BuilderSourceArchive *self = BUILDER_SOURCE_ARCHIVE (object);
switch (prop_id)
{
case PROP_URL:
g_free (self->url);
self->url = g_value_dup_string (value);
break;
case PROP_SHA256:
g_free (self->sha256);
self->sha256 = g_value_dup_string (value);
break;
case PROP_STRIP_COMPONENTS:
self->strip_components = g_value_get_uint (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static SoupURI *
get_uri (BuilderSourceArchive *self,
GError **error)
{
SoupURI *uri;
if (self->url == NULL)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "URL not specified");
return NULL;
}
uri = soup_uri_new (self->url);
if (uri == NULL)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Invalid URL '%s'", self->url);
return NULL;
}
return uri;
}
static GFile *
get_download_location (BuilderSourceArchive *self,
BuilderContext *context,
GError **error)
{
g_autoptr(SoupURI) uri = NULL;
const char *path;
g_autofree char *base_name = NULL;
GFile *download_dir = NULL;
g_autoptr(GFile) sha256_dir = NULL;
g_autoptr(GFile) file = NULL;
uri = get_uri (self, error);
if (uri == NULL)
return FALSE;
path = soup_uri_get_path (uri);
base_name = g_path_get_basename (path);
if (self->sha256 == NULL || *self->sha256 == 0)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Sha256 not specified");
return FALSE;
}
download_dir = builder_context_get_download_dir (context);
sha256_dir = g_file_get_child (download_dir, self->sha256);
file = g_file_get_child (sha256_dir, base_name);
return g_steal_pointer (&file);
}
static gboolean
builder_source_archive_download (BuilderSource *source,
BuilderContext *context,
GError **error)
{
BuilderSourceArchive *self = BUILDER_SOURCE_ARCHIVE (source);
g_autoptr (GFile) file = NULL;
g_autoptr (GFile) dir = NULL;
g_autoptr(SoupURI) uri = NULL;
SoupSession *session;
g_autofree char *url = NULL;
g_autofree char *dir_path = NULL;
g_autofree char *sha256 = NULL;
g_autofree char *base_name = NULL;
g_autoptr(SoupMessage) msg = NULL;
file = get_download_location (self, context, error);
if (file == NULL)
return FALSE;
if (g_file_query_exists (file, NULL))
return TRUE;
base_name = g_file_get_basename (file);
uri = get_uri (self, error);
if (uri == NULL)
return FALSE;
url = g_strdup (self->url);
session = builder_context_get_soup_session (context);
while (TRUE)
{
g_clear_object (&msg);
msg = soup_message_new ("GET", url);
g_debug ("GET %s", self->url);
g_print ("Downloading %s...", base_name);
soup_session_send_message (session, msg);
g_print ("done\n");
g_debug ("response: %d %s", msg->status_code, msg->reason_phrase);
if (SOUP_STATUS_IS_REDIRECTION (msg->status_code))
{
const char *header = soup_message_headers_get_one (msg->response_headers, "Location");
if (header)
{
g_autoptr(SoupURI) new_uri = soup_uri_new_with_base (soup_message_get_uri (msg), header);
g_free (url);
url = soup_uri_to_string (uri, FALSE);
g_debug (" -> %s", header);
continue;
}
}
else if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code))
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Failed to download %s (error %d): %s", base_name, msg->status_code, msg->reason_phrase);
return FALSE;
}
break; /* No redirection */
}
sha256 = g_compute_checksum_for_string (G_CHECKSUM_SHA256,
msg->response_body->data,
msg->response_body->length);
if (strcmp (sha256, self->sha256) != 0)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Wrong sha256 for %s, expected %s, was %s", base_name, self->sha256, sha256);
return FALSE;
}
dir = g_file_get_parent (file);
dir_path = g_file_get_path (dir);
g_mkdir_with_parents (dir_path, 0755);
if (!g_file_replace_contents (file,
msg->response_body->data,
msg->response_body->length,
NULL, FALSE, G_FILE_CREATE_NONE, NULL,
NULL, error))
return FALSE;
return TRUE;
}
static gboolean
tar (GFile *dir,
GError **error,
const gchar *argv1,
...)
{
g_autoptr(GSubprocessLauncher) launcher = NULL;
g_autoptr(GSubprocess) subp = NULL;
GPtrArray *args;
const gchar *arg;
va_list ap;
args = g_ptr_array_new ();
g_ptr_array_add (args, "tar");
va_start (ap, argv1);
g_ptr_array_add (args, (gchar *) argv1);
while ((arg = va_arg (ap, const gchar *)))
g_ptr_array_add (args, (gchar *) arg);
g_ptr_array_add (args, NULL);
va_end (ap);
launcher = g_subprocess_launcher_new (0);
if (dir)
{
g_autofree char *path = g_file_get_path (dir);
g_subprocess_launcher_set_cwd (launcher, path);
}
subp = g_subprocess_launcher_spawnv (launcher, (const gchar * const *) args->pdata, error);
g_ptr_array_free (args, TRUE);
if (subp == NULL ||
!g_subprocess_wait_check (subp, NULL, error))
return FALSE;
return TRUE;
}
static gboolean
unzip (GFile *dir,
GError **error,
const gchar *argv1,
...)
{
g_autoptr(GSubprocessLauncher) launcher = NULL;
g_autoptr(GSubprocess) subp = NULL;
GPtrArray *args;
const gchar *arg;
va_list ap;
args = g_ptr_array_new ();
g_ptr_array_add (args, "unzip");
va_start (ap, argv1);
g_ptr_array_add (args, (gchar *) argv1);
while ((arg = va_arg (ap, const gchar *)))
g_ptr_array_add (args, (gchar *) arg);
g_ptr_array_add (args, NULL);
va_end (ap);
launcher = g_subprocess_launcher_new (0);
if (dir)
{
g_autofree char *path = g_file_get_path (dir);
g_subprocess_launcher_set_cwd (launcher, path);
}
subp = g_subprocess_launcher_spawnv (launcher, (const gchar * const *) args->pdata, error);
g_ptr_array_free (args, TRUE);
if (subp == NULL ||
!g_subprocess_wait_check (subp, NULL, error))
return FALSE;
return TRUE;
}
BuilderArchiveType
get_type (GFile *archivefile)
{
g_autofree char *base_name = NULL;
g_autofree gchar *lower = NULL;
base_name = g_file_get_basename (archivefile);
lower = g_ascii_strdown (base_name, -1);
if (g_str_has_suffix (lower, ".tar"))
return TAR;
if (g_str_has_suffix (lower, ".tar.gz") ||
g_str_has_suffix (lower, ".tgz") ||
g_str_has_suffix (lower, ".taz"))
return TAR_GZIP;
if (g_str_has_suffix (lower, ".tar.Z") ||
g_str_has_suffix (lower, ".taZ"))
return TAR_COMPRESS;
if (g_str_has_suffix (lower, ".tar.bz2") ||
g_str_has_suffix (lower, ".tz2") ||
g_str_has_suffix (lower, ".tbz2") ||
g_str_has_suffix (lower, ".tbz"))
return TAR_BZIP2;
if (g_str_has_suffix (lower, ".tar.lz"))
return TAR_LZIP;
if (g_str_has_suffix (lower, ".tar.lzma") ||
g_str_has_suffix (lower, ".tlz"))
return TAR_LZMA;
if (g_str_has_suffix (lower, ".tar.lzo"))
return TAR_LZOP;
if (g_str_has_suffix (lower, ".tar.xz"))
return TAR_XZ;
if (g_str_has_suffix (lower, ".zip"))
return ZIP;
return UNKNOWN;
}
static gboolean
strip_components_into (GFile *dest,
GFile *src,
int level,
GError **error)
{
g_autoptr(GFileEnumerator) dir_enum = NULL;
g_autoptr(GFileInfo) child_info = NULL;
GError *temp_error = NULL;
dir_enum = g_file_enumerate_children (src, "standard::name,standard::type",
G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
NULL, error);
if (!dir_enum)
return FALSE;;
while ((child_info = g_file_enumerator_next_file (dir_enum, NULL, &temp_error)))
{
g_autoptr(GFile) child = NULL;
g_autoptr(GFile) dest_child = NULL;
g_autoptr(GFileEnumerator) dir_enum2 = NULL;
g_autoptr(GFileInfo) child_info2 = NULL;
child = g_file_get_child (src, g_file_info_get_name (child_info));
if (g_file_info_get_file_type (child_info) == G_FILE_TYPE_DIRECTORY &&
level > 0)
{
if (!strip_components_into (dest, child, level - 1, error))
return FALSE;
g_clear_object (&child_info);
continue;
}
dest_child = g_file_get_child (dest, g_file_info_get_name (child_info));
if (!g_file_move (child, dest_child, G_FILE_COPY_NONE, NULL, NULL, NULL, error))
return FALSE;
g_clear_object (&child_info);
continue;
}
if (temp_error != NULL)
{
g_propagate_error (error, temp_error);
return FALSE;
}
if (!g_file_delete (src, NULL, error))
return FALSE;
return TRUE;
}
static gboolean
builder_source_archive_extract (BuilderSource *source,
GFile *dest,
BuilderContext *context,
GError **error)
{
BuilderSourceArchive *self = BUILDER_SOURCE_ARCHIVE (source);
g_autoptr(GFile) archivefile = NULL;
g_autofree char *archive_path = NULL;
BuilderArchiveType type;
archivefile = get_download_location (self, context, error);
if (archivefile == NULL)
return FALSE;
type = get_type (archivefile);
archive_path = g_file_get_path (archivefile);
if (is_tar (type))
{
g_autofree char *strip_components = g_strdup_printf ("--strip-components=%u", self->strip_components);
/* Note: tar_decompress_flag can return NULL, so put it last */
if (!tar (dest, error, "xf", archive_path, strip_components, tar_decompress_flag (type), NULL))
return FALSE;
}
else if (type == ZIP)
{
g_autoptr(GFile) zip_dest = NULL;
if (self->strip_components > 0)
{
g_autoptr(GFile) tmp_dir_template = g_file_get_child (dest, ".uncompressXXXXXX");
g_autofree char *tmp_dir_path = g_file_get_path (tmp_dir_template);;
if (g_mkdtemp (tmp_dir_path) == NULL)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Can't create uncompress directory");
return FALSE;
}
zip_dest = g_file_new_for_path (tmp_dir_path);
}
else
zip_dest = g_object_ref (dest);
if (!unzip (zip_dest, error, archive_path, NULL))
return FALSE;
if (self->strip_components > 0)
{
if (!strip_components_into (dest, zip_dest, self->strip_components, error))
return FALSE;
}
}
else
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Unknown archive format of '%s'", archive_path);
return FALSE;
}
return TRUE;
}
static void
builder_source_archive_checksum (BuilderSource *source,
BuilderCache *cache,
BuilderContext *context)
{
BuilderSourceArchive *self = BUILDER_SOURCE_ARCHIVE (source);
builder_cache_checksum_str (cache, self->url);
builder_cache_checksum_str (cache, self->sha256);
builder_cache_checksum_uint32 (cache, self->strip_components);
}
static void
builder_source_archive_class_init (BuilderSourceArchiveClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
BuilderSourceClass *source_class = BUILDER_SOURCE_CLASS (klass);
object_class->finalize = builder_source_archive_finalize;
object_class->get_property = builder_source_archive_get_property;
object_class->set_property = builder_source_archive_set_property;
source_class->download = builder_source_archive_download;
source_class->extract = builder_source_archive_extract;
source_class->checksum = builder_source_archive_checksum;
g_object_class_install_property (object_class,
PROP_URL,
g_param_spec_string ("url",
"",
"",
NULL,
G_PARAM_READWRITE));
g_object_class_install_property (object_class,
PROP_SHA256,
g_param_spec_string ("sha256",
"",
"",
NULL,
G_PARAM_READWRITE));
g_object_class_install_property (object_class,
PROP_STRIP_COMPONENTS,
g_param_spec_uint ("strip-components",
"",
"",
0, G_MAXUINT,
1,
G_PARAM_READWRITE));
}
static void
builder_source_archive_init (BuilderSourceArchive *self)
{
self->strip_components = 1;
}

View File

@ -0,0 +1,40 @@
/*
* Copyright © 2015 Red Hat, Inc
*
* 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 2 of the License, or (at your option) any later version.
*
* This library 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 library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors:
* Alexander Larsson <alexl@redhat.com>
*/
#ifndef __BUILDER_SOURCE_ARCHIVE_H__
#define __BUILDER_SOURCE_ARCHIVE_H__
#include "builder-source.h"
G_BEGIN_DECLS
typedef struct BuilderSourceArchive BuilderSourceArchive;
#define BUILDER_TYPE_SOURCE_ARCHIVE (builder_source_archive_get_type())
#define BUILDER_SOURCE_ARCHIVE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), BUILDER_TYPE_SOURCE_ARCHIVE, BuilderSourceArchive))
#define BUILDER_IS_SOURCE_ARCHIVE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), BUILDER_TYPE_SOURCE_ARCHIVE))
GType builder_source_archive_get_type (void);
G_DEFINE_AUTOPTR_CLEANUP_FUNC(BuilderSourceArchive, g_object_unref)
G_END_DECLS
#endif /* __BUILDER_SOURCE_ARCHIVE_H__ */

View File

@ -0,0 +1,373 @@
/* builder-source-bzr.c
*
* Copyright (C) 2015 Red Hat, Inc
*
* This file 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 file 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 General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Authors:
* Alexander Larsson <alexl@redhat.com>
*/
#include "config.h"
#include <string.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/statfs.h>
#include "builder-utils.h"
#include "builder-source-bzr.h"
#include "builder-utils.h"
struct BuilderSourceBzr {
BuilderSource parent;
char *url;
};
typedef struct {
BuilderSourceClass parent_class;
} BuilderSourceBzrClass;
G_DEFINE_TYPE (BuilderSourceBzr, builder_source_bzr, BUILDER_TYPE_SOURCE);
enum {
PROP_0,
PROP_URL,
LAST_PROP
};
static void
builder_source_bzr_finalize (GObject *object)
{
BuilderSourceBzr *self = (BuilderSourceBzr *)object;
g_free (self->url);
G_OBJECT_CLASS (builder_source_bzr_parent_class)->finalize (object);
}
static void
builder_source_bzr_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
BuilderSourceBzr *self = BUILDER_SOURCE_BZR (object);
switch (prop_id)
{
case PROP_URL:
g_value_set_string (value, self->url);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
builder_source_bzr_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
BuilderSourceBzr *self = BUILDER_SOURCE_BZR (object);
switch (prop_id)
{
case PROP_URL:
g_free (self->url);
self->url = g_value_dup_string (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
typedef struct
{
GError *error;
GError *splice_error;
GMainLoop *loop;
int refs;
} BzrData;
static gboolean bzr (GFile *dir,
char **output,
GError **error,
const gchar *argv1,
...) G_GNUC_NULL_TERMINATED;
static void
bzr_data_exit (BzrData *data)
{
data->refs--;
if (data->refs == 0)
g_main_loop_quit (data->loop);
}
static void
bzr_output_spliced_cb (GObject *obj,
GAsyncResult *result,
gpointer user_data)
{
BzrData *data = user_data;
g_output_stream_splice_finish (G_OUTPUT_STREAM (obj), result, &data->splice_error);
bzr_data_exit (data);
}
static void
bzr_exit_cb (GObject *obj,
GAsyncResult *result,
gpointer user_data)
{
BzrData *data = user_data;
g_subprocess_wait_check_finish (G_SUBPROCESS (obj), result, &data->error);
bzr_data_exit (data);
}
static gboolean
bzr (GFile *dir,
char **output,
GError **error,
const gchar *argv1,
...)
{
g_autoptr(GSubprocessLauncher) launcher = NULL;
g_autoptr(GSubprocess) subp = NULL;
GPtrArray *args;
const gchar *arg;
GInputStream *in;
g_autoptr(GOutputStream) out = NULL;
g_autoptr(GMainLoop) loop = NULL;
va_list ap;
BzrData data = {0};
args = g_ptr_array_new ();
g_ptr_array_add (args, "bzr");
va_start (ap, argv1);
g_ptr_array_add (args, (gchar *) argv1);
while ((arg = va_arg (ap, const gchar *)))
g_ptr_array_add (args, (gchar *) arg);
g_ptr_array_add (args, NULL);
va_end (ap);
launcher = g_subprocess_launcher_new (0);
if (output)
g_subprocess_launcher_set_flags (launcher, G_SUBPROCESS_FLAGS_STDOUT_PIPE);
if (dir)
{
g_autofree char *path = g_file_get_path (dir);
g_subprocess_launcher_set_cwd (launcher, path);
}
subp = g_subprocess_launcher_spawnv (launcher, (const gchar * const *) args->pdata, error);
g_ptr_array_free (args, TRUE);
if (subp == NULL)
return FALSE;
loop = g_main_loop_new (NULL, FALSE);
data.loop = loop;
data.refs = 1;
if (output)
{
data.refs++;
in = g_subprocess_get_stdout_pipe (subp);
out = g_memory_output_stream_new_resizable ();
g_output_stream_splice_async (out,
in,
G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET,
0,
NULL,
bzr_output_spliced_cb,
&data);
}
g_subprocess_wait_async (subp, NULL, bzr_exit_cb, &data);
g_main_loop_run (loop);
if (data.error)
{
g_propagate_error (error, data.error);
g_clear_error (&data.splice_error);
return FALSE;
}
if (out)
{
if (data.splice_error)
{
g_propagate_error (error, data.splice_error);
return FALSE;
}
/* Null terminate */
g_output_stream_write (out, "\0", 1, NULL, NULL);
*output = g_memory_output_stream_steal_data (G_MEMORY_OUTPUT_STREAM (out));
}
return TRUE;
}
static GFile *
get_mirror_dir (BuilderSourceBzr *self, BuilderContext *context)
{
g_autoptr(GFile) bzr_dir = NULL;
g_autofree char *filename = NULL;
g_autofree char *bzr_dir_path = NULL;
bzr_dir = g_file_get_child (builder_context_get_state_dir (context),
"bzr");
bzr_dir_path = g_file_get_path (bzr_dir);
g_mkdir_with_parents (bzr_dir_path, 0755);
filename = builder_uri_to_filename (self->url);
return g_file_get_child (bzr_dir, filename);
}
static char *
get_current_commit (BuilderSourceBzr *self, BuilderContext *context, GError **error)
{
g_autoptr(GFile) mirror_dir = NULL;
char *output = NULL;
mirror_dir = get_mirror_dir (self, context);
if (!bzr (mirror_dir, &output, error,
"revno", NULL))
return NULL;
return output;
}
static gboolean
builder_source_bzr_download (BuilderSource *source,
BuilderContext *context,
GError **error)
{
BuilderSourceBzr *self = BUILDER_SOURCE_BZR (source);
g_autoptr(GFile) mirror_dir = NULL;
if (self->url == NULL)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "URL not specified");
return NULL;
}
mirror_dir = get_mirror_dir (self, context);
if (!g_file_query_exists (mirror_dir, NULL))
{
g_autofree char *filename = g_file_get_basename (mirror_dir);
g_autoptr(GFile) parent = g_file_get_parent (mirror_dir);
g_autofree char *filename_tmp = g_strconcat ("./", filename, ".clone_tmp", NULL);
g_autoptr(GFile) mirror_dir_tmp = g_file_get_child (parent, filename_tmp);
if (!bzr (parent, NULL, error,
"branch", self->url, filename_tmp, NULL) ||
!g_file_move (mirror_dir_tmp, mirror_dir, 0, NULL, NULL, NULL, error))
return FALSE;
}
else
{
if (!bzr (mirror_dir, NULL, error,
"pull", NULL))
return FALSE;
}
return TRUE;
}
static gboolean
builder_source_bzr_extract (BuilderSource *source,
GFile *dest,
BuilderContext *context,
GError **error)
{
BuilderSourceBzr *self = BUILDER_SOURCE_BZR (source);
g_autoptr(GFile) mirror_dir = NULL;
g_autofree char *mirror_dir_path = NULL;
g_autofree char *dest_path = NULL;
mirror_dir = get_mirror_dir (self, context);
mirror_dir_path = g_file_get_path (mirror_dir);
dest_path = g_file_get_path (dest);
if (!bzr (NULL, NULL, error,
"branch", "--stacked", mirror_dir_path, dest_path, "--use-existing-dir", NULL))
return FALSE;
return TRUE;
}
static void
builder_source_bzr_checksum (BuilderSource *source,
BuilderCache *cache,
BuilderContext *context)
{
BuilderSourceBzr *self = BUILDER_SOURCE_BZR (source);
g_autofree char *current_commit;
g_autoptr(GError) error = NULL;
builder_cache_checksum_str (cache, self->url);
current_commit = get_current_commit (self, context, &error);
if (current_commit)
builder_cache_checksum_str (cache, current_commit);
else if (error)
g_warning ("Failed to get current bzr checksum: %s", error->message);
}
static void
builder_source_bzr_class_init (BuilderSourceBzrClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
BuilderSourceClass *source_class = BUILDER_SOURCE_CLASS (klass);
object_class->finalize = builder_source_bzr_finalize;
object_class->get_property = builder_source_bzr_get_property;
object_class->set_property = builder_source_bzr_set_property;
source_class->download = builder_source_bzr_download;
source_class->extract = builder_source_bzr_extract;
source_class->checksum = builder_source_bzr_checksum;
g_object_class_install_property (object_class,
PROP_URL,
g_param_spec_string ("url",
"",
"",
NULL,
G_PARAM_READWRITE));
}
static void
builder_source_bzr_init (BuilderSourceBzr *self)
{
}

View File

@ -0,0 +1,40 @@
/*
* Copyright © 2015 Red Hat, Inc
*
* 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 2 of the License, or (at your option) any later version.
*
* This library 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 library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors:
* Alexander Larsson <alexl@redhat.com>
*/
#ifndef __BUILDER_SOURCE_BZR_H__
#define __BUILDER_SOURCE_BZR_H__
#include "builder-source.h"
G_BEGIN_DECLS
typedef struct BuilderSourceBzr BuilderSourceBzr;
#define BUILDER_TYPE_SOURCE_BZR (builder_source_bzr_get_type())
#define BUILDER_SOURCE_BZR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), BUILDER_TYPE_SOURCE_BZR, BuilderSourceBzr))
#define BUILDER_IS_SOURCE_BZR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), BUILDER_TYPE_SOURCE_BZR))
GType builder_source_bzr_get_type (void);
G_DEFINE_AUTOPTR_CLEANUP_FUNC(BuilderSourceBzr, g_object_unref)
G_END_DECLS
#endif /* __BUILDER_SOURCE_BZR_H__ */

View File

@ -0,0 +1,235 @@
/* builder-source-file.c
*
* Copyright (C) 2015 Red Hat, Inc
*
* This file 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 file 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 General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Authors:
* Alexander Larsson <alexl@redhat.com>
*/
#include "config.h"
#include <string.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/statfs.h>
#include "builder-utils.h"
#include "builder-source-file.h"
struct BuilderSourceFile {
BuilderSource parent;
char *path;
char *dest_filename;
};
typedef struct {
BuilderSourceClass parent_class;
} BuilderSourceFileClass;
G_DEFINE_TYPE (BuilderSourceFile, builder_source_file, BUILDER_TYPE_SOURCE);
enum {
PROP_0,
PROP_PATH,
PROP_DEST_FILENAME,
LAST_PROP
};
static void
builder_source_file_finalize (GObject *object)
{
BuilderSourceFile *self = (BuilderSourceFile *)object;
g_free (self->path);
g_free (self->dest_filename);
G_OBJECT_CLASS (builder_source_file_parent_class)->finalize (object);
}
static void
builder_source_file_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
BuilderSourceFile *self = BUILDER_SOURCE_FILE (object);
switch (prop_id)
{
case PROP_PATH:
g_value_set_string (value, self->path);
break;
case PROP_DEST_FILENAME:
g_value_set_string (value, self->dest_filename);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
builder_source_file_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
BuilderSourceFile *self = BUILDER_SOURCE_FILE (object);
switch (prop_id)
{
case PROP_PATH:
g_free (self->path);
self->path = g_value_dup_string (value);
break;
case PROP_DEST_FILENAME:
g_free (self->dest_filename);
self->dest_filename = g_value_dup_string (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static GFile *
get_source_file (BuilderSourceFile *self,
BuilderContext *context,
GError **error)
{
GFile *base_dir = builder_context_get_base_dir (context);
if (self->path == NULL || self->path[0] == 0)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "path not specified");
return NULL;
}
return g_file_resolve_relative_path (base_dir, self->path);
}
static gboolean
builder_source_file_download (BuilderSource *source,
BuilderContext *context,
GError **error)
{
BuilderSourceFile *self = BUILDER_SOURCE_FILE (source);
g_autoptr(GFile) src = NULL;
src = get_source_file (self, context, error);
if (src == NULL)
return FALSE;
if (!g_file_query_exists (src, NULL))
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Can't find file at %s", self->path);
return FALSE;
}
return TRUE;
}
static gboolean
builder_source_file_extract (BuilderSource *source,
GFile *dest,
BuilderContext *context,
GError **error)
{
BuilderSourceFile *self = BUILDER_SOURCE_FILE (source);
g_autoptr(GFile) src = NULL;
g_autoptr(GFile) dest_file = NULL;
g_autofree char *dest_filename = NULL;
src = get_source_file (self, context, error);
if (src == NULL)
return FALSE;
if (self->dest_filename)
dest_filename = g_strdup (self->dest_filename);
else
dest_filename = g_file_get_basename (src);
dest_file = g_file_get_child (dest, dest_filename);
if (!g_file_copy (src, dest_file,
G_FILE_COPY_OVERWRITE,
NULL,
NULL, NULL,
error))
return FALSE;
return TRUE;
}
static void
builder_source_file_checksum (BuilderSource *source,
BuilderCache *cache,
BuilderContext *context)
{
BuilderSourceFile *self = BUILDER_SOURCE_FILE (source);
g_autoptr(GFile) src = NULL;
g_autofree char *data = NULL;
gsize len;
src = get_source_file (self, context, NULL);
if (src == NULL)
return;
if (g_file_load_contents (src, NULL, &data, &len, NULL, NULL))
builder_cache_checksum_data (cache, (guchar *)data, len);
builder_cache_checksum_str (cache, self->path);
builder_cache_checksum_str (cache, self->dest_filename);
}
static void
builder_source_file_class_init (BuilderSourceFileClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
BuilderSourceClass *source_class = BUILDER_SOURCE_CLASS (klass);
object_class->finalize = builder_source_file_finalize;
object_class->get_property = builder_source_file_get_property;
object_class->set_property = builder_source_file_set_property;
source_class->download = builder_source_file_download;
source_class->extract = builder_source_file_extract;
source_class->checksum = builder_source_file_checksum;
g_object_class_install_property (object_class,
PROP_PATH,
g_param_spec_string ("path",
"",
"",
NULL,
G_PARAM_READWRITE));
g_object_class_install_property (object_class,
PROP_DEST_FILENAME,
g_param_spec_string ("dest-filename",
"",
"",
NULL,
G_PARAM_READWRITE));
}
static void
builder_source_file_init (BuilderSourceFile *self)
{
}

View File

@ -0,0 +1,40 @@
/*
* Copyright © 2015 Red Hat, Inc
*
* 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 2 of the License, or (at your option) any later version.
*
* This library 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 library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors:
* Alexander Larsson <alexl@redhat.com>
*/
#ifndef __BUILDER_SOURCE_FILE_H__
#define __BUILDER_SOURCE_FILE_H__
#include "builder-source.h"
G_BEGIN_DECLS
typedef struct BuilderSourceFile BuilderSourceFile;
#define BUILDER_TYPE_SOURCE_FILE (builder_source_file_get_type())
#define BUILDER_SOURCE_FILE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), BUILDER_TYPE_SOURCE_FILE, BuilderSourceFile))
#define BUILDER_IS_SOURCE_FILE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), BUILDER_TYPE_SOURCE_FILE))
GType builder_source_file_get_type (void);
G_DEFINE_AUTOPTR_CLEANUP_FUNC(BuilderSourceFile, g_object_unref)
G_END_DECLS
#endif /* __BUILDER_SOURCE_FILE_H__ */

View File

@ -0,0 +1,402 @@
/* builder-source-git.c
*
* Copyright (C) 2015 Red Hat, Inc
*
* This file 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 file 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 General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Authors:
* Alexander Larsson <alexl@redhat.com>
*/
#include "config.h"
#include <string.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/statfs.h>
#include "builder-utils.h"
#include "builder-source-git.h"
#include "builder-utils.h"
struct BuilderSourceGit {
BuilderSource parent;
char *url;
char *branch;
};
typedef struct {
BuilderSourceClass parent_class;
} BuilderSourceGitClass;
G_DEFINE_TYPE (BuilderSourceGit, builder_source_git, BUILDER_TYPE_SOURCE);
enum {
PROP_0,
PROP_URL,
PROP_BRANCH,
LAST_PROP
};
static void
builder_source_git_finalize (GObject *object)
{
BuilderSourceGit *self = (BuilderSourceGit *)object;
g_free (self->url);
g_free (self->branch);
G_OBJECT_CLASS (builder_source_git_parent_class)->finalize (object);
}
static void
builder_source_git_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
BuilderSourceGit *self = BUILDER_SOURCE_GIT (object);
switch (prop_id)
{
case PROP_URL:
g_value_set_string (value, self->url);
break;
case PROP_BRANCH:
g_value_set_string (value, self->branch);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
builder_source_git_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
BuilderSourceGit *self = BUILDER_SOURCE_GIT (object);
switch (prop_id)
{
case PROP_URL:
g_free (self->url);
self->url = g_value_dup_string (value);
break;
case PROP_BRANCH:
g_free (self->branch);
self->branch = g_value_dup_string (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
typedef struct
{
GError *error;
GError *splice_error;
GMainLoop *loop;
int refs;
} GitData;
static gboolean git (GFile *dir,
char **output,
GError **error,
const gchar *argv1,
...) G_GNUC_NULL_TERMINATED;
static void
git_data_exit (GitData *data)
{
data->refs--;
if (data->refs == 0)
g_main_loop_quit (data->loop);
}
static void
git_output_spliced_cb (GObject *obj,
GAsyncResult *result,
gpointer user_data)
{
GitData *data = user_data;
g_output_stream_splice_finish (G_OUTPUT_STREAM (obj), result, &data->splice_error);
git_data_exit (data);
}
static void
git_exit_cb (GObject *obj,
GAsyncResult *result,
gpointer user_data)
{
GitData *data = user_data;
g_subprocess_wait_check_finish (G_SUBPROCESS (obj), result, &data->error);
git_data_exit (data);
}
static gboolean
git (GFile *dir,
char **output,
GError **error,
const gchar *argv1,
...)
{
g_autoptr(GSubprocessLauncher) launcher = NULL;
g_autoptr(GSubprocess) subp = NULL;
GPtrArray *args;
const gchar *arg;
GInputStream *in;
g_autoptr(GOutputStream) out = NULL;
g_autoptr(GMainLoop) loop = NULL;
va_list ap;
GitData data = {0};
args = g_ptr_array_new ();
g_ptr_array_add (args, "git");
va_start (ap, argv1);
g_ptr_array_add (args, (gchar *) argv1);
while ((arg = va_arg (ap, const gchar *)))
g_ptr_array_add (args, (gchar *) arg);
g_ptr_array_add (args, NULL);
va_end (ap);
launcher = g_subprocess_launcher_new (0);
if (output)
g_subprocess_launcher_set_flags (launcher, G_SUBPROCESS_FLAGS_STDOUT_PIPE);
if (dir)
{
g_autofree char *path = g_file_get_path (dir);
g_subprocess_launcher_set_cwd (launcher, path);
}
subp = g_subprocess_launcher_spawnv (launcher, (const gchar * const *) args->pdata, error);
g_ptr_array_free (args, TRUE);
if (subp == NULL)
return FALSE;
loop = g_main_loop_new (NULL, FALSE);
data.loop = loop;
data.refs = 1;
if (output)
{
data.refs++;
in = g_subprocess_get_stdout_pipe (subp);
out = g_memory_output_stream_new_resizable ();
g_output_stream_splice_async (out,
in,
G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET,
0,
NULL,
git_output_spliced_cb,
&data);
}
g_subprocess_wait_async (subp, NULL, git_exit_cb, &data);
g_main_loop_run (loop);
if (data.error)
{
g_propagate_error (error, data.error);
g_clear_error (&data.splice_error);
return FALSE;
}
if (out)
{
if (data.splice_error)
{
g_propagate_error (error, data.splice_error);
return FALSE;
}
/* Null terminate */
g_output_stream_write (out, "\0", 1, NULL, NULL);
*output = g_memory_output_stream_steal_data (G_MEMORY_OUTPUT_STREAM (out));
}
return TRUE;
}
static GFile *
get_mirror_dir (BuilderSourceGit *self, BuilderContext *context)
{
g_autoptr(GFile) git_dir = NULL;
g_autofree char *filename = NULL;
g_autofree char *git_dir_path = NULL;
git_dir = g_file_get_child (builder_context_get_state_dir (context),
"git");
git_dir_path = g_file_get_path (git_dir);
g_mkdir_with_parents (git_dir_path, 0755);
filename = builder_uri_to_filename (self->url);
return g_file_get_child (git_dir, filename);
}
static const char *
get_branch (BuilderSourceGit *self)
{
if (self->branch)
return self->branch;
else
return "master";
}
static char *
get_current_commit (BuilderSourceGit *self, BuilderContext *context, GError **error)
{
g_autoptr(GFile) mirror_dir = NULL;
char *output = NULL;
mirror_dir = get_mirror_dir (self, context);
if (!git (mirror_dir, &output, error,
"rev-parse", get_branch (self), NULL))
return NULL;
return output;
}
static gboolean
builder_source_git_download (BuilderSource *source,
BuilderContext *context,
GError **error)
{
BuilderSourceGit *self = BUILDER_SOURCE_GIT (source);
g_autoptr(GFile) mirror_dir = NULL;
if (self->url == NULL)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "URL not specified");
return NULL;
}
mirror_dir = get_mirror_dir (self, context);
if (!g_file_query_exists (mirror_dir, NULL))
{
g_autofree char *filename = g_file_get_basename (mirror_dir);
g_autoptr(GFile) parent = g_file_get_parent (mirror_dir);
g_autofree char *filename_tmp = g_strconcat (filename, ".clone_tmp", NULL);
g_autoptr(GFile) mirror_dir_tmp = g_file_get_child (parent, filename_tmp);
if (!git (parent, NULL, error,
"clone", "--mirror", self->url, filename_tmp, NULL) ||
!g_file_move (mirror_dir_tmp, mirror_dir, 0, NULL, NULL, NULL, error))
return FALSE;
}
else
{
if (!git (mirror_dir, NULL, error,
"fetch", NULL))
return FALSE;
}
return TRUE;
}
static gboolean
builder_source_git_extract (BuilderSource *source,
GFile *dest,
BuilderContext *context,
GError **error)
{
BuilderSourceGit *self = BUILDER_SOURCE_GIT (source);
g_autoptr(GFile) mirror_dir = NULL;
g_autofree char *mirror_dir_path = NULL;
g_autofree char *dest_path = NULL;
mirror_dir = get_mirror_dir (self, context);
mirror_dir_path = g_file_get_path (mirror_dir);
dest_path = g_file_get_path (dest);
if (!git (NULL, NULL, error,
"clone", "--branch", get_branch (self), "--recursive", mirror_dir_path, dest_path, NULL))
return FALSE;
return TRUE;
}
static void
builder_source_git_checksum (BuilderSource *source,
BuilderCache *cache,
BuilderContext *context)
{
BuilderSourceGit *self = BUILDER_SOURCE_GIT (source);
g_autofree char *current_commit;
g_autoptr(GError) error = NULL;
builder_cache_checksum_str (cache, self->url);
builder_cache_checksum_str (cache, self->branch);
current_commit = get_current_commit (self, context, &error);
if (current_commit)
builder_cache_checksum_str (cache, current_commit);
else if (error)
g_warning ("Failed to get current git checksum: %s", error->message);
}
static void
builder_source_git_class_init (BuilderSourceGitClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
BuilderSourceClass *source_class = BUILDER_SOURCE_CLASS (klass);
object_class->finalize = builder_source_git_finalize;
object_class->get_property = builder_source_git_get_property;
object_class->set_property = builder_source_git_set_property;
source_class->download = builder_source_git_download;
source_class->extract = builder_source_git_extract;
source_class->checksum = builder_source_git_checksum;
g_object_class_install_property (object_class,
PROP_URL,
g_param_spec_string ("url",
"",
"",
NULL,
G_PARAM_READWRITE));
g_object_class_install_property (object_class,
PROP_BRANCH,
g_param_spec_string ("branch",
"",
"",
NULL,
G_PARAM_READWRITE));
}
static void
builder_source_git_init (BuilderSourceGit *self)
{
}

View File

@ -0,0 +1,40 @@
/*
* Copyright © 2015 Red Hat, Inc
*
* 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 2 of the License, or (at your option) any later version.
*
* This library 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 library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors:
* Alexander Larsson <alexl@redhat.com>
*/
#ifndef __BUILDER_SOURCE_GIT_H__
#define __BUILDER_SOURCE_GIT_H__
#include "builder-source.h"
G_BEGIN_DECLS
typedef struct BuilderSourceGit BuilderSourceGit;
#define BUILDER_TYPE_SOURCE_GIT (builder_source_git_get_type())
#define BUILDER_SOURCE_GIT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), BUILDER_TYPE_SOURCE_GIT, BuilderSourceGit))
#define BUILDER_IS_SOURCE_GIT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), BUILDER_TYPE_SOURCE_GIT))
GType builder_source_git_get_type (void);
G_DEFINE_AUTOPTR_CLEANUP_FUNC(BuilderSourceGit, g_object_unref)
G_END_DECLS
#endif /* __BUILDER_SOURCE_GIT_H__ */

View File

@ -0,0 +1,265 @@
/* builder-source-patch.c
*
* Copyright (C) 2015 Red Hat, Inc
*
* This file 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 file 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 General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Authors:
* Alexander Larsson <alexl@redhat.com>
*/
#include "config.h"
#include <string.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/statfs.h>
#include "xdg-app-utils.h"
#include "builder-utils.h"
#include "builder-source-patch.h"
struct BuilderSourcePatch {
BuilderSource parent;
char *path;
guint strip_components;
};
typedef struct {
BuilderSourceClass parent_class;
} BuilderSourcePatchClass;
G_DEFINE_TYPE (BuilderSourcePatch, builder_source_patch, BUILDER_TYPE_SOURCE);
enum {
PROP_0,
PROP_PATH,
PROP_STRIP_COMPONENTS,
LAST_PROP
};
static void
builder_source_patch_finalize (GObject *object)
{
BuilderSourcePatch *self = (BuilderSourcePatch *)object;
g_free (self->path);
G_OBJECT_CLASS (builder_source_patch_parent_class)->finalize (object);
}
static void
builder_source_patch_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
BuilderSourcePatch *self = BUILDER_SOURCE_PATCH (object);
switch (prop_id)
{
case PROP_PATH:
g_value_set_string (value, self->path);
break;
case PROP_STRIP_COMPONENTS:
g_value_set_uint (value, self->strip_components);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
builder_source_patch_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
BuilderSourcePatch *self = BUILDER_SOURCE_PATCH (object);
switch (prop_id)
{
case PROP_PATH:
g_free (self->path);
self->path = g_value_dup_string (value);
break;
case PROP_STRIP_COMPONENTS:
self->strip_components = g_value_get_uint (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static GFile *
get_source_file (BuilderSourcePatch *self,
BuilderContext *context,
GError **error)
{
if (self->path == NULL || self->path[0] == 0)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "path not specified");
return NULL;
}
return g_file_resolve_relative_path (builder_context_get_base_dir (context), self->path);
}
static gboolean
builder_source_patch_download (BuilderSource *source,
BuilderContext *context,
GError **error)
{
BuilderSourcePatch *self = BUILDER_SOURCE_PATCH (source);
g_autoptr(GFile) src = NULL;
src = get_source_file (self, context, error);
if (src == NULL)
return FALSE;
if (!g_file_query_exists (src, NULL))
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Can't find file at %s", self->path);
return FALSE;
}
return TRUE;
}
static gboolean
patch (GFile *dir,
GError **error,
const gchar *argv1,
...)
{
g_autoptr(GSubprocessLauncher) launcher = NULL;
g_autoptr(GSubprocess) subp = NULL;
GPtrArray *args;
const gchar *arg;
va_list ap;
args = g_ptr_array_new ();
g_ptr_array_add (args, "patch");
va_start (ap, argv1);
g_ptr_array_add (args, (gchar *) argv1);
while ((arg = va_arg (ap, const gchar *)))
g_ptr_array_add (args, (gchar *) arg);
g_ptr_array_add (args, NULL);
va_end (ap);
launcher = g_subprocess_launcher_new (0);
if (dir)
{
g_autofree char *path = g_file_get_path (dir);
g_subprocess_launcher_set_cwd (launcher, path);
}
subp = g_subprocess_launcher_spawnv (launcher, (const gchar * const *) args->pdata, error);
g_ptr_array_free (args, TRUE);
if (subp == NULL ||
!g_subprocess_wait_check (subp, NULL, error))
return FALSE;
return TRUE;
}
static gboolean
builder_source_patch_extract (BuilderSource *source,
GFile *dest,
BuilderContext *context,
GError **error)
{
BuilderSourcePatch *self = BUILDER_SOURCE_PATCH (source);
g_autoptr(GFile) patchfile = NULL;
g_autofree char *patch_path = NULL;
g_autofree char *strip_components = NULL;
patchfile = get_source_file (self, context, error);
if (patchfile == NULL)
return FALSE;
strip_components = g_strdup_printf ("-p%u", self->strip_components);
patch_path = g_file_get_path (patchfile);
if (!patch (dest, error, strip_components, "-i", patch_path, NULL))
return FALSE;
return TRUE;
}
static void
builder_source_patch_checksum (BuilderSource *source,
BuilderCache *cache,
BuilderContext *context)
{
BuilderSourcePatch *self = BUILDER_SOURCE_PATCH (source);
g_autoptr(GFile) src = NULL;
g_autofree char *data = NULL;
gsize len;
src = get_source_file (self, context, NULL);
if (src == NULL)
return;
if (g_file_load_contents (src, NULL, &data, &len, NULL, NULL))
builder_cache_checksum_data (cache, (guchar *)data, len);
builder_cache_checksum_str (cache, self->path);
builder_cache_checksum_uint32 (cache, self->strip_components);
}
static void
builder_source_patch_class_init (BuilderSourcePatchClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
BuilderSourceClass *source_class = BUILDER_SOURCE_CLASS (klass);
object_class->finalize = builder_source_patch_finalize;
object_class->get_property = builder_source_patch_get_property;
object_class->set_property = builder_source_patch_set_property;
source_class->download = builder_source_patch_download;
source_class->extract = builder_source_patch_extract;
source_class->checksum = builder_source_patch_checksum;
g_object_class_install_property (object_class,
PROP_PATH,
g_param_spec_string ("path",
"",
"",
NULL,
G_PARAM_READWRITE));
g_object_class_install_property (object_class,
PROP_STRIP_COMPONENTS,
g_param_spec_uint ("strip-components",
"",
"",
0, G_MAXUINT,
1,
G_PARAM_READWRITE));
}
static void
builder_source_patch_init (BuilderSourcePatch *self)
{
self->strip_components = 1;
}

View File

@ -0,0 +1,40 @@
/*
* Copyright © 2015 Red Hat, Inc
*
* 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 2 of the License, or (at your option) any later version.
*
* This library 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 library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors:
* Alexander Larsson <alexl@redhat.com>
*/
#ifndef __BUILDER_SOURCE_PATCH_H__
#define __BUILDER_SOURCE_PATCH_H__
#include "builder-source.h"
G_BEGIN_DECLS
typedef struct BuilderSourcePatch BuilderSourcePatch;
#define BUILDER_TYPE_SOURCE_PATCH (builder_source_patch_get_type())
#define BUILDER_SOURCE_PATCH(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), BUILDER_TYPE_SOURCE_PATCH, BuilderSourcePatch))
#define BUILDER_IS_SOURCE_PATCH(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), BUILDER_TYPE_SOURCE_PATCH))
GType builder_source_patch_get_type (void);
G_DEFINE_AUTOPTR_CLEANUP_FUNC(BuilderSourcePatch, g_object_unref)
G_END_DECLS
#endif /* __BUILDER_SOURCE_PATCH_H__ */

View File

@ -0,0 +1,227 @@
/* builder-source.c
*
* Copyright (C) 2015 Red Hat, Inc
*
* This file 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 file 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 General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Authors:
* Alexander Larsson <alexl@redhat.com>
*/
#include "config.h"
#include <string.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/statfs.h>
#include "builder-utils.h"
#include "builder-source.h"
#include "builder-source-archive.h"
#include "builder-source-patch.h"
#include "builder-source-git.h"
#include "builder-source-bzr.h"
#include "builder-source-file.h"
static void serializable_iface_init (JsonSerializableIface *serializable_iface);
G_DEFINE_TYPE_WITH_CODE (BuilderSource, builder_source, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (JSON_TYPE_SERIALIZABLE, serializable_iface_init));
enum {
PROP_0,
PROP_DEST,
LAST_PROP
};
static void
builder_source_finalize (GObject *object)
{
BuilderSource *self = BUILDER_SOURCE (object);
g_free (self->dest);
G_OBJECT_CLASS (builder_source_parent_class)->finalize (object);
}
static void
builder_source_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
BuilderSource *self = BUILDER_SOURCE (object);
switch (prop_id)
{
case PROP_DEST:
g_value_set_string (value, self->dest);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
builder_source_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
BuilderSource *self = BUILDER_SOURCE (object);
switch (prop_id)
{
case PROP_DEST:
g_free (self->dest);
self->dest = g_value_dup_string (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static gboolean
builder_source_real_download (BuilderSource *self,
BuilderContext *context,
GError **error)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Download not implemented for type %s", g_type_name_from_instance ((GTypeInstance *)self));
return FALSE;
}
static gboolean
builder_source_real_extract (BuilderSource *self,
GFile *dest,
BuilderContext *context,
GError **error)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Extract not implemented for type %s", g_type_name_from_instance ((GTypeInstance *)self));
return FALSE;
}
static void
builder_source_class_init (BuilderSourceClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = builder_source_finalize;
object_class->get_property = builder_source_get_property;
object_class->set_property = builder_source_set_property;
klass->download = builder_source_real_download;
klass->extract = builder_source_real_extract;
g_object_class_install_property (object_class,
PROP_DEST,
g_param_spec_string ("dest",
"",
"",
NULL,
G_PARAM_READWRITE));
}
static void
builder_source_init (BuilderSource *self)
{
}
static void
serializable_iface_init (JsonSerializableIface *serializable_iface)
{
}
BuilderSource *
builder_source_from_json (JsonNode *node)
{
JsonObject *object = json_node_get_object (node);
const gchar *type;
type = json_object_get_string_member (object, "type");
if (strcmp (type, "archive") == 0)
return (BuilderSource *)json_gobject_deserialize (BUILDER_TYPE_SOURCE_ARCHIVE, node);
if (strcmp (type, "file") == 0)
return (BuilderSource *)json_gobject_deserialize (BUILDER_TYPE_SOURCE_FILE, node);
if (strcmp (type, "patch") == 0)
return (BuilderSource *)json_gobject_deserialize (BUILDER_TYPE_SOURCE_PATCH, node);
if (strcmp (type, "git") == 0)
return (BuilderSource *)json_gobject_deserialize (BUILDER_TYPE_SOURCE_GIT, node);
if (strcmp (type, "bzr") == 0)
return (BuilderSource *)json_gobject_deserialize (BUILDER_TYPE_SOURCE_BZR, node);
else if (type == NULL)
g_warning ("Missing source type");
else
g_warning ("Unknown source type %s", type);
return NULL;
}
gboolean
builder_source_download (BuilderSource *self,
BuilderContext *context,
GError **error)
{
BuilderSourceClass *class;
class = BUILDER_SOURCE_GET_CLASS (self);
return class->download (self, context, error);
}
gboolean
builder_source_extract (BuilderSource *self,
GFile *dest,
BuilderContext *context,
GError **error)
{
BuilderSourceClass *class;
g_autoptr(GFile) real_dest;
class = BUILDER_SOURCE_GET_CLASS (self);
if (self->dest != NULL)
{
real_dest = g_file_resolve_relative_path (dest, self->dest);
if (!g_file_query_exists (real_dest, NULL) &&
!g_file_make_directory_with_parents (real_dest, NULL, error))
return FALSE;
}
else
real_dest = g_object_ref (dest);
return class->extract (self, real_dest, context, error);
}
void
builder_source_checksum (BuilderSource *self,
BuilderCache *cache,
BuilderContext *context)
{
BuilderSourceClass *class;
class = BUILDER_SOURCE_GET_CLASS (self);
builder_cache_checksum_str (cache, self->dest);
class->checksum (self, cache, context);
}

View File

@ -0,0 +1,81 @@
/*
* Copyright © 2015 Red Hat, Inc
*
* 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 2 of the License, or (at your option) any later version.
*
* This library 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 library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors:
* Alexander Larsson <alexl@redhat.com>
*/
#ifndef __BUILDER_SOURCE_H__
#define __BUILDER_SOURCE_H__
#include <json-glib/json-glib.h>
#include "builder-context.h"
#include "builder-cache.h"
G_BEGIN_DECLS
typedef struct BuilderSource BuilderSource;
#define BUILDER_TYPE_SOURCE (builder_source_get_type())
#define BUILDER_SOURCE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), BUILDER_TYPE_SOURCE, BuilderSource))
#define BUILDER_SOURCE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), BUILDER_TYPE_SOURCE, BuilderSourceClass))
#define BUILDER_IS_SOURCE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), BUILDER_TYPE_SOURCE))
#define BUILDER_IS_SOURCE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), BUILDER_TYPE_SOURCE))
#define BUILDER_SOURCE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), BUILDER_TYPE_SOURCE, BuilderSourceClass))
struct BuilderSource {
GObject parent;
char *dest;
};
typedef struct {
GObjectClass parent_class;
gboolean (* download) (BuilderSource *self,
BuilderContext *context,
GError **error);
gboolean (* extract) (BuilderSource *self,
GFile *dest,
BuilderContext *context,
GError **error);
void (* checksum) (BuilderSource *self,
BuilderCache *cache,
BuilderContext *context);
} BuilderSourceClass;
GType builder_source_get_type (void);
BuilderSource * builder_source_from_json (JsonNode *node);
gboolean builder_source_download (BuilderSource *self,
BuilderContext *context,
GError **error);
gboolean builder_source_extract (BuilderSource *self,
GFile *dest,
BuilderContext *context,
GError **error);
void builder_source_checksum (BuilderSource *self,
BuilderCache *cache,
BuilderContext *context);
G_DEFINE_AUTOPTR_CLEANUP_FUNC(BuilderSource, g_object_unref)
G_END_DECLS
#endif /* __BUILDER_SOURCE_H__ */

View File

@ -0,0 +1,119 @@
/* builder-utils.c
*
* Copyright (C) 2015 Red Hat, Inc
*
* This file 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 file 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 General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Authors:
* Alexander Larsson <alexl@redhat.com>
*/
#include "config.h"
#include <string.h>
#include "builder-utils.h"
char *
builder_uri_to_filename (const char *uri)
{
GString *s;
const char *p;
gboolean saw_slash = FALSE;
gboolean saw_after_slash = FALSE;
s = g_string_new ("");
for (p = uri; *p != 0; p++)
{
if (*p == '/')
{
saw_slash = TRUE;
if (saw_after_slash) /* Skip first slashes */
g_string_append_c (s, '_');
continue;
}
else if (saw_slash)
saw_after_slash = TRUE;
g_string_append_c (s, *p);
}
return g_string_free (s, FALSE);
}
/* Returns end of matching path prefix, or NULL if no match */
const char *
path_prefix_match (const char *pattern,
const char *string)
{
char c, test;
const char *tmp;
while (TRUE)
{
switch (c = *pattern++)
{
case 0:
if (*string == '/' || *string == 0)
return string;
return NULL;
case '?':
if (*string == '/' || *string == 0)
return NULL;
string++;
break;
case '*':
c = *pattern;
while (c == '*')
c = *++pattern;
/* special case * at end */
if (c == 0)
{
char *tmp = strchr (string, '/');
if (tmp != NULL)
return tmp;
return string + strlen (string);
}
else if (c == '/')
{
string = strchr (string, '/');
if (string == NULL)
return NULL;
break;
}
while ((test = *string) != 0)
{
tmp = path_prefix_match (pattern, string);
if (tmp != NULL)
return tmp;
if (test == '/')
break;
string++;
}
return NULL;
default:
if (c != *string)
return NULL;
string++;
break;
}
}
return NULL; /* Should not be reached */
}

View File

@ -0,0 +1,38 @@
/*
* Copyright © 2015 Red Hat, Inc
*
* 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 2 of the License, or (at your option) any later version.
*
* This library 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 library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors:
* Alexander Larsson <alexl@redhat.com>
*/
#ifndef __BUILDER_UTILS_H__
#define __BUILDER_UTILS_H__
#include <gio/gio.h>
#include <libsoup/soup.h>
G_BEGIN_DECLS
typedef struct BuilderUtils BuilderUtils;
char *builder_uri_to_filename (const char *uri);
const char *path_prefix_match (const char *pattern,
const char *string);
G_END_DECLS
#endif /* __BUILDER_UTILS_H__ */

View File

@ -0,0 +1,228 @@
/*
* Copyright © 2015 Red Hat, Inc
*
* 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 2 of the License, or (at your option) any later version.
*
* This library 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 library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors:
* Alexander Larsson <alexl@redhat.com>
*/
#include "config.h"
#include <locale.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <gio/gio.h>
#include "libglnx/libglnx.h"
#include "libgsystem.h"
#include "builder-manifest.h"
static gboolean opt_verbose;
static gboolean opt_version;
static gboolean opt_disable_cache;
static gboolean opt_download_only;
static gboolean opt_disable_download;
static gboolean opt_require_changes;
static GOptionEntry entries[] = {
{ "verbose", 'v', 0, G_OPTION_ARG_NONE, &opt_verbose, "Print debug information during command processing", NULL },
{ "version", 0, 0, G_OPTION_ARG_NONE, &opt_version, "Print version information and exit", NULL },
{ "disable-cache", 0, 0, G_OPTION_ARG_NONE, &opt_disable_cache, "Disable cache", NULL },
{ "disable-download", 0, 0, G_OPTION_ARG_NONE, &opt_disable_download, "Don't download any new sources", NULL },
{ "download-only", 0, 0, G_OPTION_ARG_NONE, &opt_download_only, "Only download sources, don't build", NULL },
{ "require-changes", 0, 0, G_OPTION_ARG_NONE, &opt_require_changes, "Don't create app dir if no changes", NULL },
{ NULL }
};
static void
message_handler (const gchar *log_domain,
GLogLevelFlags log_level,
const gchar *message,
gpointer user_data)
{
/* Make this look like normal console output */
if (log_level & G_LOG_LEVEL_DEBUG)
g_printerr ("XAB: %s\n", message);
else
g_printerr ("%s: %s\n", g_get_prgname (), message);
}
int
usage (GOptionContext *context, const char *message)
{
g_autofree gchar *help = g_option_context_get_help (context, TRUE, NULL);
g_printerr ("%s\n", message);
g_printerr ("%s", help);
return 1;
}
int
main (int argc,
char **argv)
{
g_autofree const char *old_env = NULL;
g_autoptr(GError) error = NULL;
g_autoptr(BuilderManifest) manifest = NULL;
GOptionContext *context;
const char *app_dir_path, *manifest_path;
g_autofree gchar *json = NULL;
g_autoptr(BuilderContext) build_context = NULL;
g_autoptr(GFile) base_dir = NULL;
g_autoptr(GFile) app_dir = NULL;
g_autoptr(BuilderCache) cache = NULL;
g_autofree char *cache_branch = NULL;
setlocale (LC_ALL, "");
g_log_set_handler (NULL, G_LOG_LEVEL_MESSAGE, message_handler, NULL);
g_set_prgname (argv[0]);
/* avoid gvfs (http://bugzilla.gnome.org/show_bug.cgi?id=526454) */
old_env = g_strdup (g_getenv ("GIO_USE_VFS"));
g_setenv ("GIO_USE_VFS", "local", TRUE);
g_vfs_get_default ();
if (old_env)
g_setenv ("GIO_USE_VFS", old_env, TRUE);
else
g_unsetenv ("GIO_USE_VFS");
context = g_option_context_new ("DIRECTORY MANIFEST - Build manifest");
g_option_context_add_main_entries (context, entries, NULL);
if (!g_option_context_parse (context, &argc, &argv, &error))
{
g_printerr ("option parsing failed: %s\n", error->message);
return 1;
}
if (opt_version)
{
g_print ("%s\n", PACKAGE_STRING);
exit (EXIT_SUCCESS);
}
if (opt_verbose)
g_log_set_handler (NULL, G_LOG_LEVEL_DEBUG, message_handler, NULL);
if (argc == 1)
return usage (context, "DIRECTORY must be specified");
app_dir_path = argv[1];
if (argc == 2)
return usage (context, "MANIFEST must be specified");
manifest_path = argv[2];
if (!g_file_get_contents (manifest_path, &json, NULL, &error))
{
g_printerr ("Can't load '%s': %s\n", manifest_path, error->message);
return 1;
}
manifest = (BuilderManifest *)json_gobject_from_data (BUILDER_TYPE_MANIFEST,
json, -1, &error);
if (manifest == NULL)
{
g_printerr ("Can't parse '%s': %s\n", manifest_path, error->message);
return 1;
}
base_dir = g_file_new_for_path (g_get_current_dir ());
app_dir = g_file_new_for_path (app_dir_path);
if (!gs_shutil_rm_rf (app_dir, NULL, &error))
{
g_print ("error removing old app dir '%s': %s\n", app_dir_path, error->message);
return 1;
}
build_context = builder_context_new (base_dir, app_dir);
if (!opt_disable_download)
{
if (!builder_manifest_download (manifest, build_context, &error))
{
g_print ("error: %s\n", error->message);
return 1;
}
}
if (opt_download_only)
return 0;
cache_branch = g_path_get_basename (manifest_path);
cache = builder_cache_new (builder_context_get_cache_dir (build_context), app_dir, cache_branch);
if (!builder_cache_open (cache, &error))
{
g_print ("Error opening cache: %s\n", error->message);
return 1;
}
if (opt_disable_cache) /* This disables *lookups*, but we still build the cache */
builder_cache_disable_lookups (cache);
builder_manifest_checksum (manifest, cache, build_context);
if (!builder_cache_lookup (cache))
{
g_autofree char *body =
g_strdup_printf ("Initialized %s\n",
builder_manifest_get_app_id (manifest));
if (!builder_manifest_init_app_dir (manifest, build_context, &error))
{
g_print ("error: %s\n", error->message);
return 1;
}
if (!builder_cache_commit (cache, body, &error))
{
g_print ("error: %s\n", error->message);
return 1;
}
}
if (!builder_manifest_build (manifest, cache, build_context, &error))
{
g_print ("error: %s\n", error->message);
return 1;
}
if (!builder_manifest_cleanup (manifest, cache, build_context, &error))
{
g_print ("error: %s\n", error->message);
return 1;
}
if (!builder_manifest_finish (manifest, cache, build_context, &error))
{
g_print ("error: %s\n", error->message);
return 1;
}
if (!opt_require_changes)
builder_cache_ensure_checkout (cache);
if (!builder_gc (cache, &error))
{
g_warning ("Failed to GC build cache: %s\n", error->message);
g_clear_error (&error);
}
return 0;
}

View File

@ -75,6 +75,10 @@ PKG_CHECK_MODULES(FUSE, [fuse])
AC_SUBST(FUSE_CFLAGS)
AC_SUBST(FUSE_LIBS)
PKG_CHECK_MODULES(JSON, [json-glib-1.0])
AC_SUBST(JSON_CFLAGS)
AC_SUBST(JSON_LIBS)
AC_ARG_ENABLE([seccomp],
AC_HELP_STRING([--disable-seccomp],
[Disable seccomp]),

View File

@ -37,6 +37,7 @@ man_MANS = \
xdg-app-build-finish.1 \
xdg-app-build-export.1 \
xdg-app-repo-update.1 \
xdg-app-builder.1 \
$(NULL)
xml_files = $(man_MANS:.1=.xml)

View File

@ -0,0 +1,512 @@
<?xml version='1.0'?> <!--*-nxml-*-->
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
<refentry id="xdg-app-builder">
<refentryinfo>
<title>xdg-app builder</title>
<productname>xdg-app</productname>
<authorgroup>
<author>
<contrib>Developer</contrib>
<firstname>Alexander</firstname>
<surname>Larsson</surname>
<email>alexl@redhat.com</email>
</author>
</authorgroup>
</refentryinfo>
<refmeta>
<refentrytitle>xdg-app-builder</refentrytitle>
<manvolnum>1</manvolnum>
</refmeta>
<refnamediv>
<refname>xdg-app-builder</refname>
<refpurpose>Help build application dependencies</refpurpose>
</refnamediv>
<refsynopsisdiv>
<cmdsynopsis>
<command>xdg-app-builder</command>
<arg choice="opt" rep="repeat">OPTION</arg>
<arg choice="plain">DIRECTORY</arg>
<arg choice="plain">MANIFEST</arg>
</cmdsynopsis>
</refsynopsisdiv>
<refsect1>
<title>Description</title>
<para>
<command>xdg-app-builder</command> is a wrapper around the <command>xdg-app build</command> command
that automates the building of applications and their dependencies. It is one option you can use
to build applications.
</para>
<para>
The goal of <command>xdg-app-builder</command> is to push as much knowledge about how to build modules to
the individual upstream projects. It does this by assuming that the modules adhere to the Build API specified
at https://github.com/cgwalters/build-api. This essentially means that it follows the <command>./configure
&amp;&amp; make &amp;&amp; make install</command> scheme with an optional autogen script. If the upstream
does not adhere to the API you can make it do so by adding patches and extra files.
</para>
<para>
A invocation of <command>xdg-app-builder</command> proceeds in these stages, each being specified
in detail in json format in <arg choice="plain">MANIFEST</arg>:
<itemizedlist mark='bullet'>
<listitem>
<para>Download all sources</para>
</listitem>
<listitem>
<para>Initialize the application directory with <command>xdg-app build-init</command></para>
</listitem>
<listitem>
<para>Build and install each module with <command>xdg-app build</command></para>
</listitem>
<listitem>
<para>Clean up the final build tree by removing unwanted files and e.g. stripping binaries</para>
</listitem>
<listitem>
<para>Finish the application directory with <command>xdg-app build-finish</command></para>
</listitem>
</itemizedlist>
After this you will end up with a build of the application in <arg choice="plain">DIRECTORY</arg>, which you can
export to a repository with the <command>xdg-app build-export</command> command.
</para>
<para>
At each of the above steps xdg-app caches the result, and if you build the same file again, it will start
of at the first step where something changes. For instance the first version controlled source that had
new commits added, or the first module where some changes to the <arg choice="plain">MANIFEST</arg> file caused
the build environment to change. This makes xdg-app-builder very efficient for incremental builds.
</para>
</refsect1>
<refsect1>
<title>Manifest format</title>
<refsect2>
<title>Toplevel</title>
<para>
The top level of the json file describes global
attributes of the application, and how it can be
build, and the list of modules that need to be
built.
</para>
<para>
These are the properties that are accepted:
</para>
<variablelist>
<varlistentry>
<term><option>app-id</option></term>
<listitem><para>A string defining the application id.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>version</option></term>
<listitem><para>The version of the application, defaults to master.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>runtime</option></term>
<listitem><para>The name of the runtime that the application uses.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>runtime-version</option></term>
<listitem><para>The version of theruntime that the application uses, defaults to master.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>sdk</option></term>
<listitem><para>The name of the development runtime that the application builds with.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>command</option></term>
<listitem><para>The filename or path to the main binary of the application.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>build-options</option></term>
<listitem><para>object specifying the build environment. See below for details.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>modules</option></term>
<listitem><para>An array of object specifying the modules to be built in order.
See below for details.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>cleanup</option></term>
<listitem><para>An array of file patterns that should be removed at the end.
Patterns starting with / are taken to be full pathnames, otherwise they just match
the basename.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>finish-args</option></term>
<listitem><para>An array of arguments passed to the <command>xdg-app build-finish</command> command</para></listitem>
</varlistentry>
<varlistentry>
<term><option>strip</option></term>
<listitem><para>If this is true (the default) then all ELF files will be stripped during the cleanup phase.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>rename-desktop-file</option></term>
<listitem><para>Any desktop file with this name will be renamed to a name based on app-id during the cleanup phase.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>rename-icon</option></term>
<listitem><para>Any icon with this name will be renamed to a name based on app-id during the cleanup phase.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>desktop-file-name-prefix</option></term>
<listitem><para>This string will be prefixed to the Name key in the main application desktop file.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>desktop-file-name-suffixed</option></term>
<listitem><para>This string will be suffixed to the Name key in the main application desktop file.</para></listitem>
</varlistentry>
</variablelist>
</refsect2>
<refsect2>
<title>Build Options</title>
<para>
Build options specify the build environment of a module, and can be specified globally as
well as per-module. Options can also be specified on a per-architecture basis using the arch property.
</para>
<para>
These are the properties that are accepted:
</para>
<variablelist>
<varlistentry>
<term><option>cflags</option></term>
<listitem><para>This is set in the environment variable CFLAGS during the build.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>cxxflags</option></term>
<listitem><para>This is set in the environment variable CXXFLAGS during the build.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>env</option></term>
<listitem><para>This is a dictionary defining environment variables to be set during the build.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>arch</option></term>
<listitem><para>This is a dictionary defining for each arch a separate build options object that override the main one</para></listitem>
</varlistentry>
</variablelist>
</refsect2>
<refsect2>
<title>Module</title>
<para>
Each module specifies a source that has to be separately built and installed. It contains
the build options and a list of sources to download and extract before building.
</para>
<para>
These are the properties that are accepted:
</para>
<variablelist>
<varlistentry>
<term><option>name</option></term>
<listitem><para>The name of the module, used in e.g. build logs</para></listitem>
</varlistentry>
<varlistentry>
<term><option></option>sources</term>
<listitem><para>An array of objects defining sources that will be downloaded and extracted in order</para></listitem>
</varlistentry>
<varlistentry>
<term><option>config-opts</option></term>
<listitem><para>An array of options that will be passed to configure</para></listitem>
</varlistentry>
<varlistentry>
<term><option>make-args</option></term>
<listitem><para>An array of arguments that will be passed to make</para></listitem>
</varlistentry>
<varlistentry>
<term><option>make-install-args</option></term>
<listitem><para>An array of arguments that will be passed to make install</para></listitem>
</varlistentry>
<varlistentry>
<term><option>rm-configure</option></term>
<listitem><para>If true, remove the configure script before starting build</para></listitem>
</varlistentry>
<varlistentry>
<term><option>no-autogen</option></term>
<listitem><para>Ignore the existance of an autogen script</para></listitem>
</varlistentry>
<varlistentry>
<term><option>build-options</option></term>
<listitem><para>A build options object that can override global options</para></listitem>
</varlistentry>
<varlistentry>
<term><option>cleanup</option></term>
<listitem><para>An array of file patterns that should be removed at the end.
Patterns starting with / are taken to be full pathnames, otherwise they just match
the basename. Note that any patterns will only match files installed by this module.
</para></listitem>
</varlistentry>
</variablelist>
</refsect2>
<refsect2>
<title>Sources</title>
<para>
These contain a pointer to the source that will be extracted into the source directory before
the build starts. They can be of several types, distiguished by the type property.
</para>
<refsect3>
<title>Archive sources (tar, zip)</title>
<variablelist>
<varlistentry>
<term><option>type</option></term>
<listitem><para>"archive"</para></listitem>
</varlistentry>
<varlistentry>
<term><option>url</option></term>
<listitem><para>URL of the archive</para></listitem>
</varlistentry>
<varlistentry>
<term><option>sha256</option></term>
<listitem><para>The sha256 checksum of the file, verified after download</para></listitem>
</varlistentry>
<varlistentry>
<term><option>strip-components</option></term>
<listitem><para>The number of initial pathname components to strip during extraction. Defaults to 1.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>dest</option></term>
<listitem><para>Directory inside the source dir where the archive will be extracted.</para></listitem>
</varlistentry>
</variablelist>
</refsect3>
<refsect3>
<title>Git sources</title>
<variablelist>
<varlistentry>
<term><option>type</option></term>
<listitem><para>"git"</para></listitem>
</varlistentry>
<varlistentry>
<term><option>url</option></term>
<listitem><para>URL of the git repostiory</para></listitem>
</varlistentry>
<varlistentry>
<term><option>branch</option></term>
<listitem><para>The branch/tag/commit to use from the git repostiory</para></listitem>
</varlistentry>
<varlistentry>
<term><option>dest</option></term>
<listitem><para>Directory inside the source dir where the repository will be checked out.</para></listitem>
</varlistentry>
</variablelist>
</refsect3>
<refsect3>
<title>Bzr sources</title>
<variablelist>
<varlistentry>
<term><option>type</option></term>
<listitem><para>"bzr"</para></listitem>
</varlistentry>
<varlistentry>
<term><option>url</option></term>
<listitem><para>URL of the bzr repostiory</para></listitem>
</varlistentry>
<varlistentry>
<term><option>dest</option></term>
<listitem><para>Directory inside the source dir where the repository will be checked out.</para></listitem>
</varlistentry>
</variablelist>
</refsect3>
<refsect3>
<title>File sources</title>
<variablelist>
<varlistentry>
<term><option>type</option></term>
<listitem><para>"file"</para></listitem>
</varlistentry>
<varlistentry>
<term><option>path</option></term>
<listitem><para>The path of a local file that will be copied into the source dir</para></listitem>
</varlistentry>
<varlistentry>
<term><option>dest-filename</option></term>
<listitem><para>Filename to use inside the source dir, default to the basename of path.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>dest</option></term>
<listitem><para>Directory inside the source dir where the file will be copied.</para></listitem>
</varlistentry>
</variablelist>
</refsect3>
<refsect3>
<title>Patch sources</title>
<variablelist>
<varlistentry>
<term><option>type</option></term>
<listitem><para>"patch"</para></listitem>
</varlistentry>
<varlistentry>
<term><option>path</option></term>
<listitem><para>The path of a patch file that will be applied in the source dir</para></listitem>
</varlistentry>
<varlistentry>
<term><option>strip-components</option></term>
<listitem><para>The value of the -p argument to patch, defaults to 1.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>dest</option></term>
<listitem><para>Directory inside the source dir where the patch will be applied.</para></listitem>
</varlistentry>
</variablelist>
</refsect3>
</refsect2>
</refsect1>
<refsect1>
<title>Options</title>
<para>The following options are understood:</para>
<variablelist>
<varlistentry>
<term><option>-h</option></term>
<term><option>--help</option></term>
<listitem><para>
Show help options and exit.
</para></listitem>
</varlistentry>
<varlistentry>
<term><option>-v</option></term>
<term><option>--verbose</option></term>
<listitem><para>
Print debug information during command processing.
</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--version</option></term>
<listitem><para>
Print version information and exit.
</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--disable-cache</option></term>
<listitem><para>
Don't look at the existing cache for a previous build, instead always rebuild modules.
</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--disable-download</option></term>
<listitem><para>
Don't download any sources. This only works if some version of all sources are downloaded
already. This is useful to rebuild things but without updating git or bzr repositories
from the remote repository.
</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--disable-download</option></term>
<listitem><para>
Exit successfully after downloading the required sources.
</para></listitem>
</varlistentry>
</variablelist>
</refsect1>
<refsect1>
<title>Examples</title>
<para>
<command>$ xdg-app-builder my-app-dir manifest.json</command>
</para>
<para>
Example manifest file:
</para>
<programlisting>
{
"app-id": "org.test.TestApp",
"runtime": "org.freedesktop.Platform",
"runtime-version": "1.2",
"sdk": "org.freedesktop.Sdk",
"command": "test",
"clean": [ "/include", "*.la" ],
"build-options" : {
"cflags": "-O2 -g",
"cxxflags": "-O2 -g",
"env": {
"V": "1"
},
"arch": {
"x86_64": {
"cflags": "-O3 -g",
}
}
},
"modules": [
{
{
"name": "pygobject",
"config-opts": [ "--disable-introspection" ],
"sources": [
{
"type": "archive",
"url": "http://ftp.gnome.org/pub/GNOME/sources/pygobject/2.28/pygobject-2.28.6.tar.xz",
"sha256": "fb8a1d4f665130a125011659bd347c7339c944232163dbb9a34fd0686577adb8"
},
{
"type": "patch",
"path": "required-pygobject-fix.patch"
},
{
"type": "file",
"path": "pygobject-extra-file",
"dest-filename": "extra-file"
}
]
},
{
"name": "babl",
"build-options" : { "cxxflags": "-O2 -g -std=c++11" },
"cleanup": [ "/bin" ],
"sources": [
{
"type": "git",
"url": "git://git.gnome.org/babl"
}
]
},
{
"name": "testapp",
"sources": [
{
"type": "bzr",
"url": "lp:testapp"
}
]
}
]
}
</programlisting>
</refsect1>
<refsect1>
<title>See also</title>
<para>
<citerefentry><refentrytitle>xdg-app</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
<citerefentry><refentrytitle>xdg-app-build-init</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
<citerefentry><refentrytitle>xdg-app-build-build</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
<citerefentry><refentrytitle>xdg-app-build-finish</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
<citerefentry><refentrytitle>xdg-app-build-export</refentrytitle><manvolnum>1</manvolnum></citerefentry>
</para>
</refsect1>
</refentry>