08-GestioneProgetti
Transcript
08-GestioneProgetti
Gestione progetti software Michelangelo Diligenti Ingegneria Informatica e dell'Informazione [email protected] Sommario ● ● Cosa fare quando il progetto software diviene grande? Dividere bene le classi in .cc e .h Makefiles per gestire la compilazione Gestione di progetti in un gruppo di programmatori svn e programmazione condivisa .cc e .h ● Importante separare l'interfaccia ed implementazione Interfaccia in .h, ricordatevi la seguente struttura del .h per evitare che il file possa venire incluso più volte #ifndef NOME_FILE_H #define NOME_FILE_H // codice … #endif // NOME_FILE_H Implementazione in .cc, VANTAGGI Interfaccia chiara e leggibile Tempi di compilazione ridotti Forward Declarations ● ● ● ● Non includere un file da un file .h se possibile evitarlo con forward declaration Forward declaration dichiara l'esistenza di un tipo senza specificarne le caratteristiche Velocizza la compilazione ed aiuta ad evitare dipendenze cicliche Usare forward declaration quando non serve sapere la dimensione del tipo per compilare Possibile ogni volta che nel .h ci si riferisce al tipo per indirizzo o riferimento Forward Declaration: esempio class RefParam; class ValParam; class PtrParam; class User{ PtrParam* dato; // ok non devo sapere dimensione per definire puntatore public: void Do(const RefParam &inParam ); // ok, riferimento void Do(ValParam inParam); // strano ma ok, purché senza implementazione, // ma solo definizione del metodo void Do(PtrParam* inParam); // ok, puntatore void Do(ValParam& inParam); // ok, riferimento }; Forward Declaration: esempio class ValMember; class PtrMember; class User{ public: User(); ValMember valmember; // no! serve sapere dimensione PtrMember* ptrmember; // ok! non serve dimensione }; Makefiles ● ● Dato un progetto formato da un insieme di sorgenti Definiscono come la compilazione deve avvenire per creare i binari ● Definiscono un insieme di direttive di compilazione ● Permettono di definire le dipendenze tra sorgenti Solo cosa cambia e ciò che dipende da quello che è cambiato va ricompilato Velocizzazione dei tempi di compilazione durante lo sviluppo Makefiles ● Strumenti di sviluppo comuni come Eclipse o Netbeans costruiscono Makefile per noi Tuttavia bene capire il funzionamento dei Makefile Quando si crea pacchetto software non opportuno assumere che lo strumento di sviluppo sia installato Si esporta solo il codice ed il makefile (con eventuali librerie) Il processo di istallazione usa il makefile per ricompilare il codice sulla macchina target Makefiles: invocazione ● Come lo si invoca Per costruire i binari di default usando un makefile chiamato “makefile” nella directory corrente Per costruire il binario programma usando il makefile di default digitare: “make” digitare: “make programma” Per costruire il binario programma usando il makefile “makefile” digitare: “make -f makefile programma” Makefiles: struttura ● Righe che iniziano per # sono commenti ● Makefile è una sequenza di comandi nella forma: ● target: lista_dipendenze_di_target tab comando Target: nome del risultato del comando, in genere è od un binario od un file oggetto .o lista_dipendenze_di_target: i target da cui target dipende Target costruito se una delle dipendenze è cambiata comando: regola da eseguire usando il terminale Eseguito per creare il target Makefiles: esempio # Compiling source files (uno per ogni file .cc) main.o : main.cc mydefs.h g++ -c main.cc file1.o : file1.cc file1.h mydefs.h g++ -c file1.cc file2.o : file2.cc command.h g++ -c file2.cc display.o : display.cc command.h g++ -c display.cc se file1.cc o file1.h o mydef.h sono cambiati, genera target file1.o eseguendo il comando “g++ -c file1.cc” # Alla fine, linking file oggetto programma : main.o file1.o file2.o display.o g++ -o programma main.o file1.o file2.o display.o clean : rm -f programma *.o genera target clean: ripulisce il disco e reinizia la compilazione da zero: esegue il comando “rm -f programma *.o” Makefiles: esempio più binari main.o : main.cc mydefs.h g++ -c main.cc main1.o : main1.cc mydefs.h g++ -c main1.cc file1.o : file1.cc file1.h mydefs.h g++ -c file1.cc display.o : display.cc command.h g++ -c display.cc programma : main.o file1.o g++ -o programma main.o file1.o display.o ogni binario ha linking separato programma1 : main1.o file1.o file2.o display.o g++ -o programma1 main1.o file1.o file2.o display.o all: programma programma1 clean : rm -f programma programma1 *.o target all costruisce tutti i binari, si chiama con “make all” Make: esercizio ● Costruire il makefile per la compilazione di 2-3 eseguibili a vostra scelta, meglio se su piu' sorgenti, costruiti durante il corso Phony targets ● ● Sono targets che specificano azioni, non creano targets e non hanno dipendenze Usati per ripulire lo stato della compilazione o creare archivi ripulisce i file generati dal makefile stesso, si chiama con: make clean clean: rm -f *.o lista_binary tgz: crea archivio del pacchetto dei sorgenti, si chiama con: make tgz tar cvfz pacchetto.tar.gz *.cc *.h Makefile Targets e dipendenze ● Le dipendenze possono essere aggiunte in modo incrementale – Possibile aggiungere targets che specificano solo dipendenze ma non azioni, vedremo come usarlo – Esempio #Regola di compilazione, per ora .o dipende solo da .cc corrispondente file1.o : file1.cc g++ -c file1.cc # Regola che specifica dipendenze aggiuntive # gfile1.o ora dipende sia da file1.cc che da file1.h e mydef.h file1.o: file1.h mydef.h Makefiles: variabili ● Possibile definire variabili NOME_VAR = VALORE Per accedere al valore della variabile (equivalenti): ${NOME_VAR} o $(NOME_VAR) Makefiles: esempio con variabili LD=g++ # linker CC=g++ # compilatore programma : main.o file1.o file2.o display.o ${LD} -o prog1 main.o file1.o file2.o display.o # Compiling source files main.o : main.cc mydefs.h ${CC} -c main.cc file1.o : file1.cc mydefs.h ${CC} -c file1.cc file2.o : file2.cc command.h ${CC} -c file2.cc display.o : display.cc command.h ${CC} -c display.cc ... Variabili speciali ● $@: il target per la regola corrente ● $< : il nome della prima dipendenza ● ● ● $?: il nome di tutte le dipendenze che sono cambiate $^: il nome di tutte le dipendenze separate da uno spazio $+ : come $^ ma eventuali dipendenze ripetute sono rimosse Regole implicite ● ● Usando la seguente sintassi, possibile specificare target generici Per fornire una regola generale di come passare da ogni file .cc al corrispondente .o %.o:%.cc ${CC} -c $< Makefile: esempio generale CC=g++ CFLAGS=-O6 -Wall LDFLAGS=-O6 objs = file1.o file2.o display.o # Linking, più binari possono essere generati programma : programma.o $(objs) ${CC} ${LDFLAGS} -o $@ $+ programma1 : programma1.o $(objs) ${CC} ${LDFLAGS} -o $@ $+ # Compilazione %.o:%.cc ${CC} ${CFLAGS} -c $< # Aggiunta dipendenze ulteriori programma.o : mydefs.h programma.h programma1.o : mydefs.h ... all: programma programma1 clean: rm -f programma programma1 $(objs) primo binario, costruito se: make programma secondo binario, costruito se make programma1 make all costruisce tutto Make depend ● ● Permette di non aggiungere le dipendenze a mano Analizza i files, esegue il preprocessore e controlla #include Infine aggiunge al makefile le dipendenze Si esegue con comando (cerca file Makefile): makedepend *.cc *.h Oppure makedepend -f makefile_specifico Make depend ● Spesso si mette direttamente come phony target nel makefile SRCS = file1.cc file2.cc … CFLAGS = … depend: makedepend -- $(CFLAGS) -- $(SRCS) oppure depend: makedepend -- $(CFLAGS) -- *.cc Modifica Makefile aggiungendo una linea # DO NOT DELETE THIS LINE -- make depend depends on it. Dopo questa linea vengono messe le dipendenze Make: esercizio ● Modificare il makefile costruito usando il template generale precedentemente fornito Programmazione condivisa ● Source Managment Control (SMC) Software che permette di sviluppare software in team Tracciano i cambiamenti e chi li ha fatti Permettono di tornare indietro nel tempo con il codice Agevolano la risoluzione di cambiamenti conflittuali allo stesso codice FONDAMENTALI per lo sviluppo in azienda Programmazione condivisa ● Source Managment Control (SMC) CVS: il primo ed il classico oggi un po antiquato ma ha imposto un modello che tutti conoscono Subversion (SVN), quello che studieremo Utilizzo simile al CVS Ma riscritto completamente Perforce: ottimo ma non gratuito e molti altri SMC centrealizzati e non ● SMC centralizzati hanno un repository centralizzato Ogni client ha sua copia decentralizzata Un server centrale tiene la copia di riferimento (master) SVN è centralizzato Working Copy ● Repository/Master Working Copy Working Copy SMC decentralizati non hanno repository centrale Spesso basati su Peer-to-peer, i client tengono le copie ed il repository in modo distribuito SVN, client e server ● SVN server tiene il repository master Riferimento per tutte le altre copie Le working copy sono inizializzate dal master Cambiamenti su working copy sottomessi al master Server contiene database ed altri tools come Web server per controllare lo stato del master SSH per sottomettere sul o leggere i dati dal master Repository/Master Clients Working Copy Working Copy Server Working Copy SVN: architettura Libreria per accesso e modifica working copy Accesso da linea di comando o interfaccia grafica Files che formano la working copy Metodi per accesso alla working copy (http, apache,...) Libreria per accesso e modifica del master Database che Memorizza Repository Working Copy Files SVN e comandi ● Vedremo la gestione da linea di comando ● Effettuata tramite il binario svn ed svnadmin ● Possibile anche gestire via Web Necessario installare i pacchetti corrispondenti Help generico digitando svn help ● Per avere help specifico di un comando svn help comando Esempio “svn help import” Tutti i comandi sono relativi ad una directory di lavoro SVN creazione del master # Creo directory sul server mkdir -p /var/svn/repos # creazione del master repository chiamato pps sudo svnadmin create /var/svn/repos/pps SVN importazione codice ● ● Per iniziare un repository è possibile importare un'intera directory con tutto il codice in essa contenuto Tale comando viene chiamato import Necessario solo una volta e nel caso di progetti già avviati Se il progetto parte da zero tale comando non è necessario Vedremo come aggiungere singoli files al repository SVN importazione codice # Creo directory di lavoro sul client e ci copio codice mkdir $HOME/Documents/Corsi/ProgrammazioneProgettazioneSoftware2011/src/svn/pps # Vado su directory di lavoro, da ora in poi assumerò di essere qui cd $HOME/Documents/Corsi/ProgrammazioneProgettazioneSoftware2011/src/svn cp $CODICE/costruttori/* pps # Importo il codice nel repository pps svn import pps svn+ssh://michi@localhost//var/svn/repos/pps -m 'inital import' Binario che implementa il client Operazione di importazione Opzionale: descrizione dell'operazione Utente che accede Directory che viene importata Metodo di accesso URL del master Path di accesso al master SVN creazione working copies ● ● In qualsiasi momento è possibile creare una nuova working copy Comando detto checkout # Creo una nuova working copy a partire dal repository svn checkout svn+ssh://michi@localhost//var/svn/repos/pps pps1 URL Repository che viene preso Path dove viene fatto checkout rispetto alla directory corrente SVN aggiunta files ● ● ● ● Aggiunta files sulla working copy Attenzione non basta avere file nella directory che si è checked out perché siano tracciati Solo i nuovi file aggiunti esplicitamente saranno tracciati Tale comando di aggiunta è detto add # Aggiungo file da tracciare (in path su cui è stato fatto un checkout) svn add path/file SVN edit files ● I files nella working copy che sono tracciati possono essere editati da un normale editor SVN rimozione files ● Rimozione files sulla working copy ● Comando detto delete o rm # Rimuovo file da tracciare (in path su cui è stato fatto un checkout) svn delete path/file SVN sincronizzazione ● ● Sincronizzazione da client a master per sottomettere i cambiamenti fatti viene detto commit Sottomette i cambimenti relativi all'aggiunta, rimozione e cambiamenti sui files tracciati svn commit -m “commento opzionale su cosa si sottomette” SVN sincronizzazione ● ● Sincronizzazione da master a client per ricevere i cambiamenti fatti su altre working copy viene detta sync o update Sincronizza i cambimenti relativi all'aggiunta, rimozione e cambiamenti sui files fatti da altri client svn update SVN iterazione di lavoro ● A parte le inizializzazioni, le iterazioni di lavoro con SVN sono semplici Il 95% del tempo si digitano i seguenti comandi svn update svn commit -m “commento” ● Se i cambiamenti sono su stesso file da parte di diversi client, svn cercherà di fare l'unione (merge) SVN e conflitti ● ● ● Tutto è semplice tranne in un caso Cosa succede se due persone modificano lo stesso file nello stesso punto? In tal caso SVN non permette a chi fa il commit per secondo di effettuarlo Siamo in presenza di uno o più conflitti Possibile vedere files con conflitti con comando svn status Mostra files con estensione .mine e .rNumero Necessario effettuare risoluzione dei conflitti SVN e risoluzione conflitti Il file con conflitto (riportato da svn status) ha linee con conflitti evidenziate con dei markers speciali <<<<<<< .mine ● CODICE INSERITO SULLA WORKING COPY =========== CODICE INSERITO SUL MASTER DOPO NOSTRO UPDATE >>>>>>> .r23 ● Markers evidenziano codice introdotto in working copy (.mine) e dall'altro client (.r$VERSIONE) Markers devono essere eliminati, finché non si è risolto il conflitto: il codice compila e funziona correttamente SVN e risoluzione conflitti ● ● Dopo aver editato il file, risolto il conflitto ed eliminato i markers, il conflitto è risolto Per dire a SVN che il conflitto è risolto svn resolved path/file ● Adesso possibile effettuare il commit Se ci sono conflitti, svn blocca i commit SVN e backups ● SVN usa un semplice database open-source chiamato Berkley DB Possibile fare backups del database con il comando svnadmin dump /var/svn/repos/pps > backup_file Consigliabile effettuare backups attraverso cron ogni poche ore/minuti Possibile ricaricare un backup precedente sul repository master con il comando svnadmin load /var/svn/repos/pps < backup_file SVN ed export ● Se volete rilasciare il codice a terze parti (non sviluppatori) Potete fargli fare un checkout ma questo è pesante visto che SVN si porta dietro meta-data Meglio semplicemente dargli i files Possibile esportare l'albero di files (tutti i files ricorsivamente nel repository) con il comando export svn export svn+ssh://michi@localhost//var/svn/repos/pps output Repository esportato Directory dove si esporta SVN: uso avanzato ● ● La trattazione fatta sulle slide di SVN copre solo una piccola parte di cosa può fare SVN Cose rilevanti non trattate Branching: quando si vuole avere più copie del codice Esempio per la versione stabile e quella sperimentale dello stesso Script automatici da eseguire quando si effettuano operazioni Esempio: per controllare che il codice rispetti un certo stile o compili correttamente quando viene fatto il commit SVN: uso avanzato ● Manuale completo ed ufficiale è ben 405 pagne! ● Scaricabile gratuitamente da http://svnbook.red-bean.com/ ● Copre tutti gli utilizzi più avanzati anche se è dispersivo