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., 59 Temple Place - Suite 330,
19 * Boston, MA 02111-1307, USA.
27 #include "amp-group.h"
30 #include "am-scanner.h"
31 #include "am-properties.h"
32 #include "am-writer.h"
35 #include <libanjuta/interfaces/ianjuta-project.h>
37 #include <libanjuta/anjuta-debug.h>
39 #include <glib/gi18n.h>
47 *---------------------------------------------------------------------------*/
50 error_set (GError
**error
, gint code
, const gchar
*message
)
56 /* error already created, just change the code
57 * and prepend the string */
58 (*error
)->code
= code
;
59 tmp
= (*error
)->message
;
60 (*error
)->message
= g_strconcat (message
, "\n\n", tmp
, NULL
);
64 *error
= g_error_new_literal (IANJUTA_PROJECT_ERROR
,
72 *---------------------------------------------------------------------------*/
74 /* Find if pkg-config modules are used in group targets */
76 project_load_group_module (AmpProject
*project
, AmpGroupNode
*group
)
78 AnjutaProjectNode
*target
;
79 AnjutaProjectProperty
*prop
;
80 gchar
**group_cpp
= NULL
;
82 prop
= amp_node_get_property_from_token (ANJUTA_PROJECT_NODE (group
), AM_TOKEN__CPPFLAGS
, 0);
83 if (prop
&& (prop
->value
!= NULL
)) group_cpp
= g_strsplit_set (prop
->value
, " \t", 0);
85 /* Check all targets */
86 for (target
= anjuta_project_node_first_child (ANJUTA_PROJECT_NODE (group
)); target
!= NULL
; target
= anjuta_project_node_next_sibling (target
))
88 gint type
= anjuta_project_node_get_full_type (target
) & (ANJUTA_PROJECT_ID_MASK
| ANJUTA_PROJECT_TYPE_MASK
);
89 gchar
**target_lib
= NULL
;
90 gchar
**target_cpp
= NULL
;
95 case ANJUTA_PROJECT_TARGET
| ANJUTA_PROJECT_PROGRAM
:
96 prop
= amp_node_get_property_from_token (target
, AM_TOKEN_TARGET_LDADD
, 0);
98 case ANJUTA_PROJECT_TARGET
| ANJUTA_PROJECT_STATICLIB
:
99 case ANJUTA_PROJECT_TARGET
| ANJUTA_PROJECT_SHAREDLIB
:
100 case ANJUTA_PROJECT_TARGET
| ANJUTA_PROJECT_LT_MODULE
:
101 prop
= amp_node_get_property_from_token (target
, AM_TOKEN_TARGET_LIBADD
, 0);
106 if (prop
&& (prop
->value
!= NULL
)) target_lib
= g_strsplit_set (prop
->value
, " \t", 0);
108 /* Check if targets use libraries */
109 if (target_lib
!= NULL
)
111 AnjutaProjectNode
*module
;
113 prop
= amp_node_get_property_from_token (target
, AM_TOKEN_TARGET_CPPFLAGS
, 0);
114 if (prop
&& (prop
->value
!= NULL
)) target_cpp
= g_strsplit_set (prop
->value
, " \t", 0);
116 for (module
= anjuta_project_node_first_child (ANJUTA_PROJECT_NODE (project
)); module
!= NULL
; module
= anjuta_project_node_next_sibling (module
))
118 if (anjuta_project_node_get_node_type (module
) == ANJUTA_PROJECT_MODULE
)
120 const gchar
*name
= anjuta_project_node_get_name (module
);
121 gchar
*lib_flags
= g_strconcat ("$(", name
, "_LIBS)", NULL
);
124 for (flags
= target_lib
; *flags
!= NULL
; flags
++)
127 if (strcmp (*flags
, lib_flags
) == 0)
129 gchar
*cpp_flags
= g_strconcat ("$(", name
, "_CFLAGS)", NULL
);
131 gboolean found
= FALSE
;
133 if (group_cpp
!= NULL
)
135 for (cflags
= group_cpp
; *cflags
!= NULL
; cflags
++)
137 if (strcmp (*cflags
, cpp_flags
) == 0)
144 if ((target_cpp
!= NULL
) && !found
)
146 for (cflags
= target_cpp
; *cflags
!= NULL
; cflags
++)
148 if (strcmp (*cflags
, cpp_flags
) == 0)
158 AnjutaProjectNode
*new_module
;
160 new_module
= amp_node_new_valid (target
, ANJUTA_PROJECT_MODULE
, NULL
, name
, NULL
);
161 anjuta_project_node_append (target
, new_module
);
169 g_strfreev (target_cpp
);
170 g_strfreev (target_lib
);
173 g_strfreev (group_cpp
);
179 extern const gchar
*valid_am_makefiles
[];
182 project_load_makefile (AmpProject
*project
, AmpGroupNode
*group
)
184 const gchar
**filename
;
185 AnjutaTokenFile
*tfile
;
186 GFile
*makefile
= NULL
;
187 GFile
*file
= anjuta_project_node_get_file ((AnjutaProjectNode
*)group
);
189 /* Find makefile name
190 * It has to be in the config_files list with .am extension */
191 for (filename
= valid_am_makefiles
; *filename
!= NULL
; filename
++)
193 makefile
= g_file_get_child (file
, *filename
);
194 if (file_type (file
, *filename
) == G_FILE_TYPE_REGULAR
)
196 gchar
*final_filename
= g_strdup (*filename
);
201 ptr
= g_strrstr (final_filename
,".am");
202 if (ptr
!= NULL
) *ptr
= '\0';
203 final_file
= g_file_get_child (file
, final_filename
);
204 g_free (final_filename
);
206 token
= amp_project_get_config_token (project
, final_file
);
207 g_object_unref (final_file
);
210 amp_group_node_add_token (group
, token
, AM_GROUP_TOKEN_CONFIGURE
);
214 g_object_unref (makefile
);
217 if (*filename
== NULL
)
219 /* Unable to find automake file */
223 /* Parse makefile.am */
224 DEBUG_PRINT ("Parse: %s", g_file_get_uri (makefile
));
225 tfile
= amp_group_node_set_makefile (group
, makefile
, project
);
227 project_load_group_module (project
, group
);
234 *---------------------------------------------------------------------------*/
237 amp_variable_new (gchar
*name
, AnjutaTokenType assign
, AnjutaToken
*value
)
239 AmpVariable
*variable
= NULL
;
241 g_return_val_if_fail (name
!= NULL
, NULL
);
243 variable
= g_slice_new0(AmpVariable
);
244 variable
->name
= g_strdup (name
);
245 variable
->assign
= assign
;
246 variable
->value
= value
;
252 amp_variable_free (AmpVariable
*variable
)
254 g_free (variable
->name
);
256 g_slice_free (AmpVariable
, variable
);
262 *---------------------------------------------------------------------------*/
266 amp_group_node_add_token (AmpGroupNode
*group
, AnjutaToken
*token
, AmpGroupNodeTokenCategory category
)
268 if (token
!= NULL
) group
->tokens
[category
] = g_list_prepend (group
->tokens
[category
], token
);
272 amp_group_node_remove_token (AmpGroupNode
*group
, AnjutaToken
*token
)
276 for (i
= 0; i
< AM_GROUP_TOKEN_LAST
; i
++)
278 group
->tokens
[i
] = g_list_remove (group
->tokens
[i
], token
);
283 amp_group_node_get_token (AmpGroupNode
*group
, AmpGroupNodeTokenCategory category
)
285 return group
->tokens
[category
];
289 amp_group_node_get_all_token (AmpGroupNode
*group
)
292 GList
*tokens
= NULL
;
294 for (i
= 0; i
< AM_GROUP_TOKEN_LAST
; i
++)
296 tokens
= g_list_concat (tokens
, g_list_copy (group
->tokens
[i
]));
303 amp_group_node_get_first_token (AmpGroupNode
*group
, AmpGroupNodeTokenCategory category
)
307 list
= amp_group_node_get_token (group
, category
);
308 if (list
== NULL
) return NULL
;
310 return (AnjutaToken
*)list
->data
;
314 amp_group_node_set_dist_only (AmpGroupNode
*group
, gboolean dist_only
)
316 group
->dist_only
= dist_only
;
320 on_group_monitor_changed (GFileMonitor
*monitor
,
323 GFileMonitorEvent event_type
,
326 AnjutaProjectNode
*node
= ANJUTA_PROJECT_NODE (data
);
327 AnjutaProjectNode
*root
;
329 switch (event_type
) {
330 case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT
:
331 case G_FILE_MONITOR_EVENT_CHANGED
:
332 case G_FILE_MONITOR_EVENT_DELETED
:
333 /* project can be NULL, if the node is dummy node because the
334 * original one is reloaded. */
335 root
= anjuta_project_node_root (node
);
336 if (root
!= NULL
) g_signal_emit_by_name (G_OBJECT (root
), "file-changed", data
);
344 amp_group_node_update_preset_variable (AmpGroupNode
*group
)
351 AnjutaProjectNode
*node
;
353 if (group
->preset_token
!= NULL
) anjuta_token_free (group
->preset_token
);
354 group
->preset_token
= anjuta_token_new_static (ANJUTA_TOKEN_FILE
, NULL
);
356 /* Get project root */
357 for (node
= ANJUTA_PROJECT_NODE (group
); anjuta_project_node_parent (node
) != NULL
; node
= anjuta_project_node_parent (node
));
358 root
= anjuta_project_node_get_file (node
);
360 /* Set source directory variables */
361 file
= anjuta_project_node_get_file (group
);
362 value
= anjuta_token_insert_token_list (FALSE
, NULL
,
363 ANJUTA_TOKEN_LIST
, NULL
,
364 ANJUTA_TOKEN_ARGUMENT
, NULL
,
365 ANJUTA_TOKEN_CONTENT
, ".",
367 anjuta_token_append_child (group
->preset_token
, value
);
368 var
= amp_variable_new ("srcdir", 0, value
);
369 g_hash_table_insert (group
->variables
, var
->name
, var
);
370 var
= amp_variable_new ("builddir", 0, value
);
371 g_hash_table_insert (group
->variables
, var
->name
, var
);
373 path
= g_file_get_path (file
);
374 value
= anjuta_token_insert_token_list (FALSE
, NULL
,
375 ANJUTA_TOKEN_LIST
, NULL
,
376 ANJUTA_TOKEN_ARGUMENT
, NULL
,
377 ANJUTA_TOKEN_CONTENT
, path
,
380 anjuta_token_append_child (group
->preset_token
, value
);
381 var
= amp_variable_new ("abs_srcdir", 0, value
);
382 g_hash_table_insert (group
->variables
, var
->name
, var
);
383 var
= amp_variable_new ("abs_builddir", 0, value
);
384 g_hash_table_insert (group
->variables
, var
->name
, var
);
386 path
= get_relative_path (file
, root
);
387 value
= anjuta_token_insert_token_list (FALSE
, NULL
,
388 ANJUTA_TOKEN_LIST
, NULL
,
389 ANJUTA_TOKEN_ARGUMENT
, NULL
,
390 ANJUTA_TOKEN_CONTENT
, path
,
393 anjuta_token_append_child (group
->preset_token
, value
);
394 var
= amp_variable_new ("top_srcdir", 0, value
);
395 g_hash_table_insert (group
->variables
, var
->name
, var
);
396 var
= amp_variable_new ("top_builddir", 0, value
);
397 g_hash_table_insert (group
->variables
, var
->name
, var
);
399 path
= g_file_get_path (root
);
400 value
= anjuta_token_insert_token_list (FALSE
, NULL
,
401 ANJUTA_TOKEN_LIST
, NULL
,
402 ANJUTA_TOKEN_ARGUMENT
, NULL
,
403 ANJUTA_TOKEN_CONTENT
, path
,
406 anjuta_token_append_child (group
->preset_token
, value
);
407 var
= amp_variable_new ("abs_top_srcdir", 0, value
);
408 g_hash_table_insert (group
->variables
, var
->name
, var
);
409 var
= amp_variable_new ("abs_top_builddir", 0, value
);
410 g_hash_table_insert (group
->variables
, var
->name
, var
);
414 amp_group_node_set_makefile (AmpGroupNode
*group
, GFile
*makefile
, AmpProject
*project
)
416 if (group
->makefile
!= NULL
) g_object_unref (group
->makefile
);
417 if (group
->tfile
!= NULL
) anjuta_token_file_free (group
->tfile
);
418 if (makefile
!= NULL
)
421 AmpAmScanner
*scanner
;
422 AnjutaProjectNode
*source
;
424 group
->makefile
= g_object_ref (makefile
);
425 group
->tfile
= anjuta_token_file_new (makefile
);
426 source
= amp_source_node_new (makefile
, ANJUTA_PROJECT_PROJECT
| ANJUTA_PROJECT_FRAME
| ANJUTA_PROJECT_READ_ONLY
);
427 anjuta_project_node_append (ANJUTA_PROJECT_NODE (group
), source
);
429 token
= anjuta_token_file_load (group
->tfile
, NULL
);
430 amp_project_add_file (project
, makefile
, group
->tfile
);
432 amp_group_node_update_preset_variable (group
);
434 scanner
= amp_am_scanner_new (project
, group
);
435 group
->make_token
= amp_am_scanner_parse_token (scanner
, anjuta_token_new_static (ANJUTA_TOKEN_FILE
, NULL
), token
, makefile
, NULL
);
436 amp_am_scanner_free (scanner
);
438 group
->monitor
= g_file_monitor_file (makefile
,
442 if (group
->monitor
!= NULL
)
444 g_signal_connect (G_OBJECT (group
->monitor
),
446 G_CALLBACK (on_group_monitor_changed
),
452 group
->makefile
= NULL
;
454 group
->make_token
= NULL
;
455 if (group
->monitor
) g_object_unref (group
->monitor
);
456 group
->monitor
= NULL
;
463 amp_group_node_get_makefile_token (AmpGroupNode
*group
)
465 return group
->make_token
;
469 amp_group_node_get_make_token_file (AmpGroupNode
*group
)
475 amp_group_node_update_makefile (AmpGroupNode
*group
, AnjutaToken
*token
)
477 return anjuta_token_file_update (group
->tfile
, token
);
481 amp_group_node_get_makefile_name (AmpGroupNode
*group
)
483 gchar
*basename
= NULL
;
485 if (group
->makefile
!= NULL
)
487 basename
= g_file_get_basename (group
->makefile
);
494 amp_group_node_update_node (AmpGroupNode
*group
, AmpGroupNode
*new_group
)
499 if (group
->monitor
!= NULL
)
501 g_object_unref (group
->monitor
);
502 group
->monitor
= NULL
;
504 if (group
->makefile
!= NULL
)
506 g_object_unref (group
->makefile
);
507 group
->monitor
= NULL
;
509 if (group
->preset_token
!= NULL
)
511 anjuta_token_free (group
->preset_token
);
512 group
->preset_token
= NULL
;
514 if (group
->tfile
) anjuta_token_file_free (group
->tfile
);
515 for (i
= 0; i
< AM_GROUP_TOKEN_LAST
; i
++)
517 if (group
->tokens
[i
] != NULL
) g_list_free (group
->tokens
[i
]);
519 if (group
->variables
) g_hash_table_remove_all (group
->variables
);
521 group
->dist_only
= new_group
->dist_only
;
522 group
->makefile
= new_group
->makefile
;
523 new_group
->makefile
= NULL
;
524 group
->tfile
= new_group
->tfile
;
525 new_group
->tfile
= NULL
;
526 group
->make_token
= new_group
->make_token
;
527 new_group
->make_token
= NULL
;
528 group
->preset_token
= new_group
->preset_token
;
529 new_group
->preset_token
= NULL
;
530 memcpy (group
->tokens
, new_group
->tokens
, sizeof (group
->tokens
));
531 memset (new_group
->tokens
, 0, sizeof (new_group
->tokens
));
532 hash
= group
->variables
;
533 group
->variables
= new_group
->variables
;
534 new_group
->variables
= hash
;
536 if (group
->makefile
!= NULL
)
538 group
->monitor
= g_file_monitor_file (group
->makefile
,
542 if (group
->monitor
!= NULL
)
544 g_signal_connect (G_OBJECT (group
->monitor
),
546 G_CALLBACK (on_group_monitor_changed
),
553 amp_group_node_update_variable (AmpGroupNode
*group
, AnjutaToken
*variable
)
557 AnjutaToken
*value
= NULL
;
560 arg
= anjuta_token_first_item (variable
);
561 name
= g_strstrip (anjuta_token_evaluate (arg
));
562 value
= anjuta_token_last_item (variable
);
564 var
= (AmpVariable
*)g_hash_table_lookup (group
->variables
, name
);
571 var
= amp_variable_new (name
, 0, value
);
572 g_hash_table_insert (group
->variables
, var
->name
, var
);
575 if (name
) g_free (name
);
579 amp_group_node_get_variable_token (AmpGroupNode
*group
, const gchar
*name
)
583 var
= g_hash_table_lookup (group
->variables
, name
);
585 return var
!= NULL
? var
->value
: NULL
;
589 amp_group_node_set_file (AmpGroupNode
*group
, GFile
*new_file
)
591 if (group
->base
.file
!= NULL
) g_object_unref (group
->base
.file
);
592 g_free (group
->base
.name
);
593 group
->base
.name
= NULL
;
594 group
->base
.file
= g_object_ref (new_file
);
600 amp_group_node_new (GFile
*file
, const gchar
*name
, gboolean dist_only
)
602 AmpGroupNode
*node
= NULL
;
604 node
= g_object_new (AMP_TYPE_GROUP_NODE
, NULL
);
605 node
->base
.file
= g_object_ref (file
);
606 node
->base
.name
= g_strdup (name
);
607 node
->dist_only
= dist_only
;
608 memset (node
->tokens
, 0, sizeof (node
->tokens
));
614 amp_group_node_new_valid (GFile
*file
, const gchar
*name
, gboolean dist_only
, GError
**error
)
616 /* Validate group name */
617 if (!name
|| strlen (name
) <= 0)
620 error_set (error
, IANJUTA_PROJECT_ERROR_VALIDATION_FAILED
,
621 _("Please specify group name"));
625 gboolean failed
= FALSE
;
626 const gchar
*ptr
= name
;
628 if (!isalnum (*ptr
) && (strchr ("#$:%+,-.=@^_`~/", *ptr
) == NULL
))
634 error_set (error
, IANJUTA_PROJECT_ERROR_VALIDATION_FAILED
,
635 _("Group name can only contain alphanumeric or \"#$:%+,-.=@^_`~/\" characters"));
640 return amp_group_node_new (file
, name
, dist_only
);
644 amp_group_node_free (AmpGroupNode
*node
)
646 g_object_unref (G_OBJECT (node
));
649 /* AmpNode implementation
650 *---------------------------------------------------------------------------*/
653 amp_group_node_load (AmpNode
*group
, AmpNode
*parent
, AmpProject
*project
, GError
**error
)
655 if (project_load_makefile (project
, AMP_GROUP_NODE (group
)) == NULL
)
657 g_set_error (error
, IANJUTA_PROJECT_ERROR
,
658 IANJUTA_PROJECT_ERROR_DOESNT_EXIST
,
659 _("Project doesn't exist or invalid path"));
668 amp_group_node_save (AmpNode
*group
, AmpNode
*parent
, AmpProject
*project
, GError
**error
)
670 AnjutaTokenFile
*tfile
;
671 AnjutaProjectNode
*child
;
675 /* Create directory */
676 directory
= g_file_get_parent (AMP_GROUP_NODE (group
)->makefile
);
677 g_file_make_directory (directory
, NULL
, NULL
);
678 g_object_unref (directory
);
681 tfile
= AMP_GROUP_NODE (group
)->tfile
;
684 /* Create an empty makefile */
685 g_file_replace_contents (AMP_GROUP_NODE (group
)->makefile
, "", 0, NULL
, FALSE
, G_FILE_CREATE_NONE
, NULL
, NULL
, NULL
);
690 if (anjuta_token_file_is_dirty (tfile
))
692 if (!anjuta_token_file_save (tfile
, error
)) return FALSE
;
695 /* Save all children */
696 for (child
= anjuta_project_node_first_child (ANJUTA_PROJECT_NODE (group
)); child
!= NULL
; child
= anjuta_project_node_next_sibling (child
))
698 /* Try to save all children even if some fail */
699 if (!amp_node_save (AMP_NODE (child
), group
, project
, error
)) ok
= FALSE
;
706 amp_group_node_update (AmpNode
*node
, AmpNode
*new_node
)
708 amp_group_node_update_node (AMP_GROUP_NODE (node
), AMP_GROUP_NODE (new_node
));
714 amp_group_node_write (AmpNode
*node
, AmpNode
*parent
, AmpProject
*project
, GError
**error
)
716 return amp_group_node_create_token (project
, AMP_GROUP_NODE (node
), error
);
720 amp_group_node_erase (AmpNode
*node
, AmpNode
*parent
, AmpProject
*project
, GError
**error
)
722 return amp_group_node_delete_token (project
, AMP_GROUP_NODE (node
), error
);
727 /* GObjet implementation
728 *---------------------------------------------------------------------------*/
731 G_DEFINE_DYNAMIC_TYPE (AmpGroupNode
, amp_group_node
, AMP_TYPE_NODE
);
734 amp_group_node_init (AmpGroupNode
*node
)
736 node
->base
.type
= ANJUTA_PROJECT_GROUP
;
737 node
->base
.properties_info
= amp_get_group_property_list();
738 node
->base
.state
= ANJUTA_PROJECT_CAN_ADD_GROUP
|
739 ANJUTA_PROJECT_CAN_ADD_TARGET
|
740 ANJUTA_PROJECT_CAN_REMOVE
|
741 ANJUTA_PROJECT_CAN_SAVE
;
742 node
->dist_only
= FALSE
;
743 node
->variables
= NULL
;
744 node
->makefile
= NULL
;
745 node
->variables
= g_hash_table_new_full (g_str_hash
, g_str_equal
, NULL
, (GDestroyNotify
)amp_variable_free
);
746 node
->monitor
= NULL
;
747 memset (node
->tokens
, 0, sizeof (node
->tokens
));
748 node
->preset_token
= NULL
;
752 amp_group_node_dispose (GObject
*object
)
754 AmpGroupNode
*node
= AMP_GROUP_NODE (object
);
756 if (node
->monitor
) g_object_unref (node
->monitor
);
757 node
->monitor
= NULL
;
759 if (node
->preset_token
) anjuta_token_free (node
->preset_token
);
760 node
->preset_token
= NULL
;
762 G_OBJECT_CLASS (amp_group_node_parent_class
)->dispose (object
);
766 amp_group_node_finalize (GObject
*object
)
768 AmpGroupNode
*node
= AMP_GROUP_NODE (object
);
771 if (node
->tfile
) anjuta_token_file_free (node
->tfile
);
772 if (node
->makefile
) g_object_unref (node
->makefile
);
774 for (i
= 0; i
< AM_GROUP_TOKEN_LAST
; i
++)
776 if (node
->tokens
[i
] != NULL
) g_list_free (node
->tokens
[i
]);
778 if (node
->variables
) g_hash_table_destroy (node
->variables
);
780 G_OBJECT_CLASS (amp_group_node_parent_class
)->finalize (object
);
784 amp_group_node_class_init (AmpGroupNodeClass
*klass
)
786 GObjectClass
* object_class
= G_OBJECT_CLASS (klass
);
787 AmpNodeClass
* node_class
;
789 object_class
->finalize
= amp_group_node_finalize
;
790 object_class
->dispose
= amp_group_node_dispose
;
792 node_class
= AMP_NODE_CLASS (klass
);
793 node_class
->load
= amp_group_node_load
;
794 node_class
->save
= amp_group_node_save
;
795 node_class
->update
= amp_group_node_update
;
796 node_class
->write
= amp_group_node_write
;
797 node_class
->erase
= amp_group_node_erase
;
801 amp_group_node_class_finalize (AmpGroupNodeClass
*klass
)
806 amp_group_node_register (GTypeModule
*module
)
808 amp_group_node_register_type (module
);