Build-Systeme Teil 3: SCons



  • nash35 schrieb:

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

    Ne, ehrlich gesagt nicht.



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

    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?



  • 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:

    # 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))
    

    SCon\1:

    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:

    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.



  • 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.



  • 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.



  • 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:

    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).



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



  • Sehr schöner Artikel. 👍

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

    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



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



  • 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 🙂


Anmelden zum Antworten