Thread: confusion on structuring a project with test files

  1. #1
    Registered User
    Join Date
    Jan 2024
    Posts
    23

    confusion on structuring a project with test files

    Hello,

    I follow along a tutorial on interpreters and have a directory structure like this:
    Interpreter
    --src
    ----segment.h
    ----segment.c
    ----main.c
    ----Makefile
    --test
    ----test_segment.h
    ----test_segment.c
    ----Makefile

    My segment files look like this:
    Code:
    #ifndef segment_h
    #define segment_h
    
    
    #include "common.h"
    
    
    typedef enum {
      OP_RETURN,
    } op_code;
    
    
    typedef struct {
      int count;
      int capacity;
      uint8_t* code;
    } code_segment;
    
    
    int init_code_segment(code_segment* segment);
    
    
    #endif
    Code:
    #include "segment.h"
    
    int init_code_segment(code_segment* segment){
        if (segment == NULL)
            return -1;
        segment->count = 0;
        segment->capacity = 0;
        segment->code = NULL;
        return 0;
    }
    Code:
    #include "/Users/david/Programming/C/Interpreter/src/segment.h"#include <stdlib.h>
    #include <stdio.h>
    #include <assert.h>
    
    
    void test_init_segment();
    Code:
    #include "test_segment.h"
    
    void test_init_segment(){
        code_segment* segment = (code_segment*) malloc(sizeof(code_segment));
        int exit_status = init_code_segment(segment);
        assert(exit_status == 0);
        assert(segment->count == 0 && segment->capacity == 0 && 
            segment->code == NULL);
    
    
    }
    
    
    int main(int argc, char* argv[]){
        
        test_init_segment();
    
    
        return 0;
    }
    and the makefile in the "test" directory looks like this:
    Code:
    all: test_segment
    
    test_segment: test_segment.c
        gcc -Wall -o test_segment test_segment.c
        ./test_segment
    When I execute make in the "test" directory I get:
    Code:
    ld: Undefined symbols:  _init_code_segment, referenced from:
          _test_init_segment in test_segment-0bf004.o
    clang: error: linker command failed with exit code 1 (use -v to see invocation)
    so basically the function in "segment.h" is not found although I included it in "test_segment.h". Can someone tell me whats wrong? thanks!

  2. #2
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,675
    > so basically the function in "segment.h" is not found
    It was found, otherwise you would have gotten a different error message.

    Your error is from the linker, not the compiler.

    segment.h doesn't contain the actual code.

    Your makefile should be something like
    Code:
    all: test_segment
     
    test_segment: test_segment.c
        gcc -Wall -o test_segment test_segment.c ../src/segment.c
        ./test_segment
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  3. #3
    Registered User
    Join Date
    Jan 2024
    Posts
    23
    I have deleted the subdirectory "test" and moved everything into "src".
    test_segment.h looks like this:
    Code:
    #include <stdlib.h>
    #include <stdio.h>
    #include <assert.h>
    
    
    void test_init_segment();
    test_segment.c looks like this:
    Code:
    #include "test_segment.h"
    #include "segment.h"
    
    
    void test_init_segment(){
        code_segment* segment = (code_segment*) malloc(sizeof(code_segment));
        int exit_status = init_code_segment(segment);
        assert(exit_status == 0);
        assert(segment->count == 0 && segment->capacity == 0 && 
            segment->code == NULL);
    
    
    }
    
    
    int main(int argc, char* argv[]){
        
        test_init_segment();
    
    
        return 0;
    }
    and the Makefile:

    Code:
    all: main segment test_segment
    
    test_segment: test_segment.c segment
    	gcc -Wall -o test_segment test_segment.c segment.c
    
    
    main : main.c
        gcc -Wall -o main main.c
    
    
    segment : segment.c
        gcc -Wall -o segment segment.c
    Can you tell me why I get this? Thank you!!
    Code:
    gcc -Wall -o main main.c
    gcc -Wall -o segment segment.c
    ld: Undefined symbols:
      _main, referenced from:
          <initial-undefines>
    clang: error: linker command failed with exit code 1 (use -v to see invocation)
    Last edited by john_12; 03-04-2024 at 10:19 AM.

  4. #4
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,675
    Try
    Code:
    all: main test_segment
    
    test_segment: test_segment.c segment.c
    	gcc -Wall -o test_segment test_segment.c segment.c
    
    main : main.c segment.c
    	gcc -Wall -o main main.c segment.c
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  5. #5
    Registered User
    Join Date
    Apr 2021
    Posts
    148
    You're having the same problem, only backwards.

    The C compiler can traditionally do a lot of things, and that is causing your confusion. Specifically, the compiler can also invoke the linker, ld, automatically to merge object files and libraries into executables.

    Here are the steps that need to happen:

    1. Compile a C source "translation unit" into an intermediate "object" file.
    2. [optional] Gather multiple object files into a library (static or dynamic).
    3. Link one or more object files, along with zero or more libraries, into an executable file.


    Your problem is occurring in step 3. Many times, you are reaching step 3 without meaning to do so.

    First, the -c option to the C compiler tells it to "compile only." Meaning it should stop after step 1. You want to use this a lot!


    Next, when you have multiple object files, it doesn't matter where they are. It only matters if you have all of them, and if you invoke the compiler or linker on them correctly.

    The compiler is smart enough to act in lieu of the linker. So I'm never going to have you run the linker directly. Just invoke the compiler and let it figure out what needs doing, and do it.

    You have two potential executable programs you can produce here. There is main.c which I really hope contains a main, and there is also test_segment.c which you have shown us contains a main().

    So you need to (1) add makefile targets for your executables; and (2) make sure you pass all the required files to the compiler.

    Your makefile, according to your last post, contains these lines:
    Code:
    test_segment: test_segment.c segment
        gcc -Wall -o test_segment 
    
    main : main.c
        gcc -Wall -o main main.c
    
    segment : segment.c
        gcc -Wall -o segment segment.c
    The problem here is that you are invoking gcc to compile executables, but
    not passing the right parameters to compile executables.

    The segment target should not exist, IIUC. segment is not a program,
    with a main(), it is just a bunch of functions that relate to "code segment"
    objects. So the only rule you need for compiling segment.c is to produce
    an object file (or maybe a library, when you have more of these files, but
    that's later).

    Let's rewrite that to stop after compiling, and produce an object file:
    Code:
    segment.o : segment.c
        gcc -Wall -c segment.c -o segment.o
    Next, there is test_segment.c, which you specify a dependency on segment-the-program,
    but which really depends on segment.o. Sadly, you didn't give segment.o to gcc in your
    make recipe. Let's fix that:
    Code:
    test_segment: test_segment.c segment.o
        gcc -Wall test_segment.c segment.o -o test_segment
    We can be a bit more clear, though, by separating the compile-the-c part
    from the link-the-objects part:
    Code:
    test_segment: test_segment.c segment.o
            gcc -Wall -c test_segment.c -o test_segment.o
            gcc -Wall test_segment.o segment.o -o test_segment
    Finally, we can separate the two steps so that make has a chance to only
    execute the parts that really need to be done:
    Code:
    test_segment: test_segment.o segment.o
            gcc -Wall test_segment.o segment.o -o test_segment
    
    test_segment.o : test_segment.c
            gcc -Wall -c test_segment.c -o test_segment.o
    The same is true for main.c and the main executable:
    Code:
    main: main.o segment.o
            gcc -Wall main.o segment.o -o main
    
    main.o : main.c
            gcc -Wall -c main.c -o main.o
    Please note that had you left the test/ subdirectory in place, this would all
    still be needed. The only difference would be the presence of a / in the
    names of some of the targets.

    Also, note: when you are creating tests for a subsystem, one common
    case is to create "unit tests." With unit tests, you test very detailed parts
    of the interface and implementation code -- whatever you think might be
    broken. One result of that, for C (and C++) programs, is that you may
    want to be able to call static functions, and/or access static variables.
    As you may know, file-scope objects marked static are not visible to code
    in other translation units (that is, "other C source files").

    The standard way to get around this, for unit tests only, is to #include the
    file to be tested inside the unit test code. That has the follow-on effect of
    changing the structure of the Makefile, since you no longer need to have
    the separate object file.
    Last edited by aghast; 03-04-2024 at 10:45 AM. Reason: Code block not closed

  6. #6
    Registered User
    Join Date
    Jan 2024
    Posts
    23
    hey thank you so much for taking the time to write this!! I understood it a lot better.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Replies: 26
    Last Post: 06-12-2013, 05:43 PM
  2. Confusion on header and source files
    By dnguyen1022 in forum C++ Programming
    Replies: 4
    Last Post: 01-17-2009, 03:42 AM
  3. Files declaration confusion
    By guillermoh in forum C Programming
    Replies: 1
    Last Post: 02-04-2008, 10:26 AM
  4. Structuring .cpp's and .h's
    By Sentral in forum Game Programming
    Replies: 20
    Last Post: 06-05-2006, 10:10 AM
  5. Threads, Files and a Test
    By nvoigt in forum Windows Programming
    Replies: 2
    Last Post: 11-14-2005, 09:27 AM

Tags for this Thread