Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
935 views
in Technique[技术] by (71.8m points)

Makefile in C (ubuntu) multiple definition

I am learning c and trying to build using makefile. I am stuck on the following error and don't know what to do next.

the build command is gcc -o logfind logfind.o cmdargutils.o filesystem_utils.o file_utils.o strutils.o

If I need both file_utils.o and cmdargutils.o but if I add both I get the following error.

error screenshot

ERROR

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

The source is: 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.
"); 
        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
");
        exit(INPUT_ERROR);
    }

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

        if(argc < 3) {
            printf("Not enough arguments
");
            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
", path, line_number);
            printf("	%s
", 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
");
        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;
}
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

read documentation!

First, take a few hours to read documentation of GNU make, and read how to invoke GCC. You also need to understand more about the preprocessor, so read documentation of cpp. You want to take advantage of builtin GNU make rules (so run make -p to understand them) and variables. See also this answer. You could use remake (as remake -x) to debug your Makefile. You apparently don't understand how make and how gcc should be used, so you need to read more. Read also a C tutorial, look into some C reference, and glance when needed into the C11 standard n1570. Of course, read the documentation of every function you use (e.g. printf(3) etc..). For Linux system programming, read a book like ALP and relevant man pages from syscalls(2) and intro(3) etc...

Then read How to debug small programs. You certainly want to compile with all warnings and debug info.


a better Makefile

You might try something like:

# 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 *~

Of course, you need dependencies for object files on header files. You could compute them automatically, but it is simpler to explicit them, so add something like:

strutils.o: strutils.c strutils.h

and so on for each other object files.

BTW my HelloWorld/ directory on github is a tutorial example for using make


your multiple definition bug

You are getting multiple definition of MAX_LINE because it is defined in a header file included by several translation units, hence several translation units define it.

So either make it a preprocessor constant #define MAX_LINE 1024 in your header file_utils.h, or put there only a declaration like extern const int MAX_LINE; and define it only once in a single translation unit, as const int MAX_LINE=1024; in file_utils.c


general hints

I strongly recommend doing some iterative and incremental development: code only one or two dozen lines at once, then compile them, improve them to get no warnings, debug them with the GDB debugger and test them. At last repeat all this till satisfied. I do recommend using also a version control system (like git) even for school homework.

You might want to use valgrind to hunt memory leaks and other dynamic memory allocation bugs.

You could also use some static source analyzer like clang-analyzer or even Frama-C.

Once your program is debugged, you might add optimization flags like -O2 into your CFLAGS (in particular if you benchmark it with time(1)).

You could be interested by ntfw(3).


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

2.1m questions

2.1m answers

60 comments

56.8k users

...