Kompileerimine ja käivitamine: shell, Python, C, C++, Go, Rust, Java
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 natiivne binaar
- mõni tulemus vajab eraldi käivituskeskkonda või virtuaalmasinat
See teema seob kokku:
- shebang-rida ja käivitatavad skriptid
PATH-i ja käskude leidmise- Dockeri ja koostamistööriistad
- LaTeX-i kompileerimise idee
Kiirülevaade
Eesmärk on mõista, miks eri keelte programmide käivitamine erineb: mõnda tõlgendatakse käivitamise ajal, mõni koostatakse enne eraldi artefaktiks.
| Kuju või tööriist | Milleks | Mida tavaliselt näed |
|---|---|---|
| tõlgendiga skript | käivita lähtekood otse | programmi väljund kohe |
| kompilaator | tee lähtekoodist uus artefakt | binaar või koostamiskataloog |
go build, cargo | juhi koostamissamme tööriistaga | koostamislogi ja väljundfailid |
| Java | kompileeri ja käivita eri sammudena | .class failid ja programmi väljund |
| süntaksi- või kompileerimisviga | käivitus peatub enne tulemust | veateade enne programmi tööd |
Tüüpilised algaja vead
- arvatakse, et kõik lähtefailid on otse käivitatavad
- aetakse segi koostamis- ja käivituskäsk
- unustatakse vaadata, millised failid pärast koostamist tekkisid
Ü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 koostamisväljundit ei teki |
| Python | hello.py | python3 hello.py | lähtefail, mõnikord __pycache__ |
| C | hello.c | ./hello-c pärast kompileerimist | natiivne binaarfail |
| C++ | hello.cpp | ./hello-cpp pärast kompileerimist | natiivne binaarfail |
| Go | hello.go | go run hello.go või ./hello-go | natiivne binaarfail |
| Rust | src/main.rs | cargo run või binaarfail kataloogis target/ | natiivne binaarfail ja kataloog 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 sobib käsureaõppeks hästi, sest koostamise loogika on lihtne.
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 käsugago buildteeb natiivse binaari
Go näitab hästi erinevust käivitamise ja eraldi binaari koostamise vahel.
Rust: koostamistöö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 näitab, et mõnes keeles ei koosne koostamine ainult ühest kompilaatorikäsust, vaid tervest töövoost.
Java: kompileerimine ja eraldi käivituskeskkond
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 klassifaili, mida käitab Java käivituskeskkond
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.
Peatüki täisspikker
Edasijõudnu
Eesmärk
Erista lähtekoodi, koostatud väljundit ja selle käivitajat. Vali üks paigaldatud tööriistaga keel.
Käsud ja näited
sh hello.shtõlgenda POSIX-shelliskriptpython3 hello.pykäivita Pythoni lähtefailcc hello.c -o hello-ckompileeri C lähtefail käivitatavaks failiks./hello-ckäivita äsja koostatud C programmc++ hello.cpp -o hello-cppkompileeri C++go build -o hello-go hello.gokoosta Go binaarfailcargo runRust: koosta ja käivita Cargo projektjavac Hello.javaJava: loo baitkoodifail Hello.classjava Hellokäivita klass JVM-is, ilma .class-laiendita