C su Windows

Installare un compilatore C su Windows può essere semplice quanto complesso, a seconda della procedura scelta. Ci sono due modi principali per farlo, uno più immediato e uno più flessibile. Si può decidere di installare l'IDE Visual Studio e lasciar fare tutto a lui, ma si sarà praticamente obbligati ad usare l'IDE per tutte le interazioni con gli strumenti di sviluppo C, oppure si può andare a installare gli strumenti di sviluppo manualmente senza l'IDE e andare a configurare il nostro editor di codice per fare uso degli strumenti scaricati.

Qui andrò a spiegare i dettagli della seconda procedura, dato che la prima è abbastanza semplice; basta cercare Visual Studio sul web, scaricare l'installer e seguire le istruzioni a schermo.

Potrei partire subito con la lista di cose da fare, ma preferisco prima spiegare un attimo cosa e soprattutto perché stiamo facendo le cose in questo modo. C e C++, a differenza di altri linguaggi, sono solo "standard", documenti che descrivono come C dovrebbe funzionare, ed esistono quindi diverse implementazioni di questi standard, di queste specifiche. Ogni implementazione è divisa in due componenti principali: il compilatore C, che prende il nostro codice sorgente e lo trasforma in codice macchina eseguibile, e la libreria standard C che contiene tutte le funzioni, appunto, "di libreria", come printf(), scanf(), e insomma tutte le funzioni che possiamo includere nel nostro codice con direttive come #include <stdlib.h> eccetera.

Dovrebbe essere ora un po' più chiaro cosa dobbiamo fare, ovvero installare un compilatore e una libreria standard. Su Windows esistono diversi compilatori C, ma solo una libreria standard. Questi fanno parte di "MSVC", che sta per "Microsoft Visual C", che come dice il nome sono parte della suite di Visual Studio. Per installarli è necessario scaricare i "Visual Studio Build Tools", che sono gli stessi strumenti installati con l'IDE ma senza l'IDE. I Build Tools sono sul sito di Visual Studio; basta cercarli sul web e andare in fondo alla pagina (oppure andare direttamente su https://visualstudio.microsoft.com/it/downloads/#build-tools-for-visual-studio-2022). Una volta scaricato ed avviato l'installer ci ritroveremo davanti a una sezione che permette di selezionare tanti workload diversi; a noi servono solo una manciata di componenti, quindi bisogna spostarsi nella sezione "Singoli componenti" selezionare "Funzionalità di base per C++ Build Tools", la versione più recente di MSVC (nel mio caso "MSVC v143 - VS 2022 C++ Build Tools x64/x86 (più recente)"), l'ultimo Windows SDK (nel mio caso "Windows 11 SDK (10.0.22621.0)"), e "Windows Universal C Runtime". Una volta selezionati, possiamo procedere con l'installazione. Il tutto andrà a occupare un paio di GB di spazio di archiviazione.

Screenshot del Visual Studio Installer
Componenti da selezionare dal Visual Studio Installer

A questo punto abbiamo già un ambiente di sviluppo C, benché molto minimale. Possiamo accedere al compilatore aprendo il "Prompt dei comandi di sviluppo nativo x64", e dando il comando cl. Per compilare un file si può dare cl nomefile.c, ed eseguire il .exe generato.

Per come Microsoft ha sviluppato i suoi compilatori, l'uso di questo prompt di comandi di sviluppo, a volte installato in inglese come "x64 Native Tools Command Prompt", è obbligatorio per avere accesso al compilatore C. Quindi ogni volta che vorremo andare a sviluppare codice C o C++ dovremo lanciare prima di tutto uno di quei cmd, e da lì tutti i programmi (ad esempio, se vogliamo usare VSCode, dovremo aprire un developer command prompt e digitare code per lanciarlo direttamente dal cmd di sviluppo).

Con questo set up, però, aprendo un editor di codice come VSCode non ci saranno funzionalità "smart" come segnalazione di errori, warning, suggerimenti, eccetera. Anche qui, abbiamo due opzioni: la prima è specifica a VSCode, di qualità relativamente bassa, (e ho visto che è un po' buggata, ma non avendola usata molto personalmente non ne sono sicuro), mentre la seconda è utilizzabile da pressoché tutti gli editor di codice, è più usata, dà suggerimenti migliori, ma vanno fatti dei passaggi a mano. Ovviamente, sceglieremo la seconda opzione.

La seconda opzione consiste nell'usare clangd, un Language Server basato sul compilatore Clang, che parla il Language Server Protocol (LSP). In pratica è un programmino che parla con un qualsiasi editor di codice che supporta l'LSP e gli dice se e cosa non va bene del codice presente nell'editor. Clang invece è un ottimo compilatore basato sul progetto LLVM, ed è naturale che abbia suggerimenti molto utili.

Per ottenere clangd è sufficiente scaricare l'ultima release pubblicata sulla sua pagina GitHub, disponibile al link https://github.com/clangd/clangd/releases/latest nella sezione "Assets". Una volta scaricato lo zip, possiamo estrarlo in una cartella a piacere e informare Windows della sua esistenza, così che possa essere eseguito dal nostro editor di testo; in altri termini, dobbiamo aggiungere l'eseguibile clangd alla variabile d'ambiente PATH. Per fare ciò ci basta cercare "Variabili d'ambiente" nella casella di ricerca di sistema, aprire la pagina delle impostazioni, e andare a modificare la variabile Path per l'utente corrente. Una volta schiacciato "Modifica", andiamo a schiacciare "Nuovo" nella finestra di dialogo appena aperta e incolliamo il percorso completo alla cartella bin contenuta nello zip di clangd appena estratto. Fatto ciò, possiamo schiacciare "Ok" per chiudere tutte le finestre appena aperte.

Screenshot del dialogo di sistema per modificare le variabili d'ambiente
Variabile Path modificata per clangd

Bene, ora che abbiamo installato tutta la roba che ci serve, possiamo procedere con l'ultimo passaggio: configurare l'editor di codice per fare uso di quello che abbiamo installato. Essendo clangd un server LSP standard è possibile usare un qualsiasi editor di codice molto facilmente, ed io ho scelto VSCode. Per usare clangd su VSCode è sufficiente installare l'estensione "clangd" ed assicurarsi che l'estensione C/C++ di Microsoft non sia installata, in quanto andrebbe in conflitto con clangd. Finita l'installazione, il nostro editor dovrebbe essere in grado di sfruttare tutte le funzionalità intelligenti che potremmo desiderare, come salti a definizione di funzioni, documentazione appoggiando il puntatore su un simbolo, eccetera.

Screenshot di VSCode che mostra la funzionalità di documentazione al passaggio del cursore
Alcune delle funzionalità abilitate da LSP

Nonostante LSP sia fantastico per quanto riguarda la scrittura di codice, non fornisce altre funzionalità che potremmo volere, come il debug in un click di un programma. Quest'area è meno standardizzata, per cui so dirvi solo come configurare il debugger su VSCode. Il miglior debugger per C e C++ integrato in VSCode è, a parer mio, contenuto nell'estensione CodeLLDB. Usa, appunto, il debugger LLDB, parte di LLVM proprio come clangd, e supporta il debug di programmi scritti per Windows e non. Una volta installata l'estensione, rimane un ultima parte di configurazione da fare: bisogna dire a VSCode quale programma vogliamo debuggare, come, e dove. Questa configurazione viene fatta creando un file chiamato launch.json e memorizzato nella cartella .vscode del progetto, contenente più o meno questo:

{
    "version": "0.2.0",
    "configurations": [
        {
            "type": "lldb",
            "expressions": "native",
            "request": "launch",
            "name": "Debug",
            "program": "${workspaceFolder}/mioprogetto.exe"
        }
    ]
}
{
    "version": "0.2.0",
    "configurations": [
        {
            "type": "lldb",
            "expressions": "native",
            "request": "launch",
            "name": "Debug",
            "program": "${workspaceFolder}/mioprogetto.exe"
        }
    ]
}

Quel file dice a VSCode che abbiamo creato una configurazione chiamata "Debug", da eseguire con il debugger "lldb", che deve lanciare il programma "mioprogetto.exe". L'opzione "expressions: native" dice a LLDB che vogliamo usare la classica sintassi C per le espressioni inserite nel pannello "Watch" del debugger, e non una alternativa custom di LLDB.

Bene! Ora ci basta soltanto compilare il nostro file .c dal terminale integrato di VSCode e premere il triangolino verde per far partire il debugger. Per compilare, ad esempio, un file chiamato "main.c", ci è sufficiente usare questo comando:

cl -Zi main.c
cl -Zi main.c

E verrà generato l'eseguibile main.exe, insieme ad altri file intermedi che non ci interessano. L'uso della opzione -Zi è indispensabile per il debug, in quanto senza di questa il nostro codice C verrebbe compilato senza informazioni di debug richieste, appunto, da LLDB per debuggare il programma.

Potremmo fermarci qui, ma c'è un ultima chicca che voglio lasciarvi che renderà l'esperienza di debug un po' più gradevole. Dover ricompilare a mano tutte le volte il programma prima di poterlo eseguire è un po' una rottura, vero? Beh, per me lo è, ma fortunatamente VSCode può automatizzare il tutto con solo un altro paio di righe di configurazione.

Per prima cosa andiamo a creare un nuovo file tasks.json all'interno della cartella .vscode creata in precedenza che dice all'editor come compilare il nostro progetto:

{
    "version": "2.0.0",
    "tasks": [
        {
            "label": "compile",
            "type": "process",
            "command": "cl",
            "args": ["-Zi", "mioprogetto.c"],
            "presentation": {
                "reveal": "silent",
                "focus": false
            }
        }
    ]
}
{
    "version": "2.0.0",
    "tasks": [
        {
            "label": "compile",
            "type": "process",
            "command": "cl",
            "args": ["-Zi", "mioprogetto.c"],
            "presentation": {
                "reveal": "silent",
                "focus": false
            }
        }
    ]
}

Il file definisce un task chiamato "compile" che lancia un "process", "cl", passandogli gli argomenti "-Zi" e "mioprogetto.c". Visto che è un task di bassa importanza, diciamo anche che la sua "presentation" dev'essere "silent", ovvero che l'output della compilazione dev'essere mostrato solo in caso di errore.

Il secondo e ultimo passaggio è quello di dire a VSCode che il task di compilazione dev'essere lanciato appena prima di far partire il debugger, e per fare ciò ci basta solamente aggiungere l'opzione "preLaunchTask": "compile" al file launch.json creato prima, che dovrebbe ora essere così:

{
    "version": "0.2.0",
    "configurations": [
        {
            "type": "lldb",
            "expressions": "native",
            "request": "launch",
            "name": "Debug",
            "program": "${workspaceFolder}/mioprogetto.exe",
            "preLaunchTask": "compile"
        }
    ]
}
{
    "version": "0.2.0",
    "configurations": [
        {
            "type": "lldb",
            "expressions": "native",
            "request": "launch",
            "name": "Debug",
            "program": "${workspaceFolder}/mioprogetto.exe",
            "preLaunchTask": "compile"
        }
    ]
}

E questo è tutto, potete ora giocare col vostro nuovo ambiente di sviluppo per C e C++ sul sistema operativo di casa Microsoft! A parer mio, questo setup è il miglior compromesso tra convenienza, semplicità e funzionalità, per non parlare di quanto sia più leggero rispetto a un IDE completo come Visual Studio. Usare questi strumenti in questo modo vi permette di mettere mano direttamente al livello più basso del processo costruzione dei programmi C, così da avere la possibilità di capire veramente cosa accade dietro alle quinte quando premete quel bel bottone verde per far partire il programma :)