Заголовочные файлы в нескольких каталогах: передовой опыт

Я новичок

Я пишу много кода на динамических языках (javascript, python, haskell и т. Д.), Но сейчас я изучаю C для аспирантуры и понятия не имею, что делаю.

Эта проблема

Первоначально я собирал весь свой исходный код в одном каталоге, используя make-файл, который работал довольно хорошо. Однако мой проект растет, и я хотел бы разбить исходный код на несколько каталогов (модульные тесты, утилиты, ядро ​​и т. Д.). Например, мое дерево каталогов может выглядеть следующим образом:

.
|-- src
|   |-- foo.c
|   |-- foo.h
|   `-- main.c
`-- test
    `-- test_foo.c

test/test_foo.c использует оба src/foo.c и src/foo.h . Каков наилучший / стандартный способ создать это с помощью make-файлов? Желательно, чтобы было одно правило для построения проекта и одно для построения тестов.

Примечание

Я знаю, что есть и другие способы сделать это, включая autoconf и другие автоматические решения. Однако я хотел бы понять, что происходит, и иметь возможность писать make-файлы с нуля, несмотря на его возможную непрактичность.

Любые рекомендации или советы будут оценены. Спасибо!

[Редактировать]

Итак, три приведенных решения следующие:

  • Поместите глобально используемые файлы заголовков в параллельный includeкаталог
  • используйте путь в #includeзаявлении, как в#include "../src/foo.h"
  • используйте -Iпереключатель, чтобы сообщить компилятору о местах включения

Пока мне нравится -I решение с переключением, потому что оно не требует изменения исходного кода при изменении структуры каталогов.

Ответов (4)

Решение

Для test_foo.c вам просто нужно указать компилятору, где можно найти файлы заголовков. Например

gcc -I../src -c test_foo.c

Затем компилятор также заглянет в этот каталог, чтобы найти файлы заголовков. Затем в test_foo.c вы пишете:

#include "foo.h"

РЕДАКТИРОВАТЬ : чтобы связать с foo.c, на самом деле с foo.o, вам нужно упомянуть его в списке объектных файлов. Я предполагаю, что у вас уже есть объектные файлы, а затем выполните:

gcc test_foo.o ../src/foo.o -o test

Ваш тестовый файл должен просто включать файлы заголовков напрямую с использованием относительных путей, например:

#include "../src/foo.h"

Обычный способ сделать это - чтобы файлы заголовков, используемые одним файлом C, были названы так же, как этот файл C, и в том же каталоге, а файлы заголовков, используемые многими файлами C (особенно теми, которые используются всем проектом), чтобы находиться в каталоге include, параллельном исходному каталогу C.

Я также редко использую автоинструменты GNU. Вместо этого я помещу один созданный вручную make-файл в корневой каталог.

Чтобы получить все заголовки в исходном каталоге, используйте что-то вроде этого:

get_headers = $(wildcard $(1)/*.h)
headers := $(call get_headers,src)

Затем вы можете использовать следующее, чтобы объектные файлы в тестовом каталоге зависели от этих заголовков:

test/%.o : test/%.c $(headers)
    gcc -std=c99 -pedantic -Wall -Wextra -Werror $(flags) -Isrc -g -c -o [email protected] $<

Как видите, я не фанат встроенных директив. Также обратите внимание на -I переключатель.

Получить список объектных файлов для каталога немного сложнее:

get_objects = $(patsubst %.c,%.o,$(wildcard $(1)/*.c))
test_objects = $(call get_objects,test)

Следующее правило будет создавать объекты для ваших тестов:

test : $(test_objects)

test Правил не должен просто сделать объектные файлы, но исполняемые файлы. Как написать правило зависит от структуры ваших тестов: например, вы можете создать исполняемый файл для каждого .c файла или только один, который проверяет все.