/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4; coding: utf-8 -*- */ /* amp-target.c * * Copyright (C) 2010 Sébastien Granjoux * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. * */ #ifdef HAVE_CONFIG_H #include #endif #include "amp-target.h" #include "amp-node.h" #include "am-scanner.h" #include "am-properties.h" #include "am-writer.h" #include "amp-group.h" #include #include #include #include #include #include /* Types *---------------------------------------------------------------------------*/ struct _AmpTargetNode { AnjutaProjectNode base; gchar *install; gint flags; GList* tokens; }; typedef struct _AmpTargetNodeClass AmpTargetNodeClass; struct _AmpTargetNodeClass { AmpNodeClass parent_class; }; G_DEFINE_DYNAMIC_TYPE (AmpTargetNode, amp_target_node, AMP_TYPE_NODE); /* Tagged token list * * This structure is used to keep a list of useful tokens in each * node. It is a two levels list. The level lists all kinds of token * and has a pointer of another list of token of this kind. *---------------------------------------------------------------------------*/ typedef struct _TaggedTokenItem { AmTokenType type; GList *tokens; } TaggedTokenItem; static TaggedTokenItem * tagged_token_item_new (AmTokenType type) { TaggedTokenItem *item; item = g_slice_new0(TaggedTokenItem); item->type = type; return item; } static void tagged_token_item_free (TaggedTokenItem* item) { g_list_free (item->tokens); g_slice_free (TaggedTokenItem, item); } static gint tagged_token_item_compare (gconstpointer a, gconstpointer b) { return ((TaggedTokenItem *)a)->type - (GPOINTER_TO_INT(b)); } static GList* tagged_token_list_insert (GList *list, AmTokenType type, AnjutaToken *token) { GList *existing; existing = g_list_find_custom (list, GINT_TO_POINTER (type), tagged_token_item_compare); if (existing == NULL) { /* Add a new item */ TaggedTokenItem *item; item = tagged_token_item_new (type); list = g_list_prepend (list, item); existing = list; } ((TaggedTokenItem *)(existing->data))->tokens = g_list_prepend (((TaggedTokenItem *)(existing->data))->tokens, token); return list; } static GList* tagged_token_list_get (GList *list, AmTokenType type) { GList *existing; GList *tokens = NULL; existing = g_list_find_custom (list, GINT_TO_POINTER (type), tagged_token_item_compare); if (existing != NULL) { tokens = ((TaggedTokenItem *)(existing->data))->tokens; } return tokens; } /* The returned list must be freed */ static GList* tagged_token_list_get_all (GList *list) { GList *tokens = NULL; for (; list != NULL; list = g_list_next (list)) { tokens = g_list_concat (tokens, g_list_copy (((TaggedTokenItem *)list->data)->tokens)); } return tokens; } static AnjutaTokenType tagged_token_list_next (GList *list, AmTokenType type) { AnjutaTokenType best = 0; for (list = g_list_first (list); list != NULL; list = g_list_next (list)) { TaggedTokenItem *item = (TaggedTokenItem *)list->data; if ((item->type > type) && ((best == 0) || (item->type < best))) { best = item->type; } } return best; } static GList* tagged_token_list_free (GList *list) { g_list_foreach (list, (GFunc)tagged_token_item_free, NULL); g_list_free (list); return NULL; } /* Public functions *---------------------------------------------------------------------------*/ /* Public functions *---------------------------------------------------------------------------*/ void amp_target_node_set_type (AmpTargetNode *target, AmTokenType type) { target->base.type = ANJUTA_PROJECT_TARGET | type; target->base.properties_info = amp_get_target_property_list(type); } void amp_target_node_add_token (AmpTargetNode *target, AmTokenType type, AnjutaToken *token) { target->tokens = tagged_token_list_insert (target->tokens, type, token); } void amp_target_node_remove_token (AmpTargetNode *target, AnjutaToken *token) { GList *list; for (list = target->tokens; list != NULL; list = g_list_next (list)) { TaggedTokenItem *tagged = (TaggedTokenItem *)list->data; tagged->tokens = g_list_remove (tagged->tokens, token); } } GList * amp_target_node_get_token (AmpTargetNode *target, AmTokenType type) { return tagged_token_list_get (target->tokens, type); } GList* amp_target_node_get_all_token (AmpTargetNode *target) { return tagged_token_list_get_all (target->tokens); } AnjutaTokenType amp_target_node_get_first_token_type (AmpTargetNode *target) { return tagged_token_list_next (target->tokens, 0); } AnjutaTokenType amp_target_node_get_next_token_type (AmpTargetNode *target, AnjutaTokenType type) { return tagged_token_list_next (target->tokens, type); } void amp_target_node_update_node (AmpTargetNode *node, AmpTargetNode *new_node) { g_free (node->install); g_list_free (node->tokens); node->install = new_node->install; new_node->install = NULL; node->flags = new_node->flags; node->tokens = new_node->tokens; new_node->tokens = NULL; } /* Get install directory */ const gchar * amp_target_node_get_install_directory (AmpTargetNode *node) { return node->install; } /* The target has changed which could change its children */ void amp_target_changed (AmpTargetNode *node) { GList *item; gboolean custom = FALSE; for (item = ANJUTA_PROJECT_NODE (node)->properties; item != NULL; item = g_list_next (item)) { AmpProperty *prop = (AmpProperty *)item->data; custom = ((AmpPropertyInfo *)prop->base.info)->flags & AM_PROPERTY_PREFIX_OBJECT; if (custom) break; } if (custom) { /* Update object name if the target has some custom properties */ AnjutaProjectNode *child; for (child = anjuta_project_node_first_child (ANJUTA_PROJECT_NODE (node)); child != NULL; child = anjuta_project_node_next_sibling (child)) { if (anjuta_project_node_get_node_type (child) == ANJUTA_PROJECT_OBJECT) { if (child->file != NULL) { AnjutaProjectNode *source = anjuta_project_node_first_child (child); if (source != NULL) { gchar *obj_name; const gchar *obj_ext; if (child->name != NULL) { g_free (child->name); child->name = NULL; } obj_name = g_file_get_basename (child->file); obj_ext = strrchr (obj_name, '.'); if ((obj_ext != NULL) && (obj_ext != obj_name)) { GFile *src_dir; gchar *src_name; gchar *src_ext; gchar *new_name; src_dir = g_file_get_parent (source->file); src_name = g_file_get_basename (source->file); src_ext = strrchr (src_name, '.'); if ((src_ext != NULL) && (src_ext != src_name)) *src_ext = '\0'; new_name = g_strconcat (node->base.name, "-", src_name, obj_ext, NULL); g_object_unref (child->file); child->file = g_file_get_child (src_dir, new_name); g_free (new_name); g_free (src_name); g_object_unref (src_dir); } g_free (obj_name); } } } } } } AmpTargetNode* amp_target_node_new (const gchar *name, AnjutaProjectNodeType type, const gchar *install, gint flags) { AmpTargetNode *node; node = g_object_new (AMP_TYPE_TARGET_NODE, NULL); amp_target_node_set_type (node, type); node->base.name = g_strdup (name); if ((install == NULL) && ((type & ANJUTA_PROJECT_ID_MASK) == ANJUTA_PROJECT_DATA)) { node->install = g_strdup (name); } else { node->install = g_strdup (install); } node->flags = flags; amp_node_property_add_mandatory (ANJUTA_PROJECT_NODE (node)); return node; } AmpTargetNode* amp_target_node_new_valid (const gchar *name, AnjutaProjectNodeType type, const gchar *install, gint flags, AnjutaProjectNode *parent, GError **error) { const gchar *basename; /* Check parent if present */ if (parent != NULL) { if ((anjuta_project_node_get_node_type (parent) == ANJUTA_PROJECT_GROUP) && (amp_group_node_get_makefile_token (AMP_GROUP_NODE (parent)) == NULL)) { amp_set_error (error, IANJUTA_PROJECT_ERROR_VALIDATION_FAILED, _("Target parent is not a valid group")); return NULL; } } /* Validate target name */ if (!name || strlen (name) <= 0) { amp_set_error (error, IANJUTA_PROJECT_ERROR_VALIDATION_FAILED, _("Please specify target name")); return NULL; } { gboolean failed = FALSE; const gchar *ptr = name; while (*ptr) { if (!isalnum (*ptr) && *ptr != '.' && *ptr != '-' && *ptr != '_' && *ptr != '/') failed = TRUE; ptr++; } if (failed) { amp_set_error (error, IANJUTA_PROJECT_ERROR_VALIDATION_FAILED, _("Target name can only contain alphanumeric, '_', '-', '/' or '.' characters")); return NULL; } } /* Skip eventual directory name */ basename = strrchr (name, '/'); basename = basename == NULL ? name : basename + 1; if ((type & ANJUTA_PROJECT_ID_MASK) == ANJUTA_PROJECT_SHAREDLIB) { if (strlen (basename) < 7 || strncmp (basename, "lib", strlen("lib")) != 0 || strcmp (&basename[strlen(basename) - 3], ".la") != 0) { amp_set_error (error, IANJUTA_PROJECT_ERROR_VALIDATION_FAILED, _("Shared library target name must be of the form 'libxxx.la'")); return NULL; } } else if ((type & ANJUTA_PROJECT_ID_MASK) == ANJUTA_PROJECT_STATICLIB) { if (strlen (basename) < 6 || strncmp (basename, "lib", strlen("lib")) != 0 || strcmp (&basename[strlen(basename) - 2], ".a") != 0) { amp_set_error (error, IANJUTA_PROJECT_ERROR_VALIDATION_FAILED, _("Static library target name must be of the form 'libxxx.a'")); return NULL; } } else if ((type & ANJUTA_PROJECT_ID_MASK) == ANJUTA_PROJECT_LT_MODULE) { if (strlen (basename) < 4 || strcmp (&basename[strlen(basename) - 3], ".la") != 0) { amp_set_error (error, IANJUTA_PROJECT_ERROR_VALIDATION_FAILED, _("Module target name must be of the form 'xxx.la'")); return NULL; } } return amp_target_node_new (name, type, install, flags); } void amp_target_node_free (AmpTargetNode *node) { g_object_unref (G_OBJECT (node)); } /* AmpNode implementation *---------------------------------------------------------------------------*/ static gboolean amp_target_node_update (AmpNode *node, AmpNode *new_node) { amp_target_node_update_node (AMP_TARGET_NODE (node), AMP_TARGET_NODE (new_node)); return TRUE; } static AmpNode * amp_target_node_copy (AmpNode *old_node) { AmpNode *new_node; new_node = AMP_NODE_CLASS (amp_target_node_parent_class)->copy (old_node); amp_target_node_set_type (AMP_TARGET_NODE (new_node), anjuta_project_node_get_full_type (ANJUTA_PROJECT_NODE (old_node))); return new_node; } static gboolean amp_target_node_write (AmpNode *node, AmpNode *parent, AmpProject *project, GError **error) { return amp_target_node_create_token (project, AMP_TARGET_NODE (node), error); } static gboolean amp_target_node_erase (AmpNode *target, AmpNode *parent, AmpProject *project, GError **error) { gboolean ok; GList * token_list; token_list = amp_target_node_get_all_token (AMP_TARGET_NODE (target)); ok = amp_target_node_delete_token (project, AMP_TARGET_NODE (target), token_list, error); g_list_free (token_list); /* Remove installation directory variable if the removed target was the * only one using it */ if (ok) { AnjutaProjectNode *node; const gchar *installdir; AnjutaProjectProperty *prop; gboolean used = FALSE; prop = amp_node_get_property_from_token (ANJUTA_PROJECT_NODE (target), AM_TOKEN__PROGRAMS, 6); installdir = prop->value; for (node = anjuta_project_node_first_child (ANJUTA_PROJECT_NODE (parent)); node != NULL; node = anjuta_project_node_next_sibling (node)) { if (node != ANJUTA_PROJECT_NODE (target)) { prop = amp_node_get_property_from_token (node, AM_TOKEN__PROGRAMS, 6); if ((prop != NULL) && (g_strcmp0 (installdir, prop->value) == 0)) { used = TRUE; break; } } } if (!used) { GList *item; for (item = anjuta_project_node_get_properties (ANJUTA_PROJECT_NODE (parent)); item != NULL; item = g_list_next (item)) { AmpProperty *prop = (AmpProperty *)item->data; if ((((AmpPropertyInfo *)prop->base.info)->token_type == AM_TOKEN_DIR) && (g_strcmp0 (prop->base.name, installdir) == 0)) { /* Remove directory variable */ anjuta_token_remove_list (anjuta_token_list (prop->token)); amp_group_node_update_makefile (AMP_GROUP_NODE (parent), prop->token); break; } } } }; return ok; } /* GObjet implementation *---------------------------------------------------------------------------*/ static void amp_target_node_init (AmpTargetNode *node) { node->base.type = ANJUTA_PROJECT_TARGET; node->base.state = ANJUTA_PROJECT_CAN_ADD_SOURCE | ANJUTA_PROJECT_CAN_ADD_MODULE | ANJUTA_PROJECT_CAN_REMOVE; node->install = NULL; node->flags = 0; node->tokens = NULL; } static void amp_target_node_finalize (GObject *object) { AmpTargetNode *node = AMP_TARGET_NODE (object); tagged_token_list_free (node->tokens); node->tokens = NULL; G_OBJECT_CLASS (amp_target_node_parent_class)->finalize (object); } static void amp_target_node_class_init (AmpTargetNodeClass *klass) { GObjectClass* object_class = G_OBJECT_CLASS (klass); AmpNodeClass* node_class; object_class->finalize = amp_target_node_finalize; node_class = AMP_NODE_CLASS (klass); node_class->update = amp_target_node_update; node_class->copy = amp_target_node_copy; node_class->write = amp_target_node_write; node_class->erase = amp_target_node_erase; } static void amp_target_node_class_finalize (AmpTargetNodeClass *klass) { } void amp_target_node_register (GTypeModule *module) { amp_target_node_register_type (module); }