1. Введение В
один прекрасный день, установив Linux, я решил начать изучение С/С++.
Скомпилировал первый «Helo World», «прикрутил» графическую библиотеку
SDL, возрадовался, но ненадолго: библиотека SDL является
кросс-платформенной и поэтому у меня возникло желание сделать
кросс-платформенный микро-проект для 3-х ОС: (Linux, Win32, WinCE) c
использованием библиотеки SDL. Т.к. я являюсь новичком в
программировании и в Linux, прошу не судить строго. То как я это сделал
и описал, возможно, неверно, но у моего способа есть два больших плюса:
это не сложно и это работает =). Надеюсь статья будет полезна таким же
новичкам как я.
2. Что нужно иметь ОС Linux (у меня
Kubuntu 9.04) в других дистрибутивах установка и настройка проводится
аналогично (разница лишь в командах менеджера пакетов для установки
ПО). Для написания исходного кода я использовал текстовый редактор Kate
с подсветкой С++ синтаксиса (он в составе дистрибутива), конечно можно
любой другой редактор – дело вкуса. Для компиляции и запуска полученных
исполняемых файлов – файловый менеджер GNU Midnight Commander
(устанавливается через менеджер пакетов или aptitude install mc под
рутом). И конечно-же gcc - GNU Compiler Collection http://gcc.gnu.org/, русскоязычная страничка википедии http://ru.wikipedia.org/wiki/GNU_Compiler_Collection.
Им и его портированными под другие платформы версиями мы и будем
пользоваться. Последняя по списку но не по значению для
кросс-компиляции – утилита для сборки проектов GNU Make http://www.gnu.org/software/make/manual/, страничка вики на русском http://ru.wikipedia.org/wiki/Make и хорошая статья Владимира Игнатова "Эффективное использование GNU Make". 3. Приступим. Первый проект в Linux Для начала проверим установлен ли в нашей системе компилятор g++ для чего в консоли введем g++ --version Если получаем что-то подобное : $ g++ --version c++ (Ubuntu 4.3.3-5ubuntu4) 4.3.3 и т.д. значит все нормально, g++ установлен, если что-то похожее на: $ g++ --version bash: g++: команда не найдена значит
g++ нужно установить. Из под рута вводим aptitude install g++ или же в
менеджере пакетов (у меня это Synaptic выбираем установку g++) . Теперь
g++ --version выдает нам версию нашего C++ компилятора.
Так же поступаем с утилитой Make: $ make --version GNU Make 3.81 …… Если GNU Make не установлен — ставим аналогично пакету g++ (из консоли или менеджера пакетов)
В консоли запустим Midnight Comander: $ mc если mc не запустился установим его (aptitude install mc в консоли под рутом или из Synaptic)
Попробуем наш компилятор в деле. В текстовом редакторе создадим файл main.cpp такого содержания и сохраним его в папке test:
#include <cstdio> int main() { printf("Hello Word\n"); return 0; }
перейдем
в mc в папку test, временно скроем панели commandera нажатием Ctrl-O.
Здесь в консоли запустим наш файл на компиляцию строкой: g++ -o main main.cpp здесь
–o это ключ компилятора, указывающий, что нужно создать исполняемый
файл с именем main, имя исходного файла на языке С++ main.cpp. Компиляция должна пройти без ошибок и предупреждений и завершиться созданием исполняемого файла main. Запустим наш файл из текущей директории командой: ./main Hello Word Ну вот, он работает =) Вернуть панели mc можно повторным нажатием Ctrl-O. Будем привыкать к хорошему, чтобы каждый раз не вводить строку компиляции воспользуемся утилитой GNU Make: В нашей папке test создадим простейший make-файл с именем Makefile (без расширения):
# так обозначаем комментарии make файла # зададим необходимые переменные make, # подставлять их значения можно так: $(имя_переменной)
# имя исходного и исполняемого файла TARGET= main # компилятор CC_LINUX= g++
# теперь опишем первую цель нашей сборки ее имя – linux # команды сборки обязательно предваряются Tab-ом linux: # Compiling for linux $(CC_LINUX) -o $(TARGET)_linux $(TARGET).cpp strip $(TARGET)_linux
из консоли, находясь в нашей папке test запустим Makefile командой: make linux
Здесь видно, что вместо переменных make
подставил их значения и скомпилировал исполняемый файл, в конце
запустил утилиту strip из набора утилит компилятора, которая убрала из
нашего исполняемого файла отладочную информацию существенно сократив
его размер. Особенность утилиты make в том, что если мы запустим
make без указания цели, то выполняться будет первая описанная цель -
linux (в нешем случае мы могли компилировать и make без параметров).
Теперь
мы умеем компилировать под целевую платформу Linux простые консольные
программы. Но голая консоль – это скучно, создадим простейшее
приложение с использованием графической кросс-платформенной библиотеки
SDL.
Для начала установим libsdl1.2-dev (самый простой путь –
Synaptic или другой менеджер пакетов). Для проверки работ SDL создадим
простой проект (редактируем наш старый main.cpp):
#include <cstdio> #include "SDL.h"
//Константы и переменные const int SCREEN_WIDTH= 240; const int SCREEN_HEIGHT= 320; const int SCREEN_BPP= 16; Uint16 back_color; //Этим цветом будем закрашивать экран //Основная поверхность SDL SDL_Surface *screen= NULL;
//Главная функция программы int main( int argc, char* args[] ) { //Инициализация SDL SDL_Init(SDL_INIT_VIDEO); //Создадим поверхность SDL screen= SDL_SetVideoMode(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_BPP, SDL_SWSURFACE); //Проверим, создалась ли она if(screen== NULL) printf("Unable to SetVideoMode\n"); else printf("VideoMode settings -Ok\n"); //Получим back_color в формате screen back_color= SDL_MapRGB(screen->format, 128, 0, 0); //Запрещаем доступ к поверхности SDL_LockSurface(screen); //Заполним поверхность красным SDL_FillRect (screen, NULL, back_color); //Разрешаем доступ к поверхности SDL_UnlockSurface(screen); //Отобразим поверхность SDL_Flip(screen); // show screen //Ждем 3 сек., радуемся SDL_Delay(3000); //Выход из SDL printf("Quiting SDL...\n"); SDL_FreeSurface(screen); SDL_Quit(); return 0; }
Теперь сообщим компилятору и линковщику, где брать хедеры (заголовочные
файлы .h) и либы (библиотечные файлы .a, .so) библиотеки SDL. Для этого
отредактируем наш Makefile:
TARGET= main CC_LINUX= g++ #Путь к SDL и линковка SDL.so LIBSDL= -I/usr/include/SDL -L/usr/lib -lSDL
linux: # Compiling for linux $(CC_LINUX) -o $(TARGET)_linux $(TARGET).cpp $(LIBSDL) strip $(TARGET)_linux
Ввели новую переменную LIBSDL в которой с помощь ключей –I и –L задали
путь к хедерам и либам. Линковщику ключом -l указали прилинковать
libSDL.so (префикс lib и расширение .so, .a указывать не нужно). Скомпилируем: $ make linux на этот раз вывод такой: g++ -o main_linux main.cpp -I/usr/include/SDL -L/usr/lib -lSDL strip main_linux
После сборки проекта командой make linux получаем исполняемый файл для
Linux main_linux, который покажет нам красный экран 240х320 на 3
секунды.
4. Добавляем компиляцию под Windows
Наш исходный код (файл main.cpp) без переделок может быть скомпилирован
и под Windows. Для этого применим компилятор MinGW (Minimalist GNU for
Windows) http://www.mingw.org.
MinGW обеспечивает нативную функциональность и производительность
посредством прямых вызовов Windows API и хорошо подходит для компиляции
кроссплатформенных библиотек подобных SDL и приложений c их
использованием. Установим MinGW в нашу систему. Самый простой путь – установить из
менеджера пакетов. Ставим пакет mingw32 (он потянет за собой runtime и
binutils).
Распаковываем полученный архив (я не мудрствовал и сделал это
прямо их контекстного меню архива). Переходим в mc в этот архив(так
чтобы видеть файл Makefile), гасим панели,Ctrl-O, в консоли становимся
рутом у меня $ sudo su затем устанавливаем SDL для mingw32 # make cross выводом этой команды будут много строк копирования (со знаком → в
середине). Кстати посмотрите куда кладутся .h и .a файлы, там же
увидите где потом взять SDL.dll чтобы положить его в папку с .exe
файлом (который мы скоро создадим).
Ок, все готово для компиляции под win32. Наш предыдущий исходник main.cpp вполне заработает и здесь, менять его
не нужно. Добавим только переменные CC_WIN32, LIB_WIN32, CFLAGS_WIN32,
LIBSDL_WIN32 и цель win32 в наш Makefile:
TARGET= main
#for linux CC_LINUX= g++ LIBSDL= -I/usr/include/SDL -L/usr/lib -lSDL
Запустите компиляцию командой make win32 В результате получили исполняемый файл main_win32.exe. Попробуем его запустить и получаем сообщение об ошибке: $ ./main_win32.exe err:module:import_dll Library SDL.dll (which is needed by L"H:\\test\\main_win32.exe") not found err:module:LdrInitializeThunk Main exe initialization for L"H:\\test\\main_win32.exe" failed, status c0000135
Нашему файлу необходима библиотека SDL.dll (не удивительно, ведь его
размер всего 8192 байта). Библиотеку копируем из
/usr/local/cross-tools/i386-mingw32/bin и вставляем в папку нашего
проекта — test (далее, распространяя ваши программы для платформы win32
не забывайте класть в папку с проектом SDL.dll). Запускаем ./main_win32.exe, теперь все работает.
Если Вы были внимательны, то заметили — у нас в Makefile появилась еще
одна новая цель- clean. Эта цель применяется для очистки папки проекта
от объектных и исполняемых файлов (у нас пока только для исполняемых).
Подробнее на этой цели мы остановимся позже — при описании создания
проекта с несколькими файлами.
5. Добавляем компиляцию под WindowsCE
На этот раз все еще проще. Для компиляции будем использовать GNU gcc (cegcc) — именно его разновидность -компилятор mingw32ce, обеспечивающий нативный код под WinCE. Для данной платформы воспользуемся библиотекой SDL 1.2.6 c патчем от Arisme скачать все это можно с этой странички. Но Вы можете поступить проще - скачать архив, который содержит уже установленные mingw32ce и пропатченную под WinCE SDL. Вам останется только
разархивировать и разнести по папкам файлы из opt в /opt а из usr в
/usr. И все. Для начала нам этого вполне хватит.
Наш «старый» файл main.cpp опять оставим без изменений. Makefile же
дополним для компиляции под wince переменными CC_WINCE, CFLAGS_WINCE,
LIBSDL_WINCE, добавляем цель компиляции wince. Для того, чтобы make
clean могла удалять экзешники wince подправим ее описание
соответствующим образом:
TARGET= main
#for linux CC_LINUX= g++ LIBSDL= -I/usr/include/SDL -L/usr/lib -lSDL
Компилируем под wince командой make wince По размеру исполняемого файла для wince можно догадаться что он уже
содержит SDL (она прилинкована статически) и добавления SDL.dll в папку
проекта для wince не требуется. Использование статически прилинкованной библиотеки SDL накладывает на нас определенные обязательства - мы имеем право использовать SDL в таком виде только в свободных проектах. Подробнее об использовании этой библиотеки читаем здесь.
Иногда бывает, что файл на WinCE все же отказывается запускаться
жалуясь на отсутствие необходимой библиотеки (или нескольких). Что
конкретно ему требуется можно определить (как для win32 так и для
wince) программой Dependency walker она хорошо работает в Linux Wine.
6. В добрый путь
Ну вот, в общем-то и вся премудрость. Теперь мы можем компилировать
один исходный файл под три платформы. Что-то в этой статье я намеренно
упростил (зачаточное использование мощной GNU Make), о чем-то намеренно
умолчал (флаги компиляции). Но это ведь статья а не книга =). «Копайте»
сами, для дальнейшего роста это необходимо. В любом случае если что-то
не получится, возникнут вопросы, дополнения, предложения и пожелания —
я к Вашим услугам.