8-900-374-94-44
[email protected]
Slide Image
Меню

Универсальный makefile: GNU Makefile — универсальный Makefile для нескольких целей

-o [email protected] $(LDFLAGS)

Насколько я понимаю, это правило должно принимать все объектные файлы и создавать исполняемый файл с тем же именем, что и объектный файл, но я не мог заставить его работать! Есть ли у кого-нибудь подсказка, как этого добиться?

c++ makefile rules targets
Поделиться Источник Marcel Stüttgen     12 декабря 2014 в 10:50

Содержание

2 ответа


  • Как зависимости работают на gnu Makefile?

    Как зависимости работают на gnu Makefile ? Я хочу создать файл Makefile, который: - придется применить некоторые патчи (в нашем, например, применять main.patch на main.c) - создайте программу под названием toto из исправленного файла main.c my_patch=./main.patch all: toto patch: $(my_patch) echo...

  • создание makefile для нескольких целей

    я создал Makefile, который я бы изменил так, чтобы он генерировал более одной цели, когда я запускаю make . $(LOADLIBES) $(LDLIBS) -o [email protected]

    таким образом, вы можете использовать эти переменные для выбора компилятора, указывающего флаги для препроцессора, компилятора и компоновщика. Например, вы можете изменить

    CXX , чтобы использовать компилятор для неправильного языка , и исправить ущерб, добавив LDFLAGS=-lstdc++, как это делает ваш makefile, если вы действительно хотите.

    Поделиться Mike Seymour     12 декабря 2014 в 11:11



    0

    Вот решение:

    В приведенных выше примерах я не использую переменную $(LDLIBS), которая используется встроенным правилом по умолчанию. Если я изменю свой Makefile на использование $(LDLIBS) вместо $(LDFLAGS), все будет в порядке!

    Решение #1: (встроенное правило)

    CC=gcc
    CFLAGS=-g -fopenmp
    LDFLAGS= 
    LDLIBS = -lstdc++ -lgomp
    ECHO = echo
    SILENT = @
    
    MODULES = example1 example2 example3
    
    all: $(MODULES)
    
    clean:
        $(SILENT) $(ECHO) "--- Removing object files and binaries ---"
        $(SILENT) rm -f *. $(LDLIBS) -o [email protected] 
    
    clean:
        $(SILENT) $(ECHO) "--- Removing object files and binaries ---"
        $(SILENT) rm -f *.o 
        $(SILENT) rm -f $(MODULES)
    
    .PHONY: clean
    

    Поделиться Marcel Stüttgen     16 декабря 2014 в 09:59


    Похожие вопросы:


    GNU Makefile и bash-экранирование

    В рецепте для цели я хочу сгенерировать скрипт bash, который обрабатывает аргументы командной строки ..., однако экранирование Makefile ускользает от меня цель: deps Эхо ./a.out \[email protected]\ > wrapper.a.out...


    Как получить список целей в файле makefile?

    Я немного использовал rake (a Ruby make program), и у него есть возможность получить список всех доступных целей, например > rake --tasks rake db:charset # retrieve the charset for your data......


    универсальный makefile?

    Есть ли универсальный makefile, который я могу использовать для создания простого проекта c++? на windows? я изменил рабочий файл Wii makefile на win32, но не смог построить его должным образом...


    Как зависимости работают на gnu Makefile?

    Как зависимости работают на gnu Makefile ? Я хочу создать файл Makefile, который: - придется применить некоторые патчи (в нашем, например, применять main.patch на main.c) - создайте программу под...


    создание makefile для нескольких целей

    я создал Makefile, который я бы изменил так, чтобы он генерировал более одной цели, когда я запускаю make . В своей программе я использую self predetended macros (например , TIME , REG и _DEBUG ), и...


    Занижение очень минимального makefile GNU

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


    Gnu-Make не включает файл makefile

    Из документов : Директива include предписывает make приостановить чтение текущего make-файл и прочитайте один или несколько других make-файлов, прежде чем продолжить. Директива представляет собой...


    Оптимизация Makefile для нескольких целей

    Поэтому в рамках нескольких инструментов, которые я делаю в C++, я хотел иметь возможность тестировать каждый инструмент индивидуально. Существует несколько инструментов с несколькими тестовыми...


    Преобразование GNU makefile одного пользователя в другого пользователя

    У меня есть GNU makefile, который компилирует набор исходных кодов в моей системе, и я хочу передать исходный код другому человеку (другой системе), так будет ли тот же самый GNU makefile...


    Как назначить вывод команды shell переменной Makefile для всех целей make?

    У меня есть случайная строка, которая генерируется и используется для нескольких целей make. Мой Makefile выглядит так: VALUE := $(shell strings /dev/urandom | grep -o '[[:alnum:]]' | head -n 30 |...

    c++ - Не получается написать «универсальный» makefile

    Возникла необходимость написать на С++ небольшую программу (500-1К строк), а с IDE я как-то не дружу (программируя на python и java обхожусь sublime'ом), да и хотелось узнать, как компилировать программы из консоли. Поэтому я написал "классовый" HelloWorld и начал читать этот мануал. Проблемы возникли при написании универсального makefile'a.

    "Захардкоженный" makefile выглядит так (всё лежит в одной папке):

    all: Greetings
    
    Greetings: main_greetings.o greetings_printer.o
        g++ -o Greetings main_greetings.o greetings_printer.o
    
    greetings_printer.o: GreetingsPrinter.cpp
        g++ -c -o greetings_printer.o GreetingsPrinter.cpp
    
    main_greetings.o: Greetings.cpp
        g++ -c -o main_greetings.o Greetings.cpp
    

    и команда

    make -f makefile.makefile
    

    делает всё, что нужно: программа компилируется и работает.

    Но вот попытка написать универсальный makefile оборачивается провалом. Сам makefile:

    TARGET  = $( shell basename 'pwd' )
    SOURCES = $( wildcard *.cpp ) 
    OBJECTS = $( SOURCES:%.cpp=%.o )
    
    all: $(TAGRET)
    
    $(OBJECTS): $(SOURCES)
    
    $(TAGRET): $(OBJECTS)
        $(CXX) -o $(TAGRET) $(LDFLAGS) $(OGJECTS) $(LOADLIBES) $(LDLIBS)
    
    clean:
        $(RM) $(OBJECTS) $(TAGRET)
    
    .PHONY: all clean
    

    а предыдущий вызов из консоли выдаёт такой результат:

    make: Nothing to be done for `all'.
    

    В чём ошибка?


    Попробовал вот так:

    make Greetings
    

    Вывод:

    g++     Greetings.cpp   -o Greetings
    /tmp/ccGdTOxR.o: In function `main':
    Greetings.cpp:(.text+0x1d): undefined reference to `MyGreetingsPrinter::MyGreetingsPrinter()'
    Greetings.cpp:(.text+0x29): undefined reference to `MyGreetingsPrinter::PrintGreetings()'
    collect2: ld returned 1 exit status
    make: *** [Greetings] Error 1
    

    Хотя с первой версией makefile программа работала без ошибок.

    c++ - Ошибка в Makefile

    правило

    $(OBJECTS): $(SOURCES)
        $(CC) $(CFLAGS) $< -o [email protected]
    

    не совсем корректно. Данное правило будет раскрыто как 2 правила

    build/main.o: src/main.cpp src/file.cpp
        g++ -c -Wall src/main.cpp -o build/main.o
    build/file.o: src/main.cpp src/file.cpp
        g++ -c -Wall src/main.cpp -o build/file.o
    

    Физика процесса такова:

    • $(OBJECTS): раскрываеться как 2 объектника, и Make система генерит по 1 правилу (таргету) для каждого из них. Тут все верно и ожидаемо.

    • $< подставляет первый элемент из списка после двоеточия. Вот здесь и есть основная проблема, изза которой оба таргета компилируют один и тот же файл.

    (то как Gnu Make раскрывает таргеты можно увидеть запустив 'make -np all > logfile')

    Соответственно, чтобы исправить ситуацию нужно каждому таргету подставлять правильный исходник. Чтобы автоматизировать процесс советую применить трюк с правилом

    %c: %o
        ...
    

    Ниже - пример билд системы, которая сама подхватывает все исходники в заданых директориях.

    Дервв файлов:

    .                                                                                                                                                
    |-- include                                                                                                                                      
    |   `-- func.h                                                                                                                                   
    |-- Makefile                                                                                                                                     
    |-- src                                                                                                                                          
    |   `-- func.c                                                                                                                                   
    `-- src2                                                                                                                                         
        `-- prog.c
    

    Makefile:

    #Name of outpu binary (exe) file;                
    PROGRAM_NAME := prog                             
    
    # project dirs
    export PROJECT_DIR := $(shell pwd)
    
    export SRC_DIRS    := $(PROJECT_DIR)/src
    export SRC_DIRS    += $(PROJECT_DIR)/src2
    
    export INCLUDE_DIR := $(PROJECT_DIR)/include
    export BUILD_DIR   := $(PROJECT_DIR)/build  
    export OBJ_DIR     := $(BUILD_DIR)/obj      
    export TARGET_DIR  := $(BUILD_DIR)/target   
    
    # this meens that "checkbuilddirs" is not a file but rule to run
    .PHONY: checkbuilddirs                                          
    
    #list of source files (with whitespace between them)
    SRC_FILES := $(foreach dirname, $(SRC_DIRS), $(wildcard $(dirname)/*.c))
    
    #Object files
    OBJ_FILES := $(foreach filename, $(notdir $(SRC_FILES)), $(OBJ_DIR)/$(filename:.c=.o))
    
    # dependence dirs. VPATH is defined by gnu Make and used to look for any files if we use %
    # It is like -I include dirs for compiller                                                
    VPATH:=$(sort $(SRC_DIRS) $(foreach srcfile,$(SRC_FILES),$(dir $(srcfile))))              
    
    # Include flags
    LOCAL_INCLUDES:=$(foreach dirs, $(INCLUDE_DIR), -I$(dirs))
    
    PROGRAM_FULL_NAME := $(TARGET_DIR)/$(PROGRAM_NAME)
    
    # list of lib-flags for compiler. For example for math.h use    -lm
    #LIB_FLAGS=-lm
    CC:=gcc
    CFLAGS:=-Wall $(LIB_FLAGS) $(LOCAL_INCLUDES)
    
    # rule to build application
    all: checkbuilddirs $(PROGRAM_FULL_NAME)
    
    VERBOSE ?= false
    ifeq ($(VERBOSE),true)
     QUIET  :=
    else
     QUIET  := @
    endif
    
    #build executable file
    $(PROGRAM_FULL_NAME): $(OBJ_FILES)
            @echo "Build program"
            $(QUIET)$(CC) $(CFLAGS) -o [email protected] $^
    
    # build all object file
    $(OBJ_DIR)/%.o: %.c
            @echo "Compile $(notdir $<)"
            $(QUIET)$(CC) -c $(CFLAGS) $< -o [email protected]
    
    #clean all
    clean:
            $(QUIET)rm -f $(OBJ_FILES) $(PROGRAM_FULL_NAME)
            $(QUIET)rm -f $(BUILD_DIR) -r
    
    checkbuilddirs: | $(BUILD_DIR) $(OBJ_DIR) $(TARGET_DIR)
    
    $(BUILD_DIR):
            $(QUIET)mkdir -p [email protected]
    
    $(OBJ_DIR):
            $(QUIET)mkdir -p [email protected]
    
    $(TARGET_DIR):
            $(QUIET)mkdir -p [email protected]
    

    Обратите внимание на сл. трюки:

    • команда $(wildcard $(dirname)/*.c) сама найдет все файлы *.с в указаных директориях

    • $(OBJ_DIR)/%.o: %.c - трактуется билд системой как универсальный таргет, который система применит для всех объектников. Обратите внимание, что исходный файл тут указан без указания папки.

    • переменная VPATH - как было сказано выше, все исходники в dependency указаны без папок. VPATH - специальная переменная, используеться системой Make как список папок в которых можно искать исходники. Очень похоже на PATH, только для поиска исходников.

    PS. пример проверен

    hello world в svn - занести в ignore лист все файлы Makefile.am

    Что делаю не так?

    • "Поспешность нужна только при ловле блох"
    • Про ключ --force во всех командах SVN рекомендую забыть наглухо

    Пока src не часть репозитория (? src), SVN ничего и никак не может сделать с его содержимым (его просто нет), ну и все родительские svn:ignore|svn:global-ignores на /src просто не работают

    Варианты решения (от более простого к более геморройному)

    • Сначала создать пустое дерево проекта, прописать игнор-лист (в корне - нормально для 1.8 с наследованием), добавить svn add src (все еще пустой) /src), после этого перенести все файлы на законное место в src

    Получается примерно так

    >dir /B /S
    z:\WC2\src
    z:\WC2\Makefile.am
    z:\WC2\configure.in
    
    >svn pl -v
    Properties on '.':
      svn:global-ignores
        Makefile.am
    
    >svn st --no-ignore
     M      .
    I       Makefile.am
    ?       configure.in
    ?       src
    

    (единственный M из-за propset)

    >svn add src
    A         src
    

    В /src как уже в части репо наследуется игнор-лист

    src>svn pl --show-inherited-props
    Inherited properties on '.',
    from 'Z:\WC2':
      svn:global-ignores
    

    и после копирования в него отложенных до того файлов

    >svn st --no-ignore
     M      .
    I       Makefile.am
    ?       configure.in
    A       src
    I       src\Makefile.am
    ?       src\main.c
    
    • Если пустое дерево не получается/лениво/есть возможность наколбасить, то процесс практически такой же, с единственным отличием - при svn add src надо помнить, что add рекурсивен до упора, и чтобы он не добавил лишнего, добавляем только /src, без содержимого (после добавления игнор-лист подхватится и будет легче) - это ключ --depth 'empty'

    • Универсальный, но неудобный для работы руками вариант. У svn add есть ключик --targets, параметр которого - файл со списком всех дополнительных файлов (и вообще объектов), которые нужно добавить в дополнение к поименованным в командной строке.

    Из dir|ls

    z:\WC2\src
    z:\WC2\Makefile.am
    z:\WC2\configure.in
    z:\WC2\.svn\pristine
    z:\WC2\.svn\tmp
    z:\WC2\.svn\wc.db
    z:\WC2\.svn\entries
    z:\WC2\.svn\format
    z:\WC2\src\Makefile.am
    z:\WC2\src\main.c
    

    достаточно легко получить (убиранием ненужного)

    configure.in
    src\main.c
    

    Make: современное руководство

    Руководство по современному Make

    Прежде чем пилить проект, наточи пилу!


    – Что говорит кошка, когда хочет кушать?
    – Мяу!

    – Что говорит собака, когда чует опасность?
    – Гав-гав!

    – А что говорит Вова когда хочет задеплоить проект?

    ansible-playbook -i inventory/production --tags "deploy" app-server.yml -vvv --become-user=app --extra-vars=extra.txt --vault-password-file="~/.ansible/vault.txt"


    Такие штуки незаметно отъедают ваше рабочее время и мозготопливо

    12 минут в день на рутину == 4 часа в месяц == ~1.5 рабочих недели в год


    Make позволяет нарулить себе библиотеку команд-шорткатов, которые будут отражать семантику происходящего, а не реализацию:

    make deploy, make logs, make feature resubscription

    Makefile хранится в основном репозитории вместе с кодом и становится живой документацией по частоиспользуемым при разработке командам.


    О, камон, Make - это же что-то очень древнее! Где ты это выкопал вообще?
    Я помню два года назад что-то собирал из сорцов мэйком. Разве он годится для чего-то еще?
    Слушай, я же не на сях пишу чтобы что-то там билдить. У меня уже есть npm/rake/maven, какой мне еще мэйк?

    Примерно так я думал, пока не разобрался в вопросе.

    По факту Make – это такой универсальный клей между кучей технологий используемых в современной разработке. У него минимальный оверхед по сравнению со специфичными для языков инструментами.

    Голосование в твиттере доказывает что с вероятностью в 75% вы не используете Makefile в своих проектах:

    Если это так, значит что у вас сейчас есть возможность потратив пару часов, сэкономить себе в будущем гораздо больше времени.

    Сейчас для меня Make стоит в одном ряду с Ansible, Docker и GitLab CI.
    Это добротный универсальный, по-хорошему хипстерский инструмент для упорядочивания хаоса в рабочих скриптах на проекте.

    Make всем хорош, но его официальная документация чересчур подробна, и, видимо, рассчитана на сисадминов-сишников, думающих на bash. Пробиться через неё без специальной подготовки нереально – слишком много специфики и деталей далеких от нужд современного разраба.

    Поэтому я запилил мини-руководство по полезным для современного разраба фишкам Make. Получилось так:

    Официальная документация:

    7500 строк

    Выжимка актуальной годноты:

    500 строк

    На самом деле вам не нужно читать и их 🙂


    Как же так?
    Что же делать?

    1. Чтобы понять основную идею использования Make и базовые правила, посмотрите ролик от Хекслета ▶️ (8.5 минут на полуторной скорости).

    2. Если решили применить Make на практике, то вам пригодится дока по основным фишкам и особенностям настройки Makefile.

    3. Если в стремлении навести порядок вы решили идти до конца, то для вас есть расширенная версия с примером построения цельного workflow для dev/staging/production-окружений.


    Руководство по современному Make

    1. Making your library of shortcuts
    2. Overcoming Make weirdness
    3. Multiple commands at once
    4. Subcommands
    5. Aliases
    6. Multiline commands
    7. Suppressing output
    8. Conditional execution
    9. Passing arguments

    10. Advanced scripting
    11. Putting things in order
    12. Naming conventions
    13. Full workflow automation
    14. Guiding principles


    ← Это еще дописывается

    Всё выглядит слишком просто?!
    Жмите на специальную ссылку для самых умных 😉


    💌Сорян!

    Полная версия еще не готова.
    Подпишитесь, и пришлю вам письмо, как только так сразу:


    Помогите! Я не могу открыть файл MAKEFILE!

    Загрузить Просмотр файлов Универсальный (File Magic) 

    Установить необязательные продукты - File Magic (Solvusoft) | EULA | Privacy Policy | Terms | Uninstall


    У меня есть правильная программа?

    Если на вашем компьютере не установлена ​​программа, совместимая с файлами MAKEFILE, вы не сможете ее открыть. Посмотрите, есть ли у вас одна из наиболее распространенных программ, связанных с файлами MAKEFILE, перечисленными ниже.

    Какой тип файла?

    Если вы не можете открыть файлы MAKEFILE, попробуйте выяснить тип файла. Это поможет вам найти нужную программу для ее открытия. Обычно файлы MAKEFILE считаются Необычные файлы. Однако вы можете искать тип файла, чтобы быть уверенным. Начните с правого щелчка на значке файла и нажмите «Свойства» («Дополнительная информация», если вы на Mac). Затем найдите тип файла в разделе «Тип файла» («Вид» на Mac).

    Может ли разработчик помочь?

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

    Программного обеспечения разработчик
    Makefile Unknown

    Попробуйте связаться с одним из этих разработчиков, чтобы узнать, как открыть файл MAKEFILE.

    Можно ли использовать универсальный просмотрщик файлов?

    Универсальный просмотрщик файлов - это путь, когда у вас есть файлы MAKEFILE, которые не могут быть открыты какой-либо другой программой. Установите универсальный просмотрщик файлов, например File Magic (Загрузить), и посмотрите, откроет ли ваш файл MAKEFILE. Помните, что если ваш файл несовместим, универсальный просмотрщик файлов откроет его только в двоичном формате.


    Рекомендуем

    Sorry, your browser doesn't support embedded videos.

    Загрузить Просмотр файлов Универсальный (File Magic) 

    Установить необязательные продукты - File Magic (Solvusoft) | EULA | Privacy Policy | Terms | Uninstall

    Что такое Makefile и как начать его использовать

    Введение

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

    # Bash
    touch ~/.bash_history
    ufw allow 3035/tcp || echo 'cant configure ufw'
    ufw allow http || echo 'cant configure ufw'
    docker run \
      -v /root/:/root/ \
      -v /etc:/etc \
      -v /var/run/docker.sock:/var/run/docker.sock \
      -v /var/tmp:/var/tmp \
      -v /tmp:/tmp \
      -v $PWD:/app \
      --network host \
      -w /app \
      --env-file .env \
      ansible ansible-playbook ansible/development.yml -i ansible/development --limit=localhost -vv
    grep -qxF 'fs.inotify.max_user_watches=524288' /etc/sysctl.conf || echo fs.inotify.max_user_watches=524288 | tee -a /etc/sysctl.conf || echo 'cant set max_user_watches' && sysctl -p
    sudo systemctl daemon-reload && sudo systemctl restart docker
    

    Эти команды являются лишь частью того, что необходимо выполнить при разворачивании проекта. В приведённом примере видно, что команды сами по себе длинные, содержат много флагов, а значит, их трудно не только запомнить, но и вводить вручную. Постоянно вести документацию становится сложнее с ростом проекта, она неизбежно устаревает, а порог входа для новичков становится выше, ведь уже никто не в состоянии вспомнить всех деталей проекта. Некоторые такие команды необходимо использовать каждый день, и даже не один раз в день.

    Со временем становится понятно, что нужен инструмент, способный объединить в себе подобные команды, предоставить к ним удобные шорткаты (более короткие и простые команды) и обеспечить самодокументацию проекта. Именно таким инструментом стал Makefile и утилита make. Этот гайд расскажет, как использование этих инструментов позволит свести процесс разворачивания проекта к нескольким коротким и понятным командам:

    # Bash
    make setup
    make start
    make test
    

    Что такое

    make и Makefile

    Makefile — это файл, который хранится вместе с кодом в репозитории. Его обычно помещают в корень проекта. Он выступает и как документация, и как исполняемый код. Мейкфайл скрывает за собой детали реализации и раскладывает “по полочкам” команды, а утилита make запускает их из того мейкфайла, который находится в текущей директории.

    Изначально make предназначалась для автоматизации сборки исполняемых программ и библиотек из исходного кода. Она поставлялась по умолчанию в большинство *nix дистрибутивов, что и привело к её широкому распространению и повсеместному использованию. Позже оказалось что данный инструмент удобно использовать и при разработке любых других проектов, потому что процесс в большинстве своём сводится к тем же задачам — автоматизация и сборка приложений.

    Применение мейка в проектах стало стандартом для многих разработчиков, включая крупные проекты. Примеры мейкфайла можно найти у таких проектов, как Kubernetes, Babel, Ansible и, конечно же, повсеместно на Хекслете.

    Синтаксис

    Makefile

    make запускает цели из Makefile, которые состоят из команд:

    # Makefile
    цель1: # имя цели, поддерживается kebab-case и snake_case
    	команда1 # для отступа используется табуляция, это важная деталь 
    	команда2 # команды будут выполняться последовательно и только в случае успеха предыдущей
    

    Но недостаточно просто начать использовать мейкфайл в проекте. Чтобы получить эффект от его внедрения, понадобится поработать над разделением команд на цели, а целям дать семантически подходящие имена. Поначалу, перенос команд в Makefile может привести к свалке всех команд в одну цель с «размытым» названием:

    # Makefile
    up: # разворачивание и запуск
    	cp -n .env.example .env
    	touch database/database.sqlite
    	composer install
    	npm install
    	php artisan key:generate
    	php artisan migrate --seed
    	heroku local -f Procfile.dev # запуск проекта
    

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

    # Makefile
    env-prepare: # создать .env-файл для секретов
    	cp -n .env.example .env
    
    sqlite-prepare: # подготовить локальную БД
    	touch database/database.sqlite
    
    install: # установить зависимости
    	composer install
    	npm install
    
    key: # сгенерировать ключи
    	php artisan key:generate
    
    db-prepare: # загрузить данные в БД
    	php artisan migrate --seed
    
    start: # запустить приложение
    	heroku local -f Procfile.dev
    

    Теперь, когда команды разбиты на цели, можно отдельно установить зависимости командой make install или запустить приложение через make start. Но остальные цели нужны только при первом разворачивании проекта и выполнять их нужно в определённой последовательности. Говоря языком мейкфайла, цель имеет пререквизиты:

    # Makefile
    цель1: цель2 # такой синтаксис указывает на зависимость задач — цель1 зависит от цель2
    	команда2 # команда2 выполнится только в случае успеха команды из цель2
    
    цель2:
    	команда1
    

    Задачи будут выполняться только в указанной последовательности и только в случае успеха предыдущей задачи. Значит, можно добавить цель setup, чтобы объединить в себе все необходимые действия:

    # Makefile
    setup: env-prepare sqlite-prepare install key db-prepare # можно ссылаться на цели, описанные ниже
    
    env-prepare:
    	cp -n .env.example .env
    
    sqlite-prepare:
    	touch database/database.sqlite
    
    install:
    	composer install
    	npm install
    
    key:
    	php artisan key:generate
    
    db-prepare:
    	php artisan migrate --seed
    
    start:
    	heroku local -f Procfile.dev
    

    Теперь развернуть и запустить проект достаточно двумя командами:

    # Bash
    make setup # выполнит последовательно: env-prepare sqlite-prepare install key db-prepare
    make start
    

    Благодаря проделанной работе Makefile, команды проекта вместе с флагами сведены в Makefile. Он обеспечивает правильный порядок выполнения и не важно, какие при этом задействованы языки и технологии.

    Продвинутое использование

    Фальшивая цель

    Использование make в проекте однажды может привести к появлению ошибки make: <имя-цели> is up to date., хотя всё написано правильно. Зачастую, её появление связано с наличием каталога или файла, совпадающего с именем цели. Например:

    # Makefile
    test: # цель в мейкфайле
    	php artisan test
    
    # Bash
    $ ls
    Makefile
    test # в файловой системе находится каталог с именем, как у цели в мейкфайле
    
    $ make test # попытка запустить тесты
    make: `test` is up to date.
    

    Как уже говорилось ранее, изначально make предназначалась для сборок из исходного кода. Поэтому она ищет каталог или файл с указанным именем, и пытается собрать из него проект. Чтобы изменить это поведение, необходимо в конце мейкфайла добавить .PHONY указатель на цель:

    # Makefile
    test:
    	php artisan test
    
    .PHONY: test
    
    # Bash
    $ make test
    ✓ All tests passed!
    

    Последовательный запуск команд и игнорирование ошибок

    Запуск команд можно производить по одной: make setup, make start, make test или указывать цепочкой через пробел: make setup start test. Последний способ работает как зависимость между задачами, но без описания её в мейкфайле. Сложности могут возникнуть, если одна из команд возвращает ошибку, которую нужно игнорировать. В примерах ранее такой командой было создание .env-файла при разворачивании проекта:

    # Makefile
    env-prepare:
    	cp -n .env.example .env # если файл уже создан, то повторный запуск этой команды вернёт ошибку
    

    Самый простой (но не единственный) способ «заглушить» ошибку — это сделать логическое ИЛИ прямо в мейкфайле:

    # Makefile
    env-prepare:
    	cp -n .env.example .env || true # теперь любой исход выполнения команды будет считаться успешным
    

    Добавлять такие хаки стоит с осторожностью, чтобы не «выстрелить себе в ногу» в более сложных случаях.

    Переменные

    Зачастую в команды подставляют параметры для конфигурации, указания путей, переменные окружения и make тоже позволяет этим управлять. Переменные можно прописать прямо в команде внутри мейкфайла и передавать их при вызове:

    # Makefile
    say:
    	echo "Hello, $(HELLO)!"
    
    # Bash
    $ make say HELLO=World
    echo "Hello, World!"
    Hello, World!
    
    $ make say HELLO=Kitty
    echo "Hello, Kitty!"
    Hello, Kitty!
    

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

    # Makefile
    HELLO?=World # знак вопроса указывает, что переменная опциональна. Значение после присвоения можно не указывать.
    
    say:
    	echo "Hello, $(HELLO)!"
    
    # Bash
    $ make say
    echo "Hello, World!"
    Hello, World!
    
    $ make say HELLO=Kitty
    echo "Hello, Kitty!"
    Hello, Kitty!
    

    Некоторые переменные в Makefile имеют названия отличные от системных. Например, $PWD называется $CURDIR в мейкфайле:

    # Makefile
    project-env-generate:
    	docker run --rm -e RUNNER_PLAYBOOK=ansible/development.yml \
    		-v $(CURDIR)/ansible/development:/runner/inventory \ # $(CURDIR) - то же самое, что $PWD в терминале
    		-v $(CURDIR):/runner/project \
    		ansible/ansible-runner
    

    Заключение

    В рамках данного гайда было рассказано об основных возможностях Makefile и утилиты make. Более плотное знакомство с данным инструментом откроет множество других его полезных возможностей: условия, циклы, подключение файлов. В компаниях, где имеется множество проектов, написанных разными командами в разное время, мейкфайл станет отличным подспорьем в стандартизации типовых команд: setup start test deploy ....

    Возможность описывать в мейкфале последовательно многострочные команды позволяет использовать его как «универсальный клей» между менеджерами языков и другими утилитами. Широкая распространённость этого инструмента и общая простота позволяют внедрить его в свой проект достаточно легко, без необходимости доработок. Но мейкфайл может быть по-настоящему большим и сложным, это можно увидеть на примере реальных проектов:

    Дополнительные материалы

    Мейкфайлы, использованные при составлении гайда:

    Что такое компилятор? » Универсальный файл Makefile

    для C / C ++ · GitHub

    Универсальный файл Makefile для C / C ++ · GitHub

    Мгновенно делитесь кодом, заметками и фрагментами.

    Универсальный Makefile для C / C ++

    # - вкл /
    # - *.h
    # - src /
    # - * .c
    # - * .cpp
    # - объект /
    # - * .o
    # - основной
    TARGET: = main
    ИСТОЧНИКИ: = $ (подстановочный знак src / *.c src / *. cpp)
    ОБЪЕКТОВ: = $ (patsubst src%, obj%, $ (patsubst% .c,%. O, $ (patsubst% .cpp,%. O, $ (SOURCES))))
    ВКЛЮЧИТЬ: = -I.
    LIBPATH: =
    LIBS: =
    ФЛАГИ: = -Стена
    CCFLAGS: = $ (FLAGS) -std = c99
    CXXFLAGS: = $ (ФЛАГИ)
    CC: = gcc
    Cxx: = g ++
    все: $ (ОБЪЕКТЫ)
    $ (CC) $ (CCFLAGS) $ (INCLUDE) $ (OBJECTS) -o $ (TARGET) $ (LIBPATH) $ (LIBS)
    %.o: ../src/%.c
    $ (CC) $ (CCFLAGS) $ (ВКЛЮЧИТЬ) -c $ <-o $ @
    % .o: ../src/%.cpp
    $ (CXX) $ (CXXFLAGS) $ (ВКЛЮЧИТЬ) -c $ <-o $ @
    .PHONY: чистая помощь
    чистых:
    rm -rf obj / *
    п.м. -ф $ (ЦЕЛЬ)
    справка:
    @grep -E '^ [a-zA-Z _-] + :.*? ##. * $$ '$ (MAKEFILE_LIST) | сортировать | awk 'BEGIN {FS = ":. *? ##"}; {printf "\ 033 [36m% -30s \ 033 [0m% s \ n", $$ 1, $$ 2} '
    Вы не можете выполнить это действие в настоящее время. Вы вошли в систему с другой вкладкой или окном. Перезагрузите, чтобы обновить сеанс. Вы вышли из системы на другой вкладке или в другом окне. Перезагрузите, чтобы обновить сеанс.

    Минимальный и универсальный make-файл для языка C · GitHub

    Минимальный и универсальный make-файл для языка C · GitHub

    Мгновенно делитесь кодом, заметками и фрагментами.

    Минимальный и универсальный make-файл для языка C

    CC = gcc
    CFLAGS = -Wall -Wextra -Wpedantic
    LDFLAGS =
    ИСТОЧНИКИ = $ (подстановочный знак *.в)
    ОБЪЕКТОВ = $ (ИСТОЧНИКИ: .c = .o)
    ИСПОЛНИТЕЛЬНЫЙ = exec
    все: $ (ВЫПОЛНИТЕЛЬНО)
    ./$(EXECUTABLE)
    $ (ИСПОЛНИТЕЛЬНЫЙ): $ (ОБЪЕКТЫ)
    $ (CC) $ (CFLAGS) $ (ОБЪЕКТЫ) -o $ (ИСПОЛНИТЕЛЬНЫЙ)
    $ (ОБЪЕКТЫ): $ (ИСТОЧНИКИ)
    $ (CC) $ (CFLAGS) -c $ <-o $ @
    чистых:
    п.м. $ (ИСПОЛНИТЕЛЬНЫЙ) $ (ОБЪЕКТЫ)
    Вы не можете выполнить это действие в настоящее время.Вы вошли в систему с другой вкладкой или окном. Перезагрузите, чтобы обновить сеанс. Вы вышли из системы на другой вкладке или в другом окне. Перезагрузите, чтобы обновить сеанс.

    Супер-простой Makefile для средних проектов C / C ++

    Я часто использовал Make для небольших проектов, но для более крупных это было слишком утомительно. До недавнего времени я хотел, чтобы моя система сборки выполняла за меня четыре вещи, которые я не знал, как сделать в Make:

    • Сборки вне исходного кода (объектные файлы выгружаются в каталог, отдельный от исходного)
    • Автоматические (и точные!) Зависимости заголовков
    • Автоматическое определение списка объектных / исходных файлов
    • Автоматическое создание флагов подключаемых каталогов

    Вот простой Makefile, который будет делать все это и работать с C, C ++ и сборкой:

     
    
    TARGET_EXEC? = А.вне
    
    BUILD_DIR? = ./Build
    SRC_DIRS? = ./Src
    
    SRCS: = $ (shell find $ (SRC_DIRS) -name * .cpp -or -name * .c -or -name * .s)
    OBJS: = $ (SRCS:% = $ (BUILD_DIR) /%. O)
    DEPS: = $ (OBJS: .o = .d)
    
    INC_DIRS: = $ (поиск оболочки $ (SRC_DIRS) -тип d)
    INC_FLAGS: = $ (addprefix -I, $ (INC_DIRS))
    
    CPPFLAGS? = $ (INC_FLAGS) -MMD -MP
    
    $ (BUILD_DIR) / $ (TARGET_EXEC): $ (OBJS)
    $ (CC) $ (OBJS) -o $ @ $ (LDFLAGS)
    
    # сборка
    $ (BUILD_DIR) /%. S.o:% .s
    $ (MKDIR_P) $ (dir $ @)
    $ (AS) $ (ASFLAGS) -c $ <-o $ @
    
    # c исходный код
    $ (BUILD_DIR) /%.c.o:% .c
    $ (MKDIR_P) $ (dir $ @)
    $ (CC) $ (CPPFLAGS) $ (CFLAGS) -c $ <-o $ @
    
    # c ++ исходный код
    $ (BUILD_DIR) /%. Cpp.o:% .cpp
    $ (MKDIR_P) $ (dir $ @)
    $ (CXX) $ (CPPFLAGS) $ (CXXFLAGS) -c $ <-o $ @
    
    
    .PHONY: чистый
    
    чистый:
    $ (RM) -r $ (BUILD_DIR)
    
    -включить $ (DEPS)
    
    MKDIR_P? = Mkdir -p
    
    
      

    Не так уж и плохо!
    Кроме того, если вас не интересуют сборки вне исходного кода, вы можете использовать еще более простой Makefile, который использует преимущества встроенных неявных правил:

     
    
    
    ЦЕЛЬ? = А.вне
    SRC_DIRS? = ./Src
    
    SRCS: = $ (shell find $ (SRC_DIRS) -name * .cpp -or -name * .c -or -name * .s)
    OBJS: = $ (добавляет суффикс .o, $ (базовое имя $ (SRCS)))
    DEPS: = $ (OBJS: .o = .d)
    
    INC_DIRS: = $ (поиск оболочки $ (SRC_DIRS) -тип d)
    INC_FLAGS: = $ (addprefix -I, $ (INC_DIRS))
    
    CPPFLAGS? = $ (INC_FLAGS) -MMD -MP
    
    $ (ЦЕЛЬ): $ (OBJS)
    $ (CC) $ (LDFLAGS) $ (OBJS) -o $ @ $ (LOADLIBES) $ (LDLIBS)
    
    .PHONY: чистый
    чистый:
    $ (RM) $ (ЦЕЛЬ) $ (OBJS) $ (DEPS)
    
    -включить $ (DEPS)
    
      

    Чтобы использовать один из них, поместите код Make в файл с вызовом Makefile (убедитесь, что символы TAB копируются! Make очень придирчивы к ним) и весь исходный код и заголовки в каталоге или подкаталоге ./ src (вы можете изменить этот каталог, изменив SRC_DIRS). Затем убедитесь, что CC и CFLAGS установлены на то, что вам нужно для вашего проекта, или просто используйте настройки по умолчанию.
    Затем введите и сделайте .

    Если у вас возникнут проблемы, может оказаться полезным выполнение команды make -d .

    Вот обзор того, как это работает:

    Сборки вне исходного кода

    Я хочу, чтобы все артефакты из сборки попали в какой-то каталог (я обычно называю его «./build»), отдельный от источника.Это позволяет легко выполнить чистку (всего rm -rf ./build ), даже если там останутся другие артефакты, кроме тех, которые сгенерированы с помощью Make. Это также делает многое другое, например, поиск источника с помощью grep'а, намного приятнее.

    Для того, чтобы сделать это в Make, вам в основном нужно просто добавить выходной каталог в начало правил шаблона. Например, вместо шаблона типа: % .o:% .c , который будет отображать ваши файлы .c для файлов .o в том же каталоге, вы можете использовать $ (BUILD_DIR)%.о:% .c .

    Автоматические зависимости заголовков

    Обработка зависимостей заголовков - , пожалуй, самая утомительная вещь в использовании классической техники Make. Тем более, что, если вы его испортите, вы не получите явных ошибок - вещи просто не будут повторно компилироваться, когда они должны быть. Это может привести к тому, что файлы .o имеют разные представления о том, как выглядят типы или прототипы.

    Здесь есть документация по этому поводу. Однако документы, похоже, предполагают, что файлы зависимостей генерируются на отдельном этапе от этапа компиляции, что усложняет ситуацию.

    Если вы генерируете файлы зависимостей как часть этапа компиляции, все становится намного проще. Чтобы сгенерировал файлов зависимостей, все, что вам нужно сделать, это добавить несколько флагов в команду компиляции (поддерживается как Clang, так и GCC):

    • -MMD -MP
      , который создаст файл .d рядом с файлом .o. Затем для используйте файлы .d, вам просто нужно найти их все:
    • DEPS: = $ (OBJS: .o = .d)
      , а затем - включить их:
    • -включить $ (DEPS)

    Автоматическое определение списка объектных / исходных файлов

    Сначала найдите все исходные файлы в указанных исходных каталогах.Самый простой и быстрый способ, который я нашел, - это просто раскошелиться и использовать find .

    Автоматическое создание флагов каталога включения

    Я использовал похожую технику для генерации флагов включаемого каталога. Найдите все каталоги в указанных исходных каталогах:

    • INC_DIRS: = $ (shell find $ (SRC_DIRS) -type d)
      И затем префикс -I :
    • INC_FLAGS: = $ (addprefix -I, $ (INC_DIRS))

    Надеюсь, эти методы будут вам полезны.

    Universal Makefile - Matan Silver

    Build System Frustration

    Как и любой разработчик, мне нравится использовать правильный инструмент для работы - когда дело доходит до кодирования проектов, выбор языка может значительно повлиять на производительность и рабочий процесс проекта. будущее. Для быстрых CLI и веб-проектов мне нравится использовать Python; для коротких одноразовых и специфичных для предметной области скриптов подойдет оболочка; для собственных примитивов параллелизма и простого языка я использую Golang.Многие языки, которые захватывают мир штормом, такие как Golang, Rust, Kotlin, Crystal и Nim, имеют встроенные или другие общепринятые системы сборки, которые относительно легко использовать для создания легко собираемых и устанавливаемых пакетов. Однако, будучи студентом-электротехником, который часто переходит на языки более низкого уровня, такие как C и C ++, системы сборки проектов представляют собой более серьезный барьер для входа. Чтобы решить эту проблему, у меня есть относительно простой «универсальный» Makefile для C и C ++ (и смешанный), который можно легко адаптировать для многих проектов.

    Я также экспериментировал с CMake как с системой мета-сборки. CMake и подобные утилиты имеют свои преимущества - они упрощают кросс-компиляцию, их можно использовать с множеством различных систем сборки (даже одновременно), такими как XCode, Make, Ninja и т. Д. Однако я обнаружил, что CMake позволяет будет излишним для небольших проектов с небольшим количеством (0-5) исполняемых целей. Этот Makefile лучше работает с контролем версий (меньше вещей нужно вставлять в .gitignore) и требует меньшего когнитивного понимания - обзор неявных правил Make охватывает почти все.

     1 
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20

    21
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
     
    CFLAGS = -std = c99 -pedantic -O1



    ? имя
    BUILD_DIR? =./ build
    SRC_DIRS? = ./src

    SRCS: = $ (shell find $ (SRC_DIRS) -name * .cpp -or -name * .c -or -name * .s)
    OBJS: = $ (SRCS: % = $ (BUILD_DIR) /%. O)

    DEPS: = $ (OBJS: .o = .d)

    INC_DIRS: = $ (shell find $ (SRC_DIRS) -type d)
    INC_FLAGS: = $ (addprefix -I, $ (INC_DIRS))

    CPPFLAGS = $ (INC_FLAGS) -MMD -MP -ggdb
    CXXFLAGS = -std = c ++ 14 -stdlib = libc ++
    LDFLAGS = -stdlib = libc ++










    $ (BUILD_DIR) / $ (TARGET_EXEC): $ (OBJS)
    $ (CXX) $ (OBJS) -o $ @ $ (LDFLAGS)


    $ (BUILD_DIR) /%.так:% .s
    $ (MKDIR_P) $ (dir $ @)
    $ (AS) $ (ASFLAGS) -c $ <-o $ @


    $ (BUILD_DIR) /%. co:% .c
    $ (MKDIR_P) $ (dir $ @)
    $ (CC) $ (CPPFLAGS) $ (CFLAGS) -c $ <-o $ @


    $ (BUILD_DIR) /%. Cpp.o:% .cpp
    $ ( MKDIR_P) $ (dir $ @)
    $ (CXX) $ (CPPFLAGS) $ (CXXFLAGS) -c $ <-o $ @


    .PHONY: clean

    clean:
    $ (RM) -r $ (BUILD_DIR )

    -include $ (DEPS)

    MKDIR_P? = Mkdir -p


    .PHONY: запустить
    запустить: $ (BIN)
    ./ build / project-name


    При написании этого Makefile я много использовал идеи из следующих проектов:

    Моя версия добавляет CXXFLAGS и CFLAGS, а также переключатель отладчика для компиляции GCC с отладочными символами.

    Обновление

    :

    При разработке проекта samurai-c ++ я обнаружил, что мне нужны две цели: одна утилита командной строки, которая в конечном итоге станет конечным продуктом, и один двоичный файл модульного теста для запуска во время разработки.Я пытался выполнить это разделение раньше, но столкнулся с проблемами, связанными с наличием нескольких списков объектов и зависимостей, которые мешали друг другу при вызове -include (DEPS). Я придумал следующее решение, которое поддерживает все объекты в общем списке объектов, все зависимости файлов, но держит два файла с функциями main () отдельно друг от друга, чтобы избежать ошибок компоновщика.

    Я изменил объявления переменных на следующие:

     1 
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
     TARGET_ 
    TEST_EXEC = unittest
    BUILD_DIR =./ build
    SRC_DIRS = ./src
    #HEAD_DIRS? = ./include

    SRCS: = $ (shell find $ (SRC_DIRS) -name * .cpp -or -name * .c -or -name * .s)
    SRCS_TARGET: = $ (shell find $ (SRC_DIRS) -name * .cpp! -Name unittest.cpp -or -name * .c -or -name * .s)
    SRCS_TEST: = $ (shell find $ (SRC_DIRS) - name * .cpp! -name имя-проекта.cpp -or -name * .c -or -name * .s)
    OBJS: = $ (SRCS:% = $ (BUILD_DIR) /%. o)
    OBJS_TARGET: = $ (SRCS_TARGET:% = $ (BUILD_DIR) /%. O)
    OBJS_TEST: = $ (SRCS_TEST:% = $ (BUILD_DIR) /%.o)
    DEPS: = $ (OBJS: .o = .d)

    И я изменил две двоичные цели следующим образом:
     1 
    2
    3
    4
    5
    6
    7
    8
     .PHONY: все 
    все: $ (BUILD_DIR) / $ (TARGET_EXEC) $ (BUILD_DIR) / $ (TEST_EXEC)
    $ (BUILD_DIR) / $ (TARGET_EXEC): $ (OBJET_EXEC )_3 CXX) $ (OBJS_TARGET) -o $ @ $ (LDFLAGS)

    $ (BUILD_DIR) / $ (TEST_EXEC): $ (OBJS_TEST)
    $ (CXX) $ (OBJS_TEST) -o $ @ $ (LDFLAGS)


    Таким образом, каждый двоичный файл связывается с правильным набором объектов.

    Универсальный простой Makefile - :) bikulov.ORG

    Я часто работаю на своем нетбуке, поэтому предпочитаю использовать Sublime Text с Makefiles вместо полнофункциональных IDE. Для автоматизации процесса сборки я построил (с помощью Василия Пикарда и примеров из Интернета) универсальный Makefile. Предполагается следующая структура файлов.

    Все исходники помещаются в подкаталог src . Промежуточные объектные файлы помещаются в подкаталог obj , он должен быть создан перед компиляцией.Полученный двоичный файл помещается в подкаталог bin . Если вы используете gengetopt в качестве парсера аргументов командной строки, назовите файл ggo как cmdline.ggo и * .c и * .h Файлы будут обновлены автоматически при изменении ggo.

    Следующий make-файл настроен на использование компиляторов mpi, включает библиотеки OpenGL, CUDA и GLUT, а также компилирует исходный код CUDA. Не стесняйтесь удалять ненужные фрагменты и изменять двоичное имя приложения (в первой строке).

      ПРИЛОЖЕНИЕ: = bin / app
    ИСТОЧНИКИ: = $ (подстановочный знак src / *. Cpp src / *. Cu src / *. C)
    ОБЪЕКТЫ: = $ (patsubst src%, obj%, $ (patsubst% .cu,%. Device.o, $ (patsubst% .cpp,%. O, $ (patsubst% .c,%. O, $ (ИСТОЧНИКИ) )))))
    
    ВКЛЮЧИТЬ: = -I / usr / local / cuda / include
    LIBPATH: = -L / usr / локальный / cuda / lib64
    LIBS: = -lcudart -lGL -lglut
    
    ФЛАГИ: = -O3 -ffast-math -Wall -Werror -fopenmp
    CCFLAGS: = $ (ФЛАГИ)
    CXXFLAGS: = $ (ФЛАГИ) -std = c ++ 0x
    
    GENCODE_FLAGS: = -gencode arch = compute_20, code = sm_20 -gencode arch = compute_30, code = sm_30 -gencode arch = compute_35, code = sm_35
    NVCCFLAGS: = $ (GENCODE_FLAGS) --compiler-options -fno-strict-aliasing -lineinfo -use_fast_math -Xptxas -dlcm = cg
    
    CC: = mpicc
    CXX: = mpicxx
    NVCC: = / usr / local / cuda / bin / nvcc
    
    все: $ (ОБЪЕКТЫ)
    $ (CXX) $ (CXXFLAGS) $ (ВКЛЮЧИТЬ) $ (ОБЪЕКТЫ) -o $ (ИМЯ ПРИЛОЖЕНИЯ) $ (LIBPATH) $ (LIBS)
    
    obj / cmdline.о: src / cmdline.c
    $ (CC) -Wno-unused-but-set-variable -c $ <-o $ @
    
    src / cmdline.c: src / cmdline.ggo
    gengetopt --input = src / cmdline.ggo --output-dir = src --include-getopt
    
    % .o: ../src/%.c
    $ (CC) $ (CCFLAGS) $ (ВКЛЮЧИТЬ) -c $ <-o $ @
    
    % .o: ../src/%.cpp
    $ (CXX) $ (CXXFLAGS) $ (ВКЛЮЧИТЬ) -c $ <-o $ @
    
    % .device.o: ../src/%.cu
    $ (NVCC) $ (NVCCFLAGS) -c $ <-o $ @
    
    чистый:
    rm -rf obj / *
    rm -f $ (ИМЯ ПРИЛОЖЕНИЯ)
      
    Проект

    Makefiles - Руководство

    Введение

    Makefiles Project - это набор универсальных make-файлов для сборки любого проекта. с инструментами Gnu (gcc, Gnu make и т. д.). Идея в том, что вы пишете тривиальный Makefile, который просто включает эти универсальные make-файлы, а все остальное делает для ты. Это позволяет очень легко создавать новые проекты и обеспечивать согласованность. сборок в мультибиблиотечном проекте.

    Эти правила выполняют следующие задачи:

    • скомпилировать все файлы .cpp или .c в текущем каталоге
    • архивировать объектные файлы вместе в единую объектную библиотеку
    • построить все остальные проекты, от которых зависит текущий проект
    • ссылка на изображение или общую библиотеку
    • опционально создать приложение wxWidgets

    Примечание: Эти файлы предназначены *** ТОЛЬКО для Gnu make ***.

    Make-файл содержит набор правил. Объем написания правил ограничен синтаксис make-файла. Поэтому было необходимо ограничить гибкость универсального make-файла для возможности записи это вообще.

    Написание Makefile

    Файл gcc.mak должен быть включен в ваш Makefile, чтобы установить значение по умолчанию. сделать правила для gcc.

    включить ../makefiles/gcc.mak
     

    Или любой другой путь к файлу gcc.mak.Относительный путь, а не рекомендуется абсолютный путь.

    Вам необходимо определить переменные в вашем Makefile до того, как будет включать контролировать, что будет построено:

    ИЗОБРАЖЕНИЕ: = <путь>

    Это указывает, что вы создаете программу (исполняемый образ), и путь дает как каталог, так и имя изображения.

    например ИЗОБРАЖЕНИЕ: = демо

    ОБЪЕДИНЕННЫЕ: = <имя>

    Это означает, что вы создаете общую библиотеку и имя это необработанное имя библиотеки i.е. без префикса lib или суффикса .so.

    например ПОДЕЛИТЬСЯ: = демо

    Будет создана разделяемая библиотека libdemo.so.

    ни ИЗОБРАЖЕНИЕ, ни ОБМЕН

    Без IMAGE или SHARED make-файл создает статическую библиотеку, содержащую все скомпилированные объектные файлы.

    БИБЛИОТЕКИ: = <путь> ...

    Разделенный пробелами список библиотечных каталогов для включения / связывания в сборку. Только библиотеки управляемая этой системой make, и набор правил должен быть в списке.См. Позже, как управлять сторонние библиотеки.

    например БИБЛИОТЕКИ: = ../stlplus3/containers

    Каждый путь должен указывать на исходный каталог проекта, которым также управляет система makefile.

    Обратите внимание, что часть пути, используемая для создания имени проекта - т.е. последняя часть должна быть именем каталога и не должна быть "..". В в этом случае название проекта - «контейнеры».

    например БИБЛИОТЕКИ: = ../stlplus3/source

    В этом случае суффикс «/ source» игнорируется, а имя проекта - «stlplus3».Это имя проекта должно быть именем каталога и не должно быть "..".

    В дополнение к этим ключевым переменным, которые управляют тем, что должно быть построено, есть другие переменные для управления детали сборки. Эти переменные-модификаторы следует добавить ПОСЛЕ включения файла gcc.mak. Большинство полезны те, которые позволяют включать другие библиотеки в сборку. которые не управляются этой системой make.

    Изменяемые переменные:

    CPPFLAGS + = <параметры>

    Флаги препроцессора, общие для компиляций C и C ++.Большинство распространенными и полезными из них являются дополнительные пути включения для библиотек не управляется этой системой make и, следовательно, не находится в БИБЛИОТЕКАХ список.

    CFLAGS + = <параметры>

    Устанавливает дополнительные параметры для компилятора gcc при компиляции кода C только (файлы .c). Обычно это параметры, специфичные для языка C.

    например CFLAGS + = -O3

    CXXFLAGS + = <параметры>

    Устанавливает дополнительные параметры для компилятора gcc при компиляции кода C ++. (.cpp файлы) только. Обычно это параметры, специфичные для языка C ++.

    например CXXFLAGS + = -ftemplate-depth-50

    LDFLAGS + = <параметры>

    Устанавливает дополнительные флаги для компоновщика. Они размещаются с до . объектные файлы в команде ссылки

    например LDFLAGS + = -s

    LDLIBS + = <объекты>

    Дополнительные объектные файлы или библиотеки, которые должны быть включены в ссылку, которые не управляются этим сделать систему.Они помещаются после объектных файлов в команде связывания.

    Переменная LDLIBS может содержать имена объектных файлов или архивных библиотек:

    например LDLIBS + = -lxyz

    Итак, в приведенном выше случае линкуется библиотека libxyz.a.

    Этот параметр также используется для создания зависимости от разделяемых библиотек (.dll в Windows или .so в Unix):

    например LDLIBS + = -lxyz

    Таким образом, в приведенном выше случае библиотека, с которой выполняется компоновка, - это либо xyz.dll в Windows или libxyz.so в Unix.

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

    например LDLIBS + = -L ../ xyz -lxyz

    Обратите внимание на использование '+ =' not ': ='. Вы добавляете дополнительные параметры к существующим параметры, созданные системой make.

    Есть также некоторые переменные, которые можно использовать для включения или выключения. определенные варианты. Они принимают значения «включено» или «выключено»:

    RELEASE = on | RELEASE = выкл. (По умолчанию)

    При включении создает версию выпуска заявление.У этого нет отладочной информации и компилятора включена оптимизация для создания небольшой быстрой программы. Когда выключен, это создает отладочную версию с полным отладчиком информация для использования с gdb и с оптимизацией компилятора отключен. Примечание: обычно это включается из командной строки, а не из Makefile: см. «Варианты сборки».

    GPROF = вкл | GPROF = выкл. (По умолчанию)

    При включении строит профилирующую версию приложения. для использования с профилировщиком gprof.В выключенном состоянии это создает нормальная версия. Примечание: обычно это включается командой строку, а не из Makefile: см. Варианты строительства.

    UNICODE = on | UNICODE = выключено (по умолчанию)

    При включении включает поддержку символов Юникода. По факту, это просто включает директивы компилятора -DUNICODE и -D_UNICODE, поэтому программист должен убедиться, что они привязаны к Поддержка Unicode. Большинство библиотек следуют этому соглашению.

    СТАТИЧЕСКИЙ = вкл | СТАТИЧЕСКИЙ = выкл. (По умолчанию)

    При включении заставляет компилятор связывать, где это возможно, со статическими библиотеками C и C ++ для минимизации зависимости от общих библиотеки.Это устраняет зависимость от общей библиотеки C ++ STL и может удалить и другие зависимости, в зависимости от заявление. Вам все равно нужно проверить оставшиеся неразрешимые зависимости разделяемых библиотек. Если выключено, build будет использовать общие библиотеки, где это возможно, чтобы минимизировать это размер приложения.

    VERBOSE = on | VERBOSE = выкл. (По умолчанию)

    В выключенном состоянии дает краткую однострочную сводку по каждому build команду, если нет ошибок, в этом случае стандартная ошибка отчет дан.При включении команды сборки переключился в подробный режим, чтобы дать больше диагностики.

    Достигайте целей

    Есть несколько целей, которые можно использовать с make. Идея в том, что вы запускаете make из командной строки с такой целью:

    $ make <цель>
     

    Цель не является обязательной; если отсутствует, то цель по умолчанию - использовал.

    Цели, которые в настоящее время поддерживаются проектом Makefiles:

    все (по умолчанию)

    Эквивалент цели 'build', но есть обстоятельства, когда вы можете изменить набор целей, отображаемых на «все» цели.

    сборка

    Скомпилируйте все исходные файлы и свяжите образ, если он есть (т.е. если установлена ​​переменная IMAGE) или разделяемая библиотека, если есть равен единице (т.е. установлен SHARED).

    пробег

    Запустить программу, если она есть (т. Е. Если переменная IMAGE набор). Сначала будет запущена цель сборки.

    приборка

    Удалите промежуточные файлы, созданные во время компиляции.

    чистый

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

    Это полезно, если вы внесли изменения в параметры компиляции и вам нужно начать с чистого листа.

    Цели можно комбинировать в командной строке. Например, чтобы построить и запустить проект:

    $ make build run
     

    Или даже, чтобы удалить все сгенерированные файлы, выполните сборку из stratch и затем запустите:

    $ сделать чистую сборку запустить
     

    Название проекта

    Имя проекта - это либо имя каталога, если каталог называется "источником", и в этом случае каталог выше что используется.Makefile использует имя проекта для создания имя библиотеки.

    Например, вот структура каталогов проекта STLplus:

    • stlplus
      • источник
        • Makefile
        • stlplus.hpp
        • cli_parser.cpp
        • ...
      • сообщения
      • документов

    В приведенном выше примере для Makefile, выделенного жирным шрифтом, имя проекта - «stlplus». Это потому, что каталог, содержащий Makefile, называется "источником", поэтому каталог выше что используется.Этот каталог называется «stlplus».

    Можно было бы использовать более простую структуру каталогов:

    • stlplus
      • Makefile
      • stlplus.hpp
      • cli_parser.cpp
      • ...

    В этом случае имя проекта по-прежнему «stlplus». Это потому, что каталог, содержащий исходный код, называется "stlplus", поэтому он используется как название проекта.

    Имена проектов должны быть уникальными в рамках разработки конкретного приложения.Например, вы можете работать над приложением, которое разделено на несколько проектов для кодирования. Каждый проект, составляющий у приложения должно быть другое название проекта.

    В проекте весь исходный код, то есть все заголовки (.h или .hpp) и файлы реализации (.c или .cpp) - должны находиться в одном каталоге. Makefile должен находиться в этом же каталоге. Структура с несколькими каталогами должна быть построен как несколько проектов, по одному на каталог.

    Исходные файлы

    Универсальный make-файл использует расширения файлов для определения типа компиляция для использования:

    .c
    C источник
    .cpp
    Исходный код C ++
    .rc
    определения ресурсов (только для Windows)

    Любой файл с этими расширениями в каталоге проекта будет скомпилирован с использованием компилятор, соответствующий его типу.

    Итак, если вы используете расширения .c для файлов C ++, у вас будут проблемы. Полагаю это хорошая практика - четко указать, является ли файл C или C ++, поэтому это особенность, а не ошибка.

    Еще рекомендую:

    .h
    a C заголовок
    .hpp
    заголовок C ++
    .tpp
    реализация шаблона C ++

    Рабочий каталог

    Когда вы компилируете файлы с помощью универсального make-файла, он сохраняет все скомпилированные объектный код в подкаталоге исходного каталога. Вы можете убрать просто удалив этот рабочий каталог.

    Имя рабочего каталога является производным от операционной системы и процессора. тип компьютера, на котором вы работаете.Универсальный make-файл содержит набор правил выработки этого названия.

    Причина этого в том, что некоторые люди работают над одним и тем же исходный код с разными компиляторами и, возможно, даже с дисками, которые доступны по сети с одного из множества различных компьютеры. Система makefile хранит компиляции для разных платформы раздельные. Это означает, что вы можете, например, скомпилировать свой код на одной платформе, а затем снова скомпилировать для другой платформы. В Система makefile хранит рабочие каталоги отдельно.

    Имя каталога состоит из трех частей. Например:

    GNULINUX-альфа-отладка

    Первая часть имени - это общее имя операционной системы, в данном случае Gnu / Linux. В Операционная система написана в верхнем регистре, чтобы подчеркнуть, что макрос компилятора GNULINUX будет определен как директива компилятора (в данном случае -DGNULINUX) и может использоваться в исходном коде в "#ifdef GNULINUX" директивы препроцессора.

    Вторая часть имени - это тип ЦП - в данном случае «альфа», то есть 64-битный DEC Alpha. ПРОЦЕССОР.

    Третья часть имени - вариант сборки - в данном случае «отладка». Этот означает, что это отладочная сборка программного обеспечения - см. следующий раздел для подробнее о вариантах сборки и способах их создания.

    Строительные варианты

    Система make может строить различные варианты проекта в зависимости от аргумент после make.

    марка
    построить отладочную версию (код отладки, без оптимизаций)
    сделать RELEASE = на
    построить релизную версию (без кода отладки, сильно оптимизированный)
    сделать GPROF = на
    построить версию профилирования (те же параметры, что и у версии выпуска)

    Обратите внимание, что эти варианты создаются путем указания значений используемых переменных. для управления типом сборки.См. Определения переменных RELEASE выше, GPROF, UNICODE, STATIC и VERBOSE. Фактически любая переменная, управляющая сборкой, может быть установленным либо в Makefile, то есть всегда использовать одно и то же значение, либо из командная строка, и в этом случае это разовая сборка с этой опцией.

    Идея в том, что вы разрабатываете с использованием отладочной версии. Это все символическое информация, необходимая для запуска отладчика, такого как gdb от Gnu, а также всех макросы, управляемые директивой препроцессора NDEBUG. Директива NDEBUG это стандартный метод ANSI C для включения / исключения кода отладки в вашем источник - используется макросом C assert (expr), а также расширенным макросы отладки, предоставляемые отладкой заголовка библиотеки STLplus.hpp.

    Вы можете в любой момент создать вариант профилирования для использования с gprof Gnu. Этот позволяет выявлять любые проблемы с производительностью и выборочно оптимизировать код на основе профилей производительности. Обычно 95% или более программы НЕ производительность критична, поэтому оптимизация всего - огромная трата усилий. Профилирование позволяет сосредоточить внимание на тех 5% программы, которые действительно могут выгода от оптимизации.

    Обратите внимание, что версия профилирования построена с тем же уровнем оптимизации, что и версия выпуска, чтобы дать точное представление о том, как версия выпуска будет выполнять.

    Как только вы убедитесь, что ваша программа полностью свободна от ошибок (хорошо, только шутя) и ослепительно быстро (да, хорошо, еще раз пошутил), вы можете построить вариант выпуска для отгрузки заказчику. Это будет не только около четырех раз быстрее, чем вариант отладки, но также будет намного меньше. Это также больше трудно реконструировать, потому что он не содержит отладки Информация.

    Поддиректории

    Существует отдельный набор правил make для обработки структур подкаталогов.Например, чтобы создать проект с двумя подпроектами, у вас может быть следующая структура:

    • make-файла
      • gcc.mak
      • subdirectories.mak
    • проект
      • Makefile
      • подсистема1
        • Makefile
        • xyz123.cpp
        • efg789.cpp
        • ...
      • подсистема2
        • Makefile
        • fgh546.cpp
        • kld495.cpp
        • ...

    Итак, я хочу написать Makefile (выделенный жирным шрифтом выше) для «проекта» верхнего уровня. каталог, в котором создается основной проект путем создания двух подпроектов.

    Создайте проект / Makefile, содержащий одну строку:

    включить ../makefiles/subdirectories.mak
     

    Это правило ищет набор подкаталогов, которые сами содержат Makefile и делает их.

    Использование wxWidgets

    Существует дополнительный универсальный make-файл для использования при сборке программы wxWidgets.Этот make-файл должен быть включен после gcc.mak файл.

    Например:

    ИЗОБРАЖЕНИЕ: = демонстрация
    БИБЛИОТЕКИ: = ../stlplus3
    включить ../makefiles/gcc.mak
    включить ../makefiles/wx.mak
     

    Файл wx.mak добавляет дополнительные параметры компилятора и компоновщика для wxWidgets. Оно использует сценарий wx-config, поставляемый с wxWidgets, для адаптации к вашей установке - другими словами, вам нужно запустить "configure", "make" и "make install" при сборке wxWidgets. так что программа wx-config находится на пути.

    Зависимости общей библиотеки

    По умолчанию приложение wxWidgets создается с использованием общего доступа. библиотеки для библиотек wxWidgets. Это соответствует конфигурация wxWidgets по умолчанию. Однако вы можете построить приложение без каких-либо зависимостей разделяемой библиотеки wxWidgets - как статическая сборка - указав параметр WXSTATIC в Makefile:

    ИЗОБРАЖЕНИЕ: = демонстрация
    БИБЛИОТЕКИ: = ../stlplus3
    WXSTATIC: = вкл.
    включить ../makefiles/gcc.mak
    включить ../makefiles/wx.мак
     

    Для правильной сборки у вас должна быть не-общая версия wxWidgets установлен, что достигается путем сборки wxWidgets с Параметр "--disable-shared" для сценария настройки.

    Примечание: это отличается от параметра СТАТИЧЕСКИЙ, который управляет зависит ли приложение от разделяемых библиотек C ++. В Параметр WXSTATIC определяет, зависит ли приложение от Общие библиотеки wxWidgets.

    Выбор версии wxWidgets

    Возможно установить несколько разных версий wxWidgets на в то же время и выбрать, какой из них использовать во время сборки.Этот выбор поддерживается с помощью параметра WXVERSION в Makefile:

    ИЗОБРАЖЕНИЕ: = демонстрация
    БИБЛИОТЕКИ: = ../stlplus3
    WXSTATIC: = вкл.
    WXVERSION: = 3.0
    включить ../makefiles/gcc.mak
    включить ../makefiles/wx.mak
     

    Будет выбрана версия 3.0 wxWidgets и построено приложение с ее помощью.

    Примеры

    Вот типичный Makefile для создания библиотеки статических объектов:

    БИБЛИОТЕКИ: = ../stlplus3/portability
    включить ../makefiles/gcc.mak
     

    Вот типичный Makefile для создания исполняемого образа:

    ИЗОБРАЖЕНИЕ: =../bin/ccolour
    БИБЛИОТЕКИ: = ../stlplus3/portability
    включить ../makefiles/gcc.mak
     

    Чтобы показать всю картинку, вот мультибиблиотечный проект для "ccolour" программа со структурой каталогов и make-файлами, использованными для ее создания. В Проект ccolour использует проект stlplus3. Чтобы построить весь проект, вы необходимо зайти в исходный каталог каталога ccolour и запустить сделать:

    • разработка
      • бин
      • make-файла
      • stlplus3
      • cцвет

    Универсальный Makefile под Linux - Программист искал

    Universal Makefile под Linux

    Мы разрабатываем программы в среде Linux, Незаменим для написания собственного Makefile , One Немного более крупные проекты будут содержать много файлов.c / .cpp исходные файлы. Если мы будем использовать gcc / g ++ для компиляции каждого исходного файла один за другим, эффективность будет намного ниже, но если мы сможем написать Makefile, тогда нужно будет только выполнить make, что значительно повысит эффективность разработки.

    , но Для Makefile существует множество грамматических правил, и из-за отсутствия справочных материалов новичкам все еще трудно писать, что часто пугает многих. Ниже мы представляем несколько более общих и кратких Makefile, если вы Слегка модифицировали Его можно использовать в ваших собственных проектах.

    Как я только что сказал, в Makefile есть много правил, может показаться, что вы находитесь в процессе копирования, потому что Различные системы будут вызывать ненужные ошибки во время процесса копирования , Makefile непригоден для использования, рекомендуется перейти на github, чтобы клон

    адрес github

    Я загрузил в общей сложности 4 общих make-файла, я использую MyMakefile, у каждого есть свои преимущества и недостатки, пожалуйста, не стесняйтесь !!!

    Разместите MyMakefile здесь:

      ########################################################################### ################
    ################## Поддержка гибридной компиляции C / C ++ ###################### #####
    ########################################################################## ##############
    PRJ_ROOT = # Изменять не нужно, здесь вы можете добавить путь к заголовочному файлу и т. Д., за которым следует LDFLAGS для удобства использования
    CC = g ++ # Компилятор, скомпилировать файл C, выбрать gcc, скомпилировать файл C ++, выбрать g ++ (лучше всего выбрать g ++, потому что он совместим с C)
    CFLAGS = # Если вам нужно отлаживать, добавьте сюда опцию -g, вы также можете добавить команды опций ответа в соответствии с вашими потребностями
    CPPFLAGS = # То же, что и выше
    LDFLAGS = -std = c ++ 11 -lpthread
    
    ########################################################################## ##############
     ########### Исходный файл, для каждой дополнительной цели добавьте следующий абзац #############
    ########################################################################## ##############
    
     # Исходный файл list1
    я: = 1
    SOURCES_CPP _ $ (i): = test.cpp # Добавьте и измените исходный код CPP, необходимый для текущей цели, здесь
    SOURCES_C _ $ (i): = # Добавить и изменить здесь исходный код C, необходимый для текущей цели
    TARGET _ $ (i): = testcpp # имя цели
    OBJS_CPP _ $ (i): = $ (patsubst% .cpp,%. O, $ (SOURCES_CPP _ $ (i)))
    OBJS_C _ $ (i): = $ (patsubst% .c,%. O, $ (SOURCES_C _ $ (i)))
    OBJS _ $ (i): = $ (OBJS_CPP _ $ (i)) $ (OBJS_C _ $ (i))
    
    
     # Исходный файл list2
    я: = 2
    ИСТОЧНИК_CPP _ $ (i): =
    ИСТОЧНИК_C _ $ (i): = main.c
    ЦЕЛЬ _ $ (i): = mainc
    OBJS_CPP _ $ (i): = $ (patsubst% .cpp,%. O, $ (SOURCES_CPP _ $ (i)))
    OBJS_C _ $ (i): = $ (patsubst%.c,%. o, $ (ИСТОЧНИКИ_C _ $ (i)))
    OBJS _ $ (i): = $ (OBJS_CPP _ $ (i)) $ (OBJS_C _ $ (i))
    
    
    ########################################################################## ##############
     ####### Цели и зазоры Для каждой дополнительной цели соответственно добавляется цель ############
    ########################################################################## ##############
    
    все: $ (TARGET_1) $ (TARGET_2)
    @echo "выходной файл: $ (TARGET_1) $ (TARGET_2)"
    чистый:
    rm -f * .o * .d $ (ЦЕЛЬ_1) $ (ЦЕЛЬ_2)
    
    
    ########################################################################## ##############
     ########## Цели, каждый раз, когда вы добавляете цель, добавляйте следующий абзац ###############
    ########################################################################## ##############
    
     # цели1
    $ (TARGET_1): $ (OBJS_1)
    $ (CC) $ (OBJS_1) $ (LDFLAGS) -o $ (TARGET_1)
    
     # цели2
    $ (TARGET_2): $ (OBJS_2)
    $ (CC) $ (OBJS_2) $ (LDFLAGS) -o $ (TARGET_2)
    
    
    ########################################################################## ##############
     ########### Включить для каждой дополнительной цели добавьте следующую строку ################
    ########################################################################## ##############
    
    sinclude $ (OBJS_1 :.
            
    

    Добавить комментарий

    Ваш адрес email не будет опубликован. Обязательные поля помечены *