CodeCompass
262 строки · 5.6 Кб
1#include <sys/types.h>2#include <stdint.h>3#include <stdlib.h>4#include <stdio.h>5#include <ctype.h>6
7#include "ldlogger-tool.h"8#include "ldlogger-util.h"9
10/**
11* States for GCCargument parser.
12*/
13typedef enum _GccArgsState14{
15/**16* Normal state (default).
17*/
18Normal,19/**20* After a -o paramater.
21*/
22InOutputArg
23} GccArgsState;24
25/**
26* List of file extensions accepted as source file.
27*/
28static const char* const srcExts[] = {29"c", "cc", "cpp", "cxx", "h", "hh", "hxx", "hpp", "o", "so", "a", NULL30};31
32/**
33* Check if the given path is a gcc libpath or not.
34*
35* @param path_ an absolute directory path.
36* @return true if the given path is a gcc lib path, false if not.
37*/
38static int isGccLibPath(const char* path_)39{
40/* FIXME: it could be lib32 or lib64??? */41const char* gccStart = strstr(path_, "/lib/gcc");42if (!gccStart)43{44return 0;45}46
47/* We want to filter paths like:48* /usr/lib/gcc/x86_64-linux-gnu/4.8/include
49* /usr/lib/gcc/x86_64-linux-gnu/4.8/include-fixed */
50return strstr(gccStart, "include") != NULL;51}
52
53/**
54* Processes an command line argument for a GCC like command.
55*
56* @param state_ the current state of the parser.
57* @param arg_ the current command line argument.
58* @param action the current action.
59* @return the new state.
60*/
61static GccArgsState processArgument(62GccArgsState state_,63const char* arg_,64LoggerAction* action_)65{
66char argToAdd[PATH_MAX];67strcpy(argToAdd, arg_);68
69if (state_ == InOutputArg)70{71if (!loggerMakePathAbs(arg_, argToAdd, 0))72{73strcpy(argToAdd, arg_);74}75
76loggerFileInitFromPath(&action_->output, argToAdd);77state_ = Normal;78}79else if (strcmp(arg_, "-o") == 0)80{81state_ = InOutputArg;82}83else if (arg_[0] == '-' && (arg_[1] == 'I' || arg_[1] == 'L') && arg_[2])84{85/* This is a -I or -L option with a path */86char fullPath[PATH_MAX];87if (loggerMakePathAbs(arg_ + 2, fullPath, 0))88{89argToAdd[2] = 0;90strcat(argToAdd, fullPath);91}92}93else94{95char fullPath[PATH_MAX];96if (loggerMakePathAbs(argToAdd, fullPath, 1))97{98char* ext = loggerGetFileExt(fullPath, 1);99if (ext)100{101int i;102for (i = 0; srcExts[i]; ++i)103{104if (strcmp(srcExts[i], ext) == 0)105{106strcpy(argToAdd, fullPath);107loggerVectorAddUnique(&action_->sources, loggerStrDup(fullPath),108(LoggerCmpFuc) &strcmp);109break;110}111}112}113
114free(ext);115}116}117
118if (argToAdd[0])119{120loggerVectorAdd(&action_->arguments, loggerStrDup(argToAdd));121}122
123return state_;124}
125
126/**
127* Tries to get the default header includes from a gcc(like) command and stores
128* the result into the given vector.
129*
130* @param prog_ the gcc like program / command.
131* @param args_ a vector for the arguments.
132*/
133static void getDefaultArguments(const char* prog_, LoggerVector* args_)134{
135char command[PATH_MAX];136FILE* cmdOut;137char* line = NULL;138size_t lineSize = 0;139ssize_t readSize;140int incStarted = 0;141
142strcpy(command, prog_);143strcat(command, " -xc++ -E -v - < /dev/null 2>&1");144
145cmdOut = popen(command, "r");146if (!cmdOut)147{148return;149}150
151while ((readSize = getline(&line, &lineSize, cmdOut)) >= 0)152{153char fullPath[PATH_MAX] = "-I";154char* pathEnd;155char* pathStart;156
157if (!incStarted)158{159if (strstr(line, "#include <...> search starts here"))160{161incStarted = 1;162}163continue;164}165else if (strstr(line, "End of search list"))166{167break;168}169
170/* Drop the new line character from the end of the line and the leading171whitespaces. */
172for (pathStart = line; *pathStart && isspace(*pathStart); ++pathStart) {}173for (pathEnd = pathStart; *pathEnd && !isspace(*pathEnd); ++pathEnd) {}174*pathEnd = 0;175if (pathStart[0] == 0)176{177/* WTF??? */178continue;179}180
181if (!loggerMakePathAbs(pathStart, fullPath + 2, 0))182{183/* Invalid path, skip */184continue;185}186
187if (isGccLibPath(fullPath))188{189/* We have to skip builtin gcc headers, we only need the paths to the190stdlib */
191continue;192}193
194
195loggerVectorAdd(args_, loggerStrDup(fullPath));196}197
198free(line);199pclose(cmdOut);200}
201
202int loggerGccParserCollectActions(203const char* prog_,204const char* toolName_,205const char* const argv_[],206LoggerVector* actions_)207{
208size_t i;209/* Position of the last include path + 1 */210size_t lastIncPos = 1;211GccArgsState state = Normal;212LoggerAction* action = loggerActionNew(toolName_);213
214loggerVectorAdd(&action->arguments, loggerStrDup(toolName_));215
216for (i = 1; argv_[i]; ++i)217{218state = processArgument(state, argv_[i], action);219if (argv_[i][0] == '-' && argv_[i][1] == 'I')220{221if (argv_[i][2])222{223/* -I with path argument */224lastIncPos = action->arguments.size;225}226else227{228/* The path should be the next argument */229lastIncPos = action->arguments.size + 1;230}231}232}233
234if (!getenv("CC_LOGGER_NO_DEF_DIRS"))235{236LoggerVector defIncludes;237loggerVectorInit(&defIncludes);238
239getDefaultArguments(prog_, &defIncludes);240if (defIncludes.size)241{242loggerVectorAddFrom(&action->arguments, &defIncludes,243&lastIncPos, (LoggerDupFuc) &loggerStrDup);244}245
246loggerVectorClear(&defIncludes);247}248
249/*250* Workaround for -MT and friends: if the source set contains the output,
251* then we have to remove it from the set.
252*/
253i = loggerVectorFind(&action->sources, action->output.path,254(LoggerCmpFuc) &strcmp);255if (i != SIZE_MAX)256{257loggerVectorErase(&action->sources, i);258}259
260loggerVectorAdd(actions_, action);261return 1;262}
263