ubuntuusers.de

Scala-Programme in nativen Maschinencode compilieren

Status: Gelöst | Ubuntu-Version: Ubuntu 10.10 (Maverick Meerkat)
Antworten |

mmLinuxer

Anmeldungsdatum:
21. September 2006

Beiträge: 417

Hallo!

Genau wie bei Java-Programmen, werden auch Scala-Programme in Bytecode für die JVM compiliert und dann von ihr ausgeführt. Mit dem GNU Compiler for Java kann man jedoch u.A. die Bytecode-Programme auch direkt in Maschinencode compilieren und dann direkt von der CPU (ohne JVM) ausführen. (Diese maschinen-compilierten Java- od. Scala-Programme laufen nicht unbedingt schneller, aber sie starten wesentlich schneller was bei häufigem Aufruf kleiner Programme sehr ins Gewicht fällt.)

Mein Problem ist, dass irgend was nicht klappt. Kann mir da jemand weiterhelfen?

Zuerst eine Demo der Schritte mit einem Java-Programm:

// ein Java-Programm in der Datei HelloJava.java
class HelloJava {
    public static void main(String[] args) {
        System.out.println("Hello Java"); // Display the string.
    }
}

Und nun den Java-Quellcode in Maschinencode compilieren:

# Der Java-Compiler erzeugt den Bytecode in der Datei HelloJava.class
javac HelloJava.java
# Als Test den Bytecode mit der JVM ausführen
java HelloJava
# Den Bytecode in Maschinencode compilieren
gcj --main=HelloJava -o HelloJava HelloJava.class
# Als Test das Maschinenprogramm ausführen
./HelloJava

Das hat alles wunderbar geklappt und nun die selben Vorgänge mit dem Scala-Programm:

// ein Scala-Programm in der Datei HelloScala.scala
object HelloScala {
  def main(args:Array[String]) = {
    println("Hello Scala")
  }
}

Umwandlung des Scala-Quelltextes in Bytecode und dann Maschinencode:

# Der Scala-Compiler erzeugt den Bytecode in den Dateien HelloScala.class und HelloScala$.class
scalac HelloScala.scala
# Nun testen ob der vom Scala-Compiler erzeugte reine Bytecode mit der JVM ausführbar ist => einwandfrei
java -classpath /usr/share/java/scala-library.jar:. HelloScala
# Den reinen Bytecode in Maschinencode compilieren
gcj --classpath=/usr/share/java/scala-library.jar:. --main=HelloScala -o HelloScala HelloScala.class HelloScala$.class
# aber es kommt folgende Fehlermeldung:
/tmp/ccYTNSVK.o: In function `void HelloScala$::main(JArray<java::lang::String*>*)':
cc8hiFyBjx:(.text+0x13f): undefined reference to `scala::Predef$::class$'
cc8hiFyBjx:(.text+0x150): undefined reference to `scala::Predef$::MODULE$'
cc8hiFyBjx:(.text+0x17d): undefined reference to `void scala::Predef$::println(java::lang::Object*)'
/tmp/ccYTNSVK.o: In function `int HelloScala$::$tag()':
cc8hiFyBjx:(.text+0x1a4): undefined reference to `int scala::ScalaObject$class::$tag(scala::ScalaObject*)'
/tmp/ccYTNSVK.o:(.data+0x360): undefined reference to `scala::ScalaObject::class$'
collect2: ld returned 1 exit status

Hat jemand eine Ahnung was da schief gegangen ist? Die JVM versteht den Bytecode einwandfrei, aber GNU Compiler hat Probleme. Habe ich irgend etwas vergessen oder falsch gemacht? (Ich benutze die Standardpakete von Maverick.)

lupinix

Anmeldungsdatum:
21. Oktober 2007

Beiträge: 524

Wohnort: /dev/wohnung

gcj linkt normalerweise gegen die libgcj, was eine kompilierte Java Klassenbibliothek ist. Da ist nichts Scala-mäßiges enthalten und gegen ein .jar linken geht nicht. Ausführen kann die GNU GIJ den Bytecode aber immerhin, d.h. es dürfte kein generelles Kompatibilitätsproblem sein. In den Scala Manpages ist ja auch immer wieder von GNU Java die Rede, soll damit auch gehen.

mmLinuxer

(Themenstarter)

Anmeldungsdatum:
21. September 2006

Beiträge: 417

lupinix schrieb:

gcj linkt normalerweise gegen die libgcj, was eine kompilierte Java Klassenbibliothek ist. Da ist nichts Scala-mäßiges enthalten und gegen ein .jar linken geht nicht.

Er braucht nur die jar-Datei "/usr/share/java/scala-library.jar". Kann er die nötigen darin enthaltenen class-Dateien nicht mit den HelloScala.class mitcompilieren? Nur die jar konnte ich kompilieren:

gcj -c /usr/share/java/scala-library.jar -o scala-library.o

aber

gcj -lscala-library.o --classpath=/usr/share/java/scala-library.jar:. --main=HelloScala -o HelloScala HelloScala.class HelloScala$.class

klappt dann nicht, weil er die scala-library.o angeblich nicht findet.

tischbein

Avatar von tischbein

Anmeldungsdatum:
21. Juli 2008

Beiträge: 404

mmLinuxer schrieb:

klappt dann nicht, weil er die scala-library.o angeblich nicht findet.

Lass mal das -l vor dem Dateinamen weg. ☺

mmLinuxer

(Themenstarter)

Anmeldungsdatum:
21. September 2006

Beiträge: 417

tischbein schrieb:

mmLinuxer schrieb:

klappt dann nicht, weil er die scala-library.o angeblich nicht findet.

Lass mal das -l vor dem Dateinamen weg. ☺

Es spielt keine Rolle wie ich es schreibe:

gcj -lscala-library.o --classpath=/usr/share/java/scala-library.jar:. --main=HelloScala -o HelloScala HelloScala.class HelloScala$.class
gcj -l scala-library.o --classpath=/usr/share/java/scala-library.jar:. --main=HelloScala -o HelloScala HelloScala.class HelloScala$.class
gcj -l<abs_path>/scala-library.o --classpath=/usr/share/java/scala-library.jar:. --main=HelloScala -o HelloScala HelloScala.class HelloScala$.class
gcj -l <abs_path>/scala-library.o --classpath=/usr/share/java/scala-library.jar:. --main=HelloScala -o HelloScala HelloScala.class HelloScala$.class

oder auch "scala-library" statt "scala-library.o", er sagt immer:

/usr/bin/ld: cannot find -lscala-library.o
collect2: ld returned 1 exit status

tischbein

Avatar von tischbein

Anmeldungsdatum:
21. Juli 2008

Beiträge: 404

mmLinuxer schrieb:

oder auch "scala-library" statt "scala-library.o", er sagt immer:

**OHNE** den prefix -l... 🙄

Der bequemlichkeit halber sähe dein Befehl also wie folgt aus:

gcj scala-library.o --classpath=/usr/share/java/scala-library.jar:. --main=HelloScala -o HelloScala HelloScala.class HelloScala$.class

mmLinuxer

(Themenstarter)

Anmeldungsdatum:
21. September 2006

Beiträge: 417

Danke! Jetzt funktioniert es

gcj scala-library.o --classpath=/usr/share/java/scala-library.jar:. --main=HelloScala -o HelloScala HelloScala.class HelloScala$.class

D.h. ohne "-l" linkt er nicht zu scala-library.o, sondern er baut es mit ein. "scala-library.o" ist 41MB groß und das fertig compilierte "HelloScala" ist 26MB groß. Also wird nun jedes Mini-Script über 25MB werden... Geht das auch, dass man nur 1x "scala-library.o" aufhebt und alle Mini-Scripte damit gelinkt werden? Ich dachte dass der -l Parameter dafür gut ist.

tischbein

Avatar von tischbein

Anmeldungsdatum:
21. Juli 2008

Beiträge: 404

mmLinuxer schrieb:

Danke! Jetzt funktioniert es

gcj scala-library.o --classpath=/usr/share/java/scala-library.jar:. --main=HelloScala -o HelloScala HelloScala.class HelloScala$.class

D.h. ohne "-l" linkt er nicht zu scala-library.o, sondern er baut es mit ein. "scala-library.o" ist 41MB groß und das fertig compilierte "HelloScala" ist 26MB groß. Also wird nun jedes Mini-Script über 25MB werden... Geht das auch, dass man nur 1x "scala-library.o" aufhebt und alle Mini-Scripte damit gelinkt werden? Ich dachte dass der -l Parameter dafür gut ist.

Nun, GCJ "packt" Java programme in selbstständinge umgebungen, und dafür muss natürlich der gesamte classpath eingelinkt werden... daher auch die größe.

Vielleicht wäre es eher sinnvoll lediglich .jar dateien zu erstellen, und in selbigen dateien die Scala runtime mit reinzupacken? Ich bin kein Java experte, aber glaube gehört zu haben dass so etwas möglich ist.

mmLinuxer

(Themenstarter)

Anmeldungsdatum:
21. September 2006

Beiträge: 417

tischbein schrieb:

Nun, GCJ "packt" Java programme in selbstständinge umgebungen, und dafür muss natürlich der gesamte classpath eingelinkt werden... daher auch die größe.

Vielleicht wäre es eher sinnvoll lediglich .jar dateien zu erstellen, und in selbigen dateien die Scala runtime mit reinzupacken? Ich bin kein Java experte, aber glaube gehört zu haben dass so etwas möglich ist.

Das würde gehen (jar's sind nur eine Art zip's), aber gcj würde/müsste trotzdem einen Großteil der Scala-Pakete in jedes kleine Scala-Script mithineincompilieren. Besser wäre wohl nur 1x das vollständige Scala-Paket zu kompilieren und dann die einzelnen kleinen Scala-Scripte damit zu linken (falls dieser Ausdruck stimmt - etwa wie eine DLL unter Windoof). Sind die xyz.o Binärdateien soetwas wie DLL's (Dynamic Link Libraries)?

Panke

Anmeldungsdatum:
14. Oktober 2010

Beiträge: 133

Nein, die *.so sind shared libraries (DLL) und .a sind statische Bibliotheken.

schau mal hier

tischbein

Avatar von tischbein

Anmeldungsdatum:
21. Juli 2008

Beiträge: 404

mmLinuxer schrieb:

tischbein schrieb:

Nun, GCJ "packt" Java programme in selbstständinge umgebungen, und dafür muss natürlich der gesamte classpath eingelinkt werden... daher auch die größe.

Vielleicht wäre es eher sinnvoll lediglich .jar dateien zu erstellen, und in selbigen dateien die Scala runtime mit reinzupacken? Ich bin kein Java experte, aber glaube gehört zu haben dass so etwas möglich ist.

Das würde gehen (jar's sind nur eine Art zip's), aber gcj würde/müsste trotzdem einen Großteil der Scala-Pakete in jedes kleine Scala-Script mithineincompilieren. Besser wäre wohl nur 1x das vollständige Scala-Paket zu kompilieren und dann die einzelnen kleinen Scala-Scripte damit zu linken

Richtig.

Windoof

Windows*
(windoof zu schreiben ist dumm und kindisch. sorry, ist aber so ☺)

Sind die xyz.o Binärdateien soetwas wie DLL's (Dynamic Link Libraries)?

Nein, .o dateien nennen sich 'relocatable objects', sprich zwischencode. Du kannst aber versuchen Scala in eine .so (dynamic shared object) zu linken:

gcj /usr/share/java/scala-library.jar -shared -o scala-library.so

Danach entsprechend gegen die neue .so datei linken, und mit dem Flag -Wl,-rpath=. wird auch der momentane Ordner der ausführbaren datei nach der library gesucht, was umständliches modifizieren des LD_LIBRARY_PATH vermeidet:

gcj --classpath=/usr/share/java/scala-library.jar:. --main=HelloScala -o HelloScala HelloScala.class HelloScala$.class scala-library.so -Wl,-rpath=.

mmLinuxer

(Themenstarter)

Anmeldungsdatum:
21. September 2006

Beiträge: 417

Panke schrieb:

Nein, die *.so sind shared libraries (DLL) und .a sind statische Bibliotheken. schau mal hier

Danke für den Link! Ich habe es nun nach der darin enthaltenen Anleitung versuch:

user@pc:~$ gcj -c -fPIC /usr/share/java/scala-library.jar -o scala-library.o
user@pc:~$ gcj -shared -Wl,-soname,libscala-library.so.1 -o libscala-library.so.1.0.1 scala-library.o
user@pc:~$ gcj --classpath=/usr/share/java/scala-library.jar:. HelloScala.class HelloScala$.class -o dynamically_linked -L. -lscala-library
/usr/bin/ld: cannot find -lscala-library
collect2: ld returned 1 exit status

Die dyn. Bibl. zu erstellen hat wohl geklappt, aber das Compilieren von HelloScala mit dieser dyn. Bibl. wohl nicht. Hat jemand Ideen?

mmLinuxer

(Themenstarter)

Anmeldungsdatum:
21. September 2006

Beiträge: 417

JUHUU! Danke tischbein! Das hat nun geklappt:

gcj -fPIC /usr/share/java/scala-library.jar -shared -o scala-library.so
gcj --classpath=/usr/share/java/scala-library.jar:. --main=HelloScala -o HelloScala HelloScala.class HelloScala$.class scala-library.so -Wl,-rpath=.

Es funktioniert! "scala-library.so" ist 38MB groß und "HelloScala" nur 16kB.

Panke

Anmeldungsdatum:
14. Oktober 2010

Beiträge: 133

gcj sagt dir ja schon, dass er die nicht findet. Er sucht bei den üblich verdächtigen Plätzen nach deiner Bibliothek und dazu gehört scheinbar nicht das aktuelle Verzeichnis.

-l library
           Search the library named library when linking.  (The second alternative
           with the library as a separate argument is only for POSIX compliance and is
           not recommended.)

           It makes a difference where in the command you write this option; the
           linker searches and processes libraries and object files in the order they
           are specified.  Thus, foo.o -lz bar.o searches library z after file foo.o
           but before bar.o.  If bar.o refers to functions in z, those functions may
           not be loaded.

           The linker searches a standard list of directories for the library, which
           is actually a file named liblibrary.a.  The linker then uses this file as
           if it had been specified precisely by name.

           The directories searched include several standard system directories plus
           any that you specify with -L.

           Normally the files found this way are library files---archive files whose
           members are object files.  The linker handles an archive file by scanning
           through it for members which define symbols that have so far been
           referenced but not defined.  But if the file that is found is an ordinary
           object file, it is linked in the usual fashion.  The only difference
           between using an -l option and specifying a file name is that -l surrounds
           library with lib and .a and searches several directories.

Ah super, dass das geklappt hat. Vielleicht wär das ja eine Ergänzung von scala wert?

mmLinuxer

(Themenstarter)

Anmeldungsdatum:
21. September 2006

Beiträge: 417

Panke schrieb:

Ah super, dass das geklappt hat. Vielleicht wär das ja eine Ergänzung von scala wert?

Ich werde es noch etwas testen und dann mal hier eine kompakte Beschreibung hineinstellen.

Danke an alle!

Antworten |