1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4; coding: utf-8 -*- */
2 /* amp-target.c
3  *
4  * Copyright (C) 2010  Sébastien Granjoux
5  *
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.
10  *
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.
15  *
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.
20  *
21  */
22 
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26 
27 #include "amp-target.h"
28 
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"
34 
35 #include <libanjuta/interfaces/ianjuta-project.h>
36 
37 #include <libanjuta/anjuta-debug.h>
38 
39 #include <glib/gi18n.h>
40 
41 #include <memory.h>
42 #include <string.h>
43 #include <ctype.h>
44 
45 
46 /* Types
47  *---------------------------------------------------------------------------*/
48 
49 struct _AmpTargetNode {
50 	AnjutaProjectNode base;
51 	gchar *install;
52 	gint flags;
53 	GList* tokens;
54 };
55 
56 typedef struct _AmpTargetNodeClass AmpTargetNodeClass;
57 
58 struct _AmpTargetNodeClass {
59 	AmpNodeClass parent_class;
60 };
61 
62 G_DEFINE_DYNAMIC_TYPE (AmpTargetNode, amp_target_node, AMP_TYPE_NODE);
63 
64 
65 
66 /* Tagged token list
67  *
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  *---------------------------------------------------------------------------*/
72 
73 typedef struct _TaggedTokenItem {
74 	AmTokenType type;
75 	GList *tokens;
76 } TaggedTokenItem;
77 
78 
79 static TaggedTokenItem *
tagged_token_item_new(AmTokenType type)80 tagged_token_item_new (AmTokenType type)
81 {
82     TaggedTokenItem *item;
83 
84 	item = g_slice_new0(TaggedTokenItem);
85 
86 	item->type = type;
87 
88 	return item;
89 }
90 
91 static void
tagged_token_item_free(TaggedTokenItem * item)92 tagged_token_item_free (TaggedTokenItem* item)
93 {
94 	g_list_free (item->tokens);
95     g_slice_free (TaggedTokenItem, item);
96 }
97 
98 static gint
tagged_token_item_compare(gconstpointer a,gconstpointer b)99 tagged_token_item_compare (gconstpointer a, gconstpointer b)
100 {
101 	return ((TaggedTokenItem *)a)->type - (GPOINTER_TO_INT(b));
102 }
103 
104 static GList*
tagged_token_list_insert(GList * list,AmTokenType type,AnjutaToken * token)105 tagged_token_list_insert (GList *list, AmTokenType type, AnjutaToken *token)
106 {
107 	GList *existing;
108 
109 	existing = g_list_find_custom (list, GINT_TO_POINTER (type), tagged_token_item_compare);
110 	if (existing == NULL)
111 	{
112 		/* Add a new item */
113 		TaggedTokenItem *item;
114 
115 		item = tagged_token_item_new (type);
116 		list = g_list_prepend (list, item);
117 		existing = list;
118 	}
119 
120 	((TaggedTokenItem *)(existing->data))->tokens = g_list_prepend (((TaggedTokenItem *)(existing->data))->tokens, token);
121 
122 	return list;
123 }
124 
125 static GList*
tagged_token_list_get(GList * list,AmTokenType type)126 tagged_token_list_get (GList *list, AmTokenType type)
127 {
128 	GList *existing;
129 	GList *tokens = NULL;
130 
131 	existing = g_list_find_custom (list, GINT_TO_POINTER (type), tagged_token_item_compare);
132 	if (existing != NULL)
133 	{
134 		tokens = ((TaggedTokenItem *)(existing->data))->tokens;
135 	}
136 
137 	return tokens;
138 }
139 
140 /* The returned list must be freed */
141 static GList*
tagged_token_list_get_all(GList * list)142 tagged_token_list_get_all (GList *list)
143 {
144 	GList *tokens = NULL;
145 
146 	for (; list != NULL; list = g_list_next (list))
147 	{
148 		tokens = g_list_concat (tokens, g_list_copy (((TaggedTokenItem *)list->data)->tokens));
149 	}
150 
151 	return tokens;
152 }
153 
154 static AnjutaTokenType
tagged_token_list_next(GList * list,AmTokenType type)155 tagged_token_list_next (GList *list, AmTokenType type)
156 {
157 	AnjutaTokenType best = 0;
158 
159 	for (list = g_list_first (list); list != NULL; list = g_list_next (list))
160 	{
161 		TaggedTokenItem *item = (TaggedTokenItem *)list->data;
162 
163 		if ((item->type > type) && ((best == 0) || (item->type < best)))
164 		{
165 			best = item->type;
166 		}
167 	}
168 
169 	return best;
170 }
171 
172 static GList*
tagged_token_list_free(GList * list)173 tagged_token_list_free (GList *list)
174 {
175 	g_list_foreach (list, (GFunc)tagged_token_item_free, NULL);
176 	g_list_free (list);
177 
178 	return NULL;
179 }
180 
181 /* Public functions
182  *---------------------------------------------------------------------------*/
183 
184 
185 /* Public functions
186  *---------------------------------------------------------------------------*/
187 
188 void
amp_target_node_set_type(AmpTargetNode * target,AmTokenType type)189 amp_target_node_set_type (AmpTargetNode *target, AmTokenType type)
190 {
191 	target->base.type = ANJUTA_PROJECT_TARGET | type;
192 	target->base.properties_info = amp_get_target_property_list(type);
193 }
194 
195 void
amp_target_node_add_token(AmpTargetNode * target,AmTokenType type,AnjutaToken * token)196 amp_target_node_add_token (AmpTargetNode *target, AmTokenType type, AnjutaToken *token)
197 {
198 	target->tokens = tagged_token_list_insert (target->tokens, type, token);
199 }
200 
201 void
amp_target_node_remove_token(AmpTargetNode * target,AnjutaToken * token)202 amp_target_node_remove_token (AmpTargetNode *target, AnjutaToken *token)
203 {
204 	GList *list;
205 
206 	for (list = target->tokens; list != NULL; list = g_list_next (list))
207 	{
208 		TaggedTokenItem *tagged = (TaggedTokenItem *)list->data;
209 
210 		tagged->tokens = g_list_remove (tagged->tokens, token);
211 	}
212 }
213 
214 GList *
amp_target_node_get_token(AmpTargetNode * target,AmTokenType type)215 amp_target_node_get_token (AmpTargetNode *target, AmTokenType type)
216 {
217 	return tagged_token_list_get	(target->tokens, type);
218 }
219 
220 GList*
amp_target_node_get_all_token(AmpTargetNode * target)221 amp_target_node_get_all_token (AmpTargetNode *target)
222 {
223 	return tagged_token_list_get_all (target->tokens);
224 }
225 
226 AnjutaTokenType
amp_target_node_get_first_token_type(AmpTargetNode * target)227 amp_target_node_get_first_token_type (AmpTargetNode *target)
228 {
229 	return tagged_token_list_next (target->tokens, 0);
230 }
231 
232 AnjutaTokenType
amp_target_node_get_next_token_type(AmpTargetNode * target,AnjutaTokenType type)233 amp_target_node_get_next_token_type (AmpTargetNode *target, AnjutaTokenType type)
234 {
235 	return tagged_token_list_next (target->tokens, type);
236 }
237 
238 void
amp_target_node_update_node(AmpTargetNode * node,AmpTargetNode * new_node)239 amp_target_node_update_node (AmpTargetNode *node, AmpTargetNode *new_node)
240 {
241 	g_free (node->install);
242 	g_list_free (node->tokens);
243 
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;
249 }
250 
251 /* Get install directory */
252 const gchar *
amp_target_node_get_install_directory(AmpTargetNode * node)253 amp_target_node_get_install_directory (AmpTargetNode *node)
254 {
255 	return node->install;
256 }
257 
258 /* The target has changed which could change its children */
259 void
amp_target_changed(AmpTargetNode * node)260 amp_target_changed (AmpTargetNode *node)
261 {
262 	GList *item;
263 	gboolean custom = FALSE;
264 
265 	for (item = ANJUTA_PROJECT_NODE (node)->properties; item != NULL; item = g_list_next (item))
266 	{
267 		AmpProperty *prop = (AmpProperty *)item->data;
268 
269 		custom = ((AmpPropertyInfo *)prop->base.info)->flags & AM_PROPERTY_PREFIX_OBJECT;
270 		if (custom) break;
271 	}
272 
273 	if (custom)
274 	{
275 		/* Update object name if the target has some custom properties */
276 		AnjutaProjectNode *child;
277 
278 		for (child = anjuta_project_node_first_child (ANJUTA_PROJECT_NODE (node)); child != NULL; child = anjuta_project_node_next_sibling (child))
279 		{
280 			if (anjuta_project_node_get_node_type (child) == ANJUTA_PROJECT_OBJECT)
281 			{
282 				if (child->file != NULL)
283 				{
284 					AnjutaProjectNode *source = anjuta_project_node_first_child (child);
285 
286 					if (source != NULL)
287 					{
288 						gchar *obj_name;
289 						const gchar *obj_ext;
290 
291 						if (child->name != NULL)
292 						{
293 							g_free (child->name);
294 							child->name = NULL;
295 						}
296 						obj_name = g_file_get_basename (child->file);
297 						obj_ext = strrchr (obj_name, '.');
298 						if ((obj_ext != NULL)  && (obj_ext != obj_name))
299 						{
300 							GFile *src_dir;
301 							gchar *src_name;
302 							gchar *src_ext;
303 							gchar *new_name;
304 
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);
310 
311 							g_object_unref (child->file);
312 							child->file = g_file_get_child (src_dir, new_name);
313 
314 							g_free (new_name);
315 							g_free (src_name);
316 							g_object_unref (src_dir);
317 						}
318 						g_free (obj_name);
319 					}
320 				}
321 			}
322 		}
323 	}
324 }
325 
326 AmpTargetNode*
amp_target_node_new(const gchar * name,AnjutaProjectNodeType type,const gchar * install,gint flags)327 amp_target_node_new (const gchar *name, AnjutaProjectNodeType type, const gchar *install, gint flags)
328 {
329 	AmpTargetNode *node;
330 
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);
336 	}
337 	else {
338 		node->install = g_strdup (install);
339 	}
340 	node->flags = flags;
341 
342 	amp_node_property_add_mandatory (ANJUTA_PROJECT_NODE (node));
343 
344 	return node;
345 }
346 
347 AmpTargetNode*
amp_target_node_new_valid(const gchar * name,AnjutaProjectNodeType type,const gchar * install,gint flags,AnjutaProjectNode * parent,GError ** error)348 amp_target_node_new_valid (const gchar *name, AnjutaProjectNodeType type, const gchar *install, gint flags, AnjutaProjectNode *parent, GError **error)
349 {
350 	const gchar *basename;
351 
352 	/* Check parent if present */
353 	if (parent != NULL)
354 	{
355 		if ((anjuta_project_node_get_node_type (parent) == ANJUTA_PROJECT_GROUP) &&
356 		    (amp_group_node_get_makefile_token (AMP_GROUP_NODE (parent)) == NULL))
357 		{
358 			amp_set_error (error, IANJUTA_PROJECT_ERROR_VALIDATION_FAILED,
359 		               _("Target parent is not a valid group"));
360 
361 			return NULL;
362 		}
363 	}
364 
365 	/* Validate target name */
366 	if (!name || strlen (name) <= 0)
367 	{
368 		amp_set_error (error, IANJUTA_PROJECT_ERROR_VALIDATION_FAILED,
369 		               _("Please specify target name"));
370 		return NULL;
371 	}
372 	{
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++;
380 		}
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;
385 		}
386 	}
387 
388 	/* Skip eventual directory name */
389 	basename = strrchr (name, '/');
390 	basename = basename == NULL ? name : basename + 1;
391 
392 
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;
400 		}
401 	}
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;
409 		}
410 	}
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;
417 		}
418 	}
419 
420 	return amp_target_node_new (name, type, install, flags);
421 }
422 
423 void
amp_target_node_free(AmpTargetNode * node)424 amp_target_node_free (AmpTargetNode *node)
425 {
426 	g_object_unref (G_OBJECT (node));
427 }
428 
429 
430 
431 /* AmpNode implementation
432  *---------------------------------------------------------------------------*/
433 
434 static gboolean
amp_target_node_update(AmpNode * node,AmpNode * new_node)435 amp_target_node_update (AmpNode *node, AmpNode *new_node)
436 {
437 	amp_target_node_update_node (AMP_TARGET_NODE (node), AMP_TARGET_NODE (new_node));
438 
439 	return TRUE;
440 }
441 
442 static AmpNode *
amp_target_node_copy(AmpNode * old_node)443 amp_target_node_copy (AmpNode *old_node)
444 {
445 	AmpNode *new_node;
446 
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)));
449 
450 	return new_node;
451 }
452 
453 static gboolean
amp_target_node_write(AmpNode * node,AmpNode * parent,AmpProject * project,GError ** error)454 amp_target_node_write (AmpNode *node, AmpNode *parent, AmpProject *project, GError **error)
455 {
456 	return amp_target_node_create_token (project, AMP_TARGET_NODE (node), error);
457 }
458 
459 static gboolean
amp_target_node_erase(AmpNode * target,AmpNode * parent,AmpProject * project,GError ** error)460 amp_target_node_erase (AmpNode *target, AmpNode *parent, AmpProject *project, GError **error)
461 {
462 	gboolean ok;
463 	GList * token_list;
464 
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);
468 
469 	/* Remove installation directory variable if the removed target was the
470 	 * only one using it */
471 	if (ok)
472 	{
473 		AnjutaProjectNode *node;
474 		const gchar *installdir;
475 		AnjutaProjectProperty *prop;
476 		gboolean used = FALSE;
477 
478 		prop = amp_node_get_property_from_token (ANJUTA_PROJECT_NODE (target), AM_TOKEN__PROGRAMS, 6);
479 		installdir = prop->value;
480 
481 		for (node = anjuta_project_node_first_child (ANJUTA_PROJECT_NODE (parent)); node != NULL; node = anjuta_project_node_next_sibling (node))
482 		{
483 			if (node != ANJUTA_PROJECT_NODE (target))
484 			{
485 				prop = amp_node_get_property_from_token (node, AM_TOKEN__PROGRAMS, 6);
486 				if ((prop != NULL) && (g_strcmp0 (installdir, prop->value) == 0))
487 				{
488 					used = TRUE;
489 					break;
490 				}
491 			}
492 		}
493 
494 		if (!used)
495 		{
496 			GList *item;
497 
498 			for (item = anjuta_project_node_get_properties (ANJUTA_PROJECT_NODE (parent)); item != NULL; item = g_list_next (item))
499 			{
500 				AmpProperty *prop = (AmpProperty *)item->data;
501 
502 				if ((((AmpPropertyInfo *)prop->base.info)->token_type == AM_TOKEN_DIR) && (g_strcmp0 (prop->base.name, installdir) == 0))
503 				{
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;
508 				}
509 			}
510 		}
511 	};
512 
513 	return ok;
514 }
515 
516 
517 
518 /* GObjet implementation
519  *---------------------------------------------------------------------------*/
520 
521 static void
amp_target_node_init(AmpTargetNode * node)522 amp_target_node_init (AmpTargetNode *node)
523 {
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;
531 }
532 
533 static void
amp_target_node_finalize(GObject * object)534 amp_target_node_finalize (GObject *object)
535 {
536 	AmpTargetNode *node = AMP_TARGET_NODE (object);
537 
538 	tagged_token_list_free (node->tokens);
539 	node->tokens = NULL;
540 
541 	G_OBJECT_CLASS (amp_target_node_parent_class)->finalize (object);
542 }
543 
544 static void
amp_target_node_class_init(AmpTargetNodeClass * klass)545 amp_target_node_class_init (AmpTargetNodeClass *klass)
546 {
547 	GObjectClass* object_class = G_OBJECT_CLASS (klass);
548 	AmpNodeClass* node_class;
549 
550 	object_class->finalize = amp_target_node_finalize;
551 
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;
557 }
558 
559 static void
amp_target_node_class_finalize(AmpTargetNodeClass * klass)560 amp_target_node_class_finalize (AmpTargetNodeClass *klass)
561 {
562 }
563 
564 void
amp_target_node_register(GTypeModule * module)565 amp_target_node_register (GTypeModule *module)
566 {
567 	amp_target_node_register_type (module);
568 }
569