Laboratorio di Sistemi Operativi

Tool per la programmazione

Alberto Montresor
Renzo Davoli

Copyright © 2004-2005 Alberto Montresor, Renzo Davoli

Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.2 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A copy of the license can be found at: http://www.gnu.org/licenses/fdl.html#TOC1

Sommario

Come gestire progetti di programmazione molto grandi

Come organizzarli

Come compilarli

Come lavorare in cooperazione

Nota:

Con un'enfasi sul C, ma i concetti valgono anche per altri linguaggi di programmazione

Come organizzare un progetto

Ovviamente:

Programmi di grandi dimensioni non possono essere contenuti in un file singolo

Nel C

vi sono tecniche per semplificare gestione di file multipli

non sono "enforced": sono lasciate al programmatore

Un programma grande è diviso in moduli:

i file .h contengono le funzioni prototipo del modulo ed eventuali costanti

i file .c contengono le definizioni di funzioni del modulo

i moduli sono compilati separatamente

viene generato un eseguibile tramite linking dei moduli

Esempio: sample.c

#include <stdio.h>

#include "my_math.h"

int main()

{

int a, b, c;

puts("Input three numbers:");

scanf("%d %d %d", &a, &b, &c);

printf("The average of %d %d %d is %f.\n",

a,b,c,average(a,b,c));

return 0;

}

Esempio - Modulo my_math

/* my_math.h */

#define PI 3.1415926

float average(int x,
int y, int z);

float sum(int x,
int y, int z);

/* my_math.c */

#include "my_math.h"

float average(int x, int y, int z)

{

return sum(x,y,z)/3;

}

float sum(int x,
int y, int z)

{

return x+y+z;

}

Compilare questo semplice programma

Per generare my_math.o

Abbiamo bisogno di my_math.c e my_math.h

gcc –c my_math.c

Per generare sample.o

Abbiamo bisogno di sample.c e my_math.h

gcc –c sample.c

Per generare l'eseguibile

Abbiamo bisogno di my_math.o e sample.o

gcc –o sample sample.o my_math.o

Come utilizzare make

I programmi che consistono di molti moduli sono impossibili da mantenere manualmente

Si crea un Makefile

# Makefile for the sample

sample: sample.o my_math.o

cc –o sample sample.o my_math.o

sample.o: sample.c my_math.h

cc –c sample.c

my_math.o: my_math.c my_math.h

cc –c my_math.c

clean:

rm sample *.o core

Target

Target

Dipendenze

Comandi

Indentazioni

con tab, non

spazi

Makefile

Un makefile consiste in un insieme di regole del tipo:

target ... : prerequisites ...

command

...

...

Target:

il nome di un file da generare

un'azione da svolgere

Prerequisiti:

uno o più file utilizzati come input per creare il file target

Regole

una o più azioni da eseguire

Nota:

la prima regola è quella di "default", o radice

Come utilizzare make

Come?

Si salva il file con il nome Makefile o makefile nella stessa directory contenente i file

Si lancia l'utility make

Make

trova il Makefile

verifica le regole e le dipendenze e rigenera i file per cui è necessario un update

Esempio:

Se solo sample.c è stato modificato:

cc –c sample.c

cc –o sample sample.o my_math.o

Come utilizzare make

Per rimuovere tutti i file generati

make clean

Per ricompilare, è possibile:

Rimuovere tutti i file generati e ricompilare

make clean ; make

Oppure è possibile cambiare la data di ultima modifica:

touch my_math.h ; make

La data di ultima modifica di my_math.h prende l'ora corrente

Nota: questo perchè tutti i file dipendono da my_math.h

Utilizzare make con directory multiple

Con l'aumentare del numero di file .c

diventa difficile controllare tutto con un unico makefile

conviene utilizzare più makefile, uno per modulo

Un programma viene organizzato in questo modo:

una directory per modulo

file header:

una directory per tutti i file .h

oppure: file .h di un modulo nel modulo stesso

Makefile nella directory radice per la creazione dell'eseguibile/libreria

Makefile in ogni modulo dirigono la creazione dei file .o

Un esempio di Makefile

Si consideri un programma C composto da

una struttura dati a coda queue

queue_types.h, queue_interface.h, queue.c

una struttura dati a stack stack

stack_types.h, stack_interface.h, stack.c

un modulo principale

main.c

Organizzazione

Tre sottodirectory:
stack, queue, include

Tutti i file .h in include

stack

queue

include

main.c

Makefile

Makefile

s_t.h

s_i.h

stack.c

...

Makefile

stack.c

Un esempio di Makefile

stack contiene stack.c e il seguente Makefile:

all: stack.o

stack.o: stack.c \

../include/stack_types.h \

../include/stack_interface.h

gcc -I../include -c stack.c

clean:

rm -f *.o

Note: opzione -I di gcc

Specifica uno o più path in cui cercare per trovare file .h

Path multipli separati da virgole ","

Un esempio di Makefile

queue contiene queue.c e il seguente Makefile:

all: queue.o

queue.o: queue.c \

../include/queue_types.h \

../include/queue_interface.h

gcc -I../include -c queue.c

clean:

rm -f *.o

Un esempio di Makefile

main contiene main.c e il seguente Makefile:

all: main.o stackdir queuedir

gcc -o main main.o ../stack/stack.o \

../queue/queue.o

main.o: main.c include/*.h

gcc -Iinclude -c main.c

stackdir:

cd stack ; make all

queuedir:

cd queue ; make all

Un esempio di Makefile

clean:

rm -f *.o main core

cleanall:

rm -f *.o main core

cd stack ; make clean

cd queue ; make clean

Nota:

Ogni riga di comando viene eseguita tramite una subshell

Come viene gestita la directory corrente

Cosa succede se una directory manca?

Utilizzazione delle macro nei makefile

Macro nei Makefile

Utilizzate per semplificare la modifica dei Makefile

Utilizzate per ridurre la dimensione dei file semplificando espressioni ripetute

Sintassi

Definizione: name = value

Riferimenti: $(name) o ${name}

Un esempio di Makefile, rivisitato

Makefile in stack:

CC = gcc

HDIR = ../include

INCPATH = -I$(HDIR)

DEPH = $(HDIR)/stack_types.h \
$(HDIR)/stack_interface.h

SOURCE = stack.c

OBJECTS = stack.o

all: $(OBJECTS)

stack.o: $(SOURCE) $(DEPH)

$(CC) $(INCPATH) -c $(SOURCE)

clean:

rm -f *.o

Un esempio di Makefile, rivisitato

Makefile in queue

CC = gcc

HDIR = ../include

INCPATH = -I$(HDIR)

DEPH = $(HDIR)/queuetypes.h \
$(HDIR)/queueinterface.h

SOURCE = queue.c

OBJECTS = queue.o

all: $(OBJECTS)

queue.o: $(SOURCE) $(DEPH)

$(CC) $(INCPATH) -c $(SOURCE)

clean:

rm -f *.o

Un esempio di Makefile, rivisitato

Makefile per main

CC = gcc

HDIR = include

INCPATH = -I$(HDIR)

DEPH = $(HDIR)/queue_types.h \

$(HDIR)/queue_interface.h \

$(HDIR)/stack_types.h \

$(HDIR)/stack_interface.h

OBJECTS = stack/stack.o queue/queue.o

all: main

main: main.o stackdir queuedir

$(CC) -o main main.o $(OBJECTS)

Un esempio di makefile, rivisitato

main.o: main.c $(HDIR)/*.h

$(CC) $(INCPATH) -c main.c

stackdir:

cd stack && make all

queuedir:

cd queue && make all

clean:

rm -f *.o main core

cleanall:

-rm -f *.o main core

cd stack && make clean

cd queue && make clean

Ignora eventuali errori

Solo in caso di successo con cd

Makefile "avanzati"

Le funzionalità di make non si esauriscono qui

Strutture di controllo come statement condizionali e loop

Regole implicite che agiscono come default quando non sono presenti regole esplicite

Semplici funzioni di supporto per trasformare testo

Variabili automatiche che si riferiscono a vari elementi di un Makefile, come target e dipendenze.

Per ulteriori dettagli:

http://www.gnu.org/manual/make/html_mono/make.html

Esempi

Dipendenze automatiche

make è in grado di inferire alcune dipendenze e regole

OBJECTS = main.o kbd.o command.o display.o \

insert.o search.o files.o utils.o

edit : $(OBJECTS)

cc -o edit $(objects)

main.o : defs.h

kbd.o : defs.h command.h

command.o : defs.h command.h

display.o : defs.h buffer.h

insert.o : defs.h buffer.h

search.o : defs.h buffer.h

files.o : defs.h buffer.h command.h

utils.o : defs.h

main.o : defs.h main.c

gcc -c main.c

Esempi

Uno stile alternativo

è possibile "raggruppare" le dipendenze

OBJECTS = main.o kbd.o command.o display.o \

insert.o search.o files.o utils.o

edit : $(OBJECTS)

cc -o edit $(OBJECTS)

$(OBJECTS) : defs.h

kbd.o command.o files.o : command.h

display.o insert.o search.o files.o : buffer.h

Esempi

Regole "statiche"

Specificano il comportamento per una classe di file

gnuplot: input set{eps|gpt}.gpt *.plt, output output.{eps|gpt}

.PHONY: all eps png

all: eps png

eps: quota.eps transfer.eps single.eps

png: quota.png transfer.png single.png

%.eps : %.plt seteps.gpt

gnuplot seteps.gpt $< ; mv output.eps $@

%.png : %.plt seteps.gpt

gnuplot setpng.gpt $< ; mv output.png $@

clean:

rm -f *.eps *.png

Identifica il target

Identifica il prerequisito

Afferma che sono solo azioni

Portabilità

E' sempre un problema!

Diversi tipi di: processori / compilatori / librerie / s.o.

Diverse versioni di: compilatori / librerie / s.o.

Makefile per la portabilità:

scrivere un makefile è facile

come abbiamo visto...

scrivere un buon makefile è difficile

maintanance target (install, dist, distclean, ...)

opzioni utente

gestione delle dipendenze !!!!!

scrivere un makefile portatile è molto difficile

path di librerie, installazioni, etc.

Auto-tools

GNU project "auto-tools"

automake - generazione automatica makefile

autoheader - generazione config headers; per portabilità

autoconf - genera configure script

libtool - gestione librerie statiche/dinamiche

Makefile.am

Configure.in

autoconf

automake

autoheader

Config.h.in

Makefile.in

configure

config.h

Makefile

Esempio di autoconf

programmer@c:~/project> ls -1

my_math.c

my_math.h

sample.c

programmer@c:~/project>:~/tmp> autoscan

programmer@c:~/project>:~/tmp> ls -1

autoscan.log

configure.scan

my_math.c

my_math.h

sample.c

Autoscan crea il file configure.scan,

che ci servirà come template per

creare il file configure.in

Project

autoscan

configure.scan

Esempio di autoconf

programmer@c:~/project> cat configure.scan

# -*- Autoconf -*-

# Process this file with autoconf for a configure script.

AC_PREREQ(2.57)

AC_INIT(FULL-PACKAGE-NAME, VERSION, BUG-REPORT-ADDRESS)

AC_CONFIG_SRCDIR([my_math.c])

AC_CONFIG_HEADER([config.h])

# Checks for programs.

AC_PROG_CC

# Checks for libraries.

# Checks for header files.

# Checks for typedefs, structures, and compiler characteristics.

# Checks for library functions.

AC_OUTPUT

Esempio di autoconf

programmer@c:~/project> cat configure.in

# -*- Autoconf -*-

# Process this file with autoconf for a configure script.

AC_PREREQ(2.57)

AC_INIT(sample, [1.00], [programmer@c.com])

AC_CONFIG_SRCDIR([my_math.h])

#AC_CONFIG_HEADER([config.h])

# Checks for programs.

AC_PROG_CC

# Checks for libraries.

# Checks for header files.

# Checks for typedefs, structures, and compiler characteristics.

# Checks for library functions.

AC_OUTPUT([Makefile])

Questa riga è commentata, perchè

non abbiamo bisogno di un file

config.h (per gestire portabilità)

Esempio di autoconf

programmer@c:~/project> automake -a

configure.in: `AM_INIT_AUTOMAKE' must be used

automake: no implementation of AM_INIT_AUTOMAKE was found,

automake: probably because aclocal.m4 is missing...

automake: You should run aclocal to create this file, then

automake: run automake again.

automake: no `Makefile.am' found or specified

-a aggiunge alcuni file standard al

progetto; altrimenti li richiede

automake

autoscan.in

Makefile.am

Makefile.in

Esempio di autoconf

programmer@c:~/project> cat configure.in

# -*- Autoconf -*-

# Process this file with autoconf for a configure script.

AC_PREREQ(2.57)

AC_INIT(sample, [1.00], [programmer@c.com])

AC_CONFIG_SRCDIR([my_math.h])

AM_INIT_AUTOMAKE

# Checks for programs.

AC_PROG_CC

# Checks for libraries.

# Checks for header files.

# Checks for typedefs, structures, and compiler characteristics.

# Checks for library functions.

AC_OUTPUT([Makefile])

Esempio di autoconf

programmer@c:~/project> cat Makefile.am

noinst_PROGRAMS=sample

sample_SOURCES=sample.c my_math.c

#Ulteriori esempi, commentati:

#bin_PROGRAMS=sample

#include_HEADERS=my_math.h

#lib_LIBRARIES=mymath.a

#mymath_a_SOURCE=my_math.c

Struttura generale: where_WHAT

where - dove mettere i file generati

WHAT - quale file considerare

Esempio di autoconf

programmer@c:~/project> aclocal

programmer@c:~/project> automake -a

automake

Makefile.am: required file `./NEWS' not found

Makefile.am: required file `./README' not found

Makefile.am: required file `./AUTHORS' not found

Makefile.am: required file `./ChangeLog' not found

programmer@c:~/project> touch \
README NEWS AUTHORS ChangeLog

programmer@c:~/project> automake -a

programmer@c:~/project> autoconf

autoconf

autoscan.in

Makefile.in

configure

Esempio di autoconf

programmer@c:~/project> ./configure

checking for a BSD-compatible install.. /usr/bin/install -c

checking whether build environment is sane... yes

checking for gawk... gawk

checking whether make sets $(MAKE)... yes

checking for gcc... gcc

checking for C compiler default output... a.out

checking whether the C compiler works... yes

checking whether we are cross compiling... no

checking for suffix of executables...

checking for suffix of object files... o

checking whether we are using the GNU C compiler... yes

checking whether gcc accepts -g... yes

checking for gcc option to accept ANSI C... none needed

checking for style of include used by make... GNU

checking dependency style of gcc... gcc

configure: creating ./config.status

config.status: creating Makefile

config.status: executing depfiles commands

Autoconf

Note finali

Con questo esempio abbiamo solo "scalfito" le potenzialità di auto*

Maggiori informazioni presso:

google://automake

google://autoheader

google://autoconf

google://libtool

CVS - Concurrent Versioning System

Perchè utilizzare CVS?

tenere traccia delle differenti versioni dei vostri file

mantenere le vecchie versioni

lavorare sugli stessi file da postazioni diverse

lavorare sugli stessi file in cooperazione con altre persone

Per chi è indicato?

Programmatori, scrittori (per chi usa latex)

Open-source

http://www.cvshome.org/.

CVS

Repository

/home/cvs

/home/cvs/proj1

/home/cvs/proj1/a.c,v

/home/cvs/proj1/b.c,v

/home/cvs/proj2

/home/cvs/proj2/a.tex,v

Working dir 1

Working dir 2

CVS

Repository

/home/cvs

/home/cvs/proj1

/home/cvs/proj1/a.c,v

/home/cvs/proj1/b.c,v

/home/cvs/proj2

/home/cvs/proj2/a.tex,v

Working dir 2

/home/pippo/proj1

/home/pippo/proj1/a.c

/home/pippo/proj1/b.c

Working dir 2

checkout

CVS

Repository

/home/cvs

/home/cvs/proj1

/home/cvs/proj1/a.c,v

/home/cvs/proj1/b.c,v

/home/cvs/proj2

/home/cvs/proj2/a.tex,v

Working dir 1

/home/pippo/proj1

/home/pippo/proj1/a.c

/home/pippo/proj1/b.c

/home/pippo/prog2

/home/pippo/prog2/a.tex

Working dir 2

checkout

CVS

Repository

/home/cvs

/home/cvs/proj1

/home/cvs/proj1/a.c,v

/home/cvs/proj1/b.c,v

/home/cvs/proj2

/home/cvs/proj2/a.tex,v

Working dir 1

/home/pippo/proj1

/home/pippo/proj1/a.c

/home/pippo/proj1/b.c

/home/pippo/prog2

/home/pippo/prog2/a.tex

Working dir 2

/home/pluto/proj1

/home/pluto/proj1/a.c

/home/pluto/proj1/b.c

checkout

CVS

Repository

/home/cvs

/home/cvs/proj1

/home/cvs/proj1/a.c,v

/home/cvs/proj1/b.c,v

/home/cvs/proj2

/home/cvs/proj2/a.tex,v

Working dir 1

/home/pippo/proj1

/home/pippo/proj1/a.c

/home/pippo/proj1/b.c

/home/pippo/prog2

/home/pippo/prog2/a.tex

Working dir 2

/home/pluto/proj1

/home/pluto/proj1/a.c

/home/pluto/proj1/b.c

Modificato

CVS

Repository

/home/cvs

/home/cvs/proj1

/home/cvs/proj1/a.c,v

/home/cvs/proj1/b.c,v

/home/cvs/proj2

/home/cvs/proj2/a.tex,v

Working dir 1

/home/pippo/proj1

/home/pippo/proj1/a.c

/home/pippo/proj1/b.c

/home/pippo/prog2

/home/pippo/prog2/a.tex

Working dir 2

/home/pluto/proj1

/home/pluto/proj1/a.c

/home/pluto/proj1/b.c

commit

CVS

Repository

/home/cvs

/home/cvs/proj1

/home/cvs/proj1/a.c,v

/home/cvs/proj1/b.c,v

/home/cvs/proj2

/home/cvs/proj2/a.tex,v

Working dir 1

/home/pippo/proj1

/home/pippo/proj1/a.c

/home/pippo/proj1/b.c

/home/pippo/prog2

/home/pippo/prog2/a.tex

Working dir 2

/home/pluto/proj1

/home/pluto/proj1/a.c

/home/pluto/proj1/b.c

update

CVS

Come funziona CVS?

Orientato ai file / orientato alla linea

Nessun vero e proprio concetto di "progetto"

Lavoro soprattutto con file testuali; può gestire file binari

CVS

Come indicare dove si trova un repository?

Local	path

External	:ext:user@host:path

Password server	:pserver:user@host:path

Da linea di comando:

cvs -d /home/cvs checkout proj

cvs -d :pserver:pippo@cvs.c.com:/home/cvs login
Password: xxxxxxx
cvs checkout

Tramite configurazione, una volta:

export CVSROOT=cvs.c.com:/home/cvs

export CVS_RSH=ssh

CVS - Importare un progetto

pippo@c:~> cd temp

pippo@c:~/temp> ls

my_math.c my_math.h sample.c

pippo@c:~/project> cvs -d /home/cvs import \

-m "First release" sample pippo start

N sample/sample.c

N sample/my_math.c

N sample/my_math.h

No conflicts generated by this import

pippo@c:~/project>

CVS - Importare un progetto

pippo@c:~> cvs -d /home/cvs checkout sample

cvs checkout: Updating sample

U sample/my_math.c

U sample/my_math.h

U sample/sample.c

pippo@c:~/> ls sample

CVS/ my_math.c my_math.h sample.c

pippo@c:~/> ls sample/CVS/

Entries Repository Root

pippo@c:~/> cat sample/CVS/Root

/home/cvs

Da qui in poi, non è più

necessario specificare

-d /home/cvs

CVS - Modificare un progetto

pippo@c:~/sample> cp sample.c sample2.c

pippo@c:~/sample> vi sample.c

pippo@c:~/sample> cvs add sample2.c

cvs add: scheduling file
`sample2.c' for addition

cvs add: use 'cvs commit' to

add this file permanently

#include <stdio.h>

#include "my_math.h"

int main()

{

int a, b, c;

puts("Inserisci tre numeri:");

scanf("%d %d %d", &a, &b, &c);

printf("The average of %d %d %d

is %f\n", a,b,c,average(a,b,c));

}

/* Commento finale */

CVS - Modificare un progetto

pippo@c:~/sample> cvs commit -m "Added file"

cvs commit: Examining .

Checking in sample.c;

/home/montreso/cvs/sample/sample.c,v <-- sample.c

new revision: 1.2; previous revision: 1.1

done

RCS file: /home/montreso/cvs/sample/sample2.c,v

done

Checking in sample2.c;

/home/montreso/cvs/sample/sample2.c,v <-- sample2.c

initial revision: 1.1

done

CVS - Varie ed eventuali

pippo@c:~/sample> cvs update

cvs update: Updating .

Vedere le differenze:

pippo@c:~/sample> cvs diff -r1.1 sample.c

Index: sample.c

RCS file: /home/montreso/cvs/sample/sample.c,v

retrieving revision 1.1

retrieving revision 1.2

diff -r1.1 -r1.2

6c6

< puts("Input three numbers:");

---

> puts("Inserisci tre numeri:");

10d9

< return 0;

11a11

> /* Commento finale */

Conclusioni

Si può fare di più....

Gestione delle versioni

Gestione dei conflitti

Fork di progetti

Gestione sicura dei repository

Etc. Etc.

Per ulteriori informazioni:

google://cvs book

Alberto Montresor

2004-03-16T17:48:24