NEWS
ioBroker auf neuer Maschine aufgesetzt und Adapter Probleme
-
@crunchip sorry, hab ich anscheinend überlesen...
-
@da_woody
Merci, das mit dem ble auf dem alten system zu deinstallieren hatte ich bereits versucht(hatte das funktionierende ble aber auf dem neuen ioBroker gelassen), allerdings hatte das backitup dann auch das funktionierende ble auf dem neuen system gelöscht, ohne das es wieder (funktionierend) installiert werden konnte. Und auch hatte ich das ble auf dem neuem system deinstalliert vor der backupeinspielung, mit selbem ergebnis - also das sich das ble nicht mehr installieren ließ.@crunchip
habe gestern, soweit es mir möglich war doch zumindest das alte System auf die Basis von Bullseye hochgezogen und alle Adapter dann soweit upgedatet, das im Stable keine weiteren Updates mehr angeboten wurden.
Dann sollte ich nach dem posten von "iob diag", "cat /etc/debian_version" und "sudo systemctl daemon-reload" die alte (bestehende) Installation platt machen und diese (alte Docker-) ioBroker erneut komplett neu unter Bookworm auf der alten Maschine aufsetzten und dann dort das Backup einspielen.Wie ich vorher erwähnt hatte, weis ich (rein logisch - evtl. fehlen mir hier die Hintergrundkenntnisse) nicht, was das bringen sollte, wenn ich doch eine neue Bookworm Installation auf der neuen Maschine habe und eh darauf umziehen möchte.
Oder ist die Vermutung das meine neue Maschine nicht für ioBroker geeignet ist, obwohl die frische Installation dort funktionierte? -
@gismoh sagte in ioBroker auf neuer Maschine aufgesetzt und Adapter Probleme:
Dann sollte ich nach dem posten von "iob diag", "cat /etc/debian_version" und "sudo systemctl daemon-reload" die alte (bestehende) Installation platt machen und diese (alte Docker-) ioBroker erneut komplett neu unter Bookworm auf der alten Maschine aufsetzten und dann dort das Backup einspielen.
NEIN!
Hier geht es dermaßen drunter und drüber, kreuz und quer, dass einem Helfenden sehr schnell der Überblick verloren geht.
Dieser Rat kam, weil inzwischen nicht mehr klar war, auf welcher Installation dich gerade befindest und weswegen du da gerade was machst.
gehe die Dinge systematisch an, lass nichts aus, wozu man dich auffordert. Dann kann man dir auch eher helfen.
-
@homoran
Ich bin bei weitem kein "Linux" oder "ioBroker" experte, daher versuche ich mich auch an die gegebene Hilfe zu halten.
So habe ich z.b. jeden Schritt dokumentiert, wo ich z.B. die Hilfe von @Thomas-Braun umgesetzt habe.
Versuche allgemein die Tipps/Hilfe umzusetzen, sehr gerne natürlich. Bin jeden dankbar der versucht mir weiterzuhelfen.
Wenn irgendein Tipp eine, evtl. für euch Experten eine klare Vor- oder Nacharbeitet erfordert, so ist es dann evtl. für euch selbstverständlich, aber für mich als "Noob" eben evtl. nicht.Wenn ich mich wirklich unklar ausgedrückt habe, versuche ich es erneut:
Ich möchte meine alte ioBroker Installation welche auf dem Raspi4 unter docker (nun Bullseye) läuft
auf meinen neuen Rechnerziehen:
Eine Installation in einer VM unter Proxmox worauf als Grundsystem Bookworm läuft.
Die Installation hat dort geklappt, auch der ble klappt dort grundsätzlich.
Nur nach einspielen des Backups (BackItUp) vom alten Pi, wird irgendwie der ble so zerschossen, das ich den dort bisher nicht zum laufen bekommen habe.Der alte Pi läuft soweit, das er (noch) aktiv ist und auch Sicherungen kann ich dort mit dem BackItUp Adapter ziehen.
Dieser soll danach aber für den ioBroker in "Rente" gehen.= Möchte also "nur" den bestehende ioBroker auf einen neuen PC umziehen.
-
@gismoh sagte in ioBroker auf neuer Maschine aufgesetzt und Adapter Probleme:
Ich bin bei weitem kein "Linux" oder "ioBroker" experte
das musst du auch nicht. Erstens ist nur selten wirklich Linux-Wissen nötig, zweitens wird fir dann auch geholfen.
@gismoh sagte in ioBroker auf neuer Maschine aufgesetzt und Adapter Probleme:
daher versuche ich mich auch an die gegebene Hilfe zu halten.
das ist auch unbedingt nötig, da die Helfenden davon ausgehen dass du gemacht hast, was man dir sagt.
https://forum.iobroker.net/topic/51555/hinweise-für-gute-forenbeiträge/1
hier Teil 2So etwas viele Posts später
@gismoh sagte in ioBroker auf neuer Maschine aufgesetzt und Adapter Probleme:
hatte das funktionierende ble aber auf dem neuen ioBroker gelassen
wirft dann alles bisherige wieder über den Haufen, und man müsste eigentlich nochmal ganz von vorne anfangen, was aber nicht geht, wenn du mittlerweile
@gismoh sagte in ioBroker auf neuer Maschine aufgesetzt und Adapter Probleme:
das alte System auf die Basis von Bullseye hochgezogen und alle Adapter dann soweit upgedatet
hast.
auch ein iob diag vom ursprünglichen System, von dem das Backup stammt ist dann nicht mehr möglich und ein mögliches Problem im Backup kann dann nicht mehr festgestellt werden.
@gismoh sagte in ioBroker auf neuer Maschine aufgesetzt und Adapter Probleme:
auf dem Raspi4 unter docker
Diese Info ist mir neu.
-
@homoran sagte in ioBroker auf neuer Maschine aufgesetzt und Adapter Probleme:
Diese Info ist mir neu.
ne nicht neu, sieht man ja auch anhand seiner diag Ausgabe
@gismoh sagte in ioBroker auf neuer Maschine aufgesetzt und Adapter Probleme:
Die Installation hat dort geklappt, auch der ble klappt dort grundsätzlich.
korrekt, aber es sollte vorher nichts diesbezüglich auf dem neuen schon laufen
@gismoh sagte in ioBroker auf neuer Maschine aufgesetzt und Adapter Probleme:
Nur nach einspielen des Backups (BackItUp) vom alten Pi, wird irgendwie der ble so zerschossen, das ich den dort bisher nicht zum laufen bekommen habe.
eben, daher war der Grundgedanke, das in deinem alte System der Wurm drin ist und daher das Backup im Zusammenhang, aufs neue System zu spielen, bisher scheiterte
daher sollst du auch nicht, dein altes System auf den neusten Stand bringen, sondern nur soweit, das der aktuelle Stand auch wirklich aktuell ist.
@gismoh sagte in ioBroker auf neuer Maschine aufgesetzt und Adapter Probleme:
Möchte also "nur" den bestehende ioBroker auf einen neuen PC umziehen.
jetzt bleibt die Frage, warum dein Backup nicht auf dem neuen System zu 100% funktioniert.
Daher würde ich mal da ansetzen und etwas tiefer rein schauen, vllt auf dem alten System den ble löschen und neu installieren, möglicherweise liegt da schon ein "unentdecktes" Problem vor.wie weiter oben schon erwähnt, hatte ich den ble auch laufen, anschließend irgendwann mal das OS upgedatet, sowie auch nodejs, irgendwann als ich dann mit dem ble von v0.13.3 auf die 0.13.4 gehen wollte, kam es zum selbigen Fehler
npm ERR! build_file_contents = open(build_file_path, "rU").read() npm ERR! ^^^^^^^^^^^^^^^^^^^^^^^^^^^ npm ERR! ValueError: invalid mode: 'rU' while trying to load binding.gyp npm ERR! gyp ERR! configure error
daher denk ich, ist eventuell auf deinem alten System schon was faul
-
@gismoh sagte in ioBroker auf neuer Maschine aufgesetzt und Adapter Probleme:
auf dem Raspi4 unter docker
Diese Info ist mir neu.
Steht in der ersten Zeile im ersten Post
Aber egal, waren vermutlich auch Missverständnisse dabei.
Wie auch "auf den aktuellen Stand bringen".
Wie mir bekannt, kann man die Buster Version bis zum Bullyseye bringen, danach geht es nur mit neuinstallation von Bookworm.
Somit habe ich es auf den aktuellsten Stand gebracht, wie es mir möglich war, ohne Neuinstallation.so, wie gehe ich nun weiter vor?
Dann mache ich nun ein komplettes Backup (sicherung der SD-Karte) vom alten Sytem und schaue dann ob ich den ble dort deinstalliert und wieder installiert bekomme.Edit: @Homoran
selbstverständlich ist es grundsätzlich möglich, von der vorherigen, alten Installation vom Pi ein iob diag zu erstellen, denn ich habe auch vor dem Update des Systems ein "Full-Backup (SD-Karte)" gezogen. Würde nur dauern, dies wieder einzuspielen - und weis auch nicht ob es sinnvoll wäre, da das "geupdatete System" nun ja auch läuft. -
@gismoh sagte in ioBroker auf neuer Maschine aufgesetzt und Adapter Probleme:
Wie mir bekannt, kann man die Buster Version bis zum Bullyseye bringen, danach geht es nur mit neuinstallation von Bookworm.
das geht auch weiter. nur nicht in einem Schritt.
Aber solltest du nicht soweit ich das hier mitgelesen und korrekt verstanden habe nur iob auf den höchstmöglich aktuellen Stand (für Buster) bringen?Aber ist jetzt alles müßig.
auf welcher Version ist ble im backup?
@crunchip sagte in ioBroker auf neuer Maschine aufgesetzt und Adapter Probleme:
jetzt bleibt die Frage, warum dein Backup nicht auf dem neuen System zu 100% funktioniert.
Daher würde ich mal da ansetzen und etwas tiefer rein schauen, vllt auf dem alten System den ble löschen und neu installieren, möglicherweise liegt da schon ein "unentdecktes" Problem vor.
wie weiter oben schon erwähnt, hatte ich den ble auch laufen, anschließend irgendwann mal das OS upgedatet, sowie auch nodejs, irgendwann als ich dann mit dem ble von v0.13.3 auf die 0.13.4 gehen wollte, kam es zum selbigen Fehler -
@crunchip
ble Instanz und Adapter 0.13.4 wurde vom "alten System" gelöscht.
Werde nun versuchen die neu zu installieren -
@gismoh
ble v0.13.4 ist wieder installiert und läuft (Adapter "grün"). (Auf dem Pi4) -
@gismoh wäre mal interessant wie diese
input.py
aussieht auf dem alten System/usr/lib/node_modules/npm/node_modules/node-gyp/gyp/pylib/gyp/input.py
oder ob es da die angemeckerte gibt
/opt/iobroker/node_modules/node-gyp/gyp/pylib/gyp/input.py
@gismoh sagte in ioBroker auf neuer Maschine aufgesetzt und Adapter Probleme:
Adapter "grün"
heisst aber noch nicht das er auch funktioniert
es muss auch in den Objekten derinfo.driverState
passen -
@homoran said in ioBroker auf neuer Maschine aufgesetzt und Adapter Probleme:
@gismoh sagte in ioBroker auf neuer Maschine aufgesetzt und Adapter Probleme:
Wie mir bekannt, kann man die Buster Version bis zum Bullyseye bringen, danach geht es nur mit neuinstallation von Bookworm.
das geht auch weiter. nur nicht in einem Schritt.
Aber solltest du nicht soweit ich das hier mitgelesen und korrekt verstanden habe nur iob auf den höchstmöglich aktuellen Stand (für Buster) bringen?Wie oben geschrieben, das für Buster stand wohl nicht explizit da, daher dann "Missverständniss".
Aber ist jetzt alles müßig.
Selbst für mich, das nach zu halten, denn ich hatte die Tipps von verschiedenen Leuten natürlich nachgegangenauf welcher Version ist ble im backup?
ble v0.13.4@crunchip sagte in ioBroker auf neuer Maschine aufgesetzt und Adapter Probleme:
jetzt bleibt die Frage, warum dein Backup nicht auf dem neuen System zu 100% funktioniert.
Daher würde ich mal da ansetzen und etwas tiefer rein schauen, vllt auf dem alten System den ble löschen und neu installieren, möglicherweise liegt da schon ein "unentdecktes" Problem vor.
wie weiter oben schon erwähnt, hatte ich den ble auch laufen, anschließend irgendwann mal das OS upgedatet, sowie auch nodejs, irgendwann als ich dann mit dem ble von v0.13.3 auf die 0.13.4 gehen wollte, kam es zum selbigen FehlerJa, dort hatten wir auch schon geschaut.
-
@crunchip
mom, sehe nach -
@crunchip
info.driverState = poweredOn -
# Copyright (c) 2012 Google Inc. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. import ast import gyp.common import gyp.simple_copy import multiprocessing import os.path import re import shlex import signal import subprocess import sys import threading import traceback from distutils.version import StrictVersion from gyp.common import GypError from gyp.common import OrderedSet # A list of types that are treated as linkable. linkable_types = [ "executable", "shared_library", [ File 'input.py' is unwritable ]
-
@gismoh ok
das der ble Adapter hin und wieder Probleme im Bezug auf ein nodejs upgrade macht, ist bekannt. vllt hat sich das nun gelöst, in dem du ihn ja jetzt neu installiert hast auf dem alten System.
dann jetzt nochmal ein backup machen und auf eine frische Installation packen
mal sehen was nun dabei rum kommt -
@crunchip
okay, merci mache ich gleich. -
@gismoh das ist nur der Anfang, der entscheidende Teil steh weiter unten, ab
def LoadOneBuildFile(build_file_path, data, aux_data, includes, is_target, check):
die andere Datei gibt es nicht?
-
@crunchip hab ich nun gar nicht geschaut, mache gerade das backup. Sehe aber gern gleich noch mal nach.
Edith: Werde auch dann versucen den Inhalt hier reinzukopieren. -
Copyright (c) 2012 Google Inc. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. import ast import gyp.common import gyp.simple_copy import multiprocessing import os.path import re import shlex import signal import subprocess import sys import threading import traceback from distutils.version import StrictVersion from gyp.common import GypError from gyp.common import OrderedSet # A list of types that are treated as linkable. linkable_types = [ "executable", "shared_library", "loadable_module", "mac_kernel_extension", "windows_driver", ] # A list of sections that contain links to other targets. dependency_sections = ["dependencies", "export_dependent_settings"] # base_path_sections is a list of sections defined by GYP that contain # pathnames. The generators can provide more keys, the two lists are merged # into path_sections, but you should call IsPathSection instead of using either # list directly. base_path_sections = [ "destination", "files", "include_dirs", "inputs", "libraries", "outputs", "sources", ] path_sections = set() # These per-process dictionaries are used to cache build file data when loading # in parallel mode. per_process_data = {} per_process_aux_data = {} def IsPathSection(section): # If section ends in one of the '=+?!' characters, it's applied to a section # without the trailing characters. '/' is notably absent from this list, # because there's no way for a regular expression to be treated as a path. while section and section[-1:] in "=+?!": section = section[:-1] if section in path_sections: return True # Sections matching the regexp '_(dir|file|path)s?$' are also # considered PathSections. Using manual string matching since that # is much faster than the regexp and this can be called hundreds of # thousands of times so micro performance matters. if "_" in section: tail = section[-6:] if tail[-1] == "s": tail = tail[:-1] if tail[-5:] in ("_file", "_path"): return True return tail[-4:] == "_dir" return False # base_non_configuration_keys is a list of key names that belong in the target # itself and should not be propagated into its configurations. It is merged # with a list that can come from the generator to # create non_configuration_keys. base_non_configuration_keys = [ # Sections that must exist inside targets and not configurations. "actions", "configurations", "copies", "default_configuration", "dependencies", "dependencies_original", "libraries", "postbuilds", "product_dir", "product_extension", "product_name", "product_prefix", "rules", "run_as", "sources", "standalone_static_library", "suppress_wildcard", "target_name", "toolset", "toolsets", "type", # Sections that can be found inside targets or configurations, but that # should not be propagated from targets into their configurations. "variables", ] non_configuration_keys = [] # Keys that do not belong inside a configuration dictionary. invalid_configuration_keys = [ "actions", "all_dependent_settings", "configurations", "dependencies", "direct_dependent_settings", "libraries", "link_settings", "sources", "standalone_static_library", "target_name", "type", ] # Controls whether or not the generator supports multiple toolsets. multiple_toolsets = False # Paths for converting filelist paths to output paths: { # toplevel, # qualified_output_dir, # } generator_filelist_paths = None def GetIncludedBuildFiles(build_file_path, aux_data, included=None): """Return a list of all build files included into build_file_path. The returned list will contain build_file_path as well as all other files that it included, either directly or indirectly. Note that the list may contain files that were included into a conditional section that evaluated to false and was not merged into build_file_path's dict. aux_data is a dict containing a key for each build file or included build file. Those keys provide access to dicts whose "included" keys contain lists of all other files included by the build file. included should be left at its default None value by external callers. It is used for recursion. The returned list will not contain any duplicate entries. Each build file in the list will be relative to the current directory. """ if included is None: included = [] if build_file_path in included: return included included.append(build_file_path) for included_build_file in aux_data[build_file_path].get("included", []): GetIncludedBuildFiles(included_build_file, aux_data, included) return included def CheckedEval(file_contents): """Return the eval of a gyp file. The gyp file is restricted to dictionaries and lists only, and repeated keys are not allowed. Note that this is slower than eval() is. """ syntax_tree = ast.parse(file_contents) assert isinstance(syntax_tree, ast.Module) c1 = syntax_tree.body assert len(c1) == 1 c2 = c1[0] assert isinstance(c2, ast.Expr) return CheckNode(c2.value, []) def CheckNode(node, keypath): if isinstance(node, ast.Dict): dict = {} for key, value in zip(node.keys, node.values): assert isinstance(key, ast.Str) key = key.s if key in dict: raise GypError( "Key '" + key + "' repeated at level " + repr(len(keypath) + 1) + " with key path '" + ".".join(keypath) + "'" ) kp = list(keypath) # Make a copy of the list for descending this node. kp.append(key) dict[key] = CheckNode(value, kp) return dict elif isinstance(node, ast.List): children = [] for index, child in enumerate(node.elts): kp = list(keypath) # Copy list. kp.append(repr(index)) children.append(CheckNode(child, kp)) return children elif isinstance(node, ast.Str): return node.s else: raise TypeError( "Unknown AST node at key path '" + ".".join(keypath) + "': " + repr(node) ) def LoadOneBuildFile(build_file_path, data, aux_data, includes, is_target, check): if build_file_path in data: return data[build_file_path] if os.path.exists(build_file_path): build_file_contents = open(build_file_path, encoding='utf-8').read() else: raise GypError(f"{build_file_path} not found (cwd: {os.getcwd()})") build_file_data = None try: if check: build_file_data = CheckedEval(build_file_contents) else: build_file_data = eval(build_file_contents, {"__builtins__": {}}, None) except SyntaxError as e: e.filename = build_file_path raise except Exception as e: gyp.common.ExceptionAppend(e, "while reading " + build_file_path) raise if type(build_file_data) is not dict: raise GypError("%s does not evaluate to a dictionary." % build_file_path) data[build_file_path] = build_file_data aux_data[build_file_path] = {} # Scan for includes and merge them in. if "skip_includes" not in build_file_data or not build_file_data["skip_includes"]: try: if is_target: LoadBuildFileIncludesIntoDict( build_file_data, build_file_path, data, aux_data, includes, check ) else: LoadBuildFileIncludesIntoDict( build_file_data, build_file_path, data, aux_data, None, check ) except Exception as e: gyp.common.ExceptionAppend( e, "while reading includes of " + build_file_path ) raise return build_file_data def LoadBuildFileIncludesIntoDict( subdict, subdict_path, data, aux_data, includes, check ): includes_list = [] if includes is not None: includes_list.extend(includes) if "includes" in subdict: for include in subdict["includes"]: # "include" is specified relative to subdict_path, so compute the real # path to include by appending the provided "include" to the directory # in which subdict_path resides. relative_include = os.path.normpath( os.path.join(os.path.dirname(subdict_path), include) ) includes_list.append(relative_include) # Unhook the includes list, it's no longer needed. del subdict["includes"] # Merge in the included files. for include in includes_list: if "included" not in aux_data[subdict_path]: aux_data[subdict_path]["included"] = [] aux_data[subdict_path]["included"].append(include) gyp.DebugOutput(gyp.DEBUG_INCLUDES, "Loading Included File: '%s'", include) MergeDicts( subdict, LoadOneBuildFile(include, data, aux_data, None, False, check), subdict_path, include, ) # Recurse into subdictionaries. for k, v in subdict.items(): if type(v) is dict: LoadBuildFileIncludesIntoDict(v, subdict_path, data, aux_data, None, check) elif type(v) is list: LoadBuildFileIncludesIntoList(v, subdict_path, data, aux_data, check) # This recurses into lists so that it can look for dicts. def LoadBuildFileIncludesIntoList(sublist, sublist_path, data, aux_data, check): for item in sublist: if type(item) is dict: LoadBuildFileIncludesIntoDict( item, sublist_path, data, aux_data, None, check ) elif type(item) is list: LoadBuildFileIncludesIntoList(item, sublist_path, data, aux_data, check) # Processes toolsets in all the targets. This recurses into condition entries # since they can contain toolsets as well. def ProcessToolsetsInDict(data): if "targets" in data: target_list = data["targets"] new_target_list = [] for target in target_list: # If this target already has an explicit 'toolset', and no 'toolsets' # list, don't modify it further. if "toolset" in target and "toolsets" not in target: new_target_list.append(target) continue if multiple_toolsets: toolsets = target.get("toolsets", ["target"]) else: toolsets = ["target"] # Make sure this 'toolsets' definition is only processed once. if "toolsets" in target: del target["toolsets"] if len(toolsets) > 0: # Optimization: only do copies if more than one toolset is specified. for build in toolsets[1:]: new_target = gyp.simple_copy.deepcopy(target) new_target["toolset"] = build new_target_list.append(new_target) target["toolset"] = toolsets[0] new_target_list.append(target) data["targets"] = new_target_list if "conditions" in data: for condition in data["conditions"]: if type(condition) is list: for condition_dict in condition[1:]: if type(condition_dict) is dict: ProcessToolsetsInDict(condition_dict) # TODO(mark): I don't love this name. It just means that it's going to load # a build file that contains targets and is expected to provide a targets dict # that contains the targets... def LoadTargetBuildFile( build_file_path, data, aux_data, variables, includes, depth, check, load_dependencies, ): # If depth is set, predefine the DEPTH variable to be a relative path from # this build file's directory to the directory identified by depth. if depth: # TODO(dglazkov) The backslash/forward-slash replacement at the end is a # temporary measure. This should really be addressed by keeping all paths # in POSIX until actual project generation. d = gyp.common.RelativePath(depth, os.path.dirname(build_file_path)) if d == "": variables["DEPTH"] = "." else: variables["DEPTH"] = d.replace("\\", "/") # The 'target_build_files' key is only set when loading target build files in # the non-parallel code path, where LoadTargetBuildFile is called # recursively. In the parallel code path, we don't need to check whether the # |build_file_path| has already been loaded, because the 'scheduled' set in # ParallelState guarantees that we never load the same |build_file_path| # twice. if "target_build_files" in data: if build_file_path in data["target_build_files"]: # Already loaded. return False data["target_build_files"].add(build_file_path) gyp.DebugOutput( gyp.DEBUG_INCLUDES, "Loading Target Build File '%s'", build_file_path ) build_file_data = LoadOneBuildFile( build_file_path, data, aux_data, includes, True, check ) # Store DEPTH for later use in generators. build_file_data["_DEPTH"] = depth # Set up the included_files key indicating which .gyp files contributed to # this target dict. if "included_files" in build_file_data: raise GypError(build_file_path + " must not contain included_files key") included = GetIncludedBuildFiles(build_file_path, aux_data) build_file_data["included_files"] = [] for included_file in included: # included_file is relative to the current directory, but it needs to # be made relative to build_file_path's directory. included_relative = gyp.common.RelativePath( included_file, os.path.dirname(build_file_path) ) build_file_data["included_files"].append(included_relative) # Do a first round of toolsets expansion so that conditions can be defined # per toolset. ProcessToolsetsInDict(build_file_data) # Apply "pre"/"early" variable expansions and condition evaluations. ProcessVariablesAndConditionsInDict( build_file_data, PHASE_EARLY, variables, build_file_path ) # Since some toolsets might have been defined conditionally, perform # a second round of toolsets expansion now. ProcessToolsetsInDict(build_file_data) # Look at each project's target_defaults dict, and merge settings into # targets. if "target_defaults" in build_file_data: if "targets" not in build_file_data: raise GypError("Unable to find targets in build file %s" % build_file_path) index = 0 while index < len(build_file_data["targets"]): # This procedure needs to give the impression that target_defaults is # used as defaults, and the individual targets inherit from that. # The individual targets need to be merged into the defaults. Make # a deep copy of the defaults for each target, merge the target dict # as found in the input file into that copy, and then hook up the # copy with the target-specific data merged into it as the replacement # target dict. old_target_dict = build_file_data["targets"][index] new_target_dict = gyp.simple_copy.deepcopy( build_file_data["target_defaults"] ) MergeDicts( new_target_dict, old_target_dict, build_file_path, build_file_path ) build_file_data["targets"][index] = new_target_dict index += 1 # No longer needed. del build_file_data["target_defaults"] # Look for dependencies. This means that dependency resolution occurs # after "pre" conditionals and variable expansion, but before "post" - # in other words, you can't put a "dependencies" section inside a "post" # conditional within a target. dependencies = [] if "targets" in build_file_data: for target_dict in build_file_data["targets"]: if "dependencies" not in target_dict: continue for dependency in target_dict["dependencies"]: dependencies.append( gyp.common.ResolveTarget(build_file_path, dependency, None)[0] ) if load_dependencies: for dependency in dependencies: try: LoadTargetBuildFile( dependency, data, aux_data, variables, includes, depth, check, load_dependencies, ) except Exception as e: gyp.common.ExceptionAppend( e, "while loading dependencies of %s" % build_file_path ) raise else: return (build_file_path, dependencies) def CallLoadTargetBuildFile( global_flags, build_file_path, variables, includes, depth, check, generator_input_info, ): """Wrapper around LoadTargetBuildFile for parallel processing. This wrapper is used when LoadTargetBuildFile is executed in a worker process. """ try: signal.signal(signal.SIGINT, signal.SIG_IGN) # Apply globals so that the worker process behaves the same. for key, value in global_flags.items(): globals()[key] = value SetGeneratorGlobals(generator_input_info) result = LoadTargetBuildFile( build_file_path, per_process_data, per_process_aux_data, variables, includes, depth, check, False, ) if not result: return result (build_file_path, dependencies) = result # We can safely pop the build_file_data from per_process_data because it # will never be referenced by this process again, so we don't need to keep # it in the cache. build_file_data = per_process_data.pop(build_file_path) # This gets serialized and sent back to the main process via a pipe. # It's handled in LoadTargetBuildFileCallback. return (build_file_path, build_file_data, dependencies) except GypError as e: sys.stderr.write("gyp: %s\n" % e) return None except Exception as e: print("Exception:", e, file=sys.stderr) print(traceback.format_exc(), file=sys.stderr) return None class ParallelProcessingError(Exception): pass class ParallelState: """Class to keep track of state when processing input files in parallel. If build files are loaded in parallel, use this to keep track of state during farming out and processing parallel jobs. It's stored in a global so that the callback function can have access to it. """ def __init__(self): # The multiprocessing pool. self.pool = None # The condition variable used to protect this object and notify # the main loop when there might be more data to process. self.condition = None # The "data" dict that was passed to LoadTargetBuildFileParallel self.data = None # The number of parallel calls outstanding; decremented when a response # was received. self.pending = 0 # The set of all build files that have been scheduled, so we don't # schedule the same one twice. self.scheduled = set() # A list of dependency build file paths that haven't been scheduled yet. self.dependencies = [] # Flag to indicate if there was an error in a child process. self.error = False def LoadTargetBuildFileCallback(self, result): """Handle the results of running LoadTargetBuildFile in another process. """ self.condition.acquire() if not result: self.error = True self.condition.notify() self.condition.release() return (build_file_path0, build_file_data0, dependencies0) = result self.data[build_file_path0] = build_file_data0 self.data["target_build_files"].add(build_file_path0) for new_dependency in dependencies0: if new_dependency not in self.scheduled: self.scheduled.add(new_dependency) self.dependencies.append(new_dependency) self.pending -= 1 self.condition.notify() self.condition.release() def LoadTargetBuildFilesParallel( build_files, data, variables, includes, depth, check, generator_input_info ): parallel_state = ParallelState() parallel_state.condition = threading.Condition() # Make copies of the build_files argument that we can modify while working. parallel_state.dependencies = list(build_files) parallel_state.scheduled = set(build_files) parallel_state.pending = 0 parallel_state.data = data try: parallel_state.condition.acquire() while parallel_state.dependencies or parallel_state.pending: if parallel_state.error: break if not parallel_state.dependencies: parallel_state.condition.wait() continue dependency = parallel_state.dependencies.pop() parallel_state.pending += 1 global_flags = { "path_sections": globals()["path_sections"], "non_configuration_keys": globals()["non_configuration_keys"], "multiple_toolsets": globals()["multiple_toolsets"], } if not parallel_state.pool: parallel_state.pool = multiprocessing.Pool(multiprocessing.cpu_count()) parallel_state.pool.apply_async( CallLoadTargetBuildFile, args=( global_flags, dependency, variables, includes, depth, check, generator_input_info, ), callback=parallel_state.LoadTargetBuildFileCallback, ) except KeyboardInterrupt as e: parallel_state.pool.terminate() raise e parallel_state.condition.release() parallel_state.pool.close() parallel_state.pool.join() parallel_state.pool = None if parallel_state.error: sys.exit(1) # Look for the bracket that matches the first bracket seen in a # string, and return the start and end as a tuple. For example, if # the input is something like "<(foo <(bar)) blah", then it would # return (1, 13), indicating the entire string except for the leading # "<" and trailing " blah". LBRACKETS = set("{[(") BRACKETS = {"}": "{", "]": "[", ")": "("} def FindEnclosingBracketGroup(input_str): stack = [] start = -1 for index, char in enumerate(input_str): if char in LBRACKETS: stack.append(char) if start == -1: start = index elif char in BRACKETS: if not stack: return (-1, -1) if stack.pop() != BRACKETS[char]: return (-1, -1) if not stack: return (start, index + 1) return (-1, -1) def IsStrCanonicalInt(string): """Returns True if |string| is in its canonical integer form. The canonical form is such that str(int(string)) == string. """ if type(string) is str: # This function is called a lot so for maximum performance, avoid # involving regexps which would otherwise make the code much # shorter. Regexps would need twice the time of this function. if string: if string == "0": return True if string[0] == "-": string = string[1:] if not string: return False if "1" <= string[0] <= "9": return string.isdigit() return False # This matches things like "<(asdf)", "<!(cmd)", "<!@(cmd)", "<|(list)", # "<!interpreter(arguments)", "<([list])", and even "<([)" and "<(<())". # In the last case, the inner "<()" is captured in match['content']. early_variable_re = re.compile( r"(?P<replace>(?P<type><(?:(?:!?@?)|\|)?)" r"(?P<command_string>[-a-zA-Z0-9_.]+)?" r"\((?P<is_array>\s*[?)" r"(?P<content>.*?)(]?)\))" ) # This matches the same as early_variable_re, but with '>' instead of '<'. late_variable_re = re.compile( r"(?P<replace>(?P<type>>(?:(?:!?@?)|\|)?)" r"(?P<command_string>[-a-zA-Z0-9_.]+)?" r"\((?P<is_array>\s*[?)" r"(?P<content>.*?)(]?)\))" ) # This matches the same as early_variable_re, but with '^' instead of '<'. latelate_variable_re = re.compile( r"(?P<replace>(?P<type>[\^](?:(?:!?@?)|\|)?)" r"(?P<command_string>[-a-zA-Z0-9_.]+)?" r"\((?P<is_array>\s*[?)" r"(?P<content>.*?)(]?)\))" ) # Global cache of results from running commands so they don't have to be run # more then once. cached_command_results = {} def FixupPlatformCommand(cmd): if sys.platform == "win32": if type(cmd) is list: cmd = [re.sub("^cat ", "type ", cmd[0])] + cmd[1:] else: cmd = re.sub("^cat ", "type ", cmd) return cmd PHASE_EARLY = 0 PHASE_LATE = 1 PHASE_LATELATE = 2 def ExpandVariables(input, phase, variables, build_file): # Look for the pattern that gets expanded into variables if phase == PHASE_EARLY: variable_re = early_variable_re expansion_symbol = "<" elif phase == PHASE_LATE: variable_re = late_variable_re expansion_symbol = ">" elif phase == PHASE_LATELATE: variable_re = latelate_variable_re expansion_symbol = "^" else: assert False input_str = str(input) if IsStrCanonicalInt(input_str): return int(input_str) # Do a quick scan to determine if an expensive regex search is warranted. if expansion_symbol not in input_str: return input_str # Get the entire list of matches as a list of MatchObject instances. # (using findall here would return strings instead of MatchObjects). matches = list(variable_re.finditer(input_str)) if not matches: return input_str output = input_str # Reverse the list of matches so that replacements are done right-to-left. # That ensures that earlier replacements won't mess up the string in a # way that causes later calls to find the earlier substituted text instead # of what's intended for replacement. matches.reverse() for match_group in matches: match = match_group.groupdict() gyp.DebugOutput(gyp.DEBUG_VARIABLES, "Matches: %r", match) # match['replace'] is the substring to look for, match['type'] # is the character code for the replacement type (< > <! >! <| >| <@ # >@ <!@ >!@), match['is_array'] contains a '[' for command # arrays, and match['content'] is the name of the variable (< >) # or command to run (<! >!). match['command_string'] is an optional # command string. Currently, only 'pymod_do_main' is supported. # run_command is true if a ! variant is used. run_command = "!" in match["type"] command_string = match["command_string"] # file_list is true if a | variant is used. file_list = "|" in match["type"] # Capture these now so we can adjust them later. replace_start = match_group.start("replace") replace_end = match_group.end("replace") # Find the ending paren, and re-evaluate the contained string. (c_start, c_end) = FindEnclosingBracketGroup(input_str[replace_start:]) # Adjust the replacement range to match the entire command # found by FindEnclosingBracketGroup (since the variable_re # probably doesn't match the entire command if it contained # nested variables). replace_end = replace_start + c_end # Find the "real" replacement, matching the appropriate closing # paren, and adjust the replacement start and end. replacement = input_str[replace_start:replace_end] # Figure out what the contents of the variable parens are. contents_start = replace_start + c_start + 1 contents_end = replace_end - 1 contents = input_str[contents_start:contents_end] # Do filter substitution now for <|(). # Admittedly, this is different than the evaluation order in other # contexts. However, since filtration has no chance to run on <|(), # this seems like the only obvious way to give them access to filters. if file_list: processed_variables = gyp.simple_copy.deepcopy(variables) ProcessListFiltersInDict(contents, processed_variables) # Recurse to expand variables in the contents contents = ExpandVariables(contents, phase, processed_variables, build_file) else: # Recurse to expand variables in the contents contents = ExpandVariables(contents, phase, variables, build_file) # Strip off leading/trailing whitespace so that variable matches are # simpler below (and because they are rarely needed). contents = contents.strip() # expand_to_list is true if an @ variant is used. In that case, # the expansion should result in a list. Note that the caller # is to be expecting a list in return, and not all callers do # because not all are working in list context. Also, for list # expansions, there can be no other text besides the variable # expansion in the input string. expand_to_list = "@" in match["type"] and input_str == replacement if run_command or file_list: # Find the build file's directory, so commands can be run or file lists # generated relative to it. build_file_dir = os.path.dirname(build_file) if build_file_dir == "" and not file_list: # If build_file is just a leaf filename indicating a file in the # current directory, build_file_dir might be an empty string. Set # it to None to signal to subprocess.Popen that it should run the # command in the current directory. build_file_dir = None # Support <|(listfile.txt ...) which generates a file # containing items from a gyp list, generated at gyp time. # This works around actions/rules which have more inputs than will # fit on the command line. if file_list: if type(contents) is list: contents_list = contents else: contents_list = contents.split(" ") replacement = contents_list[0] if os.path.isabs(replacement): raise GypError('| cannot handle absolute paths, got "%s"' % replacement) if not generator_filelist_paths: path = os.path.join(build_file_dir, replacement) else: if os.path.isabs(build_file_dir): toplevel = generator_filelist_paths["toplevel"] rel_build_file_dir = gyp.common.RelativePath( build_file_dir, toplevel ) else: rel_build_file_dir = build_file_dir qualified_out_dir = generator_filelist_paths["qualified_out_dir"] path = os.path.join(qualified_out_dir, rel_build_file_dir, replacement) gyp.common.EnsureDirExists(path) replacement = gyp.common.RelativePath(path, build_file_dir) f = gyp.common.WriteOnDiff(path) for i in contents_list[1:]: f.write("%s\n" % i) f.close() elif run_command: use_shell = True if match["is_array"]: contents = eval(contents) use_shell = False # Check for a cached value to avoid executing commands, or generating # file lists more than once. The cache key contains the command to be # run as well as the directory to run it from, to account for commands # that depend on their current directory. # TODO(http://code.google.com/p/gyp/issues/detail?id=111): In theory, # someone could author a set of GYP files where each time the command # is invoked it produces different output by design. When the need # arises, the syntax should be extended to support no caching off a # command's output so it is run every time. cache_key = (str(contents), build_file_dir) cached_value = cached_command_results.get(cache_key, None) if cached_value is None: gyp.DebugOutput( gyp.DEBUG_VARIABLES, "Executing command '%s' in directory '%s'", contents, build_file_dir, ) replacement = "" if command_string == "pymod_do_main": # <!pymod_do_main(modulename param eters) loads |modulename| as a # python module and then calls that module's DoMain() function, # passing ["param", "eters"] as a single list argument. For modules # that don't load quickly, this can be faster than # <!(python modulename param eters). Do this in |build_file_dir|. oldwd = os.getcwd() # Python doesn't like os.open('.'): no fchdir. if build_file_dir: # build_file_dir may be None (see above). os.chdir(build_file_dir) sys.path.append(os.getcwd()) try: parsed_contents = shlex.split(contents) try: py_module = __import__(parsed_contents[0]) except ImportError as e: raise GypError( "Error importing pymod_do_main" "module (%s): %s" % (parsed_contents[0], e) ) replacement = str( py_module.DoMain(parsed_contents[1:]) ).rstrip() finally: sys.path.pop() os.chdir(oldwd) assert replacement is not None elif command_string: raise GypError( "Unknown command string '%s' in '%s'." % (command_string, contents) ) else: # Fix up command with platform specific workarounds. contents = FixupPlatformCommand(contents) try: # stderr will be printed no matter what result = subprocess.run( contents, stdout=subprocess.PIPE, shell=use_shell, cwd=build_file_dir, check=False ) except Exception as e: raise GypError( "%s while executing command '%s' in %s" % (e, contents, build_file) ) if result.returncode > 0: raise GypError( "Call to '%s' returned exit status %d while in %s." % (contents, result.returncode, build_file) ) replacement = result.stdout.decode("utf-8").rstrip() cached_command_results[cache_key] = replacement else: gyp.DebugOutput( gyp.DEBUG_VARIABLES, "Had cache value for command '%s' in directory '%s'", contents, build_file_dir, ) replacement = cached_value else: if contents not in variables: if contents[-1] in ["!", "/"]: # In order to allow cross-compiles (nacl) to happen more naturally, # we will allow references to >(sources/) etc. to resolve to # and empty list if undefined. This allows actions to: # 'action!': [ # '>@(_sources!)', # ], # 'action/': [ # '>@(_sources/)', # ], replacement = [] else: raise GypError( "Undefined variable " + contents + " in " + build_file ) else: replacement = variables[contents] if isinstance(replacement, bytes) and not isinstance(replacement, str): replacement = replacement.decode("utf-8") # done on Python 3 only if type(replacement) is list: for item in replacement: if isinstance(item, bytes) and not isinstance(item, str): item = item.decode("utf-8") # done on Python 3 only if not contents[-1] == "/" and type(item) not in (str, int): raise GypError( "Variable " + contents + " must expand to a string or list of strings; " + "list contains a " + item.__class__.__name__ )
...höre mit dem einkopieren auf, denn die Datei scheint seeeehr lang zu sein, und habe immer nur einzelne Zeilen nacheinander kopieren können.