C(ubuntu)多重定义中的Makefile

时间:2018-12-15 07:07:51

标签: c makefile

我正在学习c并尝试使用makefile进行构建。我陷入了以下错误,不知道下一步该怎么做。

build命令是 gcc -o logfind logfind.o cmdargutils.o filesystem_utils.o file_utils.o strutils.o

如果我同时需要file_utils.o和cmdargutils.o,但是如果我同时添加,则会出现以下错误。

error screenshot

错误

file_utils.o:(.rodata+0x0): multiple definition of `MAX_LINE'
logfind.o:(.rodata+0x0): first defined here
collect2: error: ld returned 1 exit status
Makefile:2: recipe for target 'logfind' failed
make: *** [logfind] Error 1

来源是: Makefile

logfind: clean logfind.o
    gcc -o logfind logfind.o cmdargutils.o filesystem_utils.o file_utils.o strutils.o

logfind.o: logfind.c cmdargutils.o file_utils.o filesystem_utils.o strutils.o error_codes.h
    gcc -c logfind.c

cmdargutils.o: cmdargutils.c cmdargutils.h
    gcc -c cmdargutils.c

file_utils.o: file_utils.c file_utils.h
    gcc -c file_utils.c

filesystem_utils.o: filesystem_utils.c filesystem_utils.h
    gcc -c filesystem_utils.c

strutils.o: strutils.c strutils.h
    gcc -c strutils.c

clean:
    rm -f *.o logfind

cmdargutils.h

#ifndef CMD_ARG_UTILS
#define CMD_ARG_UTILS

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdbool.h>
#include "error_codes.h"
#include "strutils.h"

struct Argument {
    bool is_and_operation;
    int count;
    char **search_terms;
};

struct Argument *argument_create(int argc, char **argv, int start, bool is_and_operation);
void argument_destroy(struct Argument *argument);
struct Argument *parse_arguments(int argc, char **argv);

#endif

error_codes.h

#ifndef ERROR_CODES
#define ERROR_CODES

enum error_codes {
    MEMORY_ERROR,
    INPUT_ERROR
};

#endif

file_utils.h

#ifndef FILE_UTILS
#define FILE_UTILS

#define _GNU_SOURCE

#include <stdio.h>
#include <stdbool.h>
#include <string.h> 
#include <stdlib.h>
#include "cmdargutils.h"

const size_t MAX_LINE = 1024;

bool is_match(char *, struct Argument *); 
bool scan_file(char *, struct Argument *);

#endif

filesystem_utils.h

#ifndef FILESYSTEM_UTILS
#define FILESYSTEM_UTILS

#include <glob.h>
#include <string.h>
#include "strutils.h"

struct SearchFiles {
    int count;
    char **paths;
};

struct SearchFiles *search_files_create(int count, char** paths);
void search_files_destroy(struct SearchFiles *search_files);
struct SearchFiles *scan_directory(char *directory_path, char *pattern);

#endif

strutils.h

#ifndef STRUTILS
#define STRUTILS

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "error_codes.h"

char *strdup(const char *source);
char **copy_string_array(char **source, int start, int end);

#endif

logfind.c

#include <stdio.h>
#include <stdlib.h>
#include <glob.h>
#include "cmdargutils.h"
#include "filesystem_utils.h"
#include "file_utils.h"

int main(int argc, char **argv) {
    struct Argument *argument = parse_arguments(argc, argv);
    int i = 0;

    struct SearchFiles *search_files = scan_directory(".", "*.*");
    for(i = 0; i < search_files->count; i++) {
        scan_file(search_files->paths[i], argument);
    }

    search_files_destroy(search_files);
    argument_destroy(argument);
    return 0;    
}

cmdargutils.c

#include "cmdargutils.h"

struct Argument *argument_create(int argc, char **argv, int start, bool is_and_operation){
    struct Argument *argument = (struct Argument *)malloc(sizeof(struct Argument));

    if(!argument) {
        printf("Could not initialize arguments.\n"); 
        exit(MEMORY_ERROR);
    }
    argument->count =  argc - start;
    argument->is_and_operation = is_and_operation;
    argument->search_terms = copy_string_array(argv, start, argc);

    return argument; 
}

void argument_destroy(struct Argument *argument){
    int i = 0;

    for(i = 0; i < argument->count; i++) {
        free(argument->search_terms[i]);
    }

    free(argument->search_terms); 
    free(argument);
    argument = NULL;    
}

struct Argument *parse_arguments(int argc, char **argv) {

    struct Argument *argument = NULL;
    bool is_and_operation = true;
    int start = 0;

    if(argc < 2) {
        printf("Not enough arguments\n");
        exit(INPUT_ERROR);
    }

    char *operation = argv[1];
    if(strcmp(operation, "-o") == 0) {
        is_and_operation = false;

        if(argc < 3) {
            printf("Not enough arguments\n");
            exit(INPUT_ERROR);
        }
    }

    start = is_and_operation ? 1 : 2;

    argument = argument_create(argc, argv, start, is_and_operation); 

    return argument;
}

file_utils.c

#include "file_utils.h"

bool is_match(char *line, struct Argument *argument) {
    int i = 0;
    bool isMatch = false;
    for(i = 0; i < argument->count; i++) {
        char *found = strcasestr(line, argument->search_terms[i]);
        if(!found) {
            if(argument->is_and_operation) {
                isMatch = false;
                break;
            } else {
                continue;
            }
        } else {
            isMatch = true;
            if(argument->is_and_operation) {
                continue;
            } else {
                break;
            }
        }
    }
    return isMatch;
}

bool scan_file(char *path, struct Argument *argument) {
    FILE *file = fopen(path, "r");

    int line_number = 0;
    char *line = malloc(MAX_LINE);

    while(fgets(line, MAX_LINE - 1, file)!= NULL) {
        ++line_number;
        if(is_match(line, argument)) {
            printf("%s:%d\n", path, line_number);
            printf("\t%s\n", line);
        }
    }

    free(line);
    fclose(file);
} 

filesystem_utils.c

#include "filesystem_utils.h"

struct SearchFiles *search_files_create(int count, char** paths) {
    struct SearchFiles *search_files = (struct SearchFiles *)malloc(sizeof(struct SearchFiles));

    search_files->count = count;
    search_files->paths = copy_string_array(paths, 0, count); 

    return search_files;    
}

void search_files_destroy(struct SearchFiles *search_files) {
    int i = 0;

    for(i = 0; i < search_files->count; i++) {
        free(search_files->paths[i]);    
    }

    free(search_files->paths);
    free(search_files);
    search_files = NULL;
}

struct SearchFiles *scan_directory(char *directory_path, char *pattern) {
    glob_t globbuf;
    int error = glob(pattern, GLOB_MARK, NULL, &globbuf);

    if(!error) {
        struct SearchFiles *search_files = search_files_create(globbuf.gl_pathc, globbuf.gl_pathv);
        globfree(&globbuf);
        return search_files;
    }
    return NULL;
}

strutils.c

#include "strutils.h"

char *strdup(const char *source) {
    char *dest = malloc(strlen(source) + 1);
    if(!dest) {
        printf("Memory allocation error\n");
        exit(MEMORY_ERROR);
    }
    strcpy(dest, source);
    return dest;
}

char **copy_string_array(char **source, int start, int end) {
    char **dest = (char **)malloc(sizeof(char *) * (end - start));
    int di = 0;
    int si = start;

    for(di = 0, si = start; si < end; 
        si++, di++) {
        dest[di] = strdup(source[si]);        
    }

    return dest;
}

1 个答案:

答案 0 :(得分:0)

阅读文档!

首先,花几个小时阅读GNU make的文档,并阅读如何使用invoke GCC。您还需要进一步了解preprocessor,因此请阅读documentation of cpp。您想利用内置的GNU make规则(因此请运行make -p来理解它们)和变量。另请参见this答案。您可以使用remake(作为remake -x)来调试Makefile。您显然不了解如何使用make和应如何使用gcc,因此您需要阅读更多内容。另请阅读C tutorial,查看一些C reference,并在需要时浏览C11标准n1570。当然,请阅读您使用的每个函数的文档(例如printf(3)等。)。对于Linux系统编程,请阅读ALPsyscalls(2)等类似intro(3)的书和相关的man页面……

然后阅读How to debug small programs。您当然想编译所有警告和调试信息。


更好的Makefile

您可以尝试以下方法:

# a better Makefile
# your C compiler
CC= gcc

# the verbose remove
RM= rm -vf

# your C compilation flags
CFLAGS= -Wall -Wextra -g

# your C source files
MY_CSOURCES= logfind.c cmdargutils.c filesystem_utils.c file_utils.c strutils.c

# the corresponding object files
MY_OBJECTS= $(patsubst %.c, %.o, $(MY_CSOURCES))

# the conventional phony targets
.PHONY: all clean

# the only program is for the default target all
all: logfind
logfind: $(MY_OBJECTS)
     $(LINK.c) $< -o $@

# cleaning the mess
clean: 
     $(RM) logfind *.o *~

当然,您需要在头文件上依赖目标文件。您可以自动计算它们,但是显式它们更简单,因此添加如下内容:

strutils.o: strutils.c strutils.h

其他对象文件等等。

顺便说一句,我在github上的HelloWorld/目录是一个使用make的教程示例


您的多个定义错误

您将得到multiple definition of MAX_LINE,因为它是由几个translation units包含的头文件中的定义,因此有多个翻译单元对其进行了定义。

因此,可以在标头#define MAX_LINE 1024中将其设置为预处理器常量file_utils.h,或者仅在其中放置声明,例如extern const int MAX_LINE; define 在一个翻译单元中仅一次,如const int MAX_LINE=1024;中的file_utils.c


一般提示

我强烈建议您执行一些iterative and incremental development:一次只编写一两行代码,然后对其进行编译,改进它们以获取警告,并使用GDB debugger进行调试并对其进行测试。最后重复所有这些直到满意为止。我确实建议即使在学校作业中也使用version control系统(例如git)。

您可能想使用valgrind来发现memory leaks和其他dynamic memory allocation错误。

您还可以使用一些静态源分析器,例如clang-analyzer甚至是Frama-C

一旦调试了程序,就可以将-O2之类的optimization flags添加到CFLAGS中(特别是如果您使用time(1)对其进行基准测试)。

您可能会对ntfw(3)感兴趣。