1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4; coding: utf-8 -*- */
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.
27 #include "amp-target.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>
47 *---------------------------------------------------------------------------*/
49 struct _AmpTargetNode
{
50 AnjutaProjectNode base
;
56 typedef struct _AmpTargetNodeClass AmpTargetNodeClass
;
58 struct _AmpTargetNodeClass
{
59 AmpNodeClass parent_class
;
62 G_DEFINE_DYNAMIC_TYPE (AmpTargetNode
, amp_target_node
, AMP_TYPE_NODE
);
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
{
79 static TaggedTokenItem
*
80 tagged_token_item_new (AmTokenType type
)
82 TaggedTokenItem
*item
;
84 item
= g_slice_new0(TaggedTokenItem
);
92 tagged_token_item_free (TaggedTokenItem
* item
)
94 g_list_free (item
->tokens
);
95 g_slice_free (TaggedTokenItem
, item
);
99 tagged_token_item_compare (gconstpointer a
, gconstpointer b
)
101 return ((TaggedTokenItem
*)a
)->type
- (GPOINTER_TO_INT(b
));
105 tagged_token_list_insert (GList
*list
, AmTokenType type
, AnjutaToken
*token
)
109 existing
= g_list_find_custom (list
, GINT_TO_POINTER (type
), tagged_token_item_compare
);
110 if (existing
== NULL
)
113 TaggedTokenItem
*item
;
115 item
= tagged_token_item_new (type
);
116 list
= g_list_prepend (list
, item
);
120 ((TaggedTokenItem
*)(existing
->data
))->tokens
= g_list_prepend (((TaggedTokenItem
*)(existing
->data
))->tokens
, token
);
126 tagged_token_list_get (GList
*list
, AmTokenType type
)
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
;
140 /* The returned list must be freed */
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
));
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
)))
173 tagged_token_list_free (GList
*list
)
175 g_list_foreach (list
, (GFunc
)tagged_token_item_free
, NULL
);
182 *---------------------------------------------------------------------------*/
186 *---------------------------------------------------------------------------*/
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
);
196 amp_target_node_add_token (AmpTargetNode
*target
, AmTokenType type
, AnjutaToken
*token
)
198 target
->tokens
= tagged_token_list_insert (target
->tokens
, type
, token
);
202 amp_target_node_remove_token (AmpTargetNode
*target
, AnjutaToken
*token
)
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
);
215 amp_target_node_get_token (AmpTargetNode
*target
, AmTokenType type
)
217 return tagged_token_list_get (target
->tokens
, type
);
221 amp_target_node_get_all_token (AmpTargetNode
*target
)
223 return tagged_token_list_get_all (target
->tokens
);
227 amp_target_node_get_first_token_type (AmpTargetNode
*target
)
229 return tagged_token_list_next (target
->tokens
, 0);
233 amp_target_node_get_next_token_type (AmpTargetNode
*target
, AnjutaTokenType type
)
235 return tagged_token_list_next (target
->tokens
, type
);
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 */
253 amp_target_node_get_install_directory (AmpTargetNode
*node
)
255 return node
->install
;
258 /* The target has changed which could change its children */
260 amp_target_changed (AmpTargetNode
*node
)
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
;
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
);
289 const gchar
*obj_ext
;
291 if (child
->name
!= NULL
)
293 g_free (child
->name
);
296 obj_name
= g_file_get_basename (child
->file
);
297 obj_ext
= strrchr (obj_name
, '.');
298 if ((obj_ext
!= NULL
) && (obj_ext
!= obj_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
);
316 g_object_unref (src_dir
);
327 amp_target_node_new (const gchar
*name
, AnjutaProjectNodeType type
, const gchar
*install
, gint flags
)
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
);
338 node
->install
= g_strdup (install
);
342 amp_node_property_add_mandatory (ANJUTA_PROJECT_NODE (node
));
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 */
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"));
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"));
373 gboolean failed
= FALSE
;
374 const gchar
*ptr
= name
;
376 if (!isalnum (*ptr
) && *ptr
!= '.' && *ptr
!= '-' &&
377 *ptr
!= '_' && *ptr
!= '/')
382 amp_set_error (error
, IANJUTA_PROJECT_ERROR_VALIDATION_FAILED
,
383 _("Target name can only contain alphanumeric, '_', '-', '/' or '.' characters"));
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'"));
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'"));
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'"));
420 return amp_target_node_new (name
, type
, install
, flags
);
424 amp_target_node_free (AmpTargetNode
*node
)
426 g_object_unref (G_OBJECT (node
));
431 /* AmpNode implementation
432 *---------------------------------------------------------------------------*/
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
));
443 amp_target_node_copy (AmpNode
*old_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
)));
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
);
460 amp_target_node_erase (AmpNode
*target
, AmpNode
*parent
, AmpProject
*project
, GError
**error
)
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 */
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))
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
);
518 /* GObjet implementation
519 *---------------------------------------------------------------------------*/
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
;
534 amp_target_node_finalize (GObject
*object
)
536 AmpTargetNode
*node
= AMP_TARGET_NODE (object
);
538 tagged_token_list_free (node
->tokens
);
541 G_OBJECT_CLASS (amp_target_node_parent_class
)->finalize (object
);
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
;
560 amp_target_node_class_finalize (AmpTargetNodeClass
*klass
)
565 amp_target_node_register (GTypeModule
*module
)
567 amp_target_node_register_type (module
);