Oprogramowanie i wykorzystanie stacji roboczych Wykład 10

Transkrypt

Oprogramowanie i wykorzystanie stacji roboczych Wykład 10
Oprogramowanie i wykorzystanie
stacji roboczych
Wykład 10
Dr inż. Tomasz Olas
[email protected]
Instytut Informatyki Teoretycznej i Stosowanej
Politechnika Cz˛estochowska
Wykład 10 – p. 1/4
Wieloteksturowanie (I)
Wieloteksturowanie (multiteksturowanie) to technika
pozwalajaca
˛ na jednoczesnego nakładania na obiekt wiecej
˛
niż jednej tekstury.
Wieloteksturowanie wprowadzono w wersji 1.3 biblioteki
OpenGL. We wcześniejszych implementacjach podobny efekt
można uzyskać przy użyciu techniki teksturowania
wieloprzebiegowego, które jest jednak bardziej skomplikowane
(wymaga stosowania mieszania kolorów oraz wielokrotnego
przeliczania geometrii renderowanego obiektu) oraz znacznie
wolniejsze.
Wykład 10 – p. 2/4
Wieloteksturowanie (II)
Wybór aktualnej jednostki teksturujacej
˛ umożliwia funkcja:
void glActiveTexture (GLenum texture);
gdzie:
texture - określa numer wybranej jednostki teksturujacej
˛ (GL_TEXTUREi, gdzie
i przyjmuje wartość od 0 do 31).
W przypadku wieloteksturowania definiowanie współrz˛ednych tekstur przy użyciu
funkcji z grupy glTexCoord daje efekt tylko dla pierwszej jednostki teksturujacej
˛
(GL_TEXTURE0).
Definiowane współrzednych tekstur dla dowolnej jednostki teksturujacej umozliwiajaja˛
funkcje z grupy glMultiTexCoord (posiadaja˛ dodatkowy parametr target określajacy
˛
numer jednostki teksturujacej).
˛
Przykład:
void glMultiTexCoord2f (GLenum target, GLfloat s, GLfloat t);
Wykład 10 – p. 3/4
Pozostałe funkcje
Kopiowanie tekstury z bufora kolorów
Zmiana cz˛eści danych tekstury
Tekstury zastepcze
˛
(proxy)
Kompresja tekstur
Automatyczne generowanie współrz˛ednych tekstur
Tekstury głebi
˛
Wykład 10 – p. 4/4
Mapowanie nierówności
Mapowanie nierówności (ang. bump mapping) jest technika˛
polegajac
˛ a˛ na wykorzystaniu tekstury do modyfikacji wektora
normalnego.
Wykład 10 – p. 5/4
Mapowanie tekstur - liniowe
Wykład 10 – p. 6/4
Mapowanie tekstur - cylindryczne
Wykład 10 – p. 7/4
Mapowanie tekstur - sferyczne
Wykład 10 – p. 8/4
Selekcja obiektów
Selekcja jest w rzeczywistości trybem renderowania, w którym
żadne piksele nie sa˛ kopiowane do bufora ramki. Zamiast tego
prymitywy sa˛ rysowane w bryle widzenia (tak jak normalnie
pojawiłyby sie˛ w buforze ramki) w wyniku czego tworza˛ rekordy
trafień w buforze selekcji.
Przed przystapieniem
˛
do selekcji obiektów wcześniej należy
utworzyć bufor selekcji oraz oznaczyć (nazwać) prymitywy lub
grupy primitywów tak, aby można je było zidentyfikować w
buforze selekcji.
Nastepnie
˛
przetwarza sie˛ bufor selekcji w celu wyznaczenia,
które obiekty przecinaja˛ bryłe˛ widzenia.
Najcz˛eściej określa sie˛ bryłe˛ widzenia odpowiadajac
˛ a˛
wskaźnikowi myszy, a nastepnie
˛
sprawdza, które z obiektów
zostały wskazane przez kursor myszy.
Wykład 10 – p. 9/4
Zmiana trybu renderowania
Do zmiany trybu renderowania służy funkcja:
GLint glRenderMode (GLenum mode);
gdzie:
mode - przyjmuje nastepuj
˛ ace
˛ wartości:
GL_RENDER - tryb renderowania (domyślny),
GL_SELECT - tryb selekcji,
GL_FEEDBACK - tryb sprz˛eżenia zwrotnego.
Wykład 10 – p. 10/4
Stos nazw obiektów
Nazwy obiektów, które moga˛ zostać wybrane w trakcie selekcji
sa˛ przechowywane na stosie nazw.
Nazwy obiektów, nie sa˛ niczym innym jak cyfrowymi
identyfikatorami w postaci liczb całkowitych bez znaku.
Pojedyncza nazwa może zostać przypisana zarówno
pojedynczemu prymitywowi, jak również bardziej złożonemu
obiektowi.
Wielkość stosu nazw nie może być mniejsza niż 64 (jest
zależna od implementacji).
Przed użyciem stosu nazw obiektów należy go zainicjować:
void glInitNames();
Do nadania nazw poszczególnym obiektom służy funkcja:
void glLoadName (GLuint name);
gdzie:
name - identyfikator (nazwa) obiektu.
Wykład 10 – p. 11/4
Obiekty hierarchiczne
W przypadku, gdy obiekty maja˛ strukture˛ hierarchiczna˛
(obiekty złożone), to konieczne jest umieszczanie kolejnych
nazw obiektów na bierzacym
˛
stosie:
void glPushName(GLuint name);
Po dodaniu obiektu podrz˛ednego należy zdiać
˛ nazwe˛ obiektu
ze stosu:
void glPushName(GLuint name);
Wykład 10 – p. 12/4
Nazwanie obiektów - przykład
void MyGLWidget::paintGL()
{
glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
glTranslatef(0.0f, 0.0f, -20.0f);
glInitNames();
glPushName(0);
glLoadName(redRectangle);
glColor3f(1.0f, 0.0f, 0.0f);
glRectf(1.0f, 1.0f, 5.0f, 5.0f);
glLoadName(blueRectangle);
glColor3f(0.0f, 0.0f, 1.0f);
glRectf(-5.0f, -5.0f, -1.0f, -1.0f);
glFlush();
}
Wykład 10 – p. 13/4
Ustawienie bufora selekcji
Aby skorzystać z bufora selekcji należy utworzyć bufor selekcji
i ustawić go, przed przejściem w tryb selekcji, przy pomocy
funkcji:
void glSelectBuffer (GLsizei size, GLuint *buffer);
gdzie:
size - rozmiar bufora selekcji,
buffer - utworzony bufor selekcji, w którym przechowywane sa˛ nazwy obiektów
wybranych w trakcie selekcji - bufor powinien być na tyle duży, aby zmieścić
informacje o wszystkich obiektach wybranych w trybie selekcji.
Ilość wybranych obiektów zwracana jest przez funkcje˛
glRenderMode w momencie powrotu do trybu renderowania GL_RENDER (po zakończeniu pracy w trybie selekcji).
Wykład 10 – p. 14/4
Rekordy trafień
Informacje o wybranych obiektach zawarte sa˛ w rekordach
trafień znajujacych
˛
sie˛ w buforze selekcji.
Każdy rekord trafień zawiera przynajmniej cztery elementy
(kolejne liczby całkowite):
[0] - liczba identyfikatorów obiektów znajdujacych
˛
sie˛ na stosie nazw w
momencie trafienia,
[1] - minimalna wartość współrz˛ednych z prymitywów graficznych wchodzacych
˛
w skład wybranego obiektu (wartość współrz˛ednej z jest przekształcana z
zakresu < 0, 1 > poprzez mnożenie jej przez 232 − 1 i zaokraglenie
˛
do najbliższej
liczby całkowitej),
[2] - maksymalna wartość współrz˛ednych z prymitywów graficznych
wchodzacych
˛
w skład wybranego obiektu (wartość ta jest wyliczana analogicznie
jak powyżej),
[3] - najniższy element stosu nazw obiektów (pierwszy odłożony na stos),
[3] - najwyższy element stosu nazw obiektów (ostatni odłożony na stos),
Wykład 10 – p. 15/4
Modyfikacja bryły widzenia
Podczas tworzenia macierzy opisujacej
˛ nowa˛ bryłe˛ widzenia
bardzo przydaje sie˛ funkcja:
void gluPickMatrix(GLdouble x, GLdouble y,
GLdouble width, GLdouble height,
const GLint viewport[4]);
gdzie:
x i y - środek żadanej
˛
bryły widzenia we współrz˛ednych okna,
width i height - określaja˛ szerokość i wysokość bryły widzenia w pikselach
okna.
viewport - zawiera współrz˛edne okna dla aktualnie zdefiniowanego widoku.
Można je łatwo odczytać wywołujac:
˛
glGetIntegerv(GL_VIEWPORT, viewport);
Przed wywołaniem funkcji gluPickMatrix najpierw
zachować bieżacy
˛ stan macierzy rzutowania (czyli zachować
bieżac
˛ a˛ bryłe˛ widzenia).
Wykład 10 – p. 16/4
Wybór obiektów - przykład (I)
void MyGLWidget::mousePressEvent(QMouseEvent* event)
{
qDebug("mouse (%d, %d)\n", event->x(), event->y());
const int BUFFER_LENGTH = 128;
GLuint selectBuff[BUFFER_LENGTH];
glSelectBuffer(BUFFER_LENGTH, selectBuff);
GLint viewport[4];
glGetIntegerv(GL_VIEWPORT, viewport);
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
gluPickMatrix(event->x(), viewport[3] - event->y(), 2, 2, viewport);
gluPerspective(45.0f, 1.0, 1.0, 400.0);
Wykład 10 – p. 17/4
Wybór obiektów - przykład (II)
glRenderMode(GL_SELECT);
glMatrixMode(GL_MODELVIEW);
paintGL();
GLint hits = glRenderMode(GL_RENDER);
qDebug("Hits: %d", hits);
if (hits == 1)
qDebug("Selection object: %d", selectBuff[3]);
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
}
Wykład 10 – p. 18/4
Sprzeżenie
˛
zwrotne
Sprz˛eżenie zwrotne, podobnie jak selekcja, jest trybem
renderowania, w którym nic nie jest rysowane na ekranie.
Zamiast tego, do bufora sprz˛eżenia zwrotnego wpisywane sa˛
informacje na temat sposobu renderowania sceny. Te
informacje obejmuja˛ współrz˛edne okna przetransformowanych
wierzchołków, kolory wierzchołków już po oświetleniu, a także
dane tekstury.
Przejście do trybu sprz˛eżenia zwrotnego odbywa sie˛ podobnie
jak przejście do trybu selekcji, poprzez wywołanie funkcji
glRenderMode z parametrem GL_FEEDBACK.
W celu wypełnienia bufora sprz˛eżenia zwrotnego i powrotu do
normalnego trybu renderowania musisz wywołać funkcje˛
glRenderMode(GL_RENDER).
Wykład 10 – p. 19/4
Bufor sprzeżenia
˛
zwrotnego
Bufor sprz˛eżenia zwrotnego jest tablica˛ wartości
zmiennoprzecinkowych, tworzona˛ za pomoca˛ funkcji
glFeedbackBuffer:
void glFeedbackBuffer(GLsizei size, GLenum type, GLfloat *buffer);
gdzie:
size - maksymalna ilość pozycji zaalokowanych w buforze buffer,
type - określa rodzaj danych o wierzchołkach, umieszczanych w buforze
sprz˛eżenia zwrotnego,
buffer - buforz, w którym zapisane zostana˛ informacje dotyczace
˛ wierzchołków.
Dla każdego wierzchołka, w buforze umieszczany jest osobny blok danych.
Wykład 10 – p. 20/4
Typy danych
typ
współrz˛edne
dane koloru
dane tekstury
liczba wartości
GL_2D
x, y
-
-
2
GL_3D
x, y, z
-
-
3
GL_3D_COLOR
x, y, z
C
-
3+C
GLJD_COLOR_TEXTURE
x, y, z
C
4
7+C
GL_4D_COLOR_TEXTURE
x, y, z, w
C
4
8+C
Wykład 10 – p. 21/4
Dane sprzeżenia
˛
zwrotnego
Bufor sprz˛eżenia zwrotnego zawiera liste˛ elementów, po
których nastepuj
˛ a˛ dane wierzchołków i ewentualnie koloru i
tekstury.
Element
Prymityw
GL_POINT_TOKEN
Punkty
GL_LINE_TOKEN
Linie
GL_LINE_RESET_TOKEN
Segment linii po wyzerowaniu wzorca linii
GL_POLYGON_TOKEN
Wielokat
˛
GL_BITMAP_TOKEN
Bitmapa
GL_DRAW_PIXEL_TOKEN
Narysowany prostokat
˛ piksela
GL_COPY_PIXEL_TOKEN
Skopiowany prostokat
˛ piksela
GL_PASS_THROUGH_TOKEN
Znacznik zdefiniowany przez użytkownika
Wykład 10 – p. 22/4
Konstrukcja wielomodułowa programów
Zapewnia logiczna˛ strukture˛ i poprawia czytelność programu.
Niezależne moduły moga˛ być oddzielnie testowane i po
kompilacji wykorzystywane w różnych programach.
Umożliwia wykorzystywanie w programie w jezyku
˛
C/C++
procedur napisanych w innych jezykach
˛
programowania.
Zwieksza
˛
szybkość kompilacji w trakcie modyfikowania
programu.
Wykład 10 – p. 23/4
Make
Make jest narz˛edziem automatycznie określajacym,
˛
które
fragmenty kodu wymagaja˛ ponownej kompilacji i wykonuje ich
kompilacje.
˛
Plik makefile (Makefile) - opisuje relacje pomiedzy
˛
plikami w
projekcie oraz polecenia służace
˛ do uaktualniania
poszczególnych plików.
wywołanie:
make
make -f makefile_name
make nazwa_reguly
Reguły, dyrektywy i definicje makr powinny rozpoczynać sie˛ w
pierwszej kolumnie tekstu, natomiast polecenia musza˛ być
poprzedzone co najmniej jednym znakiem pustym.
Wykład 10 – p. 24/4
Make - reguły
Reguły (rules) opisuja˛ kiedy i w jaki sposób należy „odświeżać”
pliki.
Można rozróżnić dwa rodzaje reguł:
Reguły jawne:
zbiór_uakt [zbiór_uakt [...]]: {{ścieżki}][zbiór-uzal ...]
polecenie
przykład:
foo.o : foo.c defs.h
gcc -c -g foo.c
Reguły niejawne:
[katalog_źródł]\%.rozsz_źródł: [katalog_docelowy]\%.rozsz_docelow
polecenie
przykład:
%.o: %.c
gcc -c $<
Wykład 10 – p. 25/4
Make - makrodefinicje
Definicja:
nazwa_makrodefinicji=tekst_rozwini˛
ecia
przykład:
PROGRAM=bierki
OBJS=foo.c
Rozwiniecie:
˛
$(nazwa_makrodefinicji)
przykład:
$(PROGRAM): $(OBJS)
g++ -c $(OBJS) -o $(PROGRAM)
Wykład 10 – p. 26/4
Make - makrodefinicje predefiniowane
$* - makro to rozwija sie˛ w nazwe˛ zbioru uzależniajacego
˛
(ze
ścieżka˛ dostepu,
˛
lecz bez rozszerzenia),
$< - działa tak jak $*, z ta˛ różnica,
˛ że uzyskana nazwa zbioru
zawiera rozszerzenie,
$@ - rozwija sie˛ w nazwe˛ zbioru uzależnianego (wraz z jego
rozszerzeniem),
$: - rozwija sie˛ w ścieżk˛e dostepu
˛ do zbioru, ale bez jego
nazwy. Jeśli wystepuje
˛
w regułach jawnych, to uzyskana
zostanie ścieżka zbioru docelowego, w niejawnych zaś ścieżka
zbioru przetwarzanego.
Wykład 10 – p. 27/4
Make - przykład
PROJECT = bierki
INC
=
./include
INSTALLBIN = $(HOME)/opt/bin
CXX = g++-2.95
CXXFLAGS += -I$(INC)
OBJS = $(PROJECT).o okno.o
all: $(PROJECT)
%.o: %.cpp
$(CXX) $(CXXFLAGS) -c -o $@ $<
$(PROJECT): $(OBJS)
$(CXX) $(LDFLAGS) -o $@ $(OBJS) $(LIBS)
clean:
-rm -rf *.o
realclean: clean
-rm -rf $(PROJECT)
Wykład 10 – p. 28/4
Automatyczne generowanie zależności
Kompilator gcc posiada opcje -M, -MM, które generuja˛
zależności od plików nagłówkowych dla przetwarzanego pliku
źródłowego.
Przykład:
depend:
$(CXX) -MM $(CXXFLAGS) *.cpp > depend.mk
...
include depend.mk
Przykład wygenerowanych zależności:
domain.o: src/domain.cpp include/domain.h include/fxdrstream.h \
include/except.h
fxdrstream.o: src/fxdrstream.cpp include/fxdrstream.h
mpi_wrapper.o: src/mpi_wrapper.cpp include/mpi_wrapper.h
Wykład 10 – p. 29/4
Make - reguły standardowe
Istnieje zbiór reguł, których stosowanie powoduje, że pliki makefile
sa˛ znacznie prostsze do zrozumienia i stosowania:
all,
test,
clean,
dist,
distclean,
realclean,
install,
uninstal.
Wykład 10 – p. 30/4
Narzedzia
˛
wspierajace
˛ program make
- służy do automatycznego generowania
zależności pomiedzy
˛
plikami w projekcie (dostarczany razem z
systemem X Window),
makedepend
- służy do mechanicznego generowania plików makefile
dla systemu X. Opiera sie˛ na wywołaniu programu makedepend,
Imake
- generuje skrypt powłoki configure dopasowany do
projektu,
autoconf
- narz˛edzie podobny do autoconf - umożliwia
generowanie plików makefile, posiadajacy
˛ jednak wbudowane
mechanizmy badania zależności (podobna˛ do programu
Imake),
automake
- narz˛edzie firmy TrollTech pierwotnie utworzone dla
biblioteki Qt, ale może być wykorzystywane również w innych
projektach.
tmake
Wykład 10 – p. 31/4
GNU Autoconf (I)
Autoconf jest narz˛edziem (zestawem makr M4) służacym
˛
do
generownia skryptów powłoki, które pozwalaja˛ na
automatyczne dostosowanie pakietów oprogramowania (w
postaci kodu źródłowego) do specyfiki wielu różnych systemów
operacyjnych.
Wygenerowane skrypt konfiguracyjny (configure) jest
niezależny od narz˛edzia Autoconf i podczas kompilacji pakietu
nie jest ono wymagane.
Skrypt configure wykonuje szereg testów majacych
˛
na celu
sprawdzenie, czy i ewentualnie gdzie w systemie znajduja˛ sie˛
narz˛edzia, biblioteki, pliki nagłówkowe niezbedne
˛
do
poprawnego utworzenia programu. Po określeniu środowiska
w którym kompilowany jest program skrypt ten powinien
wygenerować odpowiednie definicje i opcje kompilacji.
Cz˛esto narz˛edzie Autoconf jest stosowany w połaczeniu
˛
z
programami Automake i Autoheader.
Wykład 10 – p. 32/4
GNU Autoconf (II)
Szczególnie ważna˛ kwestia˛ przy tworzeniu plików wejściowych
dla programu Autoconf jest określenie, co należy sprawdzać
przy kompilacji programu na różnych platformach
systemowych.
Plik konfiguracyjny tworzy sie˛ w oparciu o makra
sprawdzajace:
˛
Autoconf zawiera zbiór makr sprawdzajacych
˛
dla teypowych
elementów,
w przypadku braku gotowego testu dla danego elementu
(pliku nagłówkowego, bibilioteki, funkcji) można
wykorzystać test ogólny (np. na obecność pliku w systemie
plików),
w ostateczności należy utworzyć fragment testujacy
˛ w
jezyku
˛
Bourne shella.
Wykład 10 – p. 33/4
Tworzenie skryptu configure
W celu wygenerowania skryptu configure należy utworzyć
plik konfiguracyjny dla programu Autoconf: configure.ac (lub
configure.in).
Przykładowa struktura pliku configure.ac:
Uworzenie pliku configure nastapi
˛ po uruchomieniu
programu autoconf.
Wykład 10 – p. 34/4
Autoconf - przykład (I)
AC_INIT(sample,0.1)
AC_PROG_CXX
# Checks for performing tests
CPPUNIT="no"
AC_ARG_ENABLE(test, [--enable-test enable test suite [default=yes]],
,enable_test="yes")
if test ! "x$enable_test" = "xno"; then
AC_PATH_PROG(CPPUNIT_CONFIG,cppunit-config,no)
if test "x$CPPUNIT_CONFIG" = "xno"; then
AC_MSG_ERROR([cppunit is needed but not found.])
fi
CPPUNIT="yes"
CPPUNIT_CFLAGS=‘$CPPUNIT_CONFIG --cflags‘
CPPUNIT_LIBS=‘$CPPUNIT_CONFIG --libs‘
AC_SUBST(CPPUNIT_CFLAGS)
AC_SUBST(CPPUNIT_LIBS)
fi
AC_SUBST(CPPUNIT)
AC_CONFIG_FILES(Makefile)
AC_OUTPUT
Wykład 10 – p. 35/4
Autoconf - przykład (II)
Plik Makefile.in:
...
# CppUnit flags
ifeq ($(CPPUNIT), yes)
CXXFLAGS += @CPPUNIT_CFLAGS@
LDFLAGS += @CPPUNIT_LIBS@
endif
$(OBJ)/%.o: $(SRC)/%.cpp
$(CXX) -c $(CXXFLAGS) -o $@ $<
$(PROGRAM): $(OBJS)
$(CXX) -o $(PROGRAM) $(LDFLAGS) $(OBJ)
Wykład 10 – p. 36/4
Jam
Jednym z ciekawszych projektów, które w przyszłości moga˛
zastapić
˛ program make jest Jam.
Zalety:
Zwiekszenie
˛
projektu nie prowadzi do tak drastycznego zwiekszania
˛
sie˛ pliku
konfiguracyjnego jak ma to miejsce w przypadku programu make.
Automatycznie generowane sa˛ zależności od plików nagłówkowych.
Jam buduje duże projekty rozmieszczone w wielu katalogach za jednym
podejściem, bez rekursywnego nawracania jak to robi make, śledzac
˛ wszystkie
pliki jednocześnie.
Może pracować na wielu procesorach.
Jam jest mały (np. 92 kB), praktycznie nie obciaża
˛ procesora, i nie tworzy
dodatkowych plików (jak np. nmake, SunOS make).
Może być dowolnie konfigurowany poprzez tzw. reguły (Jamrules).
Wykład 10 – p. 37/4
Jam kontra Make
Plik konfiguracyjny dla programu Make:
progam: data.o main.o io.o
gcc data.o main.o io.o -o progam
data.o: data.c
gcc -c data.c
main.o: main.c
gcc -c main.c
io.o: io.c
gcc -c io.c
io.o: io.h
main.o: io.h data.h
data.o: data.h io.h
Plik konfiguracyjny dla programu Jam:
Main progam : data.c main.c io.c ;
Wykład 10 – p. 38/4
Apache Ant (I)
Apache Ant
jest cz˛eścia˛ projektu Apache Jakarta.
Opiera sie˛ na podobnym założeniu co make jeżeli chodzi o
zależności i reguły, ale nie zakłada że sa˛ to pliki i że głównym
celem reguł jest uaktualnienie zadań.
Używa jezyka
˛
XML.
Jest w pełni niezależny od platformy.
Został napisany z myśla˛ o Javie, ale posiada również wsparcie
do tworzenia projektu w innych jezykach
˛
programowania
(również w C++).
Ant jest napisany całkowicie w Javie.
Wykład 10 – p. 39/4
Apache Ant (II)
Plik konfiguracyjny dla programu Ant jest plikiem w formacie
XML. Zazwyczaj nosi nazwe˛ build.xml.
Składa sie˛ on z różnej ilości różnych tagów, zwanych <target>
(cel). Może to być np. kompilacja, utworzenie archiwum,
utworzenie dokumentacji czy też wysłanie poczty
elektronicznej.
Każdy cel składa sie˛ z dowolnej ilości wykonywanych zadań
<task>. Jest to prosta czynność, która bedzie
˛
wykonywana, np.
tworzenie katalogu, kopiowanie pliku, kompilacja kodu
źródłowego. Każde zadanie może posiadać parametry i być
zależne od innych zadań.
Wykład 10 – p. 40/4
Apache Ant - przykładowy plik build.xml
<?xml version="1.0" encoding="iso-8859-2"?>
<project name="first" basedir="." default="compile">
<path id="log4jclasspath">
<pathelement location="${basedir}"/>
<pathelement location="/../wspolne.jar"/>
</path>
<target name="compile">
<javac srcdir="."
classpathref="classpath"
/>
</target>
<target name="run">
<java
classpathref=""
classname="org.gridwise.Simple"
/>
</target>
</project>
Wykład 10 – p. 41/4