Plik Makefile i Biblioteki dynamiczne. Linux Ubuntu

0

Cześć, właśnie uczę się pracować na Linuxie (Ubuntu) i chciałem skompilować program. Napisałem program w C który składa się z trzech plików. Plik główny main.c, nagłówkowy calc.h i plik z obliczeniami calc.c. Chciałbym stworzyć plik makefile, tak żeby pliki calc.h i calc.c kompilowały się do biblioteki dynamicznej która następnie powinna zostać podlinkowana podczas kompilacji pliku main.c. Na razie mój makefile wygląda tak:

#include <stdio.h>
#include <stdlib.h>
 
#include "calc.h"
#include "calc.c"
 
int main()
{
int pick;
float a,b, result;
 
printf("====Pola Figur====\n");
printf("  \n");
printf("1. Pole prostokata\n");
printf("2. Pole kwadratu\n");
printf("3. Pole trojkata\n");
printf("4. Pole kola\n");
printf("  \n");
printf("Podaj wartosc: \n");
scanf("%d", &pick);
printf("  \n");
switch (pick){
case 1: 
printf("Wybrales pole prostokota. Wzor na pole prostokota to \"P=a*b\"\n");
printf("Podaj dlugosc boku a: ");
scanf("%f", &a);
printf("Podaj dlugosc boku b: ");
scanf("%f", &b);
result=rectangle(a,b);
printf("Pole prostokota wynosi: ");
printf("%f\n", result);
break;
 
case 2: 
printf("Wybrales pole kwadratu. Wzor na pole kwadratu to \"P=a*a\"\n");
printf("Podaj dlugosc boku a: ");
scanf("%f", &a);
result=square(a);
printf("Pole kwadratu wynosi: ");
printf("%f\n", result);
break;
 
case 3: 
printf("Wybrales pole trojkata. Wzor na pole trojkata to \"P=(a*h)/2\"\n");
printf("Podaj dlugosc boku a: ");
scanf("%f", &a);
printf("Podaj wysokosc trojkata: ");
scanf("%f", &b);
result=triangle(a,b);
printf("Pole trojkata wynosi: ");
printf("%f\n", result);
break;
 
case 4: 
printf("Wybrales pole kola. Wzor na pole kola to \"P=Pi*r^2\"\n");
printf("Podaj dlugosc promienia r: ");
scanf("%f", &a);
result=circle(a);
printf("Pole kola wynosi: ");
printf("%f\n", result);
break;
 
default: printf("Nie ma takiej opcji w menu");
}
 
 
return 0;
}

Plik main.c wygląda tak:

#ifndef CALC_H
#define CALC_H
 
float rectangle(float a, float b);
float square(float a);
float triangle(float a, float b);
float circle(float a);
#endif

a plik calc.c tak:

#include "calc.h"
#include <math.h>
 
float rectangle(float a, float b)
{
return a*b;
}
float square(float a)
{
return a*a;
}
float triangle(float a, float b)
{
return a*b*0.5;
}
float circle(float a)
{
return a*a*M_PI;
}

Napisałem do tego takiego makefile:

CC = gcc
  
all: main.o libfigure.so
    $(CC) main.o -Wl,-rpath=. -L. -lfigure -o program 
  
main.o: main.c
    $(CC) main.c -c -o main.o
      
calc.o: calc.c
    $(CC) calc.c -c -o calc.o
     
libfigure.so: calc.o
    $(CC) -shared -fPIC calc.o

Wyskakuje mi taki komunikat: "Makefile recipe for terget 'all' failed
make: *** [all] Error 1

0

Przecież dołączyłeś calc.c za pomocą #include to dołączasz go również poprzez bibliotekę WTF?

0

@_13th_Dragon: Ok, ale teraz jak usunełem #include "calc.c" to dalej mam taki sam błąd :(

0

Najpierw sprawdź czy polecenia się kompilują poprawnie, czyli:
gcc calc.c -c -o calc.o
gcc main.c -c -o main.o
gcc -shared -fPIC calc.o -o libfigure.so
gcc main.o -Wl,-rpath=. -L. -lfigure -o program

nie podoba mi się libfigure && -lfigure

0

Storzyłem plik makefile jak powyżej i wyskakuje błąd makefile *** brakujący separator. Stop.

0
believer88 napisał(a):

Storzyłem plik makefile jak powyżej i wyskakuje błąd makefile *** brakujący separator. Stop.

Ale zanim stworzysz Makefile (może wstawiłbyś jego kod, bo inaczej nie pomożemy z Twoim brakującym separatorem), zrób to co napisał @_13th_Dragon -- i napisz, czy działa...

PS. Jeśli plik jest taki jak powyżej, to nie będzie działał... :/ Bo @_13th_Dragon nie podał Ci nie Makefile'a lecz polecenia do wykonania -- a to nie to samo...

0

$(CC) -c -o main.o - to nie zadziała bo brakuje nazwy z plikiem źródłowym. Możesz usunąć -o, kompilator sam zamieni rozszerzenie .c na .o. Poza tym trzeba stworzyć fałszywy target all bo takiego pliku nigdzie nie tworzysz. To może wyglądać mniej więcej tak:

.PHONY: all clear
all: main

main: main.o libfigure.so
    $(CC) ...
0

@_13th_Dragon: Po wpisaniu dwóch pierwszych poleceń powstały kolejno pliki calc.o i main.o. po trzeciej komendzie dostałem komunikat cannot find -libfigure.so collect2: error: ld returned 1 exit status. Po ostatniej komendzie dostałem komunikat gcc: error: _WI,: Nie ma takiego pliku ani katalogu gcc: error: unrecognized command line option '-rpath=."

0

@jvoytech: czyli mam usunąć moje polecenie all i wpisać to co ty proponujesz?

1

libfigure.so: calc.o
$(CC) -shared -fPIC calc.o

O nie nie, to pliki .o, które mają składać się na .so muszą być skompilowane z -fPIC. Żeby skompilować to w jednej linijce piszesz coś takiego

gcc -shared -o libhello.so -fPIC hello.c

Jak chcesz Makefile to będzie jakoś tak

$(BUILD)/libhello.so: $(OBJ_DIR)/libhello.o
	@$(CXX) -o $@ $^ -shared
	@echo "$<"

$(OBJ_DIR)/%.o: $(SRC_DIR)/%.cpp
	@$(CXX) -c -fPIC $^ -o $@ 
	@echo "$<"

EDIT
A budowanie .so nie dodajesz jako zależności w recepcie do aplikacji tylko do all, a w binarce tylko linkujesz, czy w sumie wychodzi jakoś tak

.PHONY: all

all: $(BUILD)/libhello.so $(BUILD)/program

$(BUILD)/program: $(OBJ_DIR)/main.o
	@$(CXX) -o $@ $^ -L$(BUILD)/ -l:libhello.so -pthread
	@echo "$<"

$(BUILD)/libhello.so: $(OBJ_DIR)/libhello.o
	@$(CXX) -o $@ $^ -shared
	@echo "$<"

$(OBJ_DIR)/%.o: $(SRC_DIR)/%.cpp
	@$(CXX) -c -fPIC $^ -o $@ 
	@echo "$<"

Brakuje tylko definicji ścieżek i CXX. Aa, no i main.o zbuduje Ci się z fPIC, ale to to już sobie sam poprawisz.

1

@believer88:

believer88 napisał(a):

@jvoytech: czyli mam usunąć moje polecenie all i wpisać to co ty proponujesz?

ty budujesz plik wykonywalny program, więc bazowy szkielet wygląda tak:

.PHONY: all
all: program

program: main.o plik1.o plik2.o ...
    $(CC) -o program main.o plik1.o plik2.o

target all to taki ogólnie przyjęty standard, ale musi on zostać oznaczony .PHONY bo make zakłada, że plik all powstanie w wyniku działania tego przepisu, a tak nie jest.

Post wyżej @several zastosował zmienne automatyczne $@ $^, które zastępują nazwę celu i składników, pozwalając na zwięzłe pisanie Makefile, ale początkującego może trochę przerazić. Jakbym chciał je użyć to wyglądało by to tak:

program: main.o plik1.o plik2.o ...
    $(CC) -o $@  $^

ponieważ $@ to jest cel działania przepisu, a $^ wszystkie składniki od których zależy "target". $^ stosuje się przede wszystkim w linkerze. Przy kompilacji zazwyczaj potrzebujesz pierwszego składnika (*.c) i możesz zastąpić go $<, np:

main.o: main.c plik1.h plik2.h ...
    $(CC) -c main.c

zastąpiłbym tym:

main.o: main.c plik1.h plik2.h ...
    $(CC) -c $<

$< oznacza pierwszy składnik, czyli main.c

Przy bardziej zaawansowanych Makefile nie dodaje się zależności od plików nagłówkowych bo przy dodaniu/odjęciu nagłówka w pliku źródłowym (#include "plik1.h") należałoby zmodyfikować Makefile, a to może szybko stać się źródłem problemów, gdy się o tym zapomni. Do automatyzacji tej czynności uruchamia się kompilator z odpowiednimi parametrami (gcc -MM -MMD), który generuje pliki z zależnościami *.d, które dołącza się w Makefile (include).

1

Jeśli nie musisz sam pisać Makefile, to możesz rozważyć użycie CMake

Do pliku CMakeLists.txt wrzuć coś takiego:

cmake_minimum_required (VERSION 3.1) 
project (CALC) 

set ( SOURCES main.c )     # można wymienić więcej plików z "głównego" projektu rozdzielając je spacjami
set ( LIB_SOURCES calc.c ) # j.w. jeśli biblioteka ma składać się z wielu plików

add_compile_options( -Wall -Wextra )   # czy co tam lubisz...

add_executable(main ${SOURCES} )
add_library (calc SHARED ${LIB_SOURCES} )

target_link_libraries (main calc)

Potem w katalogu projektu:

mkdir build
cd build
cmake ..
VERBOSE=1 make

Do polecenia cmake możesz dodać opcję -DCMAKE_BUILD_TYPE=Release albo -DCMAKE_BUILD_TYPE=Debug

1 użytkowników online, w tym zalogowanych: 0, gości: 1