Hypercell ein ] Hypercell aus ] Zeige Navigation ] Verstecke Navigation ]
c++.de  
   
Forentreff 2012     
Bücher-Shop mit Amazon (Buchkategorien)C++ : Referenzen zu C++ : C++ Builder : Visual C++ : C# : Java : Spieleprogrammierung : Systemprogrammierung Linux : Software-Entwicklung : .NET : Compilertechnik : Algorithmen & Datenstrukturen : Objektorientierung : Entwurfsmuster : UML : eXtreme Programming : Scrum : Projektmanagement : Software-Testing : Datenbanken : Tom DeMarco : Dilbert : User Friendly
C/C++ Forum :: Die Artikel ::  Build-Systeme Teil 3: SCons     Zeige alle Beiträge auf einer Seite Auf Beitrag antworten
Autor Nachricht
GPC
Moderator

Benutzerprofil
Anmeldungsdatum: 11.07.2004
Beiträge: 6290
Beitrag GPC Moderator 20:32:07 07.02.2006   Titel:   Build-Systeme Teil 3: SCons            Zitieren

Im nunmehr dritten Teil der Serie "Build-Systeme" werde ich dieses Mal SCons vorstellen.

Inhalt:
  • 1 Hintergründe zu SCons
  • 2 Installation von SCons
  • 3 Die Datei "SConstruct"
  • 4 Erstellen von Programmen
    • 4.1 Keyword Arguments
    • 4.2 Mehrere Quelldateien
  • 5 Erstellen von Bibliotheken
  • 6 Einbinden von Bibliotheken
  • 7 Abhängigkeiten erkennen
  • 8 Auf Abhängigkeiten testen
    • 8.1 Construction Environments
    • 8.2 Auf Bibliotheken/Header/Funktionen/typedefs testen
  • 9 Zum Schluss
  • 10 What comes next...


1 Hintergründe zu SCons

SCons ist ein Open Source Build-System, welches in Python implementiert wurde und seine Wurzeln im auf Perl basierenden Cons hat. Bei der Entwicklung von SCons wurde auf Korrektheit, Geschwindigkeit und besondere Einfachheit Wert gelegt. Wie Python selbst ist SCons sehr einfach zu benutzen, sowohl für einfache Aufgaben, als auch für komplizierte Builds.
Um SCons zu benutzen, braucht es nur minimale Python-Kenntnisse. Erwerben kann man diese mit dem Tutorial auf der Python-Homepage.

SCons kann sowohl für C bzw. C++ als auch für Java verwendet werden. In diesem Artikel werde ich allerdings nur auf C++ eingehen und die Java-Anhänger auf das offizielle Tutorial verweisen.

Da es von den Herstellern von SCons bereits ein sehr gutes Tutorial gibt, werde ich bloß auf die grundlegenden Funktionen eingehen und diese relativ schnell abhandeln.

2 Installation von SCons

SCons ist, da auf Python basierend, plattformunabhängig. Um mit SCons zu arbeiten, muss Python installiert sein.
Man kann entweder die vorkompilierten Pakete installieren oder es selbst kompilieren. Die unten erwähnten Pakete kann man in der Download-Section von http://www.scons.org/ finden.

Für Windows gibt es, wie gewohnt, einen Installer, der wohl keiner weiteren Erklärung bedarf. ;)

Auf auf RPM basierenden Systemen geht die Installation relativ simpel:
Code:
rpm -uvh scons-x.x-x.noarch.rpm
Code:
rpm -uvh scons-x.x-x.noarch.rpm
Code:
rpm -uvh scons-x.x-x.noarch.rpm


Auch bei auf Debian basierenden Systemen ist es nicht sehr schwer:
Code:
apt-get install scons
Code:
apt-get install scons
Code:
apt-get install scons


Wer SCons aus den Quellen übersetzen möchte, lädt sich den Tarball (oder die Zip-Datei) herunter, entpackt sie und wechselt ins scons Verzeichnis. Dort einfach
Code:
python setup.py install
Code:
python setup.py install
Code:
python setup.py install

eingeben und SCons wird in die üblichen Verzeichnisse ( /usr/local/ ) installiert. Dies erfordert allerdings Administratorrechte; besitzt man diese nicht, so kann man folgendermaßen ein anderes Verzeichnis angeben:
Code:
python setup.py install --prefis=$HOME
Code:
python setup.py install --prefis=$HOME
Code:
python setup.py install --prefis=$HOME


3 Die Datei "SConstruct"

Diese Datei kann man in etwa mit einem Makefile oder einer Ant-XML-Datei vergleichen, denn in ihr stehen die Tasks und Targets, die SCons dann ausführt. Um die Aufgaben zu beschreiben, wird Python verwendet, d.h., dass diese Datei eigentlich ein Python-Skript ist. Im Unterschied zu Python-Skripten wird diese Datei aber nicht unbedingt geordnet abgearbeitet (siehe unten), sondern so, wie es die Erfüllung der Targets erfordert.
Die "SConstruct" sollte man an der Wurzel im Projektverzeichnis abgelegen.

4 Erstellen von Programmen

Um zu zeigen, wie man mit SCons Programme erstellen kann, werden wir das allseits beliebte "Hallo Welt"-Beispiel verwenden:
C/C++ Code:
//hello_world.cpp
#include
<iostream>

int main(int argc, char **argv) {
  std::cout<<"Hello World\n";
  return EXIT_SUCCESS;
};
C/C++ Code:
//hello_world.cpp
#include
<iostream>

int main(int argc, char **argv) {
std::cout<<"Hello World\n";
return EXIT_SUCCESS;
};
C/C++ Code:
//hello_world.cpp
#include
<iostream>

int main(int argc, char **argv) {
  std::cout<<"Hello World\n";
  return EXIT_SUCCESS;
};


Sehen wir uns die dazugehörige SConstruct-Datei an:
Code:
#SConstruct
#Kommentare beginnen in Python mit einem #-Zeichen
Program("hello_world.cpp")
Code:
#SConstruct
#Kommentare beginnen in Python mit einem #-Zeichen
Program("hello_world.cpp")
Code:
#SConstruct
#Kommentare beginnen in Python mit einem #-Zeichen
Program("hello_world.cpp")


Diese Anweisung sagt SCons zwei Dinge: Wir wollen (1) ein Programm erstellen, und zwar (2) aus der Datei "hello_world.cpp". Um den Build-Prozess zu starten, wechselt man in das Verzeichnis, in dem die Datei "SConstruct" liegt und tippt einfach
Code:
scons
Code:
scons
Code:
scons
in die Shell ein.
Auf meinem Linux-System erscheint folgende Ausgabe:
Code:
scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Building targets ...
g++ -c -o hello_world.o hello_world.cpp
g++ -o hello_world hello_world.o
scons: done building targets.
Code:
scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Building targets ...
g++ -c -o hello_world.o hello_world.cpp
g++ -o hello_world hello_world.o
scons: done building targets.
Code:
scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Building targets ...
g++ -c -o hello_world.o hello_world.cpp
g++ -o hello_world hello_world.o
scons: done building targets.

Nun wurden eine hello_world.o und eine Binary namens hello_world erstellt. Auf Windows-Systemen sollten die Dateien hello_world.obj und hello_world.exe heißen.
Es ist im Übrigen möglich, in einer SConstruct mehrere Targets zu definieren, also z.B. zwei oder drei Programme zu erstellen (wobei die Reihenfolge, wie SCons die Builds durchführt, nicht der Datei SConstruct folgen muss, d.h. es könnte sein, dass KillerApplication vor hello_world erstellt wird):
Code:
Program("hello_world.cpp") #Erstes Target
Program("KillerApplication.cpp") #Zweites Target
Code:
Program("hello_world.cpp") #Erstes Target
Program("KillerApplication.cpp") #Zweites Target
Code:
Program("hello_world.cpp") #Erstes Target
Program("KillerApplication.cpp") #Zweites Target


Möchte man nur Objektdateien erstellen, muss man das Target in der SConstruct leicht verändern:
Code:
#SConstruct
#Einfache Anführungszeichen gehen auch:
Object('hello_world.cpp')
Code:
#SConstruct
#Einfache Anführungszeichen gehen auch:
Object('hello_world.cpp')
Code:
#SConstruct
#Einfache Anführungszeichen gehen auch:
Object('hello_world.cpp')


Um den Effekt zu sehen, müssen wir zuerst die Ergebnisse des vorangegangenen Builds löschen. Dies geschieht mit einem Aufruf von scons und dem -c (oder --clean) Parameter:
Code:
scons -c
Code:
scons -c
Code:
scons -c


Führt man scons nun erneut aus, so wird nur eine hello_world.o bzw. hello_world.obj erstellt.

Die Ausgaben, die SCons um die eigentlichen Befehle erzeugt, können leicht mit dem -Q Parameter unterdrückt werden, um nur die wirklich wichtigen Informationen zu sehen:
Code:
gpc@darkstar:~/tmp$ scons -Q
g++ -c -o hello_world.o hello_world.cpp
Code:
gpc@darkstar:~/tmp$ scons -Q
g++ -c -o hello_world.o hello_world.cpp
Code:
gpc@darkstar:~/tmp$ scons -Q
g++ -c -o hello_world.o hello_world.cpp


Bisher hat SCons immer die Namen für die erzeugten Programme vergeben; das können wir aber auch selbst:
Code:
#SConstruct
Program("Hello", "hello_world.cpp")
Code:
#SConstruct
Program("Hello", "hello_world.cpp")
Code:
#SConstruct
Program("Hello", "hello_world.cpp")


Aus hello_world.cpp wird nun eine Binary namens Hello erstellt:
Code:
1
2
3
4
5
6
7
8
1
2
3
4
5
6
7
8
gpc@darkstar:~/tmp$ scons -Q
g++ -o Hello hello_world.o
gpc@darkstar:~/tmp$ ls -l
total 28
-rwxr-xr-x  1 gpc users 12755 2005-12-18 16:38 Hello*
-rw-r--r--  1 gpc users    36 2005-12-18 16:37 SConstruct
-rw-r--r--  1 gpc users   108 2005-12-18 16:31 hello_world.cpp
-rw-r--r--  1 gpc users  2264 2005-12-18 16:31 hello_world.o
Code:
1
2
3
4
5
6
7
8
gpc@darkstar:~/tmp$ scons -Q
g++ -o Hello hello_world.o
gpc@darkstar:~/tmp$ ls -l
total 28
-rwxr-xr-x 1 gpc users 12755 2005-12-18 16:38 Hello*
-rw-r--r-- 1 gpc users 36 2005-12-18 16:37 SConstruct
-rw-r--r-- 1 gpc users 108 2005-12-18 16:31 hello_world.cpp
-rw-r--r-- 1 gpc users 2264 2005-12-18 16:31 hello_world.o
Code:
1
2
3
4
5
6
7
8
gpc@darkstar:~/tmp$ scons -Q
g++ -o Hello hello_world.o
gpc@darkstar:~/tmp$ ls -l
total 28
-rwxr-xr-x  1 gpc users 12755 2005-12-18 16:38 Hello*
-rw-r--r--  1 gpc users    36 2005-12-18 16:37 SConstruct
-rw-r--r--  1 gpc users   108 2005-12-18 16:31 hello_world.cpp
-rw-r--r--  1 gpc users  2264 2005-12-18 16:31 hello_world.o

Man sieht, es wurde eine Hello erstellt.

4.1 Keyword Arguments

Wenn man die Quelldateien zuerst auflisten möchte und dann erst den Namen des Targets, so muss man mit so genannten "Keyword Arguments" arbeiten:
Code:
#Standardreihenfolge
Program(target = Hello, source = "hello_world.cpp")

#Umgekehrte Reihenfolge
Program(source = "hello_world.cpp", target = Hello)
Code:
#Standardreihenfolge
Program(target = Hello, source = "hello_world.cpp")

#Umgekehrte Reihenfolge
Program(source = "hello_world.cpp", target = Hello)
Code:
#Standardreihenfolge
Program(target = Hello, source = "hello_world.cpp")

#Umgekehrte Reihenfolge
Program(source = "hello_world.cpp", target = Hello)


4.2 Mehrere Quelldateien

Größere Programme bestehen selten aus einer einzelnen Quelldatei (sollten sie jedenfalls nicht). SCons gibt uns selbstverständlich die Möglichkeit, mehrere Quelldateien zu kompilieren:
Code:
#SConstruct
#Nehmen wir an, es gibt noch eine goodbye.cpp
Program("Hello", ["hello_world.cpp", "goodbye.cpp"])
Code:
#SConstruct
#Nehmen wir an, es gibt noch eine goodbye.cpp
Program("Hello", ["hello_world.cpp", "goodbye.cpp"])
Code:
#SConstruct
#Nehmen wir an, es gibt noch eine goodbye.cpp
Program("Hello", ["hello_world.cpp", "goodbye.cpp"])


Ein Aufruf zeigt uns Folgendes:
Code:
1
2
3
4
5
6
7
8
9
10
11
1
2
3
4
5
6
7
8
9
10
11
gpc@darkstar:~/tmp$ scons -Q
g++ -c -o goodbye.o goodbye.cpp
g++ -o Hello hello_world.o goodbye.o
gpc@darkstar:~/tmp$ ls -l
total 32
-rwxr-xr-x  1 gpc users 12799 2005-12-18 16:51 Hello*
-rw-r--r--  1 gpc users    53 2005-12-18 16:51 SConstruct
-rw-r--r--  1 gpc users     0 2005-12-18 16:51 goodbye.cpp
-rw-r--r--  1 gpc users   625 2005-12-18 16:51 goodbye.o
-rw-r--r--  1 gpc users   108 2005-12-18 16:31 hello_world.cpp
-rw-r--r--  1 gpc users  2264 2005-12-18 16:40 hello_world.o
Code:
1
2
3
4
5
6
7
8
9
10
11
gpc@darkstar:~/tmp$ scons -Q
g++ -c -o goodbye.o goodbye.cpp
g++ -o Hello hello_world.o goodbye.o
gpc@darkstar:~/tmp$ ls -l
total 32
-rwxr-xr-x 1 gpc users 12799 2005-12-18 16:51 Hello*
-rw-r--r-- 1 gpc users 53 2005-12-18 16:51 SConstruct
-rw-r--r-- 1 gpc users 0 2005-12-18 16:51 goodbye.cpp
-rw-r--r-- 1 gpc users 625 2005-12-18 16:51 goodbye.o
-rw-r--r-- 1 gpc users 108 2005-12-18 16:31 hello_world.cpp
-rw-r--r-- 1 gpc users 2264 2005-12-18 16:40 hello_world.o
Code:
1
2
3
4
5
6
7
8
9
10
11
gpc@darkstar:~/tmp$ scons -Q
g++ -c -o goodbye.o goodbye.cpp
g++ -o Hello hello_world.o goodbye.o
gpc@darkstar:~/tmp$ ls -l
total 32
-rwxr-xr-x  1 gpc users 12799 2005-12-18 16:51 Hello*
-rw-r--r--  1 gpc users    53 2005-12-18 16:51 SConstruct
-rw-r--r--  1 gpc users     0 2005-12-18 16:51 goodbye.cpp
-rw-r--r--  1 gpc users   625 2005-12-18 16:51 goodbye.o
-rw-r--r--  1 gpc users   108 2005-12-18 16:31 hello_world.cpp
-rw-r--r--  1 gpc users  2264 2005-12-18 16:40 hello_world.o

Hier sieht man, dass die Datei goodbye.cpp erfolgreich kompiliert und in Hello gelinkt wurde.

Dieses Konstrukt mit den eckigen Klammern in der SConstruct ist eine Python-Liste, welche die einzelnen Quelldateien beinhaltet. Es wäre auch möglich, nur einen Eintrag in die Liste zu setzen. Dies hätte den gleichen Effekt wie vorher, als wir nur einen String mit dem Namen übergeben haben:
Code:
#Beide Statements haben den gleichen Effekt
Program("Main", ["main.cpp"])
#Program("Main", "main.cpp")
Code:
#Beide Statements haben den gleichen Effekt
Program("Main", ["main.cpp"])
#Program("Main", "main.cpp")
Code:
#Beide Statements haben den gleichen Effekt
Program("Main", ["main.cpp"])
#Program("Main", "main.cpp")


Da das Schreiben und Lesen einer längeren Liste nicht sehr viel Spaß macht, kann man alternativ auch die Python-Split-Funktion aus dem String-Modul verwenden:
Code:
#SConstruct
Program("Calculator", Split("main.cpp calculator.cpp parser.cpp"))
Code:
#SConstruct
Program("Calculator", Split("main.cpp calculator.cpp parser.cpp"))
Code:
#SConstruct
Program("Calculator", Split("main.cpp calculator.cpp parser.cpp"))


Auch möglich:
Code:
#SConstruct
fileList = Split("main.cpp calculator.cpp parser.cpp")
Program("Calculator", fileList)
Code:
#SConstruct
fileList = Split("main.cpp calculator.cpp parser.cpp")
Program("Calculator", fileList)
Code:
#SConstruct
fileList = Split("main.cpp calculator.cpp parser.cpp")
Program("Calculator", fileList)


Über mehrere Zeilen:
Code:
#SConstruct
fileList = Split("""main.cpp
            calculator.cpp
            parser.cpp""")
Program("Calculator", fileList)
Code:
#SConstruct
fileList = Split("""main.cpp
calculator.cpp
parser.cpp""")
Program("Calculator", fileList)
Code:
#SConstruct
fileList = Split("""main.cpp
            calculator.cpp
            parser.cpp""")
Program("Calculator", fileList)

Im letzten Beispiel wurde die "triple-quote"-Syntax verwendet, die es erlaubt, einen String über mehrere Zeilen hinweg zu definieren. Auch hier können es entweder drei doppelte oder drei einzelne Anführungszeichen sein.

5 Erstellen von Bibliotheken

Abgesehen von Programmen und Objektdateien kann SCons auch mit Leichtigkeit Bibliotheken erstellen, sowohl statische als auch dynamische.

Erstellen wir eine kleine Bibliothek, die in je einer Quelldatei eine Grundrechenart bereitstellt (die Deklaration findet sich in den dazugehörigen *.hpp Dateien):
Code:
#SConstruct
fileList = Split("add.cpp sub.cpp mul.cpp div.cpp")

#Library erstellt eine statische Bibltiothek
Library(target="math", source=fileList)
Code:
#SConstruct
fileList = Split("add.cpp sub.cpp mul.cpp div.cpp")

#Library erstellt eine statische Bibltiothek
Library(target="math", source=fileList)
Code:
#SConstruct
fileList = Split("add.cpp sub.cpp mul.cpp div.cpp")

#Library erstellt eine statische Bibltiothek
Library(target="math", source=fileList)


Um nun die "Bibliothek" zu erstellen, rufen wir scons auf, was folgende Ausgabe produziert:
Code:
1
2
3
4
5
6
7
8
1
2
3
4
5
6
7
8
gpc@darkstar:~/tmp$ scons -Q
g++ -c -o add.o add.cpp
g++ -c -o div.o div.cpp
g++ -c -o sub.o sub.cpp
g++ -c -o mul.o mul.cpp
ar r libmath.a add.o sub.o mul.o div.o
ranlib libmath.a
ar: creating libmath.a
Code:
1
2
3
4
5
6
7
8
gpc@darkstar:~/tmp$ scons -Q
g++ -c -o add.o add.cpp
g++ -c -o div.o div.cpp
g++ -c -o sub.o sub.cpp
g++ -c -o mul.o mul.cpp
ar r libmath.a add.o sub.o mul.o div.o
ranlib libmath.a
ar: creating libmath.a
Code:
1
2
3
4
5
6
7
8
gpc@darkstar:~/tmp$ scons -Q
g++ -c -o add.o add.cpp
g++ -c -o div.o div.cpp
g++ -c -o sub.o sub.cpp
g++ -c -o mul.o mul.cpp
ar r libmath.a add.o sub.o mul.o div.o
ranlib libmath.a
ar: creating libmath.a

Unsere Bibliothek wurde erfolgreich erstellt.

Wer sich gerne exakt ausdrückt, der kann anstatt Library auch StaticLibrary schreiben, wenn er eine statische Bibliothek erstellen möchte. Ein Unterschied existiert nicht.

Das Erstellen einer dynamischen Bibliothek (*.so auf POSIX-Systemen, *.dll auf Windows-Systemen) ist genauso einfach:
Code:
fileList = Split("add.cpp sub.cpp mul.cpp div.cpp")

#Nun erstellen wir eine dynamische Bibliothek
SharedLibrary(target="math", source=fileList)
Code:
fileList = Split("add.cpp sub.cpp mul.cpp div.cpp")

#Nun erstellen wir eine dynamische Bibliothek
SharedLibrary(target="math", source=fileList)
Code:
fileList = Split("add.cpp sub.cpp mul.cpp div.cpp")

#Nun erstellen wir eine dynamische Bibliothek
SharedLibrary(target="math", source=fileList)


Führen wir nun SCons aus:
Code:
gpc@darkstar:~/tmp$ scons -Q
g++ -fPIC -c -o add.os add.cpp
g++ -fPIC -c -o div.os div.cpp
g++ -fPIC -c -o sub.os sub.cpp
g++ -fPIC -c -o mul.os mul.cpp
g++ -shared -o libmath.so add.os sub.os mul.os div.os
Code:
gpc@darkstar:~/tmp$ scons -Q
g++ -fPIC -c -o add.os add.cpp
g++ -fPIC -c -o div.os div.cpp
g++ -fPIC -c -o sub.os sub.cpp
g++ -fPIC -c -o mul.os mul.cpp
g++ -shared -o libmath.so add.os sub.os mul.os div.os
Code:
gpc@darkstar:~/tmp$ scons -Q
g++ -fPIC -c -o add.os add.cpp
g++ -fPIC -c -o div.os div.cpp
g++ -fPIC -c -o sub.os sub.cpp
g++ -fPIC -c -o mul.os mul.cpp
g++ -shared -o libmath.so add.os sub.os mul.os div.os

Wie wir sehen, hat SCons eine dynamische Bibliothek erstellt, und das mit so wenig Aufwand.

6 Einbinden von Bibliotheken

Natürlich wäre die beste Bibliothek nutzlos, wenn man sie nicht in seinen Programmen verwenden könnte. Damit dies möglich ist, muss man die Bibliothek natürlich in die Executable hineinlinken.

Die main.cpp sieht so aus:
C/C++ Code:
1
2
3
4
5
6
7
8
1
2
3
4
5
6
7
8
#include <iostream>
#include
"add.hpp"

int main(int argc, char **argv) {
  int result = add(2,7);
  std::cout<<result<<'\n';
  return EXIT_SUCCESS;
};
C/C++ Code:
1
2
3
4
5
6
7
8
#include <iostream>
#include
"add.hpp"

int main(int argc, char **argv) {
int result = add(2,7);
std::cout<<result<<'\n';
return EXIT_SUCCESS;
};
C/C++ Code:
1
2
3
4
5
6
7
8
#include <iostream>
#include
"add.hpp"

int main(int argc, char **argv) {
  int result = add(2,7);
  std::cout<<result<<'\n';
  return EXIT_SUCCESS;
};


Die SConstruct so:
Code:
#SConstruct
Library("math", ["add.cpp", "sub.cpp", "div.cpp", "mul.cpp"])
Program("main.cpp", LIBS=["math"], LIBPATH=".")
Code:
#SConstruct
Library("math", ["add.cpp", "sub.cpp", "div.cpp", "mul.cpp"])
Program("main.cpp", LIBS=["math"], LIBPATH=".")
Code:
#SConstruct
Library("math", ["add.cpp", "sub.cpp", "div.cpp", "mul.cpp"])
Program("main.cpp", LIBS=["math"], LIBPATH=".")


Bemerkenswert ist, dass wir den Namen der Bibliothek einfach so angeben können, ohne uns über systemspezifische Präfixe Gedanken machen zu müssen.

Wenn wir scons jetzt aufrufen, erhalten wir folgende Ausgabe:
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
1
2
3
4
5
6
7
8
9
10
11
12
13
14
gpc@darkstar:~/tmp$ scons
scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Building targets ...
g++ -c -o add.o add.cpp
g++ -c -o sub.o sub.cpp
g++ -c -o div.o div.cpp
g++ -c -o mul.o mul.cpp
ar r libmath.a add.o sub.o div.o mul.o
ranlib libmath.a
ar: creating libmath.a
g++ -c -o main.o main.cpp
g++ -o main main.o -L. -lmath
scons: done building targets.
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
gpc@darkstar:~/tmp$ scons
scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Building targets ...
g++ -c -o add.o add.cpp
g++ -c -o sub.o sub.cpp
g++ -c -o div.o div.cpp
g++ -c -o mul.o mul.cpp
ar r libmath.a add.o sub.o div.o mul.o
ranlib libmath.a
ar: creating libmath.a
g++ -c -o main.o main.cpp
g++ -o main main.o -L. -lmath
scons: done building targets.
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
gpc@darkstar:~/tmp$ scons
scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Building targets ...
g++ -c -o add.o add.cpp
g++ -c -o sub.o sub.cpp
g++ -c -o div.o div.cpp
g++ -c -o mul.o mul.cpp
ar r libmath.a add.o sub.o div.o mul.o
ranlib libmath.a
ar: creating libmath.a
g++ -c -o main.o main.cpp
g++ -o main main.o -L. -lmath
scons: done building targets.


Über das oben verwendete LIBPATH-Argument sucht SCons selbstständig nach Bibliotheken in den angegebenen Verzeichnissen:
Code:
#SConstruct
Program("Test", LIBS = "someLib", LIBPATH = ['/usr/lib', '/usr/local/lib'])
Code:
#SConstruct
Program("Test", LIBS = "someLib", LIBPATH = ['/usr/lib', '/usr/local/lib'])
Code:
#SConstruct
Program("Test", LIBS = "someLib", LIBPATH = ['/usr/lib', '/usr/local/lib'])


SCons sucht nun in den beiden Verzeichnissen, ob es eine "someLib"-Bibliothek findet. Kann keine Bibliothek gefunden werden, meldet sich SCons mit einer Fehlermeldung.

7 Abhängigkeiten erkennen

Kommen wir zu einem interessanteren Feature: der Erkennung von Abhängigkeiten. Wenn sich an unserem Programm etwas geändert hat und wir es neu kompilieren, dann wollen wir, dass nur das nötigste neu übersetzt wird und Teile, die sich nicht geändert haben, unangetastet bleiben.

Code:
1
2
3
4
5
6
7
8
9
10
1
2
3
4
5
6
7
8
9
10
gpc@darkstar:~/tmp$ scons
scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Building targets ...
g++ -c -o hello_world.o hello_world.cpp
g++ -o hello_world hello_world.o
scons: done building targets.

gpc@darkstar:~/tmp$ scons -Q
scons: `.' is up to date.
Code:
1
2
3
4
5
6
7
8
9
10
gpc@darkstar:~/tmp$ scons
scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Building targets ...
g++ -c -o hello_world.o hello_world.cpp
g++ -o hello_world hello_world.o
scons: done building targets.

gpc@darkstar:~/tmp$ scons -Q
scons: `.' is up to date.
Code:
1
2
3
4
5
6
7
8
9
10
gpc@darkstar:~/tmp$ scons
scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Building targets ...
g++ -c -o hello_world.o hello_world.cpp
g++ -o hello_world hello_world.o
scons: done building targets.

gpc@darkstar:~/tmp$ scons -Q
scons: `.' is up to date.


SCons hat erkannt, dass seit dem letzten Build keine Änderungen stattgefunden haben, und hat gar nichts gemacht. Soweit so gut.
Normalerweise erkennt SCons eine Änderung am Inhalt der Datei, nicht am Datum des letzten Zugriffs (wie make es macht).

Will man das Verhalten von make, muss man die SourceSignatures-Funktion benutzen:
Code:
1
2
3
4
5
6
7
8
1
2
3
4
5
6
7
8
#SConstruct
Program("hello_world.cpp")

#Standardverhalten:
#SourceSignatures("MD5")

#make-Verhalten:
SourceSignatures("timestamp")
Code:
1
2
3
4
5
6
7
8
#SConstruct
Program("hello_world.cpp")

#Standardverhalten:
#SourceSignatures("MD5")

#make-Verhalten:
SourceSignatures("timestamp")
Code:
1
2
3
4
5
6
7
8
#SConstruct
Program("hello_world.cpp")

#Standardverhalten:
#SourceSignatures("MD5")

#make-Verhalten:
SourceSignatures("timestamp")


Sofern ein Target von einem anderen abhängt (z.B. Programm von Bibliothek), kann man auch hier festlegen, anhand welcher Kriterien SCons über einen Rebuild entscheidet. Dies geschieht mittels der TargetSignatures-Funktion.
Das Standardverhalten ist, dass SCons anhand einer Target-Signatur, die aus den Signaturen der Quelldateien besteht, entscheidet, ob ein Rebuild notwendig ist:
Code:
Program("hello_world.cpp")
TargetSignatures("build")
Code:
Program("hello_world.cpp")
TargetSignatures("build")
Code:
Program("hello_world.cpp")
TargetSignatures("build")


Wenn sich eine Quelldatei aber so verändert hat, dass der Inhalt des Targets der gleiche bleibt, dann ist kein kompletter Rebuild notwendig. Dieses Verhalten kann man so erreichen:
Code:
Program("hello_world.cpp")
TargetSignatures("content")
Code:
Program("hello_world.cpp")
TargetSignatures("content")
Code:
Program("hello_world.cpp")
TargetSignatures("content")



Nun ist es gängige Praxis, die Schnittstelle einer Klasse in eine Header-Datei zu schreiben und die Implementation in eine Source-Datei auszulagern. Sehen wir uns folgendes kleines Programm an:
C/C++ Code:
1
2
3
4
5
6
7
8
9
1
2
3
4
5
6
7
8
9
//foo.hpp
#ifndef
FOO_HPP
#define
FOO_HPP

struct Foo {
  void bar() const;
};

#endif
C/C++ Code:
1
2
3
4
5
6
7
8
9
//foo.hpp
#ifndef
FOO_HPP
#define
FOO_HPP

struct Foo {
void bar() const;
};

#endif
C/C++ Code:
1
2
3
4
5
6
7
8
9
//foo.hpp
#ifndef
FOO_HPP
#define
FOO_HPP

struct Foo {
  void bar() const;
};

#endif

C/C++ Code:
//foo.cpp
#include
"foo.hpp"
#include
<iostream>

void Foo::bar() const {
  std::cout<<"Blub blub\n";
};
C/C++ Code:
//foo.cpp
#include
"foo.hpp"
#include
<iostream>

void Foo::bar() const {
std::cout<<"Blub blub\n";
};
C/C++ Code:
//foo.cpp
#include
"foo.hpp"
#include
<iostream>

void Foo::bar() const {
  std::cout<<"Blub blub\n";
};

C/C++ Code:
1
2
3
4
5
6
7
8
1
2
3
4
5
6
7
8
//hello_world.cpp
#include
"foo.hpp"

int main() {
  Foo f;
  f.bar();
  return 0;
};
C/C++ Code:
1
2
3
4
5
6
7
8
//hello_world.cpp
#include
"foo.hpp"

int main() {
Foo f;
f.bar();
return 0;
};
C/C++ Code:
1
2
3
4
5
6
7
8
//hello_world.cpp
#include
"foo.hpp"

int main() {
  Foo f;
  f.bar();
  return 0;
};

Wenn sich an foo.hpp etwas ändert, will ich, dass SCons das erkennt und einen Rebuild durchführt, und das sage ich SCons mit der CPPPATH-Variable:
Code:
#SConstruct
Program(['hello_world.cpp','foo.cpp'], CPPPATH = '.')
Code:
#SConstruct
Program(['hello_world.cpp','foo.cpp'], CPPPATH = '.')
Code:
#SConstruct
Program(['hello_world.cpp','foo.cpp'], CPPPATH = '.')

Nun sieht SCons im aktuellen (".") Verzeichnis nach, um herauszufinden ob ein Rebuild notwendig ist.
Analog zu der LIBPATH-Variable, kann die CPPPATH-Variable eine Liste sein:
Code:
#SConstruct
Program('hello_world.cpp', CPPPATH = ['.', '/home/gpc/tmp/include'])
Code:
#SConstruct
Program('hello_world.cpp', CPPPATH = ['.', '/home/gpc/tmp/include'])
Code:
#SConstruct
Program('hello_world.cpp', CPPPATH = ['.', '/home/gpc/tmp/include'])



8 Auf Abhängigkeiten testen

Viele Programme benutzen externe Bibliotheken, um fehlende Funktionalität einer Standardbibliothek auszugleichen. Wenn der Benutzer unser Programm benutzen will, dann braucht er ebenfalls diese Bibliothek, um das Programm erfolgreich kompilieren zu können. Hat er die Bibliothek nicht installiert, wird der Kompiliervorgang abbrechen, da der Compiler die Abhängigkeiten nicht auflösen kann. Nicht sehr schön. Eine elegantere Lösung wäre doch, vorher abzufragen, ob Bibliothek Foo existiert, und bei Nichtvorhandensein eine entsprechende Fehlermeldung auszugeben (bei den autotools wird dies von autoconf erledigt). Um dies zu bewerkstelligen, müssen wir zuerst einen kleinen Abstecher zum Thema Construction Environments machen:

8.1 Construction Environments

Eine so genannte Construction Environment gibt uns die Möglichkeit zu bestimmen, wie unsere Programme erstellt werden, welcher Compiler genutzt wird oder welche Flags gesetzt werden.

Mit folgendem Python-Code kann man eine Construction Environment erstellen:
Code:
#SConstruct
env = Environment()
Code:
#SConstruct
env = Environment()
Code:
#SConstruct
env = Environment()

Hierbei wird die Construction Environment 'env' mit den auf dem System installierten Tools initialisiert, also wird z.B. bei installiertem g++ der CC-Wert auf g++ gesetzt. Ebenso sieht es mit dem Linker und den anderen für den Build-Prozess benötigten Daten aus.
Aber es ist eben immer wieder das gleiche mit Default-Werten: Meistens wollen wir spezielle Parameter übergeben. Nun, das ist ein Leichtes:
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#SConstruct
env = Environment(CC = 'gcc', CCFLAGS = '-O2 -g')

#Ersetzen von gcc durch g++, ist ja ein cpp file
env.Replace(CC = 'g++')

#Anhaengen von -W Option
#Wuerde CCFLAGS noch nicht existieren, wuerde es angelegt werden
env.Append(CCFLAGS = ' -W')

#Alternativ kann man Prepend zum Voranstellen benutzen:
env.Prepend(CCFLAGS = ' -Weffc++ ')

#Achtung: Neuer Aufruf:
env.Program('main.cpp')
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#SConstruct
env = Environment(CC = 'gcc', CCFLAGS = '-O2 -g')

#Ersetzen von gcc durch g++, ist ja ein cpp file
env.Replace(CC = 'g++')

#Anhaengen von -W Option
#Wuerde CCFLAGS noch nicht existieren, wuerde es angelegt werden
env.Append(CCFLAGS = ' -W')

#Alternativ kann man Prepend zum Voranstellen benutzen:
env.Prepend(CCFLAGS = ' -Weffc++ ')

#Achtung: Neuer Aufruf:
env.Program('main.cpp')
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#SConstruct
env = Environment(CC = 'gcc', CCFLAGS = '-O2 -g')

#Ersetzen von gcc durch g++, ist ja ein cpp file
env.Replace(CC = 'g++')

#Anhaengen von -W Option
#Wuerde CCFLAGS noch nicht existieren, wuerde es angelegt werden
env.Append(CCFLAGS = ' -W')

#Alternativ kann man Prepend zum Voranstellen benutzen:
env.Prepend(CCFLAGS = ' -Weffc++ ')

#Achtung: Neuer Aufruf:
env.Program('main.cpp')


An der Ausgabe erkennen wir, dass scons unsere Angaben übernommen hat:
Code:
bash-3.00$ scons -Q
g++ -Weffc++ -O2 -g -W -c -o main.o main.cpp
g++ -o main main.o
Code:
bash-3.00$ scons -Q
g++ -Weffc++ -O2 -g -W -c -o main.o main.cpp
g++ -o main main.o
Code:
bash-3.00$ scons -Q
g++ -Weffc++ -O2 -g -W -c -o main.o main.cpp
g++ -o main main.o


Eine Construction Environment verfügt, vereinfacht gesagt, über ein so genanntes Dictionary (assoziatives Array), welches u.a. die CC-Variable mit dem zugeordneten Wert 'g++' beinhaltet. Somit kann man leicht nachschauen, welchen Wert die einzelnen Variablen haben. Geben wir doch einfach mal alle in der Shell aus:
Code:
1
2
3
4
5
6
7
8
9
1
2
3
4
5
6
7
8
9
#SConstruct
env = Environment()

#Einzelnes Element auswaehlen und ausgeben
print "Used compiler: ", env['CC']

dict = env.Dictionary() #Dictionary holen
for i,j in dict.iteritems(): #Über alle Elemente iterieren
  print i,j             #...und Key-Value-Paar ausgeben
Code:
1
2
3
4
5
6
7
8
9
#SConstruct
env = Environment()

#Einzelnes Element auswaehlen und ausgeben
print "Used compiler: ", env['CC']

dict = env.Dictionary() #Dictionary holen
for i,j in dict.iteritems(): #Über alle Elemente iterieren
print i,j #...und Key-Value-Paar ausgeben
Code:
1
2
3
4
5
6
7
8
9
#SConstruct
env = Environment()

#Einzelnes Element auswaehlen und ausgeben
print "Used compiler: ", env['CC']

dict = env.Dictionary() #Dictionary holen
for i,j in dict.iteritems(): #Über alle Elemente iterieren
  print i,j             #...und Key-Value-Paar ausgeben


Mit mehreren Construction Environments können wir auch mehrere Verhaltensmuster nachbilden, um z.B. einen Debug- und einen Release-Vorgang zu erstellen:
Code:
#SConstruct
release = Environment(CCFLAGS = '-O2 -Os')
debug = Environment(CCFLAGS = '-g')

release.Program('foo', 'foo.cpp')
debug.Program('bar', 'bar.cpp')
Code:
#SConstruct
release = Environment(CCFLAGS = '-O2 -Os')
debug = Environment(CCFLAGS = '-g')

release.Program('foo', 'foo.cpp')
debug.Program('bar', 'bar.cpp')
Code:
#SConstruct
release = Environment(CCFLAGS = '-O2 -Os')
debug = Environment(CCFLAGS = '-g')

release.Program('foo', 'foo.cpp')
debug.Program('bar', 'bar.cpp')


8.2 Auf Bibliotheken/Header/Funktionen/typedefs testen

Was wollten wir eigentlich in diesem Abschnitt erreichen? Richtig: Prüfen auf Abhängigkeiten. Jetzt sind wir in der Lage, dies zu tun.
Zuerst erstellen wir wie gehabt unsere Construction Environment. Dann verknüpfen wir einen Configure Context mit der Construction Environment, um die Tests durchzuführen, und beenden den Vorgang schließlich mit der Finish()-Methode:
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#SConstruct
env = Environment()
conf = Configure(env)

#Auf Bibliothek pruefen
if not conf.CheckLib('gdk'):
  print "Couldn't find gdk library. Exiting."
  Exit(1)

#Auf C-Header pruefen
if not conf.CheckCHeader('assert.h'):
  print "Couldn't find assert.h. Exiting."
  Exit(1)

#Bei Vorhandensein von Header Flag mitgeben
if conf.CheckCHeader('myHeader.h'):
  conf.env.Append('-DHAS_MY_HEADER_H')

#Auf CPP-Header pruefen
if not conf.CheckCXXHeader('list'):
  print "Couldn't find list. Exiting."
  Exit(1)

#Auf Funktion testen
if not conf.CheckFunc('memset'):
  print "Couldn't find memset. Exiting"
  Exit(1)

#Auf typedef testen
if not conf.CheckType('time_t', '#include <time.h>\n'):
  print "Couldn't find typedef time_t. Using long instead."
  conf.env.Append(CCFLAGS = '-Dtime_t=long')

env.Program('main.cpp')
 
env = conf.Finish()
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#SConstruct
env = Environment()
conf = Configure(env)

#Auf Bibliothek pruefen
if not conf.CheckLib('gdk'):
print "Couldn't find gdk library. Exiting."
Exit(1)

#Auf C-Header pruefen
if not conf.CheckCHeader('assert.h'):
print "Couldn't find assert.h. Exiting."
Exit(1)

#Bei Vorhandensein von Header Flag mitgeben
if conf.CheckCHeader('myHeader.h'):
conf.env.Append('-DHAS_MY_HEADER_H')

#Auf CPP-Header pruefen
if not conf.CheckCXXHeader('list'):
print "Couldn't find list. Exiting."
Exit(1)

#Auf Funktion testen
if not conf.CheckFunc('memset'):
print "Couldn't find memset. Exiting"
Exit(1)

#Auf typedef testen
if not conf.CheckType('time_t', '#include <time.h>\n'):
print "Couldn't find typedef time_t. Using long instead."
conf.env.Append(CCFLAGS = '-Dtime_t=long')

env.Program('main.cpp')

env = conf.Finish()
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#SConstruct
env = Environment()
conf = Configure(env)

#Auf Bibliothek pruefen
if not conf.CheckLib('gdk'):
  print "Couldn't find gdk library. Exiting."
  Exit(1)

#Auf C-Header pruefen
if not conf.CheckCHeader('assert.h'):
  print "Couldn't find assert.h. Exiting."
  Exit(1)

#Bei Vorhandensein von Header Flag mitgeben
if conf.CheckCHeader('myHeader.h'):
  conf.env.Append('-DHAS_MY_HEADER_H')

#Auf CPP-Header pruefen
if not conf.CheckCXXHeader('list'):
  print "Couldn't find list. Exiting."
  Exit(1)

#Auf Funktion testen
if not conf.CheckFunc('memset'):
  print "Couldn't find memset. Exiting"
  Exit(1)

#Auf typedef testen
if not conf.CheckType('time_t', '#include <time.h>\n'):
  print "Couldn't find typedef time_t. Using long instead."
  conf.env.Append(CCFLAGS = '-Dtime_t=long')

env.Program('main.cpp')
 
env = conf.Finish()


Es ist auch ohne weiteres möglich, benutzerdefinierte Tests zu bauen, aber dies würde den Rahmen sprengen, deshalb muss ich ein weiteres Mal auf das Tutorial verweisen.

9 Zum Schluss

Das war jetzt nur eine kleine Vorstellung von dem, was SCons kann. Alles darzustellen war aber auch nicht mein Anspruch, deshalb sei an dieser Stelle noch mal auf das offizielle SCons-Tutorial verwiesen, welches sehr viele Informationen enthält und auf (fast) alle Fragen eine Antwort bietet.

10 What comes next...

Im nächsten Artikel wird uns Artchi dann bjam, das Build-System von boost, erklären.


Zuletzt bearbeitet von GPC am 11:23:28 15.11.2006, insgesamt 4-mal bearbeitet
joomoo
Autor

Benutzerprofil
Anmeldungsdatum: 31.07.2004
Beiträge: 1520
Beitrag joomoo Autor 18:23:33 03.05.2007   Titel:              Zitieren

Klasse Artikel! Vielen Dank für die Einführung, SCons gefällt mir wahnsinnig gut!
Endlich macht programmieren unter Linux richtig Spaß!

mfg.
GPC
Moderator

Benutzerprofil
Anmeldungsdatum: 11.07.2004
Beiträge: 6290
Beitrag GPC Moderator 19:02:28 03.05.2007   Titel:              Zitieren

:)
Mr. N
Mitglied

Benutzerprofil
Anmeldungsdatum: 28.12.2001
Beiträge: 4331
Beitrag Mr. N Mitglied 19:19:07 03.05.2007   Titel:              Zitieren

Ich möchte nur mal die Idylle zerstören:

SCons hat ein paar Probleme, gerade was Konfiguration von externen Komponenten angeht. Es ist recht schwierig, mit SCons ein modulares Projekt aufzusetzen. Außerdem wird es sehr schnell sehr langsam (IIRC ~10-20 Sekunden Startzeit für ein mittelgroßes Projekt, 40 KLOC).

Ich kenne zwei Projekte, die SCons erwägt, aber dann doch irgendwann verworfen haben:
  • KDE 4
  • GOTT (:D)


Andererseits ist SCons natürlich viel angenehmer als zum Beispiel Makefiles oder auch autotools.
__HIRSCH_H__
Mitglied

Benutzerprofil
Anmeldungsdatum: 16.05.2006
Beiträge: 63
Beitrag __HIRSCH_H__ Mitglied 10:21:13 04.05.2007   Titel:              Zitieren

Mr. N schrieb:
Ich möchte nur mal die Idylle zerstören:

SCons hat ein paar Probleme, gerade was Konfiguration von externen Komponenten angeht. Es ist recht schwierig, mit SCons ein modulares Projekt aufzusetzen. Außerdem wird es sehr schnell sehr langsam (IIRC ~10-20 Sekunden Startzeit für ein mittelgroßes Projekt, 40 KLOC).

Kann ich so nicht bestätigen, ich hab hier ein Projekt mit 41kloc uns das sind die Timings für einen Rebuild ohne Änderung:

Code:
Total build time: 3.545805 seconds
Total SConscript file execution time: 1.095926 seconds
Total SCons execution time: 2.449879 seconds
Total command execution time: 0.000000 seconds
Code:
Total build time: 3.545805 seconds
Total SConscript file execution time: 1.095926 seconds
Total SCons execution time: 2.449879 seconds
Total command execution time: 0.000000 seconds
Code:
Total build time: 3.545805 seconds
Total SConscript file execution time: 1.095926 seconds
Total SCons execution time: 2.449879 seconds
Total command execution time: 0.000000 seconds


Es stimmt das Scons bei grossen Projekten langsam wird und nicht so gut skaliert, aber das beginnt erst bei einer anderen Dimension.

Das mit dem Problem bei modularen Projekten kann ich so auch nicht bestätigen sondern eher umkehren, wir haben hier ein Projekt aus 20 einzelnen Plugin-Bibliotheken die teilweise voneinander abhängen, da ist es sehr konfortabel dass SCons sich die Checksummen der Bibs merkt und bei Ändering die abhängigen Bibs neu baut.
In Zukünfigen Versionen soll sogar nur neu gebaut werden wenn die Binärkompatibilität nicht mehr gewährleistet ist (also sich Funktionssignaturen geändert haben). Das kann meines wissens kein anderes build-tool.


Zuletzt bearbeitet von __HIRSCH_H__ am 10:22:36 04.05.2007, insgesamt 1-mal bearbeitet
Mr. N at work
Unregistrierter




Beitrag Mr. N at work Unregistrierter 10:47:18 04.05.2007   Titel:              Zitieren

__HIRSCH_H__ schrieb:
Mr. N schrieb:
Ich möchte nur mal die Idylle zerstören:

SCons hat ein paar Probleme, gerade was Konfiguration von externen Komponenten angeht. Es ist recht schwierig, mit SCons ein modulares Projekt aufzusetzen. Außerdem wird es sehr schnell sehr langsam (IIRC ~10-20 Sekunden Startzeit für ein mittelgroßes Projekt, 40 KLOC).

Kann ich so nicht bestätigen, ich hab hier ein Projekt mit 41kloc uns das sind die Timings für einen Rebuild ohne Änderung:

Code:
Total build time: 3.545805 seconds
Total SConscript file execution time: 1.095926 seconds
Total SCons execution time: 2.449879 seconds
Total command execution time: 0.000000 seconds
Code:
Total build time: 3.545805 seconds
Total SConscript file execution time: 1.095926 seconds
Total SCons execution time: 2.449879 seconds
Total command execution time: 0.000000 seconds
Code:
Total build time: 3.545805 seconds
Total SConscript file execution time: 1.095926 seconds
Total SCons execution time: 2.449879 seconds
Total command execution time: 0.000000 seconds



Entweder stimmt da meine Erinnerung nicht, oder die Projekte sind etwas unterschiedlich strukturiert.

__HIRSCH_H__ schrieb:

Es stimmt das Scons bei grossen Projekten langsam wird und nicht so gut skaliert, aber das beginnt erst bei einer anderen Dimension.

Das mit dem Problem bei modularen Projekten kann ich so auch nicht bestätigen sondern eher umkehren, wir haben hier ein Projekt aus 20 einzelnen Plugin-Bibliotheken die teilweise voneinander abhängen, da ist es sehr konfortabel dass SCons sich die Checksummen der Bibs merkt und bei Ändering die abhängigen Bibs neu baut.


Das war ja auch nicht das Problem. Das Problem war das Konfigurieren der Libs. Man muss sich ja quasi für jedes Modul ein eigenes Environment basteln und 20-mal pkg-config aufrufen find ich nicht so geil.

(Unser Workaround war dann, mittels -Wl,--as-needed nur die Bibliotheken zu linken, von denen mindestens eine Funktion verwendet wurde. Ist allerdings sehr... Non-GNU binutils oder vielleicht auch nur solche für Nicht-ELF-Plattformen würde ich dafür nicht unbedingt verwenden wollen. Es gab natürlich immer noch drei unterschiedliche Environments...)

__HIRSCH_H__ schrieb:

In Zukünfigen Versionen soll sogar nur neu gebaut werden wenn die Binärkompatibilität nicht mehr gewährleistet ist (also sich Funktionssignaturen geändert haben). Das kann meines wissens kein anderes build-tool.


Wie, wird SCons jetzt wieder weiterentwickelt? :eek:
__HIRSCH_H__
Mitglied

Benutzerprofil
Anmeldungsdatum: 16.05.2006
Beiträge: 63
Beitrag __HIRSCH_H__ Mitglied 10:54:10 04.05.2007   Titel:              Zitieren

Zitat:
Wie, wird SCons jetzt wieder weiterentwickelt? :eek:

Das wurde eigentlich nie eingestellt, die letzte Version iss vom 12.04.2007. Man sollte auch die "latest" version nehmen, nicht die "stable" weil die hat schon n paar Jahre aufm Buckel. Das iss vll. auch der Grund für die unterschiedlichen Eindrücke im Bezug auf die Geschwindigkeit.
Mr. N at work
Unregistrierter




Beitrag Mr. N at work Unregistrierter 11:21:29 04.05.2007   Titel:              Zitieren

__HIRSCH_H__ schrieb:
Zitat:
Wie, wird SCons jetzt wieder weiterentwickelt? :eek:

Das wurde eigentlich nie eingestellt, die letzte Version iss vom 12.04.2007. Man sollte auch die "latest" version nehmen, nicht die "stable" weil die hat schon n paar Jahre aufm Buckel. Das iss vll. auch der Grund für die unterschiedlichen Eindrücke im Bezug auf die Geschwindigkeit.


Nee das war die "latest" von vor einem halben Jahr. Ich erinnere mich, dass die "latest" LoadableModule hatte und die "stable" nicht.
joomoo
Autor

Benutzerprofil
Anmeldungsdatum: 31.07.2004
Beiträge: 1520
Beitrag joomoo Autor 20:46:43 09.05.2007   Titel:              Zitieren

Ich hab gerade noch eine Frage die ich nicht mit google oder in der Doku finden konnte:

Wenn ich früher mit g++ `pkg-config gtkmm --cflags --libs` kompiliert habe, wie sieht das in der SConstruct dann aus?
Also, dass SCons fürs Kompilieren ein `pkg-config gtkmm --cflags` mit dranhängt und beim Linken ein `pkg-config gtkmm --libs`?
GPC
Moderator

Benutzerprofil
Anmeldungsdatum: 11.07.2004
Beiträge: 6290
Beitrag GPC Moderator 20:53:51 09.05.2007   Titel:              Zitieren

Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
env = Environment(CCFLAGS = '-O2')

def CheckPKGConfig(context, version):
     context.Message( 'Checking for pkg-config... ' )
     ret = context.TryAction('pkg-config --atleast-pkgconfig-version=%s' % version)[0]
     context.Result( ret )
     return ret

def CheckPKG(context, name):
     context.Message( 'Checking for %s... ' % name )
     ret = context.TryAction('pkg-config --exists \'%s\'' % name)[0]
     context.Result( ret )
     return ret

# Den check auf pkg-config und gtkmm hab ich auskommentiert
# Configuration:
#conf = Configure(env, custom_tests = { 'CheckPKGConfig' : CheckPKGConfig,
#                                       'CheckPKG' : CheckPKG })
#if not conf.CheckPKGConfig('0.15.0'):
#     print 'pkg-config >= 0.15.0 not found.'
#     Exit(1)
#if not conf.CheckPKG('gtkmm-2.4 >= 2.6.2'):
#     print 'gtkmm-2.4 >= 2.6.2 not found.'
#     Exit(1)
#
# Your extra checks here
#env = conf.Finish()

# Now build:

#Vermutlich kommen hier noch weiter Dateien rein :-D
sourcelist = Split("""
                        src/main.cpp
                   """)

env.ParseConfig('pkg-config --cflags --libs gtkmm-2.4')
env.Program('main', sourcelist)
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
env = Environment(CCFLAGS = '-O2')

def CheckPKGConfig(context, version):
context.Message( 'Checking for pkg-config... ' )
ret = context.TryAction('pkg-config --atleast-pkgconfig-version=%s' % version)[0]
context.Result( ret )
return ret

def CheckPKG(context, name):
context.Message( 'Checking for %s... ' % name )
ret = context.TryAction('pkg-config --exists \'%s\'' % name)[0]
context.Result( ret )
return ret

# Den check auf pkg-config und gtkmm hab ich auskommentiert
# Configuration:
#conf = Configure(env, custom_tests = { 'CheckPKGConfig' : CheckPKGConfig,
# 'CheckPKG' : CheckPKG })
#if not conf.CheckPKGConfig('0.15.0'):
# print 'pkg-config >= 0.15.0 not found.'
# Exit(1)
#if not conf.CheckPKG('gtkmm-2.4 >= 2.6.2'):
# print 'gtkmm-2.4 >= 2.6.2 not found.'
# Exit(1)
#
# Your extra checks here
#env = conf.Finish()

# Now build:

#Vermutlich kommen hier noch weiter Dateien rein :-D
sourcelist = Split("""
src/main.cpp
""")

env.ParseConfig('pkg-config --cflags --libs gtkmm-2.4')
env.Program('main', sourcelist)
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
env = Environment(CCFLAGS = '-O2')

def CheckPKGConfig(context, version):
     context.Message( 'Checking for pkg-config... ' )
     ret = context.TryAction('pkg-config --atleast-pkgconfig-version=%s' % version)[0]
     context.Result( ret )
     return ret

def CheckPKG(context, name):
     context.Message( 'Checking for %s... ' % name )
     ret = context.TryAction('pkg-config --exists \'%s\'' % name)[0]
     context.Result( ret )
     return ret

# Den check auf pkg-config und gtkmm hab ich auskommentiert
# Configuration:
#conf = Configure(env, custom_tests = { 'CheckPKGConfig' : CheckPKGConfig,
#                                       'CheckPKG' : CheckPKG })
#if not conf.CheckPKGConfig('0.15.0'):
#     print 'pkg-config >= 0.15.0 not found.'
#     Exit(1)
#if not conf.CheckPKG('gtkmm-2.4 >= 2.6.2'):
#     print 'gtkmm-2.4 >= 2.6.2 not found.'
#     Exit(1)
#
# Your extra checks here
#env = conf.Finish()

# Now build:

#Vermutlich kommen hier noch weiter Dateien rein :-D
sourcelist = Split("""
                        src/main.cpp
                   """)

env.ParseConfig('pkg-config --cflags --libs gtkmm-2.4')
env.Program('main', sourcelist)
joomoo
Autor

Benutzerprofil
Anmeldungsdatum: 31.07.2004
Beiträge: 1520
Beitrag joomoo Autor 21:09:46 09.05.2007   Titel:              Zitieren

Danke, klappt!
Kann man auch noch einstellen, dass --cflags beim build und --libs nur beim linken genommen wird?
GPC
Moderator

Benutzerprofil
Anmeldungsdatum: 11.07.2004
Beiträge: 6290
Beitrag GPC Moderator 21:40:47 09.05.2007   Titel:              Zitieren

Bin ich im Moment überfragt, sorry.
joomoo
Autor

Benutzerprofil
Anmeldungsdatum: 31.07.2004
Beiträge: 1520
Beitrag joomoo Autor 00:01:33 05.06.2007   Titel:              Zitieren

Ich hab's hingekriegt:

C/C++ Code:
env.Append(CCFLAGS = '`pkg-config glib-2.0 --cflags`', LINKFLAGS = '`pkg-config glib-2.0 --libs`')
C/C++ Code:
env.Append(CCFLAGS = '`pkg-config glib-2.0 --cflags`', LINKFLAGS = '`pkg-config glib-2.0 --libs`')
C/C++ Code:
env.Append(CCFLAGS = '`pkg-config glib-2.0 --cflags`', LINKFLAGS = '`pkg-config glib-2.0 --libs`')


Funktioniert wunderbar, das gute ist, dass man jetzt auch nicht mehr so lange Compilier-Zeilen hat.

mfg.
GPC
Moderator

Benutzerprofil
Anmeldungsdatum: 11.07.2004
Beiträge: 6290
Beitrag GPC Moderator 07:29:10 05.06.2007   Titel:              Zitieren

cool, danke :live:
joomoo
Autor

Benutzerprofil
Anmeldungsdatum: 31.07.2004
Beiträge: 1520
Beitrag joomoo Autor 00:08:09 23.06.2007   Titel:              Zitieren

Noch eine kleine Korrektur:

C/C++ Code:
env.Append(CCFLAGS = '`pkg-config glib-2.0 --cflags`', _LIBFLAGS = ' `pkg-config glib-2.0 --libs`')
C/C++ Code:
env.Append(CCFLAGS = '`pkg-config glib-2.0 --cflags`', _LIBFLAGS = ' `pkg-config glib-2.0 --libs`')
C/C++ Code:
env.Append(CCFLAGS = '`pkg-config glib-2.0 --cflags`', _LIBFLAGS = ' `pkg-config glib-2.0 --libs`')


Damit funktioniert's auch mit statischen Libraries. Das einzige was mich noch stört ist das Leerzeichen was man an den Anfang von _LIBFLAGS setzen muss.
nash35
Unregistrierter




Beitrag nash35 Unregistrierter 21:42:37 11.08.2007   Titel:              Zitieren

weiß jemand wie ich scons wieder löschen kann??
bei removescons bekomme ich nur eine Fehlermeldung das das programm zurzeit läuft.
GPC
Moderator

Benutzerprofil
Anmeldungsdatum: 11.07.2004
Beiträge: 6290
Beitrag GPC Moderator 21:47:53 11.08.2007   Titel:              Zitieren

Welches OS verwendest du denn?
nash35
Unregistrierter




Beitrag nash35 Unregistrierter 14:29:27 12.08.2007   Titel:              Zitieren

ich verwende WinXP.

ich hab glaube totalen mist gebaut bei der installation.

zuerst hab ich es in eine Python installation(2.2) die es schon auf dem system gab installiert. nur da passierte nichts wenn ich scons in der shell eingegeben habe.

dachte es liegt an Python habe python 2.5 installiert und scons auchnochmal.
wieder konnte ich scons nicht starten.
dann habe ich den pfad von python2.5 zum systempfad hinzugefügt.
dann kannte er den befehl scons, nur leider will er bei mir nun das scons öffnen das ich bei python 2.2 installiert hatte...leck.

jetzt möchte ich gerne wissen wie ich das scons schön wieder vom rechner entfernen kann.
joomoo
Autor

Benutzerprofil
Anmeldungsdatum: 31.07.2004
Beiträge: 1520
Beitrag joomoo Autor 14:41:33 12.08.2007   Titel:              Zitieren

nash35 schrieb:
ich verwende WinXP.

ich hab glaube totalen mist gebaut bei der installation.

zuerst hab ich es in eine Python installation(2.2) die es schon auf dem system gab installiert. nur da passierte nichts wenn ich scons in der shell eingegeben habe.

dachte es liegt an Python habe python 2.5 installiert und scons auchnochmal.
wieder konnte ich scons nicht starten.
dann habe ich den pfad von python2.5 zum systempfad hinzugefügt.
dann kannte er den befehl scons, nur leider will er bei mir nun das scons öffnen das ich bei python 2.2 installiert hatte...leck.

jetzt möchte ich gerne wissen wie ich das scons schön wieder vom rechner entfernen kann.

Wie hast du scons installiert?
nash35
Unregistrierter




Beitrag nash35 Unregistrierter 15:56:31 12.08.2007   Titel:              Zitieren

einfach den aktuellen Win32 installer benutzt
joomoo
Autor

Benutzerprofil
Anmeldungsdatum: 31.07.2004
Beiträge: 1520
Beitrag joomoo Autor 15:58:48 12.08.2007   Titel:              Zitieren

nash35 schrieb:
einfach den aktuellen Win32 installer benutzt

Dann deinstallierst du in dem du in den Systemsteurungen auf Software gehst und dann SCons rauspickst.
nash35
Unregistrierter




Beitrag nash35 Unregistrierter 18:25:11 12.08.2007   Titel:              Zitieren

damn jetzt taucht es doch in der Systemsteurungen auf.

Frage, muss man bei SCons noch irgendwas angeben einen Pfad zum compiler(habe VC 2005) oder so?
so richtig hab ich das mit der idee von scons nämlich noch nicht geblick..
GPC
Moderator

Benutzerprofil
Anmeldungsdatum: 11.07.2004
Beiträge: 6290
Beitrag GPC Moderator 01:01:52 13.08.2007   Titel:              Zitieren

nash35 schrieb:
Frage, muss man bei SCons noch irgendwas angeben einen Pfad zum compiler(habe VC 2005) oder so?
so richtig hab ich das mit der idee von scons nämlich noch nicht geblick..

SCons zieht sich den Pfad zum Visual C++ Compiler normalerweise aus den Umgebungsvariablen. Funktioniert denn dies nicht?
nash35
Unregistrierter




Beitrag nash35 Unregistrierter 09:47:27 13.08.2007   Titel:              Zitieren

nun ja ich möchte ein projekt mit scons kompilieren:

Zitat:
D:\Programmierung\Reflex>scons
scons: Reading SConscript files ...
ValueError: list.remove(x): x not in list:
File "D:\Programmierung\Reflex\SConstruct", line 18:
'python/SConscript'])
File "D:\Programmierung\Python25\scons-0.97\SCons\Script\SConscript.py", line
583:
return apply(method, args, kw)
File "D:\Programmierung\Python25\scons-0.97\SCons\Script\SConscript.py", line
520:
return apply(_SConscript, [self.fs,] + files, subst_kw)
File "D:\Programmierung\Python25\scons-0.97\SCons\Script\SConscript.py", line
245:
exec _file_ in call_stack[-1].globals
File "D:\Programmierung\Reflex\python\SConscript", line 58:
py_files.remove('./genreflex/genreflex-rootcint.py')
D:\Programmierung\Reflex>


und das kommt dabei raus?

hat jemand vielleicht ein anderes test projekt das ich kompilieren kann?
GPC
Moderator

Benutzerprofil
Anmeldungsdatum: 11.07.2004
Beiträge: 6290
Beitrag GPC Moderator 10:58:00 13.08.2007   Titel:              Zitieren

Gib bitte mal den Inhalt der SConstruct an.
nash35
Unregistrierter




Beitrag nash35 Unregistrierter 13:11:14 13.08.2007   Titel:              Zitieren

es handelt sich dabei um das Projekt hier
http://seal-reflex.web.cern.ch/seal-reflex/download.html

und das ist der inhalt der SConstruct
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from os import curdir
from os.path import realpath

opts = Options('custom.py')

opts.Add(BoolOption   ('test',    'Set to 1 if tests shall be compiled', 0))
opts.Add(PathOption   ('prefix',  'The installation directory of Reflex', realpath(curdir)))
opts.Add(PackageOption('gccxml',  'The >binary< directory of gccxml',     'no'))
opts.Add(PackageOption('cppunit', 'The >root< directory of CppUnit',      'no'))

env = Environment(options=opts)
Export('env')

Help(opts.GenerateHelpText(env))

SConscript(['src/SConscript',
            'inc/SConscript',
            'python/SConscript'])

if env['test'] : SConscript('test/SConscript')

env.Alias('install', env['prefix'])
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from os import curdir
from os.path import realpath

opts = Options('custom.py')

opts.Add(BoolOption ('test', 'Set to 1 if tests shall be compiled', 0))
opts.Add(PathOption ('prefix', 'The installation directory of Reflex', realpath(curdir)))
opts.Add(PackageOption('gccxml', 'The >binary< directory of gccxml', 'no'))
opts.Add(PackageOption('cppunit', 'The >root< directory of CppUnit', 'no'))

env = Environment(options=opts)
Export('env')

Help(opts.GenerateHelpText(env))

SConscript(['src/SConscript',
'inc/SConscript',
'python/SConscript'])

if env['test'] : SConscript('test/SConscript')

env.Alias('install', env['prefix'])
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from os import curdir
from os.path import realpath

opts = Options('custom.py')

opts.Add(BoolOption   ('test',    'Set to 1 if tests shall be compiled', 0))
opts.Add(PathOption   ('prefix',  'The installation directory of Reflex', realpath(curdir)))
opts.Add(PackageOption('gccxml',  'The >binary< directory of gccxml',     'no'))
opts.Add(PackageOption('cppunit', 'The >root< directory of CppUnit',      'no'))

env = Environment(options=opts)
Export('env')

Help(opts.GenerateHelpText(env))

SConscript(['src/SConscript',
            'inc/SConscript',
            'python/SConscript'])

if env['test'] : SConscript('test/SConscript')

env.Alias('install', env['prefix'])
nash35
Unregistrierter




Beitrag nash35 Unregistrierter 09:43:49 14.08.2007   Titel:              Zitieren

ihr könnt wohl auch nix damit anfangen, gell?

mhm ich hab an den Entwickler geschrieben, mal sehen was der dazu sagt.
GPC
Moderator

Benutzerprofil
Anmeldungsdatum: 11.07.2004
Beiträge: 6290
Beitrag GPC Moderator 09:49:37 14.08.2007   Titel:              Zitieren

nash35 schrieb:
ihr könnt wohl auch nix damit anfangen, gell?

Ne, ehrlich gesagt nicht.
joomoo
Autor

Benutzerprofil
Anmeldungsdatum: 31.07.2004
Beiträge: 1520
Beitrag joomoo Autor 22:03:49 02.12.2007   Titel:              Zitieren

Weiß jemand wie ich am einfachsten in verschiedenen *.in Dateien @VERSION@ durch eine Version ersetzen kann? Ich mach es jetzt so:

Code:
1
2
3
4
5
6
7
8
9
10
1
2
3
4
5
6
7
8
9
10
import os

version = "0.6.9"

#Replace @VERSION@ in certain files
files = ["jngl.pc.in", "autopackage/default.apspec.in"]
for filename in files:
    newfilename = filename.replace(".in", "")
    t = Command(newfilename, filename, "sed 's/@VERSION@/" + version + "/g' $SOURCE > $TARGET")
    Clean(t, newfilename) # Make sure scons -c does clean up tidily
Code:
1
2
3
4
5
6
7
8
9
10
import os

version = "0.6.9"

#Replace @VERSION@ in certain files
files = ["jngl.pc.in", "autopackage/default.apspec.in"]
for filename in files:
newfilename = filename.replace(".in", "")
t = Command(newfilename, filename, "sed 's/@VERSION@/" + version + "/g' $SOURCE > $TARGET")
Clean(t, newfilename) # Make sure scons -c does clean up tidily
Code:
1
2
3
4
5
6
7
8
9
10
import os

version = "0.6.9"

#Replace @VERSION@ in certain files
files = ["jngl.pc.in", "autopackage/default.apspec.in"]
for filename in files:
    newfilename = filename.replace(".in", "")
    t = Command(newfilename, filename, "sed 's/@VERSION@/" + version + "/g' $SOURCE > $TARGET")
    Clean(t, newfilename) # Make sure scons -c does clean up tidily


Nur das geht leider nur unter Linux (wegen dem sed-Befehl). Da muss es doch aus was vorgefertiges geben, oder?
BugJoe
Mitglied

Benutzerprofil
Anmeldungsdatum: 18.11.2002
Beiträge: 310
Beitrag BugJoe Mitglied 16:11:47 17.01.2008   Titel:              Zitieren

Ich hoffe ich bin hier richtig, oder sollte man zu Fragen doch lieber einen eigenen Thread auf machen?

Naja, zu meiner Frage. Und zwar löst SCons bei mir die Abhängigkeiten nicht richtig auf. Ich hab hier ein Projekt welches aus fünf Unterprojekten besteht. Vier dieser Unterprojekte erstellen jeweils eine statische Bibliothek und das fünfte Projekt (eine Binärdatei) bindet diese Biblotheken alle ein. Im Rootverzeichnis habe ich eine SConstruct Datei und in den Build-Verzeichnissen der Unterprojekte jeweils eine SConscript Datei.

Solange ich nur die 4 Bibliotheken erstellen lasse funktioniert auch alles wunderbar. Aber sobald ich das Projekt mit der Binärdatei, welche die Bibliotheken einbindet, mit aufnehmen kommt SCons aus dem Tritt. Es kompiliert nämlich das Projekt mit der Binärdatei einfach zwischen durch obwohl die Projekte mit den Bibliotheken noch gar nicht alle fertig sind. Logisch dass er dann die Binärdatei nicht gescheit linken kann wenn noch nicht alle Bibliotheken fertig sind.

Ich habe allerdings versucht meine SConstruct und SConscript Datei so portabel wie möglich zu gestalten damit man diese ohne viele verändern zu müssen auch in anderen Projekten verwenden kann. Evtl. ist mir dort ein grober Schnitzer unterlaufen. Hier mal meine Dateien:

SConstruct:
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# Projekte (Verzeichnisse) angeben in der Reihenfolge ihrer Abhaengigkeiten.
# Das erste Projekt ist das unabhaengigste.
# ["<Projektverzeichnis>", "<Verzeichnis in dem das SConscript liegt>"]
projects = [["lib1","/build"],
            ["lib2", "/build"],
            ["lib3", "/build"],
            ["lib4", "/build"],
            ["binary", ""]]


# Ab hier muss nichts mehr veraender werden
print "===================={ Beginne Buildvorgang }===================="
print "Projekte:"
for project, build_dir in projects:
    print "* " + project
print ""

projectList = ""
for project, build_dir in projects:
        projectList += project + build_dir + "/SConscript "

SConscript(Split(projectList))
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# Projekte (Verzeichnisse) angeben in der Reihenfolge ihrer Abhaengigkeiten.
# Das erste Projekt ist das unabhaengigste.
# ["<Projektverzeichnis>", "<Verzeichnis in dem das SConscript liegt>"]
projects = [["lib1","/build"],
["lib2", "/build"],
["lib3", "/build"],
["lib4", "/build"],
["binary", ""]]


# Ab hier muss nichts mehr veraender werden
print "===================={ Beginne Buildvorgang }===================="
print "Projekte:"
for project, build_dir in projects:
print "* " + project
print ""

projectList = ""
for project, build_dir in projects:
projectList += project + build_dir + "/SConscript "

SConscript(Split(projectList))
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# Projekte (Verzeichnisse) angeben in der Reihenfolge ihrer Abhaengigkeiten.
# Das erste Projekt ist das unabhaengigste.
# ["<Projektverzeichnis>", "<Verzeichnis in dem das SConscript liegt>"]
projects = [["lib1","/build"],
            ["lib2", "/build"],
            ["lib3", "/build"],
            ["lib4", "/build"],
            ["binary", ""]]


# Ab hier muss nichts mehr veraender werden
print "===================={ Beginne Buildvorgang }===================="
print "Projekte:"
for project, build_dir in projects:
    print "* " + project
print ""

projectList = ""
for project, build_dir in projects:
        projectList += project + build_dir + "/SConscript "

SConscript(Split(projectList))


SConscript:
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
from scons_utils import *

# ==============={ Projektspezifische Einstellungen }===============
# Die Dateiendung der Sourcecodedateien (z.B. *.cpp fuer C++)
SOURCE_EXTENSION = ".cpp"

# Der Name des zu erstellenden
OUTPUT_NAME      = "lib1"

# Verzeichnis in welches das fertige Kompilat hinterlegt werden soll
# (inkl. eventueller temporaerer Zwischendateien (z.B. *.obj))
BUILD_DIR        = "../build"

# Verzeichnis in dem die Sourcecodedateien liegen (normal "../src")
# Ein einfaches "." geht leider nicht weil damit die Funktion getSourceFiles() nicht klar kommt
SRC_DIR          = "../src"

# Verzeichnis in dem die Headerdateien liegen (normal "../include")
INCLUDE_DIR      = "../include"

# Pfad zu den Bibliotheken
LIB_PATH         = ""

# Benoetigte Bibliotheken
LIBS             = ""

# Typ des zu erstellenden Projekts
# OutputTypeEnum.bin(): Binaerdatei
# OutputTypeEnum.staticlib(): Statische Bibliothek (z.B. *.lib)
# OutputTypeEnum.sharedlib(): Dynamische Bibliothek (z.B. *.dll)
OUTPUT_TYPE      = OutputTypeEnum.staticlib()
# ==================================================================


# Buildprozess. Hier muss eigentlich nichts veraendert werden.
env = Environment()
env.BuildDir(BUILD_DIR, SRC_DIR, duplicate=0)
src_files = getSourceFiles(SRC_DIR, SOURCE_EXTENSION)
src_files = Split(src_files)

for i in range(0, len(src_files)):
    src_files[i] = BUILD_DIR + "/" + src_files[i]

objects = Object(src_files, CPPPATH = INCLUDE_DIR)
if OUTPUT_TYPE == OutputTypeEnum.bin():
    env.Program(OUTPUT_NAME, objects, LIBS = LIBS, LIBPATH = LIB_PATH)
elif OUTPUT_TYPE == OutputTypeEnum.staticlib():
    env.StaticLibrary(OUTPUT_NAME, objects, LIBS = LIBS, LIBPATH = LIB_PATH)
elif OUTPUT_TYPE == OutputTypeEnum.sharedlib():
    env.SharedLibrary(OUTPUT_NAME, objects, LIBS = LIBS, LIBPATH = LIB_PATH)
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
from scons_utils import *

# ==============={ Projektspezifische Einstellungen }===============
# Die Dateiendung der Sourcecodedateien (z.B. *.cpp fuer C++)
SOURCE_EXTENSION = ".cpp"

# Der Name des zu erstellenden
OUTPUT_NAME = "lib1"

# Verzeichnis in welches das fertige Kompilat hinterlegt werden soll
# (inkl. eventueller temporaerer Zwischendateien (z.B. *.obj))
BUILD_DIR = "../build"

# Verzeichnis in dem die Sourcecodedateien liegen (normal "../src")
# Ein einfaches "." geht leider nicht weil damit die Funktion getSourceFiles() nicht klar kommt
SRC_DIR = "../src"

# Verzeichnis in dem die Headerdateien liegen (normal "../include")
INCLUDE_DIR = "../include"

# Pfad zu den Bibliotheken
LIB_PATH = ""

# Benoetigte Bibliotheken
LIBS = ""

# Typ des zu erstellenden Projekts
# OutputTypeEnum.bin(): Binaerdatei
# OutputTypeEnum.staticlib(): Statische Bibliothek (z.B. *.lib)
# OutputTypeEnum.sharedlib(): Dynamische Bibliothek (z.B. *.dll)
OUTPUT_TYPE = OutputTypeEnum.staticlib()
# ==================================================================


# Buildprozess. Hier muss eigentlich nichts veraendert werden.
env = Environment()
env.BuildDir(BUILD_DIR, SRC_DIR, duplicate=0)
src_files = getSourceFiles(SRC_DIR, SOURCE_EXTENSION)
src_files = Split(src_files)

for i in range(0, len(src_files)):
src_files[i] = BUILD_DIR + "/" + src_files[i]

objects = Object(src_files, CPPPATH = INCLUDE_DIR)
if OUTPUT_TYPE == OutputTypeEnum.bin():
env.Program(OUTPUT_NAME, objects, LIBS = LIBS, LIBPATH = LIB_PATH)
elif OUTPUT_TYPE == OutputTypeEnum.staticlib():
env.StaticLibrary(OUTPUT_NAME, objects, LIBS = LIBS, LIBPATH = LIB_PATH)
elif OUTPUT_TYPE == OutputTypeEnum.sharedlib():
env.SharedLibrary(OUTPUT_NAME, objects, LIBS = LIBS, LIBPATH = LIB_PATH)
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
from scons_utils import *

# ==============={ Projektspezifische Einstellungen }===============
# Die Dateiendung der Sourcecodedateien (z.B. *.cpp fuer C++)
SOURCE_EXTENSION = ".cpp"

# Der Name des zu erstellenden
OUTPUT_NAME      = "lib1"

# Verzeichnis in welches das fertige Kompilat hinterlegt werden soll
# (inkl. eventueller temporaerer Zwischendateien (z.B. *.obj))
BUILD_DIR        = "../build"

# Verzeichnis in dem die Sourcecodedateien liegen (normal "../src")
# Ein einfaches "." geht leider nicht weil damit die Funktion getSourceFiles() nicht klar kommt
SRC_DIR          = "../src"

# Verzeichnis in dem die Headerdateien liegen (normal "../include")
INCLUDE_DIR      = "../include"

# Pfad zu den Bibliotheken
LIB_PATH         = ""

# Benoetigte Bibliotheken
LIBS             = ""

# Typ des zu erstellenden Projekts
# OutputTypeEnum.bin(): Binaerdatei
# OutputTypeEnum.staticlib(): Statische Bibliothek (z.B. *.lib)
# OutputTypeEnum.sharedlib(): Dynamische Bibliothek (z.B. *.dll)
OUTPUT_TYPE      = OutputTypeEnum.staticlib()
# ==================================================================


# Buildprozess. Hier muss eigentlich nichts veraendert werden.
env = Environment()
env.BuildDir(BUILD_DIR, SRC_DIR, duplicate=0)
src_files = getSourceFiles(SRC_DIR, SOURCE_EXTENSION)
src_files = Split(src_files)

for i in range(0, len(src_files)):
    src_files[i] = BUILD_DIR + "/" + src_files[i]

objects = Object(src_files, CPPPATH = INCLUDE_DIR)
if OUTPUT_TYPE == OutputTypeEnum.bin():
    env.Program(OUTPUT_NAME, objects, LIBS = LIBS, LIBPATH = LIB_PATH)
elif OUTPUT_TYPE == OutputTypeEnum.staticlib():
    env.StaticLibrary(OUTPUT_NAME, objects, LIBS = LIBS, LIBPATH = LIB_PATH)
elif OUTPUT_TYPE == OutputTypeEnum.sharedlib():
    env.SharedLibrary(OUTPUT_NAME, objects, LIBS = LIBS, LIBPATH = LIB_PATH)


scons_utils.py:
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import os, string

# Rekursives durchsuchen eines Verzeichnisses nach allen Sourcefiles.
# Durch die Rekursion werden alle Unterverzeichnisse mit druchsucht.
def getSourceFiles(dir, src_ext, subpath = ""):
    srcFiles = ""
   
    for node in os.listdir(dir):
        path = dir + "/" + node

        if node.endswith(src_ext):
            srcFiles += subpath + node + " "
        elif os.path.isdir(path):           
            subpath += node + "/"           
            srcFiles += getSourceFiles(path, src_ext, subpath)
    return srcFiles
   
# Enumeration fuer den Output-Typ
# bin():       Binary (z.B. *.exe)
# staticlib(): Statische Bibliothek (z.B. *.lib)
# sharedlib(): Dynamische Bibliothek (z.B. *.dll)
class OutputTypeEnum:
    @staticmethod
    def bin(): return "bin"
    @staticmethod
    def staticlib(): return "staticlib"
    @staticmethod
    def sharedlib(): return "sharedlib"
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import os, string

# Rekursives durchsuchen eines Verzeichnisses nach allen Sourcefiles.
# Durch die Rekursion werden alle Unterverzeichnisse mit druchsucht.
def getSourceFiles(dir, src_ext, subpath = ""):
srcFiles = ""

for node in os.listdir(dir):
path = dir + "/" + node

if node.endswith(src_ext):
srcFiles += subpath + node + " "
elif os.path.isdir(path):
subpath += node + "/"
srcFiles += getSourceFiles(path, src_ext, subpath)
return srcFiles

# Enumeration fuer den Output-Typ
# bin(): Binary (z.B. *.exe)
# staticlib(): Statische Bibliothek (z.B. *.lib)
# sharedlib(): Dynamische Bibliothek (z.B. *.dll)
class OutputTypeEnum:
@staticmethod
def bin(): return "bin"
@staticmethod
def staticlib(): return "staticlib"
@staticmethod
def sharedlib(): return "sharedlib"
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import os, string

# Rekursives durchsuchen eines Verzeichnisses nach allen Sourcefiles.
# Durch die Rekursion werden alle Unterverzeichnisse mit druchsucht.
def getSourceFiles(dir, src_ext, subpath = ""):
    srcFiles = ""
   
    for node in os.listdir(dir):
        path = dir + "/" + node

        if node.endswith(src_ext):
            srcFiles += subpath + node + " "
        elif os.path.isdir(path):           
            subpath += node + "/"           
            srcFiles += getSourceFiles(path, src_ext, subpath)
    return srcFiles
   
# Enumeration fuer den Output-Typ
# bin():       Binary (z.B. *.exe)
# staticlib(): Statische Bibliothek (z.B. *.lib)
# sharedlib(): Dynamische Bibliothek (z.B. *.dll)
class OutputTypeEnum:
    @staticmethod
    def bin(): return "bin"
    @staticmethod
    def staticlib(): return "staticlib"
    @staticmethod
    def sharedlib(): return "sharedlib"



Die scons_utils.py habe ich geschrieben um ein paar Dinge zu erleichtern, aber ich denke das ist alles selbsterklärend. Ich hoffe die Länge der Dateien ist nicht zu abschreckend ;)

Bei der SConscript Datei für das Binary werden zusätzlich halt noch die Bibliotheken bei der LIBS-Variablen mit angegeben.


Zuletzt bearbeitet von BugJoe am 17:37:49 17.01.2008, insgesamt 1-mal bearbeitet
GPC
Moderator

Benutzerprofil
Anmeldungsdatum: 11.07.2004
Beiträge: 6290
Beitrag GPC Moderator 00:26:38 20.01.2008   Titel:              Zitieren

Ehrlich gesagt weiß ich nicht, wie man dein Problem SCons-intern beheben kann, da SCons ja keine Reihenfolge für die Builds definiert... außer per Depends-Funktion, aber das ist nicht so das richtige Mittel.

Ich würde einfach ein Skript schreiben, welches zuerst die vier Libs baut und dann die binary.


Zuletzt bearbeitet von GPC am 00:28:31 20.01.2008, insgesamt 1-mal bearbeitet
BugJoe
Mitglied

Benutzerprofil
Anmeldungsdatum: 18.11.2002
Beiträge: 310
Beitrag BugJoe Mitglied 16:24:31 21.01.2008   Titel:              Zitieren

Danke für die Antwort.

Wenn es wirklich nicht anders geht werd ich das wohl tun müssen, auch wenn ich stark gehoft habe dass genau diese Arbeit auch intelligent von SCons erledigt wird. Gerade der fünfte Beitrag von __HIRSCH_H__ hat sich in meinen Ohren nämlich so angehört als wenn in seinem Projekt der "Buildaufbau" sehr ähnlich ist wie in meinem Projekt (wenn nicht sogar noch komplexer) und SCons die Abhängigkeiten unter den verschiedenen Biblotheken aufdröseln könnte und genau weiß was es zuerst kompilieren muss.
__HIRSCH_H__
Mitglied

Benutzerprofil
Anmeldungsdatum: 16.05.2006
Beiträge: 63
Beitrag __HIRSCH_H__ Mitglied 16:47:24 21.01.2008   Titel:              Zitieren

BugJoe schrieb:
Danke für die Antwort.

Wenn es wirklich nicht anders geht werd ich das wohl tun müssen, auch wenn ich stark gehoft habe dass genau diese Arbeit auch intelligent von SCons erledigt wird. Gerade der fünfte Beitrag von __HIRSCH_H__ hat sich in meinen Ohren nämlich so angehört als wenn in seinem Projekt der "Buildaufbau" sehr ähnlich ist wie in meinem Projekt (wenn nicht sogar noch komplexer) und SCons die Abhängigkeiten unter den verschiedenen Biblotheken aufdröseln könnte und genau weiß was es zuerst kompilieren muss.

Wenn ich mehrere voneinander abhängige Bibliotheken oder Programme baue, mach ich das meistens so, dass ich den Rückgabewert der SharedLibrary Funktion an die entsprechende Program oder SharedLibrary Funktion der abhängigen Bibliothek übergebe.
Beispiel:

Code:
lib1 = SharedLibrary(myFiles)
lib2 = SharedLibrary([otherFiles, lib1])
prog = Program([moreFiles, lib2])
Code:
lib1 = SharedLibrary(myFiles)
lib2 = SharedLibrary([otherFiles, lib1])
prog = Program([moreFiles, lib2])
Code:
lib1 = SharedLibrary(myFiles)
lib2 = SharedLibrary([otherFiles, lib1])
prog = Program([moreFiles, lib2])


Dann bestimmt scons die Abhängigkeit korrekt.
Tip: Du kannst die lib/prog objekte aus den Unterverzeichnissen mit Return('lib1') zurückgeben.

Ich hoffe es hilft dir weiter und du kannst das Konzept in deinen generischen Ansatz einbauen. Wenn ich scons scripte schreibe sind die eigentlich immer massgeschneidert und ziemlich naiv runtergehackt (KISS-Prinzip).
BugJoe
Mitglied

Benutzerprofil
Anmeldungsdatum: 18.11.2002
Beiträge: 310
Beitrag BugJoe Mitglied 19:18:03 21.01.2008   Titel:              Zitieren

Ja, vielen Dank für die Antwort. Werd ich morgen gleich mal ausprobieren. Aber das hört sich sehr gut an :)
checkHeader
Unregistrierter




Beitrag checkHeader Unregistrierter 17:27:00 26.07.2008   Titel:              Zitieren

Sehr schöner Artikel. :live:

Aber ich hab ein Problem mit boost und CheckCXXHeader() vielleicht hat von euch einer eine Idee, meine bisherige google-Suche war nicht erfolgreich.

Code:
if not conf.CheckCXXHeader('boost/test/unit_test.hpp'):
        print 'Could not find boost/test/unit_test.hpp . Please install boost (www.boost.org)'
        Exit(1)
Code:
if not conf.CheckCXXHeader('boost/test/unit_test.hpp'):
print 'Could not find boost/test/unit_test.hpp . Please install boost (www.boost.org)'
Exit(1)
Code:
if not conf.CheckCXXHeader('boost/test/unit_test.hpp'):
        print 'Could not find boost/test/unit_test.hpp . Please install boost (www.boost.org)'
        Exit(1)


Boost ist installiert und die Header liegen auch unter /usr/include/boost/... wenn man das Exit(1) raus nimmt kompiliert auch alles.

Was ist falsch an der Prüfung, bzw. was mache ich falsch an der Stelle?
Muss ich noch die System-Include-Verzeichnisse irgendwo angeben?

Danke
__HIRSCH_H__
Mitglied

Benutzerprofil
Anmeldungsdatum: 16.05.2006
Beiträge: 63
Beitrag __HIRSCH_H__ Mitglied 08:11:45 28.07.2008   Titel:              Zitieren

Was steht denn in der Datei config.log? Da sollte drinstehen welches Kommando aufgerufen wurde und welche Fehlermeldung zum Abbruch geführt hat.
checkHeader
Unregistrierter




Beitrag checkHeader Unregistrierter 11:35:29 31.07.2008   Titel:              Zitieren

Merkwürdigerweise versuchte er eine Datei zu kompilieren innerhalb der Tests die nicht existierte. Die temporären Dateien und Verzeichnisse von scons löschen, danach neu ausführen und es ging.

Versteh ich zwar nicht aber Danke :)
C/C++ Forum :: Die Artikel ::  Build-Systeme Teil 3: SCons   Auf Beitrag antworten

Zeige alle Beiträge auf einer Seite




Nächstes Thema anzeigen
Vorheriges Thema anzeigen
Sie können keine Beiträge in dieses Forum schreiben.
Sie können auf Beiträge in diesem Forum antworten.
Sie können Ihre Beiträge in diesem Forum nicht bearbeiten.
Sie können Ihre Beiträge in diesem Forum nicht löschen.
Sie können an Umfragen in diesem Forum nicht mitmachen.

Powered by phpBB © 2001, 2002 phpBB Group :: FI Theme

c++.de ist Teilnehmer des Partnerprogramms von Amazon Europe S.à.r.l. und Partner des Werbeprogramms, das zur Bereitstellung eines Mediums für Websites konzipiert wurde, mittels dessen durch die Platzierung von Werbeanzeigen und Links zu amazon.de Werbekostenerstattung verdient werden kann.

Die Vervielfältigung der auf den Seiten www.c-plusplus.de, www.c-plusplus.info, www.c-sar.de, www.c-plusplus.net und www.baeckmann.de enthaltenen Informationen ohne eine schriftliche Genehmigung des Seitenbetreibers ist untersagt (vgl. §4 Urheberrechtsgesetz). Die Nutzung und Änderung der vorgestellten Strukturen und Verfahren in privaten und kommerziellen Softwareanwendungen ist ausdrücklich erlaubt, soweit keine Rechte Dritter verletzt werden. Der Seitenbetreiber übernimmt keine Gewähr für die Funktion einzelner Beiträge oder Programmfragmente, insbesondere übernimmt er keine Haftung für eventuelle aus dem Gebrauch entstehenden Folgeschäden.