Wasmer - Pour faire tourner du code Python, C, C++, Rust, Go directement dans vos pages web #WebAssembly
Wasmer est un lanceur de WebAssembly vraiment cool puisqu’il permet de faire tourner n’importe quel code dans un conteneur directement sur votre ordinateur, votre navigateur, le cloud…etc
Mais d’abord, petit rappel pour ceux qui auraient vécu dans une grotte ces derniers temps : WebAssembly, c’est ce format binaire conçu pour être exécuté dans les navigateurs web, et qui s’est vite émancipé de ces derniers pour conquérir d’autres territoires. Et c’est là que Wasmer entre en scène !
Tout d’abord Wasmer se vante d’exécuter le WebAssembly à des vitesses proches du natif. Ensuite, comme ce sont des conteneurs, il est plus hermétique que la combinaison de Thomas Pesquet : Pas d’accès aux fichiers, au réseau ou à l’environnement, sauf si vous le demandez explicitement.
Wasmer supporte WASI et Emscripten dès le départ et grâce à son SDK, vous pouvez l’intégrer à peu près partout.
Passons maintenant à la pratique !
Pour voir Wasmer en action, rien de plus simple. Commencez par l’installer avec cette commande :
curl https://get.wasmer.io -sSfL | sh
Maintenant on va faire tourner un classique : cowsay ! Pour cela, rien de plus simple, entrez la commande suivante :
wasmer run cowsay "Coucou"
Et hop, une vache qui parle en ASCII art !
Mais Wasmer ne s’arrête pas là, puisqu’il permet d’utiliser des packages plus costauds, comme Python, C, C++, Rust, Go, QuickJS…etc.
Par exemple, je me suis fait un petit morpion en C, que j’ai compilé en wasm comme ceci :
wasmer run clang/clang --dir=. -- morbak.c -o mobak.wasm
Ce que j’ai ensuite pu lancé comme ceci :
wasmer run mobak.wasm
Et avec son SDK, ça s’implémente très facilement en javascript pour lancer du code de votre choix directement dans le navigateur. Donc on peut imaginer exécuter un peu de python ou de C++ direct dans une page web.
D’abord installez le SDK comme ceci :
npm install -S @wasmer/sdk
Voici un exemple de page web Hello World avec du Python :
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Demo</title>
<style>
body {
font-family: Arial, sans-serif;
line-height: 1.6;
margin: 0;
padding: 20px;
background-color: #f4f4f4;
}
header {
background-color: #333;
color: #fff;
text-align: center;
padding: 1rem;
}
main {
max-width: 800px;
margin: auto;
background-color: #fff;
padding: 20px;
border-radius: 5px;
box-shadow: 0 0 10px rgba(0,0,0,0.1);
}
h1, h2 {
color: #333;
}
footer {
text-align: center;
margin-top: 20px;
padding: 10px;
background-color: #333;
color: #fff;
}
#output {
background-color: #f0f0f0;
padding: 10px;
border-radius: 5px;
margin-top: 20px;
}
</style>
</head>
<body>
<header>
<h1>Mon test !</h1>
</header>
<main>
<h2>Démonstration Python avec Wasmer</h2>
<div id="output">Chargement...</div>
</main>
<footer>
<p>© 2024 Korben</p>
</footer>
<script type="module">
import { init, Wasmer } from 'https://esm.sh/@wasmer/sdk';
async function runPythonDemo() {
try {
console.log("Initialisation de Wasmer...");
await init();
console.log("Wasmer initialisé");
console.log("Chargement du package Python...");
const pkg = await Wasmer.fromRegistry("python/python");
console.log("Package Python chargé");
console.log("Exécution du code Python...");
const instance = await pkg.entrypoint.run({
args: ["-c", "print('Salut les copains !')"],
});
console.log("Attente du résultat...");
const { code, stdout } = await instance.wait();
console.log(`Résultat obtenu: code ${code}, stdout: ${stdout}`);
document.getElementById('output').innerText = `${stdout}`;
} catch (error) {
console.error("Erreur lors de l'exécution:", error);
document.getElementById('output').innerText = `Erreur: ${error.message}`;
}
}
runPythonDemo().catch(error => {
console.error("Erreur non gérée:", error);
document.getElementById('output').innerText = `Erreur non gérée: ${error.message}`;
});
</script>
</body>
</html>
Et j’ai essayé de faire la même chose avec un hello.c que j’ouvre avec le script, que je compile et dont je récupère la sortie dans la page.
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Démonstration C avec Wasmer</title>
<style>
body {
font-family: Arial, sans-serif;
line-height: 1.6;
margin: 0;
padding: 20px;
background-color: #f4f4f4;
}
header {
background-color: #333;
color: #fff;
text-align: center;
padding: 1rem;
}
main {
max-width: 800px;
margin: auto;
background-color: #fff;
padding: 20px;
border-radius: 5px;
box-shadow: 0 0 10px rgba(0,0,0,0.1);
}
h1, h2 {
color: #333;
}
footer {
text-align: center;
margin-top: 20px;
padding: 10px;
background-color: #333;
color: #fff;
}
#output {
background-color: #f0f0f0;
padding: 10px;
border-radius: 5px;
margin-top: 20px;
}
</style>
</head>
<body>
<header>
<h1>Mon test !</h1>
</header>
<main>
<h2>Démonstration C avec Wasmer et Clang</h2>
<div id="output">Chargement...</div>
</main>
<footer>
<p>© 2023 Démonstration C. Tous droits réservés.</p>
</footer>
<script type="module">
import {
init,
Wasmer,
Directory
} from "https://unpkg.com/@wasmer/sdk@latest/dist/index.mjs";
async function runCDemo() {
try {
console.log("Initialisation de Wasmer...");
await init();
console.log("Wasmer initialisé");
console.log("Chargement de Clang...");
const clang = await Wasmer.fromRegistry("clang/clang");
console.log("Clang chargé");
console.log("Chargement du fichier hello.c...");
const response = await fetch('/hello.c');
const helloCode = await response.text();
console.log("Fichier hello.c chargé");
console.log("Création du projet...");
const project = new Directory();
await project.writeFile("hello.c", helloCode);
console.log("Fichier C créé");
console.log("Compilation du code C...");
let instance = await clang.entrypoint.run({
args: ["/project/hello.c", "-o", "/project/hello.wasm"],
mount: { "/project": project },
});
const output = await instance.wait();
if (!output.ok) {
throw new Error(`Échec de la compilation. Code de sortie : ${output.code}`);
}
console.log("Compilation réussie");
console.log("Exécution du programme compilé...");
let wasm = await project.readFile("hello.wasm");
const hello = await Wasmer.fromFile(wasm);
const result = await hello.entrypoint.run();
const outputHello = await result.wait();
console.log("Résultat obtenu:", outputHello.stdout);
document.getElementById('output').innerText = outputHello.stdout;
} catch (error) {
console.error("Erreur lors de l'exécution:", error);
document.getElementById('output').innerText = `Erreur: ${error.message}`;
}
}
runCDemo().catch(error => {
console.error("Erreur non gérée:", error);
document.getElementById('output').innerText = `Erreur non gérée: ${error.message}`;
});
</script>
</body>
</html>
Et le code en C :
#include <stdio.h>
int main() {
const char *phrases[] = {
"Putain, le monde est beau !",
"Merde alors, bonjour à tous !",
"Bordel, c'est une belle journée !",
"Salut, bande de connards !",
"Foutredieu, le C c'est génial !"
};
int num_phrases = sizeof(phrases) / sizeof(phrases[0]);
// Utilisons un index fixe au lieu d'un nombre aléatoire
int index = 2; // Choisissons arbitrairement la troisième phrase
printf("%s\n", phrases[index]);
return 0;
}
Bien que Wasmer soit très puissant, il est principalement utilisé pour exécuter des petits modules WebAssembly spécifiques plutôt que des applications entières. Cependant, les possibilités sont énormes, et avec l’évolution constante de la technologie, qui sait jusqu’où cela pourra aller ?
En tout cas, j’ai trouvé ça très cool !