2 * Copyright (c) 2000-2005, Darren Hiebert
4 * This source code is released for free distribution under the terms of the
5 * GNU General Public License version 2 or (at your option) any later version.
7 * This module contains functions for generating tags for makefiles.
13 #include "general.h" /* must always come first */
35 K_MACRO
, K_TARGET
, K_INCLUDE
,
43 static roleDefinition MakeMakefileRoles
[] = {
44 { true, "included", "included" },
45 { true, "optional", "optionally included"},
48 static kindDefinition MakeKinds
[] = {
49 { true, 'm', "macro", "macros"},
50 { true, 't', "target", "targets"},
51 { true, 'I', "makefile", "makefiles",
52 .referenceOnly
= true, ATTACH_ROLES(MakeMakefileRoles
)},
57 * FUNCTION DEFINITIONS
60 static int nextChar (void)
62 int c
= getcFromInputFile ();
65 c
= getcFromInputFile ();
72 static void skipLine (void)
77 while (c
!= EOF
&& c
!= '\n');
79 ungetcToInputFile (c
);
82 static int skipToNonWhite (int c
)
84 while (c
!= '\n' && isspace (c
))
89 static bool isIdentifier (int c
)
91 return (bool)(c
!= '\0' && (isalnum (c
) || strchr (".-_/$(){}%", c
) != NULL
));
94 static bool isSpecialTarget (vString
*const name
)
97 /* All special targets begin with '.'. */
98 if (vStringLength (name
) < 1 || vStringChar (name
, i
++) != '.') {
101 while (i
< vStringLength (name
)) {
102 char ch
= vStringChar (name
, i
++);
103 if (ch
!= '_' && !isupper ((unsigned char) ch
))
111 static int makeSimpleMakeTag (vString
*const name
, makeKind kind
)
113 if (!isLanguageEnabled (getInputLanguage ()))
116 return makeSimpleTag (name
, kind
);
119 static void makeSimpleMakeRefTag (const vString
* const name
, const int kind
,
122 if (!isLanguageEnabled (getInputLanguage ()))
125 makeSimpleRefTag (name
, kind
, roleIndex
);
128 static int newTarget (vString
*const name
)
130 /* Ignore GNU Make's "special targets". */
131 if (isSpecialTarget (name
))
135 return makeSimpleMakeTag (name
, K_TARGET
);
138 static int newMacro (vString
*const name
, bool with_define_directive
, bool appending
)
144 r
= makeSimpleMakeTag (name
, K_MACRO
);
146 foreachSubparser(s
, false)
148 makeSubparser
*m
= (makeSubparser
*)s
;
150 if (m
->newMacroNotify
)
151 m
->newMacroNotify (m
, vStringValue(name
), with_define_directive
, appending
);
158 static void valueFound (vString
*const name
)
161 foreachSubparser(s
, false)
163 makeSubparser
*m
= (makeSubparser
*)s
;
166 m
->valueNotify (m
, vStringValue (name
));
171 static void directiveFound (vString
*const name
)
174 foreachSubparser (s
, false)
176 makeSubparser
*m
= (makeSubparser
*)s
;
178 if (m
->directiveNotify
)
179 m
->directiveNotify (m
, vStringValue (name
));
184 static void newInclude (vString
*const name
, bool optional
)
186 makeSimpleMakeRefTag (name
, K_INCLUDE
,
187 optional
? R_INCLUDE_OPTIONAL
: R_INCLUDE_GENERIC
);
190 static bool isAcceptableAsInclude (vString
*const name
)
192 if (strcmp (vStringValue (name
), "$") == 0)
197 static void readIdentifier (const int first
, vString
*const id
)
202 while (isIdentifier (c
) || (depth
> 0 && c
!= EOF
&& c
!= '\n'))
204 if (c
== '(' || c
== '{')
206 else if (depth
> 0 && (c
== ')' || c
== '}'))
211 ungetcToInputFile (c
);
214 static void endTargets (intArray
*targets
, unsigned long lnum
)
216 for (unsigned int i
= 0; i
< intArrayCount (targets
); i
++)
218 int cork_index
= intArrayItem (targets
, i
);
219 setTagEndLineToCorkEntry (cork_index
, lnum
);
221 intArrayClear (targets
);
224 static bool isTheLastTargetOnTheSameLine (intArray
*current_targets
,
227 if (!intArrayIsEmpty (current_targets
))
229 int r
= intArrayLast (current_targets
);
230 tagEntryInfo
*e
= getEntryInCorkQueue (r
);
231 if (e
&& e
->lineNumber
== line
)
238 static void findMakeTags (void)
240 stringList
*identifiers
= stringListNew ();
242 int current_macro
= CORK_NIL
;
243 bool in_value
= false;
244 intArray
*current_targets
= intArrayNew ();
245 bool variable_possible
= true;
246 bool appending
= false;
250 sub
= getSubparserRunningBaseparser();
252 chooseExclusiveSubparser (sub
, NULL
);
254 while ((c
= nextChar ()) != EOF
)
258 if (!intArrayIsEmpty (current_targets
))
260 if (c
== '\t' || (c
= skipToNonWhite (c
)) == '#')
262 skipLine (); /* skip rule or comment */
266 endTargets (current_targets
, getInputLineNumber () - 1);
271 stringListClear (identifiers
);
272 variable_possible
= intArrayIsEmpty (current_targets
);
277 else if (isspace (c
))
281 else if (variable_possible
&& c
== '?')
284 ungetcToInputFile (c
);
285 variable_possible
= (c
== '=');
287 else if (variable_possible
&& c
== '+')
290 ungetcToInputFile (c
);
291 variable_possible
= (c
== '=');
294 else if ((! in_value
) && variable_possible
&& c
== ':' &&
295 stringListCount (identifiers
) > 0)
298 ungetcToInputFile (c
);
302 for (i
= 0; i
< stringListCount (identifiers
); i
++)
304 int r
= newTarget (stringListItem (identifiers
, i
));
306 intArrayAdd (current_targets
, r
);
308 stringListClear (identifiers
);
311 else if (variable_possible
&& c
== '=' &&
312 stringListCount (identifiers
) == 1)
314 newMacro (stringListItem (identifiers
, 0), false, appending
);
317 unsigned long curline
= getInputLineNumber ();
318 unsigned long adj
= isTheLastTargetOnTheSameLine (current_targets
,
320 endTargets (current_targets
, curline
- adj
);
323 else if (variable_possible
&& isIdentifier (c
))
325 vString
*name
= vStringNew ();
326 readIdentifier (c
, name
);
327 stringListAdd (identifiers
, name
);
332 if (stringListCount (identifiers
) == 1)
334 if ((current_macro
!= CORK_NIL
) && ! strcmp (vStringValue (name
), "endef"))
336 setTagEndLineToCorkEntry (current_macro
, getInputLineNumber ());
337 current_macro
= CORK_NIL
;
339 else if (current_macro
!= CORK_NIL
)
341 else if (! strcmp (vStringValue (name
), "define"))
343 c
= skipToNonWhite (nextChar ());
345 /* all remaining characters on the line are the name -- even spaces */
346 while (c
!= EOF
&& c
!= '\n')
348 vStringPut (name
, c
);
352 ungetcToInputFile (c
);
353 vStringStripTrailing (name
);
355 current_macro
= newMacro (name
, true, false);
357 else if (! strcmp (vStringValue (name
), "export"))
358 stringListClear (identifiers
);
359 else if (! strcmp (vStringValue (name
), "include")
360 || ! strcmp (vStringValue (name
), "sinclude")
361 || ! strcmp (vStringValue (name
), "-include"))
363 bool optional
= (vStringValue (name
)[0] == 'i')? false: true;
366 c
= skipToNonWhite (nextChar ());
367 readIdentifier (c
, name
);
368 vStringStripTrailing (name
);
369 if (isAcceptableAsInclude(name
))
370 newInclude (name
, optional
);
372 /* non-space characters after readIdentifier() may
373 * be rejected by the function:
377 * Here, remove such characters from input stream.
381 while (c
!= EOF
&& c
!= '\n' && (!isspace (c
)));
383 ungetcToInputFile (c
);
385 if (c
== EOF
|| c
== '\n')
390 directiveFound (name
);
394 variable_possible
= false;
397 endTargets (current_targets
, getInputLineNumber ());
399 intArrayDelete (current_targets
);
400 stringListDelete (identifiers
);
404 extern parserDefinition
* MakefileParser (void)
406 static const char *const patterns
[] = { "[Mm]akefile", "GNUmakefile", NULL
};
407 static const char *const extensions
[] = { "mak", "mk", NULL
};
408 static const char *const aliases
[] = {
409 /* the mode name in emacs */
412 parserDefinition
* const def
= parserNew ("Make");
413 def
->kindTable
= MakeKinds
;
414 def
->kindCount
= ARRAY_SIZE (MakeKinds
);
415 def
->patterns
= patterns
;
416 def
->extensions
= extensions
;
417 def
->aliases
= aliases
;
418 def
->parser
= findMakeTags
;
419 def
->useCork
= CORK_QUEUE
;