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