Deploy Helper

Overview

Einleitung

Abhängigkeiten in einem Softwareprojekt zu verwalten und die abgeleiteten Skripte (z.B. Installer-Skripte) immer auf Kurs zu halten ist eine Herausforderung (sofern manuell verwaltet wird). Ziel dieses Projektes ist es, die Herausforderungen mit einer möglichen Automatisierung zu eliminieren.

Abhängigkeiten

Das Projekt baut auf folgende Komponenten auf:

Randbemerkung: In der Qt Welt gibt es speziell das Tool windeployqt.exe (im bin-Verzeichnis des Frameworks).

Anwendung

Das Programm wird wie folgt aufgerufen:

  deploy.exe <your script> [<other parameters maybe for your script>]
1
  deploy.exe dot_file_for_argument.chai
1

Kommandos

Die Script-Eninge zu den üblichen Sprachmerkmalen (if, for, Zuweisung …) unterstütz folgende weitere Befehle:

ScopeMethodeBeschreibung
Globalvar getValueFromEnvironment(key)Gibt ein Wert aus den Umgebungsvariablen zurück. Beispiel: var path = getValueFromEnvironment("PATH");
Globalvar getArgumentCount()Gibt die Anzahl der übergebenen Argumente zurück. Beispiel: deploy.exe my_script.chai var count = getArgumentCount(); // count = 2, 1 = deploy.exe, 2 = my_script.chai
Globalvar getArgument(index)Gibt den Wert des Arguments an der N. Stelle zurück. Beispiel: var exe_name = getArgument(0); // deploy.exe
GlobalwriteToConsole(content) Schreibt ein Inhalt auf die Konsole (STDCOUT).
Graphvar getNumOfFiles()Gibt die Anzahl der Module/Dateien innerhalb des Graph zurück.
GraphFile getFile(index)Gibt das Modul/Datei an einer bestimmten Stelle zurück.
GraphFileState getFileState(moduleName)Gibt den Dateistatus eines bestimmten Moduls/Datei zurück. FileState = {Valid, Error, Ignored, NoImportTable, NotFound, NotHandled}
Graphvar getFilePath(moduleName)Gibt den Dateipfad zu einem bestimmten Moduls/Datei zurück.
Graphvector getDependencies(moduleName)Gibt die Abhängigkeiten eines bestimmten Moduls/Datei zurück.
GlobaladdSearchPath(path, behaviour)Fügt ein Suchpfad für die Module hinzu. behavior = {OnlyAddDirectory, AddAllSubDirectories, AddAllSubDirectoriesRecursivly}
GlobaladdSearchPathsFromEnvironment(key)Fügt ein Suchpfad aus den Umgebungsvariable hinzu. Stehen mehrere Pfade durch ein Semikolon getrennt, so werden alle Pfade hinzugefügt. (Vgl. PATH=C:\foo;D\bar)
GlobalescapeForRegExp(path)Korrigiert den Pfad für die Regular Expression Ausdrücke.
GlobaladdFileToSearchPath(path)Fügt den Ordner eines bestimmter Datei zum Suchpfad hinzu.
GlobaladdInitialFile(path)Fügt eine EXE oder DLL als Startpunkt der Analyse hinzu.
GlobalexcludeModuleName(name)Exkludiert ein bestimmter Modulnamen aus der Analyse. Hier kann auch die Regular Expression verwendet werden von C++ ECMAScriptopen in new window
GlobalexcludeModulePath(path)Exkludiert ein bestimmter Modulpfad aus der Analyse. Hier kann auch die Regular Expression verwendet werden von C++ ECMAScriptopen in new window
GlobalignoreModuleName(name)Ignoriert ein bestimmter Modulnamen aus der Analyse. Hier kann auch die Regular Expression verwendet werden von C++ ECMAScriptopen in new window
GlobalignoreModulePath(path)Ignoriert ein bestimmter Modulpfad aus der Analyse. Hier kann auch die Regular Expression verwendet werden von C++ ECMAScriptopen in new window
Globalvar findFile(path)Sucht innerhalb der gegeben Suchpfaden nach dem absoluten Pfad.
Globalvar isExcluded(name)Prüft ob das Modul exkludiert ist.
Globalvar isIgnored(name)Prüft ob das Modul ignoriert wird.
Filevar getModuleName()Gibt den Modulnamen der Datei zurück (entspricht Basisname + Dateieendung.)
Filevar getFilePath()Gibt den absoluten Pfad zurück.
Filevar getState()Gibt den Analysezustand zurück.
Filevar getDependencies()Gibt die Modulabhänigkeiten zurück.
GlobalGraph buildGraph()Analysiert und baut den Graph anhand der Eingabeparameter.
TextFileWriterTextFileWriter(filePath)Erstellt ein neues TextFileWriter Objekt zum schreiben in eine beliebige Textdatei.
TextFileWritervar isOpen()Prüft ob die Datei erfolgreich geöffnet wurde.
TextFileWriterwrite(content)Schreibt den Inhalt in die Datei.
TextFileWriterwriteln(content)Schreibt den Inhalt in die Datei und fügt eine neue Zeile hinzu.

Innerhalb des Skriptes kann jede beliebige Ausgabeformat (in Textform) erfolgen. In den Beispiel-Skripten wird die DOT Notation verwendet, die für die Erstellung eines visuellen Abhängigkeitsgraph (s. Titelbild) verwendet werden kann. Für die Umwandung der DOT Notation in ein Bildformat wie JPG kann die Toolsuite GraphVizopen in new window (siehe das Tool dot) verwendet werden.

Beispiel

// convert file state to color string
def fileStateToColor(state) {
 if(state == NoImportTable || state == Valid) {
  return "green";
 }
 
 if(state == Valid) {
  return "green";
 }
   
 if(state == NotFound || state == Error) {
  return "red";
 } 
   
 if(state == NotHandled) {
  return "gray";
 }
   
 if(state == Ignored) {
  return "yellow";
 }   
  
 return "blue"; 
}

// setup search paths
addSearchPathsFromEnvironment("PATH");

// exclude module names starts with API-MS
excludeModuleName("(API-MS)(.*)");

// exclude module paths starts with the system root path
var systemRoot = getValueFromEnvironment("SystemRoot");
excludeModulePath("(" + escapeForRegExp(systemRoot) + ")(.*)");

// add initial files
//for(var i=0; i < getArgumentCount(); ++i) {
// var arg = getArgument(i);
// writeToConsole( arg );
 
// addFileToSearchPath( arg );
// addInitialFile( arg );
//}
addInitialFile( "C:\\SDK\\Qt\\5.7\\msvc2015\\bin\\designer.exe" );

// building dependency graph
writeToConsole( "build graph" );
var graph = buildGraph();

// create a new dot file
writeToConsole( "write output file" );
var output = TextFileWriter("dot_file_for_qt.dot");
output.writeln("digraph DirectedGraph {");
for(var i=0; i < graph.getNumOfFiles(); ++i) {
  var file = graph.getFile(i);
  // ignore error states
  if(file.getState() == NotFound || file.getState() == Error) {
    continue;
  }
    
  writeToConsole(file.getModuleName() + " -> " + file.getFilePath());
  output.writeln("  \"" + file.getModuleName() + "\" [color=" + fileStateToColor(file.getState()) + "]");

  var dependsOn = file.getDependencies();
  for(var j=0; j < dependsOn.size(); ++j) {
    // ignore error states
    var state = graph.getFileState(dependsOn[j]);
    if(state == NotFound || state == Error) {
      continue;
    }
    
    // exclude modules from dot file which is matched witht he excluding patterns
    if(isExcluded(dependsOn[j],findFile(dependsOn[j]))) {
      continue;
    }
    
    // write dot file
    output.writeln("  \"" + file.getModuleName() + "\" -> \"" + dependsOn[j] + "\"" );
  }
}

// finish the dot file
output.writeln("}");
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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83

Abschluss

Viel Vergnügen! Im Download sind Beispiel-Skripte hinterlegtopen in new window.