Pracuję aktualnie nad pewnym projektem. Pierwszy problem napotkałem już na samym początku. Projekt był tworzony pod Linuxem. Ja jako zapalony Windowsiarz nie będę przenosił się tylko dla jednego projektu na Linuxa. Rozwiązaniem byłoby postawienie maszyny wirtualnej z Linuxem. Jednak skoro mam IDE działające cross-platformowo to bez znaczenia na jakim systemie będę pracować, prawda? No właśnie nie do końca. Do projektu zostały dołączone pliki Makefile. Pliki te to skrypty które ułatwiają kompilację projektu. Jednak są to pliki Linuxowe, które nie bardzo działają na Windowsie. Oczywiście można korzystać z czegoś takiego jak Cygwin. Jednak środowisko którego chciałem użyć do tego projektu(Clion) korzysta z Cmake’a. Jest to coś podobnego do Makefile. Jednak polecenia są różne. Na początku myślałem, ze istnieje jakiś skrypt/aplikacja przerabiająca pliki Makefile do plików CMakeLists(czyli używanych przez Cmake’a). Jakie było moje zdziwienie, że czegoś takiego nie ma, a jak już jest to nie działa tak, jakbym oczekiwał. Jako, że moja walka z przepisywaniem plików Makefile do CMakeLists zakończyła się sukcesem postanowiłem opisać wszystko tutaj. Na początek przybliżę układ katalogów projektu:
Jak widać na powyższym schemacie w projekcie znajdują się 2 pliki Makefile. Jeden do „biblioteki” gauss, drugi do katalogu głównego.
Najpierw przyjrzymy się co zrobiłem w pliku CMakeLists.txt w katalogu gaus, gdyż ten plik utworzyłem w zasadzie od nowa, bez wzorowania się na Makefile. W tym drugim sprawdziłem tylko, jakie pliki potrzebne będą do zbudowania naszej „biblioteki”. Plik CMakeLists.txt w katalogu gaus zawiera tylko jedną linijkę:
add_library(GAUS_LIB matrix.c pivot.c piv_ge_solver.c pivot_ge_solver.c)
To polecenie dodaje nam bibliotekę o nazwie GAUS_LIB która jest budowana z podanych dalej plików. Biblioteka może być wykorzystywana w innych katalogach danego projektu oraz z tego co mi wiadomo po zbudowaniu przenoszona między projektami.
Znacznie ciekawszy jest plik CMakeLists.txt w katalogu głównym projektu. Jednak najpierw pokażę jak wygląda oryginalny plik Makefile:
aprox: main.o splines.o points.o aproksymator_na_bazie.o gaus/libge.a $(CC) -o aprox main.o splines.o points.o aproksymator_na_bazie.o -L gaus -l ge intrp: main.o splines.o points.o interpolator.o gaus/libge.a $(CC) -o intrp main.o splines.o points.o interpolator.o -L gaus -l ge prosta: main.o splines.o points.o prosta.o $(CC) -o prosta main.o splines.o points.o prosta.o aproksymator_na_bazie.o: makespl.h points.h gaus/piv_ge_solver.h $(CC) -I gaus -c aproksymator_na_bazie.c interpolator.o: makespl.h points.h gaus/piv_ge_solver.h $(CC) -I gaus -c interpolator.c
Mamy w nim 3 przypadki kompilacji: aprox, intrp i prosta. Każdy z tych przypadków korzysta z innych plików. Poniżej mamy zapisane jakich plików trzeba użyć, aby zbudować odpowiednie pliki wynikowe *.o. Cmake ma taką przewagę, że nie trzeba mu podawać plików nagłówkowych *.h, aby zbudować projekt. Najpierw przedstawię plik CMakeLists.txt w który przekształciłem powyższy Makefile:
cmake_minimum_required(VERSION 3.6) project(z10) set(CMAKE_CXX_STANDARD 11) include_directories("${PROJECT_SOURCE_DIR}/gaus") link_directories("${PROJECT_SOURCE_DIR}/gaus") add_subdirectory(${PROJECT_SOURCE_DIR}/gaus) set(APROX_FILES main.c splines.c points.c aproksymator_na_bazie.c) add_executable(aprox ${APROX_FILES}) target_link_libraries(aprox GAUS_LIB) set(INTRP_FILES main.c splines.c points.c interpolator.c) add_executable(interpolator ${INTRP_FILES}) target_link_libraries(interpolator GAUS_LIB) set(PROSTA_FILES main.c splines.c points.c prosta.c) add_executable(prosta ${PROSTA_FILES}) target_link_libraries(prosta GAUS_LIB)
Pierwsze 3 linijki są generowane automatycznie i dotyczą minimalnej wersji cmake’a, nazwy projektu i wersji standardu cxx, lecz nie są one tak interesujące jak pozostałe.
Komenda include_directories to odpowiednik Makefile’owego -L Komenda ta mówi kompilatorowi, gdzie szukać plików nagłówkowych które deklarujemy w plikach projektu. W tym przypadku pliki nagłówkowe z których korzysta projekt znajdują się w folderze gaus.
Następna linijka to polecenie link_directories. Mówi ona kompilatorowi, gdzie szukać bibliotek których używamy(w tym przypadku będzie to stworzona w poprzednim CMakeLists biblioteka GAUS_LIB). Ta komenda nie jest wymagana ze względu na późniejszą komendę, ale z nią projekt buduje się szybciej.
Komenda add_subdirectory „mówi” gdzie zlokalizowane są pliki projektu i inne pliki CMakeLIsts potrzebne do zbudowania projektu.
Teraz zaczynają się komendy, które bezpośrednio wpływają na to co zostanie zbudowane. Pierwsza z nich ustawia zmienną APROX_FILES, żeby zawierała pliki z których ma się zbudować plik wykonywalny. Następnie poleceniem add_executable tworzymy plik wykonywalny o nazwie aprox z plików które zawarte są w zmiennej APROX_FILES.
Teraz następuje polecenie target_link_libraries które jest bardzo ważne. Mówi ono jakich bibliotek użyć przy tworzeniu pliku wykonywalnego, czyli tak naprawdę skąd wziąć funkcje które wywoływane są w naszym projekcie.
Osobiście męczyłem się 2 dni, żeby stworzyć pliki CMakeLists, które w pełni odwzorowują funkcjonalność plików Makefile. Mam nadzieję, że dzięki temu wpisowi oszczędzę Tobie, drogi czytelniku czasu.