1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4; coding: utf-8 -*- */
2 /* mk-rule.c
3 *
4 * Copyright (C) 2009 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 "mk-rule.h"
28
29 #include "mk-project-private.h"
30 #include "mk-scanner.h"
31
32 #include <string.h>
33 #include <stdio.h>
34
35 /* Rule object
36 *---------------------------------------------------------------------------*/
37
38 /* Maximum level of dependencies when searching for source files */
39 #define MAX_DEPENDENCIES 16
40
41 /* Rule object
42 *---------------------------------------------------------------------------*/
43
44 static MkpRule*
mkp_rule_new(gchar * name,AnjutaToken * token)45 mkp_rule_new (gchar *name, AnjutaToken *token)
46 {
47 MkpRule *rule = NULL;
48
49 g_return_val_if_fail (name != NULL, NULL);
50
51 rule = g_slice_new0(MkpRule);
52 rule->name = g_strdup (name);
53 rule->rule = token;
54
55 return rule;
56 }
57
58 static void
mkp_rule_free(MkpRule * rule)59 mkp_rule_free (MkpRule *rule)
60 {
61 g_free (rule->name);
62 g_list_foreach (rule->prerequisite, (GFunc)g_free, NULL);
63 g_list_free (rule->prerequisite);
64
65 g_slice_free (MkpRule, rule);
66 }
67
68 /* Private functions
69 *---------------------------------------------------------------------------*/
70
71 /* Find all dependencies for target checking pattern rule. If no source is found,
72 * return target, else free target and return a newly allocated source name */
73
74 static GList *
mkp_project_find_dependencies(MkpProject * project,gchar * target,AnjutaProjectNode * parent,guint backtrack)75 mkp_project_find_dependencies (MkpProject *project, gchar *target, AnjutaProjectNode *parent, guint backtrack)
76 {
77 GFile *child;
78 gboolean exist;
79 GHashTableIter iter;
80 gchar *key;
81 MkpRule *rule;
82
83 /* Check pattern rules */
84 if (backtrack < MAX_DEPENDENCIES)
85 {
86 for (g_hash_table_iter_init (&iter, project->rules); g_hash_table_iter_next (&iter, (gpointer)&key, (gpointer)&rule);)
87 {
88 if (rule->pattern)
89 {
90 gchar *source;
91 GList *dependencies;
92
93 if (rule->part == NULL)
94 {
95 /* simple suffix rule */
96 source = g_strconcat (target, rule->name, NULL);
97 }
98 else
99 {
100 /* double suffix rule */
101 if (strcmp (target + strlen (target) - strlen (rule->part), rule->part) == 0)
102 {
103 gchar *suffix;
104
105 source = g_strconcat (target, rule->name, NULL);
106 suffix = source + strlen (target) - strlen (rule->part);
107
108 memcpy (suffix, rule->name, rule->part - rule->name);
109 *(suffix + (rule->part - rule->name)) = '\0';
110 }
111 else
112 {
113 continue;
114 }
115 }
116
117 dependencies = mkp_project_find_dependencies (project, source, parent, backtrack + 1);
118 if (dependencies != NULL)
119 {
120 return g_list_prepend (dependencies, target);
121 }
122 g_free (source);
123 }
124 }
125 }
126
127 child = g_file_get_child (anjuta_project_node_get_file (parent), target);
128 exist = g_file_query_exists (child, NULL);
129 //g_message ("target =%s= filename =%s=", target, g_file_get_parse_name (child));
130 g_object_unref (child);
131
132 if (!exist)
133 {
134 return NULL;
135 }
136 else
137 {
138 return g_list_prepend (NULL, target);
139 }
140 }
141
142
143 /* Parser functions
144 *---------------------------------------------------------------------------*/
145
146 void
mkp_project_add_rule(MkpProject * project,AnjutaToken * group)147 mkp_project_add_rule (MkpProject *project, AnjutaToken *group)
148 {
149 AnjutaToken *targ;
150 AnjutaToken *dep;
151 AnjutaToken *arg;
152 gboolean double_colon = FALSE;
153
154 //fprintf(stdout, "add rule\n");
155 //anjuta_token_dump (group);
156
157 targ = anjuta_token_first_item (group);
158 arg = anjuta_token_next_word (targ);
159 if (anjuta_token_get_type (arg) == MK_TOKEN_DOUBLE_COLON) double_colon = TRUE;
160 dep = anjuta_token_next_word (arg);
161 for (arg = anjuta_token_first_word (targ); arg != NULL; arg = anjuta_token_next_word (arg))
162 {
163 AnjutaToken *src = NULL;
164 gchar *target = NULL;
165 gboolean order = FALSE;
166 gboolean no_token = TRUE;
167 MkpRule *rule = NULL;
168
169 switch (anjuta_token_get_type (arg))
170 {
171 case MK_TOKEN__PHONY:
172 for (src = anjuta_token_first_word (dep); src != NULL; src = anjuta_token_next_word (src))
173 {
174 if (anjuta_token_get_type (src) != MK_TOKEN_ORDER)
175 {
176 target = anjuta_token_evaluate (src);
177
178 rule = g_hash_table_lookup (project->rules, target);
179 if (rule == NULL)
180 {
181 rule = mkp_rule_new (target, NULL);
182 g_hash_table_insert (project->rules, rule->name, rule);
183 }
184 rule->phony = TRUE;
185
186 //g_message (" with target %s", target);
187 if (target != NULL) g_free (target);
188 }
189 }
190 break;
191 case MK_TOKEN__SUFFIXES:
192 for (src = anjuta_token_first_word (dep); src != NULL; src = anjuta_token_next_word (src))
193 {
194 if (anjuta_token_get_type (src) != MK_TOKEN_ORDER)
195 {
196 gchar *suffix;
197
198 suffix = anjuta_token_evaluate (src);
199 /* The pointer value must only be not NULL, it does not matter if it is
200 * invalid */
201 g_hash_table_replace (project->suffix, suffix, suffix);
202 //g_message (" with suffix %s", suffix);
203 no_token = FALSE;
204 }
205 }
206 if (no_token == TRUE)
207 {
208 /* Clear all suffix */
209 g_hash_table_remove_all (project->suffix);
210 }
211 break;
212 case MK_TOKEN__DEFAULT:
213 case MK_TOKEN__PRECIOUS:
214 case MK_TOKEN__INTERMEDIATE:
215 case MK_TOKEN__SECONDARY:
216 case MK_TOKEN__SECONDEXPANSION:
217 case MK_TOKEN__DELETE_ON_ERROR:
218 case MK_TOKEN__IGNORE:
219 case MK_TOKEN__LOW_RESOLUTION_TIME:
220 case MK_TOKEN__SILENT:
221 case MK_TOKEN__EXPORT_ALL_VARIABLES:
222 case MK_TOKEN__NOTPARALLEL:
223 /* Do nothing with these targets, just ignore them */
224 break;
225 default:
226 target = g_strstrip (anjuta_token_evaluate (arg));
227 if (*target == '\0') break;
228 //g_message ("add rule =%s=", target);
229
230 rule = g_hash_table_lookup (project->rules, target);
231 if (rule == NULL)
232 {
233 rule = mkp_rule_new (target, group);
234 g_hash_table_insert (project->rules, rule->name, rule);
235 }
236 else
237 {
238 rule->rule = group;
239 }
240
241 for (src = anjuta_token_first_word (dep); src != NULL; src = anjuta_token_next_word (src))
242 {
243 gchar *src_name = anjuta_token_evaluate (src);
244
245 if (src_name != NULL)
246 {
247 //g_message (" with source %s", src_name);
248 if (anjuta_token_get_type (src) == MK_TOKEN_ORDER)
249 {
250 order = TRUE;
251 }
252 rule->prerequisite = g_list_prepend (rule->prerequisite, src_name);
253 }
254 }
255
256 if (target != NULL) g_free (target);
257 }
258 }
259 }
260
261 /* Public functions
262 *---------------------------------------------------------------------------*/
263
264 void
mkp_project_enumerate_targets(MkpProject * project,AnjutaProjectNode * parent)265 mkp_project_enumerate_targets (MkpProject *project, AnjutaProjectNode *parent)
266 {
267 GHashTableIter iter;
268 gpointer key;
269 MkpRule *rule;
270
271 /* Check pattern rules */
272 for (g_hash_table_iter_init (&iter, project->rules); g_hash_table_iter_next (&iter, (gpointer)&key, (gpointer)&rule);)
273 {
274 if (rule->phony) continue;
275
276 if (g_hash_table_lookup (project->suffix, rule->name))
277 {
278 /* Single suffix rule */
279 rule->pattern = TRUE;
280 rule->part = NULL;
281 }
282 else
283 {
284 GString *pattern = g_string_sized_new (16);
285 GList *suffix;
286 GList *src;
287
288 /* Check double suffix rule */
289 suffix = g_hash_table_get_keys (project->suffix);
290
291 for (src = g_list_first (suffix); src != NULL; src = g_list_next (src))
292 {
293 GList *obj;
294
295 for (obj = g_list_first (suffix); obj != NULL; obj = g_list_next (obj))
296 {
297 g_string_assign (pattern, src->data);
298 g_string_append (pattern, obj->data);
299
300 if (strcmp (pattern->str, rule->name) == 0)
301 {
302 rule->pattern = TRUE;
303 rule->part = rule->name + strlen (src->data);
304 break;
305 }
306 }
307 if (rule->pattern) break;
308 }
309
310 g_string_free (pattern, TRUE);
311 g_list_free (suffix);
312 }
313 }
314
315 /* Create new target */
316 for (g_hash_table_iter_init (&iter, project->rules); g_hash_table_iter_next (&iter, (gpointer)&key, (gpointer)&rule);)
317 {
318 MkpTarget *target;
319 AnjutaToken *prerequisite;
320 AnjutaToken *arg;
321
322 //g_message ("rule =%s=", rule->name);
323 if (rule->phony || rule->pattern) continue;
324
325 /* Create target */
326 target = MKP_TARGET(mkp_target_new (rule->name, ANJUTA_PROJECT_UNKNOWN));
327 mkp_target_add_token (target, rule->rule);
328 anjuta_project_node_append (parent, ANJUTA_PROJECT_NODE(target));
329
330 /* Get prerequisite */
331 prerequisite = anjuta_token_first_word (rule->rule);
332 if (prerequisite != NULL) prerequisite = anjuta_token_next_word (prerequisite);
333 if (prerequisite != NULL) prerequisite = anjuta_token_next_word (prerequisite);
334
335 /* Add prerequisite */
336 for (arg = anjuta_token_first_word (prerequisite); arg != NULL; arg = anjuta_token_next_word (arg))
337 {
338 AnjutaProjectNode *node;
339 gchar *name;
340 GList *dependencies;
341
342 name = anjuta_token_evaluate (arg);
343 if (name != NULL)
344 {
345 name = g_strstrip (name);
346 dependencies = mkp_project_find_dependencies (project, name, parent, 0);
347 if (dependencies == NULL)
348 {
349 /* Add only one object node */
350 node = mkp_object_new (name);
351 node->type = ANJUTA_PROJECT_OBJECT | ANJUTA_PROJECT_PROJECT;
352 anjuta_project_node_append (ANJUTA_PROJECT_NODE(target), ANJUTA_PROJECT_NODE(node));
353 g_free (name);
354 }
355 else
356 {
357 GFile *src_file;
358 gchar *name;
359
360 AnjutaProjectNode *parent = (AnjutaProjectNode *)target;
361 while (g_list_next (dependencies) != NULL)
362 {
363 /* Create object nodes */
364 name = (gchar *)dependencies->data;
365 node = mkp_object_new (name);
366 node->type = ANJUTA_PROJECT_OBJECT | ANJUTA_PROJECT_PROJECT;
367 anjuta_project_node_append (parent, node);
368 g_free (name);
369 parent = node;
370 dependencies = g_list_delete_link (dependencies, dependencies);
371 }
372
373 /* Create source node */
374 name = (gchar *)dependencies->data;
375 src_file = g_file_get_child (project->root_file, name);
376 node = mkp_source_new (src_file);
377 node->type = ANJUTA_PROJECT_SOURCE | ANJUTA_PROJECT_PROJECT;
378 g_object_unref (src_file);
379 anjuta_project_node_append (parent, node);
380 g_free (name);
381 g_list_free (dependencies);
382 }
383 }
384 }
385 }
386 }
387
388 void
mkp_project_init_rules(MkpProject * project)389 mkp_project_init_rules (MkpProject *project)
390 {
391 project->rules = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, (GDestroyNotify)mkp_rule_free);
392 project->suffix = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
393 }
394
395 void
mkp_project_free_rules(MkpProject * project)396 mkp_project_free_rules (MkpProject *project)
397 {
398 if (project->rules) g_hash_table_destroy (project->rules);
399 project->rules = NULL;
400 if (project->suffix) g_hash_table_destroy (project->suffix);
401 project->suffix = NULL;
402 }
403
404