Просто о make

Table of Contents

Как запустить Make на Windows

Make – это широко используемая для автоматизации сборки проектов утилита, которую бывает проблематично установить и запустить на windows. Сегодня я поделюсь самым простым способом, который позволит вам это сделать. Использовать мы будем Chocolatey.

Chocolatey (choco) – это менеджер пакетов для Windows, который позволяет устанавливать и управлять программным обеспечением из командной строки. Вот как установить утилиту make на Windows с помощью Chocolatey:

  • Откройте PowerShell от имени администратора.

  • Вставьте следующую команду и нажмите Enter:

Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1'))

Эта команда скачает и запустит скрипт установки Chocolatey. Подтвердите выполнение команды, если будет запрошено разрешение.

  1. Установка make с помощью Chocolatey:

После установки Chocolatey выполните следующую команду в PowerShell:

choco install make

Chocolatey автоматически загрузит и установит утилиту make и все необходимые зависимости.

make --version

Если установка прошла успешно, вы увидите вывод с информацией о версии make.

Далее, для использования make можно использовать обычный CMD, но, всегда в режиме администратора.

Если было полезно – подписывайтесь и ставьте лайки. Благодарю за внимание.

Есть папка. В ней лежит makefile и еще пару папок с файлами:

директория

Допустим, что в папке third_pracice есть файл a.txt, который мне надо удалить через makefile:

введите сюда описание изображения

В makefile я написал такую цель:

clean: rm ./third_pracice/a.txt

Когда я запускаю make, то он выдает ошибку:

PS C:\Users\cashr\Desktop\pracice\cmp> make clean
rm ./third_pracice/a.txt
process_begin: CreateProcess(NULL, rm ./third_pracice/a.txt, ...) failed.
make (e=2): Ia oaaaony iaeoe oeacaiiue oaee.
makefile:2: recipe for target 'clean' failed
make: *** [clean] Error 2

В чем проблема? Если закинуть эту команду напрямую в терминал, то проблем нет. Пробовал использовать указание “sudo”, не помогло. Как работает удаление из других папок через make?

ОС – Windows

MarianD's user avatar

4 золотых знака21 серебряный знак32 бронзовых знака

задан 9 сент. 2023 в 17:56

Fidel Castro's user avatar

Я бы del не использовал. Нужно стремиться к кросс-платформенным мейкфайлам, а del – чисто виндовая вещь.

Если у вас не работает rm, значит вы либо скачали не тот Make, либо как-то странно его запускаете.

Снесите ваш Make. Поставьте MSYS2. Там запустите pacman -S mingw-w64-ucrt-x86_64-make, чтобы установить их версию Make, и потом запускайте mingw32-make из консоли MSYS2 (ярлык MSYS2 UCRT64 в меню пуск). (Есть еще просто make, который ставится из pacman -S make – он тормозит сильнее, и для вашего случая никакой другой разницы нет.)

ответ дан 10 сент. 2023 в 4:18

HolyBlackCat's user avatar

3 золотых знака28 серебряных знаков40 бронзовых знаков

clean: rm ./third_pracice/a.txt
clean: del .\third_pracice\a.txt

потому что команде rm (POSIX) соответствует команда del (Windows) и слеши (/) обратная слеш (\).

ответ дан 9 сент. 2023 в 22:12

MarianD's user avatar

4 золотых знака21 серебряный знак32 бронзовых знака

В терминале у вас запущен интерпретатор команд powershell. В нем rm является алиасом для командлета Remove-Item. Утилита make не является интерпретатором команд и не использует powershell, для выполнения каждой директивы она тупо запускает дочерний процесс. Соответственно чтобы rm работало в make необходимо, чтобы в PATH присутствовал исполняемый файл rm (rm.exe), который обычно распространяется как часть posix coreutils или их суррогатов. Так что если собираетесь использовать rm, то make необходимо запускать из окружения, где они уже установлены, например из ранееупомянутого msys или из git bash.

ответ дан 10 сент. 2023 в 6:25

user7860670's user avatar

3 золотых знака17 серебряных знаков36 бронзовых знаков

Время на прочтение

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

Мое упорное игнорирование make в течении долгого времени, было обусловлено удобством используемых IDE, и нежеланием разбираться в этом ‘пережитке прошлого’ (по сути — ленью). Однако, все эти надоедливые кнопочки, менюшки ит.п. атрибуты всевозможных студий, заставили меня искать альтернативу тому методу работы, который я практиковал до сих пор. Нет, я не стал гуру make, но полученных мною знаний вполне достаточно для моих небольших проектов. Данная статья предназначена для тех, кто так же как и я еще совсем недавно, желают вырваться из уютного оконного рабства в аскетичный, но свободный мир шелла.

Make- основные сведения

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

1) целями (то, что данное правило делает);
2) реквизитами (то, что необходимо для выполнения правила и получения целей);
3) командами (выполняющими данные преобразования).

В общем виде синтаксис makefile можно представить так:

# Индентация осуществляется исключительно при помощи символов табуляции,
# каждой команде должен предшествовать отступ
<цели>: <реквизиты>	<команда #1>	...	<команда #n>

То есть, правило make это ответы на три вопроса:

{Из чего делаем? (реквизиты)} ---> [Как делаем? (команды)] ---> {Что делаем? (цели)}

Несложно заметить что процессы трансляции и компиляции очень красиво ложатся на эту схему:

{исходные файлы} ---> [трансляция] ---> {объектные файлы}

{объектные файлы} ---> [линковка] ---> {исполнимые файлы}

Простейший Makefile

Предположим, у нас имеется программа, состоящая всего из одного файла:

/* * main.c */
#include <stdio.h>
int main()
{	printf("Hello World!\n");	return 0;
}

Для его компиляции достаточно очень простого мэйкфайла:

hello: main.c	gcc -o hello main.c

Данный Makefile состоит из одного правила, которое в свою очередь состоит из цели — «hello», реквизита — «main.c», и команды — «gcc -o hello main.c». Теперь, для компиляции достаточно дать команду make в рабочем каталоге. По умолчанию make станет выполнять самое первое правило, если цель выполнения не была явно указана при вызове:

$ make <цель>

Компиляция из множества исходников

Предположим, что у нас имеется программа, состоящая из 2 файлов:
main.c

/* * main.c */
int main()
{	hello();	return 0;
}
/* * hello.c */
#include <stdio.h>
void hello()
{	printf("Hello World!\n");
}

Makefile, выполняющий компиляцию этой программы может выглядеть так:

hello: main.c hello.c gcc -o hello main.c hello.c

Он вполне работоспособен, однако имеет один значительный недостаток: какой — раскроем далее.

Инкрементная компиляция

Представим, что наша программа состоит из десятка- другого исходных файлов. Мы вносим изменения в один из них, и хотим ее пересобрать. Использование подхода описанного в предыдущем примере приведет к тому, что все без исключения исходные файлы будут снова скомпилированы, что негативно скажется на времени перекомпиляции. Решение — разделить компиляцию на два этапа: этап трансляции и этап линковки.

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

main.o: main.c gcc -c -o main.o main.c
hello.o: hello.c gcc -c -o hello.o hello.c
hello: main.o hello.o gcc -o hello main.o hello.o

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

:/>  Как настроить микрофон на компьютере Windows 10 |

После запуска make попытается сразу получить цель hello, но для ее создания необходимы файлы main.o и hello.o, которых пока еще нет. Поэтому выполнение правила будет отложено и make станет искать правила, описывающие получение недостающих реквизитов. Как только все реквизиты будут получены, make вернется к выполнению отложенной цели. Отсюда следует, что make выполняет правила рекурсивно.

Фиктивные цели

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

$ make	$ make install

Командой make производят компиляцию программы, командой make install — установку. Такой подход весьма удобен, поскольку все необходимое для сборки и развертывания приложения в целевой системе включено в один файл (забудем на время о скрипте configure). Обратите внимание на то, что в первом случае мы не указываем цель, а во втором целью является вовсе не создание файла install, а процесс установки приложения в систему. Проделывать такие фокусы нам позволяют так называемые фиктивные (phony) цели. Вот краткий список стандартных целей:

  • all — является стандартной целью по умолчанию. При вызове make ее можно явно не указывать.
  • clean — очистить каталог от всех файлов полученных в результате компиляции.
  • install — произвести инсталляцию
  • uninstall — и деинсталляцию соответственно.

Для того чтобы make не искал файлы с такими именами, их следует определить в Makefile, при помощи директивы .PHONY. Далее показан пример Makefile с целями all, clean, install и uninstall:

.PHONY: all clean install uninstall
all: hello
clean:	rm -rf hello *.o
main.o: main.c	gcc -c -o main.o main.c
hello.o: hello.c	gcc -c -o hello.o hello.c
hello: main.o hello.o	gcc -o hello main.o hello.o
install:	install ./hello /usr/local/bin
uninstall:	rm -rf /usr/local/bin/hello

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

Обратите внимание на то, что в цели all не указаны команды; все что ей нужно — получить реквизит hello. Зная о рекурсивной природе make, не сложно предположить как будет работать этот скрипт. Так же следует обратить особое внимание на то, что если файл hello уже имеется (остался после предыдущей компиляции) и его реквизиты не были изменены, то команда make ничего не станет пересобирать. Это классические грабли make. Так например, изменив заголовочный файл, случайно не включенный в список реквизитов, можно получить долгие часы головной боли. Поэтому, чтобы гарантированно полностью пересобрать проект, нужно предварительно очистить рабочий каталог:

$ make clean	$ make

Для выполнения целей install/uninstall вам потребуются использовать sudo.

Переменные

Все те, кто знакомы с правилом DRY (Don’t repeat yourself), наверняка уже заметили неладное, а именно — наш Makefile содержит большое число повторяющихся фрагментов, что может привести к путанице при последующих попытках его расширить или изменить. В императивных языках для этих целей у нас имеются переменные и константы; make тоже располагает подобными средствами. Переменные в make представляют собой именованные строки и определяются очень просто:

<VAR_NAME> = <value string>

Существует негласное правило, согласно которому следует именовать переменные в верхнем регистре, например:

SRC = main.c hello.c

Так мы определили список исходных файлов. Для использования значения переменной ее следует разименовать при помощи конструкции $(<VAR_NAME>); например так:

gcc -o hello $(SRC)

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

TARGET = hello
PREFIX = /usr/local/bin
.PHONY: all clean install uninstall
all: $(TARGET)
clean:	rm -rf $(TARGET) *.o
main.o: main.c	gcc -c -o main.o main.c
hello.o: hello.c	gcc -c -o hello.o hello.c
$(TARGET): main.o hello.o	gcc -o $(TARGET) main.o hello.o
install:	install $(TARGET) $(PREFIX)
uninstall:	rm -rf $(PREFIX)/$(TARGET)

Это уже посимпатичней. Думаю, теперь вышеприведенный пример для вас в особых комментариях не нуждается.

Автоматические переменные

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

  • $@ Имя цели обрабатываемого правила
  • $< Имя первой зависимости обрабатываемого правила
  • $^ Список всех зависимостей обрабатываемого правила

Если кто либо хочет произвести полную обфускацию своих скриптов — черпать вдохновение можете здесь:
Автоматические переменные

Заключение

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


Все примеры на GitHub

Установка и использование MSYS2 и Mingw-w64 под Windows

Текст был первоначально написан для сайта “Железные призраки прошлого”, но затем невозбранно перенесен на эту Wiki и теперь живет тут.

Введение

В целях ретрокомпьютинга иногда возникает необходимость “что-нибудь скомпилировать под 32-битный Windows” с минимальными затратами. Ага, ага. Вот и у меня однажды возникала
такая же ситуация. Когда-то давно я пользовался компилятором Mingw и оболочкой MSYS, так что я попробовал снова их найти и установить. Оказалось, что там “всё не так, как раньше”. Вобщем, сейчас ситуация такая: “простой” MinGW и “классический” MSYS обновляться перестали и они зачахли где-то в районе 2015 года.

Дак вот, оказывается, нынче текущая версия MSYS – это MSYS2. Это такой странный гибрид
из Cygwin и старого MSYS. Что там нового ? Подобно Cygwin он делает замену путей в стиле UNIX, маскирует расширение *.exe,
поддерживает псевдотерминалы, UNIX-сигналы и еще много чего. Это, с одной стороны, облегчает процесс сборки
всяких нетривиальных UNIX программ, а с другой стороны, теперь все MSYS2-программы, в отличии от старого MSYS тянут за собой
DLL-ку: msys-2.0.dll .

А компилятор с тулзами нынче называется Mingw-w64. Это вовсе не значит, что он 64-битный,
это просто такое имя. Он существует во всех возможных комбинациях: 32-битный (т.е. работающий на 32-бит Windows)
для билда 32-битных программ, 64-битный для билдинга 64-битных программ, и все промежуточные
варианты, то есть 32-битный для построения 64-битных и наоборот, 64-битный для 32-бит.

ПРИМЕЧАНИЕ: Надеюсь, читатели понимают разницу между Cygwin и Mingw. Кратко: Cygwin пытается воссоздать наиболее полную “среду” UNIX/POSIX на Windows, со всеми её фишками, типа fork(), особенностями файловой системы, сигналами, псевдотерминалами и т.д. в то время как Mingw – это (изначально) просто перенос компилятора GCC на Windows, без вот этого всего. А MSYS – это “оболочка”, то есть набор утилит для сборки, главные из которых – пожалуй make и bash. Подробности. Надо добавить, что вся эта магия имеется только в среде MSYS2, а полученная программа билдится с MSVCRT.DLL и большинство этих фишек не поддерживает.

ПРИМЕЧАНИЕ: С мая 2020 32-битную MSYS2 стали потихоньку сворачивать. Она еще поддерживается, но пакеты для нее выходят крайне редко, а начальный инсталлятор для 32-битной версии MSYS2 убрали с главной стравницы сюда. Следите за новостями. Таким образом, даже для сборки 32-битных приложений нужно использовать 64-битную ОС и 64-битную MSYS2. Этакая кросс-система. Увы.

2022-04-06 – Windows 7 / 8 support will be dropped late 2022 or early 2023

2023-01-15 – Dropping support for Windows 7 and 8.0

Компилятор и его запуск

Итак, давайте сначала установим тулзы для сборки: pacman --needed -S base-devel
Это установит всякие полезные для сборки утилиты в нашу MSYS2 (любой битности).
Установщик pacman знает про себя, 32-бита он или 64, берёт правильные пакеты и ставит
в правильный каталог. Репозиторий для любой MSYS2 всегда называется просто msys2 🙂
Компилятор для самой MSYS2 в base-devel не входит, да он нам и не нужен.

MSYS2 Menu
А вот теперь аккуратнее! Чтобы установить build-систему, которая делает 32-бит программы
надо установить группу mingw-w64-i686-toolchain. То есть компилятор называется Mingw-w64
(помним, что это просто название такое). Ставится он на нашу текущую MSYS2 и будет
генерить 32-битные программы (хвостик -i686). Делаем pacman -S --needed mingw-w64-i686-toolchain.
Пакеты компилятора скачаются (из репозитория mingw32) и будут установлены.
Вот только никакого компилятора не появится!

:/>  В России окно активации хр не работает по телефону ) в разделе "Окно"

Табличка с “окружениями”

Давайте запустим “MSYS2 MinGW 32bit”. Вот теперь у нас доступен нужный компилятор gcc, причем он полностью
настроен, с указанием папки include, линкера и пути к библиотекам. Ура!

Пишем программы

Создадим Си-шный файлик hello.c со стандартным библиотечным вызовом printf():

#include <stdio.h>
int main () { printf("Hello, World!\n"); return 0;
}
$ gcc -o hello hello.c

и запустим ИЗ CMD.EXE (то есть из другого окна CMD, а не из оболочки MSYS2) или из PowerShell:

C:\TEMP\TEST>hello
Hello, World!

Всё работает.
Есть и недостатки – полученный EXE-шник имеет размер почти 300Kb! Воспользуемся командой
strip и удалим из него все лишнее. Получится EXE-шник около 16К, что гораздо лучше.
Посмотрим, что у нас получилось (пример из Windows 7):

$ file hello.exe
hello.exe: PE32 executable (console) Intel 80386, for MS Windows
$ ldd hello.exe ntdll.dll => /c/Windows/SYSTEM32/ntdll.dll (0x772c0000) kernel32.dll => /c/Windows/system32/kernel32.dll (0x768e0000) KERNELBASE.dll => /c/Windows/system32/KERNELBASE.dll (0x75130000) msvcrt.dll => /c/Windows/system32/msvcrt.dll (0x76ae0000)

Как и требовалось, это “чистое” автономное 32-битное консольное Win32 приложение, которое использует только
стандартные библиотеки 32-битного Windows (можно поспорить насчет стандартности
MSVCRT.DLL, в котором и располагается printf()), но по факту, она есть практически везде. На 64-битном Windows вывод ldd будет несколько другой, поскольку там другая архитектура запуска 32-битных программ WindowsOnWindows (WoW). Скорее всего, эта программа будет
работать даже в Windows 95, поскольку формат PE-файла не изменился (не пробовал).

Давайте теперь напишем оконную программку под Windows (точнее под Win32). Вытащим
пример из старика Петзольда (Charles Petzold. Programming Windows):

#include <windows.h>
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
{ MessageBox (NULL, TEXT ("Hello, Windows!"), TEXT ("HelloMsg"), 0) ; return 0 ;
}
$ gcc -mwindows -o hellowin hellowin.c

Все работает. Чтобы указать, что у нас оконная, а не консольная программа,
используется ключ -mwindows . Первоначально ключ -m служил для указания модели машины или
процессора, а тут его приспособили для указания “подсистемы”. Если в программе есть WinMain(),
то даже -mwindows указывать не обязательно, компилятор сам понимает, но на всякий случай укажем.

ПРИМЕЧАНИЕ1: Обратите внимание, что наша программа собирается со штатной библиотекой MSVCRT.DLL. Не стоит ждать многого от нее. В MSVCRT.DLL реализован далеко не полный набор функций libc.
Кроме явного отсутствия “юниксовых” возможностей типа fork(), вида слешей в путях к файлам и т.д. расхождения могут быть совсем неожиданные (и в разных версиях Windows – разные).
Например, printf() не поддерживает формат %zd для типа size_t, поддержка UTF-8 довольно странная и другие неожиданные вещи. Компилятор
Mingw-w64 знает много таких особенностей и по мере возможностей дает предупреждения. Подробности. Несколько больше возможностей предоставляет новомодная UCRT. Если же нужны юниксовые фишки и не пугает зависимости от DLL – всегда можно собрать программу под оболочку MSYS2.

ПРИМЕЧАНИЕ2: Компиляторы gcc, а особенно g++ иногда создают код, который зависит от библиотек
libgcc и libstdc++. Чтобы полученное приложение не требовало соответствующих DLL, в командную
строку можно добавить опции -static-libgcc и -static-libstdc++. Библиотеки же, которые являются “стандартными”
для Windows, умный Mingw-w64 всегда делает динамическими, даже несмотря на присутствие опции -static
в опциях компилятора.

Консольные полноэкранные/CLI приложения

TODO. Параграф не дописан! Имеются проблемы со сборкой pdcurses!

Тут надо сначала немного углубиться в теорию. С точки зрения UNIX мы имеем две сложно-взаимодействующих абстракции:

На Windows – совершенно другая идеология управления консолью (WinAPI Console). (Пример: Пишем простое консольное приложение на чистом WinAPI). Поэтому и возникает необходимость в эмуляторе терминала и промежуточной библиотеке типа msys-2.0.dll или cygwin.dll, которая имитирует (с разной степенью успешности) поведение UNIX системы. Тем или иным способом в оболочках типа MSYS2 и CYGWIN эта проблема решена. Но с “автономными” консольными Windows приложениями неколько сложнее. На это накладывается взаимодействие программы через MSVCRT.DLL, которая имитирует файловые потоки STDIN/STDOUT/STDERR и также вносит свои особенности.

Как же быть ?

Можно предложить такое неожиданное решение. Как правило, управление терминалом в UNIX-программах редко делают напрямую, на нижнем уровне через API терминала termio/termios. Как правило, используют готовые библиотеки, например Termcap или Curses. Такую UNIX программу намного легче портировать в консольное “автономное” Windows приложение. Доверимся авторам MSYS2 в надежде, что они корректно перенесли на Windows и отладили эти библиотеки.

Надо сказать, что современных реализаций библиотеки curses есть целых две. Одна из них – ncurses, другая – pdcurses. Давайте установим ncurses для нашей 32-битной сборки: pacman -S mingw-w64-i686-ncurses.

ПРИМЕЧАНИЕ: Как было написано выше, управление консолью в Windows устроено совсем иначе, чем в UNIX, так что не обходится без глюков. Библиотека ncurses реализована таким образом, что она “чувствует”, под какой консолью она работает и включает “магию”. Если мы запускаем одну и ту же прогрмамму под UNIX-подобной консолью, она начинает управлять экраном через ESC-последовательности, а если из CMD или PowerShell – через API Windows Console. Но фокус в том, что для ESC-последовательностей нужна база терминалов, а она не всегда доступна. Это дает неприятный результат: программа на ncurses неадекватно работает из “оболочки” MSYS2 Mingw64 в которой мы ее только что откомпилировали. А из обычных CMD или PowerShell работает нормально. Как это обойти ? Если нужна программа для среды MSYS2 – скомпилируйте ее в оболочке MSYS2 MSYS с ее библиотеками, а если “чистая виндовая” – в среде MSYS2 Mingw64.

Давайте создадим простую ncurses программу для Windows (конечно же статическую):

#include <curses.h>
int main()
{	initscr();	/* Start curses mode */	printw("Hello World !!!");	/* Print Hello World */	refresh();	/* Print it on to the real screen */	getch();	/* Wait for user input */	endwin();	/* End curses mode */	return 0;
}
$ gcc -static -o curses.exe curses.c -I /usr/include/ncurses -l ncurses -DNCURSES_STATIC

Обратите внимание, на #include – тут подключается заголовочный файл curses.h, в стиле UNIX SYSVr4. Там компилятор обычно ищет curses.h прямо в /usr/include . В нашем случае это не работает, потому что у наc ДВЕ реализации curses и их заголовочные файлы лежат в разных каталогах. Так что нужно добавить ключ -I компилятору для поиска.

При запуске этой программы в CMD или PowerShell (или кликом из Проводника) все работает как надо – пишет “Hello World !!!” в верхнем левом углу, а при запуске из MSYS2 Mingw64 X86 программа вылетает (как и предупреждали):

[USER@HOST test]$ uname
MINGW32_NT-10.0-17134
[USER@HOST test]$ ./curses.exe
Error opening terminal: xterm.

Для дальнейшего погружения в ncurses есть неплохое NCURSES Programming HOWTO. Также большое количество документации и примеров содержится в исходниках ncurses.

Другая реализация curses – pdcurses сделана еще более интересно. Она может выводить текстовый TUI интерфейс с поддержкой текстовых окошек не только в текстовую консоль, но даже в окно X11, в окно SDL, использовать графику и т.д. Имеется даже форк pdcurses, который может отрисовывать текст с управлением терминалом в других оконных системах, от Windows GDI до OpenGL.

/не дописано т.к. имеются проблемы с pdcurses/

Еще одна популярная библиотека, требующих прямого управления консолью – readline. Библиотека GNU Readline очень удобна для ввода и используеся довольно широко. Самый известные примеры – bash и gdb. Давайте установим: pacman -S mingw-w64-i686-readline.

$ cp /mingw32/share/readline/rlbasic.c .
$ gcc -static -o rlbasic.exe rlbasic.c -l readline -l ncurses -DNCURSES_STATIC

Опять же, при запуске полученной программы из среды MSYS2 программа работает странно (трижды выводит всю строку и редактирование не работает), а при запуске из CMD или PowerShell – нормально (позволяет редактировать строку и ловит команду “exit”). Библиотека GNU readline подерживает множество функций, например историю ввода, которая в этом примере не обрабатывается.

:/>  Папки Local, LocalLow и Roaming в Windows 10

Полная документация по GNU Readline Library.

Библиотекой readline можно управлять с помощью файла.inputrc (под Windows ??)

Конец недописанного параграфа.

SDL

Если вы хотите быть современным молодым динамичным программистом, то лучше писать на каком-нибудь
Framework 🙂 (как етто по рюсcки? Каркас?). Например, очень большое число разных эмуляторов
винтажных систем написано на SDL.

Напишем небольшую программу под SDL:

#include <SDL2/SDL.h>
int main(int argc, char *argv[])
{ SDL_Event e; int quit = 0; SDL_Window* win; SDL_Surface* surf; SDL_Init(SDL_INIT_EVERYTHING); win = SDL_CreateWindow("Hello SDL!", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 320, 240, 0); surf = SDL_GetWindowSurface( win ); SDL_FillRect( surf, NULL, SDL_MapRGB( surf->format, 0, 0, 0xFF ) ); SDL_UpdateWindowSurface( win ); while( !quit ) { while( SDL_PollEvent( &e ) != 0 ) { if( e.type == SDL_QUIT ) { quit = 1; } } } SDL_DestroyWindow(win); SDL_Quit(); return 0;
}

Программа просто создает маленькое (320×240) окошко с заголовком, закрашивает его синим
(R=0, G=0, B=0xFF) и ждет, когда из нее выйдут.

Компиляция чуть сложнее.

  • Первое: SDL ПЕРЕОПРЕДЕЛЯЕТ функцию main(). То есть наша main() вызывается ИЗ системы SDL, а стартует программа начиная с функции SDL_main(). Это надо явно описать.

  • Второе: Поскольку мы не используем WinMain() напрямую, но хотим программу под Windows и не хотим,чтобы болталось черное окно консоли, то нам нужно ЯВНО добавлять ключ -mwindows .

  • Третье: При сборке нужно указать, что нам необходима библиотека SDL.

Получается целая куча ключей командной строки.

Можно выписать ключи на бумажку или создать make-файл.
Однако всё проще. Многие Frameworks имеют специальные конфиг-файлы, в которых указаны всё
необходимые настройки и нужно просто их использовать. SDL2 – не исключение.

$ sdl2-config --cflags --libs
-I/mingw32/include/SDL2 -Dmain=SDL_main
-L/mingw32/lib -lmingw32 -lSDL2main -lSDL2 -mwindows

Давайте просто подключим вывод в свою командную строку (это делается с помощью
обратного апострофа: ` . Читается как “Использовать ВЫВОД команды”. Подробности в man bash секция “Command Substitution” )

$ gcc -o hellosdl hellosdl.c `sdl2-config --cflags --libs`

Все откомпилировалось. НО! Если мы попытаемся теперь запустить эту программу из CMD.EXE (или просто кликнуть из проводника Windows), мы получим ошибку:

“Запуск программы невозможен, так как на компьютере отсутствует SDL2.dll .

Попробуйте переустановить программу.”

Это неудивительно, так как полученная программа требует для своей работы SDL2.DLL,
а она пока доступна только “изнутри” среды MSYS2. Можно пойти двумя путями: первый – установить
SDL2.DLL “в систему” (в C:\Windows\System32), второй: просто положить SDL2.DLL в каталог с программой. Тогда за
счет известной особенности Windows “use local DLL” программа при запуске найдет нужную
DLL и подключит. Скачаем нужный runtime (например 32-битный) на сайте
https://www.libsdl.org/download-2.0.php
и просто положим DLL в каталог с программой. Теперь всё запускается из под CMD.EXE и рисует синее окошко.

ПРИМЕЧАНИЕ: Обратите внимание, что MSYS2-шная SDL2.DLL тянет за собой кучу других DLL. Это происходит потому,
что авторы MSYS2 выбрали такой путь: всё что можно – выносить в отдельные библиотеки. Ну ОК. Это хорошо работает
для экономии памяти MSYS2,
но сильно мешает создавать “автономные” приложения Windows на Mingw. Используйте “официальную” SDL2.DLL

Второе. Можно даже саму SDL2.DLL вкомпилить статически. Можно. Но сейчас SDL2 употребляется настолько часто,
что автор считает допустимым использовать такие “инфраструктурные” компоненты, типа QT или той же SDL2 в форме DLL и ставить их “в систему”. Дело вкуса.

OpenGL

Другая самая известная библиотека – это 3D библиотека OpenGL.
По ней также существуют тысячи примеров и туториалов. Не вдаваясь в тонкости OpenGL и его реализации
на разных машинах и видеокартах, попробуем создать OpenGL Windows-приложение. Возьмем, например,
официальные примеры с официального сайта OpenGL.
Например, нарисуем красный кубик: cube.c.

Тут все-таки надо сказать, что OpenGL за время своего существования сменил несколько версий. Поэтому,
при чтении литературы иногда надо обращать внимание, про какую версию OpenGL идет речь.
Вдобавок OpenGL за это время оброс несколькими побочными библиотеками,
которые формально не входят в спецификацию, но все к ним настолько привыкли, что используют их не задумываясь.
Самые часто применяемые – это GLU и GLUT.
Библиотека GLUT, например, обеспечивает связку с операционной системой, в частности создает окно нужным способом:
через X Window на UNIX системах, или через GDI на Windows. Большинство примеров с сайта OpenGL написаны с применением GLU и GLUT. В Windows имеются библиотеки OPENGL32.DLL и GLU32.DLL но нету GLUT.

Как было написано выше, хотелось бы создать максимально независимое Windows-приложение и не таскать за собой libfreeglut.dll. К сожалению, в текущем MSYS2 не очень понятно (мне по крайней мере) разделены
библиотеки, например статическая libglut.a содержится в “базовом” пакете mingw-w64-i686-crt-git, а
реализация freeglut несет с собой libfreeglut_static.a (именно с таким именем, нарушая обычное наименование библиотек
Mingw-w64). В любом случае – установим пакет freeglut: pacman -S mingw-w64-i686-freeglut и откомпилируем наш кубик:

$ gcc -o cube -DFREEGLUT_STATIC cube.c -static -mwindows -lglut -lfreeglut_static -lgdi32 -lwinmm -lglu32 -lopengl32

ПРИМЕЧАНИЕ: Здесь собирается 32-битное приложение. Сборка 64-битных тут пока не рассматривается. Опция -mwindows
нужна, чтобы не создавалось черное окно консоли для Windows-приложений. Библиотека -lwinmm нужна для поддержки джойстика (!) в GLUT.
Библиотеку -lgdi32 в принципе можно не добавлять, она и так унаследуется.

Другая популярная библиотека-“связка” для управления окнами для OpenGL – это GLFW
Установим ее: pacman -S mingw-w64-i686-glfw . Текущая версия: glfw3. Это надо учитывать, когда читаете туториалы,
иногда речь идет о старых версиях без упоминания деталей.

Пример возьмем с официального сайта GLFW: Getting started. Там же есть
краткая инструкция по сборке: Building programs that use GLFW.
Особых сложностей не заметно, за исключением тонкостей сборки со статической и динамической библиотекой GLFW3.DLL.

$ gcc -o glfwtest -DGLFW_DLL glfwtest.c -mwindows -lglfw3 -lgdi32 -lopengl32
$ gcc -o glfwtest glfwtest.c -mwindows -static -lglfw3 -lgdi32 -lopengl32

Исполняемый файл увеличится примерно на 200К, за счет включения GLFW внутрь приложения, но зависимости
будут только от “стандартных” DLL имеющихся в Windows.

OpenGL через SD2L

Наконец, существует возможность – использовать уже знакомую нам SDL2 для “склейки” операционной
системы и OpenGL вместо GLU или GLUT. Это намного удобнее т.к. в SDL2 имеются функции ввода, управления
манипуляторами, звуком, сетью и т.д. и самое главное – обе библиотеки кросс-платформенные.

QT

Еще одна популярная кроссплатформанная библиотека.

Среди пакетов MSYS2 имеется IDE QtCreator

Параграф не написан.

Интеграция MSYS2 и VSCode

Для удобства программирования будет полезным использовать новомодную “оболочку” VSCode.

Для работы надо запускать VSCode из MSYS2 а точнее, прямо из командной строки той среды, под которую мы собираем (например “MSYS2 MinGW 32bit”). Это даст VSCode возможность правильно найти пути к нужным компиляторам и библиотекам.

Запускать VSCode надо прямо из папки (workspace), в которой мы ведем разработку, то есть “cd myproject ; Code . &” (обратите внимание на точку . что означает: взять текущий каталог как workspace. Знак & применяется для запуска VSCode в фоне и “отсоединения” консоли). При первом запуске будет создан подкаталог “.vscode” в котором будут лежать файлы .json конфигурации проекта.

Для среды MSYS2 MinGW 32bit и т.д. VSCode во встроенном терминале будет использоваться PowerShell в качестве оболочки вместо sh, поскольку мы собираем виндовую, независимую от MSYS2 программу. (Если мы будем собирать под среду MSYS2 MSYS то будет использоваться родной sh).

Get Started with C++ and MinGW-w64 and Visual Studio Code

Дополнительное чтение