Подтвердить что ты не робот

Несколько исходных файлов в C- Как именно make файлы работают?

Я относительный новичок в C, и мне нужно узнать, как работают make файлы, и я немного смущен тем, как работает комбинация файлов C. Скажем, у нас есть main.c, foo.c и bar.c. Как код должен быть написан так, чтобы main.c распознавал функции в других файлах? Кроме того, в foo.c и bar.c есть весь код, написанный в основной функции, или нам нужно написать другие функции для того, что нам нужно делать? Я прочитал учебники о том, как написаны make файлы, и это имеет смысл по большей части, но я все еще немного запутался в базовой логистике.

4b9b3361

Ответ 1

В общем, что произойдет, вы определите свои функции для других файлов в файле заголовка, которые затем могут быть включены в main.c. Например, рассмотрим эти фрагменты:

main.c:

#include "foo.h"

int main(int argc, char *argv[]) {
    do_foo();
    return 0;
}

foo.h:

void do_foo();

foo.c:

#include <stdio.h>
#include "foo.h"

void do_foo() {
    printf("foo was done\n");
}

Что произойдет, так это то, что main.c будет превращен в объектный файл (main.o), а foo.c будет преобразован в файл объекта (foo.o). Затем компоновщик свяжет эти два файла вместе, и здесь функция do_foo() в main.c "связана" с функцией в foo.o.

Пример команды GCC: gcc -o myprogram main.c foo.c

Пример makefile

myprogam: main.o foo.o
    gcc -o myprogram main.o foo.o

main.o: main.c foo.h
    gcc -c main.c

foo.o: foo.c foo.h
    gcc -c foo.c

Ответ 2

О компиляции C/С++

Когда у вас есть набор файлов, вы обычно делаете 2 вещи:

  • скомпилировать каждый исходный файл (.c) в файл объекта (.o)
  • связывает все объектные файлы с исполняемым файлом.

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

Посмотрите ниже, как выглядят команды для этого и как они обрабатываются с помощью make.


О makefiles

Makefile - это набор целей и правил для их создания. target - это "что-то, что можно построить и привести к заданному файлу". (Существуют также "фальшивые" цели, которые не приводят к файлу и просто выполняются команды - общий для него называется clean, чтобы удалить результаты компиляции).

Каждая цель состоит из двух частей:

  • список зависимостей, "список чувствительности" (другие файлы и цели, необходимые для этой цели) (после :, разделенные запятыми),
  • список команд оболочки, которые выполняются для создания этой цели (ниже, с отступом)

Рассмотрим следующий пример:

main: main.o module1.o module2.o
    g++ main.o module1.o module2.o -o main

Это означает, что: "Чтобы создать файл main, мне нужно сначала убедиться, что цели main.o, module1.o и module2.o обновлены, а затем мне нужно вызвать следующую команду..".

Это также можно переписать как:

main: main.o module1.o module2.o
    gcc $^ -o [email protected]

Переменные (все, начиная с $ - это переменная) будут расширены до списка зависимостей и целевого имени, как вы ожидаете.

Вы можете определить свои собственные переменные и развернуть их следующим образом:

OBJS = main.o module1.o module2.o

main: $(OBJS)
    # code goes here

Вы компилируете отдельные единицы перевода следующим образом:

main.o: main.c
    gcc -c $< -o [email protected]
    # note the -c option, which means: "compile, but don't link"
    # $< will expand to the first source file

Вы можете добавлять зависимости заголовка для перестройки main.o при изменении main.c или любого из его заголовков:

main.o: main.c module1.h module2.h
    gcc -c $< -o [email protected]

Чтобы не писать одну и ту же команду снова и снова, вы можете определить общее правило и просто указать зависимости (если хотите):

%.o: %.c
    gcc -c $< -o [email protected]

main.o: main.c module1.h module2.h
module1.o: module1.c module1.h module2.h

Также существует некоторая магия для автоматического создания зависимостей (см. ссылку). Один из недостатков использования Make заключается в том, что он не делает это сам по себе (как это делают некоторые строительные системы - например, SCons, которые я предпочитаю для C/С++).

Ответ 3

Функции в foo.c, которые нужно вызывать извне foo.c, должны иметь прототипы в foo.h. Внешние файлы, которые должны вызывать эти функции, должны затем #include "foo.h". foo.c и bar.c не должны иметь функцию main(), если они являются частью той же программы, что и main.c.

Makefiles определяет цели. Для простых программ у вас может быть только одна цель, которая компилирует все это. Более сложные (чтение: более крупные) программы могут иметь промежуточные цели (например, foo.o), которые позволят make избежать ненужной перекомпиляции. Способ make определяет, нужно ли перекомпилировать заданную цель, если она просматривает время модификации всех предварительных условий (материал после двоеточия), и если какой-либо из них приходит после последнего измененного времени самого целевого файла, он восстанавливается.

Вот очень простой пример:

main.c:

#include "foo.h"

int main()
{
    fooprint(12);
    return 0;
}

foo.c:

#include "stdio.h"
#include "foo.h"

void fooprint(int val)
{
    printf("A value: %d\n", val);
}

foo.h:

void fooprint(int val);

Makefile:

main: main.c foo.o
    gcc -o main main.c foo.o

foo.o: foo.c
    gcc -c foo.c

Затем вы можете запустить make main и скомпилировать foo.c в foo.o, затем скомпилировать main.c и связать его с foo.o. Если вы измените main.c, он просто перекомпилирует main.c и свяжет его с уже построенным foo.o.

Ответ 4

По существу make файл содержит правила формы:

<this file> : <needs these files>
    <and is created by this command>

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

Файл make может иметь "правила по умолчанию" на основе шаблонов, а также есть встроенные макросы для различных сценариев сопоставления файлов, а также макросы определения пользователя и включение вложенных make файлов.

Я упростил приведенную форму правила к обычному случаю. На самом деле команде вообще не нужно создавать цель, это просто команда, которая будет выполняться после того, как будут присутствовать все файлы в зависимости. Кроме того, целью может быть не файл. Часто целью верхнего уровня является цель 'dummy', называемая "все" или аналогичная.

Конечно, есть много тонкостей и нюансов, которые подробно описаны в руководстве (в GNU, в частности, есть другие утилиты make).

Ответ 5

Make имеет мало общего со структурой программы на C. Все make make определяет дерево зависимостей и выполняет команды, когда находит зависимости из-за шума. Мое высказывание в make файле:

foo.exe : foo.c bar.c baz.c

просто sez: foo.exe зависит от foo.c, bar.c и baz.c. Это, sotto vocce, расширяется, используя набор правил по умолчанию, для чего-то вроде:

foo.exe : foo.obj bar.obj baz.obj

foo.obj : foo.c

bar.obj : bar.c

baz.obj : baz.c

Сделайте просто перемещение дерева зависимостей, начиная с его корня (в данном случае foo.exe). Если цель не существует или если один из объектов, от которых она зависит, новее, чем цель, выполняются соответствующие команды. чтобы сделать зависимость правильной.

См. Управление проектами с Make из O'Reilly для большего, чем вы, вероятно, хотите знать.

Что касается второй части вашего вопроса, ответ - всего две буквы: K и R. Их Язык программирования C - одна из лучших книг по программированию, когда-либо написанных.

alt text

Ответ 6

Как работает Makefile?

= > Когда команда make выполняется на терминале, она ищет файл с именем makefile или Makefile в текущем каталоге и создает дерево зависимостей.

Если у вас несколько Make файлов, вы можете выполнить конкретную команду:

                           make -f MyMakefile

= > На основе make target, указанного в make файле, выполните проверки, существуют ли файлы зависимостей этой цели. И если они существуют, являются ли они более новыми, чем сама цель, путем сравнения временных меток файлов.

Here our first and default target is "all" which looks for main.o and function.o file dependencies. Second and third target is main.o and function.o respectively which have dependencies of main.c and function.c respectively.

= > Перед выполнением команд соответствующего целевого объекта, его зависимости должны быть выполнены, когда они не выполняются, целевые объекты этих зависимостей выполняются перед заданной целью, чтобы предоставить отсутствующие зависимости.

= > Когда целью является имя файла, сравните отметки времени целевого файла и его файлов зависимостей. Если файл зависимостей более новый, чем целевой файл, выполнение цели в противном случае не выполняется.

In our case, when first target "all" start executing it looks for main.o file dependency, if its not met. Then it goes to second target main.o which check for its dependency main.c and compare time-stamp with it. If target found main.c dependency is updated, then target execute else not. Same process is follow for next target function.o.

= > Таким образом, он завершает рекурсивно проверять все пути вниз по дереву зависимостей, на файлы исходного кода. Благодаря этому процессу экономится время, выполняя только команды, которые необходимо выполнить, на основе того, какой из исходных файлов (перечисленных как зависимости) был обновлен, и имеет более новую метку времени, чем их цель.

= > Теперь, когда цель не является именем файла (которое мы называем "специальными целями" ), очевидно, что нельзя сравнивать метки времени, чтобы проверить, являются ли зависимости целей более новыми. Поэтому такая цель всегда выполняется.

In our Makefile, special targets are "all" and "clean". As we discussed target "all" earlier, but we not discuss target clean. Target clean removes the all object files created during compilation and binary executable files according to command.

Для выполнения каждой цели сделайте отпечатки действий при их выполнении. Обратите внимание, что каждая из команд выполняется в отдельной среде суб-оболочки из-за безопасного выполнения, так что они не могут изменять текущую среду оболочки, которая может повлиять на выполнение другой цели. Например, если одна команда содержит cd newdir, текущий каталог будет изменен только для этой строки, для следующей строки текущая директория не изменится.

Источник: - http://www.firmcodes.com/linux/write-first-makefile-c-source-code-linux-tutorial/