Updated Spanish translation
[anjuta.git] / plugins / am-project / amp-target.c
blob7863598c44800b6234586748a02b7cb66938c7ed
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4; coding: utf-8 -*- */
2 /* amp-target.c
4 * Copyright (C) 2010 Sébastien Granjoux
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation; either version 2 of the
9 * License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
16 * You should have received a copy of the GNU General Public
17 * License along with this program; if not, write to the
18 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
27 #include "amp-target.h"
29 #include "amp-node.h"
30 #include "am-scanner.h"
31 #include "am-properties.h"
32 #include "am-writer.h"
33 #include "amp-group.h"
35 #include <libanjuta/interfaces/ianjuta-project.h>
37 #include <libanjuta/anjuta-debug.h>
39 #include <glib/gi18n.h>
41 #include <memory.h>
42 #include <string.h>
43 #include <ctype.h>
46 /* Types
47 *---------------------------------------------------------------------------*/
49 struct _AmpTargetNode {
50 AnjutaProjectNode base;
51 gchar *install;
52 gint flags;
53 GList* tokens;
56 typedef struct _AmpTargetNodeClass AmpTargetNodeClass;
58 struct _AmpTargetNodeClass {
59 AmpNodeClass parent_class;
62 G_DEFINE_DYNAMIC_TYPE (AmpTargetNode, amp_target_node, AMP_TYPE_NODE);
66 /* Tagged token list
68 * This structure is used to keep a list of useful tokens in each
69 * node. It is a two levels list. The level lists all kinds of token
70 * and has a pointer of another list of token of this kind.
71 *---------------------------------------------------------------------------*/
73 typedef struct _TaggedTokenItem {
74 AmTokenType type;
75 GList *tokens;
76 } TaggedTokenItem;
79 static TaggedTokenItem *
80 tagged_token_item_new (AmTokenType type)
82 TaggedTokenItem *item;
84 item = g_slice_new0(TaggedTokenItem);
86 item->type = type;
88 return item;
91 static void
92 tagged_token_item_free (TaggedTokenItem* item)
94 g_list_free (item->tokens);
95 g_slice_free (TaggedTokenItem, item);
98 static gint
99 tagged_token_item_compare (gconstpointer a, gconstpointer b)
101 return ((TaggedTokenItem *)a)->type - (GPOINTER_TO_INT(b));
104 static GList*
105 tagged_token_list_insert (GList *list, AmTokenType type, AnjutaToken *token)
107 GList *existing;
109 existing = g_list_find_custom (list, GINT_TO_POINTER (type), tagged_token_item_compare);
110 if (existing == NULL)
112 /* Add a new item */
113 TaggedTokenItem *item;
115 item = tagged_token_item_new (type);
116 list = g_list_prepend (list, item);
117 existing = list;
120 ((TaggedTokenItem *)(existing->data))->tokens = g_list_prepend (((TaggedTokenItem *)(existing->data))->tokens, token);
122 return list;
125 static GList*
126 tagged_token_list_get (GList *list, AmTokenType type)
128 GList *existing;
129 GList *tokens = NULL;
131 existing = g_list_find_custom (list, GINT_TO_POINTER (type), tagged_token_item_compare);
132 if (existing != NULL)
134 tokens = ((TaggedTokenItem *)(existing->data))->tokens;
137 return tokens;
140 /* The returned list must be freed */
141 static GList*
142 tagged_token_list_get_all (GList *list)
144 GList *tokens = NULL;
146 for (; list != NULL; list = g_list_next (list))
148 tokens = g_list_concat (tokens, g_list_copy (((TaggedTokenItem *)list->data)->tokens));
151 return tokens;
154 static AnjutaTokenType
155 tagged_token_list_next (GList *list, AmTokenType type)
157 AnjutaTokenType best = 0;
159 for (list = g_list_first (list); list != NULL; list = g_list_next (list))
161 TaggedTokenItem *item = (TaggedTokenItem *)list->data;
163 if ((item->type > type) && ((best == 0) || (item->type < best)))
165 best = item->type;
169 return best;
172 static GList*
173 tagged_token_list_free (GList *list)
175 g_list_foreach (list, (GFunc)tagged_token_item_free, NULL);
176 g_list_free (list);
178 return NULL;
181 /* Public functions
182 *---------------------------------------------------------------------------*/
185 /* Public functions
186 *---------------------------------------------------------------------------*/
188 void
189 amp_target_node_set_type (AmpTargetNode *target, AmTokenType type)
191 target->base.type = ANJUTA_PROJECT_TARGET | type;
192 target->base.properties_info = amp_get_target_property_list(type);
195 void
196 amp_target_node_add_token (AmpTargetNode *target, AmTokenType type, AnjutaToken *token)
198 target->tokens = tagged_token_list_insert (target->tokens, type, token);
201 void
202 amp_target_node_remove_token (AmpTargetNode *target, AnjutaToken *token)
204 GList *list;
206 for (list = target->tokens; list != NULL; list = g_list_next (list))
208 TaggedTokenItem *tagged = (TaggedTokenItem *)list->data;
210 tagged->tokens = g_list_remove (tagged->tokens, token);
214 GList *
215 amp_target_node_get_token (AmpTargetNode *target, AmTokenType type)
217 return tagged_token_list_get (target->tokens, type);
220 GList*
221 amp_target_node_get_all_token (AmpTargetNode *target)
223 return tagged_token_list_get_all (target->tokens);
226 AnjutaTokenType
227 amp_target_node_get_first_token_type (AmpTargetNode *target)
229 return tagged_token_list_next (target->tokens, 0);
232 AnjutaTokenType
233 amp_target_node_get_next_token_type (AmpTargetNode *target, AnjutaTokenType type)
235 return tagged_token_list_next (target->tokens, type);
238 void
239 amp_target_node_update_node (AmpTargetNode *node, AmpTargetNode *new_node)
241 g_free (node->install);
242 g_list_free (node->tokens);
244 node->install = new_node->install;
245 new_node->install = NULL;
246 node->flags = new_node->flags;
247 node->tokens = new_node->tokens;
248 new_node->tokens = NULL;
251 /* Get install directory */
252 const gchar *
253 amp_target_node_get_install_directory (AmpTargetNode *node)
255 return node->install;
258 /* The target has changed which could change its children */
259 void
260 amp_target_changed (AmpTargetNode *node)
262 GList *item;
263 gboolean custom = FALSE;
265 for (item = ANJUTA_PROJECT_NODE (node)->properties; item != NULL; item = g_list_next (item))
267 AmpProperty *prop = (AmpProperty *)item->data;
269 custom = ((AmpPropertyInfo *)prop->base.info)->flags & AM_PROPERTY_PREFIX_OBJECT;
270 if (custom) break;
273 if (custom)
275 /* Update object name if the target has some custom properties */
276 AnjutaProjectNode *child;
278 for (child = anjuta_project_node_first_child (ANJUTA_PROJECT_NODE (node)); child != NULL; child = anjuta_project_node_next_sibling (child))
280 if (anjuta_project_node_get_node_type (child) == ANJUTA_PROJECT_OBJECT)
282 if (child->file != NULL)
284 AnjutaProjectNode *source = anjuta_project_node_first_child (child);
286 if (source != NULL)
288 gchar *obj_name;
289 const gchar *obj_ext;
291 if (child->name != NULL)
293 g_free (child->name);
294 child->name = NULL;
296 obj_name = g_file_get_basename (child->file);
297 obj_ext = strrchr (obj_name, '.');
298 if ((obj_ext != NULL) && (obj_ext != obj_name))
300 GFile *src_dir;
301 gchar *src_name;
302 gchar *src_ext;
303 gchar *new_name;
305 src_dir = g_file_get_parent (source->file);
306 src_name = g_file_get_basename (source->file);
307 src_ext = strrchr (src_name, '.');
308 if ((src_ext != NULL) && (src_ext != src_name)) *src_ext = '\0';
309 new_name = g_strconcat (node->base.name, "-", src_name, obj_ext, NULL);
311 g_object_unref (child->file);
312 child->file = g_file_get_child (src_dir, new_name);
314 g_free (new_name);
315 g_free (src_name);
316 g_object_unref (src_dir);
318 g_free (obj_name);
326 AmpTargetNode*
327 amp_target_node_new (const gchar *name, AnjutaProjectNodeType type, const gchar *install, gint flags)
329 AmpTargetNode *node;
331 node = g_object_new (AMP_TYPE_TARGET_NODE, NULL);
332 amp_target_node_set_type (node, type);
333 node->base.name = g_strdup (name);
334 if ((install == NULL) && ((type & ANJUTA_PROJECT_ID_MASK) == ANJUTA_PROJECT_DATA)) {
335 node->install = g_strdup (name);
337 else {
338 node->install = g_strdup (install);
340 node->flags = flags;
342 amp_node_property_add_mandatory (ANJUTA_PROJECT_NODE (node));
344 return node;
347 AmpTargetNode*
348 amp_target_node_new_valid (const gchar *name, AnjutaProjectNodeType type, const gchar *install, gint flags, AnjutaProjectNode *parent, GError **error)
350 const gchar *basename;
352 /* Check parent if present */
353 if (parent != NULL)
355 if ((anjuta_project_node_get_node_type (parent) == ANJUTA_PROJECT_GROUP) &&
356 (amp_group_node_get_makefile_token (AMP_GROUP_NODE (parent)) == NULL))
358 amp_set_error (error, IANJUTA_PROJECT_ERROR_VALIDATION_FAILED,
359 _("Target parent is not a valid group"));
361 return NULL;
365 /* Validate target name */
366 if (!name || strlen (name) <= 0)
368 amp_set_error (error, IANJUTA_PROJECT_ERROR_VALIDATION_FAILED,
369 _("Please specify target name"));
370 return NULL;
373 gboolean failed = FALSE;
374 const gchar *ptr = name;
375 while (*ptr) {
376 if (!isalnum (*ptr) && *ptr != '.' && *ptr != '-' &&
377 *ptr != '_' && *ptr != '/')
378 failed = TRUE;
379 ptr++;
381 if (failed) {
382 amp_set_error (error, IANJUTA_PROJECT_ERROR_VALIDATION_FAILED,
383 _("Target name can only contain alphanumeric, '_', '-', '/' or '.' characters"));
384 return NULL;
388 /* Skip eventual directory name */
389 basename = strrchr (name, '/');
390 basename = basename == NULL ? name : basename + 1;
393 if ((type & ANJUTA_PROJECT_ID_MASK) == ANJUTA_PROJECT_SHAREDLIB) {
394 if (strlen (basename) < 7 ||
395 strncmp (basename, "lib", strlen("lib")) != 0 ||
396 strcmp (&basename[strlen(basename) - 3], ".la") != 0) {
397 amp_set_error (error, IANJUTA_PROJECT_ERROR_VALIDATION_FAILED,
398 _("Shared library target name must be of the form 'libxxx.la'"));
399 return NULL;
402 else if ((type & ANJUTA_PROJECT_ID_MASK) == ANJUTA_PROJECT_STATICLIB) {
403 if (strlen (basename) < 6 ||
404 strncmp (basename, "lib", strlen("lib")) != 0 ||
405 strcmp (&basename[strlen(basename) - 2], ".a") != 0) {
406 amp_set_error (error, IANJUTA_PROJECT_ERROR_VALIDATION_FAILED,
407 _("Static library target name must be of the form 'libxxx.a'"));
408 return NULL;
411 else if ((type & ANJUTA_PROJECT_ID_MASK) == ANJUTA_PROJECT_LT_MODULE) {
412 if (strlen (basename) < 4 ||
413 strcmp (&basename[strlen(basename) - 3], ".la") != 0) {
414 amp_set_error (error, IANJUTA_PROJECT_ERROR_VALIDATION_FAILED,
415 _("Module target name must be of the form 'xxx.la'"));
416 return NULL;
420 return amp_target_node_new (name, type, install, flags);
423 void
424 amp_target_node_free (AmpTargetNode *node)
426 g_object_unref (G_OBJECT (node));
431 /* AmpNode implementation
432 *---------------------------------------------------------------------------*/
434 static gboolean
435 amp_target_node_update (AmpNode *node, AmpNode *new_node)
437 amp_target_node_update_node (AMP_TARGET_NODE (node), AMP_TARGET_NODE (new_node));
439 return TRUE;
442 static AmpNode *
443 amp_target_node_copy (AmpNode *old_node)
445 AmpNode *new_node;
447 new_node = AMP_NODE_CLASS (amp_target_node_parent_class)->copy (old_node);
448 amp_target_node_set_type (AMP_TARGET_NODE (new_node), anjuta_project_node_get_full_type (ANJUTA_PROJECT_NODE (old_node)));
450 return new_node;
453 static gboolean
454 amp_target_node_write (AmpNode *node, AmpNode *parent, AmpProject *project, GError **error)
456 return amp_target_node_create_token (project, AMP_TARGET_NODE (node), error);
459 static gboolean
460 amp_target_node_erase (AmpNode *target, AmpNode *parent, AmpProject *project, GError **error)
462 gboolean ok;
463 GList * token_list;
465 token_list = amp_target_node_get_all_token (AMP_TARGET_NODE (target));
466 ok = amp_target_node_delete_token (project, AMP_TARGET_NODE (target), token_list, error);
467 g_list_free (token_list);
469 /* Remove installation directory variable if the removed target was the
470 * only one using it */
471 if (ok)
473 AnjutaProjectNode *node;
474 const gchar *installdir;
475 AnjutaProjectProperty *prop;
476 gboolean used = FALSE;
478 prop = amp_node_get_property_from_token (ANJUTA_PROJECT_NODE (target), AM_TOKEN__PROGRAMS, 6);
479 installdir = prop->value;
481 for (node = anjuta_project_node_first_child (ANJUTA_PROJECT_NODE (parent)); node != NULL; node = anjuta_project_node_next_sibling (node))
483 if (node != ANJUTA_PROJECT_NODE (target))
485 prop = amp_node_get_property_from_token (node, AM_TOKEN__PROGRAMS, 6);
486 if ((prop != NULL) && (g_strcmp0 (installdir, prop->value) == 0))
488 used = TRUE;
489 break;
494 if (!used)
496 GList *item;
498 for (item = anjuta_project_node_get_properties (ANJUTA_PROJECT_NODE (parent)); item != NULL; item = g_list_next (item))
500 AmpProperty *prop = (AmpProperty *)item->data;
502 if ((((AmpPropertyInfo *)prop->base.info)->token_type == AM_TOKEN_DIR) && (g_strcmp0 (prop->base.name, installdir) == 0))
504 /* Remove directory variable */
505 anjuta_token_remove_list (anjuta_token_list (prop->token));
506 amp_group_node_update_makefile (AMP_GROUP_NODE (parent), prop->token);
507 break;
513 return ok;
518 /* GObjet implementation
519 *---------------------------------------------------------------------------*/
521 static void
522 amp_target_node_init (AmpTargetNode *node)
524 node->base.type = ANJUTA_PROJECT_TARGET;
525 node->base.state = ANJUTA_PROJECT_CAN_ADD_SOURCE |
526 ANJUTA_PROJECT_CAN_ADD_MODULE |
527 ANJUTA_PROJECT_CAN_REMOVE;
528 node->install = NULL;
529 node->flags = 0;
530 node->tokens = NULL;
533 static void
534 amp_target_node_finalize (GObject *object)
536 AmpTargetNode *node = AMP_TARGET_NODE (object);
538 tagged_token_list_free (node->tokens);
539 node->tokens = NULL;
541 G_OBJECT_CLASS (amp_target_node_parent_class)->finalize (object);
544 static void
545 amp_target_node_class_init (AmpTargetNodeClass *klass)
547 GObjectClass* object_class = G_OBJECT_CLASS (klass);
548 AmpNodeClass* node_class;
550 object_class->finalize = amp_target_node_finalize;
552 node_class = AMP_NODE_CLASS (klass);
553 node_class->update = amp_target_node_update;
554 node_class->copy = amp_target_node_copy;
555 node_class->write = amp_target_node_write;
556 node_class->erase = amp_target_node_erase;
559 static void
560 amp_target_node_class_finalize (AmpTargetNodeClass *klass)
564 void
565 amp_target_node_register (GTypeModule *module)
567 amp_target_node_register_type (module);