Kompileerimine ja käivitamine: shell, Python, C, C++, Go, Rust, Java
Selles peatükis võrdleme lühidalt, kuidas eri keelte programmid sünnivad ja käivituvad.
Loogika
See peatükk ei ole mõeldud nende keelte õppimiseks, vaid ühe väga praktilise vahe mõistmiseks:
- mõni fail käivitatakse tõlgendiga otse
- mõni fail kompileeritakse kõigepealt teise vormi
- mõni tulemus on päris binaar
- mõni tulemus vajab eraldi runtime'i või virtuaalmasinat
See teema seob kokku:
- shebang'i ja käivitatavad skriptid
PATH-i ja käskude leidmise- Dockeri ja builditööriistad
- LaTeX-i kompileerimise idee
Kiirspikker
sh hello.shkäivitab shelliskriptipython3 hello.pykäivitab Pythoni failicc hello.c -o hello-ckompileerib C programmic++ hello.cpp -o hello-cppkompileerib C++ programmigo run hello.gokompileerib ja käivitab Go näitego build -o hello-go hello.goehitab Go binaaricargo runehitab ja käivitab Rusti projektijavac Hello.javakompileerib Java klassijava Hellokäivitab Java klassi JVM-is
Kõige tavalisemad käsud
sh skript.sh./skript.shpython3 hello.pycc hello.c -o hello-cc++ hello.cpp -o hello-cppgo build -o hello-go hello.gocargo buildcargo runjavac Hello.javajava Hello
Üks kiire võrdlustabel
| Keel | Lähtefail | Tüüpiline käivitus | Mis tekib | | --- | --- | --- | --- | | Shell | hello.sh | sh hello.sh või ./hello.sh | tavaliselt eraldi buildi ei teki | | Python | hello.py | python3 hello.py | lähtefail, mõnikord __pycache__ | | C | hello.c | ./hello-c pärast kompileerimist | päris binaar | | C++ | hello.cpp | ./hello-cpp pärast kompileerimist | päris binaar | | Go | hello.go | go run hello.go või ./hello-go | päris binaar | | Rust | src/main.rs | cargo run või binaar target/ all | päris binaar + target/ | | Java | Hello.java | java Hello | .class fail, käivitus JVM-is |
Shell: skript ja shebang
Shelli näide on kõige lihtsam, sest see on lihtsalt tekstifail, mida loeb shell.
cat > hello.sh <<'EOF'
#!/bin/sh
echo "Tere, maailm!"
echo "Tere, maailm!"
echo "Tere, maailm!"
EOF
chmod +x hello.sh
./hello.sh
Siin:
- fail on tavaline tekstifail
chmod +xlubab selle käivitada- shebang
#!/bin/shütleb, milline shell faili tõlgendab
Teine võimalus on:
sh hello.sh
Siis valid shelli käsurealt ise.
Python: tõlgendatud käivitus
Pythoni näide näeb välja samuti lihtne, aga erinevalt shellist käivitad sa tavaliselt faili Pythoni tõlgendiga.
cat > hello.py <<'EOF'
for _ in range(3):
print("Tere, maailm!")
EOF
python3 hello.py
Siin:
- lähtefail jääb tekstifailiks
python3loeb selle sisse ja käivitab- tavaliselt ei teki kohe samas mõttes eraldi käivitatavat binaari
C: kompileeri binaariks
C on klassikaline näide keelest, kus lähtekood tuleb kõigepealt kompileerida.
cat > hello.c <<'EOF'
#include <stdio.h>
int main(void) {
for (int i = 0; i < 3; i++) {
puts("Tere, maailm!");
}
return 0;
}
EOF
cc hello.c -o hello-c
./hello-c
Siin:
hello.con lähtefailcc ... -o hello-cteeb sellest binaari./hello-ckäivitab juba kompileeritud programmi
See on oluline vahe võrreldes shelli või Pythoniga: enne käivitamist tuleb ehitada eraldi käivitatav fail.
C++: sarnane C-ga, aga teise kompilaatoriga
cat > hello.cpp <<'EOF'
#include <iostream>
int main() {
for (int i = 0; i < 3; i++) {
std::cout << "Tere, maailm!" << std::endl;
}
return 0;
}
EOF
c++ hello.cpp -o hello-cpp
./hello-cpp
Loogika on sama nagu C puhul:
- lähtekood
- kompileerimine
- binaar
Go: lihtne tee ühe binaarini
Go on käsureaõppe jaoks väga tänulik näide, sest buildi loogika on sirge.
cat > hello.go <<'EOF'
package main
import "fmt"
func main() {
for i := 0; i < 3; i++ {
fmt.Println("Tere, maailm!")
}
}
EOF
go run hello.go
go build -o hello-go hello.go
./hello-go
Siin on kaks eri tööviisi:
go runkompileerib ja käivitab ühe hoogago buildteeb päris binaari
See teeb Go-st väga hea näite, kui tahad näha vahet "jooksuta kohe" ja "ehita fail valmis".
Rust: builditööriist on osa tavalisest töövoost
Rustis kasutatakse väga sageli tööriista cargo.
mkdir -p hello-rust
cd hello-rust
cargo init --bin .
cat > src/main.rs <<'EOF'
fn main() {
for _ in 0..3 {
println!("Tere, maailm!");
}
}
EOF
cargo run
cargo build --release
./target/release/hello-rust
Siin:
cargo init --bin .teeb projekti karkassicargo runehitab ja käivitabcargo build --releaseteeb optimeerituma binaari
Rusti näide on hea, sest siin on juba näha, et mõnes keeles ei käi build ainult ühe kompilaatorikäsuga, vaid terve töövoo kaudu.
Java: kompileerimine ja eraldi runtime
Java on hea kontrast C-le ja Go-le, sest kompileerimine toimub küll enne, aga tulemus ei ole tavaliselt otse natiivne binaar.
cat > Hello.java <<'EOF'
public class Hello {
public static void main(String[] args) {
for (int i = 0; i < 3; i++) {
System.out.println("Tere, maailm!");
}
}
}
EOF
javac Hello.java
java Hello
Siin:
javacteeb failistHello.classjava Hellokäivitab selle JVM-is
See on oluline vahe:
- C, C++, Go ja Rust annavad sulle tavaliselt natiivse binaari
- Java annab sulle klassifaili, mida käitab Java runtime
Millal tekib mis asi
Hea meelespea on:
- shell: tekstifail, mida tõlgendatakse
- Python: tekstifail, mida tõlgendatakse
- C/C++: natiivne binaar
- Go: natiivne binaar
- Rust: natiivne binaar, mida haldab
cargo - Java: baitkood, mida käitab JVM
Just see on põhjus, miks nende keelte buildi- ja käivitusloogika erineb.
Praktiline soovitus
Kui tahad mõtet kiiresti kätte saada, mine selles järjekorras:
- shell
- Python
- C
- Go
- Rust
- Java
See järjekord liigub lihtsast tõlgendamisest kuni keerukama builditööriista ja runtime'i loogikani.
Minitest
- Tee shelliskript, mis prindib sama rea kolm korda.
- Tee sama Pythonis.
- Kompileeri väike C programm ja käivita see.
- Võrdle
go runjago buildkäitumist. - Vaata, mis fail tekib pärast
javac Hello.java. - Selgita ühe lausega, miks Java erineb C-st käivitamise mõttes.