Newsgroup: it.comp.lang.c
Titolo: Compilare uno stesso sorgente con diverse configurazioni

Ho un progetto in C formato da un certo numero di file .c. Dovrei
poter compilare questo programma in vario modo a seconda della
configurazione scelta.
Con la configurazione posso scegliere, ad esempio, di compilare alcuni
moduli che offrono alcune funzionalità in più, oppure di cambiare
l'interfaccia grafica e cose simili. Sono su embedded, quindi non
posso permettermi librerie dinamiche o funzionalità avanzate del
sistema operativo.
Sono indeciso sul modo di procedere tra la "classica" compilazione
condizionale (preprocessore C) e le funzionalità dell'ambiente di
sviluppo.
L'ambiente di sviluppo che utilizzo, così come tanti altri, permettono
di associare al progetto diverse configurazioni (tipo le build di
CodeBlocks) dove è possibile selezionare i file del progetto da
compilare e quali no e modificare file per file le opzioni di
compilazione.
Con la compilazione condizionale, creerei un file config.h dove andrei
ad inserire tante macro per il preprocessore:
  #define CONFIG_FOO
  #define CONFIG_BAR
  ...
In tutti i file da compilare solo per alcune configurazioni farei
qualcosa del genere (per esempio in foo.c):
  #include "config.h"
  #ifdef CONFIG_FOO
  ..
  #endif
Avrei tanti config.h a seconda della configurazione che voglio
compilare.
Avete preferenze oppure mi suggerite qualcosa di più elegante? Io
opterei per il secondo approccio perchè mi sembra più portabile e
standard, anche se un po' più sporco a livello di codice (mi devo
ricordare sempre di includere quel config.h in testa ad ogni sorgente
incriminato). Inoltre, utilizzando l'approccio delle configurazioni
dell'ambiente di sviluppo, dovrei creare tutte le combinazioni
possibili: se ho un certo numero di opzioni di configurazioni è meglio
trattarle come flag singoli da abilitare o meno.
Cosa utilizza il kernel di linux, un classico programma C altamente
personalizzabile? Come fa per abilitare o meno la compilazione di un
driver, per esempio?

 driver, per esempio?
Eh, scarica i sorgenti e vedi :-)

 Eh, scarica i sorgenti e vedi :-)
Non sarà facile :-)

 driver, per esempio?
In fase di configurazione si può scegliere se integrare i drivers e
quindi precaricarli nel kernel o compilarli in moduli separati carica-
bili dinamicamente. Questo lo si indica nel makefile attraverso opzioni
(sotto forma di macro) a 3 stati: "yes", "no", "module". La via più
semplice è utilizzare un'interfaccia grafica, ncurses con il comando
"make menuconfig", oppure gtk con il comando "make gconfig", che ti
permetteranno di selezionare le opzioni che preferisci.
-- 
Non puoi insegnare qualcosa ad un uomo. You cannot teach a man anything.
Lo puoi solo aiutare   --  Galileo  Galilei  --   You can only help him
a scoprirla dentro di sé.                        discover it in himself.
Vincenzo Mercuri

 permetteranno di selezionare le opzioni che preferisci.
Quindi alla fine di tutto (eviterei ovviamente le ncurses o le gtk per
fare
la scelta delle opzioni) si riduce ad un makefile dove è definito un
listone
di macro il cui valore è "yes", "no", "module" che viene passato ad
OGNI
file durante il processo di compilazione?
Quindi mi aspetto che un sorgente del kernel inizi con qualcosa del
tipo:
  #if CONFIG_FOO=yes
  #endif
Oppure quel/quei file associati ad una opzione non selezionata non
vengono
neanche dati in pasto al compilatore grazie al Makefile che si accorge
di ciò?

 di ci�?
Non penso sia cos� semplice, e ad essere sincero non sono mai andato a
spulciarmi i Makefile del kernel. Sono uno di quelli che crede fermamente
nella bellezza delle cose utili, tipo GUI... mouse... ecc..., e sono uno
di quelli che usa Ubuntu-desktop perch� ritiene che un sistema operativo
debba aiutare l'utente e non metterlo in difficolt� ogni volta che
accende il pc :-)
-- 
Non puoi insegnare qualcosa ad un uomo. You cannot teach a man anything.
Lo puoi solo aiutare   --  Galileo  Galilei  --   You can only help him
a scoprirla dentro di s�.                        discover it in himself.
Vincenzo Mercuri

 accende il pc :-)
Sono d'accordo :-)
Comunque non penso che durante la compilazione del kernel vengano
passati
al gcc TUTTI i file del kernel, anche dei moduli non selezionati.
Secondo me
c'è un meccanismo nel Makefile che non fa neanche arrivare al gcc i
file non
selezionati... altrimenti sai che perdita di tempo inutile!

 (anzichè altri metodi?) considerando l'architettura già fissata.
Non è proprio una regola generale, ma quando si tratta di "scalare
sull'hardware" esistono i cosiddetti "autotools" e il compilatore in
primis è d'aiuto (ammesso che il sorgente sia frutto di un design
portabile). Quando devi "scalare sul software" è necessario farlo
via codice sorgente. Naturalmente nel caso di embedded non puoi far leva
tanto sui tools esterni quanto sulle tue conoscenze di quel particolare
processore. "Configurare" il tuo sorgente puo' significare tante cose.
Le mie considerazioni sono necessariamente generiche, perchè in assenza
di altri dettagli non posso ovviamente rispondere di conseguenza.
In ogni caso, una profonda conoscenza del sistema target e del tuo
ambiente di sviluppo è vitale per qualsiasi cosa tu debba fare. So che
quanto detto è ovvio, ma posso assicurare che è un tema ricorrente in
questo come in altri NG.
-- 
Non puoi insegnare qualcosa ad un uomo. You cannot teach a man anything.
Lo puoi solo aiutare   --  Galileo  Galilei  --   You can only help him
a scoprirla dentro di sé.                        discover it in himself.
Vincenzo Mercuri

  questo come in altri NG.
Quanto è vero, Santo Cielo, quanto è vero. Sto impazzendo
con un microcontrollore e il fatto che non ho mai voluto
studiare a fondo il livello dell'hardware. *Quasi* *tutti*
i costrutti che credevo più "smart" e intelligenti si sono
dimostrati rozzi e pesanti per il codice generato. E'
tutto controintuitivo, ma è l'intuizione dovuta all'ignoranza.
Se penso che con questi aggeggini si fanno le FFT, la
decodifica MP3, DSP e tante cose, tutto a numeri interi,
non posso che fare tanto di cappello ai geni che sono
riusciti a implementare cose di questo genere con vincoli
così tanto grandi.
ciao!
Apx.
-- 
Vorrei morire dormendo come mio nonno
e non urlando come i suoi passeggeri.

 di altri dettagli non posso ovviamente rispondere di conseguenza.
Beh, ti faccio un tipico esempio. Ho un bus seriale su cui comunicano
un
certo numero e tipo di nodi con un loro protocollo. A seconda della
macchina
vorrei poter scegliere di compilare il supporto per un certo nodo,
piuttosto
che per un altro.
Altro esempio: lo stesso software dovrà girare su due schede a
microcontrollore
diverse. Una con un display grafico (dove posso avere una struttura a
menu, dei
semplici disegni, ecc) ed una con display alfanumerico 2x16.
Il cuore in entrambi i casi (il bus ed altre funzionalità) rimane lo
stesso, mentre
il contorno cambia.
Comunque ho approfondito il discorso kernel di Linux e il meccanismo
della
configurazione si basa sui "kbuild Makefile".
Si parte dal .config che contiene tutte le macro:
  CONFIG_FOO=y
  CONFIG_BAR=n
Il toplevel Makefile richiama ricorsivamente tutti i Makefile che
hanno questa
semplice sintassi:
  obj-$(CONFIG_FOO) += foo.o
Alla fine si avranno due lunghe liste: obj-y e obj-n (in realtà ci
sarà anche obj-m).
Verranno compilati solo i file obj-y e non gli obj-n.
Quindi il codice rimane pulito (niente #ifdef CONFIG_FOO o #include
"config.h"
in testa al .c) e i file non selezionati non passano neanche per il
compilatore
(cosa che farebbe solo perdere tempo).
Naturalmente questo è il funzionamento ridotto all'osso, poi ci sono
altre possibilità
che non ho approfondito.
Adattare questo meccanismo al mio caso, lasciando tool esterni, non è
facile
non avendo, purtroppo, un Makefile.

 non avendo, purtroppo, un Makefile.
Io non avevo dubbi che in tutto il codice sorgente del kernel di Linux
(diversi milioni di linee) ci fosse più di un Makefile.
Però ti pregherei di postare domande diverse come post diversi, altri-
menti rischiamo di rendere la vita difficile a chi legge, e non solo.
Un post, un argomento.


 così tanto grandi.
[...]
Condivido, ma non deve impressionarti l'implementazione in sè,
è l'invenzione degli algoritmi, con carta e penna, che ha fatto
la rivoluzione. ;-)


 non solo. Un post, un argomento.
In realtà il mio argomento era sempre lo stesso: come gestire la 
configurazione di un programma C. Uno degli esempi era il kernel di linux.

 configurazione di un programma C. Uno degli esempi era il kernel di linux.
Si, quello che hanno in comune � la parola "configurazione", che ripeto,
nel modo in cui � stata usata puo' veramente significare tutto.
Dentro c'era l'emdedded, la compilazione di eventuali interfacce grafiche...
E il tutto ovviamente senza l'ombra di un codice.
Sai cosa non mi � piaciuto sulla questione "Makefile" del kernel?
Che tu hai fatto una domanda iniziale. Ottimo. Qualcuno (non io) ti ha
consigliato vivamente di studiare i sorgenti del kernel (consiglio ottimo).
Ora, nel frattempo che qualcuno (io) cercava di darti una spiegazione,
pur grossolana, su come si possono configurare i drivers, tu te ne esci
con una spiegazione sulla struttura di Makefile nel kernel per
configurare i drivers, anche se errata.
La mia domanda �: ma avevi bisogno di fare una domanda se la risposta
si trova nel semplice "studio"? Una cosa � domandare su questioni non
capite, un'altra � domandare cose che non si conoscono perch� non si
ha voglia di studiarle e poi controbattere chi ti fornisce delle spiega-
zioni valide sulla base di una studiacchiata estemporanea.
Come se io chiedessi: ma Pechino in che parte del mondo si trova?
Uno mi risponde, in Asia. Poi tu ribatti e dici, no vedi che si trova
in Giappone. L'altro si stupisce e dice: azz, allora sapevi dov'era!
Peccato che � sbagliato...
[la scelta di Pechino, Asia e Giappone l'ho calibrata e adattata al meglio]
Quello che voglio dire, � che tu non penso abbia voglia di verificare
quanto le persone ti dicano, ma hai gi� delle risposte alle tue stesse
domande, e non accetti che le risposte che ti sono date non coincidano
con quelle che gi� ti sei formulato. E non � la prima volta che succede.
A me questo non piace, davvero, ci vuole un po' di onest� anche nel
fare domande.
-- 
Non puoi insegnare qualcosa ad un uomo. You cannot teach a man anything.
Lo puoi solo aiutare   --  Galileo  Galilei  --   You can only help him
a scoprirla dentro di s�.                        discover it in himself.
Vincenzo Mercuri

pozz wrote:
_PS_: e perchč mai un codice in C viene "sporcato" dalle direttive del
preprocessore? questo inevitabilmente significa che il 99% del codice
esistente in C č "immondizia" ?


 fare domande.
Bene, mi dai anche del disonesto.
Ti ripeto che, sinceramente, mi sembrano accuse completamente infondate. 
Comunque sia ti chiedo scusa se ho mostrato il comportamento che tu dici.

 esistente in C è "immondizia" ?
Sono stato sicuramente inesatto. Non volevo dire che qualsiasi direttiva 
del preprocessore fosse il male oscuro.
Mi riferivo ad utilizzare il preprocessore, in particolare alle righe
   #include "config.h"
   #ifdef CONFIG_FOO
   ...
   #endif
all'inizio e alla fine di un sorgente C al solo scopo di compilare il 
programma con una diversa configurazione.
Ed infatti, scusami se ci ritorno, il kernel di linux non fa questo, ma 
utilizza un sistema più complicato di Makefile per evitare di compilare 
i file non richiesti.
Sistema più complicato che, ammetto, non posso utilizzare perchè sarebbe 
sovrabbondante per i miei scopi.
Comunque alla fine penso che la soluzione del preprocessore rimanga 
l'unica semplice per ottenere quello che voglio, in quanto non sembrano 
essercene altre.

 Comunque sia ti chiedo scusa se ho mostrato il comportamento che tu dici.
Dai, ammetto di essere stato duro con te. Comunque, la risposta alla
tua domanda originale è molto difficile da dare, dipende anche da com'è
strutturato il tuo codice. Purtroppo non è solo una domanda sul design,
ci sono di mezzo troppi particolari che non conosciamo. Io direi di
tentare la strada con la compilazione condizionale (preprocessore),
secondo me è la maniera più "portabile". Come ti ha detto Andrea, pas-
sare macro on -D puo' essere una soluzione. Questo se devi rendere
il tuo codice disponibile per altri utenti e altre macchine. Ma se hai
intenzione di essere il solo utente del tuo software e conosci bene le
macchine di cui disponi, direi di scrivere il codice per l'una e per
l'altra macchina come fossero due programmi diversi. Copia e incolla
le parti in comune (per il bus e le altre funzionalità). Si è banale,
quasi vergognoso per un programmatore, ma se ti fa risparmiare tempo
per risolvere il _tuo_  problema allora è una buona soluzione.
Dipende come vedi anche da a chi è destinato il software, non solo a
quale architettura.
Rendere un codice portabile puo' a volte essere molto complicato. Pensa
per esempio alla pletora di funzionalità POSIX e SuS che ci sono in giro.
Per non parlare poi delle estensioni di BSD e Linux a cui i rispettivi
utenti non vorrebbero mai rinunciare. Fai la scelta che ti sembra meno
onerosa in termini di tempo.
_PS_: non vorrei mai che qualcuno si scusasse con me, se ti aggredisco
aggrediscimi a botte di codice :-)
-- 
Non puoi insegnare qualcosa ad un uomo. You cannot teach a man anything.
Lo puoi solo aiutare   --  Galileo  Galilei  --   You can only help him
a scoprirla dentro di sé.                        discover it in himself.
Vincenzo Mercuri

 compilazione.
Che ambiente e'?
Io con il Code Composer della Texas ho inserito degli "#if" "#else" 
"#endif".
Dall'ambiente ho poi creato gli n progetti che mi servono con 
all'interno le direttive per il preprocessore.
Poi con un paio di semplici script .bat richiamo il programma timake.exe 
che mi compila tutte le versioni. Il secondo bat rinomina i file creati 
aggiungendo la versione e la data di creazione.
Mi pare strano che il tuo ide non si appoggi ad un compilatore /linker a 
linea di comando.
Ciao
G.

 linea di comando.
In effetti anche il mio ide si appoggia ad un compilater/linker a linea 
di comando, da qui anche a me č venuta l'idea di personalizzare la 
compilazione mediante script appositi.
Giusto per fare l'avvocato del diavolo, come al solito :-), il sistema 
ha solo una controindicazione per me. Il risultato (l'eseguibile) 
generato dalla compilazione con uno script/Makefile sarebbe diverso da 
quello generato con l'IDE. Questo potrebbe pure non essere un problema 
generale, ma per me lo diventa visto che la regola generale del nostro 
gruppo di programmatori č utilizzare l'IDE per creare l'eseguibile.
Credo che l'unica soluzione sia effettivamente l'utilizzo delle macro e 
del preprocessore e affidarsi alla solita compilazione tramite IDE. 
Macro che possono essere inserite in un config.h da includere sempre, 
oppure direttamente passate come argomenti del compilatore (-D).
P.S.: Sai perchč uno script genererebbe un eseguibile diverso da quello 
generato con l'IDE? Perchč quest'ultimo linka i file oggetti nello 
stesso ordine con cui sono stati aggiunti al progetto (quindi non in 
ordine alfabetico). Questo ordine č salvato in un file di progetto 
dell'IDE. Lo script che potrei fare non potrebbe conoscere questo ordine 
e quindi linkerebbe in modo diverso.

 aggrediscimi a botte di codice :-)
Se c'è da scusarsi, lo faccio volentieri :-)

 e quindi linkerebbe in modo diverso.
Ciao,
Timake utilizza i file creati dalla ide Code Composer per generare 
l'eseguibile, quindi nel mio caso non c'e' nessuna differenza nel 
lavorare in un modo o nell'altro.
Ciao
Giuseppe